@auto-engineer/generate-react-client 1.28.0 → 1.29.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 CHANGED
@@ -1,5 +1,49 @@
1
1
  # @auto-engineer/generate-react-client
2
2
 
3
+ ## 1.29.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`2ef9632`](https://github.com/BeOnAuto/auto-engineer/commit/2ef9632a2db081f201981422f2ba26b026fa7277) Thanks [@SamHatoum](https://github.com/SamHatoum)! - ### react-component-implementer
8
+ - Add staging workflow with `writeStagedStory`, `checkStagedTypes`, and `promoteFromStaging`
9
+ - Add MCP connection pool and browser pool for resource reuse
10
+ - Add context filtering and system prompt caching to reduce tokens
11
+ - Add ts-service for type-aware error feedback with property suggestions
12
+ - Parallelize type checking and browser validation
13
+ - Replace score-based visual evaluation with binary ACK/NACK criteria
14
+ - Enable incremental TypeScript checking in staging
15
+
16
+ ### pipeline
17
+ - Add `.declare().accepts()` builder for AcceptsDescriptor type
18
+ - Show source commands in pipeline graph
19
+
20
+ ### job-graph-processor
21
+ - Add `hasFailedJobs` utility and discriminate GraphProcessed vs GraphProcessingFailed
22
+ - Bridge context.emit in command handler for downstream event routing
23
+
24
+ ### server-generator-apollo-emmett
25
+ - Add cleanServerDir to preserve node_modules during regeneration
26
+ - Accept Model directly instead of modelPath
27
+
28
+ ### server-generator-nestjs
29
+ - Accept Model directly instead of modelPath
30
+
31
+ ### generate-react-client
32
+ - Lock in overwrite behavior for copyStarter
33
+
34
+ ### narrative
35
+ - Remove export-schema command
36
+
37
+ ### global
38
+ - Remove references to information-architect and model-diff packages
39
+ - Fix biome lint errors for CI compliance
40
+ - Update examples and configuration files
41
+
42
+ ### Patch Changes
43
+
44
+ - Updated dependencies [[`2ef9632`](https://github.com/BeOnAuto/auto-engineer/commit/2ef9632a2db081f201981422f2ba26b026fa7277)]:
45
+ - @auto-engineer/message-bus@1.29.0
46
+
3
47
  ## 1.28.0
4
48
 
5
49
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"copy-starter.d.ts","sourceRoot":"","sources":["../../src/copy-starter.ts"],"names":[],"mappings":"AA8BA,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWxF"}
1
+ {"version":3,"file":"copy-starter.d.ts","sourceRoot":"","sources":["../../src/copy-starter.ts"],"names":[],"mappings":"AA8BA,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUxF"}
@@ -26,7 +26,6 @@ export async function copyStarter(starterDir, targetDir) {
26
26
  const src = path.isAbsolute(starterDir) ? starterDir : path.resolve(process.cwd(), starterDir);
27
27
  const dest = path.isAbsolute(targetDir) ? targetDir : path.resolve(process.cwd(), targetDir);
28
28
  debug('Copying starter from %s to %s', src, dest);
29
- await fs.rm(dest, { recursive: true, force: true });
30
29
  const fileCount = await copyDir(src, dest);
31
30
  debug('Copied %d files to %s', fileCount, dest);
32
31
  return fileCount;
@@ -1 +1 @@
1
- {"version":3,"file":"copy-starter.js","sourceRoot":"","sources":["../../src/copy-starter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,WAAW,MAAM,OAAO,CAAC;AAEhC,MAAM,KAAK,GAAG,WAAW,CAAC,yCAAyC,CAAC,CAAC;AAErE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,gBAAgB,EAAE,WAAW,CAAC,CAAC,CAAC;AAEtE,KAAK,UAAU,OAAO,CAAC,GAAW,EAAE,IAAY;IAC9C,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QAEnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE7C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,KAAK,IAAI,MAAM,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC5C,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACrC,KAAK,EAAE,CAAC;QACV,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAAkB,EAAE,SAAiB;IACrE,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;IAC/F,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;IAE7F,KAAK,CAAC,+BAA+B,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAElD,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAE3C,KAAK,CAAC,uBAAuB,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IAChD,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"copy-starter.js","sourceRoot":"","sources":["../../src/copy-starter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,WAAW,MAAM,OAAO,CAAC;AAEhC,MAAM,KAAK,GAAG,WAAW,CAAC,yCAAyC,CAAC,CAAC;AAErE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,gBAAgB,EAAE,WAAW,CAAC,CAAC,CAAC;AAEtE,KAAK,UAAU,OAAO,CAAC,GAAW,EAAE,IAAY;IAC9C,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QAEnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE7C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,KAAK,IAAI,MAAM,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC5C,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACrC,KAAK,EAAE,CAAC;QACV,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAAkB,EAAE,SAAiB;IACrE,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;IAC/F,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;IAE7F,KAAK,CAAC,+BAA+B,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAElD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAE3C,KAAK,CAAC,uBAAuB,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IAChD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=copy-starter.specs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-starter.specs.d.ts","sourceRoot":"","sources":["../../src/copy-starter.specs.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
+ import { copyStarter } from './copy-starter.js';
6
+ describe('copyStarter', () => {
7
+ let tmpDir;
8
+ let starterDir;
9
+ let targetDir;
10
+ beforeEach(async () => {
11
+ tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'copy-starter-'));
12
+ starterDir = path.join(tmpDir, 'starter');
13
+ targetDir = path.join(tmpDir, 'target');
14
+ await fs.mkdir(starterDir, { recursive: true });
15
+ await fs.mkdir(targetDir, { recursive: true });
16
+ });
17
+ afterEach(async () => {
18
+ await fs.rm(tmpDir, { recursive: true, force: true });
19
+ });
20
+ it('preserves pre-existing files in target', async () => {
21
+ await fs.writeFile(path.join(starterDir, 'a.txt'), 'from starter');
22
+ await fs.writeFile(path.join(targetDir, 'b.txt'), 'pre-existing');
23
+ await copyStarter(starterDir, targetDir);
24
+ const aContent = await fs.readFile(path.join(targetDir, 'a.txt'), 'utf-8');
25
+ const bContent = await fs.readFile(path.join(targetDir, 'b.txt'), 'utf-8');
26
+ expect(aContent).toBe('from starter');
27
+ expect(bContent).toBe('pre-existing');
28
+ });
29
+ it('overwrites files that exist in both source and target', async () => {
30
+ await fs.writeFile(path.join(starterDir, 'a.txt'), 'new content');
31
+ await fs.writeFile(path.join(targetDir, 'a.txt'), 'old content');
32
+ await copyStarter(starterDir, targetDir);
33
+ const content = await fs.readFile(path.join(targetDir, 'a.txt'), 'utf-8');
34
+ expect(content).toBe('new content');
35
+ });
36
+ });
37
+ //# sourceMappingURL=copy-starter.specs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-starter.specs.js","sourceRoot":"","sources":["../../src/copy-starter.specs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,MAAc,CAAC;IACnB,IAAI,UAAkB,CAAC;IACvB,IAAI,SAAiB,CAAC;IAEtB,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;QACnE,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC1C,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACxC,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC;QACnE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC;QAElE,MAAM,WAAW,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAEzC,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;QAC3E,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;QAE3E,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACtC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC;QAClE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC;QAEjE,MAAM,WAAW,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAEzC,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;QAC1E,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,8 +1,15 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
1
3
  import { fileURLToPath } from "node:url";
2
- import { dirname } from "node:path";
3
4
  import type { StorybookConfig } from "@storybook/react-vite";
4
5
 
5
- const isFastMode = !!process.env.STORYBOOK_FAST;
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const managerHeadContent = readFileSync(
8
+ join(__dirname, "manager-head.html"),
9
+ "utf-8",
10
+ );
11
+
12
+ const isFastMode = process.env.STORYBOOK_FAST === "1";
6
13
 
7
14
  const config: StorybookConfig = {
8
15
  stories: isFastMode
@@ -19,6 +26,7 @@ const config: StorybookConfig = {
19
26
  },
20
27
  },
21
28
  },
29
+ getAbsolutePath("@vueless/storybook-dark-mode"),
22
30
  ],
23
31
  staticDirs: ["../public"],
24
32
  features: {
@@ -39,8 +47,13 @@ const config: StorybookConfig = {
39
47
  ...config.server,
40
48
  allowedHosts: [".worker.on.auto", ".worker-dev.on.auto"],
41
49
  };
50
+ config.define = {
51
+ ...config.define,
52
+ "import.meta.env.STORYBOOK_FAST": JSON.stringify(isFastMode),
53
+ };
42
54
  return config;
43
55
  },
56
+ managerHead: () => managerHeadContent,
44
57
  };
45
58
 
46
59
  export default config;
@@ -0,0 +1,133 @@
1
+ <script>
2
+ (function() {
3
+ // Check localStorage for dark mode state before anything renders
4
+ var isDark = false;
5
+ try {
6
+ var stored = localStorage.getItem('sb-addon-themes-3');
7
+ if (stored) {
8
+ var parsed = JSON.parse(stored);
9
+ isDark = parsed && parsed.current === 'dark';
10
+ }
11
+ } catch(e) {}
12
+
13
+ // Also check URL parameter
14
+ var urlParams = new URLSearchParams(window.location.search);
15
+ var urlTheme = urlParams.get('theme');
16
+ if (urlTheme) {
17
+ isDark = urlTheme === 'dark';
18
+ }
19
+
20
+ // Apply matching theme background colors immediately (blue-tinted grays)
21
+ document.documentElement.style.backgroundColor = isDark ? '#1a1f2e' : '#f0f3f9';
22
+ document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
23
+
24
+ // Store theme state for CSS
25
+ document.documentElement.dataset.theme = isDark ? 'dark' : 'light';
26
+ })();
27
+ </script>
28
+ <style>
29
+ html, body {
30
+ transition: none !important;
31
+ }
32
+
33
+ /* Force sidebar and nav backgrounds - target the actual container divs */
34
+ #storybook-explorer-tree,
35
+ [class*="sidebar"],
36
+ [class*="Sidebar"],
37
+ nav[class],
38
+ aside[class] {
39
+ background-color: #f0f3f9 !important;
40
+ }
41
+
42
+ html[data-theme="dark"] #storybook-explorer-tree,
43
+ html[data-theme="dark"] [class*="sidebar"],
44
+ html[data-theme="dark"] [class*="Sidebar"],
45
+ html[data-theme="dark"] nav[class],
46
+ html[data-theme="dark"] aside[class] {
47
+ background-color: #1a1f2e !important;
48
+ }
49
+
50
+ /* Force toolbar/header backgrounds */
51
+ [class*="toolbar"],
52
+ [class*="Toolbar"],
53
+ [class*="bar"],
54
+ [class*="Bar"],
55
+ header[class] {
56
+ background-color: #f0f3f9 !important;
57
+ }
58
+
59
+ html[data-theme="dark"] [class*="toolbar"],
60
+ html[data-theme="dark"] [class*="Toolbar"],
61
+ html[data-theme="dark"] [class*="bar"],
62
+ html[data-theme="dark"] [class*="Bar"],
63
+ html[data-theme="dark"] header[class] {
64
+ background-color: #1a1f2e !important;
65
+ }
66
+
67
+ /* Force ALL sidebar text to dark gray instead of blue */
68
+ [data-nodetype],
69
+ [data-nodetype] * {
70
+ color: #2d3748 !important;
71
+ }
72
+
73
+ html[data-theme="dark"] [data-nodetype],
74
+ html[data-theme="dark"] [data-nodetype] * {
75
+ color: #e2e8f0 !important;
76
+ }
77
+
78
+ /* Sidebar selection styles - subtle background */
79
+ [data-nodetype="story"][data-selected="true"],
80
+ [data-nodetype="document"][data-selected="true"],
81
+ [data-nodetype="component"][data-selected="true"],
82
+ [data-nodetype="group"][data-selected="true"] {
83
+ background-color: #e2e8f0 !important;
84
+ border-radius: 0.5rem !important;
85
+ }
86
+
87
+ [data-nodetype="story"][data-selected="true"] *,
88
+ [data-nodetype="document"][data-selected="true"] *,
89
+ [data-nodetype="component"][data-selected="true"] *,
90
+ [data-nodetype="group"][data-selected="true"] * {
91
+ color: #1a202c !important;
92
+ }
93
+
94
+ /* Dark mode selected item */
95
+ html[data-theme="dark"] [data-nodetype="story"][data-selected="true"],
96
+ html[data-theme="dark"] [data-nodetype="document"][data-selected="true"],
97
+ html[data-theme="dark"] [data-nodetype="component"][data-selected="true"],
98
+ html[data-theme="dark"] [data-nodetype="group"][data-selected="true"] {
99
+ background-color: #3d4663 !important;
100
+ }
101
+
102
+ html[data-theme="dark"] [data-nodetype="story"][data-selected="true"] *,
103
+ html[data-theme="dark"] [data-nodetype="document"][data-selected="true"] *,
104
+ html[data-theme="dark"] [data-nodetype="component"][data-selected="true"] *,
105
+ html[data-theme="dark"] [data-nodetype="group"][data-selected="true"] * {
106
+ color: #f7fafc !important;
107
+ }
108
+
109
+ /* Hover state */
110
+ [data-nodetype="story"]:hover,
111
+ [data-nodetype="document"]:hover,
112
+ [data-nodetype="component"]:hover,
113
+ [data-nodetype="group"]:hover {
114
+ background-color: #edf2f7 !important;
115
+ border-radius: 0.5rem !important;
116
+ }
117
+
118
+ html[data-theme="dark"] [data-nodetype="story"]:hover,
119
+ html[data-theme="dark"] [data-nodetype="document"]:hover,
120
+ html[data-theme="dark"] [data-nodetype="component"]:hover,
121
+ html[data-theme="dark"] [data-nodetype="group"]:hover {
122
+ background-color: #2d3748 !important;
123
+ }
124
+
125
+ /* Selected hover - keep selected state */
126
+ [data-nodetype][data-selected="true"]:hover {
127
+ background-color: #e2e8f0 !important;
128
+ }
129
+
130
+ html[data-theme="dark"] [data-nodetype][data-selected="true"]:hover {
131
+ background-color: #3d4663 !important;
132
+ }
133
+ </style>
@@ -0,0 +1,165 @@
1
+ import { addons } from 'storybook/manager-api';
2
+ import { create } from 'storybook/theming';
3
+ import { STORY_CHANGED } from 'storybook/internal/core-events';
4
+ import { UPDATE_DARK_MODE_EVENT_NAME } from '@vueless/storybook-dark-mode';
5
+
6
+ // Get initial theme from localStorage (dark mode addon) or URL parameter
7
+ const urlParams = new URLSearchParams(window.location.search);
8
+ const urlTheme = urlParams.get('theme');
9
+ const getStoredDarkMode = (): boolean => {
10
+ try {
11
+ const stored = localStorage.getItem('sb-addon-themes-3');
12
+ if (stored) {
13
+ const parsed = JSON.parse(stored);
14
+ return parsed?.current === 'dark';
15
+ }
16
+ } catch {}
17
+ return false;
18
+ };
19
+ const initialIsDark = urlTheme ? urlTheme === 'dark' : getStoredDarkMode();
20
+
21
+ // Theme colors matching app's design with subtle blue tint
22
+ // Analyzed from screenshots - app has slight blue undertone in grays
23
+
24
+ const lightTheme = create({
25
+ base: 'light',
26
+ brandTitle: ' ',
27
+ brandUrl: '/',
28
+ brandImage: '/blank.svg',
29
+
30
+ // App background colors - matching your app's blue-tinted sidebar
31
+ appBg: '#f0f3f9', // sidebar background (blue-tinted like your app)
32
+ appContentBg: '#ffffff', // main content area
33
+ appPreviewBg: '#ffffff', // preview/canvas background
34
+ appBorderColor: '#e2e8f0', // borders (blue-tinted)
35
+ appBorderRadius: 10, // 0.625rem = 10px
36
+
37
+ // Text colors - dark gray, not blue
38
+ textColor: '#2d3748', // dark gray text for sidebar items
39
+ textMutedColor: '#718096', // muted gray text
40
+ textInverseColor: '#f7fafc',// inverse text
41
+
42
+ // Toolbar/UI colors
43
+ barBg: '#f0f3f9', // toolbar background (blue-tinted)
44
+ barTextColor: '#2d3748', // toolbar text (dark gray)
45
+ barHoverColor: '#1a202c', // toolbar hover
46
+ barSelectedColor: '#1a202c',// toolbar selected
47
+
48
+ // Form colors
49
+ inputBg: '#ffffff', // input background
50
+ inputBorder: '#e2e8f0', // input border
51
+ inputTextColor: '#2d3748', // input text
52
+
53
+ // Button colors
54
+ buttonBg: '#edf2f7', // button background
55
+ buttonBorder: '#e2e8f0', // button border
56
+
57
+ // Boolean inputs
58
+ booleanBg: '#edf2f7',
59
+ booleanSelectedBg: '#2d3748',
60
+
61
+ // Grid colors for canvas
62
+ gridCellSize: 10,
63
+
64
+ // Color for links/interactive items - override the blue
65
+ colorPrimary: '#2d3748',
66
+ colorSecondary: '#4a5568',
67
+ });
68
+
69
+ const darkTheme = create({
70
+ base: 'dark',
71
+ brandTitle: ' ',
72
+ brandUrl: '/',
73
+ brandImage: '/blank.svg',
74
+
75
+ // App background colors - matching app's dark blue-tinted theme
76
+ appBg: '#1a1f2e', // sidebar background (blue-tinted dark)
77
+ appContentBg: '#131620', // main content area (darker)
78
+ appPreviewBg: '#131620', // preview/canvas background
79
+ appBorderColor: '#2a3142', // borders (blue-tinted)
80
+ appBorderRadius: 10,
81
+
82
+ // Text colors - light gray, not blue
83
+ textColor: '#e2e8f0', // light gray text
84
+ textMutedColor: '#a0aec0', // muted gray text
85
+ textInverseColor: '#1a1f2e',// inverse text
86
+
87
+ // Toolbar/UI colors
88
+ barBg: '#1a1f2e', // toolbar background
89
+ barTextColor: '#e2e8f0', // toolbar text (light gray)
90
+ barHoverColor: '#f7fafc', // toolbar hover
91
+ barSelectedColor: '#f7fafc',// toolbar selected
92
+
93
+ // Form colors
94
+ inputBg: '#242938', // input background
95
+ inputBorder: '#3d4663', // input border
96
+ inputTextColor: '#e2e8f0', // input text
97
+
98
+ // Button colors
99
+ buttonBg: '#242938', // button background
100
+ buttonBorder: '#3d4663', // button border
101
+
102
+ // Boolean inputs
103
+ booleanBg: '#242938',
104
+ booleanSelectedBg: '#e2e8f0',
105
+
106
+ // Grid colors for canvas
107
+ gridCellSize: 10,
108
+
109
+ // Color for links/interactive items - override the blue
110
+ colorPrimary: '#e2e8f0',
111
+ colorSecondary: '#a0aec0',
112
+ });
113
+
114
+ const customTheme = initialIsDark ? darkTheme : lightTheme;
115
+
116
+ // Hide the addon panel globally (Storybook 8+/10 API)
117
+ addons.setConfig({
118
+ bottomPanelHeight: 0,
119
+ layoutCustomisations: {
120
+ showPanel: () => false,
121
+ },
122
+ theme: customTheme,
123
+ });
124
+
125
+ // Set default story to Design System Overview if no path specified
126
+ if (!urlParams.has('path')) {
127
+ const newUrl = new URL(window.location.href);
128
+ newUrl.searchParams.set('path', '/story/design-system-overview--default');
129
+ window.history.replaceState({}, '', newUrl.toString());
130
+ }
131
+
132
+ // Register addon to handle navigation sync and theme after Storybook is ready
133
+ addons.register('url-sync', (api) => {
134
+ const channel = api.getChannel();
135
+ if (!channel) return;
136
+
137
+ // Set initial dark mode state (pass string "dark" or "light", not boolean)
138
+ if (initialIsDark) {
139
+ channel.emit(UPDATE_DARK_MODE_EVENT_NAME, 'dark');
140
+ }
141
+
142
+ // Report navigation changes to parent window for URL bar sync
143
+ channel.on(STORY_CHANGED, (storyId: string) => {
144
+ if (window.parent !== window) {
145
+ window.parent.postMessage({ type: 'storybook-navigation', storyId }, '*');
146
+ }
147
+ });
148
+
149
+ // Listen for theme changes from parent window
150
+ window.addEventListener('message', (event) => {
151
+ if (event.data?.type === 'storybook-theme-change' && event.data?.theme) {
152
+ const isDark = event.data.theme === 'dark';
153
+ // Update the dark mode addon
154
+ channel.emit(UPDATE_DARK_MODE_EVENT_NAME, event.data.theme);
155
+ // Update the Storybook manager theme
156
+ addons.setConfig({
157
+ theme: isDark ? darkTheme : lightTheme,
158
+ });
159
+ // Update CSS theme selector and background
160
+ document.documentElement.dataset.theme = isDark ? 'dark' : 'light';
161
+ document.documentElement.style.backgroundColor = isDark ? '#1a1f2e' : '#f0f3f9';
162
+ document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
163
+ }
164
+ });
165
+ });
@@ -0,0 +1,31 @@
1
+ <script>
2
+ (function() {
3
+ var isDark = false;
4
+ var stored, parsed, urlParams, urlTheme;
5
+ try {
6
+ stored = localStorage.getItem('sb-addon-themes-3');
7
+ if (stored) {
8
+ parsed = JSON.parse(stored);
9
+ isDark = parsed && parsed.current === 'dark';
10
+ }
11
+ } catch(_) {}
12
+
13
+ urlParams = new URLSearchParams(window.location.search);
14
+ urlTheme = urlParams.get('theme');
15
+ if (urlTheme) {
16
+ isDark = urlTheme === 'dark';
17
+ }
18
+
19
+ if (isDark) {
20
+ document.documentElement.classList.add('dark');
21
+ }
22
+ // Dark: appContentBg=#222325, Light: #FFFFFF
23
+ document.documentElement.style.backgroundColor = isDark ? '#222325' : '#FFFFFF';
24
+ document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
25
+ })();
26
+ </script>
27
+ <style>
28
+ html, body {
29
+ transition: none;
30
+ }
31
+ </style>
@@ -1,9 +1,51 @@
1
1
  import type { Preview } from '@storybook/react-vite';
