@tpitre/story-ui 4.16.8 → 4.16.10
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/dist/mcp-server/routes/canvasGenerate.d.ts.map +1 -1
- package/dist/mcp-server/routes/canvasGenerate.js +7 -6
- package/dist/templates/StoryUI/StoryUIPanel.css +1 -0
- package/dist/templates/StoryUI/voice/VoiceCanvas.d.ts.map +1 -1
- package/dist/templates/StoryUI/voice/VoiceCanvas.js +3 -12
- package/dist/templates/StoryUI/voice/VoiceCanvas.tsx +5 -7
- package/package.json +1 -1
- package/templates/StoryUI/StoryUIPanel.css +1 -0
- package/templates/StoryUI/voice/VoiceCanvas.tsx +5 -7
|
@@ -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;
|
|
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"}
|
|
@@ -120,18 +120,19 @@ render(<Canvas />);\`;
|
|
|
120
120
|
|
|
121
121
|
export const Default: StoryObj = {
|
|
122
122
|
render: () => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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);
|
|
127
127
|
|
|
128
128
|
useEffect(() => {
|
|
129
|
+
// Clear any stale code left in localStorage from older versions
|
|
130
|
+
try { localStorage.removeItem('${LS_KEY}'); } catch {}
|
|
131
|
+
|
|
129
132
|
const handler = (e: MessageEvent) => {
|
|
130
|
-
// Only accept messages from same origin to prevent cross-origin code injection
|
|
131
133
|
if (e.origin !== window.location.origin) return;
|
|
132
134
|
if (e.data?.type === 'VOICE_CANVAS_UPDATE' && typeof e.data.code === 'string') {
|
|
133
135
|
setCode(e.data.code);
|
|
134
|
-
try { localStorage.setItem('${LS_KEY}', e.data.code); } catch {}
|
|
135
136
|
}
|
|
136
137
|
};
|
|
137
138
|
window.addEventListener('message', handler);
|
|
@@ -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,
|
|
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"}
|
|
@@ -71,15 +71,10 @@ export const VoiceCanvas = React.forwardRef(function VoiceCanvas({ apiBase, prov
|
|
|
71
71
|
const iframeLoadedRef = useRef(false);
|
|
72
72
|
// ── Code → iframe bridge ─────────────────────────────────────
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* and the story reads it on mount.
|
|
74
|
+
* Push code to the story preview iframe via postMessage.
|
|
75
|
+
* No localStorage persistence — each session starts clean.
|
|
77
76
|
*/
|
|
78
77
|
const sendCodeToIframe = useCallback((code) => {
|
|
79
|
-
try {
|
|
80
|
-
localStorage.setItem(LS_KEY, code);
|
|
81
|
-
}
|
|
82
|
-
catch { }
|
|
83
78
|
if (iframeRef.current?.contentWindow && iframeLoadedRef.current) {
|
|
84
79
|
iframeRef.current.contentWindow.postMessage({ type: 'VOICE_CANVAS_UPDATE', code }, IFRAME_ORIGIN);
|
|
85
80
|
}
|
|
@@ -163,10 +158,6 @@ export const VoiceCanvas = React.forwardRef(function VoiceCanvas({ apiBase, prov
|
|
|
163
158
|
}
|
|
164
159
|
lastPromptRef.current = transcript;
|
|
165
160
|
setLastPrompt(transcript);
|
|
166
|
-
try {
|
|
167
|
-
localStorage.setItem(LS_PROMPT_KEY, firstPromptRef.current);
|
|
168
|
-
}
|
|
169
|
-
catch { }
|
|
170
161
|
conversationRef.current.push({ role: 'user', content: transcript }, { role: 'assistant', content: '[Generated canvas component]' });
|
|
171
162
|
if (conversationRef.current.length > 40) {
|
|
172
163
|
conversationRef.current = conversationRef.current.slice(-40);
|
|
@@ -592,5 +583,5 @@ export const VoiceCanvas = React.forwardRef(function VoiceCanvas({ apiBase, prov
|
|
|
592
583
|
// ── Render ─────────────────────────────────────────────────────
|
|
593
584
|
return (_jsxs("div", { className: "sui-canvas-container", children: [_jsxs("div", { className: "sui-canvas-preview", children: [!storyReady && !isGenerating && (_jsxs("div", { className: "sui-canvas-empty", children: [_jsx("div", { className: "sui-canvas-empty-icon", children: _jsxs("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }), _jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }), _jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })] }) }), _jsx("h2", { className: "sui-canvas-empty-title", children: "Voice Canvas" }), _jsx("p", { className: "sui-canvas-empty-desc", children: speechSupported
|
|
594
585
|
? 'Speak or type to build interfaces live with your design system components.'
|
|
595
|
-
: 'Type a prompt to build interfaces live with your design system components.' }), _jsx("p", { className: "sui-canvas-empty-hint", children: "Try: \"Create a product card with an image, title, price, and buy button\"" })] })), !storyReady && isGenerating && (_jsxs("div", { className: "sui-canvas-progress", children: [_jsx("div", { className: "sui-canvas-progress-spinner" }), _jsx("span", { className: "sui-canvas-progress-text", children: statusText || 'Building...' })] })), storyReady && (_jsxs("div", { className: "sui-canvas-live-wrapper", children: [isGenerating && (_jsxs("div", { className: "sui-canvas-regen-overlay", children: [_jsx("div", { className: "sui-canvas-progress-spinner sui-canvas-progress-spinner--sm" }), _jsx("span", { children: statusText || 'Regenerating...' })] })), _jsx("iframe", { ref: iframeRef, src: iframeSrc, title: "Voice Canvas Preview", className: "sui-canvas-iframe", onLoad: handleIframeLoad }, iframeKey)] })), !isGenerating && errorMessage && (_jsxs("div", { className: "sui-canvas-error", children: [_jsx("span", { children: errorMessage }), _jsx("button", { type: "button", className: "sui-canvas-error-dismiss", onClick: () => setErrorMessage(''), "aria-label": "Dismiss error", children: "\u00D7" })] })), savedMessage && (_jsxs("div", { className: "sui-canvas-saved-toast", children: [_jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("polyline", { points: "20 6 9 17 4 12" }) }), _jsxs("span", { children: ["Saved: ", savedMessage] })] }))] }), statusText && !isGenerating && (_jsx("div", { className: "sui-canvas-status-bar", children: _jsx("span", { className: "sui-canvas-explanation", children: statusText }) })), _jsxs("div", { className: `sui-canvas-bar ${isListening ? 'sui-canvas-bar--active' : ''}`, children: [_jsxs("div", { className: "sui-canvas-bar-left", children: [speechSupported && (_jsxs("button", { type: "button", className: `sui-canvas-mic ${isListening ? 'sui-canvas-mic--active' : ''}`, onClick: toggleListening, "aria-label": isListening ? 'Stop voice input' : 'Start voice input', children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }), _jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }), _jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })] }), isListening && _jsx("span", { className: "sui-canvas-mic-pulse" })] })), (isListening || isGenerating) && (_jsx("div", { className: "sui-canvas-transcript", children: isGenerating ? (_jsx("span", { className: "sui-canvas-status-rendering", children: statusText || 'Building interface...' })) : interimText ? (_jsxs("span", { className: "sui-canvas-status-interim", children: [pendingTranscript ? pendingTranscript + ' ' : '', interimText] })) : pendingTranscript ? (_jsx("span", { className: "sui-canvas-status-final", children: pendingTranscript })) : (_jsx("span", { className: "sui-canvas-status-listening", children: "Listening... describe what you want to build" })) })), !isListening && !isGenerating && (_jsx("input", { ref: textInputRef, type: "text", className: "sui-canvas-text-input", placeholder: "Type what to build...", value: textInput, onChange: (e) => setTextInput(e.target.value), onKeyDown: handleTextSubmit, disabled: isGenerating }))] }), _jsxs("div", { className: "sui-canvas-bar-right", children: [canUndo && (_jsx("button", { type: "button", className: "sui-canvas-action", onClick: undo, title: "Undo (Cmd+Z)", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("polyline", { points: "1 4 1 10 7 10" }), _jsx("path", { d: "M3.51 15a9 9 0 1 0 2.13-9.36L1 10" })] }) })), canRedo && (_jsx("button", { type: "button", className: "sui-canvas-action", onClick: redo, title: "Redo (Cmd+Shift+Z)", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("polyline", { points: "23 4 23 10 17 10" }), _jsx("path", { d: "M20.49 15a9 9 0 1 1-2.13-9.36L23 10" })] }) })), hasContent && (_jsx("button", { type: "button", className: "sui-canvas-action", onClick: saveStory, title: "Save as story", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }), _jsx("polyline", { points: "17 21 17 13 7 13 7 21" }), _jsx("polyline", { points: "7 3 7 8 15 8" })] }) })), hasContent && (_jsx("button", { type: "button", className: "sui-canvas-action", onClick: clear, title: "Clear canvas", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M3 6h18" }), _jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }), _jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })] }) }))] })] })] }));
|
|
586
|
+
: 'Type a prompt to build interfaces live with your design system components.' }), _jsx("p", { className: "sui-canvas-empty-hint", children: "Try: \"Create a product card with an image, title, price, and buy button\"" })] })), !storyReady && isGenerating && (_jsxs("div", { className: "sui-canvas-progress", children: [_jsx("div", { className: "sui-canvas-progress-spinner" }), _jsx("span", { className: "sui-canvas-progress-text", children: statusText || 'Building...' })] })), storyReady && (_jsxs("div", { className: "sui-canvas-live-wrapper", children: [isGenerating && (_jsxs("div", { className: "sui-canvas-regen-overlay", children: [_jsx("div", { className: "sui-canvas-progress-spinner sui-canvas-progress-spinner--sm" }), _jsx("span", { children: statusText || 'Regenerating...' })] })), _jsx("iframe", { ref: iframeRef, src: iframeSrc, title: "Voice Canvas Preview", className: "sui-canvas-iframe", onLoad: handleIframeLoad }, iframeKey)] })), !isGenerating && errorMessage && (_jsxs("div", { className: "sui-canvas-error", children: [_jsx("span", { children: errorMessage }), _jsx("button", { type: "button", className: "sui-canvas-error-dismiss", onClick: () => setErrorMessage(''), "aria-label": "Dismiss error", children: "\u00D7" })] })), savedMessage && (_jsxs("div", { className: "sui-canvas-saved-toast", children: [_jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("polyline", { points: "20 6 9 17 4 12" }) }), _jsxs("span", { children: ["Saved: ", savedMessage] })] }))] }), statusText && !isGenerating && (_jsx("div", { className: "sui-canvas-status-bar", children: _jsx("span", { className: "sui-canvas-explanation", children: statusText }) })), _jsxs("div", { className: `sui-canvas-bar ${isListening ? 'sui-canvas-bar--active' : ''}`, children: [_jsxs("div", { className: "sui-canvas-bar-left", children: [speechSupported && (_jsxs("button", { type: "button", className: `sui-canvas-mic ${isListening && !isGenerating ? 'sui-canvas-mic--active' : ''}`, onClick: toggleListening, disabled: isGenerating, "aria-label": isListening ? 'Stop voice input' : 'Start voice input', children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }), _jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }), _jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })] }), isListening && !isGenerating && _jsx("span", { className: "sui-canvas-mic-pulse" })] })), (isListening || isGenerating) && (_jsx("div", { className: "sui-canvas-transcript", children: isGenerating ? (_jsx("span", { className: "sui-canvas-status-rendering", children: statusText || 'Building interface...' })) : interimText ? (_jsxs("span", { className: "sui-canvas-status-interim", children: [pendingTranscript ? pendingTranscript + ' ' : '', interimText] })) : pendingTranscript ? (_jsx("span", { className: "sui-canvas-status-final", children: pendingTranscript })) : (_jsx("span", { className: "sui-canvas-status-listening", children: "Listening... describe what you want to build" })) })), !isListening && !isGenerating && (_jsx("input", { ref: textInputRef, type: "text", className: "sui-canvas-text-input", placeholder: "Type what to build...", value: textInput, onChange: (e) => setTextInput(e.target.value), onKeyDown: handleTextSubmit, disabled: isGenerating }))] }), _jsxs("div", { className: "sui-canvas-bar-right", children: [canUndo && (_jsx("button", { type: "button", className: "sui-canvas-action", onClick: undo, title: "Undo (Cmd+Z)", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("polyline", { points: "1 4 1 10 7 10" }), _jsx("path", { d: "M3.51 15a9 9 0 1 0 2.13-9.36L1 10" })] }) })), canRedo && (_jsx("button", { type: "button", className: "sui-canvas-action", onClick: redo, title: "Redo (Cmd+Shift+Z)", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("polyline", { points: "23 4 23 10 17 10" }), _jsx("path", { d: "M20.49 15a9 9 0 1 1-2.13-9.36L23 10" })] }) })), hasContent && (_jsx("button", { type: "button", className: "sui-canvas-action", onClick: saveStory, title: "Save as story", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }), _jsx("polyline", { points: "17 21 17 13 7 13 7 21" }), _jsx("polyline", { points: "7 3 7 8 15 8" })] }) })), hasContent && (_jsx("button", { type: "button", className: "sui-canvas-action", onClick: clear, title: "Clear canvas", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M3 6h18" }), _jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }), _jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })] }) }))] })] })] }));
|
|
596
587
|
});
|
|
@@ -107,12 +107,10 @@ function VoiceCanvas({
|
|
|
107
107
|
// ── Code → iframe bridge ─────────────────────────────────────
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* and the story reads it on mount.
|
|
110
|
+
* Push code to the story preview iframe via postMessage.
|
|
111
|
+
* No localStorage persistence — each session starts clean.
|
|
113
112
|
*/
|
|
114
113
|
const sendCodeToIframe = useCallback((code: string) => {
|
|
115
|
-
try { localStorage.setItem(LS_KEY, code); } catch {}
|
|
116
114
|
if (iframeRef.current?.contentWindow && iframeLoadedRef.current) {
|
|
117
115
|
iframeRef.current.contentWindow.postMessage(
|
|
118
116
|
{ type: 'VOICE_CANVAS_UPDATE', code },
|
|
@@ -212,7 +210,6 @@ function VoiceCanvas({
|
|
|
212
210
|
}
|
|
213
211
|
lastPromptRef.current = transcript;
|
|
214
212
|
setLastPrompt(transcript);
|
|
215
|
-
try { localStorage.setItem(LS_PROMPT_KEY, firstPromptRef.current); } catch {}
|
|
216
213
|
conversationRef.current.push(
|
|
217
214
|
{ role: 'user', content: transcript },
|
|
218
215
|
{ role: 'assistant', content: '[Generated canvas component]' },
|
|
@@ -731,8 +728,9 @@ function VoiceCanvas({
|
|
|
731
728
|
{speechSupported && (
|
|
732
729
|
<button
|
|
733
730
|
type="button"
|
|
734
|
-
className={`sui-canvas-mic ${isListening ? 'sui-canvas-mic--active' : ''}`}
|
|
731
|
+
className={`sui-canvas-mic ${isListening && !isGenerating ? 'sui-canvas-mic--active' : ''}`}
|
|
735
732
|
onClick={toggleListening}
|
|
733
|
+
disabled={isGenerating}
|
|
736
734
|
aria-label={isListening ? 'Stop voice input' : 'Start voice input'}
|
|
737
735
|
>
|
|
738
736
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
@@ -740,7 +738,7 @@ function VoiceCanvas({
|
|
|
740
738
|
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
|
741
739
|
<line x1="12" x2="12" y1="19" y2="22" />
|
|
742
740
|
</svg>
|
|
743
|
-
{isListening && <span className="sui-canvas-mic-pulse" />}
|
|
741
|
+
{isListening && !isGenerating && <span className="sui-canvas-mic-pulse" />}
|
|
744
742
|
</button>
|
|
745
743
|
)}
|
|
746
744
|
|
package/package.json
CHANGED
|
@@ -107,12 +107,10 @@ function VoiceCanvas({
|
|
|
107
107
|
// ── Code → iframe bridge ─────────────────────────────────────
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* and the story reads it on mount.
|
|
110
|
+
* Push code to the story preview iframe via postMessage.
|
|
111
|
+
* No localStorage persistence — each session starts clean.
|
|
113
112
|
*/
|
|
114
113
|
const sendCodeToIframe = useCallback((code: string) => {
|
|
115
|
-
try { localStorage.setItem(LS_KEY, code); } catch {}
|
|
116
114
|
if (iframeRef.current?.contentWindow && iframeLoadedRef.current) {
|
|
117
115
|
iframeRef.current.contentWindow.postMessage(
|
|
118
116
|
{ type: 'VOICE_CANVAS_UPDATE', code },
|
|
@@ -212,7 +210,6 @@ function VoiceCanvas({
|
|
|
212
210
|
}
|
|
213
211
|
lastPromptRef.current = transcript;
|
|
214
212
|
setLastPrompt(transcript);
|
|
215
|
-
try { localStorage.setItem(LS_PROMPT_KEY, firstPromptRef.current); } catch {}
|
|
216
213
|
conversationRef.current.push(
|
|
217
214
|
{ role: 'user', content: transcript },
|
|
218
215
|
{ role: 'assistant', content: '[Generated canvas component]' },
|
|
@@ -731,8 +728,9 @@ function VoiceCanvas({
|
|
|
731
728
|
{speechSupported && (
|
|
732
729
|
<button
|
|
733
730
|
type="button"
|
|
734
|
-
className={`sui-canvas-mic ${isListening ? 'sui-canvas-mic--active' : ''}`}
|
|
731
|
+
className={`sui-canvas-mic ${isListening && !isGenerating ? 'sui-canvas-mic--active' : ''}`}
|
|
735
732
|
onClick={toggleListening}
|
|
733
|
+
disabled={isGenerating}
|
|
736
734
|
aria-label={isListening ? 'Stop voice input' : 'Start voice input'}
|
|
737
735
|
>
|
|
738
736
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
@@ -740,7 +738,7 @@ function VoiceCanvas({
|
|
|
740
738
|
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
|
741
739
|
<line x1="12" x2="12" y1="19" y2="22" />
|
|
742
740
|
</svg>
|
|
743
|
-
{isListening && <span className="sui-canvas-mic-pulse" />}
|
|
741
|
+
{isListening && !isGenerating && <span className="sui-canvas-mic-pulse" />}
|
|
744
742
|
</button>
|
|
745
743
|
)}
|
|
746
744
|
|