ai-design-system 0.1.100 → 0.1.102

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.
@@ -14,7 +14,9 @@ const meta = {
14
14
  tags: ["autodocs"],
15
15
  parameters: { layout: "fullscreen" },
16
16
  args: {
17
+ onBack: fn(),
17
18
  onClose: fn(),
19
+ onSubmit: fn(),
18
20
  onPublish: fn(),
19
21
  onSceneChange: fn(),
20
22
  },
@@ -1,8 +1,11 @@
1
1
  "use client";
2
2
 
3
3
  import * as React from "react";
4
+ import type { FormEvent } from "react";
4
5
  import { Button } from "@/components/primitives/Button";
5
6
  import { Icon } from "@/components/primitives/Icon";
7
+ import { PromptInput } from "@/components/composites/PromptInput";
8
+ import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
6
9
  import { cn } from "@/lib/utils";
7
10
 
8
11
  export interface SceneItem {
@@ -18,15 +21,26 @@ export interface SceneItem {
18
21
  [key: string]: unknown;
19
22
  }
20
23
 
21
- export interface ScenePlayerProps extends React.HTMLAttributes<HTMLDivElement> {
24
+ export interface ScenePlayerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onSubmit"> {
22
25
  scenes: SceneItem[];
23
26
  title?: string;
24
27
  avatarInitials?: string;
25
28
  avatarColor?: string;
26
29
  initialSceneIndex?: number;
27
30
  isMuted?: boolean;
31
+ onBack?: () => void;
28
32
  onClose?: () => void;
29
- onPublish?: (currentScene: SceneItem) => void;
33
+ placeholder?: string;
34
+ promptValue?: string;
35
+ onPromptValueChange?: (value: string) => void;
36
+ onSubmit?: (
37
+ message: PromptInputMessage,
38
+ event: FormEvent<HTMLFormElement>
39
+ ) => void | Promise<void>;
40
+ loading?: boolean;
41
+ onStop?: () => void;
42
+ enableSpeech?: boolean;
43
+ onPublish?: (currentScene: SceneItem, text?: string) => void;
30
44
  publishPlaceholder?: string;
31
45
  onSceneChange?: (sceneIndex: number, scene: SceneItem) => void;
32
46
  }
@@ -40,15 +54,25 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
40
54
  avatarColor = "bg-indigo-600",
41
55
  initialSceneIndex = 0,
42
56
  isMuted: initialMuted = false,
57
+ onBack,
43
58
  onClose,
59
+ placeholder,
60
+ promptValue,
61
+ onPromptValueChange,
62
+ onSubmit,
63
+ loading = false,
64
+ onStop,
65
+ enableSpeech = true,
44
66
  onPublish,
45
- publishPlaceholder = "Ready to publish...",
67
+ publishPlaceholder = "Ask a question or provide instructions...",
46
68
  onSceneChange,
47
69
  className,
48
70
  ...props
49
71
  },
50
72
  ref
51
73
  ) => {
74
+ const handleBack = onBack || onClose;
75
+
52
76
  const [currentSceneIndex, setCurrentSceneIndex] = React.useState<number>(() => {
53
77
  if (initialSceneIndex >= 0 && initialSceneIndex < scenes.length) {
54
78
  return initialSceneIndex;
@@ -61,6 +85,19 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
61
85
 
62
86
  const activeScene = scenes[currentSceneIndex] || scenes[0];
63
87
 
88
+ const handlePromptSubmit = React.useCallback(
89
+ (message: PromptInputMessage, event: FormEvent<HTMLFormElement>) => {
90
+ if (onSubmit) {
91
+ onSubmit(message, event);
92
+ return;
93
+ }
94
+ if (onPublish) {
95
+ onPublish(activeScene, message.text);
96
+ }
97
+ },
98
+ [onSubmit, onPublish, activeScene]
99
+ );
100
+
64
101
  const handlePrev = React.useCallback(() => {
65
102
  if (!scenes.length) return;
66
103
  setCurrentSceneIndex((prev) => {
@@ -117,7 +154,7 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
117
154
  handleNext();
118
155
  } else if (e.key === "Escape") {
119
156
  e.preventDefault();
120
- onClose?.();
157
+ handleBack?.();
121
158
  } else if (e.key === "m" || e.key === "M") {
122
159
  e.preventDefault();
123
160
  setIsMuted((prev) => !prev);
@@ -126,7 +163,7 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
126
163
 
127
164
  window.addEventListener("keydown", handleKeyDown);
128
165
  return () => window.removeEventListener("keydown", handleKeyDown);
129
- }, [handlePrev, handleNext, onClose]);
166
+ }, [handlePrev, handleNext, handleBack]);
130
167
 
131
168
  if (!scenes || scenes.length === 0) {
132
169
  return (
@@ -137,14 +174,14 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
137
174
  {...props}
138
175
  >
139
176
  <p className="text-white/70 text-sm">No scenes available</p>
140
- {onClose && (
177
+ {handleBack && (
141
178
  <Button
142
179
  variant="ghost"
143
- onClick={onClose}
180
+ onClick={handleBack}
144
181
  className="mt-4 text-white hover:bg-white/20"
145
- data-testid="btn-close"
182
+ data-testid="btn-back"
146
183
  >
147
- Close
184
+ Back
148
185
  </Button>
149
186
  )}
150
187
  </div>
@@ -183,8 +220,22 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
183
220
  </div>
184
221
 
185
222
  {/* Story Header Overlay */}
186
- <div className="flex items-center justify-between px-4 py-2.5 z-20 shrink-0">
187
- <div className="flex items-center gap-2.5 min-w-0">
223
+ <div className="flex items-center justify-between px-3 py-2.5 z-20 shrink-0">
224
+ <div className="flex items-center gap-2 min-w-0">
225
+ {/* Back Button */}
226
+ {handleBack && (
227
+ <Button
228
+ variant="ghost"
229
+ size="icon"
230
+ data-testid="btn-back"
231
+ aria-label="Back"
232
+ onClick={handleBack}
233
+ className="h-8 w-8 rounded-full bg-black/40 hover:bg-white/20 text-white p-0 cursor-pointer shrink-0"
234
+ >
235
+ <Icon name="arrow-left" className="w-5 h-5 text-white" />
236
+ </Button>
237
+ )}
238
+
188
239
  <div
189
240
  className={cn(
190
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",
@@ -215,20 +266,6 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
215
266
  >
216
267
  <Icon name={isMuted ? "volume-x" : "volume-2"} className="w-4 h-4" />
217
268
  </Button>
218
-
219
- {/* Close Button */}
220
- {onClose && (
221
- <Button
222
- variant="ghost"
223
- size="icon"
224
- data-testid="btn-close"
225
- aria-label="Close scenes view"
226
- onClick={onClose}
227
- className="h-8 w-8 rounded-full bg-black/40 hover:bg-white/20 text-white p-0 cursor-pointer"
228
- >
229
- <Icon name="x" className="w-4 h-4" />
230
- </Button>
231
- )}
232
269
  </div>
233
270
  </div>
234
271
 
@@ -265,21 +302,18 @@ export const ScenePlayer = React.forwardRef<HTMLDivElement, ScenePlayerProps>(
265
302
  </div>
266
303
  </div>
267
304
 
268
- {/* Bottom Action Bar */}
269
- <div className="flex items-center gap-3 px-4 py-3.5 bg-black z-20 shrink-0">
270
- <div className="flex-1 h-11 rounded-full bg-white/15 border border-white/25 px-4 flex items-center text-sm text-white/65 select-none truncate">
271
- {publishPlaceholder}
272
- </div>
273
- <Button
274
- variant="ghost"
275
- size="icon"
276
- data-testid="btn-publish-scene"
277
- aria-label="Publish scene"
278
- onClick={() => onPublish?.(activeScene)}
279
- className="w-11 h-11 rounded-full bg-emerald-500 hover:bg-emerald-600 text-white shrink-0 p-0 shadow-md cursor-pointer transition-transform active:scale-95"
280
- >
281
- <Icon name="send" className="w-4 h-4 text-white" />
282
- </Button>
305
+ {/* 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">
307
+ <PromptInput
308
+ placeholder={placeholder || publishPlaceholder}
309
+ value={promptValue}
310
+ onChange={onPromptValueChange}
311
+ onSubmit={handlePromptSubmit}
312
+ loading={loading}
313
+ onStop={onStop}
314
+ enableSpeech={enableSpeech}
315
+ className="border border-border bg-background shadow-md overflow-hidden rounded-2xl"
316
+ />
283
317
  </div>
284
318
  </div>
285
319
  );
package/dist/index.cjs CHANGED
@@ -14575,9 +14575,17 @@ var ScenePlayer = React3__namespace.forwardRef(
14575
14575
  avatarColor = "bg-indigo-600",
14576
14576
  initialSceneIndex = 0,
14577
14577
  isMuted: initialMuted = false,
14578
+ onBack,
14578
14579
  onClose,
14580
+ placeholder,
14581
+ promptValue,
14582
+ onPromptValueChange,
14583
+ onSubmit,
14584
+ loading = false,
14585
+ onStop,
14586
+ enableSpeech = true,
14579
14587
  onPublish,
14580
- publishPlaceholder = "Ready to publish...",
14588
+ publishPlaceholder = "Ask a question or provide instructions...",
14581
14589
  onSceneChange,
14582
14590
  className
14583
14591
  } = _b, props = __objRest(_b, [
@@ -14587,13 +14595,22 @@ var ScenePlayer = React3__namespace.forwardRef(
14587
14595
  "avatarColor",
14588
14596
  "initialSceneIndex",
14589
14597
  "isMuted",
14598
+ "onBack",
14590
14599
  "onClose",
14600
+ "placeholder",
14601
+ "promptValue",
14602
+ "onPromptValueChange",
14603
+ "onSubmit",
14604
+ "loading",
14605
+ "onStop",
14606
+ "enableSpeech",
14591
14607
  "onPublish",
14592
14608
  "publishPlaceholder",
14593
14609
  "onSceneChange",
14594
14610
  "className"
14595
14611
  ]);
14596
14612
  var _a2;
14613
+ const handleBack = onBack || onClose;
14597
14614
  const [currentSceneIndex, setCurrentSceneIndex] = React3__namespace.useState(() => {
14598
14615
  if (initialSceneIndex >= 0 && initialSceneIndex < scenes.length) {
14599
14616
  return initialSceneIndex;
@@ -14603,6 +14620,18 @@ var ScenePlayer = React3__namespace.forwardRef(
14603
14620
  const [isMuted, setIsMuted] = React3__namespace.useState(initialMuted);
14604
14621
  const videoRef = React3__namespace.useRef(null);
14605
14622
  const activeScene = scenes[currentSceneIndex] || scenes[0];
14623
+ const handlePromptSubmit = React3__namespace.useCallback(
14624
+ (message, event) => {
14625
+ if (onSubmit) {
14626
+ onSubmit(message, event);
14627
+ return;
14628
+ }
14629
+ if (onPublish) {
14630
+ onPublish(activeScene, message.text);
14631
+ }
14632
+ },
14633
+ [onSubmit, onPublish, activeScene]
14634
+ );
14606
14635
  const handlePrev = React3__namespace.useCallback(() => {
14607
14636
  if (!scenes.length) return;
14608
14637
  setCurrentSceneIndex((prev) => {
@@ -14653,7 +14682,7 @@ var ScenePlayer = React3__namespace.forwardRef(
14653
14682
  handleNext();
14654
14683
  } else if (e.key === "Escape") {
14655
14684
  e.preventDefault();
14656
- onClose == null ? void 0 : onClose();
14685
+ handleBack == null ? void 0 : handleBack();
14657
14686
  } else if (e.key === "m" || e.key === "M") {
14658
14687
  e.preventDefault();
14659
14688
  setIsMuted((prev) => !prev);
@@ -14661,7 +14690,7 @@ var ScenePlayer = React3__namespace.forwardRef(
14661
14690
  };
14662
14691
  window.addEventListener("keydown", handleKeyDown);
14663
14692
  return () => window.removeEventListener("keydown", handleKeyDown);
14664
- }, [handlePrev, handleNext, onClose]);
14693
+ }, [handlePrev, handleNext, handleBack]);
14665
14694
  if (!scenes || scenes.length === 0) {
14666
14695
  return /* @__PURE__ */ jsxRuntime.jsxs(
14667
14696
  "div",
@@ -14672,14 +14701,14 @@ var ScenePlayer = React3__namespace.forwardRef(
14672
14701
  }, props), {
14673
14702
  children: [
14674
14703
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white/70 text-sm", children: "No scenes available" }),
14675
- onClose && /* @__PURE__ */ jsxRuntime.jsx(
14704
+ handleBack && /* @__PURE__ */ jsxRuntime.jsx(
14676
14705
  Button2,
14677
14706
  {
14678
14707
  variant: "ghost",
14679
- onClick: onClose,
14708
+ onClick: handleBack,
14680
14709
  className: "mt-4 text-white hover:bg-white/20",
14681
- "data-testid": "btn-close",
14682
- children: "Close"
14710
+ "data-testid": "btn-back",
14711
+ children: "Back"
14683
14712
  }
14684
14713
  )
14685
14714
  ]
@@ -14717,8 +14746,20 @@ var ScenePlayer = React3__namespace.forwardRef(
14717
14746
  scene.id || `scene-${idx}`
14718
14747
  );
14719
14748
  }) }),
14720
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-2.5 z-20 shrink-0", children: [
14721
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 min-w-0", children: [
14749
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-3 py-2.5 z-20 shrink-0", children: [
14750
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
14751
+ handleBack && /* @__PURE__ */ jsxRuntime.jsx(
14752
+ Button2,
14753
+ {
14754
+ variant: "ghost",
14755
+ size: "icon",
14756
+ "data-testid": "btn-back",
14757
+ "aria-label": "Back",
14758
+ onClick: handleBack,
14759
+ className: "h-8 w-8 rounded-full bg-black/40 hover:bg-white/20 text-white p-0 cursor-pointer shrink-0",
14760
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "arrow-left", className: "w-5 h-5 text-white" })
14761
+ }
14762
+ ),
14722
14763
  /* @__PURE__ */ jsxRuntime.jsx(
14723
14764
  "div",
14724
14765
  {
@@ -14739,32 +14780,18 @@ var ScenePlayer = React3__namespace.forwardRef(
14739
14780
  ] })
14740
14781
  ] })
14741
14782
  ] }),
14742
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
14743
- /* @__PURE__ */ jsxRuntime.jsx(
14744
- Button2,
14745
- {
14746
- variant: "ghost",
14747
- size: "icon",
14748
- "data-testid": "btn-toggle-mute",
14749
- "aria-label": isMuted ? "Unmute audio" : "Mute audio",
14750
- onClick: () => setIsMuted((prev) => !prev),
14751
- className: "h-8 w-8 rounded-full bg-black/40 hover:bg-white/20 text-white p-0 cursor-pointer",
14752
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: isMuted ? "volume-x" : "volume-2", className: "w-4 h-4" })
14753
- }
14754
- ),
14755
- onClose && /* @__PURE__ */ jsxRuntime.jsx(
14756
- Button2,
14757
- {
14758
- variant: "ghost",
14759
- size: "icon",
14760
- "data-testid": "btn-close",
14761
- "aria-label": "Close scenes view",
14762
- onClick: onClose,
14763
- className: "h-8 w-8 rounded-full bg-black/40 hover:bg-white/20 text-white p-0 cursor-pointer",
14764
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", className: "w-4 h-4" })
14765
- }
14766
- )
14767
- ] })
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
+ ) })
14768
14795
  ] }),
