@tpitre/story-ui 4.16.10 → 4.16.12

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/README.md CHANGED
@@ -1,19 +1,20 @@
1
1
  # Story UI
2
2
 
3
- *AI-powered Storybook story generator for any JavaScript framework*
3
+ *AI-powered Storybook story generator for any JavaScript framework and design system*
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/%40tpitre%2Fstory-ui.svg)](https://badge.fury.io/js/%40tpitre%2Fstory-ui)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- Story UI revolutionizes component documentation by automatically generating Storybook stories through AI-powered conversations. Works with **any framework** (React, Vue, Angular, Svelte, Web Components) and **any LLM provider** (Claude, OpenAI, Gemini).
8
+ Story UI generates working Storybook stories from natural language. Describe what you want, and the AI writes production-ready story code using your design system's actual components. Works with **any framework** and **any component library**.
9
9
 
10
10
  ## Why Story UI?
11
11
 
12
- - **Framework Agnostic**: Works with React, Vue, Angular, Svelte, and Web Components
13
- - **Multi-Provider AI**: Choose between Claude, OpenAI, or Google Gemini - always using the latest models
14
- - **Design System Aware**: Learns your component library and generates appropriate code
15
- - **Production Ready**: Deploy as a standalone web app with full MCP integration
16
- - **Zero Lock-in**: Use any component library - Mantine, Vuetify, Angular Material, Shoelace, or your own
12
+ - **Design-System Agnostic**: React, Vue, Angular, Svelte, Web Components — bring your own component library
13
+ - **Multi-Provider AI**: Claude, OpenAI, or Gemini with automatic model selection
14
+ - **Self-Healing Generation**: Validates generated code with TypeScript AST parsing and auto-corrects errors through an LLM retry loop
15
+ - **Voice Canvas**: Speak your component ideas and see them rendered live in Storybook
16
+ - **Storybook MCP Integration**: Fetches component docs and story patterns from Storybook's MCP addon for higher-quality output
17
+ - **Zero Lock-in**: Use Mantine, Vuetify, Angular Material, Shoelace, shadcn/ui, or your own components
17
18
 
18
19
  ---
19
20
 
@@ -40,24 +41,35 @@ Story UI will guide you through:
40
41
 
41
42
  ## Features
42
43
 
43
- ### Core Capabilities
44
- - **AI-Powered Story Generation**: Describe what you want in natural language
45
- - **Intelligent Iteration**: Modify existing stories without losing your work
46
- - **Real-time Preview**: See generated stories instantly in Storybook
47
- - **TypeScript Support**: Full type-aware story generation
48
- - **Vision Support**: Attach screenshots for visual component requests
44
+ ### AI-Powered Story Generation
45
+ Describe what you want in natural language. Story UI generates complete, working Storybook stories using your design system's components with proper imports, props, and TypeScript types.
46
+
47
+ ### Self-Healing Code Generation
48
+ When generated code has syntax errors, invalid imports, or forbidden patterns, Story UI automatically:
49
+ 1. Validates with TypeScript AST parsing and pattern checking
50
+ 2. Sends errors back to the LLM with correction context
51
+ 3. Retries up to 3 times, tracking error history to detect stuck loops
52
+ 4. Selects the best attempt if all retries fail
53
+
54
+ ### Voice Canvas
55
+ A live playground mode where you speak component ideas and see them rendered instantly in Storybook. Uses browser speech recognition with auto-submit, pauses during generation, and renders output through an iframe with `react-live`.
56
+
57
+ ### Intelligent Iteration
58
+ Continue the conversation to refine generated stories. Story UI preserves context and modifies only what you request.
59
+
60
+ ### Vision Support
61
+ Attach screenshots or mockups to your prompt. The AI uses them as reference when generating components.
49
62
 
50
63
  ### Story Management
51
- - **Edit Existing Stories**: Modify any generated story through conversation
52
- - **Delete Stories**: Remove stories directly from the Story UI panel
53
- - **Orphan Detection**: Find and clean up stories without associated chat history
54
- - **Full MCP Integration**: Manage stories via Claude Desktop or any MCP-compatible client
64
+ - Edit, delete, and organize generated stories from the panel
65
+ - Orphan detection for stories without chat history
66
+ - File-based manifest system for tracking generated assets
55
67
 
56
68
  ### Multi-Framework Support
57
69
 
58
70
  | Framework | Design Systems | Status |
59
71
  |-----------|---------------|--------|
60
- | React | Mantine, Chakra UI, Material UI, Custom | Fully Supported |
72
+ | React | Mantine, Chakra UI, Material UI, shadcn/ui, Custom | Fully Supported |
61
73
  | Vue | Vuetify, Custom | Fully Supported |
62
74
  | Angular | Angular Material, Custom | Fully Supported |
63
75
  | Svelte | Flowbite-Svelte, Custom | Fully Supported |
@@ -65,342 +77,124 @@ Story UI will guide you through:
65
77
 
66
78
  ### Multi-Provider LLM Support
67
79
 
68
- | Provider | Models | Best For |
69
- |----------|--------|----------|
70
- | **Claude** (Anthropic) | claude-opus-4-5-20251101, claude-sonnet-4-5-20250929, claude-haiku-4-5-20251001, claude-sonnet-4-20250514 | Complex reasoning, code quality |
71
- | **GPT** (OpenAI) | gpt-5.2, gpt-5.1, gpt-4o, gpt-4o-mini | Versatility, latest capabilities |
72
- | **Gemini** (Google) | gemini-3-pro-preview, gemini-2.5-pro, gemini-2.5-flash, gemini-2.0-flash | Advanced reasoning, fast generation |
73
-
74
- ### Production Deployment
75
- - **Railway**: Node.js backend with file-based story persistence
76
- - **MCP Integration**: Connect AI clients directly to production
80
+ | Provider | Models | Default |
81
+ |----------|--------|---------|
82
+ | **Claude** (Anthropic) | Claude Opus 4.6, Claude Sonnet 4.6, Claude Haiku 4.5 | `claude-sonnet-4-6` |
83
+ | **GPT** (OpenAI) | GPT-5.4, GPT-5.4 Mini, o4 Mini | `gpt-5.4` |
84
+ | **Gemini** (Google) | Gemini 3.1 Pro Preview, Gemini 3 Flash Preview, Gemini 2.5 Flash | `gemini-3.1-pro-preview` |
77
85
 
78
86
  ---
79
87
 
80
- ## Installation Options
88
+ ## Installation
81
89
 
82
- ### Option 1: Interactive Setup (Recommended)
90
+ ### Interactive Setup (Recommended)
83
91
 
84
92
  ```bash
85
93
  npx story-ui init
86
94
  ```
87
95
 
88
- The interactive installer will ask:
89
-
90
- 1. **Framework Selection**
91
- ```
92
- ? Which JavaScript framework are you using?
93
- > React
94
- Vue
95
- Angular
96
- Svelte
97
- Web Components
98
- ```
99
-
100
- 2. **Design System Selection** (varies by framework)
101
- ```
102
- # For React:
103
- ? Choose a design system:
104
- > Mantine - Most Popular
105
- Chakra UI
106
- Material UI
107
- Custom
108
-
109
- # For Vue:
110
- ? Choose a design system:
111
- > Vuetify - Most Popular
112
- Custom
113
-
114
- # For Angular:
115
- ? Choose a design system:
116
- > Angular Material - Most Popular
117
- Custom
118
- ```
119
-
120
- 3. **AI Provider Selection**
121
- ```
122
- ? Which AI provider do you prefer?
123
- > Claude (Anthropic) - Recommended
124
- OpenAI
125
- Google Gemini
126
-
127
- ? Enter your API key:
128
- ```
129
-
130
- ### Option 2: Manual Configuration
96
+ The installer prompts for:
97
+ 1. **Framework** — React, Vue, Angular, Svelte, or Web Components
98
+ 2. **Design System** — framework-specific options (Mantine, Vuetify, Angular Material, etc.)
99
+ 3. **AI Provider** — Claude (recommended), OpenAI, or Gemini
100
+ 4. **Configuration** paths, ports, and API keys
101
+
102
+ ### Manual Configuration
131
103
 
132
104
  Create `story-ui.config.js` in your project root:
133
105
 
134
106
  ```javascript
135
- export default {
107
+ module.exports = {
136
108
  // Framework: 'react' | 'vue' | 'angular' | 'svelte' | 'web-components'
137
- framework: 'react',
109
+ componentFramework: 'react',
138
110
 
139
111
  // Component library import path
140
112
  importPath: '@mantine/core',
141
113
 
142
- // Path to custom components
143
- componentsPath: './src/components',
144
-
145
114
  // Generated stories location
146
115
  generatedStoriesPath: './src/stories/generated/',
147
116
 
148
- // LLM provider configuration
149
- llmProvider: 'claude', // 'claude' | 'openai' | 'gemini'
117
+ // LLM provider: 'claude' | 'openai' | 'gemini'
118
+ llmProvider: 'claude',
119
+
120
+ // Import style: 'barrel' (default) or 'individual'
121
+ // Use 'individual' for libraries without barrel exports (shadcn/ui, Radix Vue, Angular Material)
122
+ // Use 'barrel' for libraries with index.ts barrel exports (Mantine, Chakra, Vuetify)
123
+ importStyle: 'barrel',
150
124
 
151
125
  // Story configuration
152
126
  storyPrefix: 'Generated/',
153
- defaultAuthor: 'Story UI AI'
127
+ defaultAuthor: 'Story UI AI',
128
+
129
+ // Layout rules for multi-component compositions
130
+ layoutRules: {
131
+ multiColumnWrapper: 'SimpleGrid',
132
+ columnComponent: 'div',
133
+ containerComponent: 'Container',
134
+ },
154
135
  };
155
136
  ```
156
137
 
157
- ### Advanced Configuration: Import Examples (Web Components / Custom Libraries)
138
+ ### Import Examples (Web Components / Custom Libraries)
158
139
 
159
- For component libraries with non-standard import paths (especially Web Components with local imports), use `importExamples` to teach the AI your import patterns:
140
+ For component libraries with non-standard import paths, use `importExamples` to teach the AI your patterns:
160
141
 
161
142
  ```javascript
162
- export default {
143
+ module.exports = {
163
144
  framework: 'web-components',
164
145
  importPath: '../../../components',
165
146
 
166
- // Teach the AI your library's import structure
167
147
  importExamples: [
168
148
  "import '../../../components/button/button'; // For <my-button>",
169
149
  "import '../../../components/card/card'; // For <my-card>",
170
- "import '../../../components/icon/icons/check'; // For <my-icon-check>",
171
150
  ],
172
-
173
- // ... other config
174
151
  };
175
152
  ```
176
153
 
177
- The AI uses these examples to understand your component library's folder structure and generate correct imports.
178
-
179
154
  ---
180
155
 
181
156
  ## Usage
182
157
 
183
- ### Basic Story Generation
158
+ ### Generating Stories
184
159
 
185
160
  Start the Story UI server:
186
161
  ```bash
187
162
  npm run story-ui
188
163
  ```
189
164
 
190
- Then describe what you want:
191
- ```
192
- You: "Create a product card with image, title, price, and add to cart button"
193
- ```
194
-
195
- Story UI generates:
196
- ```tsx
197
- export const ProductCard = {
198
- render: () => (
199
- <Card shadow="sm" padding="lg" radius="md" withBorder>
200
- <Card.Section>
201
- <Image src="https://example.com/product.jpg" height={160} alt="Product" />
202
- </Card.Section>
203
- <Group justify="space-between" mt="md" mb="xs">
204
- <Text fw={500}>Product Name</Text>
205
- <Badge color="pink">On Sale</Badge>
206
- </Group>
207
- <Text size="sm" c="dimmed">$29.99</Text>
208
- <Button color="blue" fullWidth mt="md" radius="md">
209
- Add to Cart
210
- </Button>
211
- </Card>
212
- )
213
- };
214
- ```
215
-
216
- ### Iterating on Stories
165
+ Open Storybook and navigate to the Story UI panel. Describe what you want:
217
166
 
218
- Continue the conversation to refine:
219
- ```
220
- You: "Make the button green and add a quantity selector"
221
- ```
222
-
223
- Story UI modifies only what you requested, preserving the rest.
224
-
225
- ### Using with Different Frameworks
226
-
227
- **Vue Example:**
228
- ```
229
- You: "Create a user profile card with avatar, name, and edit button"
230
- ```
231
-
232
- Generates Vue template:
233
- ```vue
234
- <template>
235
- <v-card class="mx-auto" max-width="400">
236
- <v-card-item>
237
- <v-avatar size="80">
238
- <v-img src="https://example.com/avatar.jpg" alt="User" />
239
- </v-avatar>
240
- <v-card-title>John Doe</v-card-title>
241
- <v-card-subtitle>Software Engineer</v-card-subtitle>
242
- </v-card-item>
243
- <v-card-actions>
244
- <v-btn color="primary" variant="outlined">Edit Profile</v-btn>
245
- </v-card-actions>
246
- </v-card>
247
- </template>
248
- ```
249
-
250
- **Angular Example:**
251
167
  ```
252
- You: "Create a data table with sorting and pagination"
253
- ```
254
-
255
- Generates Angular component:
256
- ```typescript
257
- @Component({
258
- selector: 'app-data-table',
259
- template: `
260
- <mat-table [dataSource]="dataSource" matSort>
261
- <ng-container matColumnDef="name">
262
- <mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell>
263
- <mat-cell *matCellDef="let element">{{element.name}}</mat-cell>
264
- </ng-container>
265
- <!-- Additional columns -->
266
- </mat-table>
267
- <mat-paginator [pageSizeOptions]="[5, 10, 25]" showFirstLastButtons />
268
- `
269
- })
270
- export class DataTableComponent { }
271
- ```
272
-
273
- ---
274
-
275
- ## MCP Server Integration
276
-
277
- Story UI includes a Model Context Protocol (MCP) server, allowing direct integration with AI clients like Claude Desktop and Claude Code.
278
-
279
- ### Claude Desktop Integration (Recommended)
280
-
281
- The easiest way to connect is via Claude Desktop's built-in connector UI:
282
-
283
- 1. Open **Claude Desktop**
284
- 2. Go to **Settings** → **Connectors**
285
- 3. Click **"Add custom connector"**
286
- 4. Enter:
287
- - **Name**: `Story UI React` (or any descriptive name)
288
- - **URL**: Your deployed Railway URL + `/mcp-remote/mcp`
289
- - Example: `https://your-app-name.up.railway.app/mcp-remote/mcp`
290
- 5. Click **Add**
291
- 6. **Restart Claude Desktop**
292
-
293
- > **Note**: The URL will be your own Railway deployment URL. See [Production Deployment](#production-deployment) to set up your instance.
294
-
295
- **Multiple Projects**: If you have multiple Storybook projects, add a separate connector for each:
296
- - `Story UI React` → `https://my-react-app.up.railway.app/mcp-remote/mcp`
297
- - `Story UI Vue` → `https://my-vue-app.up.railway.app/mcp-remote/mcp`
298
-
299
- Once connected, you'll have access to all Story UI tools directly in your Claude conversations:
300
- - `generate-story` - Generate Storybook stories from natural language
301
- - `list-components` - Discover available components
302
- - `list-stories` - View existing stories
303
- - `get-story` / `update-story` / `delete-story` - Manage stories
304
- - `get-component-props` - Get component property information
305
- - `test-connection` - Verify MCP connection
306
-
307
- ### Claude Code Integration
308
-
309
- Connect via Claude Code's built-in MCP support:
310
-
311
- ```bash
312
- # Add your production Railway deployment
313
- claude mcp add --transport http story-ui-react https://your-react-app.up.railway.app/mcp-remote/mcp
314
-
315
- # Add another project (if needed)
316
- claude mcp add --transport http story-ui-vue https://your-vue-app.up.railway.app/mcp-remote/mcp
317
-
318
- # For local development (default port is 4001)
319
- claude mcp add --transport http story-ui-local http://localhost:4001/mcp-remote/mcp
168
+ Create a product card with image, title, price, and add to cart button
320
169
  ```
321
170
 
322
- ### Manual Configuration (Advanced)
171
+ Story UI generates a complete `.stories.tsx` file using your design system's components, writes it to your generated stories directory, and Storybook picks it up automatically via file watcher.
323
172
 
324
- For running multiple local Story UI instances with different ports, configure your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
173
+ ### Iterating
325
174
 
326
- ```json
327
- {
328
- "mcpServers": {
329
- "story-ui-react": {
330
- "command": "npx",
331
- "args": ["@tpitre/story-ui", "start", "--port", "4001"]
332
- },
333
- "story-ui-vue": {
334
- "command": "npx",
335
- "args": ["@tpitre/story-ui", "start", "--port", "4002"]
336
- },
337
- "story-ui-angular": {
338
- "command": "npx",
339
- "args": ["@tpitre/story-ui", "start", "--port", "4003"]
340
- }
341
- }
342
- }
175
+ Continue the conversation to refine:
343
176
  ```
344
-
345
- > **Note**: When using Claude Desktop, API keys are managed through your Anthropic account - no need to configure them in the MCP server.
346
-
347
- ### Starting the Local MCP Server
348
-
349
- ```bash
350
- # Start with default port (4001)
351
- npx story-ui start
352
-
353
- # Or specify a custom port
354
- npx story-ui start --port 4002
177
+ Make the button green and add a quantity selector
355
178
  ```
356
179
 
357
- This starts the Story UI HTTP server with MCP endpoint at `http://localhost:<port>/mcp-remote/mcp`.
180
+ Story UI modifies only what you requested, preserving the rest of the story.
358
181
 
359
- ### Available MCP Commands
182
+ ### Voice Canvas
360
183
 
361
- Once connected, you can use these commands in Claude Desktop:
362
- - "Use Story UI to create a hero section with a CTA button"
363
- - "List all available components in Story UI"
364
- - "Generate a dashboard layout with sidebar navigation"
365
- - "Show me the stories I've generated"
184
+ Switch to Voice Canvas mode in the panel header. Speak your component ideas and see them rendered live. The canvas uses `react-live` for instant rendering without page reloads.
366
185
 
367
186
  ---
368
187
 
369
188
  ## Storybook MCP Integration
370
189
 
371
- Story UI can automatically connect to [Storybook MCP](https://github.com/storybookjs/addon-mcp) (`@storybook/addon-mcp`) to fetch component documentation, UI building guidelines, and existing story patterns. This enhances story generation quality by ensuring generated stories match your existing codebase patterns.
190
+ Story UI can connect to [Storybook MCP](https://github.com/storybookjs/addon-mcp) (`@storybook/addon-mcp`) to fetch component documentation, UI building guidelines, and existing story patterns. This enhances generation quality by ensuring output matches your codebase.
372
191
 
373
- ### How It Works
192
+ ### Setup
374
193
 
375
- When Story UI detects a running Storybook instance with the MCP addon, it automatically fetches:
376
-
377
- - **Component Documentation**: Props, descriptions, and usage examples for all components
378
- - **UI Building Instructions**: Storybook-specific guidelines for writing stories
379
- - **Story Patterns**: Existing story examples to match code style and conventions
380
-
381
- ### Configuration
382
-
383
- Add the Storybook MCP URL to your `story-ui.config.js`:
194
+ 1. Install `@storybook/addon-mcp` and enable `experimentalComponentsManifest` in `.storybook/main.js`:
384
195
 
385
196
  ```javascript
386
197
  export default {
387
- // ... other config options
388
-
389
- // Storybook MCP integration
390
- storybookMcpUrl: 'http://localhost:6006', // Your Storybook URL
391
- storybookMcpTimeout: 5000, // Timeout in ms (default: 5000)
392
- };
393
- ```
394
-
395
- ### Requirements
396
-
397
- 1. **Storybook 8.4+** with `@storybook/addon-mcp` installed
398
- 2. **`experimentalComponentsManifest: true`** enabled in `.storybook/main.js`:
399
-
400
- ```javascript
401
- // .storybook/main.js
402
- export default {
403
- // ... other config
404
198
  features: {
405
199
  experimentalComponentsManifest: true,
406
200
  },
@@ -411,231 +205,203 @@ export default {
411
205
  };
412
206
  ```
413
207
 
414
- ### Automatic Context Fetching
415
-
416
- When both Story UI and Storybook are running, context is fetched automatically during story generation:
208
+ 2. Add the Storybook URL to `story-ui.config.js`:
417
209
 
418
- ```
419
- 🔗 Fetching context from Storybook MCP: http://localhost:6006
420
- Storybook MCP context fetched in 2226ms
421
- - Component docs: 43 components
422
- - UI building instructions: available
423
- - Story patterns: 10 examples
210
+ ```javascript
211
+ module.exports = {
212
+ // ... other config
213
+ storybookMcpUrl: 'http://localhost:6006',
214
+ storybookMcpTimeout: 5000,
215
+ };
424
216
  ```
425
217
 
426
- This context is injected into the AI prompt, resulting in:
427
- - More accurate component prop usage
428
- - Consistent code style with existing stories
429
- - Proper import statements matching your project
218
+ When both Story UI and Storybook are running, context is fetched automatically during generation, resulting in more accurate component usage and consistent code style.
430
219
 
431
220
  ---
432
221
 
433
- ## Production Deployment
222
+ ## MCP Server Integration
434
223
 
435
- Story UI can be deployed as a standalone web application accessible from anywhere. We recommend Railway for its ease of use, but any Node.js hosting platform will work.
224
+ Story UI includes a Model Context Protocol (MCP) server for direct integration with AI clients like Claude Desktop and Claude Code.
436
225
 
437
- ### Architecture
226
+ ### Claude Desktop
438
227
 
439
- ```
440
- ┌─────────────────────────────────────────────────────────────┐
441
- │ Your Deployment (e.g., Railway) │
442
- │ ┌─────────────────────────────────────────────────────────┐│
443
- │ │ Express MCP Server (Node.js) ││
444
- │ │ - Serves Storybook with Story UI addon ││
445
- │ │ - API routes for story generation ││
446
- │ │ - Multi-provider LLM support (Claude, OpenAI, Gemini) ││
447
- │ │ - File-based story persistence ││
448
- │ └─────────────────────────────────────────────────────────┘│
449
- └──────────────────────────────────────────────────────────────┘
450
- ```
228
+ 1. Open **Claude Desktop** > **Settings** > **Connectors**
229
+ 2. Click **Add custom connector**
230
+ 3. Enter your deployment URL + `/mcp-remote/mcp`
231
+ 4. Restart Claude Desktop
451
232
 
452
- ### Deploy to Railway (Recommended)
453
-
454
- Railway provides a straightforward deployment experience with automatic HTTPS and file-based story persistence.
455
-
456
- **Quick Start:**
233
+ ### Claude Code
457
234
 
458
235
  ```bash
459
- # Install Railway CLI
460
- npm install -g @railway/cli
461
- railway login
236
+ # Production deployment
237
+ claude mcp add --transport http story-ui https://your-app.up.railway.app/mcp-remote/mcp
462
238
 
463
- # Initialize and deploy from your Storybook project
464
- railway init
465
- railway up
239
+ # Local development
240
+ claude mcp add --transport http story-ui-local http://localhost:4001/mcp-remote/mcp
466
241
  ```
467
242
 
468
- **Environment Variables (set in Railway Dashboard):**
469
- - `ANTHROPIC_API_KEY` - Required for Claude models
470
- - `OPENAI_API_KEY` - Optional, for OpenAI models
471
- - `GEMINI_API_KEY` - Optional, for Gemini models
243
+ ### Available MCP Tools
472
244
 
473
- **After Deployment:**
245
+ Once connected, these tools are available in Claude conversations:
246
+ - `generate-story` — Generate Storybook stories from natural language
247
+ - `list-components` — Discover available components
248
+ - `list-stories` — View existing generated stories
249
+ - `get-story` / `update-story` / `delete-story` — Manage stories
250
+ - `get-component-props` — Get component property information
251
+ - `test-connection` — Verify MCP connection
474
252
 
475
- Your Railway app will have a URL like `https://your-app-name.up.railway.app`. Use this URL to connect MCP clients:
253
+ ### Local STDIO Server
476
254
 
477
- ```bash
478
- # In Claude Code
479
- claude mcp add --transport http story-ui https://your-app-name.up.railway.app/mcp-remote/mcp
255
+ For Claude Desktop local integration without a deployed server:
256
+
257
+ ```json
258
+ {
259
+ "mcpServers": {
260
+ "story-ui": {
261
+ "command": "npx",
262
+ "args": ["@tpitre/story-ui", "mcp"]
263
+ }
264
+ }
265
+ }
480
266
  ```
481
267
 
482
- Or add it to Claude Desktop via **Settings** **Connectors** **Add custom connector**.
268
+ This requires the HTTP server running on port 4001 (`npm run story-ui`).
269
+
270
+ ---
271
+
272
+ ## Production Deployment
483
273
 
484
- **Important for Storybook Live Mode Deployments:**
274
+ Story UI can be deployed as a standalone web application. Railway is recommended for its simplicity.
485
275
 
486
- If deploying Storybook with Story UI integrated (where users can generate stories in the deployed app), ensure the StoryUI panel files are committed to git:
276
+ ### Deploy to Railway
487
277
 
488
278
  ```bash
489
- # Check if StoryUI is incorrectly gitignored
490
- grep "StoryUI" .gitignore
491
-
492
- # If found, remove from .gitignore and commit the panel
493
- git add src/stories/StoryUI/
494
- git commit -m "Add StoryUI panel for production"
279
+ npm install -g @railway/cli
280
+ railway login
281
+ railway init
282
+ railway up
495
283
  ```
496
284
 
497
- > **Note**: Story UI versions prior to 4.10.0 incorrectly added `src/stories/StoryUI/` to `.gitignore`. See [DEPLOYMENT.md](DEPLOYMENT.md#storybook-live-mode-deployment) for full instructions.
285
+ **Environment Variables:**
286
+ - `ANTHROPIC_API_KEY` — Required for Claude models
287
+ - `OPENAI_API_KEY` — Optional, for OpenAI models
288
+ - `GEMINI_API_KEY` — Optional, for Gemini models
289
+ - `STORYBOOK_PROXY_ENABLED` — Enable Storybook proxy mode for live demos
290
+ - `STORYBOOK_PROXY_PORT` — Internal Storybook port (default: 6006)
498
291
 
499
- See [DEPLOYMENT.md](DEPLOYMENT.md) for detailed deployment instructions and troubleshooting.
292
+ See [DEPLOYMENT.md](DEPLOYMENT.md) for detailed instructions and troubleshooting.
500
293
 
501
294
  ---
502
295
 
503
296
  ## Design System Documentation
504
297
 
505
- Story UI can learn your design system conventions to generate better stories.
298
+ Story UI reads your design system guidelines before every generation to produce better output.
506
299
 
507
- ### Directory-Based Documentation (Recommended)
300
+ ### Directory-Based (Recommended)
508
301
 
509
- Create a `story-ui-docs/` directory:
302
+ Create a `story-ui-docs/` directory with guidelines, tokens, and component documentation:
510
303
 
511
304
  ```
512
305
  story-ui-docs/
513
- ├── README.md # Overview
514
306
  ├── guidelines/
515
- │ ├── accessibility.md # A11y guidelines
516
- ├── responsive-design.md # Responsive rules
517
- │ └── brand-guidelines.md # Brand usage
307
+ │ ├── accessibility.md
308
+ └── responsive-design.md
518
309
  ├── tokens/
519
- │ ├── colors.json # Color tokens
520
- ├── spacing.md # Spacing system
521
- └── typography.json # Typography
522
- ├── components/
523
- │ ├── button.md # Button documentation
524
- │ └── forms.md # Form patterns
525
- └── patterns/
526
- ├── layouts.md # Layout patterns
527
- └── data-tables.md # Table patterns
310
+ │ ├── colors.json
311
+ └── spacing.md
312
+ └── components/
313
+ ├── button.md
314
+ └── forms.md
528
315
  ```
529
316
 
530
- ### Single-File Documentation
531
-
532
- For simpler setups, use `story-ui-considerations.md`:
317
+ ### Single-File
533
318
 
534
- ```markdown
535
- # Design System Considerations
536
-
537
- ## Color Usage
538
- - Primary actions: blue.6
539
- - Destructive actions: red.6
540
- - Success states: green.6
541
-
542
- ## Component Preferences
543
- - Use Button with variant="filled" for primary actions
544
- - Use Card with shadow="sm" for content containers
545
- ```
546
-
547
- ### Component-Specific Behaviors (Critical for Web Components)
548
-
549
- For libraries where components have specific requirements (like attributes needed for visibility), document these behaviors:
550
-
551
- ```markdown
552
- # Component-Specific Behaviors
553
-
554
- ## Alert Component (`<my-alert>`)
555
- **IMPORTANT**: The alert requires `is-active` attribute to be visible.
556
-
557
- <!-- WRONG - will not render -->
558
- <my-alert variant="success">Message</my-alert>
559
-
560
- <!-- CORRECT -->
561
- <my-alert variant="success" is-active>Message</my-alert>
562
-
563
- ## Import Patterns
564
- Components are in individual folders:
565
- - import '../../../components/alert/alert';
566
- - import '../../../components/icon/icons/check';
567
- ```
568
-
569
- The AI reads this file before every story generation, ensuring component-specific rules are followed.
319
+ For simpler setups, create `story-ui-considerations.md` in your project root with design system rules, color usage, and component preferences.
570
320
 
571
321
  ---
572
322
 
573
323
  ## CLI Reference
574
324
 
575
325
  ```bash
576
- # Initialize Story UI in your project
577
- npx story-ui init
578
-
579
- # Start the MCP server (default port: 4001)
580
- npx story-ui start
581
- npx story-ui start --port 4002 # Custom port
582
-
583
- # Run MCP STDIO server (for Claude Desktop local integration)
584
- npx story-ui mcp
585
-
586
- # Check installation status and version
587
- npx story-ui status
588
-
589
- # Update Story UI files to latest version
590
- npx story-ui update
326
+ npx story-ui init # Initialize Story UI in your project
327
+ npx story-ui start # Start the MCP server (default port: 4001)
328
+ npx story-ui mcp # Start STDIO MCP server for Claude Desktop
329
+ npx story-ui status # Check installation status and version
330
+ npx story-ui update # Update Story UI files to latest version
591
331
  ```
592
332
 
593
- ### Resetting / Uninstalling Story UI
333
+ ---
594
334
 
595
- To completely remove Story UI from your project and start fresh:
335
+ ## Environment Variables
596
336
 
597
337
  ```bash
598
- # Remove Story UI configuration and panel files
599
- rm -f story-ui.config.js
600
- rm -f story-ui-considerations.md
601
- rm -rf story-ui-docs/
602
- rm -rf src/stories/StoryUI/
603
- rm -rf src/stories/generated/
338
+ # .env
339
+ LLM_PROVIDER=claude
340
+ ANTHROPIC_API_KEY=sk-ant-...
341
+ OPENAI_API_KEY=sk-... # optional
342
+ GEMINI_API_KEY=... # optional
343
+ VITE_STORY_UI_PORT=4001
344
+ ```
604
345
 
605
- # Remove package.json script entries (manual)
606
- # Delete "story-ui" and "storybook-with-ui" from scripts
346
+ ---
607
347
 
608
- # Optionally remove the package
609
- npm uninstall @tpitre/story-ui
348
+ ## API Reference
610
349
 
611
- # Optionally clean .env (remove VITE_STORY_UI_PORT, API keys)
612
- ```
350
+ ### Story Generation
613
351
 
614
- After removal, run `npx story-ui init` to start fresh with a clean installation.
352
+ | Method | Endpoint | Description |
353
+ |--------|----------|-------------|
354
+ | `POST` | `/mcp/generate-story` | Generate story from prompt |
355
+ | `POST` | `/mcp/generate-story-stream` | Streaming story generation |
356
+ | `POST` | `/mcp/canvas-generate` | Generate + write voice canvas story |
357
+ | `POST` | `/mcp/canvas-save` | Save canvas to named story file |
615
358
 
616
- ---
359
+ ### Component Discovery
617
360
 
618
- ## API Reference
361
+ | Method | Endpoint | Description |
362
+ |--------|----------|-------------|
363
+ | `GET` | `/mcp/components` | List discovered components |
364
+ | `GET` | `/mcp/props` | Get component props |
365
+ | `GET` | `/mcp/frameworks` | List supported frameworks |
366
+ | `GET` | `/mcp/frameworks/detect` | Detect current project framework |
619
367
 
620
- ### REST Endpoints
368
+ ### Story Management
621
369
 
622
370
  | Method | Endpoint | Description |
623
371
  |--------|----------|-------------|
624
- | `POST` | `/story-ui/generate` | Generate story (specify provider in body) |
625
- | `POST` | `/story-ui/generate-stream` | Generate story with streaming |
626
- | `GET` | `/story-ui/providers` | List available LLM providers and models |
627
- | `GET` | `/story-ui/components` | List discovered components |
628
- | `GET` | `/story-ui/considerations` | Get design system context |
629
372
  | `GET` | `/mcp/stories` | List generated stories |
373
+ | `GET` | `/mcp/stories/:storyId` | Get a specific story |
374
+ | `GET` | `/mcp/stories/:storyId/content` | Get story file content |
630
375
  | `DELETE` | `/mcp/stories/:storyId` | Delete a story |
631
376
 
377
+ ### Provider Management
378
+
379
+ | Method | Endpoint | Description |
380
+ |--------|----------|-------------|
381
+ | `GET` | `/mcp/providers` | List available LLM providers |
382
+ | `GET` | `/mcp/providers/models` | List models per provider |
383
+ | `POST` | `/mcp/providers/configure` | Configure a provider |
384
+ | `POST` | `/mcp/providers/validate` | Validate an API key |
385
+ | `POST` | `/mcp/providers/model` | Set active model |
386
+
387
+ ### Manifest
388
+
389
+ | Method | Endpoint | Description |
390
+ |--------|----------|-------------|
391
+ | `GET` | `/story-ui/manifest` | Get story manifest |
392
+ | `GET` | `/story-ui/manifest/poll` | Poll for manifest changes |
393
+ | `POST` | `/story-ui/manifest/reconcile` | Reconcile manifest with filesystem |
394
+ | `DELETE` | `/story-ui/manifest/:fileName` | Remove entry from manifest |
395
+
396
+ All endpoints are also available under the `/story-ui/` prefix.
397
+
632
398
  ### Request Format
633
399
 
634
400
  ```typescript
635
401
  {
636
402
  prompt: string; // User's request
637
- provider?: string; // LLM provider: 'claude' | 'openai' | 'gemini'
638
- model?: string; // Specific model to use
403
+ provider?: string; // 'claude' | 'openai' | 'gemini'
404
+ model?: string; // Specific model ID
639
405
  previousCode?: string; // For iterations
640
406
  history?: Message[]; // Conversation history
641
407
  imageData?: string; // Base64 image for vision
@@ -644,18 +410,6 @@ After removal, run `npx story-ui init` to start fresh with a clean installation.
644
410
 
645
411
  ---
646
412
 
647
- ## Upgrading from v2
648
-
649
- Story UI v4 is backwards compatible with previous configurations. However, to take advantage of new features:
650
-
651
- 1. **Multi-Provider Support**: Add `llmProvider` to your config
652
- 2. **Framework Detection**: Add `framework` to your config for non-React projects
653
- 3. **Production Deployment**: Use `npx story-ui deploy` for one-command deployment
654
-
655
- No breaking changes - existing stories and configurations will continue to work.
656
-
657
- ---
658
-
659
413
  ## Contributing
660
414
 
661
415
  We welcome contributions! See our [Contributing Guide](CONTRIBUTING.md).
@@ -667,18 +421,17 @@ git clone https://github.com/southleft/story-ui.git
667
421
  cd story-ui
668
422
  npm install
669
423
  npm run build
670
- npm link
671
424
 
672
- # Test in a project
673
- cd your-project
674
- npm link @tpitre/story-ui
425
+ # Test in a consumer project (important: run from the consumer directory)
426
+ cd /path/to/your-storybook-project
427
+ PORT=4001 node /path/to/story-ui/dist/mcp-server/index.js
675
428
  ```
676
429
 
677
430
  ---
678
431
 
679
432
  ## License
680
433
 
681
- MIT © [Story UI Contributors](LICENSE)
434
+ MIT - [Story UI Contributors](LICENSE)
682
435
 
683
436
  ---
684
437
 
@@ -686,10 +439,6 @@ MIT © [Story UI Contributors](LICENSE)
686
439
 
687
440
  - [GitHub Repository](https://github.com/southleft/story-ui)
688
441
  - [NPM Package](https://www.npmjs.com/package/@tpitre/story-ui)
689
- - [Issues & Support](https://github.com/southleft/story-ui/issues)
690
- - [MCP Integration Guide](docs/MCP_INTEGRATION.md)
691
442
  - [Deployment Guide](DEPLOYMENT.md)
692
-
693
- ---
694
-
695
- *Story UI - Making component documentation delightful, one conversation at a time.*
443
+ - [MCP Integration Guide](docs/MCP_INTEGRATION.md)
444
+ - [Issues & Support](https://github.com/southleft/story-ui/issues)
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../cli/setup.ts"],"names":[],"mappings":"AAmDA;;GAEG;AACH,wBAAgB,iCAAiC,SA8ChD;AAoXD,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC7C,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,YAAY,CAAC,OAAO,GAAE,YAAiB,iBA6/B5D"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../cli/setup.ts"],"names":[],"mappings":"AAmDA;;GAEG;AACH,wBAAgB,iCAAiC,SA8ChD;AAoXD,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC7C,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,YAAY,CAAC,OAAO,GAAE,YAAiB,iBAqgC5D"}
package/dist/cli/setup.js CHANGED
@@ -1310,6 +1310,13 @@ VITE_STORY_UI_PORT=${answers.mcpPort || '4001'}
1310
1310
  devDependencies['concurrently'] = '^8.2.0';
1311
1311
  needsInstall = true;
1312
1312
  }
1313
+ // Check for react-live (imported directly by voice canvas templates;
1314
+ // cannot be resolved transitively when @tpitre/story-ui is symlinked)
1315
+ if (!dependencies['react-live'] && !devDependencies['react-live']) {
1316
+ console.log(chalk.blue('📦 Adding react-live dependency (required by voice canvas)...'));
1317
+ devDependencies['react-live'] = '^4.1.8';
1318
+ needsInstall = true;
1319
+ }
1313
1320
  packageJson.dependencies = dependencies;
1314
1321
  packageJson.devDependencies = devDependencies;
1315
1322
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../cli/update.ts"],"names":[],"mappings":"AASA;;;;;GAKG;AAEH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AA0UD;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAkItF;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CA+BpC"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../cli/update.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AAEH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAyYD;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAyItF;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CA+BpC"}
@@ -1,10 +1,16 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import { execSync } from 'child_process';
3
4
  import chalk from 'chalk';
4
5
  import { fileURLToPath } from 'url';
5
6
  import inquirer from 'inquirer';
6
7
  const __filename = fileURLToPath(import.meta.url);
7
8
  const __dirname = path.dirname(__filename);
9
+ // Runtime dependencies that consumer projects must have installed for
10
+ // managed template files to resolve. The voice canvas templates import
11
+ // `react-live` directly, so it cannot be resolved transitively through
12
+ // a symlinked @tpitre/story-ui install.
13
+ const REQUIRED_CONSUMER_DEPS = ['react-live'];
8
14
  // Files managed by Story UI that can be safely overwritten
9
15
  const MANAGED_FILES = [
10
16
  {
@@ -284,6 +290,62 @@ function updateConfigVersion(configPath, version) {
284
290
  return false;
285
291
  }
286
292
  }
293
+ /**
294
+ * Ensure the consumer project has the runtime deps required by managed
295
+ * template files (e.g. react-live for voice canvas). Installs any missing
296
+ * packages using the detected package manager.
297
+ */
298
+ function ensureConsumerDependencies(options) {
299
+ const cwd = process.cwd();
300
+ const packageJsonPath = path.join(cwd, 'package.json');
301
+ const result = { installed: [], errors: [] };
302
+ if (!fs.existsSync(packageJsonPath)) {
303
+ return result;
304
+ }
305
+ let packageJson;
306
+ try {
307
+ packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
308
+ }
309
+ catch (error) {
310
+ result.errors.push(`Could not read package.json: ${error.message}`);
311
+ return result;
312
+ }
313
+ const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
314
+ const missing = REQUIRED_CONSUMER_DEPS.filter((pkg) => !allDeps[pkg]);
315
+ if (missing.length === 0) {
316
+ return result;
317
+ }
318
+ if (options.dryRun) {
319
+ console.log(chalk.cyan(` 📋 Would install missing deps: ${missing.join(', ')}`));
320
+ return result;
321
+ }
322
+ // Detect package manager
323
+ const yarnLock = fs.existsSync(path.join(cwd, 'yarn.lock'));
324
+ const pnpmLock = fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'));
325
+ let installCommand = `npm install --save-dev ${missing.join(' ')}`;
326
+ if (yarnLock) {
327
+ installCommand = `yarn add --dev ${missing.join(' ')}`;
328
+ }
329
+ else if (pnpmLock) {
330
+ installCommand = `pnpm add -D ${missing.join(' ')}`;
331
+ }
332
+ console.log(chalk.bold('\n📦 Installing required runtime dependencies:'));
333
+ for (const pkg of missing) {
334
+ console.log(chalk.cyan(` • ${pkg}`));
335
+ }
336
+ console.log(chalk.gray(` Running: ${installCommand}`));
337
+ try {
338
+ execSync(installCommand, { stdio: 'inherit', cwd });
339
+ result.installed.push(...missing);
340
+ console.log(chalk.green(` ✅ Installed: ${missing.join(', ')}`));
341
+ }
342
+ catch (error) {
343
+ const message = `Failed to install ${missing.join(', ')}. Run "${installCommand}" manually.`;
344
+ result.errors.push(message);
345
+ console.log(chalk.yellow(` ⚠️ ${message}`));
346
+ }
347
+ return result;
348
+ }
287
349
  /**
288
350
  * Main update command
289
351
  */
@@ -372,7 +434,13 @@ export async function updateCommand(options = {}) {
372
434
  result.errors.push(`${file.target}: ${updateResult.error}`);
373
435
  }
374
436
  }
375
- // Step 5: Update config version tracking
437
+ // Step 5: Ensure required consumer dependencies are installed
438
+ // (e.g. react-live, which voice canvas templates import directly)
439
+ const depsResult = ensureConsumerDependencies(options);
440
+ if (depsResult.errors.length > 0) {
441
+ result.errors.push(...depsResult.errors);
442
+ }
443
+ // Step 6: Update config version tracking
376
444
  if (!options.dryRun && installation.configPath) {
377
445
  if (updateConfigVersion(installation.configPath, result.newVersion)) {
378
446
  console.log(chalk.gray(`\n Updated version tracking in ${path.basename(installation.configPath)}`));
@@ -1 +1 @@
1
- {"version":3,"file":"canvasGenerate.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/canvasGenerate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAkB5C,eAAO,MAAM,qBAAqB,oCAAoC,CAAC;AAwIvE,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAmCrD;AAID;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAU/D;AAID;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOrD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAc1D;AAgED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAsBvD;AAID,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,+CA0GtE"}
1
+ {"version":3,"file":"canvasGenerate.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/canvasGenerate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAkB5C,eAAO,MAAM,qBAAqB,oCAAoC,CAAC;AA2IvE,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAmCrD;AAID;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAU/D;AAID;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOrD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAc1D;AAgED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAsBvD;AAID,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,+CA0GtE"}
@@ -120,15 +120,18 @@ render(<Canvas />);\`;
120
120
 
121
121
  export const Default: StoryObj = {
122
122
  render: () => {
123
- // Always start with the placeholder no localStorage restore.
124
- // Code updates arrive exclusively via postMessage from the parent panel.
125
- // This prevents stale code from a previous session causing errors.
126
- const [code, setCode] = useState(PLACEHOLDER);
123
+ // Read from localStorage on mount for the initial code delivery.
124
+ // The parent panel writes code to localStorage just before mounting
125
+ // the iframe, since postMessage can't work until our listener is ready.
126
+ const [code, setCode] = useState(() => {
127
+ try {
128
+ const saved = localStorage.getItem('${LS_KEY}');
129
+ if (saved && saved.trim()) return saved;
130
+ } catch {}
131
+ return PLACEHOLDER;
132
+ });
127
133
 
128
134
  useEffect(() => {
129
- // Clear any stale code left in localStorage from older versions
130
- try { localStorage.removeItem('${LS_KEY}'); } catch {}
131
-
132
135
  const handler = (e: MessageEvent) => {
133
136
  if (e.origin !== window.location.origin) return;
134
137
  if (e.data?.type === 'VOICE_CANVAS_UPDATE' && typeof e.data.code === 'string') {
@@ -171,7 +171,7 @@ const MIGRATION_FLAG = 'story-ui-manifest-migrated-v1';
171
171
  const MIGRATION_FLAG_V2 = 'story-ui-manifest-migrated-v2';
172
172
  // v3: seeds synthetic conversations for any manifest entry with metadata.prompt but no conversation
173
173
  const MIGRATION_FLAG_V3 = 'story-ui-manifest-migrated-v3';
174
- const CONSIDERATIONS_API = () => `${getApiBase()}/mcp/considerations`;
174
+ const CONSIDERATIONS_API = () => `${getApiBase()}/story-ui/considerations`;
175
175
  function isEdgeMode() {
176
176
  if (typeof window !== 'undefined') {
177
177
  const hostname = window.location.hostname;
@@ -361,7 +361,7 @@ const MIGRATION_FLAG = 'story-ui-manifest-migrated-v1';
361
361
  const MIGRATION_FLAG_V2 = 'story-ui-manifest-migrated-v2';
362
362
  // v3: seeds synthetic conversations for any manifest entry with metadata.prompt but no conversation
363
363
  const MIGRATION_FLAG_V3 = 'story-ui-manifest-migrated-v3';
364
- const CONSIDERATIONS_API = () => `${getApiBase()}/mcp/considerations`;
364
+ const CONSIDERATIONS_API = () => `${getApiBase()}/story-ui/considerations`;
365
365
 
366
366
  function isEdgeMode(): boolean {
367
367
  if (typeof window !== 'undefined') {
@@ -1 +1 @@
1
- {"version":3,"file":"VoiceCanvas.d.ts","sourceRoot":"","sources":["../../../../templates/StoryUI/voice/VoiceCanvas.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAwE,MAAM,OAAO,CAAC;AAY7F,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAID,eAAO,MAAM,WAAW,4FA6wBtB,CAAC"}
1
+ {"version":3,"file":"VoiceCanvas.d.ts","sourceRoot":"","sources":["../../../../templates/StoryUI/voice/VoiceCanvas.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAwE,MAAM,OAAO,CAAC;AAY7F,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAID,eAAO,MAAM,WAAW,4FAsxBtB,CAAC"}
@@ -71,10 +71,18 @@ export const VoiceCanvas = React.forwardRef(function VoiceCanvas({ apiBase, prov
71
71
  const iframeLoadedRef = useRef(false);
72
72
  // ── Code → iframe bridge ─────────────────────────────────────
73
73
  /**
74
- * Push code to the story preview iframe via postMessage.
75
- * No localStorage persistence each session starts clean.
74
+ * Push code to the story preview iframe.
75
+ * Uses both localStorage (for initial load before message listener is ready)
76
+ * and postMessage (for instant updates once the iframe is running).
76
77
  */
77
78
  const sendCodeToIframe = useCallback((code) => {
79
+ // Write to localStorage so the iframe can read it on initial mount —
80
+ // postMessage alone doesn't work for the first generation because the
81
+ // iframe's React component hasn't attached its message listener yet.
82
+ try {
83
+ localStorage.setItem(LS_KEY, code);
84
+ }
85
+ catch { }
78
86
  if (iframeRef.current?.contentWindow && iframeLoadedRef.current) {
79
87
  iframeRef.current.contentWindow.postMessage({ type: 'VOICE_CANVAS_UPDATE', code }, IFRAME_ORIGIN);
80
88
  }
@@ -145,12 +153,20 @@ export const VoiceCanvas = React.forwardRef(function VoiceCanvas({ apiBase, prov
145
153
  setRedoStack([]);
146
154
  }
147
155
  setCurrentCode(newCode);
148
- sendCodeToIframe(newCode);
149
- // First generation mount the iframe
156
+ // First generation — mount the iframe. Write to localStorage
157
+ // BEFORE mounting so the iframe reads the code on initial render.
158
+ // For subsequent generations, sendCodeToIframe uses postMessage.
150
159
  if (!storyReady) {
160
+ try {
161
+ localStorage.setItem(LS_KEY, newCode);
162
+ }
163
+ catch { }
151
164
  setStoryReady(true);
152
165
  setIframeKey(k => k + 1);
153
166
  }
167
+ else {
168
+ sendCodeToIframe(newCode);
169
+ }
154
170
  // Track the first prompt for save title (describes the component),
155
171
  // and the last prompt for display in the status bar.
156
172
  if (!firstPromptRef.current) {
@@ -107,10 +107,15 @@ function VoiceCanvas({
107
107
  // ── Code → iframe bridge ─────────────────────────────────────
108
108
 
109
109
  /**
110
- * Push code to the story preview iframe via postMessage.
111
- * No localStorage persistence each session starts clean.
110
+ * Push code to the story preview iframe.
111
+ * Uses both localStorage (for initial load before message listener is ready)
112
+ * and postMessage (for instant updates once the iframe is running).
112
113
  */
113
114
  const sendCodeToIframe = useCallback((code: string) => {
115
+ // Write to localStorage so the iframe can read it on initial mount —
116
+ // postMessage alone doesn't work for the first generation because the
117
+ // iframe's React component hasn't attached its message listener yet.
118
+ try { localStorage.setItem(LS_KEY, code); } catch {}
114
119
  if (iframeRef.current?.contentWindow && iframeLoadedRef.current) {
115
120
  iframeRef.current.contentWindow.postMessage(
116
121
  { type: 'VOICE_CANVAS_UPDATE', code },
@@ -195,12 +200,16 @@ function VoiceCanvas({
195
200
  }
196
201
 
197
202
  setCurrentCode(newCode);
198
- sendCodeToIframe(newCode);
199
203
 
200
- // First generation — mount the iframe
204
+ // First generation — mount the iframe. Write to localStorage
205
+ // BEFORE mounting so the iframe reads the code on initial render.
206
+ // For subsequent generations, sendCodeToIframe uses postMessage.
201
207
  if (!storyReady) {
208
+ try { localStorage.setItem(LS_KEY, newCode); } catch {}
202
209
  setStoryReady(true);
203
210
  setIframeKey(k => k + 1);
211
+ } else {
212
+ sendCodeToIframe(newCode);
204
213
  }
205
214
 
206
215
  // Track the first prompt for save title (describes the component),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpitre/story-ui",
3
- "version": "4.16.10",
3
+ "version": "4.16.12",
4
4
  "description": "AI-powered Storybook story generator with dynamic component discovery",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -361,7 +361,7 @@ const MIGRATION_FLAG = 'story-ui-manifest-migrated-v1';
361
361
  const MIGRATION_FLAG_V2 = 'story-ui-manifest-migrated-v2';
362
362
  // v3: seeds synthetic conversations for any manifest entry with metadata.prompt but no conversation
363
363
  const MIGRATION_FLAG_V3 = 'story-ui-manifest-migrated-v3';
364
- const CONSIDERATIONS_API = () => `${getApiBase()}/mcp/considerations`;
364
+ const CONSIDERATIONS_API = () => `${getApiBase()}/story-ui/considerations`;
365
365
 
366
366
  function isEdgeMode(): boolean {
367
367
  if (typeof window !== 'undefined') {
@@ -107,10 +107,15 @@ function VoiceCanvas({
107
107
  // ── Code → iframe bridge ─────────────────────────────────────
108
108
 
109
109
  /**
110
- * Push code to the story preview iframe via postMessage.
111
- * No localStorage persistence each session starts clean.
110
+ * Push code to the story preview iframe.
111
+ * Uses both localStorage (for initial load before message listener is ready)
112
+ * and postMessage (for instant updates once the iframe is running).
112
113
  */
113
114
  const sendCodeToIframe = useCallback((code: string) => {
115
+ // Write to localStorage so the iframe can read it on initial mount —
116
+ // postMessage alone doesn't work for the first generation because the
117
+ // iframe's React component hasn't attached its message listener yet.
118
+ try { localStorage.setItem(LS_KEY, code); } catch {}
114
119
  if (iframeRef.current?.contentWindow && iframeLoadedRef.current) {
115
120
  iframeRef.current.contentWindow.postMessage(
116
121
  { type: 'VOICE_CANVAS_UPDATE', code },
@@ -195,12 +200,16 @@ function VoiceCanvas({
195
200
  }
196
201
 
197
202
  setCurrentCode(newCode);
198
- sendCodeToIframe(newCode);
199
203
 
200
- // First generation — mount the iframe
204
+ // First generation — mount the iframe. Write to localStorage
205
+ // BEFORE mounting so the iframe reads the code on initial render.
206
+ // For subsequent generations, sendCodeToIframe uses postMessage.
201
207
  if (!storyReady) {
208
+ try { localStorage.setItem(LS_KEY, newCode); } catch {}
202
209
  setStoryReady(true);
203
210
  setIframeKey(k => k + 1);
211
+ } else {
212
+ sendCodeToIframe(newCode);
204
213
  }
205
214
 
206
215
  // Track the first prompt for save title (describes the component),