chore(ui): refine composer status coexistence

This commit is contained in:
vyctorbrzezowski
2026-08-22 07:33:52 -03:00
parent 46ddec7c8c
commit dfd3e7a3e2
8 changed files with 75 additions and 13 deletions
+5
View File
@@ -223,3 +223,8 @@
2026-08-21 22:46 · Optically aligned the active Plan label with its 2/3 counter and kept the canonical spinner running across the entire hover preview · ui/src/styles/chat/progress-card.css, ITERATION-NOTES.md · COMPONENT #124301 multiline | BENCH
2026-08-21 22:55 · Made Plan use the real spinner ring at rest and animate it only on hover, then replace it with a check, the truncated final step, and 3/3 when complete · ui/src/components/session-progress-card.ts, ui/src/styles/chat/progress-card.css, ITERATION-NOTES.md · COMPONENT #124301 multiline | BENCH
2026-08-21 22:56 · Kept the active Plan spinner rotating continuously instead of gating its motion on hover · ui/src/styles/chat/progress-card.css, ITERATION-NOTES.md · COMPONENT #124301 multiline | BENCH
2026-08-22 00:03 · Kept bottom error and status underlaps behind the composer so Interrupted no longer paints across the footer or send action · ui/src/styles/chat/layout.css, ITERATION-NOTES.md · COMPONENT #124301 multiline | BENCH
2026-08-22 07:33 · Normalized every in-scope Above composer neighbor to the bench width and flow instead of inheriting transcript-only offsets · ui/src/test-helpers/composer-bench.css · COMPONENT #124301 multiline | BENCH
2026-08-22 07:33 · Unified info, danger and offline underlap rows at a vertically centered height with quieter borders · ui/src/styles/chat/layout.css · COMPONENT #124301 multiline | BENCH
2026-08-22 07:33 · Projected the canonical reconnect badge onto every queued row while the composer is offline · ui/src/pages/chat/components/chat-composer-view.ts, ui/src/pages/chat/components/chat-composer-queue.ts · COMPONENT #124301 multiline | BENCH
2026-08-22 07:33 · Matched running tasks to composer and Plan chrome and aligned both on one shared status row when they coexist · ui/src/styles/chat/tool-cards.css, ui/src/test-helpers/composer-bench.ts, ui/src/test-helpers/composer-bench.css · COMPONENT #124301 multiline | BENCH
+5
View File
@@ -114,6 +114,11 @@ Local-only working ledger. Re-read after every implementation block; move an ite
- [x] Align the Plan step label and 2/3 counter, and keep its canonical spinner active across the full hover preview.
- [x] Keep the active Plan spinner visible but paused until hover; replace it with a check and the final step plus 3/3 when complete.
- [x] Keep the active Plan spinner rotating continuously, independent of hover.
- [x] Keep bottom error and status underlaps behind the composer footer and send action.
- [x] Normalize all in-scope Above composer neighbors to the real composer width and flow.
- [x] Vertically center every lower composer status band and soften info, danger, and offline borders.
- [x] Show the canonical reconnect pill on every queued row while offline.
- [x] Match running tasks to Plan/composer chrome and share one horizontal status row when both appear.
## Observed queue contract
@@ -13,6 +13,7 @@ import { renderChatAuthorAvatar } from "./chat-author-avatar.ts";
type ChatQueueProps = {
queue: ChatQueueItem[];
offline?: boolean;
canAbort?: boolean;
onQueueRetry?: (id: string) => void;
onQueueSteer?: (id: string) => void;
@@ -114,11 +115,13 @@ function renderChatQueueItem(
) {
const authorAvatar = renderChatAuthorAvatar(item.sender);
const hasAuthorAvatar = authorAvatar !== nothing;
const stateLabel = sendStateLabel(item);
const stateLabel = props.offline
? t("chat.queue.states.waitingForReconnect")
: sendStateLabel(item);
const failed = item.sendState === "failed" || item.sendState === "unconfirmed";
const isSteer = item.queueMode === "steer";
const steerMode = isSteer && !failed;
const reconnecting = item.sendState === "waiting-reconnect";
const reconnecting = props.offline || item.sendState === "waiting-reconnect";
const busy = item.sendState === "executing-command";
const editing = props.editingId === item.id;
const canSteer =
@@ -254,6 +254,7 @@ export function renderChatComposerView(context: ChatComposerViewContext) {
const queue = renderChatQueue({
queue: props.queue,
offline: props.offline,
canAbort: showAbortableUi,
onQueueRetry: props.connected && canCompose ? props.onQueueRetry : undefined,
onQueueSteer: props.connected && canCompose ? props.onQueueSteer : undefined,
+15 -6
View File
@@ -2971,6 +2971,10 @@ button.chat-pr__diff {
--chat-composer-editor-line: calc(var(--chat-composer-editor-size) * 1.5);
position: relative;
/* Queue, goal and status rows deliberately underlap the composer. Keep the
writing surface in the foreground so their backgrounds cannot paint over
the footer controls while their exposed content remains below it. */
z-index: 1;
container-name: chat-composer;
container-type: inline-size;
display: flex;
@@ -3137,6 +3141,7 @@ button.chat-pr__diff {
.agent-chat__composer-underlaps {
--chat-composer-bottom-underlap: 18px;
--chat-composer-underlap-row-height: 44px;
position: relative;
z-index: 0;
@@ -3147,7 +3152,7 @@ button.chat-pr__diff {
)
0 0;
padding-top: calc(var(--chat-composer-shell-gap) + var(--chat-composer-bottom-underlap));
border: 1px solid color-mix(in srgb, var(--border) 78%, transparent);
border: 1px solid color-mix(in srgb, var(--border) 16%, transparent);
border-top: 0;
border-radius: 0 0 calc(20px * var(--openclaw-corner-radius-scale))
calc(20px * var(--openclaw-corner-radius-scale));
@@ -3158,7 +3163,7 @@ button.chat-pr__diff {
.agent-chat__composer-underlaps:has(.agent-chat__composer-error):not(
:has(.agent-chat__offline-hint)
) {
border-color: color-mix(in srgb, var(--danger) 26%, transparent);
border-color: color-mix(in srgb, var(--danger) 16%, transparent);
}
.agent-chat__composer-underlaps:has(.agent-chat__composer-errors) {
@@ -3168,13 +3173,14 @@ button.chat-pr__diff {
.agent-chat__composer-underlaps:has(.agent-chat__composer-notice):not(
:has(.agent-chat__composer-error)
) {
border-color: color-mix(in srgb, var(--info) 16%, transparent);
background: color-mix(in srgb, var(--info) 9%, var(--chat-composer-surface));
}
.agent-chat__composer-underlaps:has(.agent-chat__offline-hint):not(
:has(.agent-chat__composer-errors)
) {
border-color: color-mix(in srgb, var(--warn) 26%, transparent);
border-color: color-mix(in srgb, var(--warn) 16%, transparent);
background: color-mix(in srgb, var(--warn) 9%, var(--chat-composer-surface));
}
@@ -3191,9 +3197,12 @@ button.chat-pr__diff {
}
.agent-chat__offline-hint {
display: flex;
min-height: var(--chat-composer-underlap-row-height);
align-items: center;
box-sizing: border-box;
width: 100%;
padding: 7px 14px;
padding: 8px 14px;
background: color-mix(in srgb, var(--warn) 9%, var(--chat-composer-surface));
color: color-mix(in srgb, var(--warn) 72%, var(--text));
font-size: 11px;
@@ -4138,7 +4147,7 @@ button.chat-pr__diff {
align-items: center;
gap: 8px;
box-sizing: border-box;
min-height: 36px;
min-height: var(--chat-composer-underlap-row-height, 44px);
padding: 8px 10px 8px 14px;
font-size: var(--control-ui-text-sm);
line-height: 1.35;
@@ -4150,7 +4159,7 @@ button.chat-pr__diff {
align-items: center;
gap: 8px;
box-sizing: border-box;
min-height: 36px;
min-height: var(--chat-composer-underlap-row-height, 44px);
padding: 8px 10px 8px 14px;
font-size: var(--control-ui-text-sm);
line-height: 1.35;
+3 -2
View File
@@ -2048,8 +2048,9 @@
min-height: 34px;
padding: 0 12px;
color: var(--muted);
background: color-mix(in srgb, var(--bg-elevated) 76%, transparent);
border: 1px solid color-mix(in srgb, var(--text-strong) 11%, transparent);
background: var(--chat-composer-surface, var(--card));
border: 1px solid
var(--chat-composer-hairline, color-mix(in srgb, var(--text-strong) 11%, transparent));
border-radius: var(--radius-full);
font-size: var(--control-ui-text-xs);
font-variant-numeric: tabular-nums;
+34 -2
View File
@@ -479,6 +479,32 @@ body {
width: 100%;
}
.composer-bench__composer-stack {
width: 100%;
}
.composer-bench__composer-stack[data-shared-status-row] {
display: grid;
}
.composer-bench__composer-stack[data-shared-status-row]
> :is(.composer-bench__tasks-status, .agent-chat__composer-shell) {
grid-area: 1 / 1;
}
.composer-bench__composer-stack[data-shared-status-row] > .composer-bench__tasks-status {
z-index: 5;
align-self: start;
justify-self: start;
width: auto;
padding-top: 8px;
margin: 0;
}
.composer-bench__composer-stack[data-shared-status-row] .chat-tasks-status {
margin: 0;
}
.composer-bench__tasks-status {
box-sizing: border-box;
width: 100%;
@@ -492,13 +518,19 @@ body {
}
.composer-bench__neighbor {
position: relative;
box-sizing: border-box;
width: 100%;
margin-bottom: 8px;
}
.composer-bench__neighbor > :first-child {
margin-top: 0;
.composer-bench__neighbor > * {
position: relative;
inset: auto;
box-sizing: border-box;
width: 100%;
max-width: none;
margin: 0;
}
@media (prefers-reduced-motion: reduce) {
+7 -1
View File
@@ -1421,7 +1421,13 @@ function renderChatSurface(sessionList: SessionsListResult) {
onGatewayQuestionSkip: () => publishState({ inset: "none" }),
onSlashIntent: () => {},
});
return html`${renderBenchNeighbor()}${renderBenchTasksStatus()}${composer}`;
const shareStatusRow = state.tasks !== "none" && state.plan !== "none";
return html`${renderBenchNeighbor()}<div
class="composer-bench__composer-stack"
?data-shared-status-row=${shareStatusRow}
>
${renderBenchTasksStatus()}${composer}
</div>`;
}
function renderNewTargetBar() {