Skip to content

feat: desktop design system, stabilization and dependency migration - #2587

Open
devrajmahar wants to merge 10 commits into
CapSoftware:mainfrom
devrajmahar:feat/desktop-design-tokens
Open

devrajmahar wants to merge 10 commits into
CapSoftware:mainfrom
devrajmahar:feat/desktop-design-tokens

Conversation

@devrajmahar

@devrajmahar devrajmahar commented Oct 14, 2026 •

Copy link
Copy Markdown

Summary

This PR gives the GPUI desktop app a proper design system and moves its existing UI onto it. It also fixes a long list of Windows and editor bugs, improves interaction feedback, and migrates the whole monorepo to current dependencies. The app goes from an early, inconsistent state to a stable, tokenized base that is much easier to keep building on.

Design system

  • apps/desktop-gpui/platform.css is now the single source for colors, radii and shadows in light and dark. A build step compiles it into the GPUI theme.
  • apps/desktop-gpui/CSS.md documents the tokens and how to use them. AGENTS.md requires them for desktop UI work.
  • Shared controls (buttons, segmented tabs, inputs, menus, tooltips) are restyled on the tokens. Token usage in apps/desktop-gpui/src goes from 5 references in 1 file to 463 references across 51 files.
  • Removes the legacy Radix scales and the editor palette from theme.rs. Timeline segments, status chips, scrims, shadows, sliders, radio cards and the main window controls now use semantic tokens. Hardcoded colour literals drop from about 340 to about 164, and the rest are intentional: macOS materials, content overlays, platform window controls, illustrations, recording-state reds and user content.
  • Bundles the Inter and Faculty Glyphic fonts and adds loading skeletons. Icons are aligned on one set, including the chevron back icon and new reset and brush icons.

Stability and bug fixes

  • Windows: fixes the broken white window corners on the onboarding and main windows, and removes the blank ghost window that flashed on open. The teleprompter is now opaque, and the screenshot editor header is the correct 44px height.
  • Timeline: clip waveforms stay inside the rounded corners instead of cutting across them. A test covers this.
  • Editor: the background source tabs no longer overflow, and Save As removes characters that are invalid in file names.
  • Defaults: the background and camera shadows are off, and the cursor style defaults to macOS.

Feedback and UX

  • Press fills are removed app-wide, so hover is the only pointer feedback.
  • Tooltips sit offset from the cursor and have a pointer tail.
  • Export shows a success animation, with side-by-side Open File (primary) and Back to editor (outline) buttons.
  • The screenshot editor has an undoable Reset that restores the capture's default state.

Dependency migration

  • Moves to Effect 4, TypeScript 7, Vite 8, Vitest 5, Zod 4, Tailwind 4, React 19.3, Next 16.4, SolidStart 2, Expo 57 / React Native 0.86, Stripe 23, WorkOS 11 and Sentry 11. Radix, TanStack, OpenTelemetry, the AWS SDK, the AI SDK, Font Awesome, Biome and Turbo also get newer versions.
  • Ports the backend to Effect 4 (packages/web-backend, web-domain, web-api-contract-effect and the web API routes) and updates the affected tests.
  • Fixes the mobile app, Chrome extension, Discord bot, media server, render farm and status page for the new versions.
  • Bumps Bun to 1.4.3 and pins the GPUI Rust toolchain to 1.99. Updates the GPUI revision and its patches, and the vendored tao window library.

Commits

  • 0ca96ec0b chore: upgrade dependency stack across workspaces
  • 1bb0483aa docs: establish GPUI design token rules
  • 51f51f18c feat: apply desktop design tokens across the GPUI app
  • 567f46327 fix: turn the camera shadow off by default
  • 78ab8c345 fix: pin ort to 2.0.0-rc.12 so cap-rendering tests build
  • c396f9d34 refactor: migrate GPUI chrome colours to design tokens
  • 17e6aec85 Merge origin/main into feat/desktop-design-tokens
  • 931c681e0 fix: use semantic tokens instead of call-site alpha tints in GPUI controls
  • 961dcf555 fix: read Stripe billing periods from subscription items
  • 50200da82 fix: keep the requested window theme on Linux

Review follow-ups

  • Stripe billing (P1): Stripe 23 moves billing periods onto subscription items. getSubscriptionDetails and previewSeatChange now read item.current_period_end. The seat preview also moves from the removed invoices.retrieveUpcoming / listUpcomingLines to invoices.createPreview / listLineItems, and reads proration from line.parent.
  • Alpha tints (P2): The glass helpers in main_window.rs were already on tokens in c396f9d34. IconButton::glass, the text animation tiles, the text case chips, the teleprompter controls, the Studio quality radio, and the warning and danger notices now use hover_bg, active_bg, primary_subtle, border_strong, warning_subtle and negative_subtle instead of changing token alpha. Alpha remains only in content illustrations (the Fade animation preview), gradient fades and timeline track colours.
  • Linux window theme (P2): vendored tao now uses attributes.preferred_theme when one is set, and falls back to the portal colour scheme only when none was requested.

Merge with main

  • Conflicts in ci.yml, settings_pages.rs and ui/toggle.rs are resolved. ci.yml keeps main's removal of the rust-cache job and this branch's Rust 1.99. The GPUI test step called cargo +1.95.0 while the job installs 1.99, so it now uses +1.99.0.
  • The rebuilt Shortcuts page from main now uses tokens: text_negative and text_warning replace the removed Radix steps, and the selected outcome card uses surface_raised. The settings toggle stays on disabled_bg and primary.

Testing

  • cargo test in apps/desktop-gpui (after the merge): 1,104 passing.
  • cargo test -p cap-project (after the merge): 145 passing.
  • cargo test -p cap-rendering: 274 passing. cargo test -p cap-camera-effects: 24 passing.
  • cargo check --all-targets passes for apps/desktop-gpui. Biome passes on platform.css and the two Stripe actions, and they type-check cleanly.
  • Checked visually on Windows: window corners, the timeline clip corners, tooltips, the background tabs and the export success screen. After the colour migration, the editor, settings and screenshot editor were checked again.

Notes

  • crates/camera-effects now pins ort to =2.0.0-rc.12, matching main and the GPUI lockfile. The lockfile bump to rc.14 had broken the cap-rendering test build.
  • cargo clippy in the GPUI workspace reports lints that already exist on main (token_compiler.rs type complexity, chunks_exact in kernels). CI clippy covers the root workspace only.
  • The tao change is Linux-only and was not compiled locally (Windows machine).
  • tsc --noEmit in apps/web still reports about 1,100 errors from the Effect 4 / Vitest 5 migration, mostly in app/api and unit tests. None come from main's code. They need a follow-up pass before this can merge.

devrajmahar and others added 4 commits October 14, 2026 17:17
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
- Compile platform.css into GPUI colors, radii and shadows; bundle Inter and Faculty Glyphic
- Restyle shared controls on the tokens and drop press fills so hover is the only pointer feedback
- Fix Windows corners, ghost window, teleprompter border and screenshot header height
- Tooltips: offset from the cursor and add the pointer tail
- Timeline: keep clip waveforms inside the rounded corners
- Export: success animation, side-by-side primary and outline pill buttons
- Screenshot editor: undoable Reset back to the capture's default state
- Default cursor style to macOS, background shadow off, chevron back icon, Aeris reset and brush icons
- Background source tabs no longer overflow; Save As names drop invalid characters
@vercel

vercel Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

@devrajmahar is attempting to deploy a commit to the Cap Software Inc Team on Vercel.

A member of the Team first needs to authorize it.

@@ -5,7 +5,7 @@ const key = () => serverEnv().STRIPE_SECRET_KEY ?? "";
export const STRIPE_AVAILABLE = () => key() !== "";
export const stripe = () =>
new Stripe(key(), {
apiVersion: "2023-10-16",
apiVersion: "2026-09-30.endive",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Pro billing summary fails

The new Stripe API version moves billing periods onto subscription items, but getSubscriptionDetails still returns subscription.current_period_end. For an active Pro subscription, BillingSummaryCard passes that missing value to format, which throws instead of showing the billing card.

Migrate the remaining Pro billing reads alongside the SSO reads, including the period used by previewSeatChange.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/utils/src/lib/stripe/stripe.ts
Line: 8

Comment:
**Pro billing summary fails**

The new Stripe API version moves billing periods onto subscription items, but `getSubscriptionDetails` still returns `subscription.current_period_end`. For an active Pro subscription, `BillingSummaryCard` passes that missing value to `format`, which throws instead of showing the billing card.

Migrate the remaining Pro billing reads alongside the SSO reads, including the period used by `previewSeatChange`.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment thread apps/desktop-gpui/src/main_window.rs Outdated
Comment on lines +216 to +218
Theme::with_alpha(theme.primary_subtle, 0.9),
Theme::with_alpha(theme.primary_subtle, 0.75),
Theme::with_alpha(theme.primary_hover, 0.4),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Controls bypass theme colors

These migrated controls still change token alpha at the call site. AGENTS.md requires migrated desktop UI to use semantic tokens without ad-hoc tints, and CSS.md explicitly bans changing alpha to create UI states.

Use the supplied selected and hover fills directly. The same pattern appears in the migrated text animation tiles and IconButton::glass. This repository requirement must be satisfied before merging.

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop-gpui/src/main_window.rs
Line: 216-218

Comment:
**Controls bypass theme colors**

These migrated controls still change token alpha at the call site. `AGENTS.md` requires migrated desktop UI to use semantic tokens without ad-hoc tints, and `CSS.md` explicitly bans changing alpha to create UI states.

Use the supplied selected and hover fills directly. The same pattern appears in the migrated text animation tiles and `IconButton::glass`. This repository requirement must be satisfied before merging.

**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +211 to +215
// Set initial `preferred_theme` value to current portal color-scheme
#[cfg(feature = "dbus")]
let preferred_theme = super::portal::theme().ok();
#[cfg(not(feature = "dbus"))]
let preferred_theme = None;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Requested window theme ignored

Linux window creation now ignores attributes.preferred_theme and saves the portal’s current theme instead. A caller using WindowBuilder::with_theme(Some(Theme::Dark)) on a light desktop gets a light window, contrary to the builder’s documented contract.

Preserve the explicit choice and use the portal only when no theme was requested.

Prompt To Fix With AI
This is a comment left during a code review.
Path: vendor/tao/src/platform_impl/linux/window.rs
Line: 211-215

Comment:
**Requested window theme ignored**

Linux window creation now ignores `attributes.preferred_theme` and saves the portal’s current theme instead. A caller using `WindowBuilder::with_theme(Some(Theme::Dark))` on a light desktop gets a light window, contrary to the builder’s documented contract.

Preserve the explicit choice and use the portal only when no theme was requested.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatednpm/​@​types/​uuid@​9.0.8 ⏵ 11.0.010010039 -2950 -29100
Updatednpm/​@​types/​bun@​1.4.0 ⏵ 1.4.31001004890 -1100
Updatednpm/​@​react-email/​components@​0.1.1 ⏵ 1.0.1298 +110083 +150100
Updatednpm/​@​react-email/​tailwind@​1.0.5 ⏵ 2.0.797 -1100100 +150100
Updatedcargo/​tokio@​1.47.1 ⏵ 1.53.355 -710093100100
Updatednpm/​next@​16.3.0 ⏵ 16.4.061100 +7590 +19970
Updatednpm/​tauri-plugin-context-menu@​0.5.0 ⏵ 0.8.071 +110063 +277 +1100
Updatednpm/​react-router-dom@​6.30.4 ⏵ 7.18.4100 +3100 +265 -995100
Updatednpm/​supermemory@​4.24.12 ⏵ 5.0.266100100 +195 +3100
Updatednpm/​@​radix-ui/​react-label@​2.1.11 ⏵ 2.1.171001006699 +1100
Updatednpm/​eslint-config-next@​13.3.0 ⏵ 16.4.099 +110068 +498100
Updatednpm/​@​tanstack/​react-store@​0.7.7 ⏵ 0.11.2100 +110069 +491 -2100
Updatednpm/​@​radix-ui/​react-slot@​1.0.0 ⏵ 1.5.010010069 -199 +2100
Updatednpm/​@​types/​react-test-renderer@​19.1.0 ⏵ 19.3.01001007085100
Updatednpm/​@​tanstack/​react-query-devtools@​5.101.2 ⏵ 5.104.2100 +11007098 +2100
Updatednpm/​@​rive-app/​react-canvas@​4.29.3 ⏵ 4.36.0100 +110070 +199 +1100
Addednpm/​ua-parser-js@​2.0.101001001008470
Updatednpm/​@​mediabunny/​server@​1.49.0 ⏵ 1.61.499 +2010010096 +370
Updatednpm/​mediabunny@​1.49.0 ⏵ 1.61.410010010096 +270
Updatednpm/​dompurify@​3.4.11 ⏵ 3.4.17100 +1100 +410095 +370
Updatednpm/​jszip@​3.10.1 ⏵ 3.10.2100100848570
Updatednpm/​@​radix-ui/​react-dropdown-menu@​2.1.19 ⏵ 2.2.1991007199100
Updatednpm/​@​radix-ui/​react-switch@​1.3.2 ⏵ 1.4.1991007199 +1100
Updatednpm/​@​tanstack/​store@​0.7.7 ⏵ 0.11.2100 +110071 +291 +1100
Updatednpm/​@​radix-ui/​react-dialog@​1.0.0 ⏵ 1.3.19910071 -399100
Updatednpm/​@​radix-ui/​react-popover@​1.1.18 ⏵ 1.3.1991007199100
Updatednpm/​rehype-pretty-code@​0.14.3 ⏵ 0.14.5100 +110072 +186 +4100
Updatednpm/​@​typescript-eslint/​parser@​8.62.1 ⏵ 8.71.29910072 +198100
Updatednpm/​@​radix-ui/​react-tooltip@​1.2.11 ⏵ 1.4.1991007299100
Updatednpm/​@​radix-ui/​react-slider@​1.4.2 ⏵ 1.6.1991007299100
Updatednpm/​@​ai-sdk/​openai@​4.0.37 ⏵ 4.0.9373 +110094 +698100
Updatednpm/​@​radix-ui/​react-navigation-menu@​1.2.17 ⏵ 1.4.1991007399100
See 267 more rows in the dashboard

View full report

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant