@steerable/agent-shell-web 0.6.15
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.
- package/LICENSE +91 -0
- package/README.md +20 -0
- package/package.json +69 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.png +0 -0
- package/src/AppShell.tsx +24 -0
- package/src/assets/logo-generic.png +0 -0
- package/src/brand.test.ts +22 -0
- package/src/brand.ts +48 -0
- package/src/components/AgentSidebar.test.tsx +127 -0
- package/src/components/AgentSidebar.tsx +1010 -0
- package/src/components/ChatHeader.test.tsx +230 -0
- package/src/components/ChatHeader.tsx +436 -0
- package/src/components/DockPanelHeader.tsx +69 -0
- package/src/components/LocalLlmSettingsModal.test.tsx +372 -0
- package/src/components/LocalLlmSettingsModal.tsx +72 -0
- package/src/components/TerminalPanel.tsx +68 -0
- package/src/components/TerminalView.tsx +228 -0
- package/src/components/chat/ApprovalModal.test.tsx +83 -0
- package/src/components/chat/ApprovalModal.tsx +229 -0
- package/src/components/chat/AskUserModalHost.test.tsx +176 -0
- package/src/components/chat/AskUserModalHost.tsx +61 -0
- package/src/components/chat/AskUserQuestionMenu.tsx +621 -0
- package/src/components/chat/AssistantMessage.test.tsx +68 -0
- package/src/components/chat/AssistantMessage.tsx +469 -0
- package/src/components/chat/ChatInput.test.tsx +609 -0
- package/src/components/chat/ChatInput.tsx +2068 -0
- package/src/components/chat/ChatProjectBadge.tsx +362 -0
- package/src/components/chat/EmptyChat.tsx +18 -0
- package/src/components/chat/ExecPolicyPicker.test.tsx +26 -0
- package/src/components/chat/ExecPolicyPicker.tsx +120 -0
- package/src/components/chat/ExecutedActionsCard.tsx +216 -0
- package/src/components/chat/InterruptedTurnCard.tsx +53 -0
- package/src/components/chat/LocalChatPanel.tsx +408 -0
- package/src/components/chat/Markdown.test.tsx +170 -0
- package/src/components/chat/Markdown.tsx +432 -0
- package/src/components/chat/MessageList.tsx +332 -0
- package/src/components/chat/ModelPicker.test.tsx +149 -0
- package/src/components/chat/ModelPicker.tsx +289 -0
- package/src/components/chat/OrchestrationChildrenCard.tsx +40 -0
- package/src/components/chat/PlanTodoList.tsx +121 -0
- package/src/components/chat/ProjectTrustBanner.tsx +91 -0
- package/src/components/chat/SessionTreeModal.test.tsx +161 -0
- package/src/components/chat/SessionTreeModal.tsx +221 -0
- package/src/components/chat/StreamingStatus.tsx +95 -0
- package/src/components/chat/TaskOutcomeCards.test.tsx +97 -0
- package/src/components/chat/TaskOutcomeCards.tsx +128 -0
- package/src/components/chat/TaskPanelModal.test.tsx +198 -0
- package/src/components/chat/TaskPanelModal.tsx +310 -0
- package/src/components/chat/TaskProcessPanel.test.tsx +111 -0
- package/src/components/chat/TaskProcessPanel.tsx +208 -0
- package/src/components/chat/TurnProcessGroup.test.tsx +161 -0
- package/src/components/chat/TurnProcessGroup.tsx +193 -0
- package/src/components/chat/UnifiedDiff.tsx +31 -0
- package/src/components/chat/UserMessage.tsx +155 -0
- package/src/components/chat/elapsed.test.ts +42 -0
- package/src/components/chat/elapsed.ts +45 -0
- package/src/components/chat/executed-actions-model.test.ts +134 -0
- package/src/components/chat/executed-actions-model.ts +143 -0
- package/src/components/chat/orchestration-children-model.test.ts +115 -0
- package/src/components/chat/orchestration-children-model.ts +104 -0
- package/src/components/chat/timestamp.ts +24 -0
- package/src/components/chat/turn-timeline.test.ts +97 -0
- package/src/components/chat/turn-timeline.ts +132 -0
- package/src/components/chat/useChatTasks.test.ts +136 -0
- package/src/components/chat/useChatTasks.ts +134 -0
- package/src/components/chat/useCopy.ts +62 -0
- package/src/components/settings/AgentCapabilityFields.tsx +343 -0
- package/src/components/settings/AgentsSettingsPanel.test.tsx +229 -0
- package/src/components/settings/AgentsSettingsPanel.tsx +412 -0
- package/src/components/settings/DiagnoseSettingsPanel.tsx +119 -0
- package/src/components/settings/InsightsSettingsPanel.test.tsx +132 -0
- package/src/components/settings/InsightsSettingsPanel.tsx +309 -0
- package/src/components/settings/LlmSettingsPanel.tsx +875 -0
- package/src/components/settings/McpSettingsPanel.tsx +466 -0
- package/src/components/settings/ModelCapabilityChips.tsx +46 -0
- package/src/components/settings/ModelIdCombobox.test.tsx +88 -0
- package/src/components/settings/ModelIdCombobox.tsx +185 -0
- package/src/components/settings/SecuritySettingsPanel.test.tsx +122 -0
- package/src/components/settings/SecuritySettingsPanel.tsx +169 -0
- package/src/components/settings/SettingsSaveButton.tsx +35 -0
- package/src/components/settings/SkillsSettingsPanel.test.tsx +62 -0
- package/src/components/settings/SkillsSettingsPanel.tsx +334 -0
- package/src/components/settings/TelemetrySettingsPanel.tsx +173 -0
- package/src/components/settings/UsagePanel.tsx +196 -0
- package/src/components/settings/WebSearchSettingsPanel.test.tsx +90 -0
- package/src/components/settings/WebSearchSettingsPanel.tsx +183 -0
- package/src/components/settings/compat-flags-model.test.ts +71 -0
- package/src/components/settings/compat-flags-model.ts +57 -0
- package/src/components/settings/llm-vendors.test.ts +171 -0
- package/src/components/settings/llm-vendors.ts +275 -0
- package/src/components/settings/model-capabilities.test.ts +88 -0
- package/src/components/settings/model-capabilities.ts +123 -0
- package/src/components/settings/preset-choice-model.test.ts +96 -0
- package/src/components/settings/preset-choice-model.ts +75 -0
- package/src/fixtures/browser-dev-data.json +315 -0
- package/src/hooks/useChatsAndAgents.ts +182 -0
- package/src/layouts/AgentLayout.tsx +574 -0
- package/src/lib/attachments.ts +78 -0
- package/src/lib/browser-dev-electron-mock.ts +640 -0
- package/src/lib/chat-title.ts +33 -0
- package/src/lib/chat-transport.ts +346 -0
- package/src/lib/date-groups.ts +34 -0
- package/src/lib/demo-flag.ts +15 -0
- package/src/lib/electron-bridge.ts +270 -0
- package/src/lib/exec-policy.ts +25 -0
- package/src/lib/http-bridge.ts +293 -0
- package/src/lib/insights.ts +17 -0
- package/src/lib/local-api.ts +714 -0
- package/src/lib/pending-first-message.test.ts +17 -0
- package/src/lib/pending-first-message.ts +36 -0
- package/src/lib/slash-sources.ts +191 -0
- package/src/main.tsx +103 -0
- package/src/packs/registry.ts +64 -0
- package/src/pages/AgentPage.tsx +1226 -0
- package/src/pages/ChatPanelPreviewPage.tsx +264 -0
- package/src/pages/SettingsPage.test.tsx +102 -0
- package/src/pages/SettingsPage.tsx +197 -0
- package/src/styles/index.css +156 -0
- package/src/vite-env.d.ts +1 -0
- package/vite.base.ts +94 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tailwind v4 入口 + Steerable design tokens。
|
|
3
|
+
*
|
|
4
|
+
* @steerable/agent-ui 的官方 `tailwind-preset.ts` 是为 Tailwind v3 的
|
|
5
|
+
* `presets: [...]` API 设计的;v4 取消了 preset 概念,token 改成在 CSS
|
|
6
|
+
* 里用 `@theme` 块声明。下面这份 token 镜像 framework Storybook 里
|
|
7
|
+
* preview.css 的定义(packages/agent-ui/ts/.storybook/preview.css),
|
|
8
|
+
* 保持与框架视觉一致。
|
|
9
|
+
*
|
|
10
|
+
* 如果将来 framework 发布了 v4 兼容的 preset CSS(subpath export),
|
|
11
|
+
* 切换到 `@import "@steerable/agent-ui/tailwind-preset.css"` 即可。
|
|
12
|
+
*/
|
|
13
|
+
@import "tailwindcss";
|
|
14
|
+
|
|
15
|
+
@source "../**/*.{ts,tsx}";
|
|
16
|
+
@source "../../node_modules/@steerable/agent-ui/dist/**/*.js";
|
|
17
|
+
|
|
18
|
+
@theme {
|
|
19
|
+
--color-agent-canvas: hsl(0 0% 100%);
|
|
20
|
+
--color-agent-foreground: hsl(240 10% 3.9%);
|
|
21
|
+
--color-agent-muted: hsl(240 4.8% 95.9%);
|
|
22
|
+
--color-agent-muted-foreground: hsl(240 5% 32%);
|
|
23
|
+
--color-agent-border: hsl(240 5.9% 90%);
|
|
24
|
+
--color-agent-accent: hsl(217 91% 42%);
|
|
25
|
+
--color-agent-accent-foreground: hsl(0 0% 100%);
|
|
26
|
+
--color-agent-destructive: hsl(0 75% 42%);
|
|
27
|
+
--color-agent-tool-read: hsl(142 71% 22%);
|
|
28
|
+
--color-agent-tool-write: hsl(28 90% 28%);
|
|
29
|
+
--color-agent-tool-destructive: hsl(0 75% 42%);
|
|
30
|
+
|
|
31
|
+
--radius-agent-sm: calc(0.75rem - 4px);
|
|
32
|
+
--radius-agent-md: calc(0.75rem - 2px);
|
|
33
|
+
--radius-agent-lg: 0.75rem;
|
|
34
|
+
|
|
35
|
+
--animate-agent-cursor-blink: agent-cursor-blink 1s steps(1) infinite;
|
|
36
|
+
--animate-agent-shimmer: agent-shimmer 2s linear infinite;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@keyframes agent-cursor-blink {
|
|
40
|
+
0%, 100% { opacity: 1; }
|
|
41
|
+
50% { opacity: 0; }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@keyframes agent-shimmer {
|
|
45
|
+
0% { background-position: -1000px 0; }
|
|
46
|
+
100% { background-position: 1000px 0; }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
:root {
|
|
50
|
+
color-scheme: light;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
html, body, #root {
|
|
54
|
+
height: 100%;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
body {
|
|
58
|
+
margin: 0;
|
|
59
|
+
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
|
|
60
|
+
"Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
61
|
+
font-size: 14px;
|
|
62
|
+
line-height: 1.5;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/*
|
|
66
|
+
* .markdown-content — applied to the wrapper around <Markdown>. Per-element
|
|
67
|
+
* margins are handled inside Markdown.tsx; this rule just collapses the
|
|
68
|
+
* first / last element's outer margin so a chat bubble doesn't accumulate
|
|
69
|
+
* dead space at the top or bottom edge.
|
|
70
|
+
*/
|
|
71
|
+
.markdown-content > :first-child { margin-top: 0; }
|
|
72
|
+
.markdown-content > :last-child { margin-bottom: 0; }
|
|
73
|
+
|
|
74
|
+
/*
|
|
75
|
+
* ToolExecutionCard header: CJK status labels (已完成 / 失败 / …) wrap at
|
|
76
|
+
* every character when the flex row is squeezed. Keep the trailing status
|
|
77
|
+
* cluster at content width; let the truncated summary take the leftover.
|
|
78
|
+
* Host-side until @steerable/agent-ui ships the same layout classes.
|
|
79
|
+
*/
|
|
80
|
+
.steerable-tool-execution > button > span.truncate {
|
|
81
|
+
min-width: 0;
|
|
82
|
+
flex: 1 1 0%;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.steerable-tool-execution > button > span.ml-auto {
|
|
86
|
+
flex-shrink: 0;
|
|
87
|
+
white-space: nowrap;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/*
|
|
91
|
+
* ChatInput overlay: the contenteditable must keep a real text fill.
|
|
92
|
+
* macOS Pinyin inspects fill during the first letter's keydown — before
|
|
93
|
+
* `compositionstart` and before React's delegated keydown — so
|
|
94
|
+
* `color: transparent` or `-webkit-text-fill-color: transparent` on the
|
|
95
|
+
* editor commits that letter as ASCII (`ni好` instead of 你好).
|
|
96
|
+
*
|
|
97
|
+
* Glyphs therefore paint in the editor. The mirror only draws mention
|
|
98
|
+
* chip backgrounds; its own text is invisible so the two layers do not
|
|
99
|
+
* double-print. Hide the placeholder/mirror while IME marked text is
|
|
100
|
+
* showing so the underline is not covered.
|
|
101
|
+
*/
|
|
102
|
+
.chat-input-mirror {
|
|
103
|
+
color: transparent;
|
|
104
|
+
-webkit-text-fill-color: transparent;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
[data-mention-chip] {
|
|
108
|
+
pointer-events: auto;
|
|
109
|
+
color: #4f46e5;
|
|
110
|
+
-webkit-text-fill-color: currentColor;
|
|
111
|
+
background-color: color-mix(in oklab, var(--color-agent-canvas) 82%, #6366f1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
[data-mention-chip][data-mention-type="chat"] {
|
|
115
|
+
color: #059669;
|
|
116
|
+
-webkit-text-fill-color: currentColor;
|
|
117
|
+
background-color: color-mix(in oklab, var(--color-agent-canvas) 82%, #10b981);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
[data-mention-chip][data-mention-type="skill"] {
|
|
121
|
+
color: #d97706;
|
|
122
|
+
-webkit-text-fill-color: currentColor;
|
|
123
|
+
background-color: color-mix(in oklab, var(--color-agent-canvas) 82%, #f59e0b);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
[data-mention-chip][data-mention-type="mcp"] {
|
|
127
|
+
color: #0284c7;
|
|
128
|
+
-webkit-text-fill-color: currentColor;
|
|
129
|
+
background-color: color-mix(in oklab, var(--color-agent-canvas) 82%, #0ea5e9);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
[data-mention-chip][data-mention-type="plain"] {
|
|
133
|
+
color: var(--color-agent-foreground);
|
|
134
|
+
-webkit-text-fill-color: currentColor;
|
|
135
|
+
background-color: color-mix(in oklab, var(--color-agent-canvas) 88%, #71717a);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[data-mention-chip] [data-mention-remove],
|
|
139
|
+
[data-mention-chip] [data-mention-remove] * {
|
|
140
|
+
color: #fff;
|
|
141
|
+
-webkit-text-fill-color: #fff;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
[data-mention-chip] [data-mention-remove] {
|
|
145
|
+
opacity: 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
[data-mention-chip]:hover [data-mention-remove],
|
|
149
|
+
[data-mention-chip]:focus-visible [data-mention-remove] {
|
|
150
|
+
opacity: 1;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.chat-input-surface:has(.chat-input-editor[data-composing]) .chat-input-placeholder,
|
|
154
|
+
.chat-input-surface:has(.chat-input-editor[data-composing]) .chat-input-mirror {
|
|
155
|
+
visibility: hidden;
|
|
156
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
package/vite.base.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 产品 web 构建的共享 vite 配置工厂(2.3)。
|
|
3
|
+
*
|
|
4
|
+
* 每个产品(products/<id>/web/)有自己的 vite 入口:index.html +
|
|
5
|
+
* main.tsx(静态注册本产品的包渲染层贡献)+ vite.config.ts(调本工厂)。
|
|
6
|
+
* 产品的编译单元只含自己的包——未激活包代码不进 bundle,不依赖树摇
|
|
7
|
+
* 正确性(物理隔离与 node 侧对称)。
|
|
8
|
+
*
|
|
9
|
+
* Electron 加载产物时走 file:// 协议,必须用相对路径,否则资源 404。
|
|
10
|
+
* dev server 跑在固定端口,主进程的 IS_DEV 分支 loadURL('http://localhost:5173')。
|
|
11
|
+
*/
|
|
12
|
+
import { defineConfig } from 'vite';
|
|
13
|
+
import react from '@vitejs/plugin-react';
|
|
14
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
18
|
+
|
|
19
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
|
|
21
|
+
export interface ProductViteConfigOptions {
|
|
22
|
+
/** 产品目录(products/<id>/web 的上一级…即 products/<id>)。 */
|
|
23
|
+
productDir: string;
|
|
24
|
+
/** 产品 flavor(决定品牌 define 与激活包集合)。 */
|
|
25
|
+
flavor: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createProductViteConfig(options: ProductViteConfigOptions) {
|
|
29
|
+
const { productDir, flavor } = options;
|
|
30
|
+
const repoRoot = path.resolve(productDir, '../..');
|
|
31
|
+
// shell 渲染层源码锚到本包自身(3.2 起住在框架仓库
|
|
32
|
+
// packages/agent-shell/web/src),产品经 `@/` 别名引用。
|
|
33
|
+
const webSrcDir = path.resolve(HERE, 'src');
|
|
34
|
+
const readJson = (p: string) => JSON.parse(readFileSync(p, 'utf8'));
|
|
35
|
+
|
|
36
|
+
// 产品品牌(3.1):product.json 的 brand 字段优先(无包产品的品牌
|
|
37
|
+
// 来源);否则取激活包 pack.json 的 brand(0.3g 机制,读
|
|
38
|
+
// products/manifest.json 的 flavor→包映射,第一个带 brand 的包)。
|
|
39
|
+
// 都没有则用 shell 中性默认(Agent Shell)。
|
|
40
|
+
const productJsonPath = path.join(productDir, 'product.json');
|
|
41
|
+
const product = existsSync(productJsonPath) ? readJson(productJsonPath) : {};
|
|
42
|
+
const manifest = readJson(path.join(repoRoot, 'products/manifest.json'));
|
|
43
|
+
let packBrand: Record<string, string> | null = null;
|
|
44
|
+
for (const packId of manifest.flavors?.[flavor] ?? []) {
|
|
45
|
+
const pack = readJson(path.join(repoRoot, 'packages', `pack-${packId}`, 'pack.json'));
|
|
46
|
+
if (pack.brand) {
|
|
47
|
+
packBrand = pack.brand;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const brand = product.brand ?? packBrand;
|
|
52
|
+
|
|
53
|
+
return defineConfig({
|
|
54
|
+
// root 默认取 config 文件所在目录(products/<id>/web)。
|
|
55
|
+
base: './',
|
|
56
|
+
plugins: [react(), tailwindcss()],
|
|
57
|
+
define: {
|
|
58
|
+
// 显式 define 让 flavor 条件在构建期可静态折叠。
|
|
59
|
+
'import.meta.env.VITE_APP_FLAVOR': JSON.stringify(flavor),
|
|
60
|
+
// 品牌 define:产品/包声明覆盖 shell 中性默认(Agent Shell)。
|
|
61
|
+
// logo 不走 define——资产必须静态 import 才进 bundle,由包 web 模块
|
|
62
|
+
// 在注册时经 setBrandLogoUrl 注入(本包 src/brand.ts)。
|
|
63
|
+
'import.meta.env.VITE_BRAND_NAME': JSON.stringify(brand?.displayName ?? 'Steerable Shell'),
|
|
64
|
+
'import.meta.env.VITE_BRAND_TAGLINE': JSON.stringify(brand?.tagline ?? '一款本地桌面 AI 伙伴'),
|
|
65
|
+
'import.meta.env.VITE_DEFAULT_AGENT_ID': JSON.stringify(brand?.defaultAgentId ?? 'local-assistant'),
|
|
66
|
+
},
|
|
67
|
+
resolve: {
|
|
68
|
+
alias: {
|
|
69
|
+
// shell 渲染层源码(产品入口与包 web 模块都以 `@/` 引用它)。
|
|
70
|
+
'@': webSrcDir,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
// 静态资产(favicon 等中性默认)随 shell web 包分发;产品可在自己的
|
|
74
|
+
// web root 放 public/ 覆盖(vite 不合并双 publicDir——需要产品级资产
|
|
75
|
+
// 时在产品 vite.config 里显式覆盖本字段)。
|
|
76
|
+
publicDir: path.resolve(HERE, 'public'),
|
|
77
|
+
server: {
|
|
78
|
+
port: 5173,
|
|
79
|
+
strictPort: true,
|
|
80
|
+
host: '127.0.0.1',
|
|
81
|
+
// 产品 root 在 products/<id>/web;shell 渲染层源码在框架仓库
|
|
82
|
+
// (3.2 起经 file: 依赖符号链接)——两个根都放行。
|
|
83
|
+
fs: { allow: [repoRoot, path.resolve(webSrcDir, '..')] },
|
|
84
|
+
},
|
|
85
|
+
build: {
|
|
86
|
+
// 产物落在产品目录:products/<id>/web/dist(打包期被 electron-builder
|
|
87
|
+
// 映射为包内 web-dist/)。
|
|
88
|
+
outDir: 'dist',
|
|
89
|
+
emptyOutDir: true,
|
|
90
|
+
sourcemap: true,
|
|
91
|
+
target: 'chrome120',
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|