@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
|
@@ -117,7 +117,7 @@ export function ModelPicker({
|
|
|
117
117
|
if (next && status !== 'live' && !loading) void refresh();
|
|
118
118
|
}}
|
|
119
119
|
disabled={disabled}
|
|
120
|
-
className="inline-flex h-
|
|
120
|
+
className="inline-flex h-6 min-w-0 max-w-[160px] items-center gap-1 rounded-full border border-agent-border bg-agent-canvas px-1.5 text-[12px] leading-[1.45] text-agent-foreground transition-colors hover:bg-agent-foreground/5 disabled:cursor-not-allowed disabled:opacity-70 @sm:max-w-[220px]"
|
|
121
121
|
title={
|
|
122
122
|
status === 'offline'
|
|
123
123
|
? `模型目录不可用${catalog?.error ? `:${catalog.error}` : ''}(仍可手动指定模型)`
|
|
@@ -165,7 +165,7 @@ export function ModelPicker({
|
|
|
165
165
|
setModelMenuOpen(false);
|
|
166
166
|
}}
|
|
167
167
|
className={[
|
|
168
|
-
'flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-
|
|
168
|
+
'flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-[12px] leading-[1.45] transition-colors',
|
|
169
169
|
model == null
|
|
170
170
|
? 'bg-agent-foreground/10 text-agent-foreground'
|
|
171
171
|
: 'text-agent-muted-foreground hover:bg-agent-foreground/5 hover:text-agent-foreground',
|
|
@@ -192,7 +192,7 @@ export function ModelPicker({
|
|
|
192
192
|
setModelMenuOpen(false);
|
|
193
193
|
}}
|
|
194
194
|
className={[
|
|
195
|
-
'flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-
|
|
195
|
+
'flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-[12px] leading-[1.45] transition-colors',
|
|
196
196
|
isActive
|
|
197
197
|
? 'bg-agent-foreground/10 text-agent-foreground'
|
|
198
198
|
: 'text-agent-muted-foreground hover:bg-agent-foreground/5 hover:text-agent-foreground',
|
|
@@ -232,7 +232,7 @@ export function ModelPicker({
|
|
|
232
232
|
setModelMenuOpen(false);
|
|
233
233
|
}}
|
|
234
234
|
disabled={disabled}
|
|
235
|
-
className="inline-flex h-
|
|
235
|
+
className="inline-flex h-6 items-center gap-1 rounded-full border border-agent-border bg-agent-canvas px-1.5 text-[12px] leading-[1.45] text-agent-foreground transition-colors hover:bg-agent-foreground/5 disabled:cursor-not-allowed disabled:opacity-70"
|
|
236
236
|
title="推理档位(随每轮请求下发;模型不支持会直接报错)"
|
|
237
237
|
aria-haspopup="menu"
|
|
238
238
|
aria-expanded={effortMenuOpen}
|
|
@@ -253,7 +253,7 @@ export function ModelPicker({
|
|
|
253
253
|
setEffortMenuOpen(false);
|
|
254
254
|
}}
|
|
255
255
|
className={[
|
|
256
|
-
'flex w-full rounded px-2 py-1.5 text-left text-
|
|
256
|
+
'flex w-full rounded px-2 py-1.5 text-left text-[12px] leading-[1.45] transition-colors',
|
|
257
257
|
reasoningEffort == null
|
|
258
258
|
? 'bg-agent-foreground/10 text-agent-foreground'
|
|
259
259
|
: 'text-agent-muted-foreground hover:bg-agent-foreground/5 hover:text-agent-foreground',
|
|
@@ -271,7 +271,7 @@ export function ModelPicker({
|
|
|
271
271
|
setEffortMenuOpen(false);
|
|
272
272
|
}}
|
|
273
273
|
className={[
|
|
274
|
-
'flex w-full rounded px-2 py-1.5 text-left text-
|
|
274
|
+
'flex w-full rounded px-2 py-1.5 text-left text-[12px] leading-[1.45] transition-colors',
|
|
275
275
|
reasoningEffort === level
|
|
276
276
|
? 'bg-agent-foreground/10 text-agent-foreground'
|
|
277
277
|
: 'text-agent-muted-foreground hover:bg-agent-foreground/5 hover:text-agent-foreground',
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { useMemo, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
LuCircle,
|
|
4
|
+
LuCircleCheck,
|
|
5
|
+
LuCircleDot,
|
|
6
|
+
LuChevronDown,
|
|
7
|
+
LuChevronRight,
|
|
8
|
+
LuListChecks,
|
|
9
|
+
} from 'react-icons/lu';
|
|
10
|
+
import {
|
|
11
|
+
summarizeTodos,
|
|
12
|
+
type SessionTodo,
|
|
13
|
+
type TodoStatus,
|
|
14
|
+
} from './todo-list-model';
|
|
15
|
+
|
|
16
|
+
function StatusIcon({
|
|
17
|
+
status,
|
|
18
|
+
className,
|
|
19
|
+
}: {
|
|
20
|
+
status: TodoStatus;
|
|
21
|
+
className?: string;
|
|
22
|
+
}) {
|
|
23
|
+
const cls = ['h-3.5 w-3.5 shrink-0', className].filter(Boolean).join(' ');
|
|
24
|
+
if (status === 'completed') {
|
|
25
|
+
return <LuCircleCheck className={`${cls} text-emerald-600 dark:text-emerald-400`} />;
|
|
26
|
+
}
|
|
27
|
+
if (status === 'in_progress') {
|
|
28
|
+
return <LuCircleDot className={`${cls} text-blue-600 dark:text-blue-400`} />;
|
|
29
|
+
}
|
|
30
|
+
return <LuCircle className={`${cls} text-agent-muted-foreground/70`} />;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function currentTodoStep(todos: SessionTodo[]): SessionTodo | null {
|
|
34
|
+
return (
|
|
35
|
+
todos.find((todo) => todo.status === 'in_progress') ??
|
|
36
|
+
todos.find((todo) => todo.status === 'pending') ??
|
|
37
|
+
todos[todos.length - 1] ??
|
|
38
|
+
null
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function todoProgressCopy(todos: SessionTodo[]): string {
|
|
43
|
+
const summary = summarizeTodos(todos);
|
|
44
|
+
if (summary.total === 0) return '';
|
|
45
|
+
if (summary.completed === summary.total) {
|
|
46
|
+
return `${summary.completed}/${summary.total} 已完成`;
|
|
47
|
+
}
|
|
48
|
+
const currentIndex = todos.findIndex((todo) => todo.status === 'in_progress');
|
|
49
|
+
const step =
|
|
50
|
+
currentIndex >= 0 ? currentIndex + 1 : Math.min(summary.completed + 1, summary.total);
|
|
51
|
+
return `执行到 ${step}/${summary.total}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function TodoItems({
|
|
55
|
+
todos,
|
|
56
|
+
compact = false,
|
|
57
|
+
}: {
|
|
58
|
+
todos: SessionTodo[];
|
|
59
|
+
compact?: boolean;
|
|
60
|
+
}) {
|
|
61
|
+
return (
|
|
62
|
+
<ol
|
|
63
|
+
className={`m-0 list-none ${compact ? 'max-h-40 space-y-1 overflow-y-auto px-2.5 py-1.5' : 'space-y-1 px-2.5 py-1.5'}`}
|
|
64
|
+
>
|
|
65
|
+
{todos.map((todo) => {
|
|
66
|
+
const done = todo.status === 'completed';
|
|
67
|
+
const active = todo.status === 'in_progress';
|
|
68
|
+
return (
|
|
69
|
+
<li
|
|
70
|
+
key={todo.id}
|
|
71
|
+
data-testid={`todo-item-${todo.id}`}
|
|
72
|
+
data-status={todo.status}
|
|
73
|
+
className="flex items-start gap-2 text-[12px] leading-relaxed"
|
|
74
|
+
>
|
|
75
|
+
<StatusIcon status={todo.status} className="mt-[2px]" />
|
|
76
|
+
<span
|
|
77
|
+
className={
|
|
78
|
+
done
|
|
79
|
+
? 'text-agent-muted-foreground line-through'
|
|
80
|
+
: active
|
|
81
|
+
? 'font-medium text-agent-foreground'
|
|
82
|
+
: 'text-agent-foreground'
|
|
83
|
+
}
|
|
84
|
+
>
|
|
85
|
+
{todo.content}
|
|
86
|
+
</span>
|
|
87
|
+
</li>
|
|
88
|
+
);
|
|
89
|
+
})}
|
|
90
|
+
</ol>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function SessionTodoList({ todos }: { todos: SessionTodo[] }) {
|
|
95
|
+
const [expanded, setExpanded] = useState(false);
|
|
96
|
+
const current = useMemo(() => currentTodoStep(todos), [todos]);
|
|
97
|
+
const progressLabel = useMemo(() => todoProgressCopy(todos), [todos]);
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div
|
|
101
|
+
className="mb-1 overflow-hidden rounded-agent-lg border border-agent-border bg-agent-canvas shadow-sm"
|
|
102
|
+
data-testid="session-todo-list"
|
|
103
|
+
>
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
onClick={() => setExpanded((value) => !value)}
|
|
107
|
+
className="flex w-full items-center gap-1.5 px-2.5 py-1.5 text-left"
|
|
108
|
+
aria-expanded={expanded}
|
|
109
|
+
>
|
|
110
|
+
{expanded ? (
|
|
111
|
+
<LuChevronDown className="h-3.5 w-3.5 shrink-0 text-agent-muted-foreground" />
|
|
112
|
+
) : (
|
|
113
|
+
<LuChevronRight className="h-3.5 w-3.5 shrink-0 text-agent-muted-foreground" />
|
|
114
|
+
)}
|
|
115
|
+
{expanded || !current ? (
|
|
116
|
+
<LuListChecks className="h-3.5 w-3.5 shrink-0 text-agent-foreground" />
|
|
117
|
+
) : (
|
|
118
|
+
<StatusIcon status={current.status} />
|
|
119
|
+
)}
|
|
120
|
+
<span className="min-w-0 flex-1 truncate text-[12px] font-medium text-agent-foreground">
|
|
121
|
+
{expanded ? '任务清单' : (current?.content ?? '任务清单')}
|
|
122
|
+
</span>
|
|
123
|
+
{progressLabel ? (
|
|
124
|
+
<span
|
|
125
|
+
className="shrink-0 rounded-full bg-agent-muted px-1.5 py-0.5 text-[10px] font-medium text-agent-muted-foreground"
|
|
126
|
+
data-testid="todo-progress"
|
|
127
|
+
>
|
|
128
|
+
{progressLabel}
|
|
129
|
+
</span>
|
|
130
|
+
) : null}
|
|
131
|
+
</button>
|
|
132
|
+
{expanded ? <TodoItems todos={todos} compact /> : null}
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default SessionTodoList;
|
|
@@ -136,9 +136,9 @@ export function SessionTreeModal({ chatId, onClose, onBranchSwitched }: SessionT
|
|
|
136
136
|
data-session-tree-modal
|
|
137
137
|
>
|
|
138
138
|
<div className="flex max-h-[85vh] w-full max-w-lg flex-col overflow-hidden rounded-agent-lg border border-agent-border bg-agent-canvas shadow-xl">
|
|
139
|
-
<div className="flex items-center gap-2 border-b border-agent-border px-
|
|
139
|
+
<div className="flex items-center gap-2 border-b border-agent-border px-3 py-2">
|
|
140
140
|
<LuGitBranch className="h-4 w-4 shrink-0 text-agent-muted-foreground" />
|
|
141
|
-
<span className="min-w-0 flex-1 truncate text-
|
|
141
|
+
<span className="min-w-0 flex-1 truncate text-xs font-medium text-agent-foreground">
|
|
142
142
|
会话分支树
|
|
143
143
|
{data && data.nodeCount > 1 && (
|
|
144
144
|
<span className="ml-1.5 text-xs font-normal text-agent-muted-foreground">
|
|
@@ -206,7 +206,7 @@ export function SessionTreeModal({ chatId, onClose, onBranchSwitched }: SessionT
|
|
|
206
206
|
</div>
|
|
207
207
|
|
|
208
208
|
{!loading && !isEmpty && (
|
|
209
|
-
<div className="border-t border-agent-border px-
|
|
209
|
+
<div className="border-t border-agent-border px-3 py-1.5 text-[10px] text-agent-muted-foreground/80">
|
|
210
210
|
{data?.truncated
|
|
211
211
|
? '分支过多,树已被截断(仅显示部分节点)。'
|
|
212
212
|
: '点击切换分支;↑↓ 移动,Enter 切换,Esc 关闭。'}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WorkBuddy-style follow-up chips: next-turn user inputs rendered under the
|
|
3
|
-
* latest assistant reply.
|
|
4
|
-
*
|
|
3
|
+
* latest assistant reply. Source is the model's judgment of `[next_steps]`
|
|
4
|
+
* or the last paragraph — count follows that, not a fixed three. Clicking
|
|
5
|
+
* one sends that text immediately.
|
|
5
6
|
*/
|
|
6
7
|
export function SuggestedReplies({
|
|
7
8
|
suggestions,
|
|
@@ -14,7 +15,7 @@ export function SuggestedReplies({
|
|
|
14
15
|
|
|
15
16
|
return (
|
|
16
17
|
<div
|
|
17
|
-
className="mx-auto flex w-full max-w-[var(--chat-input-box-width)] flex-col items-start gap-1
|
|
18
|
+
className="mx-auto flex w-full max-w-[var(--chat-input-box-width)] flex-col items-start gap-1 px-1 pt-0.5 text-[12px]"
|
|
18
19
|
data-testid="suggested-replies"
|
|
19
20
|
>
|
|
20
21
|
{suggestions.map((text) => (
|
|
@@ -23,7 +24,7 @@ export function SuggestedReplies({
|
|
|
23
24
|
type="button"
|
|
24
25
|
data-testid="suggested-reply"
|
|
25
26
|
onClick={() => onSelect(text)}
|
|
26
|
-
className="max-w-full rounded-full border border-agent-border bg-agent-muted/40 px-
|
|
27
|
+
className="max-w-full rounded-full border border-agent-border bg-agent-muted/40 px-2 py-0.5 text-left text-[12px] leading-[1.45] text-agent-muted-foreground transition-colors hover:bg-agent-foreground/5 hover:text-agent-foreground"
|
|
27
28
|
>
|
|
28
29
|
{text}
|
|
29
30
|
</button>
|
|
@@ -135,9 +135,9 @@ export function TaskPanelModal({
|
|
|
135
135
|
data-task-panel-modal
|
|
136
136
|
>
|
|
137
137
|
<div className="flex max-h-[85vh] w-full max-w-xl flex-col overflow-hidden rounded-agent-lg border border-agent-border bg-agent-canvas shadow-xl">
|
|
138
|
-
<div className="flex items-center gap-2 border-b border-agent-border px-
|
|
138
|
+
<div className="flex items-center gap-2 border-b border-agent-border px-3 py-2">
|
|
139
139
|
<LuListTodo className="h-4 w-4 shrink-0 text-agent-muted-foreground" />
|
|
140
|
-
<span className="min-w-0 flex-1 truncate text-
|
|
140
|
+
<span className="min-w-0 flex-1 truncate text-xs font-medium text-agent-foreground">
|
|
141
141
|
后台任务
|
|
142
142
|
{tasks && tasks.length > 0 && (
|
|
143
143
|
<span className="ml-1.5 text-xs font-normal text-agent-muted-foreground">
|
|
@@ -298,7 +298,7 @@ export function TaskPanelModal({
|
|
|
298
298
|
)}
|
|
299
299
|
</div>
|
|
300
300
|
|
|
301
|
-
<div className="border-t border-agent-border px-
|
|
301
|
+
<div className="border-t border-agent-border px-3 py-1.5 text-[10px] text-agent-muted-foreground/80">
|
|
302
302
|
任务由后台智能体独立执行。点任务名可在右侧看推理过程;Esc 关闭。
|
|
303
303
|
</div>
|
|
304
304
|
</div>
|
|
@@ -35,16 +35,18 @@ export function TaskProcessPanel({
|
|
|
35
35
|
const [error, setError] = useState<string | null>(null);
|
|
36
36
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
37
37
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
38
|
+
const isAtBottomRef = useRef(true);
|
|
38
39
|
|
|
39
40
|
const checkAtBottom = useCallback(() => {
|
|
40
41
|
const el = containerRef.current;
|
|
41
42
|
if (!el) return;
|
|
42
43
|
const atBottom =
|
|
43
44
|
el.scrollHeight - el.scrollTop - el.clientHeight < NEAR_BOTTOM_THRESHOLD_PX;
|
|
45
|
+
isAtBottomRef.current = atBottom;
|
|
44
46
|
setIsAtBottom(atBottom);
|
|
45
47
|
}, []);
|
|
46
48
|
|
|
47
|
-
const scrollToBottom = useCallback((behavior: ScrollBehavior = '
|
|
49
|
+
const scrollToBottom = useCallback((behavior: ScrollBehavior = 'auto') => {
|
|
48
50
|
const el = containerRef.current;
|
|
49
51
|
if (!el) return;
|
|
50
52
|
if (behavior === 'smooth') {
|
|
@@ -65,20 +67,22 @@ export function TaskProcessPanel({
|
|
|
65
67
|
|
|
66
68
|
// 换任务 / 首屏:没有「之前在不在底部」可言,直接钉到底。
|
|
67
69
|
useLayoutEffect(() => {
|
|
68
|
-
|
|
70
|
+
isAtBottomRef.current = true;
|
|
71
|
+
scrollToBottom('auto');
|
|
69
72
|
setIsAtBottom(true);
|
|
70
73
|
}, [inspected.id, scrollToBottom]);
|
|
71
74
|
|
|
72
|
-
// live
|
|
75
|
+
// live 增量在 paint 前钉住底部。smooth scroll 会先露出一帧半截内容再往下
|
|
76
|
+
// 滑,Windows 经典滚动条就会上下跳。
|
|
73
77
|
const timelineSig = blocks
|
|
74
78
|
.map((block) =>
|
|
75
79
|
block.type === 'tools' ? `t${block.actions.length}` : `c${block.content.length}`,
|
|
76
80
|
)
|
|
77
81
|
.join('|');
|
|
78
|
-
|
|
79
|
-
if (!
|
|
80
|
-
scrollToBottom('
|
|
81
|
-
}, [timelineSig, live,
|
|
82
|
+
useLayoutEffect(() => {
|
|
83
|
+
if (!isAtBottomRef.current) return;
|
|
84
|
+
scrollToBottom('auto');
|
|
85
|
+
}, [timelineSig, live, scrollToBottom]);
|
|
82
86
|
|
|
83
87
|
useEffect(() => {
|
|
84
88
|
let cancelled = false;
|
|
@@ -145,7 +149,7 @@ export function TaskProcessPanel({
|
|
|
145
149
|
)
|
|
146
150
|
}
|
|
147
151
|
/>
|
|
148
|
-
<div className="border-b border-agent-border/60 px-
|
|
152
|
+
<div className="border-b border-agent-border/60 px-2.5 py-1.5">
|
|
149
153
|
<div className="line-clamp-3 text-xs leading-relaxed text-agent-foreground">
|
|
150
154
|
{inspected.title}
|
|
151
155
|
</div>
|
|
@@ -153,7 +157,7 @@ export function TaskProcessPanel({
|
|
|
153
157
|
<div className="relative min-h-0 flex-1 overflow-hidden">
|
|
154
158
|
<div
|
|
155
159
|
ref={containerRef}
|
|
156
|
-
className="h-full overflow-y-auto p-
|
|
160
|
+
className="h-full overflow-y-auto overflow-anchor-none p-2.5"
|
|
157
161
|
data-testid="task-process-scroll"
|
|
158
162
|
>
|
|
159
163
|
{loading ? (
|
|
@@ -168,6 +172,7 @@ export function TaskProcessPanel({
|
|
|
168
172
|
blocks={blocks}
|
|
169
173
|
isStreaming={live}
|
|
170
174
|
showThinkingContent
|
|
175
|
+
collapseWhenFinished={false}
|
|
171
176
|
agents={[]}
|
|
172
177
|
chats={[]}
|
|
173
178
|
emptyFallback={
|
|
@@ -180,7 +185,7 @@ export function TaskProcessPanel({
|
|
|
180
185
|
</div>
|
|
181
186
|
}
|
|
182
187
|
renderAnswer={(block) => (
|
|
183
|
-
<div className="text-
|
|
188
|
+
<div className="text-xs leading-relaxed text-agent-foreground">
|
|
184
189
|
<Markdown agents={[]} chats={[]}>{block.content}</Markdown>
|
|
185
190
|
</div>
|
|
186
191
|
)}
|
|
@@ -191,10 +196,11 @@ export function TaskProcessPanel({
|
|
|
191
196
|
<button
|
|
192
197
|
type="button"
|
|
193
198
|
onClick={() => {
|
|
199
|
+
isAtBottomRef.current = true;
|
|
194
200
|
setIsAtBottom(true);
|
|
195
201
|
scrollToBottom('smooth');
|
|
196
202
|
}}
|
|
197
|
-
className="absolute bottom-
|
|
203
|
+
className="absolute bottom-2 right-2 flex h-7 w-7 items-center justify-center rounded-full border border-agent-border bg-agent-canvas text-agent-foreground shadow-md transition-colors hover:bg-agent-foreground/5"
|
|
198
204
|
title="回到底部"
|
|
199
205
|
aria-label="回到底部"
|
|
200
206
|
>
|
|
@@ -57,7 +57,7 @@ export function TurnFilesCard({ files }: TurnFilesCardProps) {
|
|
|
57
57
|
className="rounded-agent-lg border border-agent-border bg-agent-canvas shadow-sm"
|
|
58
58
|
data-turn-files=""
|
|
59
59
|
>
|
|
60
|
-
<div className="flex items-center gap-1.5 border-b border-agent-border/60 px-
|
|
60
|
+
<div className="flex items-center gap-1.5 border-b border-agent-border/60 px-2.5 py-1 text-[11px] text-agent-muted-foreground">
|
|
61
61
|
<LuFiles className="h-3.5 w-3.5" />
|
|
62
62
|
<span>本轮产生了 {files.length} 个文件,点击打开</span>
|
|
63
63
|
</div>
|
|
@@ -74,7 +74,7 @@ export function TurnFilesCard({ files }: TurnFilesCardProps) {
|
|
|
74
74
|
onClick={() => void handleOpen(file)}
|
|
75
75
|
disabled={openingPath !== null}
|
|
76
76
|
title={file.path}
|
|
77
|
-
className="flex w-full items-center gap-2 px-
|
|
77
|
+
className="flex w-full items-center gap-2 px-2.5 py-1 text-left text-xs transition-colors hover:bg-agent-foreground/5 disabled:cursor-wait"
|
|
78
78
|
data-turn-file=""
|
|
79
79
|
data-kind={file.kind}
|
|
80
80
|
>
|