chore: update Cargo dependencies, configs and API clients
This commit is contained in:
parent
88a51f45cb
commit
1e94c280e9
27
.env.example
27
.env.example
@ -107,3 +107,30 @@ APP_DOMAIN_URL=http://127.0.0.1
|
|||||||
# HOOK_POOL_REDIS_BLOCK_TIMEOUT=5
|
# HOOK_POOL_REDIS_BLOCK_TIMEOUT=5
|
||||||
# HOOK_POOL_REDIS_MAX_RETRIES=3
|
# HOOK_POOL_REDIS_MAX_RETRIES=3
|
||||||
# HOOK_POOL_WORKER_ID=(随机 UUID)
|
# HOOK_POOL_WORKER_ID=(随机 UUID)
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Frontend (Vite) — 前端运行环境变量
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# API 基础 URL(为空时使用 Vite dev 代理 /api -> localhost:8080)
|
||||||
|
# VITE_API_BASE_URL=http://localhost:8080
|
||||||
|
|
||||||
|
# 前端 WebSocket 连接地址(开发模式通过 Vite 代理)
|
||||||
|
VITE_WS_URL=ws://localhost:5080
|
||||||
|
|
||||||
|
# API URL(前端 API 调用,通过 Vite 代理时可为空)
|
||||||
|
VITE_API_URL=
|
||||||
|
|
||||||
|
# WebSocket 连接模式: "raw-ws" | "socketio"
|
||||||
|
VITE_WS_MODE=raw-ws
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Frontend: Grafana Faro (RUM) — 前端性能监控(可选)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# VITE_FARO_ENABLED=false
|
||||||
|
# VITE_FARO_URL=https://faro.example.com/collect
|
||||||
|
# VITE_FARO_API_KEY=
|
||||||
|
# VITE_FARO_APP_NAME=GitDataAIWeb
|
||||||
|
# VITE_FARO_APP_ENV=production
|
||||||
|
# VITE_FARO_APP_VERSION=0.0.1
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3678,6 +3678,7 @@ dependencies = [
|
|||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
"sha1 0.11.0",
|
"sha1 0.11.0",
|
||||||
"sha2 0.11.0",
|
"sha2 0.11.0",
|
||||||
"ssh-key",
|
"ssh-key",
|
||||||
@ -9036,6 +9037,7 @@ dependencies = [
|
|||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
"session",
|
"session",
|
||||||
"sha1 0.11.0",
|
"sha1 0.11.0",
|
||||||
"sha2 0.11.0",
|
"sha2 0.11.0",
|
||||||
|
|||||||
@ -133,7 +133,7 @@ fs2 = "0.4.3"
|
|||||||
image = "0.25.10"
|
image = "0.25.10"
|
||||||
tokio = "1.50.0"
|
tokio = "1.50.0"
|
||||||
tokio-util = "0.7.18"
|
tokio-util = "0.7.18"
|
||||||
tokio-stream = "0.1.18"
|
tokio-stream = { version = "0.1.18", features = ["sync"] }
|
||||||
url = "2.5.8"
|
url = "2.5.8"
|
||||||
tower = "0.5"
|
tower = "0.5"
|
||||||
num_cpus = "1.17.0"
|
num_cpus = "1.17.0"
|
||||||
|
|||||||
431
openapi.json
431
openapi.json
@ -7359,6 +7359,158 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/projects/{project_name}/message-favorites": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Project"
|
||||||
|
],
|
||||||
|
"operationId": "project_message_favorites",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "project_name",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "page",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "per_page",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "List current user's project message favorites",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ApiResponse_ProjectMessageFavoriteResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Forbidden"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/projects/{project_name}/messages/{message_id}/favorite": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Project"
|
||||||
|
],
|
||||||
|
"operationId": "project_message_favorite_add",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "project_name",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "message_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Favorite a project message",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ApiResponse_ProjectMessageFavoriteItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Forbidden"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"tags": [
|
||||||
|
"Project"
|
||||||
|
],
|
||||||
|
"operationId": "project_message_favorite_remove",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "project_name",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "message_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Remove a project message favorite"
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Forbidden"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/projects/{project_name}/repos": {
|
"/api/projects/{project_name}/repos": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -24956,7 +25108,9 @@
|
|||||||
"required": [
|
"required": [
|
||||||
"uid",
|
"uid",
|
||||||
"username",
|
"username",
|
||||||
"has_unread_notifications"
|
"has_unread_notifications",
|
||||||
|
"language",
|
||||||
|
"timezone"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"uid": {
|
"uid": {
|
||||||
@ -24982,6 +25136,12 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
"minimum": 0
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"timezone": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -27442,6 +27602,136 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ApiResponse_ProjectMessageFavoriteItem": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"code",
|
||||||
|
"message"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"uid",
|
||||||
|
"project_uid",
|
||||||
|
"room_id",
|
||||||
|
"room_name",
|
||||||
|
"message_id",
|
||||||
|
"sender_type",
|
||||||
|
"content",
|
||||||
|
"content_type",
|
||||||
|
"send_at",
|
||||||
|
"favorited_at"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"uid": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"project_uid": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"room_id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"room_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"message_id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"sender_id": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"sender_type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"display_name": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"content_type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"send_at": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"favorited_at": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ApiResponse_ProjectMessageFavoriteResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"code",
|
||||||
|
"message"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"page",
|
||||||
|
"per_page",
|
||||||
|
"total",
|
||||||
|
"list"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"page": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"per_page": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ProjectMessageFavoriteItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ApiResponse_ProjectRepoCreateResponse": {
|
"ApiResponse_ProjectRepoCreateResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -34908,7 +35198,9 @@
|
|||||||
"required": [
|
"required": [
|
||||||
"uid",
|
"uid",
|
||||||
"username",
|
"username",
|
||||||
"has_unread_notifications"
|
"has_unread_notifications",
|
||||||
|
"language",
|
||||||
|
"timezone"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"uid": {
|
"uid": {
|
||||||
@ -34934,6 +35226,12 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
"minimum": 0
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"timezone": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -39247,6 +39545,125 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ProjectMessageFavoriteItem": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"uid",
|
||||||
|
"project_uid",
|
||||||
|
"room_id",
|
||||||
|
"room_name",
|
||||||
|
"message_id",
|
||||||
|
"sender_type",
|
||||||
|
"content",
|
||||||
|
"content_type",
|
||||||
|
"send_at",
|
||||||
|
"favorited_at"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"uid": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"project_uid": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"room_id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"room_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"message_id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"sender_id": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"sender_type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"display_name": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"content_type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"send_at": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"favorited_at": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ProjectMessageFavoriteQuery": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"page": {
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"per_page": {
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ProjectMessageFavoriteResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"page",
|
||||||
|
"per_page",
|
||||||
|
"total",
|
||||||
|
"list"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"page": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"per_page": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ProjectMessageFavoriteItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ProjectModel": {
|
"ProjectModel": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -43614,7 +44031,7 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"uid",
|
"uid",
|
||||||
"project_uid",
|
"user_uid",
|
||||||
"amount",
|
"amount",
|
||||||
"currency",
|
"currency",
|
||||||
"reason",
|
"reason",
|
||||||
@ -43626,16 +44043,16 @@
|
|||||||
"format": "uuid"
|
"format": "uuid"
|
||||||
},
|
},
|
||||||
"project_uid": {
|
"project_uid": {
|
||||||
"type": "string",
|
|
||||||
"format": "uuid"
|
|
||||||
},
|
|
||||||
"user_uid": {
|
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"null"
|
"null"
|
||||||
],
|
],
|
||||||
"format": "uuid"
|
"format": "uuid"
|
||||||
},
|
},
|
||||||
|
"user_uid": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
"amount": {
|
"amount": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"format": "double"
|
"format": "double"
|
||||||
|
|||||||
@ -157,9 +157,59 @@ export async function shareConversation(conversationId: string): Promise<{ share
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface StreamChunk {
|
export interface StreamChunk {
|
||||||
type: "token" | "thinking" | "tool_call" | "tool_result" | "done" | "error" | "title" | "billing_error";
|
type: "token" | "thinking" | "tool_call" | "tool_result" | "done" | "stopped" | "error" | "title" | "billing_error";
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
data: any;
|
data: any;
|
||||||
|
children_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* streamConversationWatch(conversationId: string): AsyncGenerator<StreamChunk> {
|
||||||
|
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL || ""}/api/ai/conversations/${conversationId}/watch`, {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error(`Watch stream request failed: ${response.status}`);
|
||||||
|
if (!response.body) throw new Error("No response body");
|
||||||
|
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
let buffer = "";
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
|
||||||
|
buffer += decoder.decode(value, { stream: true });
|
||||||
|
const lines = buffer.split("\n");
|
||||||
|
buffer = lines.pop() || "";
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (!trimmed.startsWith("data: ")) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(trimmed.slice(6));
|
||||||
|
const eventType = parsed.type || parsed.event;
|
||||||
|
const payload = parsed.data;
|
||||||
|
|
||||||
|
if (eventType === "message" && payload?.role === "assistant" && payload?.content) {
|
||||||
|
yield { type: "done", data: "ok" };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chunkType = payload?.chunk_type || eventType;
|
||||||
|
if (chunkType === "token" || chunkType === "thinking" || chunkType === "tool_call" || chunkType === "tool_result" || chunkType === "error") {
|
||||||
|
yield {
|
||||||
|
type: chunkType,
|
||||||
|
data: payload?.content ?? payload?.error ?? payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore comments and malformed events.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function* streamChat(conversationId: string, messageId: string): AsyncGenerator<StreamChunk> {
|
export async function* streamChat(conversationId: string, messageId: string): AsyncGenerator<StreamChunk> {
|
||||||
@ -197,8 +247,12 @@ export async function* streamChat(conversationId: string, messageId: string): As
|
|||||||
const parsed = JSON.parse(jsonStr);
|
const parsed = JSON.parse(jsonStr);
|
||||||
// Normalize backend SSE format: {event: "token", data: "..."} → {type: "token", data: "..."}
|
// Normalize backend SSE format: {event: "token", data: "..."} → {type: "token", data: "..."}
|
||||||
const eventType = parsed.type || parsed.event;
|
const eventType = parsed.type || parsed.event;
|
||||||
|
if (eventType === "recovery") {
|
||||||
|
yield* streamConversationWatch(conversationId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (eventType === "token" || eventType === "thinking" || eventType === "tool_call" || eventType === "tool_result" || eventType === "done" || eventType === "error" || eventType === "title" || eventType === "billing_error") {
|
if (eventType === "token" || eventType === "thinking" || eventType === "tool_call" || eventType === "tool_result" || eventType === "done" || eventType === "error" || eventType === "title" || eventType === "billing_error") {
|
||||||
yield { type: eventType, data: parsed.data };
|
yield { type: eventType, data: parsed.data, children_id: parsed.children_id };
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore unparseable lines
|
// Ignore unparseable lines
|
||||||
@ -206,3 +260,64 @@ export async function* streamChat(conversationId: string, messageId: string): As
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Stream sub-agent output via SSE.
|
||||||
|
* Connects to `GET /api/ai/subagent/{conversationId}/{childrenId}/stream`
|
||||||
|
* and yields StreamChunk events (token, thinking, done, error).
|
||||||
|
*/
|
||||||
|
export async function* streamSubAgent(conversationId: string, childrenId: string, signal?: AbortSignal): AsyncGenerator<StreamChunk> {
|
||||||
|
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL || ""}/api/ai/subagent/${conversationId}/${childrenId}/stream`, {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include",
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error(`Sub-agent stream request failed: ${response.status}`);
|
||||||
|
if (!response.body) throw new Error("No response body");
|
||||||
|
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
let buffer = "";
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
|
||||||
|
buffer += decoder.decode(value, { stream: true });
|
||||||
|
const lines = buffer.split("\n");
|
||||||
|
buffer = lines.pop() || "";
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (!trimmed.startsWith("data: ")) continue;
|
||||||
|
|
||||||
|
const jsonStr = trimmed.slice(6);
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(jsonStr);
|
||||||
|
const eventType = parsed.type || parsed.event;
|
||||||
|
if (eventType === "token" || eventType === "thinking" || eventType === "done" || eventType === "stopped" || eventType === "error") {
|
||||||
|
const data = parsed.data && typeof parsed.data === "object" && "content" in parsed.data
|
||||||
|
? parsed.data.content
|
||||||
|
: parsed.data;
|
||||||
|
yield { type: eventType, data };
|
||||||
|
} else if (eventType === "tool_call" || eventType === "tool_result") {
|
||||||
|
yield {
|
||||||
|
type: eventType,
|
||||||
|
data: parsed.data?.metadata || parsed.data,
|
||||||
|
children_id: parsed.data?.children_id || parsed.children_id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore unparseable lines
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stopSubAgent(conversationId: string, childrenId: string): Promise<void> {
|
||||||
|
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL || ""}/api/ai/subagent/${conversationId}/${childrenId}/stop`, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error(`Failed to stop sub-agent: ${response.status}`);
|
||||||
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ const axiosInstance = axios.create({
|
|||||||
const api = getApi(axiosInstance);
|
const api = getApi(axiosInstance);
|
||||||
|
|
||||||
// Export all API functions
|
// Export all API functions
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
// Auth
|
// Auth
|
||||||
api2faDisable,
|
api2faDisable,
|
||||||
@ -67,6 +68,9 @@ export const {
|
|||||||
projectBilling,
|
projectBilling,
|
||||||
projectBillingHistory,
|
projectBillingHistory,
|
||||||
projectBillingErrors,
|
projectBillingErrors,
|
||||||
|
projectMessageFavorites,
|
||||||
|
projectMessageFavoriteAdd,
|
||||||
|
projectMessageFavoriteRemove,
|
||||||
projectCreateLabel,
|
projectCreateLabel,
|
||||||
projectUpdateLabel,
|
projectUpdateLabel,
|
||||||
projectDeleteLabel,
|
projectDeleteLabel,
|
||||||
@ -163,6 +167,8 @@ export const {
|
|||||||
modelList,
|
modelList,
|
||||||
categoryList,
|
categoryList,
|
||||||
categoryCreate,
|
categoryCreate,
|
||||||
|
categoryDelete,
|
||||||
|
categoryUpdate,
|
||||||
participantList,
|
participantList,
|
||||||
pinAdd,
|
pinAdd,
|
||||||
pinRemove,
|
pinRemove,
|
||||||
@ -248,6 +254,7 @@ export const {
|
|||||||
aiMessageCreate,
|
aiMessageCreate,
|
||||||
aiMessageGet,
|
aiMessageGet,
|
||||||
aiMessageChildren,
|
aiMessageChildren,
|
||||||
|
// @ts-ignore
|
||||||
aiMessageFork,
|
aiMessageFork,
|
||||||
aiMessageResend,
|
aiMessageResend,
|
||||||
aiMessageStop,
|
aiMessageStop,
|
||||||
@ -300,6 +307,7 @@ export const {
|
|||||||
|
|
||||||
// Search
|
// Search
|
||||||
search,
|
search,
|
||||||
|
searchMessages,
|
||||||
} = api;
|
} = api;
|
||||||
|
|
||||||
// Manual avatar upload (not in generated API)
|
// Manual avatar upload (not in generated API)
|
||||||
|
|||||||
111
src/fonts.css
111
src/fonts.css
@ -0,0 +1,111 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-Thin.woff2") format("woff2");
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-ThinItalic.woff2") format("woff2");
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-ExtraLight.woff2") format("woff2");
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-ExtraLightItalic.woff2") format("woff2");
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-Light.woff2") format("woff2");
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-LightItalic.woff2") format("woff2");
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-Regular.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-Italic.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-Medium.woff2") format("woff2");
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-MediumItalic.woff2") format("woff2");
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-SemiBold.woff2") format("woff2");
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-SemiBoldItalic.woff2") format("woff2");
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-Bold.woff2") format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-BoldItalic.woff2") format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-ExtraBold.woff2") format("woff2");
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "JetBrains Mono";
|
||||||
|
src: url("/fonts/JetBrainsMono-ExtraBoldItalic.woff2") format("woff2");
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
350
src/index.css
350
src/index.css
@ -8,6 +8,12 @@
|
|||||||
|
|
||||||
@plugin "@tailwindcss/typography";
|
@plugin "@tailwindcss/typography";
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Inter,
|
||||||
|
system-ui,
|
||||||
|
sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
@ -129,192 +135,210 @@
|
|||||||
───────────────────────────────────────────── */
|
───────────────────────────────────────────── */
|
||||||
:root {
|
:root {
|
||||||
/* Surfaces (layer hierarchy, 3 layers only) */
|
/* Surfaces (layer hierarchy, 3 layers only) */
|
||||||
--surface-rail: oklch(0.98 0 0);
|
--surface-rail: oklch(0.98 0 0);
|
||||||
--surface-sidebar: oklch(0.97 0 0);
|
--surface-sidebar: oklch(0.97 0 0);
|
||||||
--surface-ground: oklch(1 0 0);
|
--surface-ground: oklch(1 0 0);
|
||||||
--surface-elevated: oklch(1 0 0);
|
--surface-elevated: oklch(1 0 0);
|
||||||
--surface-overlay: oklch(1 0 0 / 90%);
|
--surface-overlay: oklch(1 0 0 / 90%);
|
||||||
|
|
||||||
/* Borders */
|
/* Borders */
|
||||||
--border-subtle: oklch(0.90 0 0 / 40%);
|
--border-subtle: oklch(0.90 0 0 / 40%);
|
||||||
--border-default: oklch(0.88 0 0);
|
--border-default: oklch(0.88 0 0);
|
||||||
--border-strong: oklch(0.80 0 0);
|
--border-strong: oklch(0.80 0 0);
|
||||||
|
|
||||||
/* Text */
|
/* Text */
|
||||||
--text-primary: oklch(0.13 0 0);
|
--text-primary: oklch(0.13 0 0);
|
||||||
--text-secondary: oklch(0.40 0 0);
|
--text-secondary: oklch(0.40 0 0);
|
||||||
--text-muted: oklch(0.55 0 0);
|
--text-muted: oklch(0.55 0 0);
|
||||||
--text-inverse: oklch(0.985 0 0);
|
--text-inverse: oklch(0.985 0 0);
|
||||||
--text-tertiary: oklch(0.70 0 0);
|
--text-tertiary: oklch(0.70 0 0);
|
||||||
|
|
||||||
/* Brand accent — Discord blurple as reference */
|
/* Brand accent — Discord blurple as reference */
|
||||||
--accent: oklch(0.25 0 0);
|
--accent: oklch(0.25 0 0);
|
||||||
--accent-hover: oklch(0.15 0 0);
|
--accent-hover: oklch(0.15 0 0);
|
||||||
--accent-fg: oklch(0.985 0 0);
|
--accent-fg: oklch(0.985 0 0);
|
||||||
--accent-muted: oklch(0.25 0 0 / 15%);
|
--accent-muted: oklch(0.25 0 0 / 15%);
|
||||||
--accent-bg: oklch(0.25 0 0 / 8%);
|
--accent-bg: oklch(0.25 0 0 / 8%);
|
||||||
--accent-rgb: 88, 101, 242;
|
--accent-rgb: 88, 101, 242;
|
||||||
|
|
||||||
/* Status */
|
/* Status */
|
||||||
--status-online: oklch(0.55 0 0);
|
--status-online: oklch(0.55 0 0);
|
||||||
--status-idle: oklch(0.60 0 0);
|
--status-idle: oklch(0.60 0 0);
|
||||||
--status-dnd: oklch(0.50 0 0);
|
--status-dnd: oklch(0.50 0 0);
|
||||||
--status-offline: oklch(0.60 0 0);
|
--status-offline: oklch(0.60 0 0);
|
||||||
|
|
||||||
/* Semantic */
|
/* Semantic */
|
||||||
--success: oklch(0.50 0 0);
|
--success: oklch(0.50 0 0);
|
||||||
--success-alpha10: oklch(0.50 0 0 / 10%);
|
--success-alpha10: oklch(0.50 0 0 / 10%);
|
||||||
--warning: oklch(0.60 0 0);
|
--warning: oklch(0.60 0 0);
|
||||||
--warning-alpha10: oklch(0.60 0 0 / 10%);
|
--warning-alpha10: oklch(0.60 0 0 / 10%);
|
||||||
--destructive: oklch(0.45 0 0);
|
--destructive: oklch(0.45 0 0);
|
||||||
--destructive-alpha10: oklch(0.45 0 0 / 10%);
|
--destructive-alpha10: oklch(0.45 0 0 / 10%);
|
||||||
--info: oklch(0.50 0 0);
|
--info: oklch(0.50 0 0);
|
||||||
|
|
||||||
/* Role colors (from Discord) */
|
/* Role colors (from Discord) */
|
||||||
--role-red: oklch(0.50 0 0);
|
--role-red: oklch(0.50 0 0);
|
||||||
--role-orange: oklch(0.55 0 0);
|
--role-orange: oklch(0.55 0 0);
|
||||||
--role-yellow: oklch(0.60 0 0);
|
--role-yellow: oklch(0.60 0 0);
|
||||||
--role-green: oklch(0.50 0 0);
|
--role-green: oklch(0.50 0 0);
|
||||||
--role-blue: oklch(0.55 0 0);
|
--role-blue: oklch(0.55 0 0);
|
||||||
--role-purple: oklch(0.55 0 0);
|
--role-purple: oklch(0.55 0 0);
|
||||||
--role-pink: oklch(0.55 0 0);
|
--role-pink: oklch(0.55 0 0);
|
||||||
--role-gray: oklch(0.50 0 0);
|
--role-gray: oklch(0.50 0 0);
|
||||||
|
|
||||||
/* Interactive */
|
/* Interactive */
|
||||||
--interactive: oklch(0.97 0 0);
|
--interactive: oklch(0.97 0 0);
|
||||||
--interactive-hover: oklch(0.92 0 0);
|
--interactive-hover: oklch(0.92 0 0);
|
||||||
--interactive-active: oklch(0.88 0 0);
|
--interactive-active: oklch(0.88 0 0);
|
||||||
|
|
||||||
/* Hover */
|
/* Hover */
|
||||||
--hover-bg: oklch(0.95 0 0 / 70%);
|
--hover-bg: oklch(0.95 0 0 / 70%);
|
||||||
--hover-bg-strong: oklch(0.90 0 0);
|
--hover-bg-strong: oklch(0.90 0 0);
|
||||||
|
|
||||||
/* Input */
|
/* Input */
|
||||||
--input-bg: oklch(0.98 0 0);
|
--input-bg: oklch(0.98 0 0);
|
||||||
--input-placeholder: oklch(0.50 0 0);
|
--input-placeholder: oklch(0.50 0 0);
|
||||||
--input-ring: oklch(0.30 0 0);
|
--input-ring: oklch(0.30 0 0);
|
||||||
|
|
||||||
/* Heatmap (contribution graph) */
|
/* Heatmap (contribution graph) */
|
||||||
--heatmap-0: oklch(0.93 0 0);
|
--heatmap-0: oklch(0.93 0 0);
|
||||||
--heatmap-1: oklch(0.75 0.10 155);
|
--heatmap-1: oklch(0.75 0.10 155);
|
||||||
--heatmap-2: oklch(0.62 0.15 155);
|
--heatmap-2: oklch(0.62 0.15 155);
|
||||||
--heatmap-3: oklch(0.50 0.15 155);
|
--heatmap-3: oklch(0.50 0.15 155);
|
||||||
--heatmap-4: oklch(0.38 0.15 155);
|
--heatmap-4: oklch(0.38 0.15 155);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--surface-rail: oklch(0.12 0 0);
|
--surface-rail: oklch(0.12 0 0);
|
||||||
--surface-sidebar: oklch(0.15 0 0);
|
--surface-sidebar: oklch(0.15 0 0);
|
||||||
--surface-ground: oklch(0.13 0 0);
|
--surface-ground: oklch(0.13 0 0);
|
||||||
--surface-elevated: oklch(0.18 0 0);
|
--surface-elevated: oklch(0.18 0 0);
|
||||||
--surface-overlay: oklch(0.10 0 0 / 95%);
|
--surface-overlay: oklch(0.10 0 0 / 95%);
|
||||||
|
|
||||||
--border-subtle: oklch(0.30 0 0 / 30%);
|
--border-subtle: oklch(0.30 0 0 / 30%);
|
||||||
--border-default: oklch(0.35 0 0);
|
--border-default: oklch(0.35 0 0);
|
||||||
--border-strong: oklch(0.50 0 0);
|
--border-strong: oklch(0.50 0 0);
|
||||||
|
|
||||||
--text-primary: oklch(0.97 0 0);
|
--text-primary: oklch(0.97 0 0);
|
||||||
--text-secondary: oklch(0.80 0 0);
|
--text-secondary: oklch(0.80 0 0);
|
||||||
--text-muted: oklch(0.65 0 0);
|
--text-muted: oklch(0.65 0 0);
|
||||||
--text-inverse: oklch(0.13 0 0);
|
--text-inverse: oklch(0.13 0 0);
|
||||||
--text-tertiary: oklch(0.55 0 0);
|
--text-tertiary: oklch(0.55 0 0);
|
||||||
|
|
||||||
--accent: oklch(0.70 0.15 264);
|
--accent: oklch(0.70 0.15 264);
|
||||||
--accent-hover: oklch(0.78 0.15 264);
|
--accent-hover: oklch(0.78 0.15 264);
|
||||||
--accent-fg: oklch(0.10 0 0);
|
--accent-fg: oklch(0.10 0 0);
|
||||||
--accent-muted: oklch(0.70 0.15 264 / 20%);
|
--accent-muted: oklch(0.70 0.15 264 / 20%);
|
||||||
--accent-bg: oklch(0.70 0.15 264 / 12%);
|
--accent-bg: oklch(0.70 0.15 264 / 12%);
|
||||||
--accent-rgb: 88, 101, 242;
|
--accent-rgb: 88, 101, 242;
|
||||||
|
|
||||||
--status-online: oklch(0.72 0.17 155);
|
--status-online: oklch(0.72 0.17 155);
|
||||||
--status-idle: oklch(0.78 0.15 80);
|
--status-idle: oklch(0.78 0.15 80);
|
||||||
--status-dnd: oklch(0.65 0.20 25);
|
--status-dnd: oklch(0.65 0.20 25);
|
||||||
--status-offline: oklch(0.55 0 0);
|
--status-offline: oklch(0.55 0 0);
|
||||||
|
|
||||||
--success: oklch(0.72 0.17 155);
|
--success: oklch(0.72 0.17 155);
|
||||||
--success-alpha10: oklch(0.72 0.17 155 / 10%);
|
--success-alpha10: oklch(0.72 0.17 155 / 10%);
|
||||||
--warning: oklch(0.78 0.15 90);
|
--warning: oklch(0.78 0.15 90);
|
||||||
--warning-alpha10: oklch(0.78 0.15 90 / 10%);
|
--warning-alpha10: oklch(0.78 0.15 90 / 10%);
|
||||||
--destructive: oklch(0.70 0.20 25);
|
--destructive: oklch(0.70 0.20 25);
|
||||||
--destructive-alpha10: oklch(0.70 0.20 25 / 10%);
|
--destructive-alpha10: oklch(0.70 0.20 25 / 10%);
|
||||||
--info: oklch(0.70 0.15 250);
|
--info: oklch(0.70 0.15 250);
|
||||||
|
|
||||||
--role-red: oklch(0.65 0.20 20);
|
--role-red: oklch(0.65 0.20 20);
|
||||||
--role-orange: oklch(0.72 0.18 50);
|
--role-orange: oklch(0.72 0.18 50);
|
||||||
--role-yellow: oklch(0.78 0.16 85);
|
--role-yellow: oklch(0.78 0.16 85);
|
||||||
--role-green: oklch(0.70 0.17 155);
|
--role-green: oklch(0.70 0.17 155);
|
||||||
--role-blue: oklch(0.70 0.20 250);
|
--role-blue: oklch(0.70 0.20 250);
|
||||||
--role-purple: oklch(0.65 0.20 290);
|
--role-purple: oklch(0.65 0.20 290);
|
||||||
--role-pink: oklch(0.65 0.20 340);
|
--role-pink: oklch(0.65 0.20 340);
|
||||||
--role-gray: oklch(0.58 0 0);
|
--role-gray: oklch(0.58 0 0);
|
||||||
|
|
||||||
--interactive: oklch(0.18 0 0);
|
--interactive: oklch(0.18 0 0);
|
||||||
--interactive-hover: oklch(0.25 0 0);
|
--interactive-hover: oklch(0.25 0 0);
|
||||||
--interactive-active: oklch(0.32 0 0);
|
--interactive-active: oklch(0.32 0 0);
|
||||||
|
|
||||||
--hover-bg: oklch(0.22 0 0 / 60%);
|
--hover-bg: oklch(0.22 0 0 / 60%);
|
||||||
--hover-bg-strong: oklch(0.28 0 0);
|
--hover-bg-strong: oklch(0.28 0 0);
|
||||||
|
|
||||||
--input-bg: oklch(0.15 0 0);
|
--input-bg: oklch(0.15 0 0);
|
||||||
--input-placeholder: oklch(0.55 0 0);
|
--input-placeholder: oklch(0.55 0 0);
|
||||||
--input-ring: oklch(0.70 0.15 264);
|
--input-ring: oklch(0.70 0.15 264);
|
||||||
|
|
||||||
/* Heatmap (contribution graph) */
|
/* Heatmap (contribution graph) */
|
||||||
--heatmap-0: oklch(0.20 0 0);
|
--heatmap-0: oklch(0.20 0 0);
|
||||||
--heatmap-1: oklch(0.30 0.08 155);
|
--heatmap-1: oklch(0.30 0.08 155);
|
||||||
--heatmap-2: oklch(0.45 0.12 155);
|
--heatmap-2: oklch(0.45 0.12 155);
|
||||||
--heatmap-3: oklch(0.60 0.15 155);
|
--heatmap-3: oklch(0.60 0.15 155);
|
||||||
--heatmap-4: oklch(0.75 0.15 155);
|
--heatmap-4: oklch(0.75 0.15 155);
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border outline-ring/50;
|
@apply border-border outline-ring/50;
|
||||||
}
|
}
|
||||||
body {
|
|
||||||
@apply bg-background text-foreground;
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
html {
|
|
||||||
@apply font-sans;
|
html {
|
||||||
|
@apply font-sans;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Settings Modal open/close animation ─── */
|
/* ─── Settings Modal open/close animation ─── */
|
||||||
|
|
||||||
.settings-dialog[data-state="open"] {
|
.settings-dialog[data-state="open"] {
|
||||||
animation: settings-modal-open 0.2s cubic-bezier(0.4, 0, 0.2, 1) both;
|
animation: settings-modal-open 0.2s cubic-bezier(0.4, 0, 0.2, 1) both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-dialog[data-state="closed"] {
|
.settings-dialog[data-state="closed"] {
|
||||||
animation: settings-modal-close 0.15s cubic-bezier(0.4, 0, 0.2, 1) both;
|
animation: settings-modal-close 0.15s cubic-bezier(0.4, 0, 0.2, 1) both;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes settings-modal-open {
|
@keyframes settings-modal-open {
|
||||||
from { opacity: 0; }
|
from {
|
||||||
to { opacity: 1; }
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes settings-modal-close {
|
@keyframes settings-modal-close {
|
||||||
from { opacity: 1; }
|
from {
|
||||||
to { opacity: 0; }
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="dialog-overlay"][data-state="open"] {
|
[data-slot="dialog-overlay"][data-state="open"] {
|
||||||
animation: settings-overlay-open 0.2s cubic-bezier(0.4, 0, 0.2, 1) both;
|
animation: settings-overlay-open 0.2s cubic-bezier(0.4, 0, 0.2, 1) both;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="dialog-overlay"][data-state="closed"] {
|
[data-slot="dialog-overlay"][data-state="closed"] {
|
||||||
animation: settings-overlay-close 0.15s cubic-bezier(0.4, 0, 0.2, 1) both;
|
animation: settings-overlay-close 0.15s cubic-bezier(0.4, 0, 0.2, 1) both;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes settings-overlay-open {
|
@keyframes settings-overlay-open {
|
||||||
from { opacity: 0; }
|
from {
|
||||||
to { opacity: 1; }
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes settings-overlay-close {
|
@keyframes settings-overlay-close {
|
||||||
from { opacity: 1; }
|
from {
|
||||||
to { opacity: 0; }
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─────────────────────────────────────────────
|
/* ─────────────────────────────────────────────
|
||||||
@ -323,75 +347,81 @@
|
|||||||
dark mode text never falls back to defaults
|
dark mode text never falls back to defaults
|
||||||
───────────────────────────────────────────── */
|
───────────────────────────────────────────── */
|
||||||
:root .prose {
|
:root .prose {
|
||||||
--tw-prose-body: var(--text-primary);
|
--tw-prose-body: var(--text-primary);
|
||||||
--tw-prose-headings: var(--text-primary);
|
--tw-prose-headings: var(--text-primary);
|
||||||
--tw-prose-lead: var(--text-secondary);
|
--tw-prose-lead: var(--text-secondary);
|
||||||
--tw-prose-links: var(--accent);
|
--tw-prose-links: var(--accent);
|
||||||
--tw-prose-bold: var(--text-primary);
|
--tw-prose-bold: var(--text-primary);
|
||||||
--tw-prose-counters: var(--text-secondary);
|
--tw-prose-counters: var(--text-secondary);
|
||||||
--tw-prose-bullets: var(--text-muted);
|
--tw-prose-bullets: var(--text-muted);
|
||||||
--tw-prose-hr: var(--border-default);
|
--tw-prose-hr: var(--border-default);
|
||||||
--tw-prose-quotes: var(--text-secondary);
|
--tw-prose-quotes: var(--text-secondary);
|
||||||
--tw-prose-quote-borders: var(--border-default);
|
--tw-prose-quote-borders: var(--border-default);
|
||||||
--tw-prose-captions: var(--text-muted);
|
--tw-prose-captions: var(--text-muted);
|
||||||
--tw-prose-kbd: var(--text-primary);
|
--tw-prose-kbd: var(--text-primary);
|
||||||
--tw-prose-kbd-shadows: oklch(0.13 0 0 / 10%);
|
--tw-prose-kbd-shadows: oklch(0.13 0 0 / 10%);
|
||||||
--tw-prose-code: var(--accent);
|
--tw-prose-code: var(--accent);
|
||||||
--tw-prose-pre-code: var(--text-primary);
|
--tw-prose-pre-code: var(--text-primary);
|
||||||
--tw-prose-pre-bg: var(--surface-elevated);
|
--tw-prose-pre-bg: var(--surface-elevated);
|
||||||
--tw-prose-th-borders: var(--border-default);
|
--tw-prose-th-borders: var(--border-default);
|
||||||
--tw-prose-td-borders: var(--border-subtle);
|
--tw-prose-td-borders: var(--border-subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .prose {
|
.dark .prose {
|
||||||
--tw-prose-body: var(--text-primary);
|
--tw-prose-body: var(--text-primary);
|
||||||
--tw-prose-headings: var(--text-primary);
|
--tw-prose-headings: var(--text-primary);
|
||||||
--tw-prose-lead: var(--text-secondary);
|
--tw-prose-lead: var(--text-secondary);
|
||||||
--tw-prose-links: var(--accent);
|
--tw-prose-links: var(--accent);
|
||||||
--tw-prose-bold: var(--text-primary);
|
--tw-prose-bold: var(--text-primary);
|
||||||
--tw-prose-counters: var(--text-secondary);
|
--tw-prose-counters: var(--text-secondary);
|
||||||
--tw-prose-bullets: var(--text-muted);
|
--tw-prose-bullets: var(--text-muted);
|
||||||
--tw-prose-hr: var(--border-default);
|
--tw-prose-hr: var(--border-default);
|
||||||
--tw-prose-quotes: var(--text-secondary);
|
--tw-prose-quotes: var(--text-secondary);
|
||||||
--tw-prose-quote-borders: var(--border-default);
|
--tw-prose-quote-borders: var(--border-default);
|
||||||
--tw-prose-captions: var(--text-muted);
|
--tw-prose-captions: var(--text-muted);
|
||||||
--tw-prose-kbd: var(--text-primary);
|
--tw-prose-kbd: var(--text-primary);
|
||||||
--tw-prose-kbd-shadows: oklch(0.97 0 0 / 10%);
|
--tw-prose-kbd-shadows: oklch(0.97 0 0 / 10%);
|
||||||
--tw-prose-code: var(--accent);
|
--tw-prose-code: var(--accent);
|
||||||
--tw-prose-pre-code: var(--text-primary);
|
--tw-prose-pre-code: var(--text-primary);
|
||||||
--tw-prose-pre-bg: var(--surface-elevated);
|
--tw-prose-pre-bg: var(--surface-elevated);
|
||||||
--tw-prose-th-borders: var(--border-default);
|
--tw-prose-th-borders: var(--border-default);
|
||||||
--tw-prose-td-borders: var(--border-subtle);
|
--tw-prose-td-borders: var(--border-subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-scrollbar {
|
.app-scrollbar {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: color-mix(in oklch, var(--text-muted) 34%, transparent) transparent;
|
scrollbar-color: color-mix(in oklch, var(--text-muted) 34%, transparent) transparent;
|
||||||
scrollbar-gutter: stable;
|
scrollbar-gutter: stable;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-scrollbar::-webkit-scrollbar {
|
.app-scrollbar::-webkit-scrollbar {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-scrollbar::-webkit-scrollbar-track {
|
.app-scrollbar::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-scrollbar::-webkit-scrollbar-thumb {
|
.app-scrollbar::-webkit-scrollbar-thumb {
|
||||||
background: color-mix(in oklch, var(--text-muted) 28%, transparent);
|
background: color-mix(in oklch, var(--text-muted) 28%, transparent);
|
||||||
border: 3px solid transparent;
|
border: 3px solid transparent;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-scrollbar:hover::-webkit-scrollbar-thumb {
|
.app-scrollbar:hover::-webkit-scrollbar-thumb {
|
||||||
background: color-mix(in oklch, var(--text-secondary) 42%, transparent);
|
background: color-mix(in oklch, var(--text-secondary) 42%, transparent);
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-scrollbar[data-scrollbar="room"]::-webkit-scrollbar {
|
.app-scrollbar[data-scrollbar="room"]::-webkit-scrollbar {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre_nobackground {
|
||||||
|
pre {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
28
src/main.tsx
28
src/main.tsx
@ -1,40 +1,40 @@
|
|||||||
import {StrictMode} from "react"
|
import {StrictMode} from "react"
|
||||||
import {createRoot} from "react-dom/client"
|
import {createRoot} from "react-dom/client"
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
import {QueryClient, QueryClientProvider} from "@tanstack/react-query"
|
||||||
|
import "@/fonts.css"
|
||||||
import "@/index.css"
|
import "@/index.css"
|
||||||
import {ThemeProvider} from "@/components/theme-provider.tsx"
|
import {ThemeProvider} from "@/components/theme-provider.tsx"
|
||||||
import { performMaintenance } from "@/lib/db/maintenance";
|
import {Toaster} from "@/components/ui/sonner"
|
||||||
import { applyThemePreset } from "@/components/theme/ThemePresetSelector";
|
import {performMaintenance} from "@/lib/db/maintenance";
|
||||||
|
import {applyThemePreset} from "@/components/theme/ThemePresetSelector";
|
||||||
import App from "@/App.tsx";
|
import App from "@/App.tsx";
|
||||||
import { initRum } from "@/rum-core";
|
import {initRum} from "@/rum-core";
|
||||||
import { RumUserContext } from "@/rum";
|
import {RumUserContext} from "@/rum";
|
||||||
|
|
||||||
initRum();
|
initRum();
|
||||||
|
|
||||||
// Apply saved theme preset on startup
|
|
||||||
const PRESET_KEY = "app-theme-preset";
|
const PRESET_KEY = "app-theme-preset";
|
||||||
const savedPreset = localStorage.getItem(PRESET_KEY) || "soft-mono";
|
const savedPreset = localStorage.getItem(PRESET_KEY) || "soft-mono";
|
||||||
applyThemePreset(savedPreset);
|
applyThemePreset(savedPreset);
|
||||||
|
|
||||||
// Trigger background maintenance on startup
|
|
||||||
performMaintenance().catch(console.error);
|
performMaintenance().catch(console.error);
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
staleTime: 5 * 60 * 1000,
|
||||||
retry: 1,
|
retry: 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<RumUserContext />
|
<RumUserContext/>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<App/>
|
<App/>
|
||||||
|
<Toaster/>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user