Skip to content

feat: jcode-ui 0.2.0 — scoped tokens, full conversation loop, AG-UI adapter, canvas/voice, docs overhaul - #133

Merged
cnjack merged 5 commits into
mainfrom
feat/jcode-ui-0.2.0
Jul 11, 2026
Merged

feat: jcode-ui 0.2.0 — scoped tokens, full conversation loop, AG-UI adapter, canvas/voice, docs overhaul#133
cnjack merged 5 commits into
mainfrom
feat/jcode-ui-0.2.0

Conversation

@cnjack

@cnjack cnjack commented Jul 11, 2026

Copy link
Copy Markdown
Owner

What

jcode-ui / jcode-ui-core 0.2.0 (not yet published to npm): closes the gap between "a solid coding-agent UI" and a complete general-purpose agent chat library, with the jcode web app adapted and the docs site overhauled to match. Competitive analysis and roadmap live in internal-doc/chat-ui-competitive-analysis.md / chat-ui-roadmap.md; the full change list is in packages/jcode-ui/CHANGELOG.md.

Breaking (migration guide included)

  • Scoped tokens: every design token moves from :root to the [data-jcode-ui] scope with a --jcode- prefix — zero leakage into host pages. jcode-ui/compat.css bridges the legacy names (generated themes keep working unchanged); jcode-ui/shadcn.css inherits a host shadcn theme automatically. Migration guide: /chat-ui/docs/guides/migration-0.2 on the site.
  • Scoped element resets moved into @layer base via :where() — fixes unlayered resets outranking .jcode-btn and Tailwind utility classes (buttons rendered as bare text). Animation classes/keyframes gained the jcode- prefix; chat prose gained host-article isolation guards.

Library

  • Approvals generalized: Approval.options[] (arbitrary host-defined options, ACP-compatible) + resolveApprovalOption; allow_always kinds keep the two-step arming UX
  • Conversation loop: BranchPicker (Message.versions / switchVersion), regenerate, thumbs feedback, failed-turn retry, ConnectionBanner, ThreadWelcome + Suggestions, ExportButton / exportThreadMarkdown, QuoteSelection + ComposerHandle — all fail-visible (controls render only when the host implements the action)
  • Composer v2: pluggable AttachmentAdapter (upload progress / retry), drag & drop + paste-screenshot, leadingControls/trailingControls/footer slots, ModelSelector, optional dictation
  • Rendering: streaming-stable markdown (unclosed structures completed before parse, finished blocks cached), code-block chrome with copy, optional plugins/mermaid + plugins/katex subentries (dynamic-import peers)
  • Runtime-wired renderers: TaskList, FileTree, TestResults, StackTrace, Artifact container, slots on Message/ToolCallCard
  • Ecosystem: createAGUIRuntime (AG-UI protocol: SSE transport, JSON Patch shared state, 6/6 selftest), ThreadStore contract + ThreadList, jcode-ui/canvas (optional @xyflow/react peer), jcode-ui/voice (browser APIs only)

jcode web

  • Adapted to scoped tokens: tokens.base.css (generated by script/sync-web-base-tokens.sh) restores legacy names for the app chrome, compat.css maps them back into components — the Go theme generator and all generated themes are untouched. Light/dark/generated-theme chains verified in the browser.
  • Added the missing sidebar.noConversations i18n key (5 locales); fixture extended with branching / feedback / retry / ConnectionBanner demos.

Site (already deployed to origin)

  • /chat-ui interactive demo: welcome + starter pills + live typing, scripted tour replay, light/dark + mobile viewport toggles
  • Component docs grew from 10 to 21 pages with 8 new live previews (previews default to light with a per-preview theme toggle); fixed three CSS root causes that made preview text unreadable and buttons unstyled
  • New "vs. Alternatives" comparison page and 0.2 migration guide; generated API reference refreshed (226 symbols)
  • Vite dep-scan fix (static showcase pages under public/ use import maps that broke the scanner)

CI

  • CI and the Makefile hand-rolled the package build (tsc + tailwind only), skipping the new dist generation steps (compat.css, subentry styles) — both now delegate to each package's own pnpm build.

Verification

  • core/ui build + typecheck green; AG-UI selftest 6/6; markdown smoke test 25/25; web typecheck + build green; site tsc + build green; make build-web green with the CI fix
  • Both example apps (minimal / zustand) compile and build against 0.2.0 unchanged — backward compatibility holds
  • jcode web verified in the browser (light / dark / generated themes); new components exercised interactively in the fixture; docs previews verified page-by-page on a production build before deploying

After merge

  1. pnpm publish (core first, then ui — pnpm rewrites workspace:*). site/ and examples/ pin ^0.2.0, which resolves once published; for local development before that, switch them to workspace:* with '../packages/*' in pnpm-workspace.yaml.
  2. Purge the Aliyun CDN for https://www.j-code.net/ (directory refresh) — edge caches HTML for 30 days.
  3. Upgrade the cloud console to 0.2.0 + compat.css, then delete its self-maintained PermissionCard in favor of the library's options-mode ApprovalBanner.

Generated with Jack AI bot

t added 4 commits July 11, 2026 21:24
…, AG-UI, canvas/voice

Breaking: design tokens move from :root to [data-jcode-ui] with a --jcode-
prefix (compat.css bridges legacy names; shadcn.css inherits shadcn themes).
Scoped element resets now live in @layer base via :where() so they never
outrank utility/component classes; animation classes/keyframes gain the same
prefix; prose gets host-article isolation guards.

- Approvals: host-defined options (ACP-compatible) + resolveApprovalOption;
  two-step arming preserved for allow_always kinds
- Conversation loop: BranchPicker (Message.versions/switchVersion),
  regenerate, thumbs feedback, failed-turn retry, ConnectionBanner,
  ThreadWelcome + Suggestions, ExportButton/exportThreadMarkdown,
  QuoteSelection + ComposerHandle
- Composer v2: AttachmentAdapter (progress/retry), drag&drop + paste,
  leading/trailing/footer slots, ModelSelector, optional dictation
- Rendering: streaming-stable markdown with block caching, code-block
  chrome + copy, optional mermaid/katex plugin subentries
- Runtime-wired renderers: TaskList, FileTree, TestResults, StackTrace,
  Artifact container, Message/ToolCallCard slots
- createAGUIRuntime (AG-UI protocol, SSE + JSON Patch, selftest),
  ThreadStore contract + ThreadList
- New subentries: jcode-ui/canvas (@xyflow/react optional peer),
  jcode-ui/voice (browser APIs only)
- Both packages bumped to 0.2.0; CHANGELOG added

Generated with Jack AI bot
… components

The app chrome keeps theming via legacy --color-* names: tokens.base.css
(generated by script/sync-web-base-tokens.sh) restores them at :root, and
jcode-ui/compat.css maps them back into the scoped components — so the Go
theme generator and all generated themes stay untouched.

Also: add missing sidebar.noConversations i18n key (5 locales) and extend
fixture-tool-ux with branching/feedback/retry/ConnectionBanner demos.

Generated with Jack AI bot
…son + migration guides

- /chat-ui hero demo: interactive mode (welcome + starters + live typing),
  scripted tour replay, light/dark + mobile viewport toggles
- Component docs: 11 new pages (welcome/branching/connection/tasklist/
  model-selector/artifact/thread-list/code-renderers/export-quote/canvas/
  voice) with 8 live previews; previews default light with a per-preview
  theme toggle; viewport gate hardened with a rect fallback
- New docs: vs-alternatives comparison, 0.2 migration guide; components
  mapping table refreshed; generated API reference regenerated (226 symbols)
- vite: scan only the real SPA entry (public/ showcase import maps broke
  dep-scan); nested React.lazy for demos replaced with eager import
- site/examples pinned to ^0.2.0 (resolves after npm publish; use
  workspace:* + '../packages/*' locally until then)
- internal-doc: competitive analysis + 0.2 roadmap

Generated with Jack AI bot
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This release updates jcode-ui and jcode-ui-core to 0.2.0 with new runtime contracts, attachment and dictation support, conversation controls, AG-UI integration, markdown rendering, optional canvas/voice suites, scoped theming, renderers, documentation, fixtures, and interactive demos.

Changes

Core runtime and component APIs

