Move desktop UI components to shadcn/ui (Radix) and Pierre Diffs
Los mantenedores suelen responder en 1 día
@GoDiao ya está trabajando en esto.
Desde el 26/10/2026.
Evaluación
Este issue todavía no se ha evaluado.
Descripción
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".
- Lenguaje dominante
- TypeScript
- Estrellas
- 490
- Forks
- 47
- Merge medio
- 9 h 31 min
- PR fusionados (30 d)
- 76
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de GoDiao/dreamcoder
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
GoDiao/dreamcoder#198 ·
Los mantenedores suelen responder en 1 día
-
Agent teams: in-process teammates never claim tasks from the team's task listPosiblemente ocupada @Sagar07Kr la tomó hace 6 días. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
GoDiao/dreamcoder#163 · 2 comentarios · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
[good first issue] UX polish — friendlier copy for error toasts and empty statesPosiblemente ocupada @iamcipherdev la tomó hace 13 días. Abiertoarea:i18n area:ux difficulty:1 good first issue mentor-available
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
GoDiao/dreamcoder#9 · 3 comentarios ·
Los mantenedores suelen responder en 1 día
-
Add more provider presets (Groq / OpenRouter / SiliconFlow / etc.)Quizá libre de nuevo @01iamysf la tomó hace 20 días y no hay ningún pull request abierto. Abiertoarea:desktop area:provider difficulty:1 good first issue mentor-available
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
GoDiao/dreamcoder#6 · 6 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 25/100
GoDiao/dreamcoder#202 ·
Los mantenedores suelen responder en 1 día
Todos los issues de GoDiao/dreamcoder
Issues similares
-
fix(worker): keep normal prepare waiting states failure-freePosiblemente ocupada @LichKing-2234 la tomó hoy. Abiertoagent:codex priority:P1
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
AgoraIO-Extensions/agent-infra#3745 ·
Los mantenedores suelen responder en 1 día
-
The five-line work window can drop its oldest line: the per-line byte share ignores the JSON framingPosiblemente ocupada @Eapp1e la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
makecindy/cindy#7973 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Dificultad 1/5 Menos de una hora Aptitud para principiantes 78/100
lingdojo/kana-dojo#33337 · 1 comentario · 5 reacciones ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
coder/xum#7733 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
code-yeongyu/senpi#4917 · 1 comentario ·
Los mantenedores suelen responder en 1 día