14769
14796
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 relative flex items-center justify-center bg-black overflow-hidden min-h-0", children: [
14770
14797
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -14804,21 +14831,19 @@ var ScenePlayer = React3__namespace.forwardRef(
14804
14831
  )
14805
14832
  ] })
14806
14833
  ] }),
14807
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 px-4 py-3.5 bg-black z-20 shrink-0", children: [
14808
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-11 rounded-full bg-white/15 border border-white/25 px-4 flex items-center text-sm text-white/65 select-none truncate", children: publishPlaceholder }),
14809
- /* @__PURE__ */ jsxRuntime.jsx(
14810
- Button2,
14811
- {
14812
- variant: "ghost",
14813
- size: "icon",
14814
- "data-testid": "btn-publish-scene",
14815
- "aria-label": "Publish scene",
14816
- onClick: () => onPublish == null ? void 0 : onPublish(activeScene),
14817
- className: "w-11 h-11 rounded-full bg-emerald-500 hover:bg-emerald-600 text-white shrink-0 p-0 shadow-md cursor-pointer transition-transform active:scale-95",
14818
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "send", className: "w-4 h-4 text-white" })
14819
- }
14820
- )
14821
- ] })
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,
14836
+ {
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"
14845
+ }
14846
+ ) })
14822
14847
  ]
14823
14848
  })
14824
14849
  );