2
+ import type { PropsWithChildren } from 'react';
3
+ import { useEffect, useState } from 'react';
4
+ import { addons } from 'storybook/preview-api';
5
+ import { themes } from 'storybook/theming';
6
+ import { DocsContainer } from '@storybook/addon-docs/blocks';
2
7
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
8
+ import { DARK_MODE_EVENT_NAME } from '@vueless/storybook-dark-mode';
3
9
  import { initialize, mswLoader } from 'msw-storybook-addon';
4
10
  import '../src/index.css';
5
11
 
6
- initialize();
12
+ initialize({
13
+ onUnhandledRequest: 'bypass',
14
+ });
15
+
16
+ // Apply theme to the preview iframe (for component styling and background)
17
+ const applyTheme = (isDark: boolean) => {
18
+ if (isDark) {
19
+ document.documentElement.classList.add('dark');
20
+ } else {
21
+ document.documentElement.classList.remove('dark');
22
+ }
23
+ // Update background color to match Storybook theme
24
+ document.documentElement.style.backgroundColor = isDark ? '#222325' : '#FFFFFF';
25
+ document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
26
+ };
27
+
28
+ // Get initial theme from URL parameter or localStorage
29
+ const urlParams = new URLSearchParams(window.location.search);
30
+ const urlTheme = urlParams.get('theme');
31
+ const getStoredDarkMode = (): boolean => {
32
+ try {
33
+ const stored = localStorage.getItem('sb-addon-themes-3');
34
+ if (stored) {
35
+ const parsed = JSON.parse(stored);
36
+ return parsed?.current === 'dark';
37
+ }
38
+ } catch {}
39
+ return false;
40
+ };
41
+ const initialIsDark = urlTheme ? urlTheme === 'dark' : getStoredDarkMode();
42
+ applyTheme(initialIsDark);
43
+
44
+ // Listen to dark mode addon changes and sync with our CSS class
45
+ const channel = addons.getChannel();
46
+ channel.on(DARK_MODE_EVENT_NAME, (isDark: boolean) => {
47
+ applyTheme(isDark);
48
+ });
7
49
 
