illusion-code 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- illusion/__init__.py +24 -0
- illusion/__main__.py +15 -0
- illusion/_frontend/dist/index.mjs +39208 -0
- illusion/_frontend/package.json +27 -0
- illusion/_frontend/src/App.tsx +624 -0
- illusion/_frontend/src/components/CommandPicker.tsx +98 -0
- illusion/_frontend/src/components/Composer.tsx +55 -0
- illusion/_frontend/src/components/ComposerController.tsx +128 -0
- illusion/_frontend/src/components/ConversationView.tsx +750 -0
- illusion/_frontend/src/components/Footer.tsx +25 -0
- illusion/_frontend/src/components/MarkdownContent.tsx +537 -0
- illusion/_frontend/src/components/MarkdownTable.tsx +245 -0
- illusion/_frontend/src/components/ModalHost.tsx +425 -0
- illusion/_frontend/src/components/MultilineTextInput.tsx +250 -0
- illusion/_frontend/src/components/PromptInput.tsx +64 -0
- illusion/_frontend/src/components/SelectModal.tsx +78 -0
- illusion/_frontend/src/components/SidePanel.tsx +175 -0
- illusion/_frontend/src/components/Spinner.tsx +77 -0
- illusion/_frontend/src/components/StatusBar.tsx +142 -0
- illusion/_frontend/src/components/SwarmPanel.tsx +141 -0
- illusion/_frontend/src/components/TodoPanel.tsx +126 -0
- illusion/_frontend/src/components/ToolCallDisplay.tsx +202 -0
- illusion/_frontend/src/components/TranscriptPane.tsx +79 -0
- illusion/_frontend/src/components/WelcomeBanner.tsx +37 -0
- illusion/_frontend/src/hooks/useBackendSession.ts +468 -0
- illusion/_frontend/src/hooks/useTerminalSize.ts +9 -0
- illusion/_frontend/src/i18n.ts +78 -0
- illusion/_frontend/src/index.tsx +42 -0
- illusion/_frontend/src/theme/ThemeContext.tsx +19 -0
- illusion/_frontend/src/theme/builtinThemes.ts +89 -0
- illusion/_frontend/src/types.ts +110 -0
- illusion/_frontend/src/utils/markdown.ts +33 -0
- illusion/_frontend/src/utils/thinking.ts +191 -0
- illusion/_frontend/tsconfig.json +13 -0
- illusion/_web_dist/assets/index-BseIw-ik.css +10 -0
- illusion/_web_dist/assets/index-C_0ZWMuW.js +82 -0
- illusion/_web_dist/index.html +16 -0
- illusion/api/__init__.py +36 -0
- illusion/api/client.py +568 -0
- illusion/api/codex_client.py +563 -0
- illusion/api/compat.py +138 -0
- illusion/api/effort.py +128 -0
- illusion/api/errors.py +57 -0
- illusion/api/openai_client.py +819 -0
- illusion/api/provider.py +148 -0
- illusion/api/registry.py +479 -0
- illusion/api/usage.py +45 -0
- illusion/auth/__init__.py +50 -0
- illusion/auth/copilot.py +419 -0
- illusion/auth/external.py +612 -0
- illusion/auth/flows.py +58 -0
- illusion/auth/manager.py +214 -0
- illusion/auth/storage.py +372 -0
- illusion/bridge/__init__.py +38 -0
- illusion/bridge/manager.py +190 -0
- illusion/bridge/session_runner.py +84 -0
- illusion/bridge/types.py +113 -0
- illusion/bridge/work_secret.py +131 -0
- illusion/cli.py +1228 -0
- illusion/commands/__init__.py +32 -0
- illusion/commands/registry.py +1934 -0
- illusion/config/__init__.py +39 -0
- illusion/config/i18n.py +522 -0
- illusion/config/paths.py +259 -0
- illusion/config/settings.py +564 -0
- illusion/coordinator/__init__.py +41 -0
- illusion/coordinator/agent_definitions.py +1093 -0
- illusion/coordinator/coordinator_mode.py +127 -0
- illusion/engine/__init__.py +95 -0
- illusion/engine/cost_tracker.py +55 -0
- illusion/engine/messages.py +369 -0
- illusion/engine/query.py +632 -0
- illusion/engine/query_engine.py +343 -0
- illusion/engine/stream_events.py +169 -0
- illusion/hooks/__init__.py +67 -0
- illusion/hooks/events.py +43 -0
- illusion/hooks/executor.py +397 -0
- illusion/hooks/hot_reload.py +74 -0
- illusion/hooks/loader.py +133 -0
- illusion/hooks/schemas.py +121 -0
- illusion/hooks/types.py +86 -0
- illusion/mcp/__init__.py +104 -0
- illusion/mcp/client.py +377 -0
- illusion/mcp/config.py +140 -0
- illusion/mcp/types.py +175 -0
- illusion/memory/__init__.py +36 -0
- illusion/memory/manager.py +94 -0
- illusion/memory/memdir.py +58 -0
- illusion/memory/paths.py +57 -0
- illusion/memory/scan.py +120 -0
- illusion/memory/search.py +83 -0
- illusion/memory/types.py +43 -0
- illusion/output_styles/__init__.py +15 -0
- illusion/output_styles/loader.py +64 -0
- illusion/permissions/__init__.py +39 -0
- illusion/permissions/checker.py +174 -0
- illusion/permissions/modes.py +38 -0
- illusion/platforms.py +148 -0
- illusion/plugins/__init__.py +71 -0
- illusion/plugins/bundled/__init__.py +0 -0
- illusion/plugins/installer.py +59 -0
- illusion/plugins/loader.py +301 -0
- illusion/plugins/schemas.py +51 -0
- illusion/plugins/types.py +56 -0
- illusion/prompts/__init__.py +29 -0
- illusion/prompts/claudemd.py +74 -0
- illusion/prompts/context.py +187 -0
- illusion/prompts/environment.py +189 -0
- illusion/prompts/system_prompt.py +155 -0
- illusion/py.typed +0 -0
- illusion/sandbox/__init__.py +29 -0
- illusion/sandbox/adapter.py +174 -0
- illusion/services/__init__.py +59 -0
- illusion/services/compact/__init__.py +1015 -0
- illusion/services/cron.py +338 -0
- illusion/services/cron_scheduler.py +715 -0
- illusion/services/file_history.py +258 -0
- illusion/services/lsp/__init__.py +455 -0
- illusion/services/session_storage.py +237 -0
- illusion/services/token_estimation.py +72 -0
- illusion/skills/__init__.py +60 -0
- illusion/skills/bundled/__init__.py +110 -0
- illusion/skills/bundled/content/batch.md +86 -0
- illusion/skills/bundled/content/coding-guidelines.md +70 -0
- illusion/skills/bundled/content/debug.md +38 -0
- illusion/skills/bundled/content/loop.md +82 -0
- illusion/skills/bundled/content/remember.md +105 -0
- illusion/skills/bundled/content/simplify.md +53 -0
- illusion/skills/bundled/content/skillify.md +113 -0
- illusion/skills/bundled/content/stuck.md +54 -0
- illusion/skills/bundled/content/update-config.md +329 -0
- illusion/skills/bundled/content/verify.md +74 -0
- illusion/skills/loader.py +219 -0
- illusion/skills/registry.py +40 -0
- illusion/skills/types.py +24 -0
- illusion/state/__init__.py +18 -0
- illusion/state/app_state.py +67 -0
- illusion/state/store.py +93 -0
- illusion/swarm/__init__.py +71 -0
- illusion/swarm/agent_executor.py +857 -0
- illusion/swarm/in_process.py +259 -0
- illusion/swarm/subprocess_backend.py +136 -0
- illusion/swarm/team_helpers.py +123 -0
- illusion/swarm/types.py +159 -0
- illusion/swarm/worktree.py +347 -0
- illusion/tasks/__init__.py +33 -0
- illusion/tasks/local_agent_task.py +42 -0
- illusion/tasks/local_shell_task.py +27 -0
- illusion/tasks/manager.py +377 -0
- illusion/tasks/stop_task.py +21 -0
- illusion/tasks/types.py +88 -0
- illusion/tools/__init__.py +126 -0
- illusion/tools/agent_tool.py +388 -0
- illusion/tools/ask_user_question_tool.py +186 -0
- illusion/tools/base.py +149 -0
- illusion/tools/bash_tool.py +413 -0
- illusion/tools/config_tool.py +90 -0
- illusion/tools/cron_tool.py +473 -0
- illusion/tools/enter_plan_mode_tool.py +147 -0
- illusion/tools/enter_worktree_tool.py +188 -0
- illusion/tools/exit_plan_mode_tool.py +69 -0
- illusion/tools/exit_worktree_tool.py +225 -0
- illusion/tools/file_edit_tool.py +283 -0
- illusion/tools/file_read_tool.py +294 -0
- illusion/tools/file_write_tool.py +184 -0
- illusion/tools/glob_tool.py +165 -0
- illusion/tools/grep_tool.py +190 -0
- illusion/tools/list_mcp_resources_tool.py +80 -0
- illusion/tools/lsp_tool.py +333 -0
- illusion/tools/mcp_auth_tool.py +100 -0
- illusion/tools/mcp_tool.py +75 -0
- illusion/tools/notebook_edit_tool.py +242 -0
- illusion/tools/powershell_tool.py +334 -0
- illusion/tools/read_mcp_resource_tool.py +63 -0
- illusion/tools/repl_tool.py +100 -0
- illusion/tools/send_message_tool.py +112 -0
- illusion/tools/shell_common.py +187 -0
- illusion/tools/skill_tool.py +86 -0
- illusion/tools/sleep_tool.py +62 -0
- illusion/tools/structured_output_tool.py +58 -0
- illusion/tools/task_create_tool.py +98 -0
- illusion/tools/task_get_tool.py +94 -0
- illusion/tools/task_list_tool.py +94 -0
- illusion/tools/task_output_tool.py +55 -0
- illusion/tools/task_stop_tool.py +52 -0
- illusion/tools/task_update_tool.py +224 -0
- illusion/tools/team_create_tool.py +236 -0
- illusion/tools/team_delete_tool.py +104 -0
- illusion/tools/todo_write_tool.py +198 -0
- illusion/tools/tool_search_tool.py +156 -0
- illusion/tools/web_fetch_tool.py +264 -0
- illusion/tools/web_search_tool.py +186 -0
- illusion/ui/__init__.py +23 -0
- illusion/ui/app.py +258 -0
- illusion/ui/backend_host.py +1180 -0
- illusion/ui/input.py +86 -0
- illusion/ui/output.py +363 -0
- illusion/ui/permission_dialog.py +47 -0
- illusion/ui/permission_store.py +99 -0
- illusion/ui/protocol.py +384 -0
- illusion/ui/react_launcher.py +280 -0
- illusion/ui/runtime.py +787 -0
- illusion/ui/textual_app.py +603 -0
- illusion/ui/web/__init__.py +10 -0
- illusion/ui/web/server.py +87 -0
- illusion/ui/web/ws_host.py +1197 -0
- illusion/utils/__init__.py +0 -0
- illusion/utils/ripgrep.py +299 -0
- illusion/utils/shell.py +248 -0
- illusion_code-0.1.0.dist-info/METADATA +1159 -0
- illusion_code-0.1.0.dist-info/RECORD +214 -0
- illusion_code-0.1.0.dist-info/WHEEL +4 -0
- illusion_code-0.1.0.dist-info/entry_points.txt +2 -0
- illusion_code-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export type ThemeConfig = {
|
|
2
|
+
name: string;
|
|
3
|
+
colors: {
|
|
4
|
+
primary: string;
|
|
5
|
+
secondary: string;
|
|
6
|
+
accent: string;
|
|
7
|
+
foreground: string;
|
|
8
|
+
background: string;
|
|
9
|
+
muted: string;
|
|
10
|
+
success: string;
|
|
11
|
+
warning: string;
|
|
12
|
+
error: string;
|
|
13
|
+
info: string;
|
|
14
|
+
illusion: string;
|
|
15
|
+
illusionShimmer: string;
|
|
16
|
+
text: string;
|
|
17
|
+
subtle: string;
|
|
18
|
+
highlight: string;
|
|
19
|
+
promptBorder: string;
|
|
20
|
+
suggestion: string;
|
|
21
|
+
permission: string;
|
|
22
|
+
};
|
|
23
|
+
icons: {
|
|
24
|
+
spinner: string[];
|
|
25
|
+
tool: string;
|
|
26
|
+
assistant: string;
|
|
27
|
+
user: string;
|
|
28
|
+
system: string;
|
|
29
|
+
success: string;
|
|
30
|
+
error: string;
|
|
31
|
+
pending: string;
|
|
32
|
+
inProgress: string;
|
|
33
|
+
completed: string;
|
|
34
|
+
bullet: string;
|
|
35
|
+
arrow: string;
|
|
36
|
+
check: string;
|
|
37
|
+
cross: string;
|
|
38
|
+
chevron: string;
|
|
39
|
+
dot: string;
|
|
40
|
+
pointer: string;
|
|
41
|
+
middleDot: string;
|
|
42
|
+
resultPrefix: string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const defaultTheme: ThemeConfig = {
|
|
47
|
+
name: 'default',
|
|
48
|
+
colors: {
|
|
49
|
+
primary: '#56d4dd',
|
|
50
|
+
secondary: 'white',
|
|
51
|
+
accent: 'magenta',
|
|
52
|
+
foreground: 'white',
|
|
53
|
+
background: 'black',
|
|
54
|
+
muted: '#9ca3af',
|
|
55
|
+
success: 'green',
|
|
56
|
+
warning: 'yellow',
|
|
57
|
+
error: 'red',
|
|
58
|
+
info: '#89ddff',
|
|
59
|
+
illusion: '#f0c8b0',
|
|
60
|
+
illusionShimmer: '#e07070',
|
|
61
|
+
text: 'white',
|
|
62
|
+
subtle: '#a8b2c1',
|
|
63
|
+
highlight: '#56d4dd',
|
|
64
|
+
promptBorder: '#8b949e',
|
|
65
|
+
suggestion: '#89ddff',
|
|
66
|
+
permission: '#bb9af7',
|
|
67
|
+
},
|
|
68
|
+
icons: {
|
|
69
|
+
spinner: ['·', '◌', '◎', '◌'],
|
|
70
|
+
tool: '●',
|
|
71
|
+
assistant: '●',
|
|
72
|
+
user: '❯',
|
|
73
|
+
system: '✻',
|
|
74
|
+
success: '✓',
|
|
75
|
+
error: '✗',
|
|
76
|
+
pending: '○',
|
|
77
|
+
inProgress: '◐',
|
|
78
|
+
completed: '●',
|
|
79
|
+
bullet: '•',
|
|
80
|
+
arrow: '→',
|
|
81
|
+
check: '✓',
|
|
82
|
+
cross: '✗',
|
|
83
|
+
chevron: '›',
|
|
84
|
+
dot: '●',
|
|
85
|
+
pointer: '❯',
|
|
86
|
+
middleDot: '·',
|
|
87
|
+
resultPrefix: '⎿',
|
|
88
|
+
},
|
|
89
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export type PendingToolCall = {
|
|
2
|
+
tool_name: string;
|
|
3
|
+
tool_use_id: string;
|
|
4
|
+
tool_input?: Record<string, unknown>;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type FrontendConfig = {
|
|
8
|
+
backend_command: string[];
|
|
9
|
+
initial_prompt?: string | null;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type TranscriptItem = {
|
|
13
|
+
role: 'system' | 'user' | 'assistant' | 'assistant_streaming' | 'tool' | 'tool_result' | 'log';
|
|
14
|
+
text: string;
|
|
15
|
+
tool_name?: string;
|
|
16
|
+
tool_input?: Record<string, unknown>;
|
|
17
|
+
is_error?: boolean;
|
|
18
|
+
reasoning?: string;
|
|
19
|
+
tool_use_id?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type TaskSnapshot = {
|
|
23
|
+
id: string;
|
|
24
|
+
type: string;
|
|
25
|
+
status: string;
|
|
26
|
+
description: string;
|
|
27
|
+
metadata: Record<string, string>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type McpServerSnapshot = {
|
|
31
|
+
name: string;
|
|
32
|
+
state: string;
|
|
33
|
+
detail?: string;
|
|
34
|
+
transport?: string;
|
|
35
|
+
auth_configured?: boolean;
|
|
36
|
+
tool_count?: number;
|
|
37
|
+
resource_count?: number;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type BridgeSessionSnapshot = {
|
|
41
|
+
session_id: string;
|
|
42
|
+
command: string;
|
|
43
|
+
cwd: string;
|
|
44
|
+
pid: number;
|
|
45
|
+
status: string;
|
|
46
|
+
started_at: number;
|
|
47
|
+
output_path: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type SelectOptionPayload = {
|
|
51
|
+
value: string;
|
|
52
|
+
label: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
active?: boolean;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type SelectRequestPayload = {
|
|
58
|
+
title: string;
|
|
59
|
+
command: string;
|
|
60
|
+
options: SelectOptionPayload[];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type TodoItemSnapshot = {
|
|
64
|
+
content: string;
|
|
65
|
+
status: 'pending' | 'in_progress' | 'completed';
|
|
66
|
+
activeForm: string;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type SwarmTeammateSnapshot = {
|
|
70
|
+
name: string;
|
|
71
|
+
status: 'running' | 'idle' | 'done' | 'error';
|
|
72
|
+
duration?: number;
|
|
73
|
+
task?: string;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type SwarmNotificationSnapshot = {
|
|
77
|
+
from: string;
|
|
78
|
+
message: string;
|
|
79
|
+
timestamp: number;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export type BackendEvent = {
|
|
83
|
+
type: string;
|
|
84
|
+
message?: string | null;
|
|
85
|
+
item?: TranscriptItem | null;
|
|
86
|
+
state?: Record<string, unknown> | null;
|
|
87
|
+
tasks?: TaskSnapshot[] | null;
|
|
88
|
+
mcp_servers?: McpServerSnapshot[] | null;
|
|
89
|
+
bridge_sessions?: BridgeSessionSnapshot[] | null;
|
|
90
|
+
commands?: string[] | null;
|
|
91
|
+
modal?: Record<string, unknown> | null;
|
|
92
|
+
select_options?: SelectOptionPayload[] | null;
|
|
93
|
+
tool_name?: string | null;
|
|
94
|
+
tool_input?: Record<string, unknown> | null;
|
|
95
|
+
tool_use_id?: string | null;
|
|
96
|
+
output?: string | null;
|
|
97
|
+
is_error?: boolean | null;
|
|
98
|
+
// New event payloads
|
|
99
|
+
todo_items?: TodoItemSnapshot[] | null;
|
|
100
|
+
todo_markdown?: string | null;
|
|
101
|
+
plan_mode?: string | null;
|
|
102
|
+
swarm_teammates?: SwarmTeammateSnapshot[] | null;
|
|
103
|
+
swarm_notifications?: SwarmNotificationSnapshot[] | null;
|
|
104
|
+
reasoning?: string | null;
|
|
105
|
+
command_result_data?: {
|
|
106
|
+
message: string;
|
|
107
|
+
type: 'success' | 'error' | 'info';
|
|
108
|
+
} | null;
|
|
109
|
+
items?: TranscriptItem[] | null;
|
|
110
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import stripAnsi from 'strip-ansi';
|
|
2
|
+
import stringWidth from 'string-width';
|
|
3
|
+
import wrapAnsi from 'wrap-ansi';
|
|
4
|
+
|
|
5
|
+
export {stripAnsi, stringWidth, wrapAnsi};
|
|
6
|
+
|
|
7
|
+
export function padAligned(
|
|
8
|
+
content: string,
|
|
9
|
+
displayWidth: number,
|
|
10
|
+
targetWidth: number,
|
|
11
|
+
align: 'left' | 'center' | 'right' | null | undefined,
|
|
12
|
+
): string {
|
|
13
|
+
const padding = Math.max(0, targetWidth - displayWidth);
|
|
14
|
+
if (align === 'center') {
|
|
15
|
+
const leftPad = Math.floor(padding / 2);
|
|
16
|
+
return ' '.repeat(leftPad) + content + ' '.repeat(padding - leftPad);
|
|
17
|
+
}
|
|
18
|
+
if (align === 'right') {
|
|
19
|
+
return ' '.repeat(padding) + content;
|
|
20
|
+
}
|
|
21
|
+
return content + ' '.repeat(padding);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function wrapText(text: string, width: number, options?: {hard?: boolean}): string[] {
|
|
25
|
+
if (width <= 0) return [text];
|
|
26
|
+
const trimmedText = text.trimEnd();
|
|
27
|
+
const wrapped = wrapAnsi(trimmedText, width, {
|
|
28
|
+
hard: options?.hard ?? false,
|
|
29
|
+
trim: false,
|
|
30
|
+
});
|
|
31
|
+
const lines = wrapped.split('\n').filter((line) => line.length > 0);
|
|
32
|
+
return lines.length > 0 ? lines : [''];
|
|
33
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 思考过程处理工具
|
|
3
|
+
*
|
|
4
|
+
* 处理两种思考过程来源:
|
|
5
|
+
* 1. XML 标签包裹:`<think...</think` (DeepSeek、MiniMax 等模型)
|
|
6
|
+
* 2. 独立 reasoning 文本:通过后端 reasoning_content 字段传输(Kimi k2.5 等 OpenAI 兼容模型)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// 匹配完整的 <think...> 和 </think...> 标签
|
|
10
|
+
const THINK_OPEN_TAG = /<think\b[^>]*>/gi;
|
|
11
|
+
const THINK_CLOSE_TAG = /<\/think\b[^>]*>/gi;
|
|
12
|
+
// 匹配未闭合的 <think 开头标签(流式传输时标签可能被截断)
|
|
13
|
+
const THINK_OPEN_INCOMPLETE = /<th(?:i(?:n(?:k)?)?)?\s*$/i;
|
|
14
|
+
// 匹配完整的开闭标签对及其内容
|
|
15
|
+
const THINK_BLOCK_FULL = /<think\b[^>]*>[\s\S]*?<\/think\b[^>]*>/gi;
|
|
16
|
+
// DeepSeek 工具调用残留标记(例如 <|DSML|tool_calls)
|
|
17
|
+
const DSML_TOOL_CALL_PREFIX = /<\s*[||]\s*DSML\s*[||]\s*tool_calls[^\n>]*>?/gi;
|
|
18
|
+
// 兼容常见工具调用 XML 片段
|
|
19
|
+
const TOOL_CALL_XML_BLOCK = /<tool_call\b[^>]*>[\s\S]*?<\/tool_call\b[^>]*>/gi;
|
|
20
|
+
const TOOL_CALL_XML_TAG = /<\/?(?:tool_call|arg_key|arg_value)\b[^>]*>/gi;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 清除 `<think` 标签包裹的思考块(保留标签外的内容)
|
|
24
|
+
* 处理完整标签、嵌套标签和流式截断的不完整标签
|
|
25
|
+
*/
|
|
26
|
+
export function stripThinkTags(raw: string): string {
|
|
27
|
+
if (!raw) return '';
|
|
28
|
+
|
|
29
|
+
// 先移除完整的 <think...</think 块(包括内容)
|
|
30
|
+
let result = raw.replace(THINK_BLOCK_FULL, '');
|
|
31
|
+
|
|
32
|
+
// 移除孤立的闭合标签
|
|
33
|
+
result = result.replace(THINK_CLOSE_TAG, '');
|
|
34
|
+
|
|
35
|
+
// 移除孤立的开标签(可能是不完整的块)
|
|
36
|
+
result = result.replace(THINK_OPEN_TAG, '');
|
|
37
|
+
|
|
38
|
+
// 移除被截断的不完整开标签(如 "<th"、"<thi"、"<thin")
|
|
39
|
+
result = result.replace(THINK_OPEN_INCOMPLETE, '');
|
|
40
|
+
|
|
41
|
+
return result.trim();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 从包含 `<think` 标签的文本中提取思考过程内容
|
|
46
|
+
* 返回标签内的文本(去除标签本身)
|
|
47
|
+
*/
|
|
48
|
+
export function extractThinkContent(raw: string): string {
|
|
49
|
+
if (!raw) return '';
|
|
50
|
+
|
|
51
|
+
const parts: string[] = [];
|
|
52
|
+
let remaining = raw;
|
|
53
|
+
|
|
54
|
+
// 提取所有完整 <think...</think 块的内容
|
|
55
|
+
let match: RegExpExecArray | null;
|
|
56
|
+
THINK_BLOCK_FULL.lastIndex = 0;
|
|
57
|
+
let lastIndex = 0;
|
|
58
|
+
|
|
59
|
+
while ((match = THINK_BLOCK_FULL.exec(remaining)) !== null) {
|
|
60
|
+
// 标签前的内容中检查是否有孤立的开标签
|
|
61
|
+
const before = remaining.slice(lastIndex, match.index);
|
|
62
|
+
if (THINK_OPEN_TAG.test(before)) {
|
|
63
|
+
// 孤立开标签后的内容也是思考过程
|
|
64
|
+
THINK_OPEN_TAG.lastIndex = 0;
|
|
65
|
+
const openMatch = THINK_OPEN_TAG.exec(before);
|
|
66
|
+
if (openMatch) {
|
|
67
|
+
parts.push(before.slice(openMatch.index + openMatch[0].length));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 提取标签对内的内容
|
|
72
|
+
const inner = match[0].replace(THINK_OPEN_TAG, '').replace(THINK_CLOSE_TAG, '');
|
|
73
|
+
parts.push(inner.trim());
|
|
74
|
+
|
|
75
|
+
lastIndex = match.index + match[0].length;
|
|
76
|
+
THINK_OPEN_TAG.lastIndex = 0;
|
|
77
|
+
THINK_CLOSE_TAG.lastIndex = 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 检查剩余文本中是否有未闭合的开标签(流式截断场景)
|
|
81
|
+
const tail = remaining.slice(lastIndex);
|
|
82
|
+
THINK_OPEN_TAG.lastIndex = 0;
|
|
83
|
+
const openInTail = THINK_OPEN_TAG.exec(tail);
|
|
84
|
+
if (openInTail) {
|
|
85
|
+
parts.push(tail.slice(openInTail.index + openInTail[0].length).trim());
|
|
86
|
+
} else if (THINK_OPEN_INCOMPLETE.test(tail)) {
|
|
87
|
+
// 不完整标签,忽略
|
|
88
|
+
} else {
|
|
89
|
+
// 如果没有匹配到任何 think 块,检查是否有推理文本
|
|
90
|
+
// (不会走到这里如果没有 think 标签)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return parts.filter(Boolean).join('\n');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function mergeReasoning(a: string | undefined, b: string): string {
|
|
97
|
+
const parts: string[] = [];
|
|
98
|
+
if (a) appendUnique(parts, a);
|
|
99
|
+
if (b) appendUnique(parts, b);
|
|
100
|
+
return parts.join('\n').trim();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 判断文本是否包含 `<think` 标签
|
|
105
|
+
*/
|
|
106
|
+
export function hasThinkTags(raw: string): boolean {
|
|
107
|
+
if (!raw) return false;
|
|
108
|
+
return /<think\b/i.test(raw);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function stripToolCallArtifacts(raw: string): string {
|
|
112
|
+
if (!raw) return '';
|
|
113
|
+
return raw
|
|
114
|
+
.replace(DSML_TOOL_CALL_PREFIX, '')
|
|
115
|
+
.replace(TOOL_CALL_XML_BLOCK, '')
|
|
116
|
+
.replace(TOOL_CALL_XML_TAG, '');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function normalizeCompareText(raw: string): string {
|
|
120
|
+
return stripToolCallArtifacts(raw)
|
|
121
|
+
.replace(/\s+/g, ' ')
|
|
122
|
+
.trim();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function appendUnique(parts: string[], value: string): void {
|
|
126
|
+
const cleaned = stripToolCallArtifacts(value).trim();
|
|
127
|
+
if (!cleaned) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const candidateNorm = normalizeCompareText(cleaned);
|
|
132
|
+
if (!candidateNorm) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (const existing of parts) {
|
|
137
|
+
const existingNorm = normalizeCompareText(existing);
|
|
138
|
+
if (!existingNorm) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (existingNorm === candidateNorm || existingNorm.includes(candidateNorm)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (let i = parts.length - 1; i >= 0; i -= 1) {
|
|
147
|
+
const existingNorm = normalizeCompareText(parts[i]);
|
|
148
|
+
if (candidateNorm.includes(existingNorm)) {
|
|
149
|
+
parts.splice(i, 1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
parts.push(cleaned);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 渲染助手消息文本,根据 showThinking 决定是否显示思考过程
|
|
158
|
+
*
|
|
159
|
+
* @param raw 原始文本(可能包含 `<think` 标签)
|
|
160
|
+
* @param showThinking 是否显示思考过程
|
|
161
|
+
* @param reasoning 独立的推理文本(来自 reasoning_content 字段,无标签)
|
|
162
|
+
* @returns 显示给用户的文本
|
|
163
|
+
*/
|
|
164
|
+
export function renderAssistantText(
|
|
165
|
+
raw: string,
|
|
166
|
+
showThinking: boolean,
|
|
167
|
+
reasoning?: string,
|
|
168
|
+
): string {
|
|
169
|
+
if (!raw && !reasoning) return '';
|
|
170
|
+
|
|
171
|
+
const sanitizedRaw = stripToolCallArtifacts(raw);
|
|
172
|
+
const sanitizedReasoning = stripToolCallArtifacts(reasoning ?? '');
|
|
173
|
+
const hasTags = hasThinkTags(sanitizedRaw);
|
|
174
|
+
let cleanText = sanitizedRaw;
|
|
175
|
+
let thinkContent = '';
|
|
176
|
+
|
|
177
|
+
if (hasTags) {
|
|
178
|
+
thinkContent = stripToolCallArtifacts(extractThinkContent(sanitizedRaw));
|
|
179
|
+
cleanText = stripToolCallArtifacts(stripThinkTags(sanitizedRaw));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (showThinking) {
|
|
183
|
+
const parts: string[] = [];
|
|
184
|
+
if (sanitizedReasoning) appendUnique(parts, sanitizedReasoning);
|
|
185
|
+
if (thinkContent) appendUnique(parts, thinkContent);
|
|
186
|
+
if (cleanText) appendUnique(parts, cleanText);
|
|
187
|
+
return parts.filter(Boolean).join('\n\n').trim();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return cleanText.trim();
|
|
191
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"strict": false,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"types": ["node"]
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*.ts", "src/**/*.tsx"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
|
2
|
+
Theme: GitHub
|
|
3
|
+
Description: Light theme as seen on github.com
|
|
4
|
+
Author: github.com
|
|
5
|
+
Maintainer: @Hirse
|
|
6
|
+
Updated: 2021-05-15
|
|
7
|
+
|
|
8
|
+
Outdated base version: https://github.com/primer/github-syntax-light
|
|
9
|
+
Current colors taken from GitHub's CSS
|
|
10
|
+
*/.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#005cc5}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-comment,.hljs-code,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:JetBrains Mono,Fira Code,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-20{bottom:5rem}.bottom-full{bottom:100%}.left-0{left:0}.left-4{left:1rem}.right-0{right:0}.right-4{right:1rem}.right-6{right:1.5rem}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.m-1\.5{margin:.375rem}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.mx-auto{margin-left:auto;margin-right:auto}.my-3{margin-top:.75rem;margin-bottom:.75rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-0\.5{margin-left:.125rem}.ml-3\.5{margin-left:.875rem}.ml-auto{margin-left:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.hidden{display:none}.h-1\.5{height:.375rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-40{max-height:10rem}.max-h-56{max-height:14rem}.max-h-60{max-height:15rem}.max-h-64{max-height:16rem}.max-h-\[140px\]{max-height:140px}.max-h-\[40vh\]{max-height:40vh}.max-h-\[50vh\]{max-height:50vh}.max-h-\[70vh\]{max-height:70vh}.min-h-\[36px\]{min-height:36px}.w-0\.5{width:.125rem}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[420px\]{width:420px}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-\[160px\]{min-width:160px}.max-w-5xl{max-width:64rem}.max-w-\[min\(82\%\,64ch\)\]{max-width:min(82%,64ch)}.max-w-full{max-width:100%}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-90{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-bounce{animation:bounce 1s infinite}@keyframes fade-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.animate-fade-in{animation:fade-in .2s ease-out}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-col-resize{cursor:col-resize}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.select-text{-webkit-user-select:text;-moz-user-select:text;user-select:text}.resize-none{resize:none}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-\[12px\]{border-radius:12px}.rounded-\[6px\]{border-radius:6px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:20px}.rounded-md{border-radius:12px}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-\[\#e5e5e5\]{--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity, 1))}.border-border-light{border-color:#94a3b833}.border-primary{--tw-border-opacity: 1;border-color:rgb(99 102 241 / var(--tw-border-opacity, 1))}.border-primary\/20{border-color:#6366f133}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity, 1))}.bg-\[rgba\(0\,0\,0\,0\.031\)\]{background-color:#00000008}.bg-black\/30{background-color:#0000004d}.bg-content-secondary{--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity, 1))}.bg-danger{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-primary{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity, 1))}.bg-primary-light{background-color:#6366f11a}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-success{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity, 1))}.bg-surface-card{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-surface-card-alt{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.bg-surface-hover{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity, 1))}.bg-surface-main{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-warning{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.p-2\.5{padding:.625rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-1{padding-bottom:.25rem}.pb-1\.5{padding-bottom:.375rem}.pb-2\.5{padding-bottom:.625rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pl-3{padding-left:.75rem}.pr-2{padding-right:.5rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.align-middle{vertical-align:middle}.font-display{font-family:Playfair Display,Georgia,serif}.font-mono{font-family:JetBrains Mono,Fira Code,monospace}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-normal{line-height:1.5}.leading-relaxed{line-height:1.625}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-amber-500{--tw-text-opacity: 1;color:rgb(245 158 11 / var(--tw-text-opacity, 1))}.text-content-disabled{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-content-primary{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity, 1))}.text-content-secondary{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-danger{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-primary{--tw-text-opacity: 1;color:rgb(99 102 241 / var(--tw-text-opacity, 1))}.text-warning{--tw-text-opacity: 1;color:rgb(245 158 11 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.line-through{text-decoration-line:line-through}.placeholder-content-disabled::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(148 163 184 / var(--tw-placeholder-opacity, 1))}.placeholder-content-disabled::placeholder{--tw-placeholder-opacity: 1;color:rgb(148 163 184 / var(--tw-placeholder-opacity, 1))}.accent-danger{accent-color:#EF4444}.opacity-75{opacity:.75}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-soft{--tw-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 1px 3px rgba(0, 0, 0, .06);--tw-shadow-colored: 0 1px 2px var(--tw-shadow-color), 0 1px 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-500{transition-duration:.5s}:root{--primary: #6366F1;--primary-hover: #4F46E5;--primary-light: rgba(99, 102, 241, .1);--secondary: #8B5CF6;--accent: #F59E0B;--success: #10B981;--warning: #F59E0B;--danger: #EF4444;--bg-main: #F8FAFC;--bg-card: #FFFFFF;--bg-card-alt: #F1F5F9;--bg-hover: #E2E8F0;--bg-input: #FFFFFF;--border-light: rgba(148, 163, 184, .2);--border-medium: rgba(148, 163, 184, .4);--text-primary: #1E293B;--text-secondary: #64748B;--text-disabled: #94A3B8}*{margin:0;padding:0;box-sizing:border-box}html{font-size:15px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{font-family:Inter,Inter Fallback,system-ui,-apple-system,BlinkMacSystemFont,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif;font-feature-settings:"cv01","ss01","ss03";background:#f8fafc;color:#1e293b;min-height:100vh}pre code.hljs,.prose pre code{border-radius:6px;font-size:13px;line-height:1.7;font-family:JetBrains Mono,Fira Code,monospace;background:#f1f5f9;border:1px solid rgba(148,163,184,.2)}pre code.hljs{display:block;padding:8px 12px;overflow-x:auto}.prose code{font-family:JetBrains Mono,Fira Code,monospace;background:transparent;padding:0;border-radius:0;font-size:.85em;color:#6366f1;font-weight:500}.prose code:before,.prose code:after{content:""!important}.prose h1 code,.prose h2 code,.prose h3 code,.prose h4 code{font-size:inherit;color:inherit;font-weight:inherit}.prose sup{font-size:.75em;vertical-align:super;line-height:0}.prose pre{border-radius:6px;border:1px solid rgba(148,163,184,.2);background:#f1f5f9;margin:1em 0;overflow:hidden}.prose pre code{background:transparent;padding:0;border:none;font-size:13px;color:#1e293b}.prose table{width:auto;border-collapse:collapse;font-size:.875rem;margin:1em 0;border:1px solid rgba(148,163,184,.3)}.prose thead{background:#f1f5f9}.prose th,.prose th[align]{padding:.5rem .75rem;text-align:center!important;font-weight:600;color:#1e293b;border-bottom:1px solid rgba(148,163,184,.3);border-right:1px solid rgba(148,163,184,.3);font-size:.8125rem}.prose th:last-child{border-right:none}.prose td{padding:.5rem .75rem;border-bottom:1px solid rgba(148,163,184,.15);border-right:1px solid rgba(148,163,184,.15);color:#64748b}.prose td:last-child{border-right:none}.prose tbody tr:hover{background:#6366f10d}.prose tbody tr:last-child td{border-bottom:none}.prose blockquote{border-left:4px solid #6366F1;padding:.75em 1em;margin:1em 0;background:#6366f10d;color:#4f46e5;border-radius:0 6px 6px 0}.prose blockquote p{margin:0}.prose ul,.prose ol{padding-left:1.5em;margin:.5em 0;list-style-type:disc}.prose ol{list-style-type:decimal}.prose li{margin:.25em 0;color:#1e293b}.prose li::marker{color:#6366f1;font-size:.85em}.prose ul ul{list-style-type:circle}.prose ul ul ul{list-style-type:square}.prose a{color:#6366f1;text-decoration:underline;text-underline-offset:2px}.prose a:hover{color:#4f46e5}.prose hr{border:none;border-top:1px solid rgba(148,163,184,.2);margin:1.5em 0}.prose h1,.prose h2,.prose h3,.prose h4{color:#1e293b;font-weight:700;margin-top:1.5em;margin-bottom:.5em}.prose h1{font-size:1.5em;border-bottom:2px solid rgba(148,163,184,.2);padding-bottom:.3em}.prose h2{font-size:1.25em;border-bottom:1px solid rgba(148,163,184,.1);padding-bottom:.3em}.prose h3{font-size:1.125em}.prose img{max-width:100%;border-radius:6px;margin:1em 0;border:1px solid rgba(148,163,184,.2)}.prose{font-size:1rem;line-height:1.8;font-family:Inter,sans-serif;color:#1e293b}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#94a3b8}::-moz-selection{background:#6366f133;color:#1e293b}::selection{background:#6366f133;color:#1e293b}*:focus-visible{outline:2px solid #6366F1;outline-offset:2px}@keyframes pulse-scale{0%,to{transform:scale(1);opacity:1}50%{transform:scale(.5);opacity:.5}}.animate-pulse-scale{animation:pulse-scale 1.2s ease-in-out infinite}@keyframes blink{0%,to{opacity:1}50%{opacity:0}}.animate-blink{animation:blink .8s ease-in-out infinite}.hover\:bg-danger-hover:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.hover\:bg-primary-hover:hover{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity, 1))}.hover\:bg-primary\/20:hover{background-color:#6366f133}.hover\:bg-red-200:hover{--tw-bg-opacity: 1;background-color:rgb(254 202 202 / var(--tw-bg-opacity, 1))}.hover\:bg-red-50:hover{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.hover\:bg-surface-hover:hover{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity, 1))}.hover\:text-content-primary:hover{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity, 1))}.hover\:text-content-secondary:hover{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.focus\:border-content-disabled:focus{--tw-border-opacity: 1;border-color:rgb(148 163 184 / var(--tw-border-opacity, 1))}.active\:bg-primary\/30:active{background-color:#6366f14d}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:768px){.md\:left-5{left:1.25rem}.md\:right-5{right:1.25rem}.md\:px-10{padding-left:2.5rem;padding-right:2.5rem}.md\:px-5{padding-left:1.25rem;padding-right:1.25rem}}@media(min-width:1024px){.lg\:px-16{padding-left:4rem;padding-right:4rem}}
|