Layer / File(s) Summary
Runtime, approval, composer, and thread contracts
packages/jcode-ui-core/src/{types,runtime,primitives,threads}/...
Adds AG-UI runtime support, connection state, optional runtime actions, approval options, attachment adapters, Composer refs/slots/dictation, thread stores, and markdown export.
Conversation components
packages/jcode-ui/src/components/*
Adds branching, feedback, retry, connection, suggestions, export, quote selection, model selection, task lists, artifacts, thread lists, and welcome states.
Rendering and optional suites
packages/jcode-ui/src/lib/*, packages/jcode-ui/src/toolRenderers/*, packages/jcode-ui/src/{canvas,voice,plugins}/*
Adds streaming markdown, code-block chrome, Mermaid/KaTeX hooks, file/test/stack renderers, workflow canvas components, and voice components.

Theming and integration

Layer / File(s) Summary
Scoped styling and compatibility
packages/jcode-ui/src/styles/*, packages/jcode-ui/scripts/*, web/src/styles/*
Moves tokens to [data-jcode-ui] with --jcode-* names, prefixes animation utilities, adds feature styles, and generates legacy compatibility tokens.
Documentation and demos
site/docs/*, site/src/playground/*, packages/jcode-ui/README.md, packages/jcode-ui/CHANGELOG.md
Documents the 0.2.0 APIs, migration path, optional suites, comparisons, and new interactive examples.
Fixtures and application wiring
packages/jcode-ui/fixture/*, web/fixture-tool-ux/*, web/src/i18n/*, site/package.json
Updates fixtures, connection-state demonstrations, package versions, workspace release exclusions, and localized empty states.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • cnjack/jcode#38: Related todo status and rendering contract alignment.
  • cnjack/jcode#122: Earlier reusable jcode-ui and jcode-ui-core foundation extended by this release.
  • cnjack/jcode#128: Overlapping attachment and attachment-preview refactoring.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main 0.2.0 upgrade and the major feature areas added in this PR.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/jcode-ui-0.2.0

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.

The CI workflow and Makefile hand-rolled the package build (tsc + tailwind
only), silently skipping the 0.2.0 build steps that generate compat.css /
shadcn.css and copy the voice/canvas subentry styles into dist — so web's
'jcode-ui/compat.css' import failed to resolve. Delegate to 'pnpm build' in
each package so there is a single source of truth for build steps.

Generated with Jack AI bot
@cnjack
cnjack merged commit a724817 into main Jul 11, 2026
2 checks passed
@cnjack
cnjack deleted the feat/jcode-ui-0.2.0 branch July 11, 2026 13:34

@cnjack cnjack left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Senior review — PR #133 (jcode-ui 0.2.0)

Reviewed via 4 parallel deep-dives across: jcode-ui-core runtime/threads, jcode-ui conversation-loop components + streaming markdown, canvas/voice/plugins/toolRenderers, and the web app's CSS token-scoping migration + CI wiring. Style/lint/naming ignored per scope; only findings I'm ≥80% confident are real defects are included below. Finding 4 (CI build break) was independently re-verified by diffing ci.yml against the new build:css script.


Finding 1 — CI build will fail: jcode-ui/compat.css import has no producer in the CI pipeline

Impact: Hard build break, directly contradicts the PR's "zero migration needed for web" claim.
Evidence: web/src/styles.css:21 adds @import 'jcode-ui/compat.css';, resolved via packages/jcode-ui/package.json's exports["./compat.css"] → ./dist/compat.css, which is only produced by the new build:css step (... && node scripts/generate-compat-css.mjs && cp src/styles/shadcn.css dist/shadcn.css, package.json:74). .github/workflows/ci.yml:98-102 was not updated in this PR — it still hand-runs the old npx tailwindcss -i src/styles/entry.css -o dist/styles.css --minify and never invokes generate-compat-css.mjs or copies shadcn.css. The very next CI step (vite build in web/) will fail to resolve the import.
Suggested fix: Update the "Build packages" CI step to pnpm --filter jcode-ui run build (or run build:css) instead of the manual command list, so CI stays in sync with the package's actual build requirements.


Finding 2 — AG-UI runtime: overlapping runs corrupt isRunning state and can silently drop or duplicate messages

Impact: A stale (superseded) run's finally block unconditionally calls setRunning(false) even when a newer run is active, since that call isn't gated the same way the adjacent controller = null reset is (if (controller === ac)). Composer re-enables and the "thinking" indicator disappears mid-turn while the stale run keeps mutating the timeline in the background. sendMessage has no guard against being invoked while a run is already in flight, and the old AbortController is silently overwritten so stop() can only ever abort the most recent run.
Evidence: packages/jcode-ui-core/src/runtime/aguiRuntime.ts:402-419 (runLoop finally block), :421-443 (sendMessage, no in-flight guard).
Suggested fix: Gate setRunning(false) the same way as the controller-nulling (if (controller === ac) setRunning(false)); add a guard in sendMessage to reject/queue or explicitly abort-before-restart.

Related, same root cause class — Composer.send() silently discards data on the AG-UI adapter:

  • enqueueMessage is a documented no-op in aguiRuntime.ts:450, but Composer.send() (primitives/Composer.tsx:444-462) clears input unconditionally after calling it — a user typing a follow-up mid-run has their message vanish with no error, since the Enter-key handler isn't gated on isRunning (:547-551).
  • send() also calls setPending([]) unconditionally (:460), wiping attachments still 'uploading'/'error', not just the ones actually sent (doneNow) — an in-flight upload is orphaned and its later-resolving promise updates a pending slot that no longer exists.

Suggested fix: Only clear pending slots that were actually included in the sent message; disable/queue send while attachments are uploading or the runtime has no real queue support.


Finding 3 — Streaming markdown: loose lists / multi-paragraph list items are permanently shredded, not just during streaming

Impact: MarkdownBody routes all message content — complete or still-streaming — through useStreamingMarkdown (Message.tsx:356-373). splitTopLevelBlocks treats any blank line as a hard top-level-block boundary with no list-continuation awareness (streamingMarkdown.ts:188-217), so a CommonMark "loose list" (items separated by a blank line — very common in LLM output) or a list item spanning multiple paragraphs gets torn into N independent blocks, each parsed by marked with no knowledge of the surrounding list. Verified: splitTopLevelBlocks("1. Item one\n\n2. Item two\n\n3. Item three") returns 3 separate single-item blocks instead of one ordered list. This is a persistent rendering regression for ordinary historical messages, not a streaming-only artifact.
Evidence: packages/jcode-ui/src/lib/streamingMarkdown.ts:188-217, useStreamingMarkdown.ts:21-47, Message.tsx:356-373.
Suggested fix: Track "inside a list" the same way fenced-code spans are tracked, so a blank line followed by a list-continuation line doesn't flush the block; or require ≥2 consecutive blank lines to force a split.


Finding 4 — CSS cascade: moving the element reset into @layer base makes it lose to any unlayered host CSS, not just to .jcode-btn/Tailwind utilities as intended

Impact: Per the CSS cascade-layers spec, any unlayered normal-priority rule beats any layered normal-priority rule regardless of specificity. Pre-PR, the reset (.jcode-thread button, etc.) was unlayered at specificity (0,1,1) and reliably beat a host's own unlayered button {}/textarea {} CSS — the common case for host apps with generic form-element resets. Now that it's in @layer base, it loses unconditionally to any such unlayered host rule, and since Tailwind utilities are also layered (@layer theme, base, components, utilities;, entry.css:14-20), an unlayered host button {} now beats jcode-ui's Tailwind-driven button styling too — reintroducing, for host CSS, the exact "styles bleed into jcode-ui controls" bug class this release is meant to fix. Silent, no test will catch it; only manifests for hosts with generic unlayered element-selector CSS.
Evidence: packages/jcode-ui/src/styles/components.css:16-47 vs. pre-PR baseline (unlayered), entry.css:14-20.
Suggested fix: Scope the reset under [data-jcode-ui] :where(...) instead of relying on layer ordering alone to retain the "beats plain host CSS" guarantee, or explicitly document this new failure mode in the migration guide.


Finding 5 — Mermaid plugin: unsanitized innerHTML with an overridable security level

Impact: registerMermaid({ securityLevel: 'loose' }) — a plausible integration choice to enable clickable diagram nodes — silently overrides the safe 'strict' default because ...opts is spread after it (mermaid.ts:60). The rendered SVG is then assigned via node.innerHTML = svg with no DOMPurify pass (mermaid.ts:96), unlike every other HTML-injection path in this package (markdown, katex), which always sanitizes before dangerouslySetInnerHTML. Mermaid diagram source in an LLM response is attacker-adjacent (prompt-injection); with 'loose' set, a malicious node label could execute script. Not exploitable under default settings, but the API invites the risky opt-in without warning it also removes the only sanitization layer on this path.
Evidence: packages/jcode-ui/src/plugins/mermaid.ts:60,96.
Suggested fix: Don't let ...opts silently override securityLevel (spread first, force the field after unless explicitly and deliberately opted into); add a DOMPurify SVG-profile pass before node.innerHTML = svg as defense-in-depth.


Minor (lower confidence / smaller blast radius, included for completeness)

  • ConnectionBanner.tsx:27-41: the "Reconnected" flash's auto-dismiss timer is cancelled by its own effect re-running (flashRecovered is both set and listed as a dependency), so the banner sticks indefinitely after the first reconnect instead of disappearing after 2s.
  • Message.tsx:124-139,276-314: regenerate/retry/feedback handlers have no in-flight/duplicate-submission guard — rapid double-clicks can fire the action twice before props reflect the first response.
  • script/sync-web-base-tokens.sh isn't wired into any build/CI step (confirmed via grep across package.json/Makefile/workflows), so web/src/styles/tokens.base.css can silently drift stale if tokens.css changes without a manual re-run.

Overall Risk: High

Driven primarily by Finding 1 (verified CI build break as currently configured — the "zero migration" claim doesn't hold without a CI fix) and Finding 2 (genuine concurrency/data-loss bugs in the flagship new AG-UI runtime adapter, reachable via realistic interaction patterns like fast stop→resend). Findings 3–5 are real but narrower in blast radius (rendering regression, cascade edge case, and an opt-in-gated XSS surface).

Top Findings

  1. CI build break: jcode-ui/compat.css has no producer in the unmodified ci.yml build step (Finding 1)
  2. AG-UI runtime concurrency bug corrupts isRunning + Composer silently drops messages/attachments (Finding 2)
  3. Streaming markdown permanently breaks loose-list rendering for all messages, not just while streaming (Finding 3)
  4. CSS @layer change loses to unlayered host CSS it used to beat, undermining the scoping migration's stated purpose (Finding 4)
  5. Mermaid plugin: overridable security level + unsanitized innerHTML (Finding 5, opt-in-gated)

Generated by Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 19

🧹 Nitpick comments (8)
packages/jcode-ui/src/voice/voice.css (1)

84-84: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep color fallbacks token-driven.

The #fff fallbacks contradict this file’s token-only styling contract. Define and rely on the corresponding --jcode-color-on-* tokens instead.

Also applies to: 210-210, 246-246

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/jcode-ui/src/voice/voice.css` at line 84, Remove the hardcoded `#fff`
fallbacks from the color declarations at the referenced locations in voice.css,
including the corresponding rules near the other affected lines. Use the
appropriate --jcode-color-on-* token directly, ensuring each token is defined
and the file remains token-only.
packages/jcode-ui/src/components/ModelSelector.tsx (1)

150-194: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add aria-activedescendant and focus restoration for screen reader support.

Two accessibility gaps in the popup menu:

  1. No aria-activedescendant: The search input is the focused element during keyboard navigation, but without aria-activedescendant pointing to the active option's id, screen reader users can't perceive which option is highlighted as they arrow through the list.

  2. No focus restoration: When the menu closes (via selection, Escape, or outside click), focus falls to <body> instead of returning to the trigger button. This breaks keyboard navigation flow.

♿ Proposed accessibility improvements
@@ -54,6 +54,7 @@
   const rootRef = useRef<HTMLDivElement>(null)
   const searchRef = useRef<HTMLInputElement>(null)
+  const triggerRef = useRef<HTMLButtonElement>(null)

@@ -150,6 +151,7 @@
       {open && (
-        <div className="jcode-model-selector__menu" role="listbox">
+        <div className="jcode-model-selector__menu" role="listbox" id={`${rootId}-listbox`}>
           <div className="jcode-model-selector__search">
             <MagnifyingGlassIcon className="jcode-model-selector__search-icon" />
             <input
               ref={searchRef}
               value={query}
               onChange={(e) => setQuery(e.target.value)}
               onKeyDown={onSearchKeyDown}
               placeholder="Search models…"
               aria-label="Search models"
+              aria-controls={`${rootId}-listbox`}
+              aria-activedescendant={flat[activeIndex] ? `${rootId}-opt-${flat[activeIndex].id}` : undefined}
             />
           </div>
@@ -174,6 +176,7 @@
                     <button
                       key={opt.id}
+                      id={`${rootId}-opt-${opt.id}`}
                       type="button"
                       role="option"
                       aria-selected={isSelected}

For focus restoration, update choose and the Escape handler to call triggerRef.current?.focus() after setOpen(false):

@@ -109,6 +109,7 @@
   function choose(id: string) {
     onChange(id)
     setOpen(false)
+    triggerRef.current?.focus()
     setQuery('')
   }
@@ -127,6 +128,7 @@
     } else if (e.key === 'Escape') {
       e.preventDefault()
       setOpen(false)
+      triggerRef.current?.focus()
     }

Also add ref={triggerRef} to the trigger <button> and generate a stable rootId (e.g., via useId()).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/jcode-ui/src/components/ModelSelector.tsx` around lines 150 - 194,
Update ModelSelector’s open-menu accessibility flow: create a stable rootId with
useId, attach triggerRef to the trigger button, set the search input’s
aria-activedescendant to the active option’s id, and assign matching stable ids
to rendered option buttons. In choose and the Escape handler, restore focus to
triggerRef.current after closing the menu; preserve existing keyboard and
selection behavior.
site/src/playground/component-demo.css (1)

652-679: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated light-palette variable block.

The 24-line --color-*/--code-*/--hljs-* block is copy-pasted identically between .jcode-live-demo.light and .jcode-demo-preview-slot.light. Consider grouping the selectors so the variables are declared once.

