ai-design-system 0.1.102 → 0.1.103

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.
@@ -19,6 +19,8 @@ const meta = {
19
19
  onSubmit: fn(),
20
20
  onPublish: fn(),
21
21
  onSceneChange: fn(),
22
+ onPlayingChange: fn(),
23
+ onDownload: fn(),
22
24
  },
23
25
  } satisfies Meta<typeof ScenePlayer>;
24
26
 
@@ -28,6 +28,11 @@ export interface ScenePlayerProps extends Omit<React.HTMLAttributes<HTMLDivEleme
28
28
  avatarColor?: string;
29
29
  initialSceneIndex?: number;
30
30
  isMuted?: boolean;
31
+ isPlaying?: boolean;
32
+ onPlayingChange?: (isPlaying: boolean) => void;
33
+ videoUrl?: string;
34
+ downloadUrl?: string;
35
+ onDownload?: () => void;
31
36
  onBack?: () => void;
32
37
  onClose?: () => void;
33
38
  placeholder?: string;
@@ -54,6 +59,11 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
54
59
  avatarColor = "bg-indigo-600",
55
60
  initialSceneIndex = 0,
56
61
  isMuted: initialMuted = false,
62
+ isPlaying: isPlayingProp,
63
+ onPlayingChange,
64
+ videoUrl,
65
+ downloadUrl,
66
+ onDownload,
57
67
  onBack,
58
68
  onClose,
59
69
  placeholder,
@@ -80,6 +90,22 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
80
90
  return 0;
81
91
  });
82
92
 
93
+ const [isPlayingState, setIsPlayingState] = React.useState<boolean>(isPlayingProp ?? true);
94
+ const isPlaying = isPlayingProp !== undefined ? isPlayingProp : isPlayingState;
95
+
96
+ const setIsPlaying = React.useCallback(
97
+ (value: boolean | ((prev: boolean) => boolean)) => {
98
+ const nextVal = typeof value === "function" ? value(isPlaying) : value;
99
+ setIsPlayingState(nextVal);
100
+ onPlayingChange?.(nextVal);
101
+ },
102
+ [isPlaying, onPlayingChange]
103
+ );
104
+
105
+ const togglePlay = React.useCallback(() => {
106
+ setIsPlaying((prev) => !prev);
107
+ }, [setIsPlaying]);
108
+
83
109
  const [isMuted, setIsMuted] = React.useState<boolean>(initialMuted);
84
110
  const videoRef = React.useRef<HTMLVideoElement | null>(null);
85
111
 
@@ -117,41 +143,91 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
117
143
  }, [scenes, onSceneChange]);
118
144
 
119
145
  const handleSceneEnded = React.useCallback(() => {
120
- if (!scenes.length) return;
146
+ if (!scenes.length || !isPlaying) return;
121
147
  setCurrentSceneIndex((prev) => {
122
148
  const nextIdx = prev < scenes.length - 1 ? prev + 1 : 0;
123
149
  onSceneChange?.(nextIdx, scenes[nextIdx]);
124
150
  return nextIdx;
125
151
  });
126
- }, [scenes, onSceneChange]);
152
+ }, [scenes, onSceneChange, isPlaying]);
153
+
154
+ const handleDownload = React.useCallback(async () => {
155
+ if (onDownload) {
156
+ onDownload();
157
+ return;
158
+ }
159
+ const targetUrl = downloadUrl || videoUrl || activeScene?.scene_clip_url;
160
+ if (!targetUrl) return;
127
161
 
128
- // Autoplay & mute handling
162
+ try {
163
+ const response = await fetch(targetUrl);
164
+ if (!response.ok) throw new Error("Fetch failed");
165
+ const blob = await response.blob();
166
+ const blobUrl = URL.createObjectURL(blob);
167
+ const link = document.createElement("a");
168
+ link.href = blobUrl;
169
+ link.download = targetUrl.split("/").pop()?.split("?")[0] || "video.mp4";
170
+ document.body.appendChild(link);
171
+ link.click();
172
+ document.body.removeChild(link);
173
+ setTimeout(() => URL.revokeObjectURL(blobUrl), 1000);
174
+ } catch {
175
+ const link = document.createElement("a");
176
+ link.href = targetUrl;
177
+ link.download = targetUrl.split("/").pop()?.split("?")[0] || "video.mp4";
178
+ link.target = "_blank";
179
+ link.rel = "noopener noreferrer";
180
+ document.body.appendChild(link);
181
+ link.click();
182
+ document.body.removeChild(link);
183
+ }
184
+ }, [onDownload, downloadUrl, videoUrl, activeScene?.scene_clip_url]);
185
+
186
+ // Playback & mute synchronization
129
187
  React.useEffect(() => {
130
188
  const video = videoRef.current;
131
189
  if (!video) return;
132
190
 
133
191
  video.muted = isMuted;
134
- const playPromise = video.play();
135
- if (playPromise !== undefined) {
136
- playPromise.catch(() => {
137
- if (!isMuted) {
138
- video.muted = true;
139
- setIsMuted(true);
140
- video.play().catch(() => {});
141
- }
142
- });
192
+
193
+ if (isPlaying) {
194
+ const playPromise = video.play();
195
+ if (playPromise !== undefined) {
196
+ playPromise.catch(() => {
197
+ if (!isMuted) {
198
+ video.muted = true;
199
+ setIsMuted(true);
200
+ video.play().catch(() => {});
201
+ }
202
+ });
203
+ }
204
+ } else {
205
+ video.pause();
143
206
  }
144
- }, [activeScene?.scene_clip_url, isMuted]);
207
+ }, [activeScene?.scene_clip_url, isMuted, isPlaying]);
145
208
 
146
- // Keyboard navigation
209
+ // Keyboard navigation & playback controls
147
210
  React.useEffect(() => {
148
211
  const handleKeyDown = (e: KeyboardEvent) => {
212
+ const target = e.target as HTMLElement | null;
213
+ if (
214
+ target &&
215
+ (target.tagName === "INPUT" ||
216
+ target.tagName === "TEXTAREA" ||
217
+ target.isContentEditable)
218
+ ) {
219
+ return;
220
+ }
221
+
149
222
  if (e.key === "ArrowLeft") {
150
223
  e.preventDefault();
151
224
  handlePrev();
152
- } else if (e.key === "ArrowRight" || e.key === " ") {
225
+ } else if (e.key === "ArrowRight") {
153
226
  e.preventDefault();
154
227
  handleNext();
228
+ } else if (e.key === " " || e.key === "k" || e.key === "K") {
229
+ e.preventDefault();
230
+ togglePlay();
155
231
  } else if (e.key === "Escape") {
156
232
  e.preventDefault();
157
233
  handleBack?.();
@@ -163,7 +239,7 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
163
239
 
164
240
  window.addEventListener("keydown", handleKeyDown);
165
241
  return () => window.removeEventListener("keydown", handleKeyDown);
166
- }, [handlePrev, handleNext, handleBack]);
242
+ }, [handlePrev, handleNext, handleBack, togglePlay]);
167
243
 
168
244
  if (!scenes || scenes.length === 0) {
169
245
  return (
@@ -236,14 +312,6 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
236
312
  </Button>
237
313
  )}
238
314
 
239
- <div
240
- className={cn(
241
- "w-9 h-9 rounded-full flex items-center justify-center font-bold text-sm text-white shrink-0 ring-1.5 ring-white/80 overflow-hidden",
242
- avatarColor
243
- )}
244
- >
245
- {avatarInitials}
246
- </div>
247
315
  <div className="flex flex-col min-w-0 justify-center">
248
316
  <span className="font-bold text-sm text-white truncate max-w-[200px]">
249
317
  {title}
@@ -255,6 +323,18 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
255
323
  </div>
256
324
 
257
325
  <div className="flex items-center gap-2 shrink-0">
326
+ {/* Play/Pause Toggle Button */}
327
+ <Button
328
+ variant="ghost"
329
+ size="icon"
330
+ data-testid="btn-toggle-play"
331
+ aria-label={isPlaying ? "Pause scene" : "Play scene"}
332
+ onClick={togglePlay}
333
+ className="h-8 w-8 rounded-full bg-black/40 hover:bg-white/20 text-white p-0 cursor-pointer"
334
+ >
335
+ <Icon name={isPlaying ? "pause" : "play"} className="w-4 h-4" />
336
+ </Button>
337
+
258
338
  {/* Mute/Unmute Toggle Button */}
259
339
  <Button
260
340
  variant="ghost"
@@ -266,6 +346,18 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
266
346
  >
267
347
  <Icon name={isMuted ? "volume-x" : "volume-2"} className="w-4 h-4" />
268
348
  </Button>
349
+
350
+ {/* Download Video Button */}
351
+ <Button
352
+ variant="ghost"
353
+ size="icon"
354
+ data-testid="btn-download"
355
+ aria-label="Download video"
356
+ onClick={handleDownload}
357
+ className="h-8 w-8 rounded-full bg-black/40 hover:bg-white/20 text-white p-0 cursor-pointer"
358
+ >
359
+ <Icon name="download" className="w-4 h-4" />
360
+ </Button>
269
361
  </div>
270
362
  </div>
271
363
 
@@ -303,7 +395,18 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
303
395
  </div>
304
396
 
305
397
  {/* Bottom PromptInput Composite from Design System */}
306
- <div className="p-3 bg-gradient-to-t from-black via-black/90 to-transparent pt-4 z-20 shrink-0">
398
+ <div
399
+ className="p-3 bg-gradient-to-t from-black via-black/90 to-transparent pt-4 z-20 shrink-0"
400
+ onFocusCapture={() => {
401
+ setIsPlaying(false);
402
+ }}
403
+ onClickCapture={() => {
404
+ setIsPlaying(false);
405
+ }}
406
+ onTouchStartCapture={() => {
407
+ setIsPlaying(false);
408
+ }}
409
+ >
307
410
  <PromptInput
308
411
  placeholder={placeholder || publishPlaceholder}
309
412
  value={promptValue}
package/dist/index.cjs CHANGED
@@ -1026,6 +1026,14 @@ var defaultIcons = {
1026
1026
  viewBox: "0 0 24 24",
1027
1027
  path: "M8 5v14l11-7z"
1028
1028
  },
1029
+ "pause": {
1030
+ name: "pause",
1031
+ viewBox: "0 0 24 24",
1032
+ elements: [
1033
+ { type: "rect", attrs: { x: "14", y: "4", width: "4", height: "16", rx: "1" } },
1034
+ { type: "rect", attrs: { x: "6", y: "4", width: "4", height: "16", rx: "1" } }
1035
+ ]
1036
+ },
1029
1037
  "file-text": {
1030
1038
  name: "file-text",
1031
1039
  viewBox: "0 0 24 24",
@@ -14575,6 +14583,11 @@ var ScenePlayer = React3__namespace.forwardRef(
14575
14583
  avatarColor = "bg-indigo-600",
14576
14584
  initialSceneIndex = 0,
14577
14585
  isMuted: initialMuted = false,
14586
+ isPlaying: isPlayingProp,
14587
+ onPlayingChange,
14588
+ videoUrl,
14589
+ downloadUrl,
14590
+ onDownload,
14578
14591
  onBack,
14579
14592
  onClose,
14580
14593
  placeholder,
@@ -14595,6 +14608,11 @@ var ScenePlayer = React3__namespace.forwardRef(
14595
14608
  "avatarColor",
14596
14609
  "initialSceneIndex",
14597
14610
  "isMuted",
14611
+ "isPlaying",
14612
+ "onPlayingChange",
14613
+ "videoUrl",
14614
+ "downloadUrl",
14615
+ "onDownload",
14598
14616
  "onBack",
14599
14617
  "onClose",
14600
14618
  "placeholder",
@@ -14617,6 +14635,19 @@ var ScenePlayer = React3__namespace.forwardRef(
14617
14635
  }
14618
14636
  return 0;
14619
14637
  });
14638
+ const [isPlayingState, setIsPlayingState] = React3__namespace.useState(isPlayingProp != null ? isPlayingProp : true);
14639
+ const isPlaying = isPlayingProp !== void 0 ? isPlayingProp : isPlayingState;
14640
+ const setIsPlaying = React3__namespace.useCallback(
14641
+ (value) => {
14642
+ const nextVal = typeof value === "function" ? value(isPlaying) : value;
14643
+ setIsPlayingState(nextVal);
14644
+ onPlayingChange == null ? void 0 : onPlayingChange(nextVal);
14645
+ },
14646
+ [isPlaying, onPlayingChange]
14647
+ );
14648
+ const togglePlay = React3__namespace.useCallback(() => {
14649
+ setIsPlaying((prev) => !prev);
14650
+ }, [setIsPlaying]);
14620
14651
  const [isMuted, setIsMuted] = React3__namespace.useState(initialMuted);
14621
14652
  const videoRef = React3__namespace.useRef(null);
14622
14653
  const activeScene = scenes[currentSceneIndex] || scenes[0];
@@ -14649,37 +14680,79 @@ var ScenePlayer = React3__namespace.forwardRef(
14649
14680
  });
14650
14681
  }, [scenes, onSceneChange]);
14651
14682
  const handleSceneEnded = React3__namespace.useCallback(() => {
14652
- if (!scenes.length) return;
14683
+ if (!scenes.length || !isPlaying) return;
14653
14684
  setCurrentSceneIndex((prev) => {
14654
14685
  const nextIdx = prev < scenes.length - 1 ? prev + 1 : 0;
14655
14686
  onSceneChange == null ? void 0 : onSceneChange(nextIdx, scenes[nextIdx]);
14656
14687
  return nextIdx;
14657
14688
  });
14658
- }, [scenes, onSceneChange]);
14689
+ }, [scenes, onSceneChange, isPlaying]);
14690
+ const handleDownload = React3__namespace.useCallback(async () => {
14691
+ var _a3, _b2;
14692
+ if (onDownload) {
14693
+ onDownload();
14694
+ return;
14695
+ }
14696
+ const targetUrl = downloadUrl || videoUrl || (activeScene == null ? void 0 : activeScene.scene_clip_url);
14697
+ if (!targetUrl) return;
14698
+ try {
14699
+ const response = await fetch(targetUrl);
14700
+ if (!response.ok) throw new Error("Fetch failed");
14701
+ const blob = await response.blob();
14702
+ const blobUrl = URL.createObjectURL(blob);
14703
+ const link = document.createElement("a");
14704
+ link.href = blobUrl;
14705
+ link.download = ((_a3 = targetUrl.split("/").pop()) == null ? void 0 : _a3.split("?")[0]) || "video.mp4";
14706
+ document.body.appendChild(link);
14707
+ link.click();
14708
+ document.body.removeChild(link);
14709
+ setTimeout(() => URL.revokeObjectURL(blobUrl), 1e3);
14710
+ } catch (e) {
14711
+ const link = document.createElement("a");
14712
+ link.href = targetUrl;
14713
+ link.download = ((_b2 = targetUrl.split("/").pop()) == null ? void 0 : _b2.split("?")[0]) || "video.mp4";
14714
+ link.target = "_blank";
14715
+ link.rel = "noopener noreferrer";
14716
+ document.body.appendChild(link);
14717
+ link.click();
14718
+ document.body.removeChild(link);
14719
+ }
14720
+ }, [onDownload, downloadUrl, videoUrl, activeScene == null ? void 0 : activeScene.scene_clip_url]);
14659
14721
  React3__namespace.useEffect(() => {
14660
14722
  const video = videoRef.current;
14661
14723
  if (!video) return;
14662
14724
  video.muted = isMuted;
14663
- const playPromise = video.play();
14664
- if (playPromise !== void 0) {
14665
- playPromise.catch(() => {
14666
- if (!isMuted) {
14667
- video.muted = true;
14668
- setIsMuted(true);
14669
- video.play().catch(() => {
14670
- });
14671
- }
14672
- });
14725
+ if (isPlaying) {
14726
+ const playPromise = video.play();
14727
+ if (playPromise !== void 0) {
14728
+ playPromise.catch(() => {
14729
+ if (!isMuted) {
14730
+ video.muted = true;
14731
+ setIsMuted(true);
14732
+ video.play().catch(() => {
14733
+ });
14734
+ }
14735
+ });
14736
+ }
14737
+ } else {
14738
+ video.pause();
14673
14739
  }
14674
- }, [activeScene == null ? void 0 : activeScene.scene_clip_url, isMuted]);
14740
+ }, [activeScene == null ? void 0 : activeScene.scene_clip_url, isMuted, isPlaying]);
14675
14741
  React3__namespace.useEffect(() => {
14676
14742
  const handleKeyDown = (e) => {
14743
+ const target = e.target;
14744
+ if (target && (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable)) {
14745
+ return;
14746
+ }
14677
14747
  if (e.key === "ArrowLeft") {
14678
14748
  e.preventDefault();
14679
14749
  handlePrev();
14680
- } else if (e.key === "ArrowRight" || e.key === " ") {
14750
+ } else if (e.key === "ArrowRight") {
14681
14751
  e.preventDefault();
14682
14752
  handleNext();
14753
+ } else if (e.key === " " || e.key === "k" || e.key === "K") {
14754
+ e.preventDefault();
14755
+ togglePlay();
14683
14756
  } else if (e.key === "Escape") {
14684
14757
  e.preventDefault();
14685
14758
  handleBack == null ? void 0 : handleBack();
@@ -14690,7 +14763,7 @@ var ScenePlayer = React3__namespace.forwardRef(
14690
14763
  };
14691
14764
  window.addEventListener("keydown", handleKeyDown);
14692
14765
  return () => window.removeEventListener("keydown", handleKeyDown);
14693
- }, [handlePrev, handleNext, handleBack]);
14766
+ }, [handlePrev, handleNext, handleBack, togglePlay]);
14694
14767
  if (!scenes || scenes.length === 0) {
14695
14768
  return /* @__PURE__ */ jsxRuntime.jsxs(
14696
14769
  "div",
@@ -14760,16 +14833,6 @@ var ScenePlayer = React3__namespace.forwardRef(
14760
14833
  children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "arrow-left", className: "w-5 h-5 text-white" })
14761
14834
  }
14762
14835
  ),
14763
- /* @__PURE__ */ jsxRuntime.jsx(
14764
- "div",
14765
- {
14766
- className: cn(
14767
- "w-9 h-9 rounded-full flex items-center justify-center font-bold text-sm text-white shrink-0 ring-1.5 ring-white/80 overflow-hidden",
14768
- avatarColor
14769
- ),
14770
- children: avatarInitials
14771
- }
14772
- ),
14773
14836
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0 justify-center", children: [
14774
14837
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold text-sm text-white truncate max-w-[200px]", children: title }),
14775
14838
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/75 truncate", children: [
@@ -14780,18 +14843,44 @@ var ScenePlayer = React3__namespace.forwardRef(
14780
14843
  ] })
14781
14844
  ] })
14782
14845
  ] }),
14783
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
14784
- Button2,
14785
- {
14786
- variant: "ghost",
14787
- size: "icon",
14788
- "data-testid": "btn-toggle-mute",
14789
- "aria-label": isMuted ? "Unmute audio" : "Mute audio",
14790
- onClick: () => setIsMuted((prev) => !prev),
14791
- className: "h-8 w-8 rounded-full bg-black/40 hover:bg-white/20 text-white p-0 cursor-pointer",
14792
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: isMuted ? "volume-x" : "volume-2", className: "w-4 h-4" })
14793
- }
14794
- ) })
14846
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
14847
+ /* @__PURE__ */ jsxRuntime.jsx(
14848
+ Button2,
14849
+ {
14850
+ variant: "ghost",
14851
+ size: "icon",
14852
+ "data-testid": "btn-toggle-play",
14853
+ "aria-label": isPlaying ? "Pause scene" : "Play scene",
14854
+ onClick: togglePlay,
14855
+ className: "h-8 w-8 rounded-full bg-black/40 hover:bg-white/20 text-white p-0 cursor-pointer",
14856
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: isPlaying ? "pause" : "play", className: "w-4 h-4" })
14857
+ }
14858
+ ),
14859
+ /* @__PURE__ */ jsxRuntime.jsx(
14860
+ Button2,
14861
+ {
14862
+ variant: "ghost",
14863
+ size: "icon",
14864
+ "data-testid": "btn-toggle-mute",
14865
+ "aria-label": isMuted ? "Unmute audio" : "Mute audio",
14866
+ onClick: () => setIsMuted((prev) => !prev),
14867
+ className: "h-8 w-8 rounded-full bg-black/40 hover:bg-white/20 text-white p-0 cursor-pointer",
14868
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: isMuted ? "volume-x" : "volume-2", className: "w-4 h-4" })
14869
+ }
14870
+ ),
14871
+ /* @__PURE__ */ jsxRuntime.jsx(
14872
+ Button2,
14873
+ {
14874
+ variant: "ghost",
14875
+ size: "icon",
14876
+ "data-testid": "btn-download",
14877
+ "aria-label": "Download video",
14878
+ onClick: handleDownload,
14879
+ className: "h-8 w-8 rounded-full bg-black/40 hover:bg-white/20 text-white p-0 cursor-pointer",
14880
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "download", className: "w-4 h-4" })
14881
+ }
14882
+ )
14883
+ ] })
14795
14884
  ] }),
14796
14885
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 relative flex items-center justify-center bg-black overflow-hidden min-h-0", children: [
14797
14886
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -14831,19 +14920,34 @@ var ScenePlayer = React3__namespace.forwardRef(
14831
14920
  )
14832
14921
  ] })
14833
14922
  ] }),
14834
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 bg-gradient-to-t from-black via-black/90 to-transparent pt-4 z-20 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
14835
- PromptInput2,
14923
+ /* @__PURE__ */ jsxRuntime.jsx(
14924
+ "div",
14836
14925
  {
14837
- placeholder: placeholder || publishPlaceholder,
14838
- value: promptValue,
14839
- onChange: onPromptValueChange,
14840
- onSubmit: handlePromptSubmit,
14841
- loading,
14842
- onStop,
14843
- enableSpeech,
14844
- className: "border border-border bg-background shadow-md overflow-hidden rounded-2xl"
14926
+ className: "p-3 bg-gradient-to-t from-black via-black/90 to-transparent pt-4 z-20 shrink-0",
14927
+ onFocusCapture: () => {
14928
+ setIsPlaying(false);
14929
+ },
14930
+ onClickCapture: () => {
14931
+ setIsPlaying(false);
14932
+ },
14933
+ onTouchStartCapture: () => {
14934
+ setIsPlaying(false);
14935
+ },
14936
+ children: /* @__PURE__ */ jsxRuntime.jsx(
14937
+ PromptInput2,
14938
+ {
14939
+ placeholder: placeholder || publishPlaceholder,
14940
+ value: promptValue,
14941
+ onChange: onPromptValueChange,
14942
+ onSubmit: handlePromptSubmit,
14943
+ loading,
14944
+ onStop,
14945
+ enableSpeech,
14946
+ className: "border border-border bg-background shadow-md overflow-hidden rounded-2xl"
14947
+ }
14948
+ )
14845
14949
  }
14846
- ) })
14950
+ )
14847
14951
  ]
14848
14952
  })
14849
14953
  );