@geenius/storybook 0.1.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Antigravity
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # @geenius/storybook
2
+
3
+ Shared storybook shell, theme, layout, and Vite preset for all `@geenius` packages.
4
+
5
+ Provides a consistent look-and-feel, design tokens, and development infrastructure so every package storybook (React, SolidJS, Vue, Svelte) shares the same UI shell without duplicating code.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pnpm add @geenius/storybook
11
+ ```
12
+
13
+ ## What's Inside
14
+
15
+ | Export | Description |
16
+ |---|---|
17
+ | `@geenius/storybook` | Barrel — all types, surface utils, runtime utils, Vite preset |
18
+ | `@geenius/storybook/surface` | Story catalog definition and query utilities |
19
+ | `@geenius/storybook/react` | Shared React storybook shell and layout primitives |
20
+ | `@geenius/storybook/solidjs` | Shared SolidJS storybook shell and layout primitives |
21
+ | `@geenius/storybook/types` | TypeScript type definitions |
22
+ | `@geenius/storybook/utils` | Hash routing, theme management, keyboard shortcuts |
23
+ | `@geenius/storybook/vite` | `createStorybookViteConfig()` factory |
24
+ | `@geenius/storybook/styles` | Shell layout CSS (sidebar, panels, dashboard, comparison) |
25
+ | `@geenius/storybook/theme` | Component design tokens (OKLCH dark/light + Tailwind v4 `@theme`) |
26
+ | `@geenius/storybook/html` | Base HTML template with font imports |
27
+
28
+ ## Quick Start
29
+
30
+ ### 1. Define your story surface
31
+
32
+ ```ts
33
+ // apps/storybook-react/src/surface.ts
34
+ import { defineSurface } from '@geenius/storybook/surface'
35
+
36
+ export const SURFACE = defineSurface([
37
+ {
38
+ title: 'Primitives',
39
+ stories: [
40
+ { id: 'button', label: 'Button' },
41
+ { id: 'input', label: 'Input' },
42
+ ],
43
+ },
44
+ ])
45
+ ```
46
+
47
+ ### 2. Create a Vite config
48
+
49
+ ```ts
50
+ // apps/storybook-react/vite.config.ts
51
+ import { createStorybookViteConfig } from '@geenius/storybook/vite'
52
+ import react from '@vitejs/plugin-react'
53
+ import tailwindcss from '@tailwindcss/vite'
54
+
55
+ export default createStorybookViteConfig({
56
+ framework: 'react',
57
+ port: 3050,
58
+ plugins: [react()],
59
+ tailwind: tailwindcss(),
60
+ })
61
+ ```
62
+
63
+ ### 3. Import styles
64
+
65
+ ```css
66
+ /* apps/storybook-react/src/index.css */
67
+ @import "@geenius/storybook/styles";
68
+ @import "@geenius/storybook/theme";
69
+ @import "tailwindcss";
70
+ ```
71
+
72
+ ### 4. Use shell classes in your App
73
+
74
+ ```tsx
75
+ import { ReactStorybookApp } from '@geenius/storybook/react'
76
+
77
+ export function App() {
78
+ return (
79
+ <ReactStorybookApp
80
+ packageName="Geenius UI"
81
+ frameworkLabel="React"
82
+ overview="Shared component stories with the standard Geenius shell."
83
+ sections={SURFACE}
84
+ components={COMPONENTS}
85
+ defaultStoryId="button"
86
+ />
87
+ )
88
+ }
89
+ ```
90
+
91
+ ## Shell CSS Classes
92
+
93
+ All classes use the `gsb-` prefix to avoid collisions.
94
+
95
+ ### Layout
96
+ - `.gsb-shell` — Root grid (sidebar + main)
97
+ - `.gsb-sidebar` — Sticky sidebar with blur backdrop
98
+ - `.gsb-main` / `.gsb-main__inner` — Constrained content area
99
+
100
+ ### Sidebar
101
+ - `.gsb-sidebar__item` — Nav item (add `.is-active` for selected state)
102
+ - `.gsb-sidebar__section` / `__section-title` — Grouped sections
103
+ - `.gsb-search__input` — Filter input
104
+ - `.gsb-theme-toggle` — Theme switch button
105
+
106
+ ### Content
107
+ - `.gsb-hero` — Landing hero card
108
+ - `.gsb-comparison` — 2-column TW vs CSS comparison grid
109
+ - `.gsb-comparison__canvas` — Individual comparison pane
110
+ - `.gsb-card` — Content card (modifiers: `--accent`, `--success`, `--warning`, `--danger`, `--interactive`)
111
+ - `.gsb-metrics` / `.gsb-metric` — Stats grid
112
+ - `.gsb-note` — Callout (modifiers: `--warning`, `--success`, `--danger`)
113
+
114
+ ### Utilities
115
+ - `.gsb-stack` — Vertical flex gap
116
+ - `.gsb-inline` — Horizontal flex wrap
117
+ - `.gsb-frame` — Bordered container
118
+ - `.gsb-pill` / `.gsb-pill--accent` — Tag/badge
119
+ - `.gsb-eyebrow` — Uppercase label
120
+ - `.gsb-count` — Numeric badge
121
+ - `.gsb-muted` — Muted text
122
+
123
+ ### Responsive
124
+ - `<= 860px`: Sidebar becomes mobile drawer (`.is-open` + `.gsb-backdrop`)
125
+ - `<= 1080px`: 3-column grids collapse to 2
126
+ - `<= 640px`: Tighter padding
127
+
128
+ ## Package Contract
129
+
130
+ This package follows the [Geenius Package Golden Standard](../PACKAGE_GOLDEN_STANDARD.md).
131
+
132
+ - Versioned via **Changesets**
133
+ - CI: `ci.yml` (lint + build + test on PR/push)
134
+ - Release: `release.yml` (changesets/action on main)
135
+ - ESM-only, Node >= 20
@@ -0,0 +1,65 @@
1
+ // src/surface.ts
2
+ function defineSurface(surface) {
3
+ return surface;
4
+ }
5
+ function getSurfaceIds(surface) {
6
+ return surface.flatMap(
7
+ (section) => section.stories.map((story) => story.id)
8
+ );
9
+ }
10
+ function getSurfaceEntries(surface) {
11
+ return surface.flatMap((section) => [...section.stories]);
12
+ }
13
+ function getSurfaceComponentCount(surface) {
14
+ return surface.reduce((sum, section) => sum + section.stories.length, 0);
15
+ }
16
+ function findSurfaceEntry(surface, id) {
17
+ for (const section of surface) {
18
+ const entry = section.stories.find((s) => s.id === id);
19
+ if (entry) return entry;
20
+ }
21
+ return void 0;
22
+ }
23
+ function findSurfaceSection(surface, storyId) {
24
+ return surface.find(
25
+ (section) => section.stories.some((s) => s.id === storyId)
26
+ );
27
+ }
28
+ function filterSurface(surface, query) {
29
+ const q = query.toLowerCase().trim();
30
+ if (!q) return [...surface];
31
+ return surface.map((section) => ({
32
+ ...section,
33
+ stories: section.stories.filter(
34
+ (story) => story.label.toLowerCase().includes(q) || story.id.toLowerCase().includes(q) || story.description?.toLowerCase().includes(q) || story.tags?.some((tag) => tag.toLowerCase().includes(q))
35
+ )
36
+ })).filter((section) => section.stories.length > 0);
37
+ }
38
+ function getSurfaceSnapshot(surface) {
39
+ return surface.map((section) => ({
40
+ title: section.title,
41
+ stories: section.stories.map((story) => ({
42
+ id: story.id,
43
+ label: story.label,
44
+ description: story.description,
45
+ tags: story.tags
46
+ }))
47
+ }));
48
+ }
49
+ function validateSurface(surface) {
50
+ const seen = /* @__PURE__ */ new Set();
51
+ const duplicates = [];
52
+ for (const section of surface) {
53
+ for (const story of section.stories) {
54
+ if (seen.has(story.id)) {
55
+ duplicates.push(story.id);
56
+ }
57
+ seen.add(story.id);
58
+ }
59
+ }
60
+ return duplicates;
61
+ }
62
+
63
+ export { defineSurface, filterSurface, findSurfaceEntry, findSurfaceSection, getSurfaceComponentCount, getSurfaceEntries, getSurfaceIds, getSurfaceSnapshot, validateSurface };
64
+ //# sourceMappingURL=chunk-FB7KPF72.js.map
65
+ //# sourceMappingURL=chunk-FB7KPF72.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/surface.ts"],"names":[],"mappings":"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","file":"chunk-FB7KPF72.js","sourcesContent":["/* ============================================================================\n @geenius/storybook — Story Surface Utilities\n ============================================================================\n Framework-agnostic helpers for defining, querying, and validating the\n story catalog that drives sidebar navigation and dashboards.\n ============================================================================ */\n\nimport type {\n StorySurface,\n StorySurfaceEntry,\n StorySurfaceSection,\n} from './types.js'\n\n// Re-export types for convenience\nexport type { StorySurface, StorySurfaceEntry, StorySurfaceSection }\n\n/**\n * Define a story surface with type-safety.\n * The `as const satisfies` pattern is applied by the consumer; this helper\n * validates and returns the surface unchanged.\n */\nexport function defineSurface<const T extends StorySurface>(surface: T): T {\n return surface\n}\n\n/** Extract a flat array of all story IDs from a surface. */\nexport function getSurfaceIds(surface: StorySurface): string[] {\n return surface.flatMap((section) =>\n section.stories.map((story) => story.id),\n )\n}\n\n/** Extract a flat array of all story entries from a surface. */\nexport function getSurfaceEntries(surface: StorySurface): StorySurfaceEntry[] {\n return surface.flatMap((section) => [...section.stories])\n}\n\n/** Get the total component count across all sections. */\nexport function getSurfaceComponentCount(surface: StorySurface): number {\n return surface.reduce((sum, section) => sum + section.stories.length, 0)\n}\n\n/** Look up a story entry by ID. Returns `undefined` if not found. */\nexport function findSurfaceEntry(\n surface: StorySurface,\n id: string,\n): StorySurfaceEntry | undefined {\n for (const section of surface) {\n const entry = section.stories.find((s) => s.id === id)\n if (entry) return entry\n }\n return undefined\n}\n\n/** Look up which section a story belongs to. */\nexport function findSurfaceSection(\n surface: StorySurface,\n storyId: string,\n): StorySurfaceSection | undefined {\n return surface.find((section) =>\n section.stories.some((s) => s.id === storyId),\n )\n}\n\n/**\n * Filter a surface by a search query (case-insensitive match against\n * story label, id, or description). Returns a new surface with only\n * matching entries, omitting empty sections.\n */\nexport function filterSurface(\n surface: StorySurface,\n query: string,\n): StorySurfaceSection[] {\n const q = query.toLowerCase().trim()\n if (!q) return [...surface]\n\n return surface\n .map((section) => ({\n ...section,\n stories: section.stories.filter(\n (story) =>\n story.label.toLowerCase().includes(q) ||\n story.id.toLowerCase().includes(q) ||\n story.description?.toLowerCase().includes(q) ||\n story.tags?.some((tag) => tag.toLowerCase().includes(q)),\n ),\n }))\n .filter((section) => section.stories.length > 0)\n}\n\n/**\n * Create a JSON-serialisable snapshot of the surface for testing\n * or documentation generation.\n */\nexport function getSurfaceSnapshot(\n surface: StorySurface,\n): {\n title: string\n stories: {\n id: string\n label: string\n description?: string\n tags?: readonly string[]\n }[]\n}[] {\n return surface.map((section) => ({\n title: section.title,\n stories: section.stories.map((story) => ({\n id: story.id,\n label: story.label,\n description: story.description,\n tags: story.tags,\n })),\n }))\n}\n\n/**\n * Validate that all story IDs in a surface are unique.\n * Returns an array of duplicate IDs (empty if valid).\n */\nexport function validateSurface(surface: StorySurface): string[] {\n const seen = new Set<string>()\n const duplicates: string[] = []\n\n for (const section of surface) {\n for (const story of section.stories) {\n if (seen.has(story.id)) {\n duplicates.push(story.id)\n }\n seen.add(story.id)\n }\n }\n\n return duplicates\n}\n"]}
@@ -0,0 +1,65 @@
1
+ // src/utils.ts
2
+ function getHashRoute(validIds) {
3
+ if (typeof window === "undefined") return "dashboard";
4
+ const hash = window.location.hash.replace("#/", "").replace("#", "");
5
+ if (!hash || hash === "dashboard") return "dashboard";
6
+ if (validIds.includes(hash)) return hash;
7
+ return "dashboard";
8
+ }
9
+ function setHashRoute(view) {
10
+ if (typeof window === "undefined") return;
11
+ window.location.hash = `#/${view}`;
12
+ }
13
+ var THEME_STORAGE_KEY = "geenius-storybook-theme";
14
+ function getStoredTheme(fallback = "dark") {
15
+ if (typeof window === "undefined") return fallback;
16
+ const stored = localStorage.getItem(THEME_STORAGE_KEY);
17
+ if (stored === "dark" || stored === "light" || stored === "system") {
18
+ return stored;
19
+ }
20
+ return fallback;
21
+ }
22
+ function setStoredTheme(mode) {
23
+ if (typeof window === "undefined") return;
24
+ localStorage.setItem(THEME_STORAGE_KEY, mode);
25
+ }
26
+ function resolveTheme(mode) {
27
+ if (mode !== "system") return mode;
28
+ if (typeof window === "undefined") return "dark";
29
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
30
+ }
31
+ function applyTheme(effective) {
32
+ if (typeof document === "undefined") return;
33
+ document.documentElement.setAttribute("data-theme", effective);
34
+ document.documentElement.classList.toggle("light", effective === "light");
35
+ }
36
+ function computeDashboardStats(surface, meta) {
37
+ return {
38
+ componentCount: surface.reduce(
39
+ (sum, section) => sum + section.stories.length,
40
+ 0
41
+ ),
42
+ sectionCount: surface.length,
43
+ styleVariantCount: meta.styleVariants.length,
44
+ frameworkCount: meta.frameworks.length
45
+ };
46
+ }
47
+ var DEFAULT_SHORTCUTS = {
48
+ toggleSidebar: "Escape",
49
+ toggleTheme: "t",
50
+ focusSearch: "/",
51
+ goHome: "h"
52
+ };
53
+ function matchesShortcut(event, shortcut) {
54
+ const parts = shortcut.split("+").map((p) => p.trim().toLowerCase());
55
+ const key = parts.pop();
56
+ const needsMeta = parts.includes("cmd") || parts.includes("meta");
57
+ const needsCtrl = parts.includes("ctrl");
58
+ const needsShift = parts.includes("shift");
59
+ const needsAlt = parts.includes("alt");
60
+ return event.key.toLowerCase() === key && event.metaKey === needsMeta && event.ctrlKey === needsCtrl && event.shiftKey === needsShift && event.altKey === needsAlt;
61
+ }
62
+
63
+ export { DEFAULT_SHORTCUTS, applyTheme, computeDashboardStats, getHashRoute, getStoredTheme, matchesShortcut, resolveTheme, setHashRoute, setStoredTheme };
64
+ //# sourceMappingURL=chunk-IEHIPVSX.js.map
65
+ //# sourceMappingURL=chunk-IEHIPVSX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts"],"names":[],"mappings":"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","file":"chunk-IEHIPVSX.js","sourcesContent":["/* ============================================================================\n @geenius/storybook — Runtime Utilities\n ============================================================================\n Framework-agnostic helpers for hash routing, theme management, and\n dashboard stat computation.\n ============================================================================ */\n\nimport type {\n ActiveView,\n DashboardStats,\n StorybookMeta,\n StorySurface,\n ThemeMode,\n} from './types.js'\n\n// ---------------------------------------------------------------------------\n// Hash routing\n// ---------------------------------------------------------------------------\n\n/** Parse the current `window.location.hash` into an `ActiveView`. */\nexport function getHashRoute<TId extends string>(\n validIds: readonly TId[],\n): ActiveView<TId> {\n if (typeof window === 'undefined') return 'dashboard'\n const hash = window.location.hash.replace('#/', '').replace('#', '')\n if (!hash || hash === 'dashboard') return 'dashboard'\n if ((validIds as readonly string[]).includes(hash)) return hash as TId\n return 'dashboard'\n}\n\n/** Push a new hash route without full page reload. */\nexport function setHashRoute(view: string): void {\n if (typeof window === 'undefined') return\n window.location.hash = `#/${view}`\n}\n\n// ---------------------------------------------------------------------------\n// Theme management\n// ---------------------------------------------------------------------------\n\nconst THEME_STORAGE_KEY = 'geenius-storybook-theme'\n\n/** Read the stored theme preference, falling back to the given default. */\nexport function getStoredTheme(fallback: ThemeMode = 'dark'): ThemeMode {\n if (typeof window === 'undefined') return fallback\n const stored = localStorage.getItem(THEME_STORAGE_KEY)\n if (stored === 'dark' || stored === 'light' || stored === 'system') {\n return stored\n }\n return fallback\n}\n\n/** Persist the theme preference. */\nexport function setStoredTheme(mode: ThemeMode): void {\n if (typeof window === 'undefined') return\n localStorage.setItem(THEME_STORAGE_KEY, mode)\n}\n\n/**\n * Resolve a theme mode to an effective `'dark' | 'light'` value,\n * accounting for `'system'` by querying `prefers-color-scheme`.\n */\nexport function resolveTheme(mode: ThemeMode): 'dark' | 'light' {\n if (mode !== 'system') return mode\n if (typeof window === 'undefined') return 'dark'\n return window.matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light'\n}\n\n/**\n * Apply the given effective theme to the document root.\n * Sets the `data-theme` attribute and toggles the `light` class.\n */\nexport function applyTheme(effective: 'dark' | 'light'): void {\n if (typeof document === 'undefined') return\n document.documentElement.setAttribute('data-theme', effective)\n document.documentElement.classList.toggle('light', effective === 'light')\n}\n\n// ---------------------------------------------------------------------------\n// Dashboard stats\n// ---------------------------------------------------------------------------\n\n/** Compute dashboard statistics from a surface and meta. */\nexport function computeDashboardStats(\n surface: StorySurface,\n meta: StorybookMeta,\n): DashboardStats {\n return {\n componentCount: surface.reduce(\n (sum, section) => sum + section.stories.length,\n 0,\n ),\n sectionCount: surface.length,\n styleVariantCount: meta.styleVariants.length,\n frameworkCount: meta.frameworks.length,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Keyboard shortcuts\n// ---------------------------------------------------------------------------\n\n/** Key bindings for common storybook actions. */\nexport interface KeyboardShortcuts {\n /** Toggle sidebar visibility (mobile). Default: `Escape` */\n toggleSidebar: string\n /** Toggle theme mode. Default: `t` */\n toggleTheme: string\n /** Focus search input. Default: `/` or `Cmd+K` */\n focusSearch: string\n /** Navigate to dashboard. Default: `h` */\n goHome: string\n}\n\nexport const DEFAULT_SHORTCUTS: KeyboardShortcuts = {\n toggleSidebar: 'Escape',\n toggleTheme: 't',\n focusSearch: '/',\n goHome: 'h',\n}\n\n/**\n * Check whether a KeyboardEvent matches a shortcut string.\n * Supports modifiers: `Cmd+K`, `Ctrl+Shift+P`, etc.\n */\nexport function matchesShortcut(event: KeyboardEvent, shortcut: string): boolean {\n const parts = shortcut.split('+').map((p) => p.trim().toLowerCase())\n const key = parts.pop()!\n const needsMeta = parts.includes('cmd') || parts.includes('meta')\n const needsCtrl = parts.includes('ctrl')\n const needsShift = parts.includes('shift')\n const needsAlt = parts.includes('alt')\n\n return (\n event.key.toLowerCase() === key &&\n event.metaKey === needsMeta &&\n event.ctrlKey === needsCtrl &&\n event.shiftKey === needsShift &&\n event.altKey === needsAlt\n )\n}\n"]}
@@ -0,0 +1,43 @@
1
+ import { mergeConfig } from 'vite';
2
+
3
+ // src/vite.ts
4
+ function createStorybookViteConfig(options) {
5
+ const {
6
+ framework,
7
+ port,
8
+ plugins = [],
9
+ aliases = {},
10
+ tailwind,
11
+ config
12
+ } = options;
13
+ const resolvedPlugins = [...plugins];
14
+ if (tailwind !== false && tailwind != null) {
15
+ resolvedPlugins.push(tailwind);
16
+ }
17
+ const baseConfig = {
18
+ plugins: resolvedPlugins,
19
+ resolve: {
20
+ alias: {
21
+ "@": new URL("./src", `file://${process.cwd()}/`).pathname,
22
+ ...aliases
23
+ }
24
+ },
25
+ server: {
26
+ port,
27
+ strictPort: false
28
+ },
29
+ build: {
30
+ target: "es2022",
31
+ sourcemap: true
32
+ },
33
+ define: {
34
+ __GSB_FRAMEWORK__: JSON.stringify(framework),
35
+ __GSB_PORT__: JSON.stringify(port)
36
+ }
37
+ };
38
+ return config ? mergeConfig(baseConfig, config) : baseConfig;
39
+ }
40
+
41
+ export { createStorybookViteConfig };
42
+ //# sourceMappingURL=chunk-PRS2B2OG.js.map
43
+ //# sourceMappingURL=chunk-PRS2B2OG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vite.ts"],"names":[],"mappings":";;;AA0BO,SAAS,0BAA0B,OAAA,EAA+B;AACvE,EAAA,MAAM;AAAA,IACJ,SAAA;AAAA,IACA,IAAA;AAAA,IACA,UAAU,EAAC;AAAA,IACX,UAAU,EAAC;AAAA,IACX,QAAA;AAAA,IACA;AAAA,GACF,GAAI,OAAA;AAGJ,EAAA,MAAM,eAAA,GAAkB,CAAC,GAAG,OAAO,CAAA;AAEnC,EAAA,IAAI,QAAA,KAAa,KAAA,IAAS,QAAA,IAAY,IAAA,EAAM;AAC1C,IAAA,eAAA,CAAgB,KAAK,QAAQ,CAAA;AAAA,EAC/B;AAEA,EAAA,MAAM,UAAA,GAAa;AAAA,IACjB,OAAA,EAAS,eAAA;AAAA,IAET,OAAA,EAAS;AAAA,MACP,KAAA,EAAO;AAAA,QACL,GAAA,EAAK,IAAI,GAAA,CAAI,OAAA,EAAS,UAAU,OAAA,CAAQ,GAAA,EAAK,CAAA,CAAA,CAAG,CAAA,CAAE,QAAA;AAAA,QAClD,GAAG;AAAA;AACL,KACF;AAAA,IAEA,MAAA,EAAQ;AAAA,MACN,IAAA;AAAA,MACA,UAAA,EAAY;AAAA,KACd;AAAA,IAEA,KAAA,EAAO;AAAA,MACL,MAAA,EAAQ,QAAA;AAAA,MACR,SAAA,EAAW;AAAA,KACb;AAAA,IAEA,MAAA,EAAQ;AAAA,MACN,iBAAA,EAAmB,IAAA,CAAK,SAAA,CAAU,SAAS,CAAA;AAAA,MAC3C,YAAA,EAAc,IAAA,CAAK,SAAA,CAAU,IAAI;AAAA;AACnC,GACF;AAEA,EAAA,OAAO,MAAA,GAAS,WAAA,CAAY,UAAA,EAAY,MAAM,CAAA,GAAI,UAAA;AACpD","file":"chunk-PRS2B2OG.js","sourcesContent":["/* ============================================================================\n @geenius/storybook — Vite Config Preset\n ============================================================================\n Factory function that produces a complete Vite config for any framework\n storybook app. Consumers call it from their own `vite.config.ts`.\n ============================================================================ */\n\nimport { mergeConfig, type UserConfig } from 'vite'\nimport type { StorybookViteOptions } from './types.js'\n\n/**\n * Create a Vite configuration for a storybook app.\n *\n * @example\n * ```ts\n * // apps/storybook-react/vite.config.ts\n * import { createStorybookViteConfig } from '@geenius/storybook/vite'\n * import react from '@vitejs/plugin-react'\n *\n * export default createStorybookViteConfig({\n * framework: 'react',\n * port: 3050,\n * plugins: [react()],\n * })\n * ```\n */\nexport function createStorybookViteConfig(options: StorybookViteOptions) {\n const {\n framework,\n port,\n plugins = [],\n aliases = {},\n tailwind,\n config,\n } = options\n\n // Build plugin list — framework plugins first, then optional tailwind\n const resolvedPlugins = [...plugins]\n\n if (tailwind !== false && tailwind != null) {\n resolvedPlugins.push(tailwind)\n }\n\n const baseConfig = {\n plugins: resolvedPlugins,\n\n resolve: {\n alias: {\n '@': new URL('./src', `file://${process.cwd()}/`).pathname,\n ...aliases,\n },\n },\n\n server: {\n port,\n strictPort: false,\n },\n\n build: {\n target: 'es2022',\n sourcemap: true,\n },\n\n define: {\n __GSB_FRAMEWORK__: JSON.stringify(framework),\n __GSB_PORT__: JSON.stringify(port),\n },\n } satisfies UserConfig\n\n return config ? mergeConfig(baseConfig, config) : baseConfig\n}\n\n// Re-export the options type for consumers\nexport type { StorybookViteOptions }\n"]}
@@ -0,0 +1,5 @@
1
+ export { ActiveView, DashboardStats, Framework, StorySurface, StorySurfaceEntry, StorySurfaceId, StorySurfaceSection, StorybookMeta, StorybookViteOptions, StyleVariant, ThemeMode } from './types.js';
2
+ export { DEFAULT_SHORTCUTS, KeyboardShortcuts, applyTheme, computeDashboardStats, getHashRoute, getStoredTheme, matchesShortcut, resolveTheme, setHashRoute, setStoredTheme } from './utils.js';
3
+ export { defineSurface, filterSurface, findSurfaceEntry, findSurfaceSection, getSurfaceComponentCount, getSurfaceEntries, getSurfaceIds, getSurfaceSnapshot, validateSurface } from './surface.js';
4
+ export { createStorybookViteConfig } from './vite.js';
5
+ import 'vite';
@@ -0,0 +1,18 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Geenius Storybook</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
+ <link
10
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&family=Inter:wght@300..700&display=swap"
11
+ rel="stylesheet"
12
+ />
13
+ </head>
14
+ <body>
15
+ <div id="root"></div>
16
+ <script type="module" src="/src/main.tsx"></script>
17
+ </body>
18
+ </html>
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { defineSurface, filterSurface, findSurfaceEntry, findSurfaceSection, getSurfaceComponentCount, getSurfaceEntries, getSurfaceIds, getSurfaceSnapshot, validateSurface } from './chunk-FB7KPF72.js';
2
+ export { DEFAULT_SHORTCUTS, applyTheme, computeDashboardStats, getHashRoute, getStoredTheme, matchesShortcut, resolveTheme, setHashRoute, setStoredTheme } from './chunk-IEHIPVSX.js';
3
+ export { createStorybookViteConfig } from './chunk-PRS2B2OG.js';
4
+ //# sourceMappingURL=index.js.map
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,58 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ComponentType, ReactNode } from 'react';
3
+
4
+ type StoryEntry<StoryId extends string> = {
5
+ id: StoryId;
6
+ label: string;
7
+ description?: string;
8
+ tags?: readonly string[];
9
+ };
10
+ type StorySection<StoryId extends string> = {
11
+ title: string;
12
+ stories: readonly StoryEntry<StoryId>[];
13
+ };
14
+ type StoryComponentMap<StoryId extends string> = Record<StoryId, ComponentType<Record<string, never>>>;
15
+ declare function StoryDeck(props: {
16
+ eyebrow?: string;
17
+ title: string;
18
+ summary: string;
19
+ children: ReactNode;
20
+ actions?: ReactNode;
21
+ }): react_jsx_runtime.JSX.Element;
22
+ declare function StoryGrid(props: {
23
+ columns?: 2 | 3;
24
+ children: ReactNode;
25
+ }): react_jsx_runtime.JSX.Element;
26
+ declare function StoryCard(props: {
27
+ title: string;
28
+ summary?: string;
29
+ tone?: 'default' | 'accent' | 'success' | 'warning' | 'danger';
30
+ children: ReactNode;
31
+ }): react_jsx_runtime.JSX.Element;
32
+ declare function StoryNote(props: {
33
+ tone?: 'info' | 'warning' | 'success' | 'danger';
34
+ children: ReactNode;
35
+ }): react_jsx_runtime.JSX.Element;
36
+ declare function StoryMetrics(props: {
37
+ items: ReadonlyArray<{
38
+ label: string;
39
+ value: string;
40
+ detail?: string;
41
+ }>;
42
+ }): react_jsx_runtime.JSX.Element;
43
+ declare function StoryComparison(props: {
44
+ primaryLabel: string;
45
+ secondaryLabel: string;
46
+ primary: ReactNode;
47
+ secondary: ReactNode;
48
+ }): react_jsx_runtime.JSX.Element;
49
+ declare function ReactStorybookApp<StoryId extends string>(props: {
50
+ packageName: string;
51
+ frameworkLabel: string;
52
+ overview: string;
53
+ sections: readonly StorySection<StoryId>[];
54
+ components: StoryComponentMap<StoryId>;
55
+ defaultStoryId: StoryId;
56
+ }): react_jsx_runtime.JSX.Element;
57
+
58
+ export { ReactStorybookApp, StoryCard, StoryComparison, type StoryComponentMap, StoryDeck, type StoryEntry, StoryGrid, StoryMetrics, StoryNote, type StorySection };