♻️ Proposed dedup
-.jcode-live-demo.light {
-  --color-background: `#f2f3f0`;
-  --color-surface: `#ffffff`;
-  ...
-  --hljs-punctuation: `#a1a1aa`;
-}
+.jcode-live-demo.light,
+.jcode-demo-preview-slot.light {
+  --color-background: `#f2f3f0`;
+  --color-surface: `#ffffff`;
+  ...
+  --hljs-punctuation: `#a1a1aa`;
+}

Also applies to: 699-727

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@site/src/playground/component-demo.css` around lines 652 - 679, Deduplicate
the identical light-palette custom property declarations shared by
`.jcode-live-demo.light` and `.jcode-demo-preview-slot.light`. Group both
selectors into one rule containing the existing `--color-*`, `--code-*`, and
`--hljs-*` variables, while preserving their current scoped light-mode behavior.
web/src/styles/tokens.base.css (1)

29-30: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Ignore value-keyword-case for the font stacks here
BlinkMacSystemFont, SFMono-Regular, and Menlo are font-family names, and this file is generated; relax the lint rule for font-family or update the source token file before regenerating.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/styles/tokens.base.css` around lines 29 - 30, Update the generated
font token handling for --font-sans and --font-mono so the value-keyword-case
lint rule is relaxed for font-family declarations, or modify the source token
definition and regenerate this file. Preserve the existing font stacks and their
casing.

Source: Linters/SAST tools

packages/jcode-ui/src/components/Attachment.tsx (1)

207-239: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add onError handler to PendingTile image for consistency with Attachment.

The Attachment component handles broken images via onError={() => setBroken(true)} with a fallback, but PendingTile's image (line 223) has no such guard. A corrupted-but-readable file could produce a broken <img> with no recovery UI. Consider adding a broken state with a fallback to the error/retry presentation.

