Move desktop UI components to shadcn/ui (Radix) and Pierre Diffs
I maintainer di solito rispondono entro 1 giorno
@GoDiao ci sta già lavorando.
Dal 26/10/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
Summary
Replace the hand-written desktop components in desktop/src/components/shared (about 2,500 lines) and the hand-rolled chat pieces with shadcn/ui components on Radix, plus Pierre Diffs for code review. Everything is restyled through our own theme tokens, so all seven themes keep working.
This lands as one PR, so the whole change can be judged at once. Commits inside it follow the order below.
The per-component picks were made on a side-by-side comparison of our components against shadcn, AI Elements, prompt-kit, ReUI, Kibo UI and Pierre Diffs.
Why
-
Behavior, not just looks. A few examples from the current code:
Dropdownopens from a clickablediv, so the keyboard can't open it. Arrow keys don't move through items, and the menu isn't repositioned near the window edge.Modalhandles Escape, but it doesn't keep focus inside the dialog and doesn't return focus to the trigger when it closes.Toastusesanimate-in slide-in-from-rightclasses that nothing in the project defines, so toasts have no enter animation. Its close button is a bare×with no accessible name.- The workspace file tree has no arrow-key navigation.
Radix provides all of this.
-
One place for UI. Today the same patterns are written several times:
Button.tsxcarries its own copy ofSpinner.- The workspace panel tabs are a separate implementation from
SegmentedControl. - Popovers and menus outside
sharedare hand-built (9 files setrole="dialog"orrole="menu"themselves). - Icons mix lucide with Material Symbols (142 uses in 41 files).
-
The chat UI we want already exists. shadcn now ships
Trace,Code Block,MessageandBubblein its core registry. It also has an AI Elements section (tool, reasoning, file tree, …) built only from those parts, with no AI SDK dependency, so it fits our server-owned session state. -
It unblocks #189. The browser panel has to hide its native view while any overlay is open. That is simple when every dialog, menu and popover is a Radix primitive, and messy with the current mix. #189 also injects theme tokens into agent pages, so token names should be settled first.
Component mapping
| Ours | Becomes | Source |
|---|---|---|
Button |
Button | shadcn |
Input, Textarea, SearchField |
Input, Textarea, Field, InputGroup | shadcn |
Switch |
Switch | shadcn |
SegmentedControl, workspace panel tabs |
ToggleGroup, Tabs | shadcn |
Dropdown and hand-built menus |
DropdownMenu, Select, ContextMenu (file tree right-click), Popover (composer "+" menu) | shadcn |
Modal |
Dialog | shadcn |
ConfirmDialog |
AlertDialog | shadcn |
Toast |
Sonner | shadcn |
MobileBottomSheet |
Drawer | shadcn |
CommandPalette |
Command | shadcn |
UserMessage |
Message + Bubble | shadcn |
ThinkingBlock, ToolCallBlock |
Trace lines, with shimmer on the running line | shadcn (AI Elements reasoning / tool) |
CodeViewer, code in tool output |
Code Block | shadcn |
DiffViewer |
Pierre Diffs | @pierre/diffs |
| Workspace file tree | File Tree (Collapsible + Button), plus tree keyboard navigation (up/down, left/right to collapse/expand, Home/End, async expand) | shadcn (AI Elements file tree) |
AppShell panel resize |
Resizable (react-resizable-panels) |
shadcn |
title tooltips (~30 components) |
Tooltip | shadcn |
ShortcutsDialog keys |
Kbd | shadcn |
| Panel empty and loading states | Empty, Skeleton | shadcn |
ContextUsageIndicator |
Context | shadcn (AI Elements) |
SessionTaskBar, InlineTaskSummary |
Plan / Task | shadcn (AI Elements) |
Kept as ours, with their inner buttons and cards swapped for the new components:
PermissionDialog. Its content (tool icon, pending state, "allow for this session", raw input) is richer than Confirmation.- The composer (
ComposerShell/ChatInput). Its logic (slash commands, @-file search, pasted images) is untouched. AskUserQuestion. shadcn's Questionnaire needs@shadcn/react/questionnaire(not Radix), and its free-text answer is a single-line input where Enter confirms the question. Ours keeps multi-line answers, IME composition and Ctrl/Cmd+Enter to submit.CurrentTurnChangeCard. Checkpoint is a marker and a button that trims client-side messages. Our card shows the per-file changes and runs the server-side rewind (confirm, stop generation if needed, rewind, reload history, restore the input). Checkpoint's marker style can be borrowed.
Behavior that stays: consecutive thinking and tool calls are still grouped into one block (ToolCallGroup). Only each line inside the group changes to the Trace style. Bash calls keep showing $ command instead of JSON input.
Rules for the code that comes in
- One primitive layer: Radix only, through the
radix-uipackage. Generate components with the shadcn CLI (bunx shadcn add) using the Radix base, not by copying registry source (it contains template placeholders), then switch icons, tokens and paths to ours. - Sonner follows our theme store instead of
next-themes. - Our tokens only: map shadcn's variables (
--background,--primary,--border,--radius, …) onto the existing tokens indesktop/src/theme/globals.cssandaurora.css. Then replace any hard-coded color, radius or shadow in copied code. Check all seven themes. - One component per concept: one Button, one Dialog, one menu. Copied code uses our components, never a second copy.
- One icon set: lucide. Material Symbols goes away, and so does its font.
- One highlighter: shiki, for code blocks, diffs and the file preview. Code uses shiki's own light/dark theme rather than our code tokens.
prism-react-rendererandreact-diff-viewer-continuedare removed. While shiki loads, or if it fails, code shows as plain text. - Project rules still apply to copied code:
- no comments;
- i18n for every visible string;
- store selectors, not whole objects;
- tests select by role and label.
Dependencies
Added:
radix-ui;react-resizable-panels;sonner,vaul,cmdk;class-variance-authority,clsx,tailwind-merge;@pierre/diffs.
Removed:
prism-react-renderer;react-diff-viewer-continued;- the Material Symbols font.
Build target: safari15 → safari17.5 in desktop/vite.config.ts, because Pierre Diffs needs Safari 17.5+ (Chromium 123+). This only affects the H5 page on older iPhones; the old-Safari fallback in CodeViewer is removed, and there is no separate plain-text diff path.
Upgraded: React and React DOM 18 → 19, with their types. Current shadcn components are written for React 19 (refs as props, no forwardRef), so the copied code stays close to upstream.
Plan inside the PR
- React 19. Upgrade React and fix what breaks. All three checks must pass before any component work.
- Foundation. Add
components.json, thecnhelper and the token mapping. Nothing should change visually at this step. - Base components. Swap the internals of each
sharedcomponent, keeping its props where possible so call sites change little. Delete what is replaced. - Overlays. Move every hand-built dialog, menu and popover onto Dialog, DropdownMenu, ContextMenu and Popover. Add Tooltip.
- Chat. Port Message/Bubble, the Trace lines inside
ToolCallGroupand Code Block. Swap the inner parts ofPermissionDialog,AskUserQuestion,CurrentTurnChangeCardand the composer. - Developer views. Port Pierre Diffs, the file tree and Resizable. Views that move into Pierre's Shadow DOM get new selection handling for line comments and selection quotes. Remove prism and the old diff viewer.
- Cleanup. Remove Material Symbols, the dead classes and the unused shared files. Write a short
components/READMElisting each component and where it came from.
Out of scope
- A visual redesign beyond what the new components bring. That is a separate step on top of this one.
- #189 features (browser panel, inline UI). They build on this.
- New components without a current use, such as Terminal, StackTrace or Commit. Add them when a feature needs them.
How to check
bun test,bun run lintandbun run test -- --runpass.- Every overlay works by keyboard alone: open, move, select, Escape, focus returns.
- All seven themes, in both languages, in the desktop app and in the H5 page at phone width.
- A long session mounts and streams no slower than on
main, with the benchmark from #172 (0/200/1,000 history messages, a 60,000-character reply written 80 characters every 50 ms, three runs each; mount, React and forced-layout time), same Electron version, window width and build mode. - Installer size before and after (same architecture and packaging config), with the frontend asset size listed separately.
Resolved during review
- Pierre Diffs theming. Pierre inherits CSS variables from its host, so no per-color injection is needed. Syntax colors come from shiki's light/dark theme; diff backgrounds, line numbers, fonts and surrounding UI map to our tokens through Pierre's
--diffs-…-overridevariables. Light or dark follows the app theme, not the system: dark, midnight and aurora are dark, the other four are light. Checked in all seven themes before the old viewer is removed. - Checkpoint: not used; see "Kept as ours".
- Questionnaire: not used; see "Kept as ours".
- Lingua principale
- TypeScript
- Stelle
- 490
- Fork
- 47
- Merge medio
- 10h 40m
- PR unite (30g)
- 83
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di GoDiao/dreamcoder
-
[good first issue] UX polish — friendlier copy for error toasts and empty statesForse già presa @iamcipherdev l’ha presa 14 giorni fa. Apertaarea:i18n area:ux difficulty:1 good first issue mentor-available
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
GoDiao/dreamcoder#9 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Add more provider presets (Groq / OpenRouter / SiliconFlow / etc.)Forse di nuovo libera @01iamysf l’ha presa 22 giorni fa e non c’è nessuna pull request aperta. Apertaarea:desktop area:provider difficulty:1 good first issue mentor-available
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
GoDiao/dreamcoder#6 · 6 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
area:desktop area:ux difficulty:2 enhancement help wanted
Difficoltà 4/5 3-5 giorni Idoneità per principianti 65/100
GoDiao/dreamcoder#218 ·
I maintainer di solito rispondono entro 1 giorno
-
area:desktop bug difficulty:2 help wanted
Difficoltà 4/5 3-5 giorni Idoneità per principianti 65/100
GoDiao/dreamcoder#217 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 55/100
GoDiao/dreamcoder#216 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di GoDiao/dreamcoder
Issue simili
-
area:cli bug P4
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
bug from-studio
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
esengine/DeepSeek-Reasonix#14868 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
linxiv-dev/linXiv#384 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
factory-active task-identify-harness-labels-done task-identify-issue-type-done
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
vercel/ai#24302 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno