@steerable/agent-shell-web 0.6.26 → 0.6.28
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/package.json +4 -4
- package/src/components/AgentSidebar.tsx +24 -24
- package/src/components/BrandLockup.tsx +1 -1
- package/src/components/ChatHeader.tsx +5 -15
- package/src/components/LocalLlmSettingsModal.tsx +5 -5
- package/src/components/TerminalPanel.tsx +1 -1
- package/src/components/chat/ApprovalModal.tsx +5 -5
- package/src/components/chat/AskUserQuestionMenu.tsx +13 -13
- package/src/components/chat/AssistantMessage.tsx +41 -13
- package/src/components/chat/ChatInput.tsx +24 -24
- package/src/components/chat/ExecPolicyPicker.tsx +4 -4
- package/src/components/chat/ExecutedActionsCard.tsx +10 -3
- package/src/components/chat/FilePathCode.tsx +2 -1
- package/src/components/chat/LocalChatPanel.tsx +39 -4
- package/src/components/chat/Markdown.tsx +15 -14
- package/src/components/chat/MessageList.tsx +39 -16
- package/src/components/chat/ModelPicker.tsx +6 -6
- package/src/components/chat/SessionTodoList.tsx +137 -0
- package/src/components/chat/SessionTreeModal.tsx +3 -3
- package/src/components/chat/SuggestedReplies.tsx +5 -4
- package/src/components/chat/TaskPanelModal.tsx +3 -3
- package/src/components/chat/TaskProcessPanel.tsx +17 -11
- package/src/components/chat/TurnFilesCard.tsx +2 -2
- package/src/components/chat/TurnProcessGroup.tsx +281 -117
- package/src/components/chat/UserMessage.tsx +4 -4
- package/src/components/chat/next-steps-tags.ts +28 -0
- package/src/components/chat/process-status.ts +125 -32
- package/src/components/chat/todo-list-model.ts +167 -0
- package/src/components/chat/turn-timeline.ts +100 -11
- package/src/components/settings/AgentsSettingsPanel.tsx +2 -2
- package/src/components/settings/AppearanceSettingsPanel.tsx +43 -30
- package/src/components/settings/DiagnoseSettingsPanel.tsx +2 -2
- package/src/components/settings/InsightsSettingsPanel.tsx +4 -4
- package/src/components/settings/LlmSettingsPanel.tsx +10 -10
- package/src/components/settings/McpSettingsPanel.tsx +3 -3
- package/src/components/settings/ModelIdCombobox.tsx +2 -2
- package/src/components/settings/SecuritySettingsPanel.tsx +1 -1
- package/src/components/settings/SettingsSaveButton.tsx +1 -1
- package/src/components/settings/SkillsSettingsPanel.tsx +2 -2
- package/src/components/settings/TelemetrySettingsPanel.tsx +2 -2
- package/src/components/settings/UsagePanel.tsx +14 -10
- package/src/components/settings/WebSearchSettingsPanel.tsx +2 -2
- package/src/layouts/AgentLayout.tsx +3 -3
- package/src/lib/chat-transport.ts +68 -12
- package/src/lib/show-thinking-content.ts +40 -15
- package/src/pages/AgentPage.tsx +56 -6
- package/src/pages/ChatPanelPreviewPage.tsx +88 -8
- package/src/pages/SettingsPage.tsx +39 -39
- package/src/styles/index.css +12 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useMemo, useState } from 'react';
|
|
1
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import type { ChatMessage } from '@steerable/agent-protocol';
|
|
3
3
|
import { LocalChatPanel } from '@/components/chat/LocalChatPanel';
|
|
4
4
|
import { ChatHeader } from '@/components/ChatHeader';
|
|
@@ -82,7 +82,28 @@ Tests: 1 failed, 2 passed, 3 total
|
|
|
82
82
|
},
|
|
83
83
|
];
|
|
84
84
|
|
|
85
|
+
const MOCK_TODOS = [
|
|
86
|
+
{ id: 'survey', content: '检查服务连通性', status: 'completed' as const },
|
|
87
|
+
{ id: 'test', content: '跑本目录测试套件', status: 'in_progress' as const },
|
|
88
|
+
{ id: 'fix', content: '修失败的断言颜色', status: 'pending' as const },
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const MOCK_TODO_ACTION: ExecutedAction = {
|
|
92
|
+
tool: 'todo_write',
|
|
93
|
+
arguments: { todos: MOCK_TODOS },
|
|
94
|
+
result: {
|
|
95
|
+
success: true,
|
|
96
|
+
data: {
|
|
97
|
+
value: {
|
|
98
|
+
todos: MOCK_TODOS,
|
|
99
|
+
summary: { total: 3, pending: 1, inProgress: 1, completed: 1 },
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
85
105
|
const MOCK_ACTIONS: ExecutedAction[] = [
|
|
106
|
+
MOCK_TODO_ACTION,
|
|
86
107
|
{
|
|
87
108
|
tool: 'check_connection',
|
|
88
109
|
arguments: { host: '127.0.0.1', port: 8765 },
|
|
@@ -107,11 +128,12 @@ const MOCK_ACTIONS: ExecutedAction[] = [
|
|
|
107
128
|
|
|
108
129
|
const MOCK_TIMELINE: TurnBlock[] = [
|
|
109
130
|
{ type: 'reasoning', content: '先检查服务连通性,再跑测试套件。' },
|
|
110
|
-
{ type: 'tools', actions: [
|
|
111
|
-
{ type: 'text', content: '连接正常。接下来跑测试套件。' },
|
|
131
|
+
{ type: 'tools', actions: [MOCK_TODO_ACTION] },
|
|
112
132
|
{ type: 'tools', actions: [MOCK_ACTIONS[1]] },
|
|
113
|
-
{ type: '
|
|
133
|
+
{ type: 'text', content: '连接正常。接下来跑测试套件。' },
|
|
114
134
|
{ type: 'tools', actions: [MOCK_ACTIONS[2]] },
|
|
135
|
+
{ type: 'reasoning', content: '测试失败,读一下失败文件再给建议。' },
|
|
136
|
+
{ type: 'tools', actions: [MOCK_ACTIONS[3]] },
|
|
115
137
|
{ type: 'text', content: MOCK_MESSAGES[1].content ?? '' },
|
|
116
138
|
];
|
|
117
139
|
|
|
@@ -123,17 +145,53 @@ const MOCK_TIMELINE: TurnBlock[] = [
|
|
|
123
145
|
* StreamingStatus's "正在思考..." baseline.
|
|
124
146
|
* • `tools-run` — in-flight bubble, round 2, 2 tools just ran; verifies
|
|
125
147
|
* StreamingStatus's "已调用 N 个工具" + "Round N" badge interaction.
|
|
148
|
+
* • `reasoning-stream` — reasoning tokens arrive over time; verifies the
|
|
149
|
+
* 5-line peek stays visible and the list scrollbar sticks to the bottom.
|
|
126
150
|
*/
|
|
127
|
-
type PreviewScene = 'static' | 'thinking' | 'tools-run';
|
|
151
|
+
type PreviewScene = 'static' | 'thinking' | 'tools-run' | 'reasoning-stream';
|
|
152
|
+
|
|
153
|
+
const STREAMING_REASONING = [
|
|
154
|
+
'先检查服务是否还能连上。',
|
|
155
|
+
'如果端口通了,再跑本目录的测试套件。',
|
|
156
|
+
'失败的话只读失败文件,不要整仓扫一遍。',
|
|
157
|
+
'断言颜色对不上时优先看 AssistantMessage 的默认色。',
|
|
158
|
+
'改完再复跑一次,确认没有带出新的失败。',
|
|
159
|
+
'工具调用保持最少:连通性检查、跑测试、必要时读文件。',
|
|
160
|
+
'最后用一两句说清楚结果和下一步。',
|
|
161
|
+
'如果还在思考,后面的句子会被 5 行窗口裁掉。',
|
|
162
|
+
'比较 a < b 时不要被 Markdown 当成 HTML 标签吃掉。',
|
|
163
|
+
'滚动条应一直钉在最下面,不要随着 token 上下跳。',
|
|
164
|
+
].join('\n');
|
|
128
165
|
|
|
129
166
|
const SCENES: { id: PreviewScene; label: string }[] = [
|
|
130
167
|
{ id: 'static', label: '静态历史' },
|
|
131
168
|
{ id: 'thinking', label: '流式 · 空内容' },
|
|
132
169
|
{ id: 'tools-run', label: '流式 · 工具已跑 · round 2' },
|
|
170
|
+
{ id: 'reasoning-stream', label: '流式 · 推理打字' },
|
|
133
171
|
];
|
|
134
172
|
|
|
135
173
|
export function ChatPanelPreviewPage() {
|
|
136
174
|
const [scene, setScene] = useState<PreviewScene>('static');
|
|
175
|
+
const [streamedReasoning, setStreamedReasoning] = useState('');
|
|
176
|
+
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
if (scene !== 'reasoning-stream') {
|
|
179
|
+
setStreamedReasoning('');
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
setStreamedReasoning('');
|
|
183
|
+
let index = 0;
|
|
184
|
+
const id = window.setInterval(() => {
|
|
185
|
+
index += 2;
|
|
186
|
+
if (index >= STREAMING_REASONING.length) {
|
|
187
|
+
setStreamedReasoning(STREAMING_REASONING);
|
|
188
|
+
window.clearInterval(id);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
setStreamedReasoning(STREAMING_REASONING.slice(0, index));
|
|
192
|
+
}, 40);
|
|
193
|
+
return () => window.clearInterval(id);
|
|
194
|
+
}, [scene]);
|
|
137
195
|
|
|
138
196
|
// Explicit struct type so TS doesn't try to narrow `actionsByMsgId` to the
|
|
139
197
|
// union of {} | { m2: ... } — both shapes are valid `Record<string, ...>`
|
|
@@ -198,7 +256,7 @@ export function ChatPanelPreviewPage() {
|
|
|
198
256
|
'改完再复跑一次,确认没有带出新的失败。',
|
|
199
257
|
'工具调用保持最少:连通性检查、跑测试、必要时读文件。',
|
|
200
258
|
'最后用一两句说清楚结果和下一步。',
|
|
201
|
-
'如果还在思考,后面的句子会被
|
|
259
|
+
'如果还在思考,后面的句子会被 5 行窗口裁掉。',
|
|
202
260
|
].join('\n'),
|
|
203
261
|
},
|
|
204
262
|
{
|
|
@@ -209,6 +267,28 @@ export function ChatPanelPreviewPage() {
|
|
|
209
267
|
currentTurnStartedAtMs: Date.now() - 12_000,
|
|
210
268
|
currentRound: 2,
|
|
211
269
|
};
|
|
270
|
+
case 'reasoning-stream':
|
|
271
|
+
return {
|
|
272
|
+
messages: [
|
|
273
|
+
MOCK_MESSAGES[0],
|
|
274
|
+
{
|
|
275
|
+
id: 'm-streaming',
|
|
276
|
+
role: 'assistant',
|
|
277
|
+
agentId: MOCK_AGENT.id,
|
|
278
|
+
content: '',
|
|
279
|
+
createdAt: new Date().toISOString(),
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
isStreaming: true,
|
|
283
|
+
actionsByMsgId: {} as Record<string, ExecutedAction[]>,
|
|
284
|
+
currentTurnActions: [],
|
|
285
|
+
timelineByMsgId: {} as Record<string, TurnBlock[]>,
|
|
286
|
+
currentTurnTimeline: streamedReasoning
|
|
287
|
+
? [{ type: 'reasoning', content: streamedReasoning }]
|
|
288
|
+
: [],
|
|
289
|
+
currentTurnStartedAtMs: Date.now() - 1_000,
|
|
290
|
+
currentRound: 1,
|
|
291
|
+
};
|
|
212
292
|
case 'static':
|
|
213
293
|
default:
|
|
214
294
|
return {
|
|
@@ -222,11 +302,11 @@ export function ChatPanelPreviewPage() {
|
|
|
222
302
|
suggestedReplies: ['修一下失败的断言颜色', '把测试再跑一遍', '解释这次失败的原因'],
|
|
223
303
|
};
|
|
224
304
|
}
|
|
225
|
-
}, [scene]);
|
|
305
|
+
}, [scene, streamedReasoning]);
|
|
226
306
|
|
|
227
307
|
return (
|
|
228
308
|
<div className="flex h-full w-full flex-col bg-agent-muted/30">
|
|
229
|
-
<div className="flex shrink-0 items-center gap-2 border-b border-agent-border bg-agent-canvas px-
|
|
309
|
+
<div className="flex shrink-0 items-center gap-2 border-b border-agent-border bg-agent-canvas px-2.5 py-1.5 text-xs">
|
|
230
310
|
<span className="text-agent-muted-foreground">
|
|
231
311
|
/preview/chat (dev harness)
|
|
232
312
|
</span>
|
|
@@ -59,8 +59,8 @@ export function SettingsPage() {
|
|
|
59
59
|
|
|
60
60
|
return (
|
|
61
61
|
<div className="flex h-full w-full flex-col overflow-hidden">
|
|
62
|
-
<header className="flex h-
|
|
63
|
-
<h1 className="text-
|
|
62
|
+
<header className="flex h-9 flex-shrink-0 items-center justify-between gap-2 border-b border-agent-border px-2.5">
|
|
63
|
+
<h1 className="text-xs font-semibold text-agent-foreground">{title}</h1>
|
|
64
64
|
{section === 'general' && (
|
|
65
65
|
<SettingsSaveButton
|
|
66
66
|
testId="settings-header-save"
|
|
@@ -73,7 +73,7 @@ export function SettingsPage() {
|
|
|
73
73
|
</header>
|
|
74
74
|
|
|
75
75
|
<div className="min-h-0 flex-1 overflow-y-auto">
|
|
76
|
-
<div className="mx-auto max-w-3xl space-y-
|
|
76
|
+
<div className="mx-auto max-w-3xl space-y-4 px-3 py-3">
|
|
77
77
|
{!isElectron() && (
|
|
78
78
|
<p className="rounded-agent-md border border-agent-destructive/20 bg-agent-destructive/10 p-2.5 text-xs text-agent-destructive">
|
|
79
79
|
浏览器预览模式 — 没有 Electron IPC 桥接,部分设置不可用。
|
|
@@ -81,9 +81,9 @@ export function SettingsPage() {
|
|
|
81
81
|
)}
|
|
82
82
|
|
|
83
83
|
{section === 'skills' && (
|
|
84
|
-
<section className="space-y-
|
|
85
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
86
|
-
<LuBlocks className="h-
|
|
84
|
+
<section className="space-y-2" data-testid="settings-section-skills">
|
|
85
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
86
|
+
<LuBlocks className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
87
87
|
本地技能
|
|
88
88
|
</h2>
|
|
89
89
|
<SkillsSettingsPanel />
|
|
@@ -91,9 +91,9 @@ export function SettingsPage() {
|
|
|
91
91
|
)}
|
|
92
92
|
|
|
93
93
|
{section === 'mcp' && (
|
|
94
|
-
<section className="space-y-
|
|
95
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
96
|
-
<LuPlug className="h-
|
|
94
|
+
<section className="space-y-2" data-testid="settings-section-mcp">
|
|
95
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
96
|
+
<LuPlug className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
97
97
|
MCP 服务
|
|
98
98
|
</h2>
|
|
99
99
|
<McpSettingsPanel />
|
|
@@ -101,9 +101,9 @@ export function SettingsPage() {
|
|
|
101
101
|
)}
|
|
102
102
|
|
|
103
103
|
{section === 'agents' && (
|
|
104
|
-
<section className="space-y-
|
|
105
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
106
|
-
<LuBot className="h-
|
|
104
|
+
<section className="space-y-2" data-testid="settings-section-agents">
|
|
105
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
106
|
+
<LuBot className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
107
107
|
智能体
|
|
108
108
|
</h2>
|
|
109
109
|
<AgentsSettingsPanel onCatalogChange={catalog?.refreshAgents} />
|
|
@@ -112,17 +112,17 @@ export function SettingsPage() {
|
|
|
112
112
|
|
|
113
113
|
{section === 'general' && (
|
|
114
114
|
<>
|
|
115
|
-
<section className="space-y-
|
|
116
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
117
|
-
<LuMonitor className="h-
|
|
115
|
+
<section className="space-y-2" data-testid="settings-section-appearance">
|
|
116
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
117
|
+
<LuMonitor className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
118
118
|
界面
|
|
119
119
|
</h2>
|
|
120
120
|
<AppearanceSettingsPanel />
|
|
121
121
|
</section>
|
|
122
122
|
|
|
123
|
-
<section className="space-y-
|
|
124
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
125
|
-
<LuSettings className="h-
|
|
123
|
+
<section className="space-y-2" data-testid="settings-section-llm">
|
|
124
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
125
|
+
<LuSettings className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
126
126
|
本地模型设置
|
|
127
127
|
</h2>
|
|
128
128
|
<LlmSettingsPanel
|
|
@@ -132,49 +132,49 @@ export function SettingsPage() {
|
|
|
132
132
|
/>
|
|
133
133
|
</section>
|
|
134
134
|
|
|
135
|
-
<section className="space-y-
|
|
136
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
137
|
-
<LuSearch className="h-
|
|
135
|
+
<section className="space-y-2" data-testid="settings-section-web-search">
|
|
136
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
137
|
+
<LuSearch className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
138
138
|
网络搜索
|
|
139
139
|
</h2>
|
|
140
140
|
<WebSearchSettingsPanel />
|
|
141
141
|
</section>
|
|
142
142
|
|
|
143
|
-
<section className="space-y-
|
|
144
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
145
|
-
<LuChartBar className="h-
|
|
143
|
+
<section className="space-y-2" data-testid="settings-section-usage">
|
|
144
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
145
|
+
<LuChartBar className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
146
146
|
用量与成本
|
|
147
147
|
</h2>
|
|
148
148
|
<UsagePanel />
|
|
149
149
|
</section>
|
|
150
150
|
|
|
151
|
-
<section className="space-y-
|
|
152
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
153
|
-
<LuNetwork className="h-
|
|
151
|
+
<section className="space-y-2" data-testid="settings-section-diagnose">
|
|
152
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
153
|
+
<LuNetwork className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
154
154
|
链路诊断
|
|
155
155
|
</h2>
|
|
156
156
|
<DiagnoseSettingsPanel />
|
|
157
157
|
</section>
|
|
158
158
|
|
|
159
|
-
<section className="space-y-
|
|
160
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
161
|
-
<LuShieldCheck className="h-
|
|
159
|
+
<section className="space-y-2" data-testid="settings-section-security">
|
|
160
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
161
|
+
<LuShieldCheck className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
162
162
|
安全
|
|
163
163
|
</h2>
|
|
164
164
|
<SecuritySettingsPanel />
|
|
165
165
|
</section>
|
|
166
166
|
|
|
167
|
-
<section className="space-y-
|
|
168
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
169
|
-
<LuChartBar className="h-
|
|
167
|
+
<section className="space-y-2" data-testid="settings-section-insights">
|
|
168
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
169
|
+
<LuChartBar className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
170
170
|
帮助改进产品
|
|
171
171
|
</h2>
|
|
172
172
|
<InsightsSettingsPanel />
|
|
173
173
|
</section>
|
|
174
174
|
|
|
175
|
-
<section className="space-y-
|
|
176
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
177
|
-
<LuActivity className="h-
|
|
175
|
+
<section className="space-y-2" data-testid="settings-section-telemetry">
|
|
176
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
177
|
+
<LuActivity className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
178
178
|
遥测(OTLP)
|
|
179
179
|
</h2>
|
|
180
180
|
<TelemetrySettingsPanel />
|
|
@@ -185,12 +185,12 @@ export function SettingsPage() {
|
|
|
185
185
|
{getPackSettingsPanels().map((panel) => (
|
|
186
186
|
<section
|
|
187
187
|
key={panel.panelId}
|
|
188
|
-
className="space-y-
|
|
188
|
+
className="space-y-2"
|
|
189
189
|
data-testid={`settings-section-${panel.panelId}`}
|
|
190
190
|
>
|
|
191
|
-
<h2 className="flex items-center gap-1.5 text-
|
|
191
|
+
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
192
192
|
{panel.Icon ? (
|
|
193
|
-
<panel.Icon className="h-
|
|
193
|
+
<panel.Icon className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
194
194
|
) : null}
|
|
195
195
|
{panel.title}
|
|
196
196
|
</h2>
|
package/src/styles/index.css
CHANGED
|
@@ -58,8 +58,8 @@ body {
|
|
|
58
58
|
margin: 0;
|
|
59
59
|
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
|
|
60
60
|
"Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
61
|
-
font-size:
|
|
62
|
-
line-height: 1.
|
|
61
|
+
font-size: 12px;
|
|
62
|
+
line-height: 1.45;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/*
|
|
@@ -87,6 +87,16 @@ body {
|
|
|
87
87
|
white-space: nowrap;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/* 12px 字号下收紧工具行:框架卡默认还是 px-3 py-2。 */
|
|
91
|
+
.steerable-tool-execution > button {
|
|
92
|
+
gap: 0.375rem;
|
|
93
|
+
padding: 0.25rem 0.625rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.steerable-tool-execution > button + div {
|
|
97
|
+
padding: 0.375rem 0.625rem;
|
|
98
|
+
}
|
|
99
|
+
|
|
90
100
|
/*
|
|
91
101
|
* ChatInput overlay: the contenteditable must keep a real text fill.
|
|
92
102
|
* macOS Pinyin inspects fill during the first letter's keydown — before
|