♻️ Suggested onError handling for PendingTile
 const PendingTile = memo(function PendingTile({ item, size = 56 }: { item: PendingAttachmentItem; size?: number }) {
   const { attachment: a, status, remove, retry } = item
   const isError = status === 'error'
   const isUploading = status === 'uploading'
   const pct = Math.round((a.progress ?? 0) * 100)
+  const [broken, setBroken] = useState(false)
 
   // Image with inline data → thumbnail tile with an overlay.
-  if (a.kind === 'image' && a.data) {
+  if (a.kind === 'image' && a.data && !broken) {
     const src = imageSrc({ data: a.data, media_type: a.media_type || 'image/*', name: a.name })
     return (
       <div
         className={`jcode-pending-image${isError ? ' is-error' : ''}`}
         style={{ width: size, height: size }}
         title={a.error || a.name}
       >
-        <img src={src} alt={a.name} draggable={false} />
+        <img src={src} alt={a.name} draggable={false} onError={() => setBroken(true)} />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/jcode-ui/src/components/Attachment.tsx` around lines 207 - 239,
Update PendingTile to track image load failure and add an onError handler to its
thumbnail img, matching Attachment’s broken-image behavior. When the pending
image fails to load, render the existing error/retry presentation with the
appropriate fallback instead of leaving a broken image displayed.
packages/jcode-ui/src/styles/welcome.css (1)

3-150: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Scope non-portaled selectors under [data-jcode-ui] for consistency with conversation.css.

conversation.css (this same PR) explicitly documents the strategy: "every rule is scoped under [data-jcode-ui] so nothing leaks to host apps." welcome.css uses bare class selectors throughout, breaking that contract. The .jcode-quote-btn is a legitimate exception — it's portaled to document.body and cannot be scoped under [data-jcode-ui]. However, the remaining selectors (.jcode-welcome, .jcode-suggestions, .jcode-suggestion, .jcode-export-btn, .jcode-thread-followups) are rendered inside the component tree and should be scoped to prevent style leakage to host apps.

♻️ Proposed partial scoping
-.jcode-welcome {
+[data-jcode-ui] .jcode-welcome {
   display: flex;
   align-items: center;
   justify-content: center;
   min-height: 100%;
   padding-top: 3rem;
   padding-bottom: 3rem;
 }
-.jcode-welcome__inner {
+[data-jcode-ui] .jcode-welcome__inner {
   display: flex;
   flex-direction: column;
   align-items: center;
   text-align: center;
   gap: 0.5rem;
   max-width: 34rem;
   animation: jcode-fade-up var(--jcode-duration-slow) var(--jcode-ease-out) both;
 }
-.jcode-welcome__logo {
+[data-jcode-ui] .jcode-welcome__logo {
   margin-bottom: 0.25rem;
 }
-.jcode-welcome__title {
+[data-jcode-ui] .jcode-welcome__title {
   font-family: var(--jcode-font-sans);
   font-size: 1.15rem;
   font-weight: 600;
   letter-spacing: -0.01em;
   color: var(--jcode-color-foreground);
   margin: 0;
 }
-.jcode-welcome__subtitle {
+[data-jcode-ui] .jcode-welcome__subtitle {
   font-size: 0.85rem;
   line-height: 1.55;
   color: var(--jcode-color-muted-foreground);
   margin: 0;
 }
-.jcode-welcome__extra {
+[data-jcode-ui] .jcode-welcome__extra {
   margin-top: 1rem;
   width: 100%;
 }
 
 /* Suggestion pills — quiet chips that light up on hover. */
-.jcode-suggestions {
+[data-jcode-ui] .jcode-suggestions {
   display: flex;
   flex-wrap: wrap;
   justify-content: center;
   gap: 0.5rem;
 }
-.jcode-suggestions--scroll {
+[data-jcode-ui] .jcode-suggestions--scroll {
   flex-wrap: nowrap;
   justify-content: flex-start;
   overflow-x: auto;
   scrollbar-width: none;
   padding-bottom: 2px;
 }
-.jcode-suggestions--scroll::-webkit-scrollbar {
+[data-jcode-ui] .jcode-suggestions--scroll::-webkit-scrollbar {
   display: none;
 }
-.jcode-suggestion {
+[data-jcode-ui] .jcode-suggestion {
   font: inherit;
   font-size: 0.8rem;
   color: var(--jcode-color-foreground);
   background: var(--jcode-color-surface);
   border: 1px solid var(--jcode-color-border);
   border-radius: var(--jcode-radius-pill);
   padding: 0.35rem 0.85rem;
   cursor: pointer;
   white-space: nowrap;
   box-shadow: var(--jcode-shadow-sm);
   transition:
     border-color var(--jcode-duration-fast) var(--jcode-ease-out),
     background-color var(--jcode-duration-fast) var(--jcode-ease-out),
     transform var(--jcode-duration-fast) var(--jcode-ease-out);
 }
-.jcode-suggestion:hover:not(:disabled) {
+[data-jcode-ui] .jcode-suggestion:hover:not(:disabled) {
   border-color: var(--jcode-accent-border);
   background: var(--jcode-accent-wash-soft);
 }
-.jcode-suggestion:active:not(:disabled) {
+[data-jcode-ui] .jcode-suggestion:active:not(:disabled) {
   transform: scale(0.97);
 }
-.jcode-suggestion:disabled {
+[data-jcode-ui] .jcode-suggestion:disabled {
   opacity: 0.5;
   cursor: default;
 }
-.jcode-suggestion:focus-visible {
+[data-jcode-ui] .jcode-suggestion:focus-visible {
   outline: 2px solid var(--jcode-accent-border);
   outline-offset: 1px;
 }
 
 /* Follow-up placement under the last turn: align with the chat column. */
-.jcode-thread-followups {
+[data-jcode-ui] .jcode-thread-followups {
   padding-top: 0.25rem;
   padding-bottom: 0.75rem;
 }
-.jcode-thread-followups .jcode-suggestions {
+[data-jcode-ui] .jcode-thread-followups .jcode-suggestions {
   justify-content: flex-start;
 }
 
 /* Export button — quiet chrome control. */
-.jcode-export-btn {
+[data-jcode-ui] .jcode-export-btn {
   display: inline-flex;
   align-items: center;
   gap: 0.35rem;
   font: inherit;
   font-size: 0.72rem;
   color: var(--jcode-color-muted-foreground);
   background: transparent;
   border: 1px solid var(--jcode-color-border);
   border-radius: var(--jcode-radius-md);
   padding: 0.25rem 0.6rem;
   cursor: pointer;
   transition:
     color var(--jcode-duration-fast) var(--jcode-ease-out),
     border-color var(--jcode-duration-fast) var(--jcode-ease-out),
     background-color var(--jcode-duration-fast) var(--jcode-ease-out);
 }
-.jcode-export-btn:hover {
+[data-jcode-ui] .jcode-export-btn:hover {
   color: var(--jcode-color-foreground);
   background: var(--jcode-neutral-wash-soft);
 }
-.jcode-export-btn:focus-visible {
+[data-jcode-ui] .jcode-export-btn:focus-visible {
   outline: 2px solid var(--jcode-accent-border);
   outline-offset: 1px;
 }
 
 /* Floating quote-selection button (portal to body) — cannot be scoped under [data-jcode-ui]. */
 .jcode-quote-btn {
   position: fixed;
   /* ... unchanged ... */
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/jcode-ui/src/styles/welcome.css` around lines 3 - 150, Scope all
non-portaled selectors in welcome.css under [data-jcode-ui], including
.jcode-welcome, .jcode-suggestions and its states, .jcode-suggestion and its
states, .jcode-thread-followups, and .jcode-export-btn and its states. Keep
.jcode-quote-btn and its hover rule unscoped because it is portaled to
document.body.
internal-doc/chat-ui-roadmap.md (1)

34-34: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fix markdown table formatting flagged by markdownlint.

Unescaped pipe characters in table cell content create extra columns, and the 生态推广 table rows are missing their third column. This causes content truncation in rendered markdown.

  • Line 34: Escape the | characters in 'allow_once'|'allow_always'|'deny'|'custom' as \|.
  • Line 43: Same issue — locate and escape the stray | in the cell content.
  • Lines 88-93: Add the missing 细节 column to each row (or reduce the header to 2 columns).

As per coding guidelines, internal PRDs and design notes belong in internal-doc/ — file placement is correct.

Also applies to: 43-43, 88-93

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal-doc/chat-ui-roadmap.md` at line 34, 修正 chat-ui-roadmap.md 中的
Markdown 表格格式:在第 34 行和第 43 行相关单元格内,将类型联合中的竖线转义为 \|,避免被解析为额外列;检查“生态推广”表格并为第 88-93
行每行补齐缺失的“细节”列,或相应调整表头为两列,确保表格内容完整渲染。

Sources: Coding guidelines, Linters/SAST tools

packages/jcode-ui-core/src/primitives/ApprovalBlock.tsx (1)

118-123: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Render DefaultPending as an element for consistency.

renderResolved falls back to <DefaultResolved .../> (an element) but this branch invokes DefaultPending({...}) as a plain function. It works today only because DefaultPending uses no hooks; calling it directly gives it no fiber, so adding any hook later (behind ??) would violate the rules of hooks. Prefer <DefaultPending approval={approval} actions={decisionActions} />.

♻️ Consistent element rendering
-      {renderPending?.(approval, decisionActions) ?? DefaultPending({ approval, actions: decisionActions })}
+      {renderPending?.(approval, decisionActions) ?? <DefaultPending approval={approval} actions={decisionActions} />}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/jcode-ui-core/src/primitives/ApprovalBlock.tsx` around lines 118 -
123, Update the fallback in ApprovalBlock’s render path to render DefaultPending
as a JSX element with approval and decisionActions props, matching the
renderResolved fallback; leave the custom renderPending?.(approval,
decisionActions) behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/jcode-ui-core/src/primitives/Composer.tsx`:
- Around line 444-462: Update the send callback to clear the dictation buffers
dictBaseRef and dictFinalRef when a message is sent, alongside the existing text
and image resets. Include these refs in the useCallback dependency list as
required by their usage, preserving the existing send behavior.
- Around line 458-460: Update the composer reset flow around setPending([]) to
preserve or explicitly cancel all non-done attachment slots before clearing
state. Ensure uploading and error attachments invoke the existing
attachmentAdapter.remove() cleanup path, while completed attachments retain
their current behavior.

In `@packages/jcode-ui-core/src/runtime/aguiRuntime.ts`:
- Around line 448-454: Replace the no-op enqueueMessage implementation in the
AG-UI adapter with behavior that preserves messages submitted while isRunning,
either by buffering them until the current turn completes or by preventing
Composer.send() from submitting during a run. Ensure the draft is not silently
lost when Enter is pressed, while keeping the existing no-op behavior for
unrelated approval, ask-user, and edit channels.

In `@packages/jcode-ui/scripts/smoke-markdown.mjs`:
- Around line 88-89: Update the hashString determinism assertion by assigning
each hashString('abc') call to a separate variable before comparing them.
Preserve the existing comparison and content-distinction assertion while
avoiding the self-comparison lint failure.

In `@packages/jcode-ui/src/canvas/canvas.css`:
- Around line 28-43: Add an empty line before the background declaration in the
[data-jcode-ui] .react-flow and .jcode-wf-canvas .react-flow rule, separating
background from the preceding --xy-* custom properties to satisfy stylelint.

In `@packages/jcode-ui/src/canvas/WorkflowCanvas.tsx`:
- Around line 41-72: Update WorkflowCanvas to destructure panOnDrag,
nodesDraggable, nodesConnectable, and elementsSelectable from its props with
defaults derived from interactive, excluding them from rest. Pass these
destructured values directly to ReactFlow so spreading rest cannot override the
interactive-controlled behavior.

In `@packages/jcode-ui/src/components/ConnectionBanner.tsx`:
- Around line 27-41: Update the useEffect managing flashRecovered so it tracks
the flash state through a ref, removes flashRecovered from the dependency array,
and depends only on connection. Preserve the immediate clear behavior when the
connection drops and ensure the RECONNECTED_MS timeout remains active long
enough to auto-hide the recovery banner.

In `@packages/jcode-ui/src/styles/components.css`:
- Around line 409-412: Remove the deprecated word-break declaration from the
affected style rule in components.css, relying on overflow-wrap: anywhere for
wrapping behavior; alternatively set word-break to normal to satisfy Stylelint.

In `@packages/jcode-ui/src/styles/p5.css`:
- Line 373: Replace the deprecated word-break: break-word declaration with
overflow-wrap: break-word at all three occurrences, including the declarations
near the referenced styles, while preserving the surrounding style rules.

In `@packages/jcode-ui/src/toolRenderers/fileTree.tsx`:
- Around line 124-133: Update cleanLine to implement the documented
trailing-annotation cleanup before returning: remove directory markers such as
“(dir)” and size-column suffixes while preserving valid paths and filenames, so
parsePathList does not create spurious child nodes. If this behavior is not
intended, remove the misleading comment instead.

In `@packages/jcode-ui/src/voice/Transcription.tsx`:
- Around line 116-118: Update the setRef callback in Transcription so it assigns
activeRef.current only when both isActive is true and node is non-null; ignore
callback-detach calls with null so an old active segment cannot clear the newly
assigned ref during backward seeking.

In `@packages/jcode-ui/src/voice/voice.css`:
- Line 33: Replace the deprecated clip declaration in the visually hidden voice
styles with clip-path using an inset(50%) value, preserving the existing
hidden-region behavior.

In `@site/docs/chat-ui/api/generated.md`:
- Around line 20-140: Align the generated API index links with the anchors
emitted by the corresponding sections: update the `#jcode-ui-*` targets in the
symbol index to match the actual rendered slugs, or add explicit matching anchor
ids to each generated section. Apply the change consistently across component,
function, interface, and type entries so every index link resolves correctly.
- Around line 539-580: Update the API docs extraction regex in
generate_jcode_ui_api_docs.mjs to associate each JSDoc comment only with its
immediately following declaration, preventing it from spanning multiple comment
blocks or including the stray */. Regenerate the documentation so the
balanceInlineCode entry contains only its own declaration and documentation.

In `@site/docs/chat-ui/components/artifact.md`:
- Around line 17-25: Update the Artifact example to import the documented
DocumentIcon symbol before it is used in the icon prop, keeping the existing
Artifact and stylesheet imports and example behavior unchanged.

In `@site/docs/chat-ui/components/branch-picker.md`:
- Around line 49-54: Align the top-level Message.content value with the active
v2 entry by removing the extra “— lock-free reads.” text, so both strings
exactly match while preserving the existing versions data.

In `@site/docs/chat-ui/components/connection-banner.md`:
- Line 48: Update the Runtime state Markdown link in connection-banner.md to
target /chat-ui/docs/runtime, matching the actual documentation route; do not
retain the incorrect /chat-ui/docs/guides/runtime path unless an intentional
redirect is added.

In `@site/src/playground/demoSources.ts`:
- Around line 471-486: Make the `artifact` demo snippet self-contained by
declaring or importing valid implementations for `copy`, `source`, and `setOpen`
within the generated `Demo` example. Preserve the existing `Artifact` UI and
ensure the pasted snippet compiles without relying on identifiers from the
surrounding playground.

In `@web/src/styles.css`:
- Around line 14-21: Move the four stylesheet imports in styles.css above all
preceding CSS rules so they remain valid and are processed by browsers. Preserve
their current order and keep the generated theme import alongside the other
imports.

---

Nitpick comments:
In `@internal-doc/chat-ui-roadmap.md`:
- Line 34: 修正 chat-ui-roadmap.md 中的 Markdown 表格格式:在第 34 行和第 43
行相关单元格内,将类型联合中的竖线转义为 \|,避免被解析为额外列;检查“生态推广”表格并为第 88-93
行每行补齐缺失的“细节”列,或相应调整表头为两列,确保表格内容完整渲染。

In `@packages/jcode-ui-core/src/primitives/ApprovalBlock.tsx`:
- Around line 118-123: Update the fallback in ApprovalBlock’s render path to
render DefaultPending as a JSX element with approval and decisionActions props,
matching the renderResolved fallback; leave the custom renderPending?.(approval,
decisionActions) behavior unchanged.

In `@packages/jcode-ui/src/components/Attachment.tsx`:
- Around line 207-239: Update PendingTile to track image load failure and add an
onError handler to its thumbnail img, matching Attachment’s broken-image
behavior. When the pending image fails to load, render the existing error/retry
presentation with the appropriate fallback instead of leaving a broken image
displayed.

In `@packages/jcode-ui/src/components/ModelSelector.tsx`:
- Around line 150-194: Update ModelSelector’s open-menu accessibility flow:
create a stable rootId with useId, attach triggerRef to the trigger button, set
the search input’s aria-activedescendant to the active option’s id, and assign
matching stable ids to rendered option buttons. In choose and the Escape
handler, restore focus to triggerRef.current after closing the menu; preserve
existing keyboard and selection behavior.

In `@packages/jcode-ui/src/styles/welcome.css`:
- Around line 3-150: Scope all non-portaled selectors in welcome.css under
[data-jcode-ui], including .jcode-welcome, .jcode-suggestions and its states,
.jcode-suggestion and its states, .jcode-thread-followups, and .jcode-export-btn
and its states. Keep .jcode-quote-btn and its hover rule unscoped because it is
portaled to document.body.

In `@packages/jcode-ui/src/voice/voice.css`:
- Line 84: Remove the hardcoded `#fff` fallbacks from the color declarations at
the referenced locations in voice.css, including the corresponding rules near
the other affected lines. Use the appropriate --jcode-color-on-* token directly,
ensuring each token is defined and the file remains token-only.

In `@site/src/playground/component-demo.css`:
- Around line 652-679: Deduplicate the identical light-palette custom property
declarations shared by `.jcode-live-demo.light` and
`.jcode-demo-preview-slot.light`. Group both selectors into one rule containing
the existing `--color-*`, `--code-*`, and `--hljs-*` variables, while preserving
their current scoped light-mode behavior.

In `@web/src/styles/tokens.base.css`:
- Around line 29-30: Update the generated font token handling for --font-sans
and --font-mono so the value-keyword-case lint rule is relaxed for font-family
declarations, or modify the source token definition and regenerate this file.
Preserve the existing font stacks and their casing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 826a1c46-0df4-4c36-902c-56c45db06697

📥 Commits

Reviewing files that changed from the base of the PR and between 7b4c48f and cd5a0f3.

⛔ 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 (132)
  • examples/jcode-ui-minimal/package.json
  • examples/jcode-ui-zustand/package.json
  • internal-doc/chat-ui-competitive-analysis.md
  • internal-doc/chat-ui-roadmap.md
  • packages/jcode-ui-core/package.json
  • packages/jcode-ui-core/src/export/markdown.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/attachmentAdapter.ts
  • packages/jcode-ui-core/src/primitives/index.ts
  • packages/jcode-ui-core/src/runtime/agui.selftest.ts
  • packages/jcode-ui-core/src/runtime/agui.ts
  • packages/jcode-ui-core/src/runtime/aguiEvents.ts
  • packages/jcode-ui-core/src/runtime/aguiRuntime.ts
  • packages/jcode-ui-core/src/runtime/index.ts
  • packages/jcode-ui-core/src/runtime/mockRuntime.ts
  • packages/jcode-ui-core/src/threads/context.tsx
  • packages/jcode-ui-core/src/threads/index.ts
  • packages/jcode-ui-core/src/threads/store.ts
  • packages/jcode-ui-core/src/types/index.ts
  • packages/jcode-ui/CHANGELOG.md
  • packages/jcode-ui/README.md
  • packages/jcode-ui/fixture/main.tsx
  • packages/jcode-ui/package.json
  • packages/jcode-ui/scripts/generate-compat-css.mjs
  • packages/jcode-ui/scripts/smoke-markdown.mjs
  • packages/jcode-ui/src/canvas/CanvasControls.tsx
  • packages/jcode-ui/src/canvas/CanvasPanel.tsx
  • packages/jcode-ui/src/canvas/WorkflowCanvas.tsx
  • packages/jcode-ui/src/canvas/WorkflowEdge.tsx
  • packages/jcode-ui/src/canvas/WorkflowNode.tsx
  • packages/jcode-ui/src/canvas/canvas.css
  • packages/jcode-ui/src/canvas/index.ts
  • packages/jcode-ui/src/canvas/toolTreeToGraph.ts
  • packages/jcode-ui/src/components/ApprovalBanner.tsx
  • packages/jcode-ui/src/components/Artifact.tsx
  • packages/jcode-ui/src/components/AskUserCard.tsx
  • packages/jcode-ui/src/components/Attachment.tsx
  • packages/jcode-ui/src/components/BranchPicker.tsx
  • packages/jcode-ui/src/components/ChatInput.tsx
  • packages/jcode-ui/src/components/CompactToolRow.tsx
  • packages/jcode-ui/src/components/ConnectionBanner.tsx
  • packages/jcode-ui/src/components/ContextBar.tsx
  • packages/jcode-ui/src/components/ExploringGroupCard.tsx
  • packages/jcode-ui/src/components/ExportButton.tsx
  • packages/jcode-ui/src/components/Message.tsx
  • packages/jcode-ui/src/components/ModelSelector.tsx
  • packages/jcode-ui/src/components/QuoteSelection.tsx
  • packages/jcode-ui/src/components/Reasoning.tsx
  • packages/jcode-ui/src/components/Sources.tsx
  • packages/jcode-ui/src/components/Suggestions.tsx
  • packages/jcode-ui/src/components/TaskList.tsx
  • packages/jcode-ui/src/components/Thread.tsx
  • packages/jcode-ui/src/components/ThreadList.tsx
  • packages/jcode-ui/src/components/ThreadWelcome.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/markdown.ts
  • packages/jcode-ui/src/lib/streamingMarkdown.ts
  • packages/jcode-ui/src/lib/useStreamingMarkdown.ts
  • packages/jcode-ui/src/plugins/external-modules.d.ts
  • packages/jcode-ui/src/plugins/katex.ts
  • packages/jcode-ui/src/plugins/mermaid.ts
  • packages/jcode-ui/src/styles/animations.css
  • packages/jcode-ui/src/styles/components.css
  • packages/jcode-ui/src/styles/composer2.css
  • packages/jcode-ui/src/styles/conversation.css
  • packages/jcode-ui/src/styles/entry.css
  • packages/jcode-ui/src/styles/markdown-chrome.css
  • packages/jcode-ui/src/styles/p5.css
  • packages/jcode-ui/src/styles/shadcn.css
  • packages/jcode-ui/src/styles/threadlist.css
  • packages/jcode-ui/src/styles/tokens.css
  • packages/jcode-ui/src/styles/welcome.css
  • packages/jcode-ui/src/toolRenderers/browserShot.tsx
  • packages/jcode-ui/src/toolRenderers/diff.tsx
  • packages/jcode-ui/src/toolRenderers/fileTree.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/stackTrace.tsx
  • packages/jcode-ui/src/toolRenderers/team.tsx
  • packages/jcode-ui/src/toolRenderers/terminal.tsx
  • packages/jcode-ui/src/toolRenderers/testResults.tsx
  • packages/jcode-ui/src/toolRenderers/todo.tsx
  • packages/jcode-ui/src/voice/AudioPlayer.tsx
  • packages/jcode-ui/src/voice/SpeechInput.tsx
  • packages/jcode-ui/src/voice/Transcription.tsx
  • packages/jcode-ui/src/voice/VoiceVisualizer.tsx
  • packages/jcode-ui/src/voice/index.ts
  • packages/jcode-ui/src/voice/voice.css
  • script/sync-web-base-tokens.sh
  • site/docs/chat-ui/api/generated.md
  • site/docs/chat-ui/comparison.md
  • site/docs/chat-ui/components.md
  • site/docs/chat-ui/components/artifact.md
  • site/docs/chat-ui/components/branch-picker.md
  • site/docs/chat-ui/components/canvas.md
  • site/docs/chat-ui/components/connection-banner.md
  • site/docs/chat-ui/components/export-quote.md
  • site/docs/chat-ui/components/model-selector.md
  • site/docs/chat-ui/components/task-list.md
  • site/docs/chat-ui/components/thread-list.md
  • site/docs/chat-ui/components/thread-welcome.md
  • site/docs/chat-ui/components/tool-renderers-code.md
  • site/docs/chat-ui/components/voice.md
  • site/docs/chat-ui/guides/migration-0.2.md
  • site/package.json
  • site/pnpm-workspace.yaml
  • site/src/pages/ChatUIPage.tsx
  • site/src/pages/chatui/ChatUiDocPage.tsx
  • site/src/playground/ChatDemo.tsx
  • site/src/playground/ComponentDemo.tsx
  • site/src/playground/component-demo.css
  • site/src/playground/demoSources.ts
  • site/vite.config.ts
  • web/fixture-tool-ux/main.tsx
  • web/src/i18n/locales/en.ts
  • web/src/i18n/locales/ja.ts
  • web/src/i18n/locales/ko.ts
  • web/src/i18n/locales/zh-Hans.ts
  • web/src/i18n/locales/zh-Hant.ts
  • web/src/styles.css
  • web/src/styles/tokens.base.css

Comment on lines 444 to +462
const send = useCallback(() => {
if (!canSend) return
const imgs = images.length > 0 ? images : undefined
const doneNow = pending.filter((s) => s.status === 'done').map((s) => s.attachment)
const attachmentImages: ChatImage[] = doneNow
.filter((a) => a.kind === 'image' && a.data)
.map((a) => ({ data: a.data as string, media_type: a.media_type || 'image/*', name: a.name }))
const allImages = [...images, ...attachmentImages]
const imgs = allImages.length > 0 ? allImages : undefined
if (isRunning) {
actions.enqueueMessage(text.trim(), imgs)
} else {
actions.sendMessage(text.trim(), imgs)
}
if (doneNow.length > 0) onSendAttachments?.(doneNow)
setText('')
setImages([])
setPending([])
onSent?.()
}, [actions, canSend, images, isRunning, onSent, text])
}, [actions, canSend, images, isRunning, onSendAttachments, onSent, pending, text])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant file and the dictation-related symbols.
git ls-files 'packages/jcode-ui-core/src/primitives/Composer.tsx'
ast-grep outline packages/jcode-ui-core/src/primitives/Composer.tsx --view expanded || true
rg -n "dictBaseRef|dictFinalRef|enableDictation|onresult|recognition|send = useCallback|setText\(''\)" packages/jcode-ui-core/src/primitives/Composer.tsx
sed -n '1,260p' packages/jcode-ui-core/src/primitives/Composer.tsx
sed -n '260,620p' packages/jcode-ui-core/src/primitives/Composer.tsx

Repository: cnjack/jcode

Length of output: 25416


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('packages/jcode-ui-core/src/primitives/Composer.tsx')
text = p.read_text()
for needle in ['dictBaseRef', 'dictFinalRef', 'enableDictation', 'onresult', 'send = useCallback', "setText('')"]:
    print(f'== {needle} ==')
    for i, line in enumerate(text.splitlines(), 1):
        if needle in line:
            start = max(1, i-12)
            end = min(len(text.splitlines()), i+20)
            for j in range(start, end+1):
                print(f'{j:4}: {text.splitlines()[j-1]}')
            print()
            break
PY

Repository: cnjack/jcode

Length of output: 8427


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
p = Path('packages/jcode-ui-core/src/primitives/Composer.tsx')
lines = p.read_text().splitlines()
needles = ['dictBaseRef', 'dictFinalRef', 'enableDictation', 'onresult', 'send = useCallback', "setText('')"]
for needle in needles:
    print(f'## {needle}')
    for i, line in enumerate(lines, 1):
        if needle in line:
            start = max(1, i-15)
            end = min(len(lines), i+35)
            for j in range(start, end+1):
                print(f'{j:4}: {lines[j-1]}')
            print()
            break
PY

Repository: cnjack/jcode

Length of output: 12425


Reset dictation buffers on send
When dictation is active, clear dictBaseRef and dictFinalRef in send() so the next recognition result doesn’t repopulate the textarea with text that was already sent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/jcode-ui-core/src/primitives/Composer.tsx` around lines 444 - 462,
Update the send callback to clear the dictation buffers dictBaseRef and
dictFinalRef when a message is sent, alongside the existing text and image
resets. Include these refs in the useCallback dependency list as required by
their usage, preserving the existing send behavior.

Comment on lines 458 to +460
setText('')
setImages([])
setPending([])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the target file and related attachment APIs.
ast-grep outline packages/jcode-ui-core/src/primitives/Composer.tsx --view expanded || true
printf '\n--- search for pending / enqueueMessage / remove / upload status ---\n'
rg -n "pending|enqueueMessage|onSendAttachments|remove\\(|uploading|error|done" packages/jcode-ui-core/src -S

Repository: cnjack/jcode

Length of output: 15968


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Composer.tsx relevant slice ---'
sed -n '316,470p' packages/jcode-ui-core/src/primitives/Composer.tsx

printf '\n%s\n' '--- attachmentAdapter.ts ---'
sed -n '1,180p' packages/jcode-ui-core/src/primitives/attachmentAdapter.ts

Repository: cnjack/jcode

Length of output: 9771


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find tests/docs that describe composer send semantics or pending cleanup.
rg -n "setPending\\(\\[\\]\\)|onSendAttachments|remove\\?|uploading → done / error|sendMessage\\(text, images\\)|enqueueMessage\\(text, imgs\\)|pending-attachment" packages/jcode-ui-core/src -S

Repository: cnjack/jcode

Length of output: 1705


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,35p' packages/jcode-ui-core/src/primitives/Composer.tsx

Repository: cnjack/jcode

Length of output: 1709


Preserve non-done attachments before clearing the composer.
setPending([]) drops uploading/error slots without calling attachmentAdapter.remove(), so an in-flight upload or failed retry can disappear from the UI while its cleanup never runs. Cancel or retain the non-done slots first.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/jcode-ui-core/src/primitives/Composer.tsx` around lines 458 - 460,
Update the composer reset flow around setPending([]) to preserve or explicitly
cancel all non-done attachment slots before clearing state. Ensure uploading and
error attachments invoke the existing attachmentAdapter.remove() cleanup path,
while completed attachments retain their current behavior.

Comment on lines +448 to +454
// AG-UI has no client-side queue/approval/ask_user/edit channel in this
// adapter; kept present with full types so the UI never crashes calling them.
enqueueMessage: () => {},
removeQueuedMessage: () => {},
resolveApproval: () => {},
submitAskUser: () => {},
editMessage: () => {},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and locate Composer.send plus AG UI runtime actions.
git ls-files 'packages/jcode-ui-core/src/runtime/aguiRuntime.ts' 'packages/jcode-ui-core/src/**/Composer*' 'packages/jcode-ui-core/src/**/composer*' 'packages/jcode-ui-core/src/**/runtime*' | sed 's#^`#FILE` #'

printf '\n--- aguiRuntime outline ---\n'
ast-grep outline packages/jcode-ui-core/src/runtime/aguiRuntime.ts --view expanded || true

printf '\n--- search for enqueueMessage / send / isRunning ---\n'
rg -n "enqueueMessage|removeQueuedMessage|resolveApproval|submitAskUser|editMessage|isRunning|setText\\(''\\)|setText\\(\"\"\\)|send\\(" packages/jcode-ui-core/src -g '!**/*.map'

Repository: cnjack/jcode

Length of output: 9966


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read only the relevant sections once the files are located.
sed -n '420,470p' packages/jcode-ui-core/src/runtime/aguiRuntime.ts 2>/dev/null || true

# Search for the Composer implementation if present.
for f in $(git ls-files 'packages/jcode-ui-core/src/**' | grep -E 'Composer|composer'); do
  echo "--- $f ---"
  wc -l "$f"
done

Repository: cnjack/jcode

Length of output: 1581


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the AG UI runtime implementation and the Composer send path.
sed -n '430,470p' packages/jcode-ui-core/src/runtime/aguiRuntime.ts 2>/dev/null || true

# Locate the Composer implementation and its send handler.
rg -n --hidden --glob '!**/dist/**' --glob '!**/build/**' --glob '!**/node_modules/**' \
  "function send\\(|const send =|send\\s*\\(|enqueueMessage\\(|isRunning" packages/jcode-ui-core/src

Repository: cnjack/jcode

Length of output: 5995


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the Composer send handler and keyboard/submission path.
sed -n '436,560p' packages/jcode-ui-core/src/primitives/Composer.tsx

Repository: cnjack/jcode

Length of output: 4155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Also inspect any docs/comments around streaming behavior in Composer.
sed -n '1,120p' packages/jcode-ui-core/src/primitives/Composer.tsx

Repository: cnjack/jcode

Length of output: 5491


enqueueMessage needs a real queue here

Composer.send() calls enqueueMessage() when isRunning, then clears the textarea. With this no-op, pressing Enter during a run drops the draft without any feedback. Either buffer the message until the turn ends or prevent send while running.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/jcode-ui-core/src/runtime/aguiRuntime.ts` around lines 448 - 454,
Replace the no-op enqueueMessage implementation in the AG-UI adapter with
behavior that preserves messages submitted while isRunning, either by buffering
them until the current turn completes or by preventing Composer.send() from
submitting during a run. Ensure the draft is not silently lost when Enter is
pressed, while keeping the existing no-op behavior for unrelated approval,
ask-user, and edit channels.

Comment on lines +88 to +89
assert('hashString is deterministic', hashString('abc') === hashString('abc'))
assert('hashString distinguishes content', hashString('abc') !== hashString('abd'))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Avoid the self-comparison lint failure.

Biome flags hashString('abc') === hashString('abc'). Store the two calls in separate variables before comparing so the determinism assertion remains valid and lint passes.

🧰 Tools
🪛 Biome (2.5.1)

[error] 88-88: This comparison uses the same expression on both sides.

(lint/suspicious/noSelfCompare)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/jcode-ui/scripts/smoke-markdown.mjs` around lines 88 - 89, Update
the hashString determinism assertion by assigning each hashString('abc') call to
a separate variable before comparing them. Preserve the existing comparison and
content-distinction assertion while avoiding the self-comparison lint failure.

Source: Linters/SAST tools

Comment on lines +28 to +43
[data-jcode-ui] .react-flow,
.jcode-wf-canvas .react-flow {
--xy-background-color: transparent;
--xy-background-pattern-color: var(--jcode-color-border);
--xy-edge-stroke: var(--jcode-color-border);
--xy-edge-stroke-width: 1.5;
--xy-edge-stroke-selected: var(--jcode-color-primary);
--xy-connectionline-stroke: var(--jcode-accent-border);
--xy-connectionline-stroke-width: 1.5;
--xy-handle-background-color: var(--jcode-color-surface);
--xy-handle-border-color: var(--jcode-color-border);
--xy-edge-label-background-color: var(--jcode-color-surface);
--xy-edge-label-color: var(--jcode-color-muted-foreground);
--xy-attribution-background-color: transparent;
background: transparent;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add empty line before background declaration to satisfy stylelint.

Stylelint reports a declaration-empty-line-before error at line 42: background: transparent; follows a series of --xy-* custom property declarations and needs a separating empty line.

🎨 Proposed fix
  --xy-attribution-background-color: transparent;
+
  background: transparent;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[data-jcode-ui] .react-flow,
.jcode-wf-canvas .react-flow {
--xy-background-color: transparent;
--xy-background-pattern-color: var(--jcode-color-border);
--xy-edge-stroke: var(--jcode-color-border);
--xy-edge-stroke-width: 1.5;
--xy-edge-stroke-selected: var(--jcode-color-primary);
--xy-connectionline-stroke: var(--jcode-accent-border);
--xy-connectionline-stroke-width: 1.5;
--xy-handle-background-color: var(--jcode-color-surface);
--xy-handle-border-color: var(--jcode-color-border);
--xy-edge-label-background-color: var(--jcode-color-surface);
--xy-edge-label-color: var(--jcode-color-muted-foreground);
--xy-attribution-background-color: transparent;
background: transparent;
}
[data-jcode-ui] .react-flow,
.jcode-wf-canvas .react-flow {
--xy-background-color: transparent;
--xy-background-pattern-color: var(--jcode-color-border);
--xy-edge-stroke: var(--jcode-color-border);
--xy-edge-stroke-width: 1.5;
--xy-edge-stroke-selected: var(--jcode-color-primary);
--xy-connectionline-stroke: var(--jcode-accent-border);
--xy-connectionline-stroke-width: 1.5;
--xy-handle-background-color: var(--jcode-color-surface);
--xy-handle-border-color: var(--jcode-color-border);
--xy-edge-label-background-color: var(--jcode-color-surface);
--xy-edge-label-color: var(--jcode-color-muted-foreground);
--xy-attribution-background-color: transparent;
background: transparent;
}
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 42-42: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/jcode-ui/src/canvas/canvas.css` around lines 28 - 43, Add an empty
line before the background declaration in the [data-jcode-ui] .react-flow and
.jcode-wf-canvas .react-flow rule, separating background from the preceding
--xy-* custom properties to satisfy stylelint.

Source: Linters/SAST tools

Comment on lines +17 to +25
```tsx
import { Artifact } from 'jcode-ui'
import 'jcode-ui/styles.css'

<Artifact
title="vite.config.ts"
subtitle="7 lines · typescript"
icon={<DocumentIcon />}
actions={<button type="button" onClick={() => copy(source)}>Copy</button>}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Import the documented icon.

DocumentIcon is used but never imported, so the copy-paste example does not compile.

Proposed fix
 import { Artifact } from 'jcode-ui'
+import { DocumentIcon } from '`@heroicons/react/24/outline`'
 import 'jcode-ui/styles.css'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```tsx
import { Artifact } from 'jcode-ui'
import 'jcode-ui/styles.css'
<Artifact
title="vite.config.ts"
subtitle="7 lines · typescript"
icon={<DocumentIcon />}
actions={<button type="button" onClick={() => copy(source)}>Copy</button>}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@site/docs/chat-ui/components/artifact.md` around lines 17 - 25, Update the
Artifact example to import the documented DocumentIcon symbol before it is used
in the icon prop, keeping the existing Artifact and stylesheet imports and
example behavior unchanged.

Source: Coding guidelines

Comment on lines +49 to +54
content: 'Use sync.Map for the shared registry — lock-free reads.', // mirrors v2
timestamp: Date.now(),
activeVersionId: 'v2',
versions: [
{ id: 'v1', content: 'Wrap map access in a sync.Mutex.', timestamp: Date.now() },
{ id: 'v2', content: 'Use sync.Map for the shared registry.', timestamp: Date.now() },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Example content doesn't match the v2 version it claims to mirror.

Line 17 states Message.content always mirrors the active version, and the comment on line 49 says // mirrors v2, but the content string includes "— lock-free reads." which is absent from v2's content on line 54. Align the two strings to avoid confusing readers about the data model.

📝 Suggested fix
-  content: 'Use sync.Map for the shared registry — lock-free reads.', // mirrors v2
+  content: 'Use sync.Map for the shared registry.', // mirrors v2
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
content: 'Use sync.Map for the shared registry — lock-free reads.', // mirrors v2
timestamp: Date.now(),
activeVersionId: 'v2',
versions: [
{ id: 'v1', content: 'Wrap map access in a sync.Mutex.', timestamp: Date.now() },
{ id: 'v2', content: 'Use sync.Map for the shared registry.', timestamp: Date.now() },
content: 'Use sync.Map for the shared registry.', // mirrors v2
timestamp: Date.now(),
activeVersionId: 'v2',
versions: [
{ id: 'v1', content: 'Wrap map access in a sync.Mutex.', timestamp: Date.now() },
{ id: 'v2', content: 'Use sync.Map for the shared registry.', timestamp: Date.now() },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@site/docs/chat-ui/components/branch-picker.md` around lines 49 - 54, Align
the top-level Message.content value with the active v2 entry by removing the
extra “— lock-free reads.” text, so both strings exactly match while preserving
the existing versions data.


## Related

- [Runtime state](/chat-ui/docs/guides/runtime)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the runtime guide page exists
fd -i "runtime" site/docs/chat-ui/guides/ 2>/dev/null || echo "NOT FOUND in site/docs/chat-ui/guides/"
fd -i "runtime" site/docs/chat-ui/ 2>/dev/null || echo "NOT FOUND in site/docs/chat-ui/"

Repository: cnjack/jcode

Length of output: 212


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files matching runtime under chat-ui =="
fd -a -t f 'runtime.md' site/docs/chat-ui || true
echo

echo "== any guides/runtime references =="
rg -n --hidden --glob 'site/docs/**' 'guides/runtime|/chat-ui/docs/guides/runtime|Runtime state' site/docs || true
echo

echo "== chat-ui docs tree (top levels) =="
find site/docs/chat-ui -maxdepth 2 -type f | sort | sed -n '1,200p'

Repository: cnjack/jcode

Length of output: 2369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== front matter for runtime docs =="
sed -n '1,40p' site/docs/chat-ui/runtime.md
echo

echo "== front matter for guides index =="
sed -n '1,40p' site/docs/chat-ui/guides.md
echo

echo "== any permalink/redirect/alias settings in chat-ui docs =="
rg -n --hidden --glob 'site/docs/chat-ui/**/*.md' 'permalink:|redirect|alias|aliases:' site/docs/chat-ui || true
echo

echo "== any docs config that might rewrite URLs =="
rg -n --hidden --glob 'site/**' 'permalink|redirect_from|aliases|just-the-docs|jekyll' site || true

Repository: cnjack/jcode

Length of output: 3553


Fix the Runtime state link
/chat-ui/docs/guides/runtime doesn’t match the docs layout; the Runtime page is at /chat-ui/docs/runtime. Update the link or add a redirect if that’s the intended target.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@site/docs/chat-ui/components/connection-banner.md` at line 48, Update the
Runtime state Markdown link in connection-banner.md to target
/chat-ui/docs/runtime, matching the actual documentation route; do not retain
the incorrect /chat-ui/docs/guides/runtime path unless an intentional redirect
is added.

Comment on lines +471 to +486
artifact: `import { Artifact } from 'jcode-ui'
import 'jcode-ui/styles.css'

export function Demo() {
return (
<Artifact
title="vite.config.ts"
subtitle="7 lines · typescript"
actions={<button type="button" onClick={() => copy(source)}>Copy</button>}
onClose={() => setOpen(false)}
>
<pre style={{ margin: 0, padding: '0.75rem' }}>{source}</pre>
</Artifact>
)
}`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Undefined identifiers in the artifact demo snippet.

copy, source, and setOpen are referenced but never declared/imported in this snippet, so pasting it verbatim (as the file header promises: "short and copy-pasteable") will fail to compile.

🐛 Proposed fix — self-contained snippet
-  artifact: `import { Artifact } from 'jcode-ui'
+  artifact: `import { useState } from 'react'
+import { Artifact } from 'jcode-ui'
 import 'jcode-ui/styles.css'
 
+const source = \`import { defineConfig } from 'vite'
+
+export default defineConfig({
+  plugins: [],
+})\`
+
 export function Demo() {
+  const [copied, setCopied] = useState(false)
+  const copy = () => {
+    void navigator.clipboard?.writeText(source).then(() => setCopied(true))
+  }
   return (
     <Artifact
       title="vite.config.ts"
       subtitle="7 lines · typescript"
-      actions={<button type="button" onClick={() => copy(source)}>Copy</button>}
-      onClose={() => setOpen(false)}
+      actions={<button type="button" onClick={copy}>{copied ? 'Copied ✓' : 'Copy'}</button>}
     >
       <pre style={{ margin: 0, padding: '0.75rem' }}>{source}</pre>
     </Artifact>
   )
 }`,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
artifact: `import { Artifact } from 'jcode-ui'
import 'jcode-ui/styles.css'
export function Demo() {
return (
<Artifact
title="vite.config.ts"
subtitle="7 lines · typescript"
actions={<button type="button" onClick={() => copy(source)}>Copy</button>}
onClose={() => setOpen(false)}
>
<pre style={{ margin: 0, padding: '0.75rem' }}>{source}</pre>
</Artifact>
)
}`,
artifact: `import { useState } from 'react'
import { Artifact } from 'jcode-ui'
import 'jcode-ui/styles.css'
const source = \`import { defineConfig } from 'vite'
export default defineConfig({
plugins: [],
})\`
export function Demo() {
const [copied, setCopied] = useState(false)
const copy = () => {
void navigator.clipboard?.writeText(source).then(() => setCopied(true))
}
return (
<Artifact
title="vite.config.ts"
subtitle="7 lines · typescript"
actions={<button type="button" onClick={copy}>{copied ? 'Copied ✓' : 'Copy'}</button>}
>
<pre style={{ margin: 0, padding: '0.75rem' }}>{source}</pre>
</Artifact>
)
}`,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@site/src/playground/demoSources.ts` around lines 471 - 486, Make the
`artifact` demo snippet self-contained by declaring or importing valid
implementations for `copy`, `source`, and `setOpen` within the generated `Demo`
example. Preserve the existing `Artifact` UI and ensure the pasted snippet
compiles without relying on identifiers from the surrounding playground.

Comment thread web/src/styles.css
Comment on lines 14 to +21
@import 'jcode-ui/styles.css';
@import './styles/tokens.base.css';

/* Generated themes (dracula, nord, midnight, solarized, etc.) — produced by
`go generate ./internal/theme/...` from palette.go. Defines
html[data-theme="<id>"] color overrides. Copied from web/src/styles/. */
@import './styles/tokens.generated.css';
@import 'jcode-ui/compat.css';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Move these imports before preceding CSS rules.

Stylelint reports all four as invalidly positioned. CSS imports after a non-import rule can be ignored by browsers, which would drop the UI styles/token bridges entirely.

🧰 Tools
🪛 Stylelint (17.14.0)

[error] 14-14: Invalid position for @import rule (no-invalid-position-at-import-rule)

(no-invalid-position-at-import-rule)


[error] 15-15: Invalid position for @import rule (no-invalid-position-at-import-rule)

(no-invalid-position-at-import-rule)


[error] 20-20: Invalid position for @import rule (no-invalid-position-at-import-rule)

(no-invalid-position-at-import-rule)


[error] 21-21: Invalid position for @import rule (no-invalid-position-at-import-rule)

(no-invalid-position-at-import-rule)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/styles.css` around lines 14 - 21, Move the four stylesheet imports in
styles.css above all preceding CSS rules so they remain valid and are processed
by browsers. Preserve their current order and keep the generated theme import
alongside the other imports.

Source: Linters/SAST tools

cnjack added a commit that referenced this pull request Jul 11, 2026
fix: address PR #133 review findings — AG-UI queue, composer, banner, docs generator
cnjack added a commit that referenced this pull request Jul 12, 2026
feat: jcode-ui 0.2.0 — scoped tokens, full conversation loop, AG-UI adapter, canvas/voice, docs overhaul
cnjack added a commit that referenced this pull request Jul 12, 2026
Functional fixes:
- agui runtime: implement the type-ahead queue — enqueueMessage buffers
  drafts into RuntimeState.queued and drains one per natural turn end
  (never after stop()); removeQueuedMessage works. Previously a no-op
  silently dropped drafts composed mid-run.
- Composer.send(): keep uploading/error attachment slots (only done ones
  are consumed) and reset dictation buffers so recognized text can't
  repopulate the textarea after sending.
- ConnectionBanner: drop flashRecovered from the effect deps — the
  self-triggered re-run cancelled its own timeout, leaving the
  'Reconnected' flash on screen forever.
- WorkflowCanvas: interactive-driven flags now come after {...rest} with
  per-flag override support, so the spread can't silently undo them.
- Transcription: assign-only active ref — backward seeks no longer wipe
  the ref via the stale segment's null-clear.
- FileTree renderer: implement the documented trailing-annotation cleanup
  ((dir) suffixes, double-space columns).
- API docs generator: forbid '*/' inside the JSDoc capture (backtracking
  welded earlier comments + code onto the next symbol), count only
  parentheses in function-signature scanning ('>' in arrows corrupted the
  depth), and emit real anchor ids matching the index links. Regenerated:
  275 symbols (previously-swallowed declarations now extracted).

Docs/examples:
- artifact.md: import the icon used; demoSources artifact snippet made
  self-contained; branch-picker.md content mirrors its active version;
  connection-banner.md runtime link fixed.

Hygiene:
- web/src/styles.css: all @imports moved before other at-rules (late
  imports are spec-invalid).
- Deprecated CSS replaced (word-break: break-word, clip); canvas.css
  stylelint spacing; smoke script self-comparison lint.
- Packaging: ./package.json export on both packages; selftest artifacts
  excluded from the core tarball.

Versions: jcode-ui-core 0.2.1, jcode-ui 0.2.2 (publish after merge, with
pnpm publish).

Generated with Jack AI bot
cnjack added a commit that referenced this pull request Jul 12, 2026
fix: address PR #133 review findings — AG-UI queue, composer, banner, docs generator
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