sparkecoder 0.1.20 → 0.1.22

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.
Files changed (100) hide show
  1. package/dist/agent/index.d.ts +2 -2
  2. package/dist/agent/index.js +53 -3
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +397 -46
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +2 -1
  7. package/dist/db/index.js.map +1 -1
  8. package/dist/{index-BzedNBK-.d.ts → index-CNwLFGiZ.d.ts} +24 -3
  9. package/dist/index.d.ts +4 -4
  10. package/dist/index.js +392 -41
  11. package/dist/index.js.map +1 -1
  12. package/dist/{schema-CkrIadxa.d.ts → schema-Df7MU3nM.d.ts} +26 -3
  13. package/dist/server/index.js +392 -41
  14. package/dist/server/index.js.map +1 -1
  15. package/dist/tools/index.js.map +1 -1
  16. package/package.json +1 -1
  17. package/web/.next/BUILD_ID +1 -1
  18. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  19. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  20. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  21. package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
  22. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  23. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  24. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  25. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  26. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  35. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  36. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  41. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  42. package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
  43. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
  44. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
  45. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
  46. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  48. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  49. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_1d78db71._.js → 2374f_387a1437._.js} +1 -1
  50. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_378282b1._.js → 2374f_5f58fd73._.js} +1 -1
  51. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_30f9df13._.js → 2374f_65fcfd95._.js} +1 -1
  52. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_8825dcc9._.js → 2374f_741f6b67._.js} +1 -1
  53. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_9bf3c7f3._.js → 2374f_814be2c9._.js} +2 -2
  54. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_5de336d2._.js → 2374f_84859a94._.js} +1 -1
  55. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_bbc99511._.js → 2374f_cfd0137a._.js} +1 -1
  56. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_d94c2b70._.js → 2374f_f1038f7c._.js} +1 -1
  57. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__a984d933._.js → [root-of-the-server]__3ec22171._.js} +2 -2
  58. package/web/.next/standalone/web/.next/server/chunks/ssr/web_96bca05b._.js +1 -1
  59. package/web/.next/standalone/web/.next/server/chunks/ssr/web_c7618534._.js +8 -0
  60. package/web/.next/standalone/web/.next/server/chunks/ssr/web_d7d3e40d._.js +1 -1
  61. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  62. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  63. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  64. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  65. package/web/.next/standalone/web/.next/static/chunks/{55705f91c6cfe307.js → 3bb454ca848ec78e.js} +3 -3
  66. package/web/.next/standalone/web/.next/static/chunks/{5ec82ce8f3aabaf0.js → 5e5b485d77ac0d8f.js} +1 -1
  67. package/web/.next/standalone/web/.next/static/chunks/{c9d8a4b9a763e232.js → beb9625c4a470042.js} +1 -1
  68. package/web/.next/standalone/web/.next/static/chunks/c81c1aec4369c77f.js +5 -0
  69. package/web/.next/standalone/web/.next/static/chunks/cb355fac10c6ad11.css +1 -0
  70. package/web/.next/standalone/web/.next/static/static/chunks/{55705f91c6cfe307.js → 3bb454ca848ec78e.js} +3 -3
  71. package/web/.next/standalone/web/.next/static/static/chunks/{5ec82ce8f3aabaf0.js → 5e5b485d77ac0d8f.js} +1 -1
  72. package/web/.next/standalone/web/.next/static/static/chunks/{c9d8a4b9a763e232.js → beb9625c4a470042.js} +1 -1
  73. package/web/.next/standalone/web/.next/static/static/chunks/c81c1aec4369c77f.js +5 -0
  74. package/web/.next/standalone/web/.next/static/static/chunks/cb355fac10c6ad11.css +1 -0
  75. package/web/.next/standalone/web/src/app/(main)/layout.tsx +2 -2
  76. package/web/.next/standalone/web/src/components/ai-elements/speech-input.tsx +89 -36
  77. package/web/.next/standalone/web/src/components/chat-interface.tsx +354 -38
  78. package/web/.next/standalone/web/src/components/ui/sidebar.tsx +2 -2
  79. package/web/.next/standalone/web/src/lib/api.ts +133 -2
  80. package/web/.next/static/chunks/{55705f91c6cfe307.js → 3bb454ca848ec78e.js} +3 -3
  81. package/web/.next/static/chunks/{5ec82ce8f3aabaf0.js → 5e5b485d77ac0d8f.js} +1 -1
  82. package/web/.next/static/chunks/{c9d8a4b9a763e232.js → beb9625c4a470042.js} +1 -1
  83. package/web/.next/static/chunks/c81c1aec4369c77f.js +5 -0
  84. package/web/.next/static/chunks/cb355fac10c6ad11.css +1 -0
  85. package/web/.next/standalone/web/.next/server/chunks/ssr/web_19b6934c._.js +0 -8
  86. package/web/.next/standalone/web/.next/static/chunks/61d61c75ce7cd4ba.js +0 -5
  87. package/web/.next/standalone/web/.next/static/chunks/d0a69c59b1c0d99c.css +0 -1
  88. package/web/.next/standalone/web/.next/static/static/chunks/61d61c75ce7cd4ba.js +0 -5
  89. package/web/.next/standalone/web/.next/static/static/chunks/d0a69c59b1c0d99c.css +0 -1
  90. package/web/.next/static/chunks/61d61c75ce7cd4ba.js +0 -5
  91. package/web/.next/static/chunks/d0a69c59b1c0d99c.css +0 -1
  92. /package/web/.next/standalone/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_buildManifest.js +0 -0
  93. /package/web/.next/standalone/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_clientMiddlewareManifest.json +0 -0
  94. /package/web/.next/standalone/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_ssgManifest.js +0 -0
  95. /package/web/.next/standalone/web/.next/static/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_buildManifest.js +0 -0
  96. /package/web/.next/standalone/web/.next/static/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_clientMiddlewareManifest.json +0 -0
  97. /package/web/.next/standalone/web/.next/static/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_ssgManifest.js +0 -0
  98. /package/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_buildManifest.js +0 -0
  99. /package/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_clientMiddlewareManifest.json +0 -0
  100. /package/web/.next/static/{ogmHZngrFt0TlARPSetHj → n86r6x1RoUipFp6nLIk-R}/_ssgManifest.js +0 -0
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { Button } from "@/components/ui/button";
4
4
  import { cn } from "@/lib/utils";
5
- import { LoaderIcon, MicIcon, SquareIcon } from "lucide-react";
5
+ import { LoaderIcon, MicIcon, MicOffIcon } from "lucide-react";
6
6
  import {
7
7
  type ComponentProps,
8
8
  useCallback,
@@ -17,6 +17,7 @@ interface SpeechRecognition extends EventTarget {
17
17
  lang: string;
18
18
  start(): void;
19
19
  stop(): void;
20
+ abort(): void;
20
21
  onstart: ((this: SpeechRecognition, ev: Event) => void) | null;
21
22
  onend: ((this: SpeechRecognition, ev: Event) => void) | null;
22
23
  onresult:
@@ -67,8 +68,11 @@ declare global {
67
68
 
68
69
  type SpeechInputMode = "speech-recognition" | "media-recorder" | "none";
69
70
 
70
- export type SpeechInputProps = ComponentProps<typeof Button> & {
71
+ export type SpeechInputProps = Omit<ComponentProps<typeof Button>, 'onClick'> & {
72
+ /** Called with finalized transcript text (confirmed words) */
71
73
  onTranscriptionChange?: (text: string) => void;
74
+ /** Called with interim transcript text as user speaks (live preview) */
75
+ onInterimTranscription?: (text: string) => void;
72
76
  /**
73
77
  * Callback for when audio is recorded using MediaRecorder fallback.
74
78
  * This is called in browsers that don't support the Web Speech API (Firefox, Safari).
@@ -98,19 +102,20 @@ const detectSpeechInputMode = (): SpeechInputMode => {
98
102
  export const SpeechInput = ({
99
103
  className,
100
104
  onTranscriptionChange,
105
+ onInterimTranscription,
101
106
  onAudioRecorded,
102
107
  lang = "en-US",
108
+ disabled,
103
109
  ...props
104
110
  }: SpeechInputProps) => {
105
111
  const [isListening, setIsListening] = useState(false);
106
112
  const [isProcessing, setIsProcessing] = useState(false);
107
113
  const [mode, setMode] = useState<SpeechInputMode>("none");
108
- const [recognition, setRecognition] = useState<SpeechRecognition | null>(
109
- null
110
- );
111
114
  const recognitionRef = useRef<SpeechRecognition | null>(null);
112
115
  const mediaRecorderRef = useRef<MediaRecorder | null>(null);
113
116
  const audioChunksRef = useRef<Blob[]>([]);
117
+ // Track the accumulated final transcript for the current session
118
+ const sessionTranscriptRef = useRef<string>("");
114
119
 
115
120
  // Detect mode on mount
116
121
  useEffect(() => {
@@ -123,9 +128,9 @@ export const SpeechInput = ({
123
128
  return;
124
129
  }
125
130
 
126
- const SpeechRecognition =
131
+ const SpeechRecognitionAPI =
127
132
  window.SpeechRecognition || window.webkitSpeechRecognition;
128
- const speechRecognition = new SpeechRecognition();
133
+ const speechRecognition = new SpeechRecognitionAPI();
129
134
 
130
135
  speechRecognition.continuous = true;
131
136
  speechRecognition.interimResults = true;
@@ -133,41 +138,58 @@ export const SpeechInput = ({
133
138
 
134
139
  speechRecognition.onstart = () => {
135
140
  setIsListening(true);
141
+ sessionTranscriptRef.current = "";
136
142
  };
137
143
 
138
144
  speechRecognition.onend = () => {
139
145
  setIsListening(false);
146
+ // Clear any interim display when stopped
147
+ onInterimTranscription?.("");
140
148
  };
141
149
 
142
150
  speechRecognition.onresult = (event) => {
143
- let finalTranscript = "";
151
+ let interimTranscript = "";
152
+ let newFinalTranscript = "";
144
153
 
145
154
  for (let i = event.resultIndex; i < event.results.length; i++) {
146
155
  const result = event.results[i];
156
+ const transcript = result[0]?.transcript ?? "";
157
+
147
158
  if (result.isFinal) {
148
- finalTranscript += result[0]?.transcript ?? "";
159
+ newFinalTranscript += transcript;
160
+ } else {
161
+ interimTranscript += transcript;
149
162
  }
150
163
  }
151
164
 
152
- if (finalTranscript) {
153
- onTranscriptionChange?.(finalTranscript);
165
+ // If we have new finalized text, add it to the input
166
+ if (newFinalTranscript) {
167
+ sessionTranscriptRef.current += newFinalTranscript;
168
+ onTranscriptionChange?.(newFinalTranscript);
169
+ // Clear interim since it's now final
170
+ onInterimTranscription?.("");
171
+ }
172
+
173
+ // Show interim results as live preview
174
+ if (interimTranscript) {
175
+ onInterimTranscription?.(interimTranscript);
154
176
  }
155
177
  };
156
178
 
157
179
  speechRecognition.onerror = (event) => {
158
180
  console.error("Speech recognition error:", event.error);
159
181
  setIsListening(false);
182
+ onInterimTranscription?.("");
160
183
  };
161
184
 
162
185
  recognitionRef.current = speechRecognition;
163
- setRecognition(speechRecognition);
164
186
 
165
187
  return () => {
166
188
  if (recognitionRef.current) {
167
- recognitionRef.current.stop();
189
+ recognitionRef.current.abort();
168
190
  }
169
191
  };
170
- }, [mode, onTranscriptionChange, lang]);
192
+ }, [mode, lang, onTranscriptionChange, onInterimTranscription]);
171
193
 
172
194
  // Start MediaRecorder recording
173
195
  const startMediaRecorder = useCallback(async () => {
@@ -241,11 +263,16 @@ export const SpeechInput = ({
241
263
  }, []);
242
264
 
243
265
  const toggleListening = useCallback(() => {
244
- if (mode === "speech-recognition" && recognition) {
266
+ if (mode === "speech-recognition" && recognitionRef.current) {
245
267
  if (isListening) {
246
- recognition.stop();
268
+ recognitionRef.current.stop();
247
269
  } else {
248
- recognition.start();
270
+ try {
271
+ recognitionRef.current.start();
272
+ } catch (e) {
273
+ // Recognition might already be started
274
+ console.warn("Speech recognition start error:", e);
275
+ }
249
276
  }
250
277
  } else if (mode === "media-recorder") {
251
278
  if (isListening) {
@@ -254,45 +281,71 @@ export const SpeechInput = ({
254
281
  startMediaRecorder();
255
282
  }
256
283
  }
257
- }, [mode, recognition, isListening, startMediaRecorder, stopMediaRecorder]);
284
+ }, [mode, isListening, startMediaRecorder, stopMediaRecorder]);
258
285
 
259
286
  // Determine if button should be disabled
260
- const isDisabled =
287
+ const isButtonDisabled =
288
+ disabled ||
261
289
  mode === "none" ||
262
- (mode === "speech-recognition" && !recognition) ||
290
+ (mode === "speech-recognition" && !recognitionRef.current) ||
263
291
  (mode === "media-recorder" && !onAudioRecorded) ||
264
292
  isProcessing;
265
293
 
294
+ // Not supported - show disabled mic with tooltip hint
295
+ if (mode === "none") {
296
+ return (
297
+ <Button
298
+ type="button"
299
+ className={cn(
300
+ "relative rounded-full opacity-50 cursor-not-allowed",
301
+ className
302
+ )}
303
+ disabled
304
+ title="Speech recognition not supported in this browser"
305
+ {...props}
306
+ >
307
+ <MicOffIcon className="size-4" />
308
+ </Button>
309
+ );
310
+ }
311
+
266
312
  return (
267
313
  <div className="relative inline-flex items-center justify-center">
268
- {/* Animated pulse rings */}
269
- {isListening &&
270
- [0, 1, 2].map((index) => (
271
- <div
272
- className="absolute inset-0 animate-ping rounded-full border-2 border-red-400/30"
273
- key={index}
274
- style={{
275
- animationDelay: `${index * 0.3}s`,
276
- animationDuration: "2s",
277
- }}
314
+ {/* Animated pulse rings when listening */}
315
+ {isListening && (
316
+ <>
317
+ <span className="absolute inset-0 rounded-full bg-red-500/20 animate-ping" />
318
+ <span
319
+ className="absolute inset-0 rounded-full bg-red-500/10 animate-ping"
320
+ style={{ animationDelay: "0.2s" }}
278
321
  />
279
- ))}
322
+ </>
323
+ )}
280
324
 
281
325
  {/* Main record button */}
282
326
  <Button
327
+ type="button"
283
328
  className={cn(
284
- "relative z-10 rounded-full transition-all duration-300",
329
+ "relative z-10 rounded-full transition-all duration-200",
285
330
  isListening
286
- ? "bg-destructive text-white hover:bg-destructive/80 hover:text-white"
287
- : "bg-primary text-primary-foreground hover:bg-primary/80 hover:text-primary-foreground",
331
+ ? "bg-red-500 text-white hover:bg-red-600 shadow-lg shadow-red-500/25"
332
+ : "hover:bg-muted",
288
333
  className
289
334
  )}
290
- disabled={isDisabled}
335
+ disabled={isButtonDisabled}
291
336
  onClick={toggleListening}
337
+ title={isListening ? "Stop recording" : "Start voice input"}
338
+ variant={isListening ? "default" : "ghost"}
292
339
  {...props}
293
340
  >
294
341
  {isProcessing && <LoaderIcon className="size-4 animate-spin" />}
295
- {!isProcessing && isListening && <SquareIcon className="size-4" />}
342
+ {!isProcessing && isListening && (
343
+ <div className="relative">
344
+ <MicIcon className="size-4" />
345
+ {/* Recording indicator dot */}
346
+ <span className="absolute -top-0.5 -right-0.5 size-2 bg-white rounded-full animate-pulse" />
347
+ </div>
348
+ )}
296
349
  {!(isProcessing || isListening) && <MicIcon className="size-4" />}
297
350
  </Button>
298
351
  </div>