fix(agents): one spinner per conversation, counts on one line

Work in progress shimmers on its label (a run's counts and current call, a
running tool, "Thinking") instead of showing a spinner beside it; the working
row at the conversation's end is the only spinner. A run's counts keep their
line, and what runs now ellipsizes in the space left.
This commit is contained in:
l0ng-ai
2026-11-11 15:32:40 +08:00
parent 9474af4de1
commit 18b286e7e0
2 changed files with 42 additions and 12 deletions
+34
View File
@@ -685,8 +685,17 @@
min-width: 0;
display: none;
}
/* The counts keep their line; what runs now gives way and ellipsizes. */
.processParts {
flex: 0 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* What the run is doing now, after its counts. */
.processCurrent {
flex: 1 1 0;
min-width: 0;
overflow: hidden;
color: var(--fg-subtle);
@@ -1559,3 +1568,28 @@
.planCurrent {
color: var(--fg);
}
/* Work in progress: a light sweeps across its label. The conversation's one spinner is the
working row at its end. */
.shimmer {
background: linear-gradient(90deg, var(--fg-muted) 40%, var(--fg) 50%, var(--fg-muted) 60%) 0 0 / 300% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: shimmer 2s linear infinite;
}
@keyframes shimmer {
from {
background-position: 100% 0;
}
to {
background-position: 0 0;
}
}
@media (prefers-reduced-motion: reduce) {
.shimmer {
animation: none;
background: none;
color: var(--fg-muted);
}
}
+8 -12
View File
@@ -1194,13 +1194,11 @@ function ThoughtEntry({
open={open}
summary={
<summary>
<Icon name="chevron-right" size={12} className={styles.thoughtChevron} />
{working ? (
<Spinner size={12} />
) : (
<Icon name="chevron-right" size={12} className={styles.thoughtChevron} />
)}
{working ? (
<T>Thinking</T>
<span className={styles.shimmer}>
<T>Thinking</T>
</span>
) : (
<span className={styles.thoughtPreview}>{thoughtPreview(text) || <T>Reasoning</T>}</span>
)}
@@ -1643,7 +1641,7 @@ function TurnFold({
summary={
<summary className={styles.processSummary}>
<Icon name="chevron-right" size={12} className={styles.processChevron} />
<span>{parts.join(" · ")}</span>
<span className={styles.processParts}>{parts.join(" · ")}</span>
{work.added > 0 && <span className={styles.added}>+{work.added}</span>}
{work.removed > 0 && <span className={styles.removed}>−{work.removed}</span>}
{took && <span className={styles.muted}>{took}</span>}
@@ -1698,11 +1696,10 @@ const ProcessRun = memo(function ProcessRun({
summary={
<summary className={styles.processSummary}>
<Icon name="chevron-right" size={12} className={styles.processChevron} />
{(working || live) && <Spinner size={12} />}
<span>{parts.join(" · ")}</span>
<span className={clsx(styles.processParts, (working || live) && styles.shimmer)}>{parts.join(" · ")}</span>
{added > 0 && <span className={styles.added}>+{added}</span>}
{removed > 0 && <span className={styles.removed}>−{removed}</span>}
{current && <span className={styles.processCurrent}>{current}</span>}
{current && <span className={clsx(styles.processCurrent, styles.shimmer)}>{current}</span>}
</summary>
}
>
@@ -1771,7 +1768,7 @@ const ToolEntry = memo(function ToolEntry({
const head = (
<>
<Icon name={TOOL_ICONS[entry.kind] ?? "run"} size={14} />
<span className={styles.toolTitle} title={entry.title || entry.kind}>
<span className={clsx(styles.toolTitle, liveTool(entry) && styles.shimmer)} title={entry.title || entry.kind}>
{shortTitle(task, entry.title || entry.kind)}
</span>
{entry.diffs.map((d) => (
@@ -1788,7 +1785,6 @@ const ToolEntry = memo(function ToolEntry({
<span className={styles.added}>+{d.added}</span> <span className={styles.removed}>−{d.removed}</span>
</button>
))}
{(entry.status === "running" || entry.status === "pending") && <Spinner size={12} />}
{entry.status === "failed" && <Icon name="error" size={12} />}
</>
);