8
50
  const queryClient = new QueryClient({
9
51
  defaultOptions: {
@@ -13,7 +55,28 @@ const queryClient = new QueryClient({
13
55
  },
14
56
  });
15
57
 
58
+ // Themed docs container that responds to dark mode
59
+ function ThemedDocsContainer({
60
+ children,
61
+ context,
62
+ }: PropsWithChildren<{ context: Parameters<typeof DocsContainer>[0]['context'] }>) {
63
+ const [isDark, setIsDark] = useState(initialIsDark);
64
+
65
+ useEffect(() => {
66
+ const ch = addons.getChannel();
67
+ ch.on(DARK_MODE_EVENT_NAME, setIsDark);
68
+ return () => ch.off(DARK_MODE_EVENT_NAME, setIsDark);
69
+ }, []);
70
+
71
+ return (
72
+ <DocsContainer context={context} theme={isDark ? themes.dark : themes.light}>
73
+ {children}
74
+ </DocsContainer>
75
+ );
76
+ }
77
+
16
78
  const preview: Preview = {
79
+ tags: ['autodocs'],
17
80
  decorators: [
18
81
  (Story) => (
19
82
  <QueryClientProvider client={queryClient}>
@@ -33,10 +96,20 @@ const preview: Preview = {
33
96
  },
34
97
  layout: 'padded',
35
98
  options: {
99
+ showPanel: false,
36
100
  storySort: {
37
- order: ['Design System', 'UI Components'],
101
+ order: ['Design System', ['Overview', '*'], 'UI Components'],
38
102
  },
39
103
  },
104
+ darkMode: {
105
+ current: initialIsDark ? 'dark' : 'light',
106
+ stylePreview: true,
107
+ dark: themes.dark,
108
+ light: themes.light,
109
+ },
110
+ docs: {
111
+ container: ThemedDocsContainer,
112
+ },
40
113
  },
41
114
  initialGlobals: {
42
115
  showPanel: false,
@@ -9,6 +9,7 @@
9
9
  "preview": "vite preview",
10
10
  "type-check": "tsc --noEmit -p tsconfig.app.json",
11
11
  "storybook": "storybook dev -p 6006",
12
+ "storybook:fast": "STORYBOOK_FAST=1 storybook dev -p 6006",
12
13
  "build-storybook": "storybook build"
13
14
  },
14
15
  "dependencies": {
@@ -20,6 +21,7 @@
20
21
  "cmdk": "^1.1.1",
21
22
  "date-fns": "^4.1.0",
22
23
  "embla-carousel-react": "^8.6.0",
24
+ "graphql-request": "^7.4.0",
23
25
  "input-otp": "^1.4.2",
24
26
  "lucide-react": "^0.539.0",
25
27
  "next-themes": "^0.4.6",
@@ -44,6 +46,7 @@
44
46
  "@types/react": "^19.2.7",
45
47
  "@types/react-dom": "^19.2.3",
46
48
  "@vitejs/plugin-react-swc": "^4.2.2",
49
+ "@vueless/storybook-dark-mode": "^10.0.7",
47
50
  "msw": "^2.12.10",
48
51
  "msw-storybook-addon": "^2.0.6",
49
52
  "storybook": "^10.2.8",