Skip to content

refactor(web): migrate the Web workbench UI to React - #384

Merged
tt-a1i merged 16 commits into
openpi-dev:mainfrom
6iKUN6:codex/web-react-mvp
Sep 7, 2026
Merged

refactor(web): migrate the Web workbench UI to React#384
tt-a1i merged 16 commits into
openpi-dev:mainfrom
6iKUN6:codex/web-react-mvp

Conversation

@6iKUN6

@6iKUN6 6iKUN6 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Problem

The Web workbench's imperative browser script mixes transcript rendering, Session navigation, streaming state, and interaction logic. This PR replaces it with typed React components and a browser store. Related to #76; the visual reference is #352 at 1b340f2, while compatibility follows current main 0b282430798939a19412ff83fce277916f5d0cf1.

The integration review found missing newer-main behavior in the original React branch: exact turn cancellation, canonical theme preference, reliable prompt admission/retry and queue receipts, quiet/stalled connection recovery, and running-state protection. These are restored before merge.

Value

Give the workbench component, state, and protocol boundaries that are easier to maintain and test, with Vite HMR for development and self-contained production assets. The React implementation carries forward #352's visual work; merging the legacy implementation separately is unnecessary.

Approach

  • React 19/TypeScript, Zustand, Vite, Tailwind, Astryx primitives, sanitized Markdown, and local bundled assets.
  • Preserve Pi/OpenPI runtime, Session ownership, authenticated commands, and canonical snapshot/event semantics.
  • Port main's canonical light/dark/system preference, exact-turn Stop, native follow-up receipts, stable admission IDs and uncertain retries, heartbeat refresh, and bounded connection reads. Also reject truncated/invalid successful prompt receipts and bound SSE response-header waits.
  • Keep same-message streaming pinned when appropriate, force scrolling after send, attribute activity only to the current Session, and preserve background terminal projections and an accessible persistent heading.
  • Integrate main's Pi 0.85.1 peer resolution, CLI shutdown fixes, and CI/package isolation. Ship only the fixed Web asset allowlist; build checks reject committed bundle drift.

Validation

Final integrated source HEAD: 71462796f60246e9e0cf0eb35e3545ba72f8ae99.

  • bun run check: passed, including frontend build/typecheck, format and lint.
  • bun run test: 1,442 Node tests passed, one platform skip; 81 Vitest tests passed.
  • bun run test:web:e2e using local Chrome: 4 passed, including desktop/mobile, reduced motion, dark running state, exact cancellation request, and axe checks.
  • Core regression tests first reproduced failures before repair. Legacy DOM/source-string checks are replaced by store/protocol/component tests against the actual React implementation.
  • npm pack --dry-run --json: all four production assets included.
  • Isolated Pi package discovery: pi list identified only this checkout; offline RPC startup exposed the expected OpenPI status/widget without errors.
  • Two independent final reviews: clean; the final activity attribution finding was repaired and re-reviewed.
  • Final-head remote CI: all passed on Node 22.19.0, Node 24, Node 26, Windows, and Web E2E, including packed CLI and committed-source install smokes: https://github.com/openpi-dev/openpi/actions/runs/34080319155.

Browser tests run the production WebHost. The running/cancellation fixture intercepts canonical snapshots and command receipts; it is not a live-provider or long-running Workflow acceptance claim. The user's installed OpenPI source was not changed.

Impact

The browser implementation and development/build tooling change. Backend protocol, model-visible tools/context, permissions, Session persistence and canonical configuration do not change. Theme continues to use Pi's existing preference. Production remains local and self-contained, with no Vite/CDN requirement at runtime.

The single JavaScript bundle is approximately 714 kB raw / 213 kB gzip and still triggers Vite's size warning. This is a maintainability migration, without a measured rendering-speedup claim. Historical Markdown parsing during streaming remains a separate performance opportunity.

Design and integration evidence.

Record the frozen PR openpi-dev#352 behavior baseline, Pi-native ownership boundaries, selected frontend stack, static delivery contract, and validation matrix for the local migration experiment.
Document default port fallback, strict explicit ports, fast startup diagnostics, and TUI error projection for the local React validation branch.
- replace the imperative browser UI with typed React and Zustand projections
- add Vite HMR, production asset builds, and bounded port conflict handling
- cover the Web store, packaged host, responsive browser flows, and artifact drift in CI
@github-actions github-actions Bot added documentation Improvements or additions to documentation area:github GitHub workflows, templates, ownership, or tests labels Sep 4, 2026
@6iKUN6

6iKUN6 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

@tt-a1i Could you please review the technical direction and implementation of this React Web MVP? Thanks.

@tt-a1i

tt-a1i commented Sep 4, 2026 via email

Copy link
Copy Markdown
Collaborator

- disable npm peer resolution for Pi-provided host packages
- guard the Git install contract and record the production smoke rationale
@github-actions github-actions Bot added the area:setup OpenPI setup, configuration, or setup documentation label Sep 4, 2026

@tt-a1i tt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The React direction is sound and keeps Pi as the runtime source of truth, but this draft is not ready to merge yet. I found two user-visible parity regressions, an unenforced development-startup bound, a supported-Node test failure, and a distribution-notice gap.

