@tpitre/story-ui 4.16.5 → 4.16.7
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/canvasSave.d.ts.map +1 -1
- package/dist/mcp-server/routes/canvasSave.js +14 -5
- package/dist/templates/StoryUI/voice/VoiceCanvas.d.ts.map +1 -1
- package/dist/templates/StoryUI/voice/VoiceCanvas.js +23 -0
- package/dist/templates/StoryUI/voice/VoiceCanvas.tsx +23 -0
- package/package.json +1 -1
- package/templates/StoryUI/voice/VoiceCanvas.tsx +23 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvasSave.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/canvasSave.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AA0I5C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"canvasSave.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/canvasSave.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AA0I5C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAkDzF;AAID,+DAA+D;AAC/D,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAatD;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,+CAiHlE"}
|
|
@@ -127,7 +127,11 @@ ${jsx}
|
|
|
127
127
|
*/
|
|
128
128
|
export function jsxCodeToStory(jsxCode, title, importPath) {
|
|
129
129
|
// Remove the render(<Canvas />) call at the end
|
|
130
|
-
|
|
130
|
+
let cleanCode = jsxCode.replace(/\nrender\s*\(<Canvas\s*\/>\);?\s*$/, '').trim();
|
|
131
|
+
// The canvas code is generated for react-live where `React` is a global.
|
|
132
|
+
// Replace `React.useState(...)` → `useState(...)` etc. so the saved story
|
|
133
|
+
// works with named imports and doesn't need a default React import.
|
|
134
|
+
cleanCode = cleanCode.replace(/\bReact\.(useState|useEffect|useCallback|useMemo|useRef|useReducer|useContext|createElement|Fragment)\b/g, '$1');
|
|
131
135
|
// Extract component names used in JSX (uppercase identifiers after '<')
|
|
132
136
|
const tagMatches = cleanCode.match(/(?<=<)([A-Z][a-zA-Z.]*)/g) ?? [];
|
|
133
137
|
const componentSet = new Set();
|
|
@@ -137,14 +141,19 @@ export function jsxCodeToStory(jsxCode, title, importPath) {
|
|
|
137
141
|
componentSet.add(base);
|
|
138
142
|
}
|
|
139
143
|
const sortedComponents = Array.from(componentSet).sort();
|
|
140
|
-
// Detect React hooks used
|
|
141
|
-
const hookNames = ['useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useReducer', 'useContext'];
|
|
144
|
+
// Detect React hooks and utilities used
|
|
145
|
+
const hookNames = ['useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useReducer', 'useContext', 'createElement', 'Fragment'];
|
|
142
146
|
const usedHooks = hookNames.filter(h => new RegExp(`\\b${h}\\b`).test(cleanCode));
|
|
147
|
+
// If the code still references `React.` for anything else, import React as a whole
|
|
148
|
+
const needsReactImport = /\bReact\./.test(cleanCode);
|
|
143
149
|
const lines = [];
|
|
144
|
-
if (sortedComponents.length > 0) {
|
|
150
|
+
if (sortedComponents.length > 0 && importPath) {
|
|
145
151
|
lines.push(`import { ${sortedComponents.join(', ')} } from '${importPath}';`);
|
|
146
152
|
}
|
|
147
|
-
if (
|
|
153
|
+
if (needsReactImport) {
|
|
154
|
+
lines.push(`import React${usedHooks.length > 0 ? `, { ${usedHooks.join(', ')} }` : ''} from 'react';`);
|
|
155
|
+
}
|
|
156
|
+
else if (usedHooks.length > 0) {
|
|
148
157
|
lines.push(`import { ${usedHooks.join(', ')} } from 'react';`);
|
|
149
158
|
}
|
|
150
159
|
lines.push(`import type { Meta, StoryObj } from '@storybook/react';`);
|
|
@@ -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,4FAowBtB,CAAC"}
|
|
@@ -59,6 +59,7 @@ export const VoiceCanvas = React.forwardRef(function VoiceCanvas({ apiBase, prov
|
|
|
59
59
|
const audioCheckRef = useRef(null);
|
|
60
60
|
const audioStreamRef = useRef(null);
|
|
61
61
|
const stopListeningRef = useRef(() => { });
|
|
62
|
+
const startListeningRef = useRef(() => { });
|
|
62
63
|
const currentCodeRef = useRef(currentCode);
|
|
63
64
|
currentCodeRef.current = currentCode;
|
|
64
65
|
// Incremented on every new generation to prevent stale finally blocks from
|
|
@@ -90,6 +91,18 @@ export const VoiceCanvas = React.forwardRef(function VoiceCanvas({ apiBase, prov
|
|
|
90
91
|
// Stamp this generation so stale finally blocks from aborted requests
|
|
91
92
|
// don't clobber the state of a newer in-flight request.
|
|
92
93
|
const genId = ++generationCounterRef.current;
|
|
94
|
+
// Pause voice recognition while the LLM is thinking so the user can
|
|
95
|
+
// talk freely without triggering new requests or aborting this one.
|
|
96
|
+
// The mic resumes automatically when generation completes.
|
|
97
|
+
const wasListening = isListeningRef.current;
|
|
98
|
+
if (wasListening && recognitionRef.current) {
|
|
99
|
+
try {
|
|
100
|
+
recognitionRef.current.stop();
|
|
101
|
+
}
|
|
102
|
+
catch { /* already stopped */ }
|
|
103
|
+
recognitionRef.current = null;
|
|
104
|
+
// Keep isListeningRef.current = true so we know to resume later
|
|
105
|
+
}
|
|
93
106
|
setIsGenerating(true);
|
|
94
107
|
setStatusText('Thinking...');
|
|
95
108
|
setErrorMessage('');
|
|
@@ -179,6 +192,15 @@ export const VoiceCanvas = React.forwardRef(function VoiceCanvas({ apiBase, prov
|
|
|
179
192
|
if (generationCounterRef.current === genId) {
|
|
180
193
|
setIsGenerating(false);
|
|
181
194
|
abortRef.current = null;
|
|
195
|
+
// Resume voice recognition if it was active before generation started.
|
|
196
|
+
// This lets the user keep talking hands-free across multiple edits.
|
|
197
|
+
if (wasListening && isListeningRef.current) {
|
|
198
|
+
setTimeout(() => {
|
|
199
|
+
if (isListeningRef.current && !recognitionRef.current) {
|
|
200
|
+
startListeningRef.current();
|
|
201
|
+
}
|
|
202
|
+
}, 300);
|
|
203
|
+
}
|
|
182
204
|
}
|
|
183
205
|
}
|
|
184
206
|
}, [apiBase, provider, model, storyReady, sendCodeToIframe, onError]);
|
|
@@ -456,6 +478,7 @@ export const VoiceCanvas = React.forwardRef(function VoiceCanvas({ apiBase, prov
|
|
|
456
478
|
setIsListening(false);
|
|
457
479
|
}
|
|
458
480
|
}, [clear, undo, redo, scheduleIntent, saveStory]);
|
|
481
|
+
startListeningRef.current = startListening;
|
|
459
482
|
// ── Voice: stop ────────────────────────────────────────────────
|
|
460
483
|
const stopListening = useCallback(() => {
|
|
461
484
|
isListeningRef.current = false;
|
|
@@ -93,6 +93,7 @@ function VoiceCanvas({
|
|
|
93
93
|
const audioCheckRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
94
94
|
const audioStreamRef = useRef<MediaStream | null>(null);
|
|
95
95
|
const stopListeningRef = useRef<() => void>(() => {});
|
|
96
|
+
const startListeningRef = useRef<() => void>(() => {});
|
|
96
97
|
const currentCodeRef = useRef(currentCode);
|
|
97
98
|
currentCodeRef.current = currentCode;
|
|
98
99
|
// Incremented on every new generation to prevent stale finally blocks from
|
|
@@ -129,6 +130,16 @@ function VoiceCanvas({
|
|
|
129
130
|
// don't clobber the state of a newer in-flight request.
|
|
130
131
|
const genId = ++generationCounterRef.current;
|
|
131
132
|
|
|
133
|
+
// Pause voice recognition while the LLM is thinking so the user can
|
|
134
|
+
// talk freely without triggering new requests or aborting this one.
|
|
135
|
+
// The mic resumes automatically when generation completes.
|
|
136
|
+
const wasListening = isListeningRef.current;
|
|
137
|
+
if (wasListening && recognitionRef.current) {
|
|
138
|
+
try { recognitionRef.current.stop(); } catch { /* already stopped */ }
|
|
139
|
+
recognitionRef.current = null;
|
|
140
|
+
// Keep isListeningRef.current = true so we know to resume later
|
|
141
|
+
}
|
|
142
|
+
|
|
132
143
|
setIsGenerating(true);
|
|
133
144
|
setStatusText('Thinking...');
|
|
134
145
|
setErrorMessage('');
|
|
@@ -227,6 +238,16 @@ function VoiceCanvas({
|
|
|
227
238
|
if (generationCounterRef.current === genId) {
|
|
228
239
|
setIsGenerating(false);
|
|
229
240
|
abortRef.current = null;
|
|
241
|
+
|
|
242
|
+
// Resume voice recognition if it was active before generation started.
|
|
243
|
+
// This lets the user keep talking hands-free across multiple edits.
|
|
244
|
+
if (wasListening && isListeningRef.current) {
|
|
245
|
+
setTimeout(() => {
|
|
246
|
+
if (isListeningRef.current && !recognitionRef.current) {
|
|
247
|
+
startListeningRef.current();
|
|
248
|
+
}
|
|
249
|
+
}, 300);
|
|
250
|
+
}
|
|
230
251
|
}
|
|
231
252
|
}
|
|
232
253
|
}, [apiBase, provider, model, storyReady, sendCodeToIframe, onError]);
|
|
@@ -495,6 +516,8 @@ function VoiceCanvas({
|
|
|
495
516
|
}
|
|
496
517
|
}, [clear, undo, redo, scheduleIntent, saveStory]);
|
|
497
518
|
|
|
519
|
+
startListeningRef.current = startListening;
|
|
520
|
+
|
|
498
521
|
// ── Voice: stop ────────────────────────────────────────────────
|
|
499
522
|
|
|
500
523
|
const stopListening = useCallback(() => {
|
package/package.json
CHANGED
|
@@ -93,6 +93,7 @@ function VoiceCanvas({
|
|
|
93
93
|
const audioCheckRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
94
94
|
const audioStreamRef = useRef<MediaStream | null>(null);
|
|
95
95
|
const stopListeningRef = useRef<() => void>(() => {});
|
|
96
|
+
const startListeningRef = useRef<() => void>(() => {});
|
|
96
97
|
const currentCodeRef = useRef(currentCode);
|
|
97
98
|
currentCodeRef.current = currentCode;
|
|
98
99
|
// Incremented on every new generation to prevent stale finally blocks from
|
|
@@ -129,6 +130,16 @@ function VoiceCanvas({
|
|
|
129
130
|
// don't clobber the state of a newer in-flight request.
|
|
130
131
|
const genId = ++generationCounterRef.current;
|
|
131
132
|
|
|
133
|
+
// Pause voice recognition while the LLM is thinking so the user can
|
|
134
|
+
// talk freely without triggering new requests or aborting this one.
|
|
135
|
+
// The mic resumes automatically when generation completes.
|
|
136
|
+
const wasListening = isListeningRef.current;
|
|
137
|
+
if (wasListening && recognitionRef.current) {
|
|
138
|
+
try { recognitionRef.current.stop(); } catch { /* already stopped */ }
|
|
139
|
+
recognitionRef.current = null;
|
|
140
|
+
// Keep isListeningRef.current = true so we know to resume later
|
|
141
|
+
}
|
|
142
|
+
|
|
132
143
|
setIsGenerating(true);
|
|
133
144
|
setStatusText('Thinking...');
|
|
134
145
|
setErrorMessage('');
|
|
@@ -227,6 +238,16 @@ function VoiceCanvas({
|
|
|
227
238
|
if (generationCounterRef.current === genId) {
|
|
228
239
|
setIsGenerating(false);
|
|
229
240
|
abortRef.current = null;
|
|
241
|
+
|
|
242
|
+
// Resume voice recognition if it was active before generation started.
|
|
243
|
+
// This lets the user keep talking hands-free across multiple edits.
|
|
244
|
+
if (wasListening && isListeningRef.current) {
|
|
245
|
+
setTimeout(() => {
|
|
246
|
+
if (isListeningRef.current && !recognitionRef.current) {
|
|
247
|
+
startListeningRef.current();
|
|
248
|
+
}
|
|
249
|
+
}, 300);
|
|
250
|
+
}
|
|
230
251
|
}
|
|
231
252
|
}
|
|
232
253
|
}, [apiBase, provider, model, storyReady, sendCodeToIframe, onError]);
|
|
@@ -495,6 +516,8 @@ function VoiceCanvas({
|
|
|
495
516
|
}
|
|
496
517
|
}, [clear, undo, redo, scheduleIntent, saveStory]);
|
|
497
518
|
|
|
519
|
+
startListeningRef.current = startListening;
|
|
520
|
+
|
|
498
521
|
// ── Voice: stop ────────────────────────────────────────────────
|
|
499
522
|
|
|
500
523
|
const stopListening = useCallback(() => {
|