mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-10-23 06:41:16 +00:00
## Summary The token/cost line that closes every agent message previously turned into two rows when expanded: a bare `Tokens` label line (chevron + label) with the six metric chips on a second line below it. This PR keeps the disclosure on one row in both states — expanding swaps the collapsed summary chips for all six chips on the very same line as the chevron. ## Problem - Collapsed, the row is a compact single line: `› INPUT 3K · OUTPUT 54 · COST $0.76`. - Expanding it replaced that line with a label-only row (`› Tokens`) plus a separate chip row, so a one-line affordance grew to two rows and pushed the transcript content down twice as much. ## Change - **Expanded = same row.** The six chips (input, output, reasoning, cache read, cache write, cost) now render in place of the summary chips, beside the chevron. The `Tokens` label line and the separate content row are gone; at wide widths the expanded card is exactly as tall as the collapsed card. - **Wrapping is preserved.** Expansion exists to read the values, so the expanded row is never ellipsized: when width runs out, the chips wrap onto further lines in place (this wrapping behaviour is unchanged from before). - **Collapsed is untouched.** The collapsed summary keeps its single ellipsized line; its rendering is pixel-identical to before. **Expanded — wide layout** (before shows the label line + chip row; after is one row): <img width="884" height="344" alt="wide-expanded" src="https://github.com/user-attachments/assets/06abd5cd-b15d-4b29-ab39-1202d4c35ca5" /> **Expanded — narrow layout, 220 px** (both wrap instead of truncating; after drops the label line and starts the chips beside the chevron): <img width="390" height="339" alt="narrow-expanded" src="https://github.com/user-attachments/assets/e4e5fbe7-a37c-42f6-8f73-3628d21ac2c6" /> **Collapsed — unchanged** (wide and narrow; pixel-identical before/after, narrow still ellipsizes): <img width="884" height="310" alt="wide-collapsed" src="https://github.com/user-attachments/assets/71b40a15-6a19-4232-8eef-e0ce1327ff90" /> <img width="390" height="175" alt="narrow-collapsed" src="https://github.com/user-attachments/assets/f77ebdd9-0540-4c74-9f2b-3a3fb64a7d9a" /> ## Implementation notes - `packages/ui/src/components/message-usage.tsx`: the toggle now always renders `chevron + chips` — the collapsed state renders the three summary chips, the expanded state renders all six in the same slot. The separate expanded content `<div>` and its `aria-controls` wiring are removed. - `packages/ui/src/styles/messaging/message-usage.css`: `.message-step-usage` is the expanded wrapping row (`flex-wrap`, no truncation); the collapsed `.message-step-usage-summary` keeps its single-line ellipsis. The unused `.message-step-usage-label` / `.message-step-usage-content` rules are deleted. The chevron pins to the first chip row when the row wraps. - **Accessibility:** the button's `aria-label` carries all six full values when expanded (`aria-label` overrides the visible chips, so they are repeated there deliberately); the collapsed label is unchanged. The shared disclosure chevron rotation keeps working via the generic `aria-expanded` rule. - **dev-docs/TRANSCRIPT.md:** the message-token disclosure convention note is updated to describe the one-row layout. ## Help pages No help change needed: `docs/help/*/conversations.md` documents the line as *"expand it for reasoning and cache counts"*, which remains true — the documented behaviour is unchanged and the two-row layout was never documented. ## Validation - TDD red→green: the new assertions were watched failing first (`0 !== 6` chips on the row, stale `aria-controls`), then passing. - `tests/browser/usage-metrics.test.ts` — 5/5, now also covering: expanded chips on the toggle's row; single row at wide width; wrapping onto multiple rows at 220 px in LTR **and** RTL with no horizontal overflow and no ellipsis; chevron level with the first chip row; Space/Enter collapse restores the three-chip summary. - Related suites that render the usage row: `status-tokens` (4/4), `transcript-presets` (1/1), `search-highlight` (0 failures). - Visual: before/after screenshots captured from the real fixture at 900 px and 220 px (the composites above); collapsed rendering verified pixel-identical.
4.9 KiB
4.9 KiB
Transcript rendering
Message content visibility and presets, token lines, tool renderers, native session lifecycle signals and transcript re-projection.
Agent conventions
- The message-content popup and Chat settings share
components/transcript-visibility.tsand the semantic icons fromcomponents/message-content-icons.ts; tool presentation metadata lives independently of renderers incomponents/tool-call/tool-presentation.ts. Popup styles live instyles/components/transcript-filters.css. - Transcript presets are Nothing (hides every row, including all tools), Minimal, Balanced and Everything (
TRANSCRIPT_PRESETS); saved Detailed choices load as Custom with their effective tool column. Chat settings shows the four as buttons; the popup shows the same choice as a native select under its title, where a disabled Custom entry appears only after a manual row change. Escape closes an open list before the popup. Preset names and descriptions are localized in every locale; covered bytests/browser/transcript-presets.test.ts. - Status Tokens are independent of message token visibility. A collapsed section uses the non-interactive
renderSummaryslot for one ellipsized session input/output/cost line; expanded content shows session metrics then descendant-inclusive totals as separate wrapping groups. Keep this layout instyles/panels/status-tokens.css. - Message token disclosures keep one row in both states: collapsed shows the input/output/cost summary chips on a single ellipsized line; expanded swaps the same row to all six chips beside the chevron, which wrap onto further lines when narrow instead of truncating (expansion exists to read every value). Keep the metric renderer local and chip/row styles in
styles/messaging/message-usage.css. - Tool-result images retain native
state.contentand render through the sharedcomponents/tool-call/output-images.tsxsurface, including MCP and specialized tools. Keep image bytes out of text projections (copy/search/speech); image styles live instyles/messaging/tool-call/images.css. - Code Mode reads native
metadata.toolCallsintool-call/renderers/execute-data.ts; keep call-list styling instyles/messaging/tool-call/execute.css, with native image/output ownership in the shared tool shell. - Native websearch text is parsed conservatively by
tool-call/renderers/websearch-data.ts; unknown/oversized responses use the shared raw renderer. Recognized provider-consent Forms retain the generic schema/answer path, with layout instyles/messaging/tool-call/websearch.css. - Session lifecycle features use OpenCode V2's published signals: one
session.execution.{succeeded,failed,interrupted}per busy period (lib/native-execution-events.ts) andsession.retry.scheduledfor provider backoff. The schema-declaredsession.idle/session.statusare never published; never wire a feature only to them. A terminal event is a hint untilsession.activeconfirms settlement, which alone announces "A session finished", only for root sessions ending in success, failure or an inactivity stop. Transcript reducers retire at settlement, deferred while prompts stay parked in their inbox or a user shell or unsettled tool call still needs reconciling there. Token/cost lines (components/message-usage.tsx) appear once per agent message: the row of an assistant message's final text (the one carrying "delete tools and reasoning") renders, once that message has native usage, the sum (sumMessageUsage) of its own usage and of every message holding a tool or reasoning part that cleanup selects (technicalCleanupParts). Reuse that selection rather than a separate projection. The line renders inside the content row, so it never shifts<Index>items or splits a technical group (whose Delete needs two tools); tool-only steps get no line. A message whose text is followed by tools also counts in the next message's line, as its tools belong to that selection. Sums cover the loaded transcript window only, like cleanup. Capture real streams withscripts/test-session-lifecycle-native.mjs. - Native transcript events reduce only through
stores/opencode-data.ts(never add another partial-part reducer), which publishes bounded snapshots throughhydrateMessages(message-v2/instance-store.ts); these per-event re-projections must not invalidate unchanged messages:hydrateMessagesreuses the residentMessageRecordobject whenever content, explicit timestamps and prompt-display metadata are unchanged, and an identical whole snapshot returns before any store write or session-revision bump. Part/info comparisons use allocation-free structural equality (partDataEqual), neverJSON.stringify. The SDK reduce mutates rows in place, so reference identity can never prove a session unchanged — never add a reference-anchored projection skip. - Shared speech byte/MediaSource transport lives in
lib/audio-utils.ts; playback ownership and cancellation stay with its callers.