mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-09 17:19:02 +00:00
feat(web-shell): polish stats table layout and todo panel UI (#6559)
* feat(web-shell): polish stats table layout and todo panel UI - Use CSS grid for model usage table with fixed column widths - Add loading spinner for in_progress todo items - Add strikethrough for completed todo items - Introduce nested variant for PivotRow to show thoughts as output sub-item - Clarify i18n labels: stats.prompt -> Input Tokens, stats.output -> Output Tokens * fix(web-shell): address review suggestions for stats table and todo panel - Add prefers-reduced-motion media query for todo spinner (accessibility) - Right-align numeric columns in model usage table for magnitude comparison - Consolidate duplicate i18n keys (stats.prompt/output → stats.inputTokens/outputTokens) * fix(web-shell): address stats review feedback --------- Co-authored-by: ytahdn <ytahdn@gmail.com> Co-authored-by: 易良 <1204183885@qq.com>
This commit is contained in:
parent
8c896f6b09
commit
48e5d5d0d7
5 changed files with 69 additions and 20 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -247,11 +247,13 @@ function StatsOverview({ status }: { status: DaemonSessionStatsStatus }) {
|
|||
<div className={styles.spacer} />
|
||||
|
||||
{/* Header */}
|
||||
<div className={styles.tableRow}>
|
||||
<div className={`${styles.tableRow} ${styles.modelUsageRow}`}>
|
||||
<span className={styles.tableNameCol}>{t('stats.modelUsage')}</span>
|
||||
<span className={styles.tableNumCol}>{t('stats.reqs')}</span>
|
||||
<span className={styles.tableNumCol}>{t('stats.inputTokens')}</span>
|
||||
<span className={styles.tableNumCol}>
|
||||
<span className={styles.tableValueCol}>{t('stats.reqs')}</span>
|
||||
<span className={styles.tableValueCol}>
|
||||
{t('stats.inputTokens')}
|
||||
</span>
|
||||
<span className={styles.tableValueCol}>
|
||||
{t('stats.outputTokens')}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -259,15 +261,18 @@ function StatsOverview({ status }: { status: DaemonSessionStatsStatus }) {
|
|||
|
||||
{/* Rows */}
|
||||
{entries.map((e) => (
|
||||
<div key={e.key} className={styles.tableRow}>
|
||||
<div
|
||||
key={e.key}
|
||||
className={`${styles.tableRow} ${styles.modelUsageRow}`}
|
||||
>
|
||||
<span className={styles.tableNameCol}>{e.label}</span>
|
||||
<span className={styles.tableNumCol}>
|
||||
<span className={styles.tableValueCol}>
|
||||
{e.metrics.api.totalRequests}
|
||||
</span>
|
||||
<span className={`${styles.tableNumCol} ${styles.warning}`}>
|
||||
<span className={`${styles.tableValueCol} ${styles.warning}`}>
|
||||
{e.metrics.tokens.prompt.toLocaleString()}
|
||||
</span>
|
||||
<span className={`${styles.tableNumCol} ${styles.warning}`}>
|
||||
<span className={`${styles.tableValueCol} ${styles.warning}`}>
|
||||
{e.metrics.tokens.candidates.toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -366,7 +371,7 @@ function ModelStatsCard({ status }: { status: DaemonSessionStatsStatus }) {
|
|||
))}
|
||||
/>
|
||||
<PivotRow
|
||||
metric={t('stats.prompt')}
|
||||
metric={t('stats.inputTokens')}
|
||||
values={vals((m) => m.tokens.prompt.toLocaleString())}
|
||||
variant="sub"
|
||||
/>
|
||||
|
|
@ -382,6 +387,11 @@ function ModelStatsCard({ status }: { status: DaemonSessionStatsStatus }) {
|
|||
variant="sub"
|
||||
/>
|
||||
)}
|
||||
<PivotRow
|
||||
metric={t('stats.outputTokens')}
|
||||
values={vals((m) => m.tokens.candidates.toLocaleString())}
|
||||
variant="sub"
|
||||
/>
|
||||
{hasThoughts && (
|
||||
<PivotRow
|
||||
metric={t('stats.thoughts')}
|
||||
|
|
@ -389,11 +399,6 @@ function ModelStatsCard({ status }: { status: DaemonSessionStatsStatus }) {
|
|||
variant="sub"
|
||||
/>
|
||||
)}
|
||||
<PivotRow
|
||||
metric={t('stats.output')}
|
||||
values={vals((m) => m.tokens.candidates.toLocaleString())}
|
||||
variant="sub"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,11 @@ export const TodoPanel = memo(function TodoPanel({
|
|||
className={`${styles.item} ${getStatusClass(todo.status)}`}
|
||||
>
|
||||
<span className={styles.icon} aria-hidden="true">
|
||||
{getTodoStatusIcon(todo.status)}
|
||||
{todo.status === 'in_progress' ? (
|
||||
<span className={styles.loadingIcon} />
|
||||
) : (
|
||||
getTodoStatusIcon(todo.status)
|
||||
)}
|
||||
</span>
|
||||
<span className={styles.content} title={todo.content}>
|
||||
{todo.content}
|
||||
|
|
|
|||
|
|
@ -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': '请求',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue