From c080498cecefd374302ca3a8baa511ecc0f57882 Mon Sep 17 00:00:00 2001 From: Yilin Jing Date: Sun, 27 Sep 2026 04:55:20 -0400 Subject: [PATCH 1/4] A Vercel-style theme switch: system, light or dark Co-Authored-By: Claude Opus 5.5 --- canvas/canvas.html | 17 ++++++++++ canvas/home.html | 17 ++++++++++ canvas/index.html | 17 ++++++++++ canvas/src/App.tsx | 13 ++++++-- canvas/src/AppShell.tsx | 6 ++-- canvas/src/CanvasStrip.tsx | 13 ++++++-- canvas/src/CanvasTabBar.tsx | 2 ++ canvas/src/ThemeSwitcher.tsx | 38 ++++++++++++++++++++++ canvas/src/canvasChrome.tsx | 9 +++--- canvas/src/canvasGround.ts | 7 ++-- canvas/src/geistIcons.tsx | 4 +++ canvas/src/index.css | 62 +++++++++++++++++++++++++++++++++--- canvas/src/theme.ts | 28 ++++++++++++++++ canvas/src/tokens.css | 38 ++++++++++++++++------ desktop/main.ts | 13 +++++++- desktop/preload.ts | 2 ++ 16 files changed, 257 insertions(+), 29 deletions(-) create mode 100644 canvas/src/ThemeSwitcher.tsx create mode 100644 canvas/src/theme.ts diff --git a/canvas/canvas.html b/canvas/canvas.html index d9dd0218..99dec487 100644 --- a/canvas/canvas.html +++ b/canvas/canvas.html @@ -2,6 +2,23 @@ + + Super Prototyping diff --git a/canvas/home.html b/canvas/home.html index 244ac6a7..ead562d2 100644 --- a/canvas/home.html +++ b/canvas/home.html @@ -2,6 +2,23 @@ + + diff --git a/canvas/index.html b/canvas/index.html index 3700b0ee..9d647701 100644 --- a/canvas/index.html +++ b/canvas/index.html @@ -2,6 +2,23 @@ + + diff --git a/canvas/src/App.tsx b/canvas/src/App.tsx index fed6a7fe..0624c2b0 100644 --- a/canvas/src/App.tsx +++ b/canvas/src/App.tsx @@ -89,6 +89,7 @@ import { import { BOARDS_CHANGED, canvasIndex } from "./canvasIndex"; import { lockedOverlayUtils } from "./lockedIndicator"; import { installCanvasComments, readCommentUser } from "./canvasComments"; +import { storedTheme, THEME_KEY } from "./theme"; import { CanvasLinkPaste, agentBoardPaths, @@ -1256,9 +1257,14 @@ export default function App() { function handleMount(editor: Editor) { setEditor(editor); - // tldraw's own dark theme, to match the panel's. A dark rail against tldraw's near-white ground - // looks like two apps in one window, and the ground is most of the window. - editor.user.updateUserPreferences({ colorScheme: "dark" }); + // tldraw's scheme is the page's (tokens.css), or a dark rail sits against a near-white ground + // and the window looks like two apps. tldraw's "system" and the page's follow the same OS + // preference. Another page picking one reaches this one as a storage event (theme.ts). + editor.user.updateUserPreferences({ colorScheme: storedTheme() }); + const scheme = (e: StorageEvent) => + e.key === THEME_KEY && + editor.user.updateUserPreferences({ colorScheme: storedTheme() }); + window.addEventListener("storage", scheme); initializeCanvas(editor); // After the library, which is what creates the pages both are keyed to, and content first, // since a comment can be pinned to a shape the person put there. @@ -1281,6 +1287,7 @@ export default function App() { requestAnimationFrame(() => editor.zoomToFit()); const disposeZoom = installDoubleClickZoom(editor); return () => { + window.removeEventListener("storage", scheme); disposeContent(); disposeComments(); disposeZoom(); diff --git a/canvas/src/AppShell.tsx b/canvas/src/AppShell.tsx index 2c16aadd..92a9fe93 100644 --- a/canvas/src/AppShell.tsx +++ b/canvas/src/AppShell.tsx @@ -30,11 +30,13 @@ import { Onboarding } from "./Onboarding"; declare global { interface Window { - /** The desktop app's own (desktop/preload.ts), absent in a browser: the onboarding's answer - * and its update check, both about the app and not the project. */ + /** The desktop app's own (desktop/preload.ts), absent in a browser: the onboarding's answer, + * its update check and the theme, all about the app and not the project. */ startup?: { agent(id: string): Promise; check(): Promise; + /** The top bar's theme, for the window's own title bar (ThemeSwitcher.tsx). */ + theme(theme: string): void; }; /** The window's side of the frame (here): what the canvas has in front, at what address. */ spShell?: { diff --git a/canvas/src/CanvasStrip.tsx b/canvas/src/CanvasStrip.tsx index 4b662f26..8f6ae900 100644 --- a/canvas/src/CanvasStrip.tsx +++ b/canvas/src/CanvasStrip.tsx @@ -3,7 +3,12 @@ import { CanvasChromeContext } from "./canvasChrome"; import { shortName } from "./canvasLibrary"; import { docsOf, ownCanvases, pageOf, tabFor, tabUrl } from "./canvasTabs"; import { canvasIndex, LAYOUT_CHANGED } from "./canvasIndex"; -import { groundEditable, setGround, useGround } from "./canvasGround"; +import { + DEFAULT_GROUND, + groundEditable, + setGround, + useGround, +} from "./canvasGround"; import { ViewIcon } from "./CanvasTabBar"; import { shareUrl, sheetPageUrl, type CanvasTab } from "./canvasUrl"; import { @@ -203,7 +208,11 @@ export function CanvasStrip() {