@dfosco/storyboard 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/favicon.svg +12 -0
- package/dist/storyboard-ui.css +1 -1
- package/dist/storyboard-ui.js +13907 -10816
- package/dist/storyboard-ui.js.map +1 -1
- package/dist/tailwind.css +1 -1
- package/package.json +138 -127
- package/scaffold/AGENTS.md +9 -7
- package/scaffold/README.md +18 -0
- package/scaffold/deploy.yml +21 -0
- package/scaffold/gitignore +8 -14
- package/scaffold/preview.yml +40 -0
- package/scaffold/{manifest.json → scaffold.config.json} +73 -3
- package/scaffold/scripts/aggregate-artifacts.mjs +257 -0
- package/scaffold/skills/canvas/SKILL.md +1 -1
- package/scaffold/skills/migrate-0.5.0/SKILL.md +1 -1
- package/scaffold/skills/ship/SKILL.md +2 -2
- package/scaffold/skills/worktree/SKILL.md +1 -1
- package/scaffold/src/_prototype.jsx +17 -0
- package/scaffold/src/_story.jsx +13 -0
- package/scaffold/src/library/README.md +34 -0
- package/scaffold/src/library/_app.jsx +53 -0
- package/scaffold/src/library/_app.module.css +8 -0
- package/scaffold/src/library/create.jsx +10 -0
- package/scaffold/src/library/index.jsx +14 -0
- package/scaffold/src/library/mount.jsx +52 -0
- package/scaffold/src/library/prototypes-entry.jsx +57 -0
- package/scaffold/src/library/routes.jsx +205 -0
- package/scaffold/src/library/viewfinder.jsx +13 -0
- package/scaffold/src/library/workspace.jsx +17 -0
- package/scripts/aggregate-artifacts.mjs +257 -0
- package/src/core/artifact/operations.js +19 -13
- package/src/core/autosync/server.test.js +1 -1
- package/src/core/canvas/githubEmbeds.js +12 -0
- package/src/core/canvas/githubEmbeds.test.js +71 -2
- package/src/core/canvas/hot-pool.js +10 -11
- package/src/core/canvas/materializer.js +39 -13
- package/src/core/canvas/server.js +324 -69
- package/src/core/canvas/server.test.js +365 -2
- package/src/core/canvas/terminal-server.js +144 -57
- package/src/core/canvas/undoRedo.js +174 -0
- package/src/core/canvas/undoRedo.test.js +250 -0
- package/src/core/cli/buildManifest.js +34 -0
- package/src/core/cli/canvasRedo.js +44 -0
- package/src/core/cli/canvasUndo.js +40 -0
- package/src/core/cli/create.js +116 -18
- package/src/core/cli/dev.js +23 -35
- package/src/core/cli/index.js +21 -0
- package/src/core/cli/schemas.js +2 -3
- package/src/core/cli/serverUrl.js +24 -0
- package/src/core/cli/setup.js +14 -25
- package/src/core/cli/terminal-welcome.js +12 -6
- package/src/core/cli/updateVersion.js +42 -2
- package/src/core/cli/workleaf.js +140 -0
- package/src/core/comments/api.js +1 -1
- package/src/core/comments/queries.js +3 -0
- package/src/core/comments/ui/CommentWindow.jsx +3 -2
- package/src/core/data/artifactIndex.js +235 -0
- package/src/core/data/artifactIndex.test.js +203 -0
- package/src/core/data/artifactManifest.js +290 -0
- package/src/core/data/artifactManifest.test.js +207 -0
- package/src/core/data/viewfinder.js +47 -7
- package/src/core/index.js +6 -1
- package/src/core/lib/components/ui/avatar/avatar-fallback.jsx +2 -2
- package/src/core/lib/components/ui/avatar/avatar-image.jsx +2 -2
- package/src/core/lib/components/ui/avatar/avatar.jsx +2 -2
- package/src/core/lib/components/ui/checkbox/checkbox.jsx +12 -9
- package/src/core/lib/components/ui/dialog/dialog-content.jsx +45 -31
- package/src/core/lib/components/ui/dialog/dialog-trigger.jsx +9 -5
- package/src/core/lib/components/ui/dialog/dialog.jsx +12 -3
- package/src/core/lib/components/ui/dialog/dialog.module.css +118 -0
- package/src/core/lib/components/ui/dialog/index.js +4 -34
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +3 -3
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +19 -9
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +8 -8
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +15 -4
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +14 -7
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +8 -4
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +4 -4
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +8 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu.jsx +2 -2
- package/src/core/lib/components/ui/label/label.jsx +1 -2
- package/src/core/lib/components/ui/panel/panel-close.jsx +7 -4
- package/src/core/lib/components/ui/panel/panel-content.jsx +7 -9
- package/src/core/lib/components/ui/panel/panel-title.jsx +3 -3
- package/src/core/lib/components/ui/panel/panel.jsx +11 -2
- package/src/core/lib/components/ui/separator/separator.jsx +6 -4
- package/src/core/lib/components/ui/tooltip/tooltip-content.jsx +7 -6
- package/src/core/lib/components/ui/tooltip/tooltip-trigger.jsx +13 -6
- package/src/core/lib/components/ui/tooltip/tooltip.jsx +5 -5
- package/src/core/mountStoryboardCore.js +234 -74
- package/src/core/mountStoryboardCore.test.js +174 -0
- package/src/core/rename-watcher/watcher.js +329 -36
- package/src/core/rename-watcher/watcher.test.js +253 -0
- package/src/core/scaffold/fragments.js +287 -0
- package/src/core/scaffold/fragments.test.js +401 -0
- package/src/core/scaffold/index.js +291 -0
- package/src/core/scaffold/migrateLegacyGitignore.js +157 -0
- package/src/core/scaffold/scaffold-integration.test.js +247 -0
- package/src/core/scaffold.js +4 -97
- package/src/core/stores/configSchema.js +41 -2
- package/src/core/stores/recentArtifacts.js +49 -2
- package/src/core/stores/recentArtifacts.test.js +59 -6
- package/src/core/stores/themeBootstrap.js +264 -0
- package/src/core/stores/themeBootstrap.test.js +211 -0
- package/src/core/stores/themeStore.test.js +154 -0
- package/src/core/stores/themeStore.ts +235 -220
- package/src/core/styles/generateThemesCss.js +63 -0
- package/src/core/styles/tailwind.css +12 -4
- package/src/core/styles/workspace-chrome.css +30 -0
- package/src/core/tools/handlers/paletteTheme.js +11 -8
- package/src/core/ui/ActionMenuButton.jsx +2 -1
- package/src/core/ui/CanvasCreateMenu.jsx +1 -19
- package/src/core/ui/CoreUIBar.jsx +10 -7
- package/src/core/ui/ThemeMenuButton.jsx +45 -30
- package/src/core/vite/server-plugin.js +36 -0
- package/src/core/workleaf/cleanup.js +102 -0
- package/src/core/workleaf/create.js +199 -0
- package/src/core/workleaf/delete.js +101 -0
- package/src/core/workleaf/list.js +57 -0
- package/src/core/workleaf/merge.js +238 -0
- package/src/core/workleaf/paths.js +146 -0
- package/src/core/workleaf/paths.test.js +230 -0
- package/src/core/workshop/features/createCanvas/CreateCanvasForm.jsx +28 -21
- package/src/core/workshop/features/createFlow/server.js +5 -1
- package/src/core/workshop/features/createPrototype/server.js +5 -1
- package/src/core/workshop/features/templateIndex.js +28 -1
- package/src/core/worktree/port.js +48 -5
- package/src/core/worktree/port.test.js +42 -0
- package/src/internals/ArtifactForm/ArtifactForm.jsx +173 -112
- package/src/internals/ArtifactForm/ArtifactForm.module.css +48 -66
- package/src/internals/ArtifactForm/artifactSchemas.js +40 -5
- package/src/internals/BaseUiForm/BaseUiForm.jsx +293 -0
- package/src/internals/BaseUiForm/BaseUiForm.module.css +542 -0
- package/src/internals/BranchesDropdown/BranchesDropdown.jsx +69 -0
- package/src/internals/BranchesDropdown/BranchesDropdown.module.css +84 -0
- package/src/internals/CommandPalette/CommandPalette.jsx +25 -19
- package/src/internals/CommandPalette/CreateArtifactForm.jsx +150 -0
- package/src/internals/CommandPalette/CreateDialog.jsx +20 -110
- package/src/internals/CommandPalette/WidgetArtifactDialog.jsx +1 -1
- package/src/internals/CommandPalette/createHelpers.js +30 -0
- package/src/internals/CreatePage/CreatePage.jsx +57 -0
- package/src/internals/CreatePage/CreatePage.module.css +34 -0
- package/src/internals/Dialog.jsx +51 -0
- package/src/internals/SimpleWorkspace/SimpleWorkspace.jsx +533 -0
- package/src/internals/SimpleWorkspace/SimpleWorkspace.module.css +323 -0
- package/src/internals/Tooltip.jsx +39 -0
- package/src/internals/Viewfinder.jsx +196 -48
- package/src/internals/Viewfinder.module.css +72 -1
- package/src/internals/canvas/CanvasPage.jsx +267 -124
- package/src/internals/canvas/CanvasPage.multiselect.test.jsx +39 -26
- package/src/internals/canvas/PageSelector.jsx +15 -1
- package/src/internals/canvas/PageSelector.module.css +14 -0
- package/src/internals/canvas/canvasApi.js +40 -5
- package/src/internals/canvas/isolation/createIsolationMiddleware.js +68 -0
- package/src/internals/canvas/isolation/mountIsolationApp.jsx +88 -0
- package/src/internals/canvas/isolation/prototypeRoutes.jsx +131 -0
- package/src/internals/canvas/isolation/prototypesEntry.jsx +28 -0
- package/src/internals/canvas/isolation/storiesEntry.jsx +20 -0
- package/src/internals/canvas/isolation/storyRoutes.jsx +55 -0
- package/src/internals/canvas/useUndoRedo.js +75 -60
- package/src/internals/canvas/useUndoRedo.test.js +46 -178
- package/src/internals/canvas/widgets/ExpandedPaneTopBar.jsx +1 -1
- package/src/internals/canvas/widgets/FrozenTerminalOverlay.jsx +30 -21
- package/src/internals/canvas/widgets/ImageWidget.jsx +7 -2
- package/src/internals/canvas/widgets/LinkPreview.jsx +45 -1
- package/src/internals/canvas/widgets/LinkPreview.module.css +71 -0
- package/src/internals/canvas/widgets/PrototypeEmbed.jsx +69 -10
- package/src/internals/canvas/widgets/PrototypeEmbed.test.jsx +114 -0
- package/src/internals/canvas/widgets/StorySetWidget.jsx +8 -4
- package/src/internals/canvas/widgets/StoryWidget.jsx +21 -38
- package/src/internals/canvas/widgets/TerminalReadWidget.jsx +13 -15
- package/src/internals/canvas/widgets/WidgetChrome.jsx +1 -1
- package/src/internals/canvas/widgets/WidgetChrome.module.css +15 -0
- package/src/internals/canvas/widgets/embedTheme.js +2 -2
- package/src/internals/canvas/widgets/expandUtils.js +19 -19
- package/src/internals/canvas/widgets/normalizeLegacyEmbedSrc.js +74 -0
- package/src/internals/context.jsx +22 -1
- package/src/internals/context.test.jsx +47 -0
- package/src/internals/hooks/useArtifactIndex.js +31 -0
- package/src/internals/hooks/useArtifactIndex.test.js +45 -0
- package/src/internals/hooks/usePrototypeReloadGuard.js +14 -8
- package/src/internals/index.js +6 -0
- package/src/internals/loadConsumerWrapper.js +60 -0
- package/src/internals/story/ComponentSetPage.jsx +75 -34
- package/src/internals/story/StoryPage.jsx +16 -25
- package/src/internals/vite/data-plugin.js +313 -148
- package/src/internals/vite/data-plugin.test.js +11 -11
- package/{scaffold/storyboard.config.json → storyboard.config.json} +25 -0
- package/terminal.config.json +1 -1
- package/src/core/lib/components/ui/card/card-action.jsx +0 -11
- package/src/core/lib/components/ui/card/card-content.jsx +0 -11
- package/src/core/lib/components/ui/card/card-description.jsx +0 -11
- package/src/core/lib/components/ui/card/card-footer.jsx +0 -11
- package/src/core/lib/components/ui/card/card-header.jsx +0 -19
- package/src/core/lib/components/ui/card/card-title.jsx +0 -11
- package/src/core/lib/components/ui/card/card.jsx +0 -17
- package/src/core/lib/components/ui/card/index.js +0 -25
- package/src/core/lib/components/ui/collapsible/collapsible-content.jsx +0 -7
- package/src/core/lib/components/ui/collapsible/collapsible-trigger.jsx +0 -7
- package/src/core/lib/components/ui/collapsible/collapsible.jsx +0 -7
- package/src/core/lib/components/ui/collapsible/index.js +0 -13
- package/src/core/lib/components/ui/dialog/dialog-close.jsx +0 -7
- package/src/core/lib/components/ui/dialog/dialog-description.jsx +0 -15
- package/src/core/lib/components/ui/dialog/dialog-footer.jsx +0 -23
- package/src/core/lib/components/ui/dialog/dialog-header.jsx +0 -11
- package/src/core/lib/components/ui/dialog/dialog-overlay.jsx +0 -15
- package/src/core/lib/components/ui/dialog/dialog-portal.jsx +0 -4
- package/src/core/lib/components/ui/dialog/dialog-title.jsx +0 -15
- package/src/core/lib/components/ui/popover/index.js +0 -28
- package/src/core/lib/components/ui/popover/popover-close.jsx +0 -7
- package/src/core/lib/components/ui/popover/popover-content.jsx +0 -22
- package/src/core/lib/components/ui/popover/popover-description.jsx +0 -11
- package/src/core/lib/components/ui/popover/popover-header.jsx +0 -11
- package/src/core/lib/components/ui/popover/popover-portal.jsx +0 -4
- package/src/core/lib/components/ui/popover/popover-title.jsx +0 -11
- package/src/core/lib/components/ui/popover/popover-trigger.jsx +0 -8
- package/src/core/lib/components/ui/popover/popover.jsx +0 -4
- package/src/core/lib/components/ui/select/index.js +0 -37
- package/src/core/lib/components/ui/select/select-content.jsx +0 -30
- package/src/core/lib/components/ui/select/select-group-heading.jsx +0 -17
- package/src/core/lib/components/ui/select/select-group.jsx +0 -15
- package/src/core/lib/components/ui/select/select-item.jsx +0 -26
- package/src/core/lib/components/ui/select/select-label.jsx +0 -11
- package/src/core/lib/components/ui/select/select-portal.jsx +0 -4
- package/src/core/lib/components/ui/select/select-scroll-down-button.jsx +0 -18
- package/src/core/lib/components/ui/select/select-scroll-up-button.jsx +0 -18
- package/src/core/lib/components/ui/select/select-separator.jsx +0 -15
- package/src/core/lib/components/ui/select/select-trigger.jsx +0 -25
- package/src/core/lib/components/ui/select/select.jsx +0 -4
- package/src/core/lib/components/ui/sheet/index.js +0 -34
- package/src/core/lib/components/ui/sheet/sheet-close.jsx +0 -7
- package/src/core/lib/components/ui/sheet/sheet-content.jsx +0 -35
- package/src/core/lib/components/ui/sheet/sheet-description.jsx +0 -15
- package/src/core/lib/components/ui/sheet/sheet-footer.jsx +0 -11
- package/src/core/lib/components/ui/sheet/sheet-header.jsx +0 -11
- package/src/core/lib/components/ui/sheet/sheet-overlay.jsx +0 -15
- package/src/core/lib/components/ui/sheet/sheet-portal.jsx +0 -4
- package/src/core/lib/components/ui/sheet/sheet-title.jsx +0 -15
- package/src/core/lib/components/ui/sheet/sheet-trigger.jsx +0 -7
- package/src/core/lib/components/ui/sheet/sheet.jsx +0 -4
- package/src/core/lib/components/ui/toggle/index.js +0 -8
- package/src/core/lib/components/ui/toggle/toggle.jsx +0 -36
- package/src/core/lib/components/ui/toggle-group/index.js +0 -10
- package/src/core/lib/components/ui/toggle-group/toggle-group-item.jsx +0 -29
- package/src/core/lib/components/ui/toggle-group/toggle-group.jsx +0 -43
- package/src/internals/canvas/StoryErrorBoundary.jsx +0 -47
- package/src/internals/canvas/componentIsolate.jsx +0 -144
- package/src/internals/canvas/componentSetIsolate.jsx +0 -300
- package/src/internals/canvas/widgets/InlineStoryRenderer.jsx +0 -129
- package/src/internals/canvas/widgets/InlineStoryRenderer.test.jsx +0 -68
- package/src/primer/ThemeSync.jsx +0 -73
- package/src/primer/index.js +0 -1
|
@@ -1,211 +1,292 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Theme Store —
|
|
2
|
+
* Theme Store — config-driven theming layer.
|
|
3
3
|
*
|
|
4
|
-
* Reads
|
|
5
|
-
* (`
|
|
6
|
-
* `data-sb-theme` attribute on `<html>`, and dispatches a
|
|
7
|
-
* `storyboard:theme:changed` custom event so that non-store consumers
|
|
8
|
-
* (React ThemeProvider, etc.) can react.
|
|
4
|
+
* Reads its theme + surface registry from the unified config store
|
|
5
|
+
* (`getConfig("theming")`). For each configured surface this module:
|
|
9
6
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* 1. Watches the DOM for `[data-sb-surface="<id>"]` elements and writes
|
|
8
|
+
* `data-sb-<id>-theme="<resolvedThemeId>"` plus every key/value pair
|
|
9
|
+
* from the active theme's `attrs` companion map on those elements.
|
|
10
|
+
* 2. Writes the legacy `<html>` attribute for the canonical surfaces
|
|
11
|
+
* (`data-sb-theme`, `data-sb-toolbar-theme`, `data-sb-code-theme`,
|
|
12
|
+
* `data-sb-canvas-theme`) so existing CSS selectors keep working
|
|
13
|
+
* without migration.
|
|
14
|
+
*
|
|
15
|
+
* State:
|
|
16
|
+
* - `sb-color-scheme` — the active global theme id (or "system")
|
|
17
|
+
* - `sb-theme-sync` — `Record<surfaceId, boolean>` of per-surface sync
|
|
18
|
+
*
|
|
19
|
+
* Both keys are persisted in localStorage. A `storage` event listener picks
|
|
20
|
+
* up cross-frame writes so prototype iframes stay in sync with the parent.
|
|
21
|
+
*
|
|
22
|
+
* Public API stays source-compatible with the pre-refactor module:
|
|
23
|
+
* - `setTheme`, `getTheme`, `themeState`, `THEMES` (now derived from config)
|
|
24
|
+
* - `setThemeSyncTarget`, `getThemeSyncTargets`, `themeSyncState`
|
|
25
|
+
*
|
|
26
|
+
* New exports:
|
|
27
|
+
* - `getSurfaceSync`, `setSurfaceSync` — config-key-aware accessors
|
|
28
|
+
* - `getResolvedFor(surfaceId)` — convenience for consumers
|
|
13
29
|
*/
|
|
14
30
|
|
|
31
|
+
import { getConfig, subscribeToConfig } from './configStore.js'
|
|
32
|
+
import { configDefaults } from './configSchema.js'
|
|
33
|
+
import {
|
|
34
|
+
THEME_STORAGE_KEY,
|
|
35
|
+
SYNC_STORAGE_KEY,
|
|
36
|
+
LEGACY_SURFACE_ATTR,
|
|
37
|
+
readStoredTheme,
|
|
38
|
+
readStoredSync,
|
|
39
|
+
resolveTheme,
|
|
40
|
+
computeBySurface,
|
|
41
|
+
applyThemeToDom,
|
|
42
|
+
} from './themeBootstrap.js'
|
|
43
|
+
|
|
15
44
|
// ---------------------------------------------------------------------------
|
|
16
45
|
// Types
|
|
17
46
|
// ---------------------------------------------------------------------------
|
|
18
47
|
|
|
19
|
-
export type
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
| 'light_colorblind'
|
|
23
|
-
| 'dark'
|
|
24
|
-
| 'dark_colorblind'
|
|
25
|
-
| 'dark_high_contrast'
|
|
26
|
-
| 'dark_dimmed'
|
|
27
|
-
|
|
28
|
-
export interface ThemeOption {
|
|
29
|
-
name: string
|
|
30
|
-
value: ThemeValue
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface ThemeState {
|
|
34
|
-
/** The stored theme value (may be "system") */
|
|
35
|
-
theme: ThemeValue
|
|
36
|
-
/** The resolved CSS theme value (never "system") */
|
|
37
|
-
resolved: string
|
|
38
|
-
}
|
|
48
|
+
export type ThemeOption = { name: string; value: string }
|
|
49
|
+
export type ThemeValue = string
|
|
50
|
+
export interface ThemeState { theme: ThemeValue; resolved: string }
|
|
39
51
|
|
|
40
52
|
type Subscriber<T> = (value: T) => void
|
|
41
53
|
type Unsubscriber = () => void
|
|
42
54
|
|
|
43
|
-
interface Readable<T> {
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
interface Writable<T> extends Readable<T> {
|
|
48
|
-
set(value: T): void
|
|
49
|
-
}
|
|
55
|
+
interface Readable<T> { subscribe(run: Subscriber<T>): Unsubscriber }
|
|
56
|
+
interface Writable<T> extends Readable<T> { set(value: T): void }
|
|
50
57
|
|
|
51
58
|
function writable<T>(initial: T): Writable<T> {
|
|
52
59
|
let value = initial
|
|
53
60
|
const subs = new Set<Subscriber<T>>()
|
|
54
61
|
return {
|
|
55
|
-
set(v: T) {
|
|
56
|
-
value = v
|
|
57
|
-
subs.forEach((fn) => fn(value))
|
|
58
|
-
},
|
|
62
|
+
set(v: T) { value = v; subs.forEach((fn) => fn(value)) },
|
|
59
63
|
subscribe(run: Subscriber<T>): Unsubscriber {
|
|
60
|
-
subs.add(run)
|
|
61
|
-
run(value)
|
|
64
|
+
subs.add(run); run(value)
|
|
62
65
|
return () => { subs.delete(run) }
|
|
63
66
|
},
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
// ---------------------------------------------------------------------------
|
|
68
|
-
//
|
|
71
|
+
// Config snapshot
|
|
69
72
|
// ---------------------------------------------------------------------------
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
{ name: 'Dark Dimmed', value: 'dark_dimmed' },
|
|
81
|
-
]
|
|
74
|
+
function themesConfig(): any {
|
|
75
|
+
// configStore may not be seeded yet (themeStore is imported at the top
|
|
76
|
+
// of the core barrel, before mountStoryboardCore runs). Fall back to the
|
|
77
|
+
// schema defaults so consumers always see the canonical 4-surface set
|
|
78
|
+
// even if storyboard.config.json omits the `theming` block entirely.
|
|
79
|
+
const fromStore = getConfig('theming')
|
|
80
|
+
if (fromStore && (fromStore.surfaces || fromStore.themes)) return fromStore
|
|
81
|
+
return (configDefaults as any).theming || {}
|
|
82
|
+
}
|
|
82
83
|
|
|
83
84
|
// ---------------------------------------------------------------------------
|
|
84
|
-
//
|
|
85
|
+
// Persistent state
|
|
85
86
|
// ---------------------------------------------------------------------------
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function resolveTheme(value: ThemeValue): string {
|
|
93
|
-
return value === 'system' ? getSystemTheme() : value
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function readStoredTheme(): ThemeValue {
|
|
97
|
-
if (typeof localStorage === 'undefined') return 'system'
|
|
98
|
-
const stored = localStorage.getItem(STORAGE_KEY)
|
|
99
|
-
if (!stored) return 'system'
|
|
100
|
-
// Legacy values (pre-system support) are plain scheme names
|
|
101
|
-
if (stored === 'system') return 'system'
|
|
102
|
-
return stored as ThemeValue
|
|
103
|
-
}
|
|
88
|
+
let _current: ThemeValue = readStoredTheme(themesConfig())
|
|
89
|
+
let _syncTargets: Record<string, boolean> = readStoredSync(themesConfig())
|
|
104
90
|
|
|
105
91
|
function snapshot(theme: ThemeValue): ThemeState {
|
|
106
92
|
return { theme, resolved: resolveTheme(theme) }
|
|
107
93
|
}
|
|
108
94
|
|
|
95
|
+
const _store = writable<ThemeState>(snapshot(_current))
|
|
96
|
+
const _syncStore = writable<Record<string, boolean>>(_syncTargets)
|
|
97
|
+
|
|
109
98
|
// ---------------------------------------------------------------------------
|
|
110
|
-
//
|
|
99
|
+
// DOM write + change broadcast
|
|
111
100
|
// ---------------------------------------------------------------------------
|
|
112
101
|
|
|
113
|
-
let _current: ThemeValue = readStoredTheme()
|
|
114
|
-
const _store = writable<ThemeState>(snapshot(_current))
|
|
115
|
-
|
|
116
102
|
function _applyToDOM(theme: ThemeValue, resolved: string): void {
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
// Per-target resolved themes
|
|
121
|
-
const prototypeTheme = _syncTargets.prototype ? resolved : 'light'
|
|
122
|
-
const toolbarTheme = _syncTargets.toolbar ? resolved : 'light'
|
|
123
|
-
const codeTheme = _syncTargets.codeBoxes ? resolved : 'light'
|
|
124
|
-
const canvasTheme = _syncTargets.canvas ? resolved : 'light'
|
|
125
|
-
|
|
126
|
-
// Internal attributes
|
|
127
|
-
el.setAttribute('data-sb-theme', prototypeTheme)
|
|
128
|
-
el.setAttribute('data-sb-code-theme', codeTheme)
|
|
129
|
-
|
|
130
|
-
// Toolbar theme — follows global theme when synced, stays light otherwise
|
|
131
|
-
el.setAttribute('data-sb-toolbar-theme', toolbarTheme)
|
|
132
|
-
el.setAttribute('data-sb-canvas-theme', canvasTheme)
|
|
133
|
-
|
|
134
|
-
// Primer CSS attributes — these drive @primer/react ThemeProvider and
|
|
135
|
-
// Primer CSS custom-property layers without needing React state updates.
|
|
136
|
-
if (theme === 'system' && _syncTargets.prototype) {
|
|
137
|
-
el.setAttribute('data-color-mode', 'auto')
|
|
138
|
-
el.setAttribute('data-light-theme', 'light')
|
|
139
|
-
el.setAttribute('data-dark-theme', 'dark')
|
|
140
|
-
} else if (prototypeTheme.startsWith('dark')) {
|
|
141
|
-
el.setAttribute('data-color-mode', 'dark')
|
|
142
|
-
el.setAttribute('data-dark-theme', prototypeTheme)
|
|
143
|
-
el.setAttribute('data-light-theme', 'light')
|
|
144
|
-
} else {
|
|
145
|
-
el.setAttribute('data-color-mode', 'light')
|
|
146
|
-
el.setAttribute('data-light-theme', prototypeTheme)
|
|
147
|
-
el.setAttribute('data-dark-theme', 'dark')
|
|
148
|
-
}
|
|
103
|
+
const cfg = themesConfig()
|
|
104
|
+
const bySurface = computeBySurface(cfg, resolved, _syncTargets)
|
|
105
|
+
applyThemeToDom(cfg, bySurface)
|
|
149
106
|
}
|
|
150
107
|
|
|
151
108
|
function _dispatchEvent(theme: ThemeValue, resolved: string): void {
|
|
152
109
|
if (typeof document === 'undefined') return
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
110
|
+
const cfg = themesConfig()
|
|
111
|
+
const bySurface = computeBySurface(cfg, resolved, _syncTargets)
|
|
112
|
+
|
|
113
|
+
// bySurface carries the new shape. Legacy keys (prototypeTheme,
|
|
114
|
+
// prototypeResolved, toolbarResolved, codeResolved, canvasResolved) are
|
|
115
|
+
// included so existing listeners — Primer's ThemeSync, embed bridges —
|
|
116
|
+
// continue to work without a coordinated upgrade.
|
|
117
|
+
document.dispatchEvent(new CustomEvent('storyboard:theme:changed', {
|
|
118
|
+
detail: {
|
|
119
|
+
theme,
|
|
120
|
+
resolved,
|
|
121
|
+
bySurface,
|
|
122
|
+
// Legacy mirror — keep until consumers migrate.
|
|
123
|
+
prototypeTheme: bySurface.prototype,
|
|
124
|
+
prototypeResolved: bySurface.prototype,
|
|
125
|
+
toolbarResolved: bySurface.toolbar,
|
|
126
|
+
codeResolved: bySurface.codeBoxes,
|
|
127
|
+
canvasResolved: bySurface.canvas,
|
|
128
|
+
},
|
|
129
|
+
}))
|
|
172
130
|
}
|
|
173
131
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// MutationObserver — pick up surface elements mounted/changed post-load
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
let _observerInstalled = false
|
|
137
|
+
|
|
138
|
+
function _installSurfaceObserver(): void {
|
|
139
|
+
if (_observerInstalled) return
|
|
140
|
+
if (typeof document === 'undefined' || typeof MutationObserver === 'undefined') return
|
|
141
|
+
if (!document.body) {
|
|
142
|
+
// Body not ready yet — try again once it is.
|
|
143
|
+
document.addEventListener('DOMContentLoaded', _installSurfaceObserver, { once: true })
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
_observerInstalled = true
|
|
147
|
+
|
|
148
|
+
const reapply = () => _applyToDOM(_current, resolveTheme(_current))
|
|
149
|
+
const observer = new MutationObserver((mutations) => {
|
|
150
|
+
let needsReapply = false
|
|
151
|
+
for (const m of mutations) {
|
|
152
|
+
if (m.type === 'attributes' && m.attributeName === 'data-sb-surface') {
|
|
153
|
+
needsReapply = true; break
|
|
154
|
+
}
|
|
155
|
+
if (m.type === 'childList') {
|
|
156
|
+
// Only re-apply when an added node could carry/contain a surface marker.
|
|
157
|
+
for (const node of m.addedNodes) {
|
|
158
|
+
if (!(node instanceof Element)) continue
|
|
159
|
+
if (node.hasAttribute('data-sb-surface') ||
|
|
160
|
+
node.querySelector?.('[data-sb-surface]')) {
|
|
161
|
+
needsReapply = true; break
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (needsReapply) break
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (needsReapply) reapply()
|
|
168
|
+
})
|
|
169
|
+
observer.observe(document.body, {
|
|
170
|
+
childList: true,
|
|
171
|
+
subtree: true,
|
|
172
|
+
attributes: true,
|
|
173
|
+
attributeFilter: ['data-sb-surface'],
|
|
174
|
+
})
|
|
179
175
|
}
|
|
180
176
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
// Config change subscription — re-derive state and re-apply when the
|
|
179
|
+
// themes registry changes (e.g. HMR of storyboard.config.json).
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
subscribeToConfig(() => {
|
|
183
|
+
const cfg = themesConfig()
|
|
184
|
+
// Re-read sync defaults for any newly-added surfaces (they get the
|
|
185
|
+
// config default if not present in storage).
|
|
186
|
+
_syncTargets = readStoredSync(cfg)
|
|
187
|
+
_syncStore.set(_syncTargets)
|
|
188
|
+
const state = snapshot(_current)
|
|
189
|
+
_store.set(state)
|
|
190
|
+
_applyToDOM(_current, state.resolved)
|
|
191
|
+
_dispatchEvent(_current, state.resolved)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Public API
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
export function getTheme(): ThemeValue { return _current }
|
|
199
|
+
|
|
185
200
|
export function setTheme(value: ThemeValue): void {
|
|
186
201
|
_current = value
|
|
187
|
-
|
|
188
202
|
if (typeof localStorage !== 'undefined') {
|
|
189
|
-
if (value === 'system')
|
|
190
|
-
|
|
191
|
-
} else {
|
|
192
|
-
localStorage.setItem(STORAGE_KEY, value)
|
|
193
|
-
}
|
|
203
|
+
if (value === 'system') localStorage.removeItem(THEME_STORAGE_KEY)
|
|
204
|
+
else localStorage.setItem(THEME_STORAGE_KEY, value)
|
|
194
205
|
}
|
|
195
|
-
|
|
196
206
|
const state = snapshot(value)
|
|
197
207
|
_store.set(state)
|
|
198
208
|
_applyToDOM(value, state.resolved)
|
|
199
209
|
_dispatchEvent(value, state.resolved)
|
|
200
210
|
}
|
|
201
211
|
|
|
212
|
+
export const themeState: Readable<ThemeState> = { subscribe: _store.subscribe }
|
|
213
|
+
|
|
202
214
|
/**
|
|
203
|
-
*
|
|
215
|
+
* Get a copy of the current per-surface sync map.
|
|
204
216
|
*/
|
|
205
|
-
export
|
|
217
|
+
export function getSurfaceSync(): Record<string, boolean> { return { ..._syncTargets } }
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Set the sync flag for a single surface. Persisted to localStorage.
|
|
221
|
+
*/
|
|
222
|
+
export function setSurfaceSync(surfaceId: string, value: boolean): void {
|
|
223
|
+
_syncTargets = { ..._syncTargets, [surfaceId]: value }
|
|
224
|
+
_syncStore.set(_syncTargets)
|
|
225
|
+
if (typeof localStorage !== 'undefined') {
|
|
226
|
+
localStorage.setItem(SYNC_STORAGE_KEY, JSON.stringify(_syncTargets))
|
|
227
|
+
}
|
|
228
|
+
const state = snapshot(_current)
|
|
229
|
+
_applyToDOM(_current, state.resolved)
|
|
230
|
+
_dispatchEvent(_current, state.resolved)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export const surfaceSyncState: Readable<Record<string, boolean>> = { subscribe: _syncStore.subscribe }
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Convenience accessor — what theme is the given surface currently
|
|
237
|
+
* resolving to?
|
|
238
|
+
*/
|
|
239
|
+
export function getResolvedFor(surfaceId: string): string {
|
|
240
|
+
const resolved = resolveTheme(_current)
|
|
241
|
+
return _syncTargets[surfaceId] ? resolved : 'light'
|
|
242
|
+
}
|
|
206
243
|
|
|
207
244
|
// ---------------------------------------------------------------------------
|
|
208
|
-
//
|
|
245
|
+
// Backward-compat exports
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Derived from the active config. Kept so existing switchers
|
|
250
|
+
* (ThemeMenuButton, CoreUIBar, paletteTheme) keep working until they migrate
|
|
251
|
+
* to the config-driven shape. Always reflects the current registry.
|
|
252
|
+
*/
|
|
253
|
+
export const THEMES: ThemeOption[] = new Proxy([] as ThemeOption[], {
|
|
254
|
+
get(_target, prop) {
|
|
255
|
+
const themes = themesConfig().themes || {}
|
|
256
|
+
const list: ThemeOption[] = Object.entries(themes).map(([value, def]: [string, any]) => ({
|
|
257
|
+
value,
|
|
258
|
+
name: def?.label || value,
|
|
259
|
+
}))
|
|
260
|
+
// "system" is a synthetic theme — surface it at the top if not present
|
|
261
|
+
// and the default is system. (Matches the legacy fixed ordering.)
|
|
262
|
+
if (!list.some(t => t.value === 'system')) {
|
|
263
|
+
list.unshift({ name: 'System', value: 'system' })
|
|
264
|
+
}
|
|
265
|
+
const value = (list as any)[prop]
|
|
266
|
+
return typeof value === 'function' ? value.bind(list) : value
|
|
267
|
+
},
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
/** Deprecated alias for getSurfaceSync — typed shape kept for old call sites. */
|
|
271
|
+
export interface ThemeSyncTargets {
|
|
272
|
+
prototype: boolean
|
|
273
|
+
toolbar: boolean
|
|
274
|
+
codeBoxes: boolean
|
|
275
|
+
canvas: boolean
|
|
276
|
+
[key: string]: boolean
|
|
277
|
+
}
|
|
278
|
+
export function getThemeSyncTargets(): ThemeSyncTargets {
|
|
279
|
+
return getSurfaceSync() as ThemeSyncTargets
|
|
280
|
+
}
|
|
281
|
+
export function setThemeSyncTarget(target: keyof ThemeSyncTargets, value: boolean): void {
|
|
282
|
+
setSurfaceSync(String(target), value)
|
|
283
|
+
}
|
|
284
|
+
export const themeSyncState: Readable<ThemeSyncTargets> = {
|
|
285
|
+
subscribe(run) { return _syncStore.subscribe((v) => run(v as ThemeSyncTargets)) },
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
// OS preference + cross-frame storage listeners
|
|
209
290
|
// ---------------------------------------------------------------------------
|
|
210
291
|
|
|
211
292
|
if (typeof window !== 'undefined') {
|
|
@@ -219,23 +300,20 @@ if (typeof window !== 'undefined') {
|
|
|
219
300
|
_dispatchEvent('system', state.resolved)
|
|
220
301
|
})
|
|
221
302
|
|
|
222
|
-
// Cross-frame sync
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
// pick that up here and re-apply the DOM attributes / dispatch the
|
|
226
|
-
// theme-changed event. Without this, Tailwind utilities (and Primer
|
|
227
|
-
// CSS vars) inside the iframe stay stuck on the load-time theme.
|
|
303
|
+
// Cross-frame sync via the storage event: prototype iframes share the
|
|
304
|
+
// same origin as the parent, so a localStorage write from the toolbar
|
|
305
|
+
// theme switcher needs to repaint the iframe too.
|
|
228
306
|
window.addEventListener('storage', (e) => {
|
|
229
307
|
if (e.storageArea !== localStorage) return
|
|
230
|
-
if (e.key ===
|
|
231
|
-
const next = readStoredTheme()
|
|
308
|
+
if (e.key === THEME_STORAGE_KEY || e.key === null) {
|
|
309
|
+
const next = readStoredTheme(themesConfig())
|
|
232
310
|
_current = next
|
|
233
311
|
const state = snapshot(next)
|
|
234
312
|
_store.set(state)
|
|
235
313
|
_applyToDOM(next, state.resolved)
|
|
236
314
|
_dispatchEvent(next, state.resolved)
|
|
237
315
|
} else if (e.key === SYNC_STORAGE_KEY) {
|
|
238
|
-
_syncTargets = readStoredSync()
|
|
316
|
+
_syncTargets = readStoredSync(themesConfig())
|
|
239
317
|
_syncStore.set(_syncTargets)
|
|
240
318
|
const state = snapshot(_current)
|
|
241
319
|
_applyToDOM(_current, state.resolved)
|
|
@@ -245,71 +323,8 @@ if (typeof window !== 'undefined') {
|
|
|
245
323
|
}
|
|
246
324
|
|
|
247
325
|
// ---------------------------------------------------------------------------
|
|
248
|
-
//
|
|
249
|
-
// ---------------------------------------------------------------------------
|
|
250
|
-
|
|
251
|
-
/** Which parts of the UI follow the global theme */
|
|
252
|
-
export interface ThemeSyncTargets {
|
|
253
|
-
prototype: boolean
|
|
254
|
-
toolbar: boolean
|
|
255
|
-
codeBoxes: boolean
|
|
256
|
-
canvas: boolean
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const SYNC_STORAGE_KEY = 'sb-theme-sync'
|
|
260
|
-
|
|
261
|
-
const DEFAULT_SYNC: ThemeSyncTargets = {
|
|
262
|
-
prototype: true,
|
|
263
|
-
toolbar: false,
|
|
264
|
-
codeBoxes: true,
|
|
265
|
-
canvas: true,
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function readStoredSync(): ThemeSyncTargets {
|
|
269
|
-
if (typeof localStorage === 'undefined') return { ...DEFAULT_SYNC }
|
|
270
|
-
try {
|
|
271
|
-
const raw = localStorage.getItem(SYNC_STORAGE_KEY)
|
|
272
|
-
if (!raw) return { ...DEFAULT_SYNC }
|
|
273
|
-
return { ...DEFAULT_SYNC, ...JSON.parse(raw) }
|
|
274
|
-
} catch {
|
|
275
|
-
return { ...DEFAULT_SYNC }
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
let _syncTargets: ThemeSyncTargets = readStoredSync()
|
|
280
|
-
const _syncStore = writable<ThemeSyncTargets>(_syncTargets)
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Get the current theme sync targets.
|
|
284
|
-
*/
|
|
285
|
-
export function getThemeSyncTargets(): ThemeSyncTargets {
|
|
286
|
-
return { ..._syncTargets }
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Set a theme sync target. Persists to localStorage.
|
|
291
|
-
*/
|
|
292
|
-
export function setThemeSyncTarget(target: keyof ThemeSyncTargets, value: boolean): void {
|
|
293
|
-
_syncTargets = { ..._syncTargets, [target]: value }
|
|
294
|
-
_syncStore.set(_syncTargets)
|
|
295
|
-
|
|
296
|
-
if (typeof localStorage !== 'undefined') {
|
|
297
|
-
localStorage.setItem(SYNC_STORAGE_KEY, JSON.stringify(_syncTargets))
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Re-apply DOM attributes so toolbar/codebox can react
|
|
301
|
-
const state = snapshot(_current)
|
|
302
|
-
_applyToDOM(_current, state.resolved)
|
|
303
|
-
_dispatchEvent(_current, state.resolved)
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Readable store for sync target state.
|
|
308
|
-
*/
|
|
309
|
-
export const themeSyncState: Readable<ThemeSyncTargets> = { subscribe: _syncStore.subscribe }
|
|
310
|
-
|
|
311
|
-
// ---------------------------------------------------------------------------
|
|
312
|
-
// Boot — apply the stored theme immediately on import
|
|
326
|
+
// Boot — install observer and paint immediately
|
|
313
327
|
// ---------------------------------------------------------------------------
|
|
314
328
|
|
|
329
|
+
_installSurfaceObserver()
|
|
315
330
|
_applyToDOM(_current, resolveTheme(_current))
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate the contents of `themes.generated.css` from the project's
|
|
3
|
+
* themes config. Emits only the Tailwind scaffolding needed at compile
|
|
4
|
+
* time — the `@custom-variant dark (…)` selector list — so the `dark:`
|
|
5
|
+
* utility variant is aware of every configured surface attribute
|
|
6
|
+
* (`data-sb-<surface>-theme`) and the legacy `<html>` attribute set.
|
|
7
|
+
*
|
|
8
|
+
* No CSS-variable values are emitted here. Components own their own
|
|
9
|
+
* variable mappings in tailwind.css / core-ui-colors.css / per-component CSS.
|
|
10
|
+
*
|
|
11
|
+
* Theme ids whose key starts with "dark" trip the variant via prefix match
|
|
12
|
+
* (`[data-sb-…-theme^="dark"]`), so new dark variants like "dark_midnight"
|
|
13
|
+
* just work without changing this rule.
|
|
14
|
+
*
|
|
15
|
+
* This module is consumed by:
|
|
16
|
+
* - packages/storyboard/src/internals/vite/data-plugin.js — writes the
|
|
17
|
+
* file on dev boot and on storyboard.config.json changes.
|
|
18
|
+
* - packages/storyboard/scripts/gen-themes-css.mjs — pre-step for the
|
|
19
|
+
* standalone `tailwindcss` CLI invoked by `build:css`.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export const GENERATED_THEMES_CSS_FILENAME = 'themes.generated.css'
|
|
23
|
+
|
|
24
|
+
export const GENERATED_BANNER =
|
|
25
|
+
'/* Auto-generated by @dfosco/storyboard data plugin — do not edit. */'
|
|
26
|
+
|
|
27
|
+
const LEGACY_SURFACE_ATTRS = [
|
|
28
|
+
'data-sb-toolbar-theme',
|
|
29
|
+
'data-sb-code-theme',
|
|
30
|
+
'data-sb-canvas-theme',
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
export function generateThemesCss(themesCfg) {
|
|
34
|
+
const surfaces = Object.keys(themesCfg?.surfaces || {})
|
|
35
|
+
const selectors = ['[data-sb-theme^="dark"]', '[data-sb-theme^="dark"] *']
|
|
36
|
+
|
|
37
|
+
for (const id of surfaces) {
|
|
38
|
+
const attr = `data-sb-${id.toLowerCase()}-theme`
|
|
39
|
+
if (!selectors.includes(`[${attr}^="dark"]`)) {
|
|
40
|
+
selectors.push(`[${attr}^="dark"]`, `[${attr}^="dark"] *`)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Legacy <html> attrs — kept in the selector list even when the
|
|
45
|
+
// corresponding surface is removed from config so existing CSS that
|
|
46
|
+
// selects on the legacy attribute names continues to participate in
|
|
47
|
+
// the `dark:` variant.
|
|
48
|
+
for (const attr of LEGACY_SURFACE_ATTRS) {
|
|
49
|
+
if (!selectors.includes(`[${attr}^="dark"]`)) {
|
|
50
|
+
selectors.push(`[${attr}^="dark"]`, `[${attr}^="dark"] *`)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return [
|
|
55
|
+
GENERATED_BANNER,
|
|
56
|
+
'@custom-variant dark (',
|
|
57
|
+
' &:where(',
|
|
58
|
+
' ' + selectors.join(',\n '),
|
|
59
|
+
' )',
|
|
60
|
+
');',
|
|
61
|
+
'',
|
|
62
|
+
].join('\n')
|
|
63
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
@import "tw-animate-css";
|
|
3
|
+
@import "./themes.generated.css";
|
|
3
4
|
@source "../**/*.{jsx,js,ts}";
|
|
4
5
|
|
|
5
6
|
@font-face {
|
|
@@ -22,8 +23,6 @@
|
|
|
22
23
|
font-display: swap;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
@custom-variant dark (&:where([data-sb-theme^="dark"], [data-sb-theme^="dark"] *));
|
|
26
|
-
|
|
27
26
|
@theme {
|
|
28
27
|
--color-background: hsl(0 0% 100%);
|
|
29
28
|
--color-foreground: hsl(222.2 84% 4.9%);
|
|
@@ -176,9 +175,18 @@
|
|
|
176
175
|
}
|
|
177
176
|
}
|
|
178
177
|
|
|
178
|
+
/* Superellipse corners via the CSS Houdini Paint API.
|
|
179
|
+
* Only Chromium browsers ship paint() today — Firefox and Safari fall back
|
|
180
|
+
* to a regular border-radius via the @supports query below. Override the
|
|
181
|
+
* fallback radius per-element with --sb--smooth-corners-fallback if needed. */
|
|
179
182
|
@utility smooth-corners {
|
|
180
|
-
|
|
181
|
-
|
|
183
|
+
border-radius: var(--sb--smooth-corners-fallback, 0.75rem);
|
|
184
|
+
|
|
185
|
+
@supports (mask-image: paint(smooth-corners)) {
|
|
186
|
+
border-radius: 0;
|
|
187
|
+
mask-image: paint(smooth-corners);
|
|
188
|
+
-webkit-mask-image: paint(smooth-corners);
|
|
189
|
+
}
|
|
182
190
|
}
|
|
183
191
|
|
|
184
192
|
@layer base {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workspace-chrome.css — CSS dependencies for storyboard's own workspace
|
|
3
|
+
* UI (Workspace, Viewfinder, CanvasPage, BranchBar, CoreUIBar). Imported
|
|
4
|
+
* by `src/library/index.jsx` so the workspace SPA loads them.
|
|
5
|
+
*
|
|
6
|
+
* Prototypes and stories do NOT load this stylesheet — their CSS chain
|
|
7
|
+
* is owned by the consumer's `src/_prototype.jsx` / `src/_story.jsx`.
|
|
8
|
+
*
|
|
9
|
+
* Sources:
|
|
10
|
+
* - @primer/primitives — supplies the --fgColor-*, --bgColor-*,
|
|
11
|
+
* --borderColor-*, and theme attr selectors that the ~200 CSS module
|
|
12
|
+
* references across the workspace surfaces depend on.
|
|
13
|
+
*/
|
|
14
|
+
@import '@primer/primitives/dist/css/base/size/size.css';
|
|
15
|
+
@import '@primer/primitives/dist/css/base/typography/typography.css';
|
|
16
|
+
@import '@primer/primitives/dist/css/base/motion/motion.css';
|
|
17
|
+
@import '@primer/primitives/dist/css/functional/size/border.css';
|
|
18
|
+
@import '@primer/primitives/dist/css/functional/size/breakpoints.css';
|
|
19
|
+
@import '@primer/primitives/dist/css/functional/size/size-coarse.css';
|
|
20
|
+
@import '@primer/primitives/dist/css/functional/size/size-fine.css';
|
|
21
|
+
@import '@primer/primitives/dist/css/functional/size/size.css';
|
|
22
|
+
@import '@primer/primitives/dist/css/functional/size/viewport.css';
|
|
23
|
+
@import '@primer/primitives/dist/css/functional/typography/typography.css';
|
|
24
|
+
|
|
25
|
+
@import '@primer/primitives/dist/css/functional/themes/light.css';
|
|
26
|
+
@import '@primer/primitives/dist/css/functional/themes/light-colorblind.css';
|
|
27
|
+
@import '@primer/primitives/dist/css/functional/themes/dark.css';
|
|
28
|
+
@import '@primer/primitives/dist/css/functional/themes/dark-colorblind.css';
|
|
29
|
+
@import '@primer/primitives/dist/css/functional/themes/dark-high-contrast.css';
|
|
30
|
+
@import '@primer/primitives/dist/css/functional/themes/dark-dimmed.css';
|