@tpitre/story-ui 4.16.3 → 4.16.5

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.
@@ -109,6 +109,17 @@ app.post('/mcp/generate-story-stream', generateStoryFromPromptStream);
109
109
  // Voice Canvas endpoints
110
110
  app.post('/mcp/canvas-generate', canvasGenerateHandler); // generate + write voice-canvas.stories.tsx
111
111
  app.post('/mcp/canvas-save', canvasSaveHandler); // save canvas to named .stories.tsx
112
+ // Ensure voice-canvas story template exists (lightweight, no LLM call)
113
+ app.post('/mcp/canvas-ensure', (_req, res) => {
114
+ try {
115
+ const storiesDir = config.generatedStoriesPath || './src/stories/generated/';
116
+ ensureVoiceCanvasStory(storiesDir);
117
+ return res.json({ ok: true });
118
+ }
119
+ catch (err) {
120
+ return res.json({ ok: false });
121
+ }
122
+ });
112
123
  // Manifest — story ↔ chat source of truth
113
124
  // NOTE: /reconcile must be registered BEFORE /:fileName to avoid route conflict
114
125
  app.get('/story-ui/manifest/poll', manifestPollHandler);
@@ -82,7 +82,7 @@ const VOICE_CANVAS_TEMPLATE = `import React, { useState, useEffect } from 'react
82
82
  import { LiveProvider, LivePreview, LiveError } from 'react-live';
83
83
  import type { Meta, StoryObj } from '@storybook/react';
84
84
 
85
- const meta: Meta = { title: 'Generated/Voice Canvas' };
85
+ const meta: Meta = { title: 'Generated/Voice Canvas', tags: ['voice-canvas-internal'] };
86
86
  export default meta;
87
87
 
88
88
  // Design system components set by .storybook/preview.tsx via:
@@ -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,4FAgvBtB,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,4FA6uBtB,CAAC"}
@@ -515,19 +515,16 @@ export const VoiceCanvas = React.forwardRef(function VoiceCanvas({ apiBase, prov
515
515
  // Storybook reloads the page when a new .stories.tsx file is saved.
516
516
  // Code is already persisted to localStorage by sendCodeToIframe,
517
517
  // so we just need to read it back and restore storyReady on mount.
518
+ // Voice Canvas always starts with a clean slate — no session restore.
519
+ // Each time the user switches to the Canvas tab or reloads, they see
520
+ // the empty "describe what you want to build" state, same as Chat.
521
+ // localStorage is still used for postMessage bridging within a single
522
+ // generation session, but we clear it on mount so stale code from a
523
+ // previous session never auto-loads.
518
524
  useEffect(() => {
519
525
  try {
520
- const savedCode = localStorage.getItem(LS_KEY);
521
- if (savedCode && savedCode.trim()) {
522
- setCurrentCode(savedCode);
523
- setStoryReady(true);
524
- }
525
- const savedPrompt = localStorage.getItem(LS_PROMPT_KEY);
526
- if (savedPrompt) {
527
- firstPromptRef.current = savedPrompt;
528
- lastPromptRef.current = savedPrompt;
529
- setLastPrompt(savedPrompt);
530
- }
526
+ localStorage.removeItem(LS_KEY);
527
+ localStorage.removeItem(LS_PROMPT_KEY);
531
528
  }
532
529
  catch { /* localStorage unavailable */ }
533
530
  }, []); // eslint-disable-line react-hooks/exhaustive-deps
@@ -549,19 +549,16 @@ function VoiceCanvas({
549
549
  // Code is already persisted to localStorage by sendCodeToIframe,
550
550
  // so we just need to read it back and restore storyReady on mount.
551
551
 
552
+ // Voice Canvas always starts with a clean slate — no session restore.
553
+ // Each time the user switches to the Canvas tab or reloads, they see
554
+ // the empty "describe what you want to build" state, same as Chat.
555
+ // localStorage is still used for postMessage bridging within a single
556
+ // generation session, but we clear it on mount so stale code from a
557
+ // previous session never auto-loads.
552
558
  useEffect(() => {
553
559
  try {
554
- const savedCode = localStorage.getItem(LS_KEY);
555
- if (savedCode && savedCode.trim()) {
556
- setCurrentCode(savedCode);
557
- setStoryReady(true);
558
- }
559
- const savedPrompt = localStorage.getItem(LS_PROMPT_KEY);
560
- if (savedPrompt) {
561
- firstPromptRef.current = savedPrompt;
562
- lastPromptRef.current = savedPrompt;
563
- setLastPrompt(savedPrompt);
564
- }
560
+ localStorage.removeItem(LS_KEY);
561
+ localStorage.removeItem(LS_PROMPT_KEY);
565
562
  } catch { /* localStorage unavailable */ }
566
563
  }, []); // eslint-disable-line react-hooks/exhaustive-deps
567
564
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpitre/story-ui",
3
- "version": "4.16.3",
3
+ "version": "4.16.5",
4
4
  "description": "AI-powered Storybook story generator with dynamic component discovery",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -549,19 +549,16 @@ function VoiceCanvas({
549
549
  // Code is already persisted to localStorage by sendCodeToIframe,
550
550
  // so we just need to read it back and restore storyReady on mount.
551
551
 
552
+ // Voice Canvas always starts with a clean slate — no session restore.
553
+ // Each time the user switches to the Canvas tab or reloads, they see
554
+ // the empty "describe what you want to build" state, same as Chat.
555
+ // localStorage is still used for postMessage bridging within a single
556
+ // generation session, but we clear it on mount so stale code from a
557
+ // previous session never auto-loads.
552
558
  useEffect(() => {
553
559
  try {
554
- const savedCode = localStorage.getItem(LS_KEY);
555
- if (savedCode && savedCode.trim()) {
556
- setCurrentCode(savedCode);
557
- setStoryReady(true);
558
- }
559
- const savedPrompt = localStorage.getItem(LS_PROMPT_KEY);
560
- if (savedPrompt) {
561
- firstPromptRef.current = savedPrompt;
562
- lastPromptRef.current = savedPrompt;
563
- setLastPrompt(savedPrompt);
564
- }
560
+ localStorage.removeItem(LS_KEY);
561
+ localStorage.removeItem(LS_PROMPT_KEY);
565
562
  } catch { /* localStorage unavailable */ }
566
563
  }, []); // eslint-disable-line react-hooks/exhaustive-deps
567
564