I reviewed exact head 13f3032decdcc600e7d46d848b2cc0097fd54d7e. Locally, bun run check and bun run test:web:e2e pass (3/3). On Node 26.3.0, the focused Web store suite fails 21/21 because the tests access an unavailable unqualified localStorage. The required Node 22 CI job is also currently red: the packed standalone CLI smoke attempts to fetch unavailable @earendil-works/pi-tui@0.85.0 and receives 404, so the recorded installed-package validation is not current.

Please also update the design record's source boundary to identify this PR and an exact implementation head when the evidence is frozen, rather than only a mutable local branch plus the base and PR #352.

Comment thread scripts/dev-web.mjs Outdated
if (startupFailure) throw startupFailure;
try {
const response = await fetch(endpoint);
const response = await fetch(endpoint, {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P1] Enforce the advertised readiness deadline on each probe. The outer 15-second loop does not bound this awaited fetch: a peer can accept the TCP connection and never return headers, leaving dev:web stuck here forever and preventing startup.waitForFailure() from being observed. Please abort each request at the remaining deadline (and when startup fails), and add a hanging-peer regression test.

Comment thread web/ui/src/store/web-store.ts Outdated
}
},
setQuery(query) {
set({ query: query.trim() });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P2] Preserve the raw value of this controlled input. Trimming in the setter rewrites "foo " to "foo" immediately; typing the next character then produces "foobar" instead of "foo bar". Keep the raw query in state and trim only the value used for matching, with a test covering spaces in a search term.

Comment thread web/ui/src/components/Markdown.tsx Outdated
return (
<div className="markdown">
<ReactMarkdown
remarkPlugins={[remarkGfm]}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P2] Preserve the frozen baseline's soft-line-break behavior. PR #352 configured Marked with breaks: true, while GFM alone renders "first line\nsecond line" without a <br>; this component sits under .message-content, so it does not inherit the user-message white-space: pre-wrap rule and the browser collapses the newline. Add remark-breaks (or an equivalent rendering rule) and a parity test.

Comment thread tests/web/web-store.spec.ts Outdated

afterEach(() => {
sessionStorage.clear();
localStorage.clear();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P2] Keep this suite compatible with the declared node >=22.19.0 range. Under Node 26.3.0, the native Web Storage global shadows jsdom and unqualified localStorage is undefined, so all 21 store tests fail in this cleanup. I reproduced the same failure at this head. Use window.localStorage (including the assertion below) or explicitly configure the test environment, then cover a supported newer Node in CI.

Comment thread package.json
"undici": "8.9.0"
},
"devDependencies": {
"@astryxdesign/core": "0.5.2",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P1] Add notices for the code now shipped in the committed production bundle. web/dist/app.js embeds React/ReactDOM, Zustand, Lucide, i18next, eventsource-parser, React Markdown/remark/rehype, Astryx, and related runtime code, but THIRD_PARTY_NOTICES.md still lists only the Sessions extension. The relevant MIT/ISC terms and retained copyright notices need to accompany distribution before this can leave draft.

@tt-a1i

tt-a1i commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Reviewed at exact head 13f3032decdcc600e7d46d848b2cc0097fd54d7e and submitted REQUEST_CHANGES.

The React/Pi-native direction looks good, but the draft still needs fixes for the unbounded development readiness probe, controlled-search whitespace regression, Markdown soft-break parity, Node 26 Web-store test compatibility, and bundled frontend notices. The required Node 22 packed-CLI smoke is also currently failing with an unavailable @earendil-works/pi-tui@0.85.0 tarball.

Use an explicitly referenced timer for each readiness probe so Node 22 does not let the test process or launcher exit while the fetch is still pending. Clear the timer after every probe.
Advance the design evidence boundary to the readiness timer fix and record the focused Node 22 and Node 24 regression runs.
Replace the superseded load-sensitive test note with the successful Node 24 full-suite result observed after the readiness fix.
Ensure the CLI cannot advertise a ready Web Host before its SIGINT and SIGTERM cleanup handlers are installed.
Advance the implementation boundary and distinguish focused CLI evidence from unrelated full-suite timing failures.
@6iKUN6
6iKUN6 marked this pull request as ready for review September 4, 2026 13:42
@6iKUN6
6iKUN6 requested a review from a team as a code owner September 4, 2026 13:42
@6iKUN6
6iKUN6 requested a review from tt-a1i September 4, 2026 13:46
@6iKUN6

6iKUN6 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

@tt-a1i The review feedback has been addressed, the merge conflict is resolved, and CI is green. Could you please re-review when convenient?

prompt_accepted and the HTTP receipt were resetting an active turn
to preparing. Preserve running unless the command already settled,
matching the openpi-dev#364 live-phase rule without copying legacy app.js.
@tt-a1i
tt-a1i merged commit 5262158 into openpi-dev:main Sep 7, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:github GitHub workflows, templates, ownership, or tests area:setup OpenPI setup, configuration, or setup documentation documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants