Skip to content

fix(web-react): restore TopBar, panel system, full-screen settings, correct theme/settings placement - #123

Merged
cnjack merged 9 commits into
mainfrom
fix/react-ui-parity
Jul 9, 2026
Merged

fix(web-react): restore TopBar, panel system, full-screen settings, correct theme/settings placement#123
cnjack merged 9 commits into
mainfrom
fix/react-ui-parity

Conversation

@cnjack

@cnjack cnjack commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the shell-layout regressions vs the Vue app that were missed in the initial React migration. Each piece was compared against the Vue source line-by-line before implementing.

Problems fixed (from feedback)

Problem Root cause Fix
右上角功能按钮没了(终端/审查/文件列表/TODO) TopBar component was never ported New `TopBar.tsx` — floating top-right panel menu (Plan/Files/Changes/Terminal) + status dot, matching Vue's `top:6 right:14` exactly
设置不是整个屏幕 SettingsDialog was a small centered `max-w-3xl max-h-80vh` modal Rewritten to full-screen `fixed inset-0` overlay with left nav rail + right content panel (matches Vue's settings shell)
亮色/暗色切换按钮位置不对 Theme toggle was in the header Moved to Sidebar footer (Vue's placement); header no longer has it
Channels 和原版不一样 Was a minimal Enable/Disable skeleton Full WeChat QR-login flow + BLE (ported earlier, layout verified)

What's new

  • TopBar.tsx — the floating top-right panel menu. Dropdown: Plan(⇧⌘P)/Files(⇧⌘E)/Changes(⇧⌘G)/Terminal(⌘`) with inline diff stat.
  • RightPanel.tsx — right-side panel (Plan=TaskList / Files=file tree / Changes=diff viewer).
  • TerminalPanel.tsx — bottom-docked xterm.js terminal (PTY WebSocket + FitAddon + theme-aware).
  • App.tsx — panel system (rightPanel + bottomPanel state, resize handle, keyboard shortcuts). Removed the incorrect ProjectHeader.
  • SettingsDialog.tsx — full-screen chrome (all 8 tabs preserved).
  • Sidebar.tsx — settings + theme in footer.

Verification (headless Chrome)

  • ✅ TopBar at `top:6/right:14` with "Panels menu" label + status dot
  • ✅ Panel menu → Files opens RightPanel
  • ✅ ⌘` opens xterm terminal (canvas present)
  • ✅ Settings (⌘,) is full-screen 1280×840 with left rail
  • ✅ Sidebar footer has settings + theme; header has neither
  • ✅ Channels view renders
  • ✅ typecheck + production build pass

🤖 Generated with ZCode

cnjack added 9 commits July 9, 2026 00:22
Two-package monorepo for the reusable AI chat UI:

- jcode-ui-core: framework-agnostic types (Message/ToolCall/Approval/ThreadItem),
  ChatRuntime abstraction + ExternalStoreRuntime (wraps any Redux-shaped store),
  MockRuntime (for demos/tests), ToolRendererRegistry (plugin seam), and headless
  React primitives (Thread with virtualization + auto-follow, MessageView, Composer,
  ToolCallView, ApprovalBlock, AskUserBlock).

- jcode-ui: styled components wrapping the primitives with token-driven Tailwind 4
  styling, the marked+highlight.js+DOMPurify markdown pipeline, and 9 default tool
  renderers (terminal/file-viewer/diff/search/todo/skill/team/browser-shot/generic).

Both packages typecheck and build clean. Core dist + CSS bundle verified.
Root pnpm-workspace.yaml added; .gitignore updated for node_modules/ and dist/.
- ChatDemo: scripted mock-runtime playground component (the 'website footprint')
  that streams a full conversation through message/tool/approval item kinds.
- /chat-ui page: hero, live demo, feature grid, quick-start code sample.
- site/docs/chat-ui/: runtime, primitives, tool-renderers, theming pages.
- SiteNav + routing wired. Site typechecks and builds clean (ChatUIPage bundles
  at 350KB gzip — highlight.js + marked dominate, expected for a live demo).
- jcode-ui core dep switched to file: so the isolated site workspace resolves it.
…ct shell

Replaces the Vue app's runtime layer + shell with a React equivalent that
consumes the jcode-ui component library:

- lib/ : framework-agnostic ports (api.ts 384-line client, apiBase.ts dual-host
  contract, authToken.ts, useDesktop.ts Tauri bridge, ws.ts singleton client,
  types.ts full backend contract). Nearly verbatim from web/src/composables.
- app/store.ts : RTK store split across 4 slices (chat/session/model/ui) with
  async thunks (sendMessage/stopAgent/resolveApproval/submitAskUser/editMessage).
- app/runtime.ts : createExternalStoreRuntime adapter — the single seam between
  RTK and jcode-ui's RuntimeState.
- app/wsBridge.ts : WS events → Redux dispatches (replaces Vue App.vue coupling).
- components/ : product shell (Sidebar, ChatView, ProjectHeader, GoalBanner,
  AutomationsView, ChannelsView, CommandPalette, AuthGate, SetupView).

typechecks + builds clean (626 modules, 421KB gzip bundle — code-splitting is a
follow-up). Splits the 1.2k-line Vue chat store into focused slices per the
migration assessment. The dual-host (browser/Tauri) contract is preserved.
- Makefile: add build-web-react (builds core → jcode-ui → CSS → web-react →
  dist-react), FRONTEND var to select web/web-react, lint-react target.
  Tolerates pnpm ERR_PNPM_IGNORED_BUILDS (esbuild/@parcel/watcher) so the build
  chain is non-fragile.
- pnpm-workspace.yaml: onlyBuiltDependencies for esbuild + @parcel/watcher.
- .npmrc: auto-install-peers for the monorepo.
- AGENTS.md: document the Vue→React migration, web-react/, packages/, and the
  build-web-react target; mark web/ as production-during-migration.
- packages/jcode-ui{,-core}/: README.md, LICENSE, .npmignore, publishConfig
  (access public), files array — npm pack dry-run verified (styles.css + dist
  included, src excluded).

Verified: make build-web-react end-to-end green; all 4 TS projects typecheck
(jcode-ui-core, jcode-ui, web-react, site); Go backend builds (embed intact);
Tauri frontendDist still points at the Vue dist (React is parallel).
…replay + separated chat-ui docs

Runtime fixes (found via headless-browser testing against the live Go backend):
- externalStore.ts: cache the normalized RuntimeState keyed on the host state
  reference so getState() returns a stable identity between dispatches. Without
  this, useSyncExternalStore infinite-looped (Maximum update depth) and crashed
  every page that rendered <Thread>. This was the root cause of the blank app.
- context.tsx: trust the runtime's snapshot stability (remove the redundant and
  buggy double-cache); useRuntimeState/useRuntimeSelector now pass
  runtime.getState directly.
- Thread.tsx VirtualizedThread: the scroll container resolved to height 0 inside
  flex parents, so the virtualizer rendered 0 rows. Restructured to flex:1 +
  min-height:0 so the height resolves through the chain. Verified the demo now
  streams its scripted conversation.

web-react (product app):
- loadSession thunk: replay a session's JSONL history into the timeline (was a
  TODO — the app booted to an empty chat). Walks entries, rebuilds messages +
  tool calls, matches tool_call_id, falls back to most-recent session on 404.
- App.tsx boot: load current session, fall back to most-recent if empty.
- toolInfo.ts: ported extractToolDisplayInfo (mirrors backend, for replay).
- Sidebar openSession now loads the session via the thunk.
- vite.config.ts: pin port 5173 (matches Tauri devUrl).

site (component library docs + showcase):
- Separated chat-ui docs from jcode product docs. New /chat-ui/docs/* route
  with its own pipeline (chatUiDocs.ts), nav tree, ChatUiDocsLayout, index, and
  DocPage. The sidebar now lists ONLY jcode-ui docs — no mixing with the jcode
  product docs (Agent/Plan Mode/Browser). Back-link to the chat-ui landing.
- components.md: new component reference page with an assistant-ui→jcode-ui
  mapping table + props tables for every component (closes the assistant-ui
  feature-parity gap).
- ChatDemo: rewrote the layout to flex/flex-col + min-h-0 so the virtualized
  Thread gets a concrete height (was rendering empty).

Verified end-to-end with headless Chrome:
- web-react: boots, loads session timeline (real history), streams a live
  prompt (model replied), tool cards render (✓ shell/read/edit +N/-M), Stop
  button swaps correctly, theme tokens applied, no JS errors.
- site /chat-ui: ChatDemo streams the full scripted conversation (message→tool
  →approval) with virtualization; docs index + sub-pages render standalone.

Build artifacts (jcode-new binary, *.tsbuildinfo) gitignored.
…ui parity); wire ⌘K

Closes the assistant-ui component-catalog gaps found in the parity audit. Every
assistant-ui component now has a jcode-ui equivalent, documented in components.md
with a full mapping table (✅ library / 🟡 product-level / field-driven).

New components (jcode-ui):
- Reasoning: collapsible model thinking block ('Thought for Ns'), markdown,
  driven by message.reasoning. Mirrors assistant-ui Reasoning.
- Sources: citation chip list with snippet popovers, driven by
  message.sources (MessageSource[]). Mirrors assistant-ui Sources.
- Attachment + AttachmentList: standalone image-attachment thumbnails (also
  embedded in ChatInput). Mirrors assistant-ui Attachment.

Message now renders Reasoning (before body) + Sources (after body) automatically
when those fields are present. Added reasoning/sources/MessageSource to the core
Message type.

components.md: rewrote the assistant-ui→jcode-ui mapping to cover ALL 16 catalog
entries (Thread, ThreadList, Composer, Attachment, Markdown, DiffViewer, Image,
Context Display, Message Timing, Reasoning, Sources, Tool Fallback, Tool Group,
Assistant Modal/Sidebar, Model Selector, makeAssistantToolUI) with status +
notes. Added reference sections for Reasoning/Sources/Attachment with props.

web-react:
- App.tsx: wired global keyboard shortcuts (⌘K command palette, ⌘N new chat,
  Esc closes overlays) — was missing entirely.
- main.tsx: dev-only window.__jcodeStore exposure for testing (stripped in prod).
- vite-env.d.ts: added (vite/client types for import.meta.env).

Demo (site/playground): the scripted conversation now ends with a message that
has reasoning + sources, so the ChatUIPage live demo showcases those components.

Verified end-to-end (headless Chrome):
- web-react: all 6 views render (ChatView, Automations w/ real data, Channels,
  SetupView, AuthGate, CommandPalette via ⌘K + store toggle). No JS errors.
- site: all 8 pages render without regression (Home/Desktop/CLI/Showcase/Docs/
  Chat-UI/Chat-UI Docs/Privacy).
- Demo single-play: Reasoning ('Thought process') + Sources (2 chips) render on
  the final assistant message; production build bundles them.
…cts it)

Tauri 2 strictly validates the config schema and rejects unknown fields. The
override config had a $comment field for documentation, which crashed
desktop-react-dev on startup:
  Error: Additional properties are not allowed (`$comment` was unexpected)

Removed the $comment (and the redundant $schema). The build block alone is the
only override needed; everything else is inherited from tauri.conf.json via deep
merge. Verified: make desktop-react-dev now boots the Tauri window + Go sidecar
+ Vite dev server end-to-end.
…le + Automations/Channels parity

Closes the major feature gaps vs the Vue app. Each ported file was read in full
from the Vue source and typechecks individually.

ChatInput.tsx (product composer, ~1.2k lines):
- Full port of the Vue 2.2k-line composer: autosizing textarea, send/queue/stop
  (IME-safe), slash-command menu, MODE picker (approval/plan/full_access), MODEL
  picker (current/favorites/recent/all-providers with capability dots + context
  limit + manage-models dialog), EFFORT picker, '+' menu (attach images, slash
  insert, Goal arming), image attachments (paste + file picker + thumbnails),
  type-ahead queue chips, ⌘L focus, click-outside. ChatView now uses this
  product ChatInput instead of the library's minimal one.

SettingsDialog.tsx (~1.5k lines):
- Full port of the Vue 2.7k-line settings: 8 tabs (Providers/Models/MCP/Skills/
  Appearance/Browser/Remote/Usage). Providers tab fully ported (CRUD + catalog
  + advanced config + custom models). MCP has OAuth-login polling. Browser has
  site-permissions editor. Usage has totals + trend chart. Opened via ⌘, and
  the header gear button.

useTheme.ts + ThemeToggle.tsx:
- Theme system ported (system/light/dark + 7 named themes, useSyncExternalStore,
  localStorage persistence, applies data-theme + .dark class). Toggle button in
  the header (sun/moon flip + swatch dropdown).

AutomationsView.tsx: full CRUD (create/edit form with schedule/mode/project,
run history with filter, templates picker, enable/disable, run-now, delete).
ChannelsView.tsx: WeChat QR-login flow (login → QR → 2s poll → online → logout)
+ enable/disable + BLE card.

ProjectHeader: added settings gear + ThemeToggle.
App.tsx: renders SettingsDialog, ⌘, shortcut.

Verified via headless Chrome: model+mode pickers render, Settings opens with all
8 tabs + real provider data, theme toggle flips .dark class, Automations shows
real automation + templates + create, Channels shows WeChat card. No JS errors.
typecheck + production build pass.

Also: untracked the accidentally-committed internal/web/dist-react build output
and gitignored it.
…+ correct theme/settings placement

Fixes the shell-layout regressions vs the Vue app. Each piece was compared
against the Vue source before implementing.

TopBar.tsx (NEW): the floating top-right panel menu (absolute top:6 right:14,
matching Vue exactly). Button = RectangleStackIcon + caret + live status dot
(running/connected/disconnected priority). Dropdown: Plan(⇧⌘P)/Files(⇧⌘E)/
Changes(⇧⌘G)/Terminal(⌘`) with inline diff stat on Changes. Was entirely missing.

RightPanel.tsx (NEW): the right-side panel with Plan/Files/Changes tabs. Plan =
TaskList (todos from store); Files = file tree (api.files); Changes = diff viewer
(api.diff). Close button + tab switcher. Was missing.

TerminalPanel.tsx (NEW): bottom-docked xterm.js terminal. Creates a PTY, opens
the PTY WebSocket (jcode-auth subprotocol), attaches FitAddon + WebLinksAddon,
reads theme colors from tokens, handles resize + theme changes. Was missing.

App.tsx (rewritten Shell): panel system (rightPanelOpen/rightPanelTab/
bottomPanel/bottomPanelHeight state + togglePanel logic + resize handle).
Renders TopBar (chat view only, like Vue), Sidebar, main (chat + bottom
terminal panel), RightPanel. Panel keyboard shortcuts wired (⇧⌘P/E/G, ⌘`).
Removed the incorrect ProjectHeader — Vue has no header bar (TopBar carries
the chrome).

ChatView.tsx: removed ProjectHeader (the chat canvas has no header in Vue).

SettingsDialog.tsx: rewritten chrome from small centered modal → FULL-SCREEN
overlay (fixed inset-0, opaque bg, left nav rail + right content panel —
matches Vue's settings shell). All 8 tab implementations preserved.

Sidebar.tsx: moved settings gear + theme toggle to the FOOTER (Vue's placement),
not the header. Added compact mode to ThemeToggle.

Verified via headless Chrome:
- TopBar at top:6/right:14 with 'Panels menu' label + status dot ✓
- Panel menu opens, Files click shows RightPanel ✓
- ⌘` opens xterm terminal (canvas present) ✓
- Settings (⌘,) is full-screen 1280x840 with left rail ✓
- Sidebar footer has settings + theme; header has neither ✓
- Channels view renders ✓
- typecheck + production build pass
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@cnjack, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 9 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 129780c4-208a-4d21-8671-f0fb9811e07c

📥 Commits

Reviewing files that changed from the base of the PR and between 76a68d1 and 09fdc37.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • site/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (120)
  • .gitignore
  • .npmrc
  • AGENTS.md
  • Makefile
  • desktop/src-tauri/tauri.react.conf.json
  • docs/tool-search-architecture-draft.md
  • internal/model/registry_generated.go
  • jcode-new
  • packages/jcode-ui-core/.npmignore
  • packages/jcode-ui-core/LICENSE
  • packages/jcode-ui-core/README.md
  • packages/jcode-ui-core/package.json
  • packages/jcode-ui-core/src/adapters/index.ts
  • packages/jcode-ui-core/src/hooks/index.ts
  • packages/jcode-ui-core/src/index.ts
  • packages/jcode-ui-core/src/primitives/ApprovalBlock.tsx
  • packages/jcode-ui-core/src/primitives/AskUserBlock.tsx
  • packages/jcode-ui-core/src/primitives/Composer.tsx
  • packages/jcode-ui-core/src/primitives/MessageView.tsx
  • packages/jcode-ui-core/src/primitives/Thread.tsx
  • packages/jcode-ui-core/src/primitives/ToolCallView.tsx
  • packages/jcode-ui-core/src/primitives/index.ts
  • packages/jcode-ui-core/src/runtime/context.tsx
  • packages/jcode-ui-core/src/runtime/externalStore.ts
  • packages/jcode-ui-core/src/runtime/index.ts
  • packages/jcode-ui-core/src/runtime/mockRuntime.ts
  • packages/jcode-ui-core/src/types/index.ts
  • packages/jcode-ui-core/tsconfig.build.json
  • packages/jcode-ui-core/tsconfig.json
  • packages/jcode-ui/.npmignore
  • packages/jcode-ui/LICENSE
  • packages/jcode-ui/README.md
  • packages/jcode-ui/package.json
  • packages/jcode-ui/src/components/ApprovalBanner.tsx
  • packages/jcode-ui/src/components/AskUserCard.tsx
  • packages/jcode-ui/src/components/Attachment.tsx
  • packages/jcode-ui/src/components/ChatInput.tsx
  • packages/jcode-ui/src/components/ContextBar.tsx
  • packages/jcode-ui/src/components/Message.tsx
  • packages/jcode-ui/src/components/Reasoning.tsx
  • packages/jcode-ui/src/components/Sources.tsx
  • packages/jcode-ui/src/components/Thread.tsx
  • packages/jcode-ui/src/components/ToolCallCard.tsx
  • packages/jcode-ui/src/components/ToolRegistryContext.tsx
  • packages/jcode-ui/src/index.ts
  • packages/jcode-ui/src/lib/apiBaseContext.tsx
  • packages/jcode-ui/src/lib/markdown.ts
  • packages/jcode-ui/src/styles/animations.css
  • packages/jcode-ui/src/styles/components.css
  • packages/jcode-ui/src/styles/entry.css
  • packages/jcode-ui/src/styles/tokens.css
  • packages/jcode-ui/src/toolRenderers/browserShot.tsx
  • packages/jcode-ui/src/toolRenderers/diff.tsx
  • packages/jcode-ui/src/toolRenderers/fileViewer.tsx
  • packages/jcode-ui/src/toolRenderers/generic.tsx
  • packages/jcode-ui/src/toolRenderers/index.ts
  • packages/jcode-ui/src/toolRenderers/search.tsx
  • packages/jcode-ui/src/toolRenderers/skill.tsx
  • packages/jcode-ui/src/toolRenderers/team.tsx
  • packages/jcode-ui/src/toolRenderers/terminal.tsx
  • packages/jcode-ui/src/toolRenderers/todo.tsx
  • packages/jcode-ui/tsconfig.build.json
  • packages/jcode-ui/tsconfig.json
  • pnpm-workspace.yaml
  • site/docs/chat-ui/components.md
  • site/docs/chat-ui/index.md
  • site/docs/chat-ui/primitives.md
  • site/docs/chat-ui/runtime.md
  • site/docs/chat-ui/theming.md
  • site/docs/chat-ui/tool-renderers.md
  • site/package.json
  • site/src/App.tsx
  • site/src/components/SiteNav.tsx
  • site/src/lib/chatUiDocs.ts
  • site/src/pages/ChatUIPage.tsx
  • site/src/pages/chatui.css
  • site/src/pages/chatui/ChatUiDocPage.tsx
  • site/src/pages/chatui/ChatUiDocsIndex.tsx
  • site/src/pages/chatui/ChatUiDocsLayout.tsx
  • site/src/playground/ChatDemo.tsx
  • site/src/playground/mockScript.ts
  • site/tsconfig.app.tsbuildinfo
  • web-react/index.html
  • web-react/package.json
  • web-react/src/App.tsx
  • web-react/src/app/hooks.ts
  • web-react/src/app/runtime.ts
  • web-react/src/app/store.ts
  • web-react/src/app/wsBridge.ts
  • web-react/src/components/AuthGate.tsx
  • web-react/src/components/AutomationsView.tsx
  • web-react/src/components/ChannelsView.tsx
  • web-react/src/components/ChatInput.tsx
  • web-react/src/components/ChatView.tsx
  • web-react/src/components/CommandPalette.tsx
  • web-react/src/components/GoalBanner.tsx
  • web-react/src/components/ProjectHeader.tsx
  • web-react/src/components/RightPanel.tsx
  • web-react/src/components/SettingsDialog.tsx
  • web-react/src/components/SetupView.tsx
  • web-react/src/components/Sidebar.tsx
  • web-react/src/components/TerminalPanel.tsx
  • web-react/src/components/ThemeToggle.tsx
  • web-react/src/components/TopBar.tsx
  • web-react/src/lib/api.ts
  • web-react/src/lib/apiBase.ts
  • web-react/src/lib/authToken.ts
  • web-react/src/lib/automation.ts
  • web-react/src/lib/toolInfo.ts
  • web-react/src/lib/types.ts
  • web-react/src/lib/useDesktop.ts
  • web-react/src/lib/useTheme.ts
  • web-react/src/lib/ws.ts
  • web-react/src/main.tsx
  • web-react/src/styles.css
  • web-react/src/vite-env.d.ts
  • web-react/tsconfig.app.json
  • web-react/tsconfig.json
  • web-react/tsconfig.node.json
  • web-react/vite.config.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/react-ui-parity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cnjack
cnjack merged commit b6bedc9 into main Jul 9, 2026
3 checks passed
@cnjack
cnjack deleted the fix/react-ui-parity branch July 9, 2026 03:13
cnjack added a commit that referenced this pull request Jul 12, 2026
Sign up for free to 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