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_MAX_RETRIES=3
|
||||
# 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",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"sha1 0.11.0",
|
||||
"sha2 0.11.0",
|
||||
"ssh-key",
|
||||
@ -9036,6 +9037,7 @@ dependencies = [
|
||||
"sea-orm",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"session",
|
||||
"sha1 0.11.0",
|
||||
"sha2 0.11.0",
|
||||
|
||||
@ -133,7 +133,7 @@ fs2 = "0.4.3"
|
||||
image = "0.25.10"
|
||||
tokio = "1.50.0"
|
||||
tokio-util = "0.7.18"
|
||||
tokio-stream = "0.1.18"
|
||||
tokio-stream = { version = "0.1.18", features = ["sync"] }
|
||||
url = "2.5.8"
|
||||
tower = "0.5"
|
||||
num_cpus = "1.17.0"
|
||||
|
||||
433
openapi.json
433
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": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@ -24956,7 +25108,9 @@
|
||||
"required": [
|
||||
"uid",
|
||||
"username",
|
||||
"has_unread_notifications"
|
||||
"has_unread_notifications",
|
||||
"language",
|
||||
"timezone"
|
||||
],
|
||||
"properties": {
|
||||
"uid": {
|
||||
@ -24982,6 +25136,12 @@
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"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": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -34908,7 +35198,9 @@
|
||||
"required": [
|
||||
"uid",
|
||||
"username",
|
||||
"has_unread_notifications"
|
||||
"has_unread_notifications",
|
||||
"language",
|
||||
"timezone"
|
||||
],
|
||||
"properties": {
|
||||
"uid": {
|
||||
@ -34934,6 +35226,12 @@
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"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": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -43614,7 +44031,7 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"uid",
|
||||
"project_uid",
|
||||
"user_uid",
|
||||
"amount",
|
||||
"currency",
|
||||
"reason",
|
||||
@ -43626,16 +44043,16 @@
|
||||
"format": "uuid"
|
||||
},
|
||||
"project_uid": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"user_uid": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "uuid"
|
||||
},
|
||||
"user_uid": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"amount": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
@ -44410,4 +44827,4 @@
|
||||
"description": "AI conversation and messaging"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,9 +157,59 @@ export async function shareConversation(conversationId: string): Promise<{ share
|
||||
}
|
||||
|
||||
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
|
||||
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> {
|
||||
@ -197,8 +247,12 @@ export async function* streamChat(conversationId: string, messageId: string): As
|
||||
const parsed = JSON.parse(jsonStr);
|
||||
// Normalize backend SSE format: {event: "token", data: "..."} → {type: "token", data: "..."}
|
||||
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") {
|
||||
yield { type: eventType, data: parsed.data };
|
||||
yield { type: eventType, data: parsed.data, children_id: parsed.children_id };
|
||||
}
|
||||
} catch {
|
||||
// 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);
|
||||
|
||||
// Export all API functions
|
||||
|
||||
export const {
|
||||
// Auth
|
||||
api2faDisable,
|
||||
@ -67,6 +68,9 @@ export const {
|
||||
projectBilling,
|
||||
projectBillingHistory,
|
||||
projectBillingErrors,
|
||||
projectMessageFavorites,
|
||||
projectMessageFavoriteAdd,
|
||||
projectMessageFavoriteRemove,
|
||||
projectCreateLabel,
|
||||
projectUpdateLabel,
|
||||
projectDeleteLabel,
|
||||
@ -163,6 +167,8 @@ export const {
|
||||
modelList,
|
||||
categoryList,
|
||||
categoryCreate,
|
||||
categoryDelete,
|
||||
categoryUpdate,
|
||||
participantList,
|
||||
pinAdd,
|
||||
pinRemove,
|
||||
@ -248,6 +254,7 @@ export const {
|
||||
aiMessageCreate,
|
||||
aiMessageGet,
|
||||
aiMessageChildren,
|
||||
// @ts-ignore
|
||||
aiMessageFork,
|
||||
aiMessageResend,
|
||||
aiMessageStop,
|
||||
@ -300,6 +307,7 @@ export const {
|
||||
|
||||
// Search
|
||||
search,
|
||||
searchMessages,
|
||||
} = 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";
|
||||
|
||||
body {
|
||||
font-family: Inter,
|
||||
system-ui,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
@ -129,192 +135,210 @@
|
||||
───────────────────────────────────────────── */
|
||||
:root {
|
||||
/* Surfaces (layer hierarchy, 3 layers only) */
|
||||
--surface-rail: oklch(0.98 0 0);
|
||||
--surface-sidebar: oklch(0.97 0 0);
|
||||
--surface-ground: oklch(1 0 0);
|
||||
--surface-rail: oklch(0.98 0 0);
|
||||
--surface-sidebar: oklch(0.97 0 0);
|
||||
--surface-ground: 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 */
|
||||
--border-subtle: oklch(0.90 0 0 / 40%);
|
||||
--border-default: oklch(0.88 0 0);
|
||||
--border-strong: oklch(0.80 0 0);
|
||||
--border-subtle: oklch(0.90 0 0 / 40%);
|
||||
--border-default: oklch(0.88 0 0);
|
||||
--border-strong: oklch(0.80 0 0);
|
||||
|
||||
/* Text */
|
||||
--text-primary: oklch(0.13 0 0);
|
||||
--text-secondary: oklch(0.40 0 0);
|
||||
--text-muted: oklch(0.55 0 0);
|
||||
--text-inverse: oklch(0.985 0 0);
|
||||
--text-tertiary: oklch(0.70 0 0);
|
||||
--text-primary: oklch(0.13 0 0);
|
||||
--text-secondary: oklch(0.40 0 0);
|
||||
--text-muted: oklch(0.55 0 0);
|
||||
--text-inverse: oklch(0.985 0 0);
|
||||
--text-tertiary: oklch(0.70 0 0);
|
||||
|
||||
/* Brand accent — Discord blurple as reference */
|
||||
--accent: oklch(0.25 0 0);
|
||||
--accent-hover: oklch(0.15 0 0);
|
||||
--accent-fg: oklch(0.985 0 0);
|
||||
--accent-muted: oklch(0.25 0 0 / 15%);
|
||||
--accent-bg: oklch(0.25 0 0 / 8%);
|
||||
--accent-rgb: 88, 101, 242;
|
||||
--accent: oklch(0.25 0 0);
|
||||
--accent-hover: oklch(0.15 0 0);
|
||||
--accent-fg: oklch(0.985 0 0);
|
||||
--accent-muted: oklch(0.25 0 0 / 15%);
|
||||
--accent-bg: oklch(0.25 0 0 / 8%);
|
||||
--accent-rgb: 88, 101, 242;
|
||||
|
||||
/* Status */
|
||||
--status-online: oklch(0.55 0 0);
|
||||
--status-idle: oklch(0.60 0 0);
|
||||
--status-dnd: oklch(0.50 0 0);
|
||||
--status-offline: oklch(0.60 0 0);
|
||||
--status-online: oklch(0.55 0 0);
|
||||
--status-idle: oklch(0.60 0 0);
|
||||
--status-dnd: oklch(0.50 0 0);
|
||||
--status-offline: oklch(0.60 0 0);
|
||||
|
||||
/* Semantic */
|
||||
--success: oklch(0.50 0 0);
|
||||
--success-alpha10: oklch(0.50 0 0 / 10%);
|
||||
--warning: oklch(0.60 0 0);
|
||||
--warning-alpha10: oklch(0.60 0 0 / 10%);
|
||||
--destructive: oklch(0.45 0 0);
|
||||
--success: oklch(0.50 0 0);
|
||||
--success-alpha10: oklch(0.50 0 0 / 10%);
|
||||
--warning: oklch(0.60 0 0);
|
||||
--warning-alpha10: oklch(0.60 0 0 / 10%);
|
||||
--destructive: oklch(0.45 0 0);
|
||||
--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-red: oklch(0.50 0 0);
|
||||
--role-orange: oklch(0.55 0 0);
|
||||
--role-yellow: oklch(0.60 0 0);
|
||||
--role-green: oklch(0.50 0 0);
|
||||
--role-blue: oklch(0.55 0 0);
|
||||
--role-purple: oklch(0.55 0 0);
|
||||
--role-pink: oklch(0.55 0 0);
|
||||
--role-gray: oklch(0.50 0 0);
|
||||
--role-red: oklch(0.50 0 0);
|
||||
--role-orange: oklch(0.55 0 0);
|
||||
--role-yellow: oklch(0.60 0 0);
|
||||
--role-green: oklch(0.50 0 0);
|
||||
--role-blue: oklch(0.55 0 0);
|
||||
--role-purple: oklch(0.55 0 0);
|
||||
--role-pink: oklch(0.55 0 0);
|
||||
--role-gray: oklch(0.50 0 0);
|
||||
|
||||
/* Interactive */
|
||||
--interactive: oklch(0.97 0 0);
|
||||
--interactive: oklch(0.97 0 0);
|
||||
--interactive-hover: oklch(0.92 0 0);
|
||||
--interactive-active: oklch(0.88 0 0);
|
||||
|
||||
/* Hover */
|
||||
--hover-bg: oklch(0.95 0 0 / 70%);
|
||||
--hover-bg-strong: oklch(0.90 0 0);
|
||||
--hover-bg: oklch(0.95 0 0 / 70%);
|
||||
--hover-bg-strong: oklch(0.90 0 0);
|
||||
|
||||
/* Input */
|
||||
--input-bg: oklch(0.98 0 0);
|
||||
--input-bg: oklch(0.98 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-0: oklch(0.93 0 0);
|
||||
--heatmap-1: oklch(0.75 0.10 155);
|
||||
--heatmap-2: oklch(0.62 0.15 155);
|
||||
--heatmap-3: oklch(0.50 0.15 155);
|
||||
--heatmap-4: oklch(0.38 0.15 155);
|
||||
--heatmap-0: oklch(0.93 0 0);
|
||||
--heatmap-1: oklch(0.75 0.10 155);
|
||||
--heatmap-2: oklch(0.62 0.15 155);
|
||||
--heatmap-3: oklch(0.50 0.15 155);
|
||||
--heatmap-4: oklch(0.38 0.15 155);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--surface-rail: oklch(0.12 0 0);
|
||||
--surface-sidebar: oklch(0.15 0 0);
|
||||
--surface-ground: oklch(0.13 0 0);
|
||||
--surface-rail: oklch(0.12 0 0);
|
||||
--surface-sidebar: oklch(0.15 0 0);
|
||||
--surface-ground: oklch(0.13 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-default: oklch(0.35 0 0);
|
||||
--border-strong: oklch(0.50 0 0);
|
||||
--border-subtle: oklch(0.30 0 0 / 30%);
|
||||
--border-default: oklch(0.35 0 0);
|
||||
--border-strong: oklch(0.50 0 0);
|
||||
|
||||
--text-primary: oklch(0.97 0 0);
|
||||
--text-secondary: oklch(0.80 0 0);
|
||||
--text-muted: oklch(0.65 0 0);
|
||||
--text-inverse: oklch(0.13 0 0);
|
||||
--text-tertiary: oklch(0.55 0 0);
|
||||
--text-primary: oklch(0.97 0 0);
|
||||
--text-secondary: oklch(0.80 0 0);
|
||||
--text-muted: oklch(0.65 0 0);
|
||||
--text-inverse: oklch(0.13 0 0);
|
||||
--text-tertiary: oklch(0.55 0 0);
|
||||
|
||||
--accent: oklch(0.70 0.15 264);
|
||||
--accent-hover: oklch(0.78 0.15 264);
|
||||
--accent-fg: oklch(0.10 0 0);
|
||||
--accent-muted: oklch(0.70 0.15 264 / 20%);
|
||||
--accent-bg: oklch(0.70 0.15 264 / 12%);
|
||||
--accent-rgb: 88, 101, 242;
|
||||
--accent: oklch(0.70 0.15 264);
|
||||
--accent-hover: oklch(0.78 0.15 264);
|
||||
--accent-fg: oklch(0.10 0 0);
|
||||
--accent-muted: oklch(0.70 0.15 264 / 20%);
|
||||
--accent-bg: oklch(0.70 0.15 264 / 12%);
|
||||
--accent-rgb: 88, 101, 242;
|
||||
|
||||
--status-online: oklch(0.72 0.17 155);
|
||||
--status-idle: oklch(0.78 0.15 80);
|
||||
--status-dnd: oklch(0.65 0.20 25);
|
||||
--status-offline: oklch(0.55 0 0);
|
||||
--status-online: oklch(0.72 0.17 155);
|
||||
--status-idle: oklch(0.78 0.15 80);
|
||||
--status-dnd: oklch(0.65 0.20 25);
|
||||
--status-offline: oklch(0.55 0 0);
|
||||
|
||||
--success: oklch(0.72 0.17 155);
|
||||
--success-alpha10: oklch(0.72 0.17 155 / 10%);
|
||||
--warning: oklch(0.78 0.15 90);
|
||||
--warning-alpha10: oklch(0.78 0.15 90 / 10%);
|
||||
--destructive: oklch(0.70 0.20 25);
|
||||
--success: oklch(0.72 0.17 155);
|
||||
--success-alpha10: oklch(0.72 0.17 155 / 10%);
|
||||
--warning: oklch(0.78 0.15 90);
|
||||
--warning-alpha10: oklch(0.78 0.15 90 / 10%);
|
||||
--destructive: oklch(0.70 0.20 25);
|
||||
--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-orange: oklch(0.72 0.18 50);
|
||||
--role-yellow: oklch(0.78 0.16 85);
|
||||
--role-green: oklch(0.70 0.17 155);
|
||||
--role-blue: oklch(0.70 0.20 250);
|
||||
--role-purple: oklch(0.65 0.20 290);
|
||||
--role-pink: oklch(0.65 0.20 340);
|
||||
--role-gray: oklch(0.58 0 0);
|
||||
--role-red: oklch(0.65 0.20 20);
|
||||
--role-orange: oklch(0.72 0.18 50);
|
||||
--role-yellow: oklch(0.78 0.16 85);
|
||||
--role-green: oklch(0.70 0.17 155);
|
||||
--role-blue: oklch(0.70 0.20 250);
|
||||
--role-purple: oklch(0.65 0.20 290);
|
||||
--role-pink: oklch(0.65 0.20 340);
|
||||
--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-active: oklch(0.32 0 0);
|
||||
|
||||
--hover-bg: oklch(0.22 0 0 / 60%);
|
||||
--hover-bg-strong: oklch(0.28 0 0);
|
||||
--hover-bg: oklch(0.22 0 0 / 60%);
|
||||
--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-ring: oklch(0.70 0.15 264);
|
||||
--input-ring: oklch(0.70 0.15 264);
|
||||
|
||||
/* Heatmap (contribution graph) */
|
||||
--heatmap-0: oklch(0.20 0 0);
|
||||
--heatmap-1: oklch(0.30 0.08 155);
|
||||
--heatmap-2: oklch(0.45 0.12 155);
|
||||
--heatmap-3: oklch(0.60 0.15 155);
|
||||
--heatmap-4: oklch(0.75 0.15 155);
|
||||
--heatmap-0: oklch(0.20 0 0);
|
||||
--heatmap-1: oklch(0.30 0.08 155);
|
||||
--heatmap-2: oklch(0.45 0.12 155);
|
||||
--heatmap-3: oklch(0.60 0.15 155);
|
||||
--heatmap-4: oklch(0.75 0.15 155);
|
||||
}
|
||||
|
||||
@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-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"] {
|
||||
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 {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes settings-modal-close {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[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"] {
|
||||
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 {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes settings-overlay-close {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ─────────────────────────────────────────────
|
||||
@ -323,75 +347,81 @@
|
||||
dark mode text never falls back to defaults
|
||||
───────────────────────────────────────────── */
|
||||
:root .prose {
|
||||
--tw-prose-body: var(--text-primary);
|
||||
--tw-prose-headings: var(--text-primary);
|
||||
--tw-prose-lead: var(--text-secondary);
|
||||
--tw-prose-links: var(--accent);
|
||||
--tw-prose-bold: var(--text-primary);
|
||||
--tw-prose-counters: var(--text-secondary);
|
||||
--tw-prose-bullets: var(--text-muted);
|
||||
--tw-prose-hr: var(--border-default);
|
||||
--tw-prose-quotes: var(--text-secondary);
|
||||
--tw-prose-quote-borders: var(--border-default);
|
||||
--tw-prose-captions: var(--text-muted);
|
||||
--tw-prose-kbd: var(--text-primary);
|
||||
--tw-prose-kbd-shadows: oklch(0.13 0 0 / 10%);
|
||||
--tw-prose-code: var(--accent);
|
||||
--tw-prose-pre-code: var(--text-primary);
|
||||
--tw-prose-pre-bg: var(--surface-elevated);
|
||||
--tw-prose-th-borders: var(--border-default);
|
||||
--tw-prose-td-borders: var(--border-subtle);
|
||||
--tw-prose-body: var(--text-primary);
|
||||
--tw-prose-headings: var(--text-primary);
|
||||
--tw-prose-lead: var(--text-secondary);
|
||||
--tw-prose-links: var(--accent);
|
||||
--tw-prose-bold: var(--text-primary);
|
||||
--tw-prose-counters: var(--text-secondary);
|
||||
--tw-prose-bullets: var(--text-muted);
|
||||
--tw-prose-hr: var(--border-default);
|
||||
--tw-prose-quotes: var(--text-secondary);
|
||||
--tw-prose-quote-borders: var(--border-default);
|
||||
--tw-prose-captions: var(--text-muted);
|
||||
--tw-prose-kbd: var(--text-primary);
|
||||
--tw-prose-kbd-shadows: oklch(0.13 0 0 / 10%);
|
||||
--tw-prose-code: var(--accent);
|
||||
--tw-prose-pre-code: var(--text-primary);
|
||||
--tw-prose-pre-bg: var(--surface-elevated);
|
||||
--tw-prose-th-borders: var(--border-default);
|
||||
--tw-prose-td-borders: var(--border-subtle);
|
||||
}
|
||||
|
||||
.dark .prose {
|
||||
--tw-prose-body: var(--text-primary);
|
||||
--tw-prose-headings: var(--text-primary);
|
||||
--tw-prose-lead: var(--text-secondary);
|
||||
--tw-prose-links: var(--accent);
|
||||
--tw-prose-bold: var(--text-primary);
|
||||
--tw-prose-counters: var(--text-secondary);
|
||||
--tw-prose-bullets: var(--text-muted);
|
||||
--tw-prose-hr: var(--border-default);
|
||||
--tw-prose-quotes: var(--text-secondary);
|
||||
--tw-prose-quote-borders: var(--border-default);
|
||||
--tw-prose-captions: var(--text-muted);
|
||||
--tw-prose-kbd: var(--text-primary);
|
||||
--tw-prose-kbd-shadows: oklch(0.97 0 0 / 10%);
|
||||
--tw-prose-code: var(--accent);
|
||||
--tw-prose-pre-code: var(--text-primary);
|
||||
--tw-prose-pre-bg: var(--surface-elevated);
|
||||
--tw-prose-th-borders: var(--border-default);
|
||||
--tw-prose-td-borders: var(--border-subtle);
|
||||
--tw-prose-body: var(--text-primary);
|
||||
--tw-prose-headings: var(--text-primary);
|
||||
--tw-prose-lead: var(--text-secondary);
|
||||
--tw-prose-links: var(--accent);
|
||||
--tw-prose-bold: var(--text-primary);
|
||||
--tw-prose-counters: var(--text-secondary);
|
||||
--tw-prose-bullets: var(--text-muted);
|
||||
--tw-prose-hr: var(--border-default);
|
||||
--tw-prose-quotes: var(--text-secondary);
|
||||
--tw-prose-quote-borders: var(--border-default);
|
||||
--tw-prose-captions: var(--text-muted);
|
||||
--tw-prose-kbd: var(--text-primary);
|
||||
--tw-prose-kbd-shadows: oklch(0.97 0 0 / 10%);
|
||||
--tw-prose-code: var(--accent);
|
||||
--tw-prose-pre-code: var(--text-primary);
|
||||
--tw-prose-pre-bg: var(--surface-elevated);
|
||||
--tw-prose-th-borders: var(--border-default);
|
||||
--tw-prose-td-borders: var(--border-subtle);
|
||||
}
|
||||
|
||||
.app-scrollbar {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: color-mix(in oklch, var(--text-muted) 34%, transparent) transparent;
|
||||
scrollbar-gutter: stable;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: color-mix(in oklch, var(--text-muted) 34%, transparent) transparent;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.app-scrollbar::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.app-scrollbar::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.app-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: color-mix(in oklch, var(--text-muted) 28%, transparent);
|
||||
border: 3px solid transparent;
|
||||
border-radius: 999px;
|
||||
background-clip: padding-box;
|
||||
background: color-mix(in oklch, var(--text-muted) 28%, transparent);
|
||||
border: 3px solid transparent;
|
||||
border-radius: 999px;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.app-scrollbar:hover::-webkit-scrollbar-thumb {
|
||||
background: color-mix(in oklch, var(--text-secondary) 42%, transparent);
|
||||
border: 2px solid transparent;
|
||||
background-clip: padding-box;
|
||||
background: color-mix(in oklch, var(--text-secondary) 42%, transparent);
|
||||
border: 2px solid transparent;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.app-scrollbar[data-scrollbar="room"]::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.pre_nobackground {
|
||||
pre {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
30
src/main.tsx
30
src/main.tsx
@ -1,41 +1,41 @@
|
||||
import {StrictMode} from "react"
|
||||
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 {ThemeProvider} from "@/components/theme-provider.tsx"
|
||||
import { performMaintenance } from "@/lib/db/maintenance";
|
||||
import { applyThemePreset } from "@/components/theme/ThemePresetSelector";
|
||||
import {Toaster} from "@/components/ui/sonner"
|
||||
import {performMaintenance} from "@/lib/db/maintenance";
|
||||
import {applyThemePreset} from "@/components/theme/ThemePresetSelector";
|
||||
import App from "@/App.tsx";
|
||||
import { initRum } from "@/rum-core";
|
||||
import { RumUserContext } from "@/rum";
|
||||
import {initRum} from "@/rum-core";
|
||||
import {RumUserContext} from "@/rum";
|
||||
|
||||
initRum();
|
||||
|
||||
// Apply saved theme preset on startup
|
||||
const PRESET_KEY = "app-theme-preset";
|
||||
const savedPreset = localStorage.getItem(PRESET_KEY) || "soft-mono";
|
||||
applyThemePreset(savedPreset);
|
||||
|
||||
// Trigger background maintenance on startup
|
||||
performMaintenance().catch(console.error);
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
retry: 1,
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 5 * 60 * 1000,
|
||||
retry: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RumUserContext />
|
||||
<RumUserContext/>
|
||||
<ThemeProvider>
|
||||
<App/>
|
||||
<Toaster/>
|
||||
</ThemeProvider>
|
||||
</QueryClientProvider>
|
||||
</StrictMode>
|
||||
)
|
||||
)
|
||||
@ -15,6 +15,7 @@
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"resolveJsonModule": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user