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.
- package/components/composites/ScenePlayer/ScenePlayer.stories.tsx +2 -0
- package/components/composites/ScenePlayer/ScenePlayer.tsx +128 -25
- package/dist/index.cjs +152 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +0 -6
- package/dist/index.d.ts +128 -123
- package/dist/index.js +152 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
135
|
-
if (
|
|
136
|
-
playPromise.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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"
|
|
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
|
|
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
|
-
|
|
14664
|
-
|
|
14665
|
-
playPromise
|
|
14666
|
-
|
|
14667
|
-
|
|
14668
|
-
|
|
14669
|
-
|
|
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"
|
|
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.
|
|
14784
|
-
|
|
14785
|
-
|
|
14786
|
-
|
|
14787
|
-
|
|
14788
|
-
|
|
14789
|
-
|
|
14790
|
-
|
|
14791
|
-
|
|
14792
|
-
|
|
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(
|
|
14835
|
-
|
|
14923
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14924
|
+
"div",
|
|
14836
14925
|
{
|
|
14837
|
-
|
|
14838
|
-
|
|
14839
|
-
|
|
14840
|
-
|
|
14841
|
-
|
|
14842
|
-
|
|
14843
|
-
|
|
14844
|
-
|
|
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
|
);
|