diff --git a/packages/web-shell/client/components/messages/StatsMessage.module.css b/packages/web-shell/client/components/messages/StatsMessage.module.css
index d32e5092fa..b515e37175 100644
--- a/packages/web-shell/client/components/messages/StatsMessage.module.css
+++ b/packages/web-shell/client/components/messages/StatsMessage.module.css
@@ -118,6 +118,14 @@
min-height: 20px;
}
+.modelUsageRow {
+ display: grid;
+ grid-template-columns:
+ minmax(14ch, 1.4fr)
+ repeat(3, minmax(9ch, 1fr));
+ column-gap: 16px;
+}
+
.tableNameCol {
color: var(--foreground);
font-weight: 700;
@@ -142,6 +150,13 @@
white-space: nowrap;
}
+.tableValueCol {
+ min-width: 9ch;
+ color: var(--foreground);
+ text-align: right;
+ white-space: nowrap;
+}
+
/* ---------- Semantic colors ---------- */
.success {
diff --git a/packages/web-shell/client/components/messages/StatsMessage.tsx b/packages/web-shell/client/components/messages/StatsMessage.tsx
index d08cc9b295..fbc0132402 100644
--- a/packages/web-shell/client/components/messages/StatsMessage.tsx
+++ b/packages/web-shell/client/components/messages/StatsMessage.tsx
@@ -247,11 +247,13 @@ function StatsOverview({ status }: { status: DaemonSessionStatsStatus }) {
+
{t('stats.modelUsage')}
- {t('stats.reqs')}
- {t('stats.inputTokens')}
-
+ {t('stats.reqs')}
+
+ {t('stats.inputTokens')}
+
+
{t('stats.outputTokens')}
@@ -259,15 +261,18 @@ function StatsOverview({ status }: { status: DaemonSessionStatsStatus }) {
{/* Rows */}
{entries.map((e) => (
-
+
{e.label}
-
+
{e.metrics.api.totalRequests}
-
+
{e.metrics.tokens.prompt.toLocaleString()}
-
+
{e.metrics.tokens.candidates.toLocaleString()}
@@ -366,7 +371,7 @@ function ModelStatsCard({ status }: { status: DaemonSessionStatsStatus }) {
))}
/>
m.tokens.prompt.toLocaleString())}
variant="sub"
/>
@@ -382,6 +387,11 @@ function ModelStatsCard({ status }: { status: DaemonSessionStatsStatus }) {
variant="sub"
/>
)}
+ m.tokens.candidates.toLocaleString())}
+ variant="sub"
+ />
{hasThoughts && (
)}
- m.tokens.candidates.toLocaleString())}
- variant="sub"
- />
);
}
diff --git a/packages/web-shell/client/components/panels/TodoPanel.module.css b/packages/web-shell/client/components/panels/TodoPanel.module.css
index 73a28ce119..5cf178eead 100644
--- a/packages/web-shell/client/components/panels/TodoPanel.module.css
+++ b/packages/web-shell/client/components/panels/TodoPanel.module.css
@@ -118,6 +118,15 @@
font-size: 13px;
}
+.loadingIcon {
+ width: 12px;
+ height: 12px;
+ border: 2px solid color-mix(in srgb, var(--agent-blue-500) 24%, transparent);
+ border-top-color: var(--agent-blue-500);
+ border-radius: 50%;
+ animation: todoPanelSpin 0.8s linear infinite;
+}
+
.content {
min-width: 0;
overflow: hidden;
@@ -131,13 +140,33 @@
color: var(--muted-foreground);
}
+.completed .content {
+ text-decoration: line-through;
+ text-decoration-color: color-mix(
+ in srgb,
+ var(--muted-foreground) 72%,
+ transparent
+ );
+}
+
.inProgress .icon,
.inProgress .content {
color: var(--foreground);
- font-weight: 600;
}
.pending .icon,
.pending .content {
color: var(--muted-foreground);
}
+
+@keyframes todoPanelSpin {
+ to {
+ transform: rotate(1turn);
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .loadingIcon {
+ animation: none;
+ }
+}
diff --git a/packages/web-shell/client/components/panels/TodoPanel.tsx b/packages/web-shell/client/components/panels/TodoPanel.tsx
index dba61863c0..e46a4d7114 100644
--- a/packages/web-shell/client/components/panels/TodoPanel.tsx
+++ b/packages/web-shell/client/components/panels/TodoPanel.tsx
@@ -72,7 +72,11 @@ export const TodoPanel = memo(function TodoPanel({
className={`${styles.item} ${getStatusClass(todo.status)}`}
>
- {getTodoStatusIcon(todo.status)}
+ {todo.status === 'in_progress' ? (
+
+ ) : (
+ getTodoStatusIcon(todo.status)
+ )}
{todo.content}
diff --git a/packages/web-shell/client/i18n.tsx b/packages/web-shell/client/i18n.tsx
index 68bb6e6d50..cd11058d09 100644
--- a/packages/web-shell/client/i18n.tsx
+++ b/packages/web-shell/client/i18n.tsx
@@ -1431,11 +1431,9 @@ const EN: Messages = {
'stats.modified': 'Modified:',
'stats.noApiCalls': 'No API calls have been made in this session.',
'stats.noToolCalls': 'No tool calls have been made in this session.',
- 'stats.output': 'Output',
'stats.outputTokens': 'Output Tokens',
'stats.overview': 'Session Overview',
'stats.performance': 'Performance',
- 'stats.prompt': 'Prompt',
'stats.prompts': 'Prompts',
'stats.rejected': 'Rejected:',
'stats.reqs': 'Reqs',
@@ -3073,11 +3071,9 @@ const ZH: Messages = {
'stats.modified': '已修改:',
'stats.noApiCalls': '本次会话暂无 API 调用。',
'stats.noToolCalls': '本次会话暂无工具调用。',
- 'stats.output': '输出',
'stats.outputTokens': '输出 Token',
'stats.overview': '会话概览',
'stats.performance': '性能',
- 'stats.prompt': '提示',
'stats.prompts': '对话轮次',
'stats.rejected': '已拒绝:',
'stats.reqs': '请求',