Skip to content

emrg: gui renderer React migration Batch 0 — infra scaffold (Vite 8 + React 19 + TS strict + Vitest + SnapshotStore) - #991

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

emrg: gui renderer React migration Batch 0 — infra scaffold (Vite 8 + React 19 + TS strict + Vitest + SnapshotStore)#991
argszero merged 1 commit into
masterfrom
feature/gui-react-renderer-batch0

Conversation

@argszero

Copy link
Copy Markdown
Owner

GUI renderer React migration — Batch 0 (infrastructure scaffold)

Addresses the host rant 2026-08-26T12:16:48 (GUI renderer full React migration, route A full rewrite, decisions D1–D7 finalized) per the authoritative design doc ~/.emrg/designs/gui-react-migration-design.md v0.1. This is the first of the multi-cycle relay batches (Batch 0–5).

Batch 0 scope (design §5): standalone React scaffold inside emrg/gui/renderer/ — no vanilla renderer change. The old renderer/js/*.js files, renderer/index.html, main.js, preload.js, and the daemon protocol stay byte-identical until the Batch 5 one-shot switch (decision D3).

What's in this PR

  • renderer/package.json — React 19 + Vite 8 + TypeScript 7 (strict) + Vitest 4 + @testing-library. @tiptap deliberately NOT installed yet (design: Batch 0 item 1).
  • vite.config.tsroot: "src" → output renderer/dist/index.html + dist/assets/* (decision D1). No inline scripts → CSP script-src 'self'-safe; base: "./" for main.js loadFile(file://) later. Vitest (jsdom) config.
  • tsconfig.jsonstrict: true (decision D7).
  • src/ React skeleton (design §4.1 component tree):
    • main.tsx, App.tsx (ErrorBoundary → I18nProvider → Shell)
    • components/ErrorBoundary.tsx — React port of the vanilla error-boundary.js (rant 2026-08-25T21:13): full-screen overlay, 500-char summary, copy/reload buttons, built-in zh/en fallback (sits above the provider, cannot use context — matches vanilla)
    • components/Shell.tsx — zero-risk placeholder layout (design Batch 0 item 4)
    • lib/snapshot-store.ts — self-developed SnapshotStore (decision D2, grok-bot pattern, zero deps) + hooks/useSnapshotStore.ts (useSyncExternalStore)
    • lib/utils.ts — pure helpers ported from vanilla utils.js (escapeHtml / genRequestId / relTime with injectable translate fn)
    • lib/i18n.tsx — React i18n context skeleton (full 934-line dict migration is Batch 1)
    • src/index.html — Vite entry template (distinct from the vanilla renderer/index.html)
  • Tests — 19 Vitest tests: 5 snapshot-store + 9 utils + 3 ErrorBoundary + 2 App smoke (design Batch 0 item 5: mount App, assert skeleton exists)
  • CI — new test.yml step: npm ci + typecheck + vitest + vite build in emrg/gui/renderer (node 22, same as the existing GUI step)
  • Electron packagingemrg/gui/package.jsonbuild.files now excludes the renderer toolchain (node_modules / dist / src / test / config) from the packaged app
  • Test discovery — gui npm test pinned to "node --test \"test/*.test.js\"" so node --test no longer auto-discovers the renderer vitest files
  • Agent.md — doc-count guard (emrg: sync test counts to 484 + guard test against doc drift (recurs #426/#430/#510) #511) sync: GUI 262 tests unchanged, renderer vitest suite 19 added

External contracts preserved

  • window.emrg preload bridge: untouched (46 invoke + onEvent methods)
  • daemon broadcast event types: untouched (message_delta/done/tool_started/…)
  • main.js / preload.js: zero diff; still loads the vanilla renderer/index.html

Verification (local)

  • cd emrg/gui/renderer && npm run typecheck — clean (TS strict)
  • cd emrg/gui/renderer && npm test — 19/19 pass
  • cd emrg/gui/renderer && npm run builddist/index.html + dist/assets/* (CSP-safe)
  • cd emrg/gui && EMRG_SKIP_INTEGRATION=1 npm test — 262 tests, 0 failures
  • pytest tests/ — 1094 passed, 1 skipped
  • python -c "from emrg.client.app import run_client" + python -m emrg --help — OK

Follow-ups

Batches 1–5 (pure-logic migration → chat → sidebar/panels → dialogs → switch & cleanup) land in subsequent evolution cycles. This batch is self-contained: the vanilla renderer keeps working untouched.

… React 19 + TS strict + Vitest + SnapshotStore)
Renderer migration (rant 2026-08-26T12:16:48, design
~/.emrg/designs/gui-react-migration-design.md v0.1, D1-D7 decided):
standalone emrg/gui/renderer/ package — no vanilla renderer change.
- renderer/package.json: react/react-dom 19 + vite 8 + typescript 7 +
vitest 4 + @testing-library (no @tiptap yet)
- vite.config.ts: root src/ -> dist/index.html + assets/* (CSP
script-src 'self' safe, base ./); vitest jsdom config
- tsconfig.json: strict (D7)
- src/: main.tsx, App.tsx (ErrorBoundary -> I18nProvider -> Shell),
components/ErrorBoundary.tsx (vanilla error-boundary.js port),
components/Shell.tsx, lib/snapshot-store.ts (D2, zero-dep store +
useSyncExternalStore), lib/utils.ts (pure helpers port), lib/i18n.tsx
(context skeleton; full dict in Batch 1), shell.css
- tests: 19 vitest (5 snapshot-store + 9 utils + 3 ErrorBoundary +
2 App smoke)
- CI: test.yml renderer step (npm ci + typecheck + test + build)
- gui npm test pinned to test/*.test.js (node --test would otherwise
pick up renderer vitest files); electron-builder excludes renderer
toolchain from package
- Agent.md: doc-count guard sync (262 gui + 19 renderer vitest)
Verification: renderer typecheck/vitest/build green, gui 262 tests
(254 pass + 8 integration skipped), pytest 1094 passed/1 skipped,
import + CLI checks OK.

@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 cyc20260826-131007 (1/3)

Review: Batch 0 infra scaffold for the GUI renderer React migration (rant 2026-08-26T12:16:48, design doc D1–D7).

  • CI: test + test-windows both green (actionlint gate #444 + doc-count guard #511 validated the workflow/Agent.md changes)
  • External contracts untouched: window.emrg bridge, main.js, preload.js, daemon protocol — zero diff on those files
  • Vite build emits dist/index.html + dist/assets/* (no inline script → CSP script-src 'self' safe; base ./ for file:// loadFile)
  • tsconfig strict (D7); SnapshotStore (D2) is a clean zero-dep store + useSyncExternalStore hook
  • gui npm test pinned to test/*.test.js — node --test otherwise auto-discovers renderer vitest files
  • electron-builder excludes renderer toolchain from packaged app
  • 19 vitest tests cover store/utils/ErrorBoundary/App; vanilla renderer untouched until Batch 5 (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 cyc20260826-131914 (2/3)

Re-review: head bcf4963 unchanged since 1/3 vote; CI test + test-windows both green (actionlint gate + doc-count guard pass); MERGEABLE/CLEAN. Independent-cycle verification confirms the Batch 0 scaffold — no issues found.

@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 cyc20260826-132628 (3/3)

Third consecutive independent-cycle approval (131007 → 131914 → 132628, no ❌ in between). Head bcf4963 unchanged; CI test + test-windows green; MERGEABLE/CLEAN. Merge condition met.

@argszero
argszero merged commit bfb7bce into masterAug 26, 2026
2 checks passed
argszero added a commit that referenced this pull request Aug 26, 2026
…ds/copywriting) + preload API-surface guard (#992)
Renderer migration Batch 1 (design doc v0.1 §5, rant 2026-08-26T12:16:48,
D1-D7 decided). Follows #991 (Batch 0) now merged.
- renderer/src/lib/commands.ts: vanilla renderer/js/commands.js ported to TS
(pure logic, no DOM/window deps) — COMMANDS registry (16), parseInput
three-state, getCompletions prefix filter, hintText with injectable t
- renderer/src/lib/copywriting.ts: vanilla renderer/js/copywriting.js ported
to TS — toolPhrases/buildCopy/createCopywriting factory (injectable t,
refresh() locale-switch rebuild)
- renderer/src/lib/commands.test.ts + copywriting.test.ts: vitest tests
mirroring the old node:test assertions (design: pure-logic tests migrate
to Vitest with identical assertions); old test/commands.test.js stays
until Batch 5 switch (D3/D6)
- emrg/gui/test/preload-api.test.js: window.emrg API-surface guard — freezes
the full 53-member contract (52 invoke + onEvent) with channel/event-name
assertions (red line #1; closes gap found in cycle cyc20260826-131007:
design doc said 46 methods but preload.js actually exposes 52 + onEvent,
only spot checks existed before)
- Agent.md: doc-count guard sync — gui 262→265 (+3 preload-api), renderer
vitest 19→34 (+11 commands +4 copywriting), API surface 46→52+onEvent
Verification: renderer typecheck clean, renderer vitest 34/34, gui npm test
265 (257 pass + 8 integration skipped), pytest 1094 passed/1 skipped,
doc-count guard 4/4, import + CLI OK.
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
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