feat: jcode-ui 0.2.0 — scoped tokens, full conversation loop, AG-UI adapter, canvas/voice, docs overhaul - #133
Conversation
…, 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
Generated with Jack AI bot
📝 WalkthroughWalkthroughThis release updates ChangesCore runtime and component APIs
Theming and integration
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
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
left a comment
There was a problem hiding this comment.
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:
enqueueMessageis a documented no-op inaguiRuntime.ts:450, butComposer.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 onisRunning(:547-551).send()also callssetPending([])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 apendingslot 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 (flashRecoveredis 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.shisn't wired into any build/CI step (confirmed via grep across package.json/Makefile/workflows), soweb/src/styles/tokens.base.csscan silently drift stale iftokens.csschanges 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
- CI build break:
jcode-ui/compat.csshas no producer in the unmodifiedci.ymlbuild step (Finding 1) - AG-UI runtime concurrency bug corrupts
isRunning+ Composer silently drops messages/attachments (Finding 2) - Streaming markdown permanently breaks loose-list rendering for all messages, not just while streaming (Finding 3)
- CSS
@layerchange loses to unlayered host CSS it used to beat, undermining the scoping migration's stated purpose (Finding 4) - Mermaid plugin: overridable security level + unsanitized
innerHTML(Finding 5, opt-in-gated)
Generated by Claude Code
There was a problem hiding this comment.
Actionable comments posted: 19
🧹 Nitpick comments (8)
packages/jcode-ui/src/voice/voice.css (1)
84-84: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winKeep color fallbacks token-driven.
The
#ffffallbacks 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 winAdd
aria-activedescendantand focus restoration for screen reader support.Two accessibility gaps in the popup menu:
No
aria-activedescendant: The search input is the focused element during keyboard navigation, but withoutaria-activedescendantpointing to the active option'sid, screen reader users can't perceive which option is highlighted as they arrow through the list.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
chooseand the Escape handler to calltriggerRef.current?.focus()aftersetOpen(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 stablerootId(e.g., viauseId()).🤖 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 winDuplicated light-palette variable block.
The 24-line
--color-*/--code-*/--hljs-*block is copy-pasted identically between.jcode-live-demo.lightand.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 winIgnore
value-keyword-casefor the font stacks here
BlinkMacSystemFont,SFMono-Regular, andMenloare font-family names, and this file is generated; relax the lint rule forfont-familyor 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 winAdd
onErrorhandler toPendingTileimage for consistency withAttachment.The
Attachmentcomponent handles broken images viaonError={() => setBroken(true)}with a fallback, butPendingTile'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 winScope non-portaled selectors under
[data-jcode-ui]for consistency withconversation.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.cssuses bare class selectors throughout, breaking that contract. The.jcode-quote-btnis a legitimate exception — it's portaled todocument.bodyand 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 valueFix 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 valueRender
DefaultPendingas an element for consistency.
renderResolvedfalls back to<DefaultResolved .../>(an element) but this branch invokesDefaultPending({...})as a plain function. It works today only becauseDefaultPendinguses 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
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsite/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (132)
examples/jcode-ui-minimal/package.jsonexamples/jcode-ui-zustand/package.jsoninternal-doc/chat-ui-competitive-analysis.mdinternal-doc/chat-ui-roadmap.mdpackages/jcode-ui-core/package.jsonpackages/jcode-ui-core/src/export/markdown.tspackages/jcode-ui-core/src/index.tspackages/jcode-ui-core/src/primitives/ApprovalBlock.tsxpackages/jcode-ui-core/src/primitives/AskUserBlock.tsxpackages/jcode-ui-core/src/primitives/Composer.tsxpackages/jcode-ui-core/src/primitives/MessageView.tsxpackages/jcode-ui-core/src/primitives/Thread.tsxpackages/jcode-ui-core/src/primitives/ToolCallView.tsxpackages/jcode-ui-core/src/primitives/attachmentAdapter.tspackages/jcode-ui-core/src/primitives/index.tspackages/jcode-ui-core/src/runtime/agui.selftest.tspackages/jcode-ui-core/src/runtime/agui.tspackages/jcode-ui-core/src/runtime/aguiEvents.tspackages/jcode-ui-core/src/runtime/aguiRuntime.tspackages/jcode-ui-core/src/runtime/index.tspackages/jcode-ui-core/src/runtime/mockRuntime.tspackages/jcode-ui-core/src/threads/context.tsxpackages/jcode-ui-core/src/threads/index.tspackages/jcode-ui-core/src/threads/store.tspackages/jcode-ui-core/src/types/index.tspackages/jcode-ui/CHANGELOG.mdpackages/jcode-ui/README.mdpackages/jcode-ui/fixture/main.tsxpackages/jcode-ui/package.jsonpackages/jcode-ui/scripts/generate-compat-css.mjspackages/jcode-ui/scripts/smoke-markdown.mjspackages/jcode-ui/src/canvas/CanvasControls.tsxpackages/jcode-ui/src/canvas/CanvasPanel.tsxpackages/jcode-ui/src/canvas/WorkflowCanvas.tsxpackages/jcode-ui/src/canvas/WorkflowEdge.tsxpackages/jcode-ui/src/canvas/WorkflowNode.tsxpackages/jcode-ui/src/canvas/canvas.csspackages/jcode-ui/src/canvas/index.tspackages/jcode-ui/src/canvas/toolTreeToGraph.tspackages/jcode-ui/src/components/ApprovalBanner.tsxpackages/jcode-ui/src/components/Artifact.tsxpackages/jcode-ui/src/components/AskUserCard.tsxpackages/jcode-ui/src/components/Attachment.tsxpackages/jcode-ui/src/components/BranchPicker.tsxpackages/jcode-ui/src/components/ChatInput.tsxpackages/jcode-ui/src/components/CompactToolRow.tsxpackages/jcode-ui/src/components/ConnectionBanner.tsxpackages/jcode-ui/src/components/ContextBar.tsxpackages/jcode-ui/src/components/ExploringGroupCard.tsxpackages/jcode-ui/src/components/ExportButton.tsxpackages/jcode-ui/src/components/Message.tsxpackages/jcode-ui/src/components/ModelSelector.tsxpackages/jcode-ui/src/components/QuoteSelection.tsxpackages/jcode-ui/src/components/Reasoning.tsxpackages/jcode-ui/src/components/Sources.tsxpackages/jcode-ui/src/components/Suggestions.tsxpackages/jcode-ui/src/components/TaskList.tsxpackages/jcode-ui/src/components/Thread.tsxpackages/jcode-ui/src/components/ThreadList.tsxpackages/jcode-ui/src/components/ThreadWelcome.tsxpackages/jcode-ui/src/components/ToolCallCard.tsxpackages/jcode-ui/src/components/ToolRegistryContext.tsxpackages/jcode-ui/src/index.tspackages/jcode-ui/src/lib/markdown.tspackages/jcode-ui/src/lib/streamingMarkdown.tspackages/jcode-ui/src/lib/useStreamingMarkdown.tspackages/jcode-ui/src/plugins/external-modules.d.tspackages/jcode-ui/src/plugins/katex.tspackages/jcode-ui/src/plugins/mermaid.tspackages/jcode-ui/src/styles/animations.csspackages/jcode-ui/src/styles/components.csspackages/jcode-ui/src/styles/composer2.csspackages/jcode-ui/src/styles/conversation.csspackages/jcode-ui/src/styles/entry.csspackages/jcode-ui/src/styles/markdown-chrome.csspackages/jcode-ui/src/styles/p5.csspackages/jcode-ui/src/styles/shadcn.csspackages/jcode-ui/src/styles/threadlist.csspackages/jcode-ui/src/styles/tokens.csspackages/jcode-ui/src/styles/welcome.csspackages/jcode-ui/src/toolRenderers/browserShot.tsxpackages/jcode-ui/src/toolRenderers/diff.tsxpackages/jcode-ui/src/toolRenderers/fileTree.tsxpackages/jcode-ui/src/toolRenderers/fileViewer.tsxpackages/jcode-ui/src/toolRenderers/generic.tsxpackages/jcode-ui/src/toolRenderers/index.tspackages/jcode-ui/src/toolRenderers/search.tsxpackages/jcode-ui/src/toolRenderers/skill.tsxpackages/jcode-ui/src/toolRenderers/stackTrace.tsxpackages/jcode-ui/src/toolRenderers/team.tsxpackages/jcode-ui/src/toolRenderers/terminal.tsxpackages/jcode-ui/src/toolRenderers/testResults.tsxpackages/jcode-ui/src/toolRenderers/todo.tsxpackages/jcode-ui/src/voice/AudioPlayer.tsxpackages/jcode-ui/src/voice/SpeechInput.tsxpackages/jcode-ui/src/voice/Transcription.tsxpackages/jcode-ui/src/voice/VoiceVisualizer.tsxpackages/jcode-ui/src/voice/index.tspackages/jcode-ui/src/voice/voice.cssscript/sync-web-base-tokens.shsite/docs/chat-ui/api/generated.mdsite/docs/chat-ui/comparison.mdsite/docs/chat-ui/components.mdsite/docs/chat-ui/components/artifact.mdsite/docs/chat-ui/components/branch-picker.mdsite/docs/chat-ui/components/canvas.mdsite/docs/chat-ui/components/connection-banner.mdsite/docs/chat-ui/components/export-quote.mdsite/docs/chat-ui/components/model-selector.mdsite/docs/chat-ui/components/task-list.mdsite/docs/chat-ui/components/thread-list.mdsite/docs/chat-ui/components/thread-welcome.mdsite/docs/chat-ui/components/tool-renderers-code.mdsite/docs/chat-ui/components/voice.mdsite/docs/chat-ui/guides/migration-0.2.mdsite/package.jsonsite/pnpm-workspace.yamlsite/src/pages/ChatUIPage.tsxsite/src/pages/chatui/ChatUiDocPage.tsxsite/src/playground/ChatDemo.tsxsite/src/playground/ComponentDemo.tsxsite/src/playground/component-demo.csssite/src/playground/demoSources.tssite/vite.config.tsweb/fixture-tool-ux/main.tsxweb/src/i18n/locales/en.tsweb/src/i18n/locales/ja.tsweb/src/i18n/locales/ko.tsweb/src/i18n/locales/zh-Hans.tsweb/src/i18n/locales/zh-Hant.tsweb/src/styles.cssweb/src/styles/tokens.base.css
| 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]) |
There was a problem hiding this comment.
🎯 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.tsxRepository: 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
PYRepository: 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
PYRepository: 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.
| setText('') | ||
| setImages([]) | ||
| setPending([]) |
There was a problem hiding this comment.
🎯 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 -SRepository: 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.tsRepository: 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 -SRepository: cnjack/jcode
Length of output: 1705
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,35p' packages/jcode-ui-core/src/primitives/Composer.tsxRepository: 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.
| // 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: () => {}, |
There was a problem hiding this comment.
🩺 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"
doneRepository: 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/srcRepository: 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.tsxRepository: 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.tsxRepository: 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.
| assert('hashString is deterministic', hashString('abc') === hashString('abc')) | ||
| assert('hashString distinguishes content', hashString('abc') !== hashString('abd')) |
There was a problem hiding this comment.
📐 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
| [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; | ||
| } |
There was a problem hiding this comment.
📐 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.
| [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
| ```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>} |
There was a problem hiding this comment.
🎯 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.
| ```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
| 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() }, |
There was a problem hiding this comment.
🎯 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.
| 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) |
There was a problem hiding this comment.
🎯 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 || trueRepository: 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.
| 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> | ||
| ) | ||
| }`, | ||
|
|
There was a problem hiding this comment.
🎯 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.
| 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.
| @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'; |
There was a problem hiding this comment.
🎯 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
fix: address PR #133 review findings — AG-UI queue, composer, banner, docs generator
feat: jcode-ui 0.2.0 — scoped tokens, full conversation loop, AG-UI adapter, canvas/voice, docs overhaul
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
fix: address PR #133 review findings — AG-UI queue, composer, banner, docs generator
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 inpackages/jcode-ui/CHANGELOG.md.Breaking (migration guide included)
:rootto the[data-jcode-ui]scope with a--jcode-prefix — zero leakage into host pages.jcode-ui/compat.cssbridges the legacy names (generated themes keep working unchanged);jcode-ui/shadcn.cssinherits a host shadcn theme automatically. Migration guide:/chat-ui/docs/guides/migration-0.2on the site.@layer basevia:where()— fixes unlayered resets outranking.jcode-btnand Tailwind utility classes (buttons rendered as bare text). Animation classes/keyframes gained thejcode-prefix; chat prose gained host-article isolation guards.Library
Approval.options[](arbitrary host-defined options, ACP-compatible) +resolveApprovalOption;allow_alwayskinds keep the two-step arming UXMessage.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)AttachmentAdapter(upload progress / retry), drag & drop + paste-screenshot,leadingControls/trailingControls/footerslots, ModelSelector, optional dictationplugins/mermaid+plugins/katexsubentries (dynamic-import peers)slotson Message/ToolCallCardcreateAGUIRuntime(AG-UI protocol: SSE transport, JSON Patch shared state, 6/6 selftest),ThreadStorecontract + ThreadList,jcode-ui/canvas(optional@xyflow/reactpeer),jcode-ui/voice(browser APIs only)jcode web
tokens.base.css(generated byscript/sync-web-base-tokens.sh) restores legacy names for the app chrome,compat.cssmaps them back into components — the Go theme generator and all generated themes are untouched. Light/dark/generated-theme chains verified in the browser.sidebar.noConversationsi18n key (5 locales); fixture extended with branching / feedback / retry / ConnectionBanner demos.Site (already deployed to origin)
/chat-uiinteractive demo: welcome + starter pills + live typing, scripted tour replay, light/dark + mobile viewport togglespublic/use import maps that broke the scanner)CI
compat.css, subentry styles) — both now delegate to each package's ownpnpm build.Verification
make build-webgreen with the CI fixAfter merge
pnpm publish(core first, then ui — pnpm rewritesworkspace:*).site/andexamples/pin^0.2.0, which resolves once published; for local development before that, switch them toworkspace:*with'../packages/*'inpnpm-workspace.yaml.https://www.j-code.net/(directory refresh) — edge caches HTML for 30 days.compat.css, then delete its self-maintained PermissionCard in favor of the library's options-mode ApprovalBanner.Generated with Jack AI bot