Skip to content

emrg: gui renderer Batch 2 — chat transcript store + TranscriptView component - #994

Merged
argszero merged 1 commit into
masterfrom
feature/gui-react-batch2-chat
Aug 26, 2026
Merged

emrg: gui renderer Batch 2 — chat transcript store + TranscriptView component#994
argszero merged 1 commit into
masterfrom
feature/gui-react-batch2-chat

Conversation

@argszero

Copy link
Copy Markdown
Owner

GUI renderer React migration — Batch 2 (chat area core)

Part of the staged React migration (design ~/.emrg/designs/gui-react-migration-design.md, Batch 2). Vanilla renderer stays live until the Batch 5 switch (design D3) — this batch adds the React-side modules independently, exactly like Batches 0–1.

What's in this PR

1. renderer/src/lib/transcript.ts — pure chat transcript state machine (port of renderer/js/chat.js, 471 lines)

  • Per-session state buckets keyed by sid (P3: entries / groupIndex / toolRowIndex / doneRids); sid=null keeps the legacy single-session bucket behavior.
  • Streaming deltas accumulate text per request_id into the active text segment; deltas arriving after done are dropped (rant 14:11); a text segment sealed by a following tool call opens a new segment on the next delta (rant 21:57:10 / 21:09, preserves TUI interleaving order and removes the typing cursor on sealed segments).
  • Tool rows: running → done/failed, elapsed only recorded on success (feeds group summary sums), content captured for the 2000-char truncation + expand behavior (G91/G131).
  • Consecutive-tool merge groups (rant 21:28:49): a completed standalone row followed by a new tool creates a group; a following tool appends to an existing group (auto-expanding a collapsed group to show the running row); a group with ≥2 completed rows shows the summary bar (count + total elapsed) and auto-collapses unless the user manually expanded it (userExpanded, rule 5).
  • done finalizes segments (typing off), tracks doneRids (>500 → cleared), and pushes timeout / max-rounds hint system messages via an injected t (same injectable-i18n pattern as copywriting.ts).
  • Subscribe/version API for useSyncExternalStore; no DOM/scroll/empty-state side effects (component layer owns those).

2. renderer/src/lib/transcript.test.ts — 15 vitest tests

Mirrors the old emrg/gui/test/tool-group.test.js assertions (merge → 1 group with 3 rows + summary 3 个工具执行 · 1.9s + collapse, bar click → expand + user-expanded, new tool stays expanded; text interleave → no merge, 2 independent rows) plus delta accumulation, segment sealing, late-delta drop, done/timeout/maxRounds, per-session isolation, unregister, clear/clearTyping, row output toggle, failed-row elapsed absence, and the subscribe/version contract.

3. renderer/src/components/TranscriptView.tsx — React chat area component

  • Renders the transcript store via useSyncExternalStore (version snapshot); class names match the vanilla DOM (.msg.user / .msg.assistant / .tool-row / .tool-group / .history-load-bar), so the existing CSS applies unchanged at the Batch 5 switch.
  • Streaming segments render as plain text; on done they render full markdown through the injectable createMarkdownRenderer (no marked/DOMPurify/hljs bundled yet — falls back to escaped text without vendors). The ✦ marker is rendered outside the markdown text so it never breaks block syntax.
  • Tool rows: spinner / ✓ per status, human-readable labels via toolPhrases, intent line, · Ns elapsed, hidden-by-default output toggled by row click, expand-full-text button for >2000 chars.
  • Merge groups: summary bar + collapse/expand with user-expanded persistence; remote-client broadcast label when the stream is not own.

4. renderer/src/components/TranscriptView.test.tsx — 7 component tests

Rendering of user/system/history/load-bar, streaming → markdown-on-done (injected fake renderer), tool-row lifecycle with elapsed + output toggle, failed rows, group summary + collapse + expand persistence, remote label for broadcast streams / absent for own streams, and the sid=null default bucket.

Verification

  • cd emrg/gui/renderer && npm run typecheck ✅ (TS strict)
  • cd emrg/gui/renderer && npm test78 passed (was 56: +15 transcript +7 TranscriptView)
  • cd emrg/gui/renderer && npm run build ✅ (dist/index.html + assets, CSP-safe)
  • cd emrg/gui && npm test ✅ 265 (257 pass / 8 skipped — unchanged)
  • pytest tests/ ✅ 1094 passed + 1 skipped (doc-count guard emrg: sync test counts to 484 + guard test against doc drift (recurs #426/#430/#510) #511: Agent.md renderer count synced to 78)
  • python -c "from emrg.client.app import run_client" ✅ · python -m emrg --help

No changes to the daemon, window.emrg preload bridge (53 members), main.js/preload.js, or the daemon protocol.

…omponent
Port chat.js logic to a pure per-session state machine (lib/transcript.ts):
delta/done/toolStart/toolEnd/clearTyping/clear with sid buckets (P3),
tool-group merge (rant 21:28:49: summary + collapse + user-expanded) —
15 vitest tests mirroring the old tool-group.test.js assertions.
Add React TranscriptView component: streaming plain-text -> markdown-on-done
via injectable renderer, tool rows running/done/failed with elapsed and
expandable output, merge-group bar, remote label — 7 component tests.
Vanilla renderer stays live until Batch 5 switch (design D3).

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260826-150007

Re-verified the full diff vs master (15b2c7d):

  • lib/transcript.ts is a faithful pure-logic port of chat.js: per-session state buckets (P3), delta accumulation per rid with sealed-segment reopening (rant 21:57:10), doneRids late-delta drop (rant 14:11), consecutive-tool merge groups with summary/collapse/user-expanded (rant 21:28:49), timeout/max-rounds hints via injected t. No DOM side effects; subscribe/getVersion contract for useSyncExternalStore.
  • 15 store tests mirror the old tool-group.test.js assertions (merge → 1 group/3 rows/1.9s summary/collapse, bar-click expand + user-expanded persistence, text-interleave no-merge) plus delta/done/isolation/clear/subscription coverage.
  • TranscriptView.tsx renders the store with vanilla-matching class names (CSS reuse at Batch 5), streaming plain-text → markdown-on-done via injectable renderer (✦ outside markdown text), tool-row lifecycle with elapsed + expandable output, merge-group bar, remote-client label.
  • 7 component tests with a fake markdown renderer (store mutations wrapped in act()).
  • Agent.md renderer count synced 56→78 (breakdown sums correctly; doc-count guard #511 passes).

CI: test + test-windows both pass (run 32940129214). Clean to merge after 3 votes.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260826-150924

Second independent review pass:

  • head f74cc9c unchanged since the 1/3 vote; diff vs master still +1085 lines across the 5 expected files.
  • Re-spot-checked transcript.ts: P3 per-session buckets, doneRids late-delta guard (>500 clear), segment sealing on tool interleave, merge-group collapse/user-expanded logic — all faithful to chat.js semantics.
  • CI: test + test-windows both pass (run 32940129214).
  • Agent.md count 56→78 with breakdown summing to 78 (doc-count guard #511 satisfied).

2/3 — one more vote to merge.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260826-151642

Third consecutive vote (150007 → 150924 → 151642, no ❌ in between).

  • head f74cc9c unchanged; CI test + test-windows still green (run 32940129214); CLEAN/MERGEABLE.
  • Full diff re-confirmed (+1085 lines / 5 files: transcript.ts store, 15 store tests, TranscriptView, 7 component tests, Agent.md count 56→78).

3/3 — merging.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@argszero