From 68ee4050642813b5db78266798dc12e00403a25a Mon Sep 17 00:00:00 2001 From: morgmart <98432065+morgmart@users.noreply.github.com> Date: Tue, 8 Sep 2026 10:52:05 -0700 Subject: [PATCH] feat(desktop-new): shape product UI and system docs Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com> --- desktop-new/AGENTS.md | 3 +- desktop-new/MAINTAINING_DESIGN_SYSTEM.md | 75 ++++++ .../src/app/routes/design/agents-guide.tsx | 6 + .../src/app/routes/design/composer.tsx | 7 - .../src/app/routes/design/design-guide.tsx | 6 + desktop-new/src/app/routes/design/growth.tsx | 7 - .../src/app/routes/design/maintaining.tsx | 6 + .../src/app/routes/design/vocabulary.tsx | 7 - .../agent-activity/ui/AgentActivityRail.tsx | 5 +- .../src/features/composer/ui/EmojiPicker.tsx | 3 +- .../features/composer/ui/MessageComposer.tsx | 54 +++- .../ui/ConversationComposerDock.tsx | 37 +-- .../design-system/ui/ComponentsPage.tsx | 3 +- .../design-system/ui/ComposerPage.tsx | 79 ------ .../design-system/ui/DesignSystemLayout.tsx | 138 +++++----- .../features/design-system/ui/GrowthPage.tsx | 89 ------- .../design-system/ui/MarkdownPage.tsx | 90 +++++++ .../design-system/ui/OverviewPage.tsx | 15 +- .../design-system/ui/SystemDocumentPage.tsx | 18 ++ .../design-system/ui/VocabularyPage.tsx | 93 ------- .../design-system/ui/componentSpecimens.tsx | 72 ++++++ .../design-system/ui/composerSpecimens.tsx | 63 +++++ desktop-new/src/features/navigation/types.ts | 2 +- .../navigation/ui/MessagesNavigator.tsx | 86 +----- .../src/features/navigation/useNavigation.ts | 2 +- desktop-new/src/features/sessions/types.ts | 5 +- .../src/features/sessions/ui/AppShell.tsx | 42 ++- .../features/sessions/ui/NewSessionView.tsx | 2 +- .../sessions/ui/ParticipantDialog.tsx | 6 +- .../src/features/sessions/ui/SessionView.tsx | 66 ++--- desktop-new/src/shared/runtime/client.ts | 36 +++ desktop-new/src/shared/styles/components.css | 114 +++++++- .../src/shared/styles/design-components.css | 244 ++++++++++++------ desktop-new/src/shared/styles/product.css | 121 +++++++-- .../src/shared/ui/ConversationHeader.tsx | 54 ++++ desktop-new/src/shared/ui/IconButton.tsx | 8 +- desktop-new/src/shared/ui/SearchField.tsx | 5 +- desktop-new/src/shared/ui/ShimmerText.tsx | 21 ++ desktop-new/src/shared/ui/registry.test.ts | 16 +- desktop-new/src/shared/ui/registry.ts | 70 ++++- desktop-new/tests/sessions.spec.ts | 4 +- 41 files changed, 1129 insertions(+), 651 deletions(-) create mode 100644 desktop-new/MAINTAINING_DESIGN_SYSTEM.md create mode 100644 desktop-new/src/app/routes/design/agents-guide.tsx delete mode 100644 desktop-new/src/app/routes/design/composer.tsx create mode 100644 desktop-new/src/app/routes/design/design-guide.tsx delete mode 100644 desktop-new/src/app/routes/design/growth.tsx create mode 100644 desktop-new/src/app/routes/design/maintaining.tsx delete mode 100644 desktop-new/src/app/routes/design/vocabulary.tsx delete mode 100644 desktop-new/src/features/design-system/ui/ComposerPage.tsx delete mode 100644 desktop-new/src/features/design-system/ui/GrowthPage.tsx create mode 100644 desktop-new/src/features/design-system/ui/MarkdownPage.tsx create mode 100644 desktop-new/src/features/design-system/ui/SystemDocumentPage.tsx delete mode 100644 desktop-new/src/features/design-system/ui/VocabularyPage.tsx create mode 100644 desktop-new/src/features/design-system/ui/composerSpecimens.tsx create mode 100644 desktop-new/src/shared/ui/ConversationHeader.tsx create mode 100644 desktop-new/src/shared/ui/ShimmerText.tsx diff --git a/desktop-new/AGENTS.md b/desktop-new/AGENTS.md index e041514d20..24f9c5af20 100644 --- a/desktop-new/AGENTS.md +++ b/desktop-new/AGENTS.md @@ -201,8 +201,7 @@ Deliberately, so nobody assumes it was forgotten: ## Architecture -This client is built on **composable capabilities** — see the plan in Morgan's -vault. A capability owns a durable product truth: it may be behavior-only, +This client is built on **composable capabilities**. A capability owns a durable product truth: it may be behavior-only, UI-bearing, or both. A feature is an organizational boundary, a surface/view owns spatial arrangement, and shared UI owns generic visual and interaction primitives. diff --git a/desktop-new/MAINTAINING_DESIGN_SYSTEM.md b/desktop-new/MAINTAINING_DESIGN_SYSTEM.md new file mode 100644 index 0000000000..22e4e70481 --- /dev/null +++ b/desktop-new/MAINTAINING_DESIGN_SYSTEM.md @@ -0,0 +1,75 @@ +# Maintaining the design system + +## The point + +The system helps Buzz stay clear and coherent as it grows. It should make the common choice easy, while leaving room for a design to be specific, surprising, or new. It is a shared memory of decisions that have been made—not a gate a designer needs to pass through. + +When the system cannot express the right design, change the system. Do not distort the design to satisfy an old rule. + +## Start with the work in front of you + +Build the screen or interaction you are trying to make. Use the existing colors, type, surfaces, and components when they fit. Look at the result in context, not just in a token table. + +A repeated need is evidence. One-off work is evidence too: it may reveal that a ramp step is wrong, a component needs another supported state, or a rule no longer reflects the product. + +## Color: use the ramps first + +Each color ramp has a version for light mode and a version for dark mode. A step has the same job in both: a quiet neutral surface, a selected row, a strong accent, or readable colored text. That lets a screen use a normal ramp step such as `neutral-2` and still work in both modes. + +A semantic color name is earned when a plain ramp step cannot tell the truth: + +- Light and dark need different steps for the same job. A panel, for example, needs a different relationship to its surroundings in each mode. +- A name protects a real rule. The three text levels and the single shared border weight are examples. +- A pattern has appeared enough times that giving it one name makes the product easier to understand and keep consistent. + +Do not create a semantic name just to avoid choosing a ramp step. If a name points to the same step in both modes, it is usually hiding a decision rather than recording one. + +## Refine the ramp when the product teaches us something + +The ramps are not sacred. They began as a useful starting point and should change when real product work gives us better evidence. + +The lighter search-field neutral is a good example. A real prototype showed that the old value was too heavy, so we corrected the neutral ramp instead of adding a search-only color or a fractional stop. That improvement now benefits every quiet compact surface that uses the same step. + +Before adding a new step or a new role, ask: + +1. Is an existing step already the right answer? +2. If not, is the ramp itself missing the right relationship? +3. Does light mode need one step and dark mode another? If so, give that relationship a semantic name. +4. Will people see this pattern again, or is it one moment in one screen? + +## Components grow from real repetition + +A shared component is a promise: the same interaction and visual language will work the same way wherever it appears. + +Build the first version where it is needed. Once another real use appears, decide what is truly shared: + +- a generic building block belongs in the design system; +- Buzz-specific behavior belongs with the product capability that owns it; +- a complete arrangement should only become reusable when another surface needs that exact arrangement. + +Do not build a catalogue in advance. Small, proven components are more flexible than a large component with a long list of switches. + +## Keep the important states visible + +Every interactive piece should have a clear default, hover, selected state where it applies, and disabled state where it matters. Selection is a lasting statement, not just a stronger hover. Keyboard focus should be visible, and the same action should work with a pointer or keyboard. + +Check narrow, medium, and wide layouts. Check light and dark mode together. A decision that works only in a component specimen is not finished. + +## What the guards are for + +The automated checks protect decisions that are easy to accidentally undo: readable contrast, the shared type scale, and the color system’s light/dark structure. + +They are guardrails, not judges. Each one has a named exception list with room to explain why a specific design needs to differ. Adding an exception is normal when it reflects a deliberate design choice. If the exceptions start pointing in one direction, improve or remove the rule instead of accumulating workarounds. + +A passing check means the system is internally consistent. Looking at the actual interface tells us whether the system is good. We need both. + +## A good change leaves a trace + +When you change the system, make the decision easy to find: + +- update the relevant token, component, or guidance; +- show the result on the design-system site where useful; +- use the new thing in real product work, not only in documentation; +- note the reason in plain language close to the decision. + +The goal is not to freeze Buzz into a style. It is to give every future designer and builder a clear starting point—and the confidence to improve it when the work asks for more. diff --git a/desktop-new/src/app/routes/design/agents-guide.tsx b/desktop-new/src/app/routes/design/agents-guide.tsx new file mode 100644 index 0000000000..490a3ed173 --- /dev/null +++ b/desktop-new/src/app/routes/design/agents-guide.tsx @@ -0,0 +1,6 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { SystemDocumentPage } from "@/features/design-system/ui/SystemDocumentPage"; + +export const Route = createFileRoute("/design/agents-guide")({ + component: () => , +}); diff --git a/desktop-new/src/app/routes/design/composer.tsx b/desktop-new/src/app/routes/design/composer.tsx deleted file mode 100644 index 8200f42dfa..0000000000 --- a/desktop-new/src/app/routes/design/composer.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; - -import { ComposerPage } from "@/features/design-system/ui/ComposerPage"; - -export const Route = createFileRoute("/design/composer")({ - component: ComposerPage, -}); diff --git a/desktop-new/src/app/routes/design/design-guide.tsx b/desktop-new/src/app/routes/design/design-guide.tsx new file mode 100644 index 0000000000..f2e62fb58e --- /dev/null +++ b/desktop-new/src/app/routes/design/design-guide.tsx @@ -0,0 +1,6 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { SystemDocumentPage } from "@/features/design-system/ui/SystemDocumentPage"; + +export const Route = createFileRoute("/design/design-guide")({ + component: () => , +}); diff --git a/desktop-new/src/app/routes/design/growth.tsx b/desktop-new/src/app/routes/design/growth.tsx deleted file mode 100644 index 732be720fc..0000000000 --- a/desktop-new/src/app/routes/design/growth.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; - -import { GrowthPage } from "@/features/design-system/ui/GrowthPage"; - -export const Route = createFileRoute("/design/growth")({ - component: GrowthPage, -}); diff --git a/desktop-new/src/app/routes/design/maintaining.tsx b/desktop-new/src/app/routes/design/maintaining.tsx new file mode 100644 index 0000000000..1c2c4c9b5a --- /dev/null +++ b/desktop-new/src/app/routes/design/maintaining.tsx @@ -0,0 +1,6 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { SystemDocumentPage } from "@/features/design-system/ui/SystemDocumentPage"; + +export const Route = createFileRoute("/design/maintaining")({ + component: () => , +}); diff --git a/desktop-new/src/app/routes/design/vocabulary.tsx b/desktop-new/src/app/routes/design/vocabulary.tsx deleted file mode 100644 index 102554e6fe..0000000000 --- a/desktop-new/src/app/routes/design/vocabulary.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; - -import { VocabularyPage } from "@/features/design-system/ui/VocabularyPage"; - -export const Route = createFileRoute("/design/vocabulary")({ - component: VocabularyPage, -}); diff --git a/desktop-new/src/features/agent-activity/ui/AgentActivityRail.tsx b/desktop-new/src/features/agent-activity/ui/AgentActivityRail.tsx index cc53963661..334685ec0f 100644 --- a/desktop-new/src/features/agent-activity/ui/AgentActivityRail.tsx +++ b/desktop-new/src/features/agent-activity/ui/AgentActivityRail.tsx @@ -1,3 +1,5 @@ +import { ShimmerText } from "@/shared/ui/ShimmerText"; + import { latestActivityItem } from "../activityProjection"; import type { AgentTurn } from "../types"; @@ -21,9 +23,8 @@ export function AgentActivityRail({ turns }: { turns: AgentTurn[] }) { - {label} + {label} ); })} diff --git a/desktop-new/src/features/composer/ui/EmojiPicker.tsx b/desktop-new/src/features/composer/ui/EmojiPicker.tsx index 71bf573fd2..68d2766460 100644 --- a/desktop-new/src/features/composer/ui/EmojiPicker.tsx +++ b/desktop-new/src/features/composer/ui/EmojiPicker.tsx @@ -53,7 +53,8 @@ export function EmojiPicker({