@tangle-network/agent-app 0.45.66 → 0.46.2
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/README.md +7 -7
- package/dist/assistant/index.js +3 -3
- package/dist/chat-react/index.js +2 -2
- package/dist/chunk-2RTYQU4W.js +441 -0
- package/dist/chunk-2RTYQU4W.js.map +1 -0
- package/dist/{chunk-23J72UUA.js → chunk-DINGA2MO.js} +94 -560
- package/dist/chunk-DINGA2MO.js.map +1 -0
- package/dist/{chunk-AWM4H5XR.js → chunk-MBKNKAN2.js} +6 -4
- package/dist/{chunk-AWM4H5XR.js.map → chunk-MBKNKAN2.js.map} +1 -1
- package/dist/chunk-YDOQE47G.js +518 -0
- package/dist/chunk-YDOQE47G.js.map +1 -0
- package/dist/studio/generation.d.ts +14 -23
- package/dist/studio/index.d.ts +1 -0
- package/dist/studio/index.js +27 -15
- package/dist/studio/model-options.d.ts +61 -0
- package/dist/studio-react/composer-option-controls.d.ts +140 -0
- package/dist/studio-react/generation-notice.d.ts +18 -0
- package/dist/studio-react/index.d.ts +5 -9
- package/dist/studio-react/index.js +912 -630
- package/dist/studio-react/index.js.map +1 -1
- package/dist/studio-react/studio-composer.d.ts +34 -0
- package/dist/studio-react/studio.css +51 -0
- package/dist/theme/tokens.css +18 -0
- package/dist/web-react/index.js +11 -11
- package/package.json +10 -10
- package/dist/chunk-23J72UUA.js.map +0 -1
- package/dist/chunk-E2CWFUUP.js +0 -250
- package/dist/chunk-E2CWFUUP.js.map +0 -1
- package/dist/chunk-MLG6XKPV.js +0 -44
- package/dist/chunk-MLG6XKPV.js.map +0 -1
- package/dist/studio-react/avatar-composer.d.ts +0 -8
- package/dist/studio-react/composer-hero.d.ts +0 -10
- package/dist/studio-react/composer-shell.d.ts +0 -18
- package/dist/studio-react/image-composer.d.ts +0 -8
- package/dist/studio-react/speech-composer.d.ts +0 -4
- package/dist/studio-react/transcription-composer.d.ts +0 -12
- package/dist/studio-react/video-composer.d.ts +0 -10
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
IMAGE_QUALITIES,
|
|
3
|
-
IMAGE_SIZE_HINT,
|
|
4
|
-
IMAGE_SIZE_PATTERN,
|
|
5
|
-
MAX_IMAGE_COUNT,
|
|
6
|
-
MIN_IMAGE_COUNT,
|
|
7
|
-
VIDEO_ASPECT_RATIOS,
|
|
8
|
-
VIDEO_DURATIONS,
|
|
9
|
-
VIDEO_RESOLUTIONS,
|
|
10
2
|
buildGenerationRequestBody,
|
|
3
|
+
curateComposerModels,
|
|
11
4
|
failedOptimisticGeneration,
|
|
12
5
|
generationError,
|
|
13
6
|
generationStatus,
|
|
14
7
|
generationVaultPath,
|
|
8
|
+
imageToVideoSibling,
|
|
15
9
|
isGenerationType,
|
|
16
10
|
isLocalGeneration,
|
|
17
11
|
latestBatchOf,
|
|
@@ -20,20 +14,30 @@ import {
|
|
|
20
14
|
modelMessage,
|
|
21
15
|
normalizeImageCount,
|
|
22
16
|
optimisticGeneration,
|
|
23
|
-
|
|
17
|
+
optionChoices,
|
|
24
18
|
preferredModelId,
|
|
19
|
+
reconcileOptionValues,
|
|
25
20
|
relativeTime,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
resolveComposerOptions,
|
|
22
|
+
supportsCustomImageSize,
|
|
23
|
+
textToVideoSibling,
|
|
24
|
+
userSafeGenerationMessage,
|
|
25
|
+
validateCustomImageSize
|
|
26
|
+
} from "../chunk-2RTYQU4W.js";
|
|
29
27
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
CheckGlyph,
|
|
29
|
+
ChevronDown,
|
|
30
|
+
OVERLAY_SHADOW,
|
|
31
|
+
POPOVER_OPTION_FOCUS,
|
|
32
|
+
PopoverSurface,
|
|
33
|
+
ProviderLogo,
|
|
34
|
+
usePopover
|
|
35
|
+
} from "../chunk-YDOQE47G.js";
|
|
32
36
|
|
|
33
37
|
// src/studio-react/studio-workspace.tsx
|
|
34
|
-
import { useRef as useRef3, useState as
|
|
38
|
+
import { useRef as useRef3, useState as useState4 } from "react";
|
|
35
39
|
import { useSearchParams } from "react-router";
|
|
36
|
-
import { Button as
|
|
40
|
+
import { Button as Button4 } from "@tangle-network/sandbox-ui/primitives";
|
|
37
41
|
import { Images as Images2 } from "lucide-react";
|
|
38
42
|
|
|
39
43
|
// src/studio-react/use-studio-generations.ts
|
|
@@ -92,274 +96,651 @@ function useStudioGenerations(loaderGenerations, options = {}) {
|
|
|
92
96
|
return { mergedGenerations, latestBatch, onGenerated };
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
// src/studio-react/composer
|
|
96
|
-
import { useEffect as
|
|
97
|
-
import {
|
|
98
|
-
Button,
|
|
99
|
-
Input as Input4,
|
|
100
|
-
Label as Label2,
|
|
101
|
-
Textarea,
|
|
102
|
-
Select,
|
|
103
|
-
SelectContent,
|
|
104
|
-
SelectItem,
|
|
105
|
-
SelectTrigger,
|
|
106
|
-
SelectValue
|
|
107
|
-
} from "@tangle-network/sandbox-ui/primitives";
|
|
108
|
-
import { Sparkles } from "lucide-react";
|
|
99
|
+
// src/studio-react/studio-composer.tsx
|
|
100
|
+
import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
|
|
101
|
+
import { AudioLines, ArrowUp, Image as ImageIcon, Video } from "lucide-react";
|
|
109
102
|
|
|
110
|
-
// src/studio-react/
|
|
111
|
-
import {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
103
|
+
// src/studio-react/composer-option-controls.tsx
|
|
104
|
+
import {
|
|
105
|
+
useCallback as useCallback2,
|
|
106
|
+
useEffect as useEffect2,
|
|
107
|
+
useId,
|
|
108
|
+
useState as useState2
|
|
109
|
+
} from "react";
|
|
110
|
+
import { ImagePlus, Volume2, VolumeX, X } from "lucide-react";
|
|
111
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
112
|
+
var PILL = "inline-flex h-7 flex-none items-center gap-1.5 whitespace-nowrap rounded-full border border-border bg-card px-2.5 text-[12.5px] font-medium text-foreground transition hover:bg-accent";
|
|
113
|
+
var MENU_PANEL = `flex min-w-[184px] flex-col overflow-y-auto rounded-xl border border-border bg-popover p-1 text-popover-foreground ${OVERLAY_SHADOW}`;
|
|
114
|
+
var MENU_HEADER = "px-2.5 pb-1 pt-1.5 text-[10.5px] font-semibold uppercase tracking-[0.08em] text-muted-foreground";
|
|
115
|
+
function menuRowClass(selected) {
|
|
116
|
+
return `flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-[13px] transition ${POPOVER_OPTION_FOCUS} ${selected ? "bg-primary/10 font-medium" : "hover:bg-accent"}`;
|
|
123
117
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
118
|
+
var MAX_FADE = 32;
|
|
119
|
+
function MediaTypeSegments({
|
|
120
|
+
value,
|
|
121
|
+
segments,
|
|
122
|
+
onChange
|
|
123
|
+
}) {
|
|
124
|
+
return /* @__PURE__ */ jsx(
|
|
125
|
+
"div",
|
|
126
|
+
{
|
|
127
|
+
role: "group",
|
|
128
|
+
"aria-label": "Media type",
|
|
129
|
+
className: "flex flex-none items-center gap-0.5 rounded-full border border-border bg-muted p-[3px]",
|
|
130
|
+
children: segments.map(({ type, label, icon: Icon }) => {
|
|
131
|
+
const active = type === value;
|
|
132
|
+
return /* @__PURE__ */ jsxs(
|
|
133
|
+
"button",
|
|
134
|
+
{
|
|
135
|
+
type: "button",
|
|
136
|
+
"aria-pressed": active,
|
|
137
|
+
"aria-label": label,
|
|
138
|
+
title: label,
|
|
139
|
+
onClick: () => onChange(type),
|
|
140
|
+
className: `inline-flex h-7 items-center gap-1.5 whitespace-nowrap rounded-full text-[12.5px] transition ${active ? "bg-card px-2.5 font-medium text-foreground shadow-sm" : "px-2 text-muted-foreground hover:text-foreground"}`,
|
|
141
|
+
children: [
|
|
142
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0", strokeWidth: 1.5 }),
|
|
143
|
+
active && /* @__PURE__ */ jsx("span", { children: label })
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
type
|
|
147
|
+
);
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
function ComposerBand({
|
|
153
|
+
bandRef,
|
|
154
|
+
resetKey,
|
|
133
155
|
children
|
|
134
156
|
}) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
]
|
|
157
|
+
const sync = useCallback2(() => {
|
|
158
|
+
const band = bandRef.current;
|
|
159
|
+
if (!band) return;
|
|
160
|
+
const hiddenStart = band.scrollLeft;
|
|
161
|
+
const hiddenEnd = Math.max(0, band.scrollWidth - band.clientWidth - band.scrollLeft);
|
|
162
|
+
const atStart = hiddenStart > 1;
|
|
163
|
+
const atEnd = hiddenEnd > 1;
|
|
164
|
+
band.dataset.overflow = atStart && atEnd ? "both" : atStart ? "start" : atEnd ? "end" : "none";
|
|
165
|
+
band.style.setProperty("--fade-start", `${atStart ? Math.min(MAX_FADE, hiddenStart) : 0}px`);
|
|
166
|
+
band.style.setProperty("--fade-end", `${atEnd ? Math.min(MAX_FADE, hiddenEnd) : 0}px`);
|
|
167
|
+
}, [bandRef]);
|
|
168
|
+
useEffect2(sync);
|
|
169
|
+
useEffect2(() => {
|
|
170
|
+
const band = bandRef.current;
|
|
171
|
+
window.addEventListener("resize", sync);
|
|
172
|
+
const observer = typeof ResizeObserver === "undefined" || !band ? null : new ResizeObserver(sync);
|
|
173
|
+
if (band) observer?.observe(band);
|
|
174
|
+
return () => {
|
|
175
|
+
window.removeEventListener("resize", sync);
|
|
176
|
+
observer?.disconnect();
|
|
177
|
+
};
|
|
178
|
+
}, [bandRef, sync]);
|
|
179
|
+
useEffect2(() => {
|
|
180
|
+
const band = bandRef.current;
|
|
181
|
+
if (band) band.scrollLeft = 0;
|
|
182
|
+
sync();
|
|
183
|
+
}, [bandRef, resetKey, sync]);
|
|
184
|
+
return /* @__PURE__ */ jsx(
|
|
185
|
+
"div",
|
|
186
|
+
{
|
|
187
|
+
ref: bandRef,
|
|
188
|
+
"data-overflow": "none",
|
|
189
|
+
onScroll: sync,
|
|
190
|
+
className: "studio-band flex min-w-0 flex-1 flex-nowrap items-center gap-2 overflow-x-auto",
|
|
191
|
+
children
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
function useCloseWhenScrolledOut(open, setOpen, triggerRef, bandRef) {
|
|
196
|
+
useEffect2(() => {
|
|
197
|
+
const band = bandRef.current;
|
|
198
|
+
if (!open || !band) return;
|
|
199
|
+
const onScroll = () => {
|
|
200
|
+
const trigger = triggerRef.current;
|
|
201
|
+
if (!trigger) return;
|
|
202
|
+
const anchor = trigger.getBoundingClientRect();
|
|
203
|
+
const box = band.getBoundingClientRect();
|
|
204
|
+
const centre = anchor.left + anchor.width / 2;
|
|
205
|
+
if (centre < box.left || centre > box.right) setOpen(false);
|
|
206
|
+
};
|
|
207
|
+
band.addEventListener("scroll", onScroll);
|
|
208
|
+
return () => band.removeEventListener("scroll", onScroll);
|
|
209
|
+
}, [bandRef, open, setOpen, triggerRef]);
|
|
146
210
|
}
|
|
147
|
-
function
|
|
211
|
+
function OptionPill({
|
|
212
|
+
label,
|
|
148
213
|
value,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
214
|
+
choices,
|
|
215
|
+
onSelect,
|
|
216
|
+
bandRef,
|
|
217
|
+
custom
|
|
152
218
|
}) {
|
|
153
|
-
|
|
154
|
-
|
|
219
|
+
const [open, setOpen] = useState2(false);
|
|
220
|
+
const [customOpen, setCustomOpen] = useState2(false);
|
|
221
|
+
const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen);
|
|
222
|
+
const panelId = useId();
|
|
223
|
+
useCloseWhenScrolledOut(open, setOpen, triggerRef, bandRef);
|
|
224
|
+
const selected = choices.find((choice) => choice.value === value);
|
|
225
|
+
const close = () => {
|
|
226
|
+
setOpen(false);
|
|
227
|
+
setCustomOpen(false);
|
|
228
|
+
};
|
|
229
|
+
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative inline-flex flex-none", children: [
|
|
230
|
+
/* @__PURE__ */ jsxs(
|
|
155
231
|
"button",
|
|
156
232
|
{
|
|
157
233
|
type: "button",
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
234
|
+
...triggerProps,
|
|
235
|
+
"aria-controls": open ? panelId : void 0,
|
|
236
|
+
title: label,
|
|
237
|
+
"aria-label": `${label}: ${selected?.label ?? "not set"}`,
|
|
238
|
+
onClick: () => {
|
|
239
|
+
setCustomOpen(false);
|
|
240
|
+
setOpen(!open);
|
|
241
|
+
},
|
|
242
|
+
className: PILL,
|
|
243
|
+
children: [
|
|
244
|
+
/* @__PURE__ */ jsx("span", { children: selected?.label ?? "\u2014" }),
|
|
245
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 shrink-0 text-muted-foreground" })
|
|
246
|
+
]
|
|
163
247
|
}
|
|
164
248
|
),
|
|
165
|
-
/* @__PURE__ */
|
|
166
|
-
|
|
167
|
-
"button",
|
|
249
|
+
/* @__PURE__ */ jsxs(
|
|
250
|
+
PopoverSurface,
|
|
168
251
|
{
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
252
|
+
open,
|
|
253
|
+
id: panelId,
|
|
254
|
+
role: customOpen ? void 0 : "menu",
|
|
255
|
+
triggerRef,
|
|
256
|
+
panelRef,
|
|
257
|
+
className: MENU_PANEL,
|
|
258
|
+
children: [
|
|
259
|
+
/* @__PURE__ */ jsx("div", { className: MENU_HEADER, children: label }),
|
|
260
|
+
customOpen && custom ? custom.render({ close }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
261
|
+
choices.map((choice) => /* @__PURE__ */ jsxs(
|
|
262
|
+
"button",
|
|
263
|
+
{
|
|
264
|
+
type: "button",
|
|
265
|
+
role: "menuitemradio",
|
|
266
|
+
"aria-checked": choice.value === value,
|
|
267
|
+
onClick: () => {
|
|
268
|
+
onSelect(choice.value);
|
|
269
|
+
close();
|
|
270
|
+
},
|
|
271
|
+
className: menuRowClass(choice.value === value),
|
|
272
|
+
children: [
|
|
273
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: choice.label }),
|
|
274
|
+
choice.value === value && /* @__PURE__ */ jsx(CheckGlyph, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-primary" })
|
|
275
|
+
]
|
|
276
|
+
},
|
|
277
|
+
String(choice.value)
|
|
278
|
+
)),
|
|
279
|
+
custom && /* @__PURE__ */ jsx(
|
|
280
|
+
"button",
|
|
281
|
+
{
|
|
282
|
+
type: "button",
|
|
283
|
+
onClick: () => setCustomOpen(true),
|
|
284
|
+
className: `${menuRowClass(false)} text-muted-foreground`,
|
|
285
|
+
children: custom.label
|
|
286
|
+
}
|
|
287
|
+
)
|
|
288
|
+
] })
|
|
289
|
+
]
|
|
175
290
|
}
|
|
176
291
|
)
|
|
177
292
|
] });
|
|
178
293
|
}
|
|
179
|
-
function
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
294
|
+
function CustomSizeForm({
|
|
295
|
+
initial,
|
|
296
|
+
onApply,
|
|
297
|
+
onCancel
|
|
298
|
+
}) {
|
|
299
|
+
const parsed = /^(\d+)x(\d+)$/.exec(initial ?? "");
|
|
300
|
+
const [width, setWidth] = useState2(parsed?.[1] ?? "");
|
|
301
|
+
const [height, setHeight] = useState2(parsed?.[2] ?? "");
|
|
302
|
+
const [error, setError] = useState2(null);
|
|
303
|
+
const fieldClass = "h-8 w-[74px] rounded-md border border-input bg-background px-2 text-[13px] tabular-nums";
|
|
304
|
+
function apply() {
|
|
305
|
+
const verdict = validateCustomImageSize(Number(width), Number(height));
|
|
306
|
+
if (!verdict.ok) {
|
|
307
|
+
setError(verdict.reason);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
onApply(`${Number(width)}x${Number(height)}`);
|
|
311
|
+
}
|
|
312
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex w-[228px] flex-col gap-2 p-1.5", children: [
|
|
313
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
314
|
+
/* @__PURE__ */ jsx(
|
|
315
|
+
"input",
|
|
316
|
+
{
|
|
317
|
+
type: "number",
|
|
318
|
+
value: width,
|
|
319
|
+
"aria-label": "Custom width",
|
|
320
|
+
onChange: (event) => {
|
|
321
|
+
setWidth(event.target.value);
|
|
322
|
+
setError(null);
|
|
323
|
+
},
|
|
324
|
+
className: fieldClass
|
|
325
|
+
}
|
|
326
|
+
),
|
|
327
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-muted-foreground", children: "\xD7" }),
|
|
328
|
+
/* @__PURE__ */ jsx(
|
|
329
|
+
"input",
|
|
330
|
+
{
|
|
331
|
+
type: "number",
|
|
332
|
+
value: height,
|
|
333
|
+
"aria-label": "Custom height",
|
|
334
|
+
onChange: (event) => {
|
|
335
|
+
setHeight(event.target.value);
|
|
336
|
+
setError(null);
|
|
337
|
+
},
|
|
338
|
+
className: fieldClass
|
|
339
|
+
}
|
|
340
|
+
)
|
|
184
341
|
] }),
|
|
185
|
-
/* @__PURE__ */ jsx("
|
|
342
|
+
error ? /* @__PURE__ */ jsx("p", { className: "text-[12px] text-destructive", children: error }) : /* @__PURE__ */ jsx("p", { className: "text-[12px] text-muted-foreground", children: "Multiples of 16, long edge up to 3840, ratio between 1:3 and 3:1." }),
|
|
343
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1.5", children: [
|
|
344
|
+
/* @__PURE__ */ jsx(
|
|
345
|
+
"button",
|
|
346
|
+
{
|
|
347
|
+
type: "button",
|
|
348
|
+
onClick: onCancel,
|
|
349
|
+
className: "rounded-md px-2.5 py-1 text-[12.5px] font-medium text-muted-foreground transition hover:bg-accent hover:text-foreground",
|
|
350
|
+
children: "Cancel"
|
|
351
|
+
}
|
|
352
|
+
),
|
|
353
|
+
/* @__PURE__ */ jsx(
|
|
354
|
+
"button",
|
|
355
|
+
{
|
|
356
|
+
type: "button",
|
|
357
|
+
onClick: apply,
|
|
358
|
+
className: "rounded-md bg-primary px-2.5 py-1 text-[12.5px] font-medium text-primary-foreground transition hover:opacity-90",
|
|
359
|
+
children: "Apply"
|
|
360
|
+
}
|
|
361
|
+
)
|
|
362
|
+
] })
|
|
186
363
|
] });
|
|
187
364
|
}
|
|
188
|
-
function
|
|
189
|
-
return /* @__PURE__ */
|
|
365
|
+
function AudioTogglePill({ on, onToggle }) {
|
|
366
|
+
return /* @__PURE__ */ jsxs(
|
|
367
|
+
"button",
|
|
368
|
+
{
|
|
369
|
+
type: "button",
|
|
370
|
+
"aria-pressed": on,
|
|
371
|
+
title: "Audio",
|
|
372
|
+
onClick: () => onToggle(!on),
|
|
373
|
+
className: `${PILL} ${on ? "" : "text-muted-foreground"}`,
|
|
374
|
+
children: [
|
|
375
|
+
on ? /* @__PURE__ */ jsx(Volume2, { className: "h-4 w-4 shrink-0", strokeWidth: 1.5 }) : /* @__PURE__ */ jsx(VolumeX, { className: "h-4 w-4 shrink-0", strokeWidth: 1.5 }),
|
|
376
|
+
/* @__PURE__ */ jsx("span", { children: on ? "Audio on" : "Audio off" })
|
|
377
|
+
]
|
|
378
|
+
}
|
|
379
|
+
);
|
|
190
380
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
quality,
|
|
198
|
-
imageCount,
|
|
199
|
-
onSizeChange,
|
|
200
|
-
onQualityChange,
|
|
201
|
-
onImageCountChange
|
|
381
|
+
function ReferencePill({
|
|
382
|
+
url,
|
|
383
|
+
onAttach,
|
|
384
|
+
onRemove,
|
|
385
|
+
pick,
|
|
386
|
+
bandRef
|
|
202
387
|
}) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
388
|
+
const [open, setOpen] = useState2(false);
|
|
389
|
+
const [draft, setDraft] = useState2("");
|
|
390
|
+
const [error, setError] = useState2(null);
|
|
391
|
+
const { containerRef, triggerRef, panelRef } = usePopover(open, setOpen);
|
|
392
|
+
const panelId = useId();
|
|
393
|
+
useCloseWhenScrolledOut(open, setOpen, triggerRef, bandRef);
|
|
394
|
+
if (url) {
|
|
395
|
+
return /* @__PURE__ */ jsxs("div", { className: `${PILL} border-primary bg-primary/10 pr-1.5 text-primary hover:bg-primary/10`, children: [
|
|
396
|
+
/* @__PURE__ */ jsx("img", { src: url, alt: "", className: "h-[18px] w-[18px] shrink-0 rounded-[5px] object-cover" }),
|
|
397
|
+
/* @__PURE__ */ jsx("span", { children: "Reference" }),
|
|
398
|
+
/* @__PURE__ */ jsx(
|
|
399
|
+
"button",
|
|
207
400
|
{
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
className: "
|
|
401
|
+
type: "button",
|
|
402
|
+
"aria-label": "Remove reference image",
|
|
403
|
+
onClick: onRemove,
|
|
404
|
+
className: "ml-0.5 inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full transition hover:bg-primary/20",
|
|
405
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3", strokeWidth: 2 })
|
|
213
406
|
}
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
407
|
+
)
|
|
408
|
+
] });
|
|
409
|
+
}
|
|
410
|
+
function attach() {
|
|
411
|
+
const value = draft.trim();
|
|
412
|
+
if (!/^https?:\/\//i.test(value)) {
|
|
413
|
+
setError("Enter a URL starting with http:// or https://");
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
onAttach(value);
|
|
417
|
+
setDraft("");
|
|
418
|
+
setError(null);
|
|
419
|
+
setOpen(false);
|
|
420
|
+
}
|
|
421
|
+
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative inline-flex flex-none", children: [
|
|
422
|
+
/* @__PURE__ */ jsxs(
|
|
423
|
+
"button",
|
|
219
424
|
{
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
425
|
+
type: "button",
|
|
426
|
+
ref: triggerRef,
|
|
427
|
+
...pick ? {} : { "aria-haspopup": true, "aria-expanded": open, "aria-controls": open ? panelId : void 0 },
|
|
428
|
+
title: "Reference image",
|
|
429
|
+
onClick: () => {
|
|
430
|
+
if (!pick) {
|
|
431
|
+
setOpen(!open);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
void pick().then((picked) => {
|
|
435
|
+
if (picked) onAttach(picked);
|
|
436
|
+
});
|
|
437
|
+
},
|
|
438
|
+
className: PILL,
|
|
439
|
+
children: [
|
|
440
|
+
/* @__PURE__ */ jsx(ImagePlus, { className: "h-4 w-4 shrink-0 text-muted-foreground", strokeWidth: 1.5 }),
|
|
441
|
+
/* @__PURE__ */ jsx("span", { children: "Reference image" })
|
|
442
|
+
]
|
|
224
443
|
}
|
|
225
|
-
)
|
|
444
|
+
),
|
|
445
|
+
!pick && /* @__PURE__ */ jsxs(
|
|
446
|
+
PopoverSurface,
|
|
447
|
+
{
|
|
448
|
+
open,
|
|
449
|
+
id: panelId,
|
|
450
|
+
triggerRef,
|
|
451
|
+
panelRef,
|
|
452
|
+
className: MENU_PANEL,
|
|
453
|
+
children: [
|
|
454
|
+
/* @__PURE__ */ jsx("div", { className: MENU_HEADER, children: "Reference image" }),
|
|
455
|
+
/* @__PURE__ */ jsxs("div", { className: "flex w-[260px] flex-col gap-2 p-1.5", children: [
|
|
456
|
+
/* @__PURE__ */ jsx(
|
|
457
|
+
"input",
|
|
458
|
+
{
|
|
459
|
+
type: "url",
|
|
460
|
+
value: draft,
|
|
461
|
+
"aria-label": "Reference image URL",
|
|
462
|
+
placeholder: "https://\u2026",
|
|
463
|
+
onChange: (event) => {
|
|
464
|
+
setDraft(event.target.value);
|
|
465
|
+
setError(null);
|
|
466
|
+
},
|
|
467
|
+
className: "h-8 w-full rounded-md border border-input bg-background px-2 text-[13px]"
|
|
468
|
+
}
|
|
469
|
+
),
|
|
470
|
+
error && /* @__PURE__ */ jsx("p", { className: "text-[12px] text-destructive", children: error }),
|
|
471
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1.5", children: [
|
|
472
|
+
/* @__PURE__ */ jsx(
|
|
473
|
+
"button",
|
|
474
|
+
{
|
|
475
|
+
type: "button",
|
|
476
|
+
onClick: () => setOpen(false),
|
|
477
|
+
className: "rounded-md px-2.5 py-1 text-[12.5px] font-medium text-muted-foreground transition hover:bg-accent hover:text-foreground",
|
|
478
|
+
children: "Cancel"
|
|
479
|
+
}
|
|
480
|
+
),
|
|
481
|
+
/* @__PURE__ */ jsx(
|
|
482
|
+
"button",
|
|
483
|
+
{
|
|
484
|
+
type: "button",
|
|
485
|
+
onClick: attach,
|
|
486
|
+
className: "rounded-md bg-primary px-2.5 py-1 text-[12.5px] font-medium text-primary-foreground transition hover:opacity-90",
|
|
487
|
+
children: "Attach"
|
|
488
|
+
}
|
|
489
|
+
)
|
|
490
|
+
] })
|
|
491
|
+
] })
|
|
492
|
+
]
|
|
493
|
+
}
|
|
494
|
+
)
|
|
226
495
|
] });
|
|
227
496
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
aspectRatio,
|
|
236
|
-
referenceImageUrl,
|
|
237
|
-
onDurationChange,
|
|
238
|
-
onResolutionChange,
|
|
239
|
-
onAspectRatioChange,
|
|
240
|
-
onReferenceImageUrlChange
|
|
497
|
+
function ModelPill({
|
|
498
|
+
models,
|
|
499
|
+
value,
|
|
500
|
+
displayName,
|
|
501
|
+
provider,
|
|
502
|
+
onSelect,
|
|
503
|
+
bandRef
|
|
241
504
|
}) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
505
|
+
const [open, setOpen] = useState2(false);
|
|
506
|
+
const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen);
|
|
507
|
+
const panelId = useId();
|
|
508
|
+
useCloseWhenScrolledOut(open, setOpen, triggerRef, bandRef);
|
|
509
|
+
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative inline-flex flex-none", children: [
|
|
510
|
+
/* @__PURE__ */ jsxs(
|
|
511
|
+
"button",
|
|
512
|
+
{
|
|
513
|
+
type: "button",
|
|
514
|
+
...triggerProps,
|
|
515
|
+
"aria-controls": open ? panelId : void 0,
|
|
516
|
+
title: "Model",
|
|
517
|
+
"aria-label": `Model: ${displayName}`,
|
|
518
|
+
onClick: () => setOpen(!open),
|
|
519
|
+
className: PILL,
|
|
520
|
+
children: [
|
|
521
|
+
provider && /* @__PURE__ */ jsx(ProviderLogo, { provider, size: 14 }),
|
|
522
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-[168px] truncate", children: displayName }),
|
|
523
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 shrink-0 text-muted-foreground" })
|
|
524
|
+
]
|
|
525
|
+
}
|
|
526
|
+
),
|
|
527
|
+
/* @__PURE__ */ jsxs(
|
|
528
|
+
PopoverSurface,
|
|
529
|
+
{
|
|
530
|
+
open,
|
|
531
|
+
id: panelId,
|
|
532
|
+
role: "menu",
|
|
533
|
+
triggerRef,
|
|
534
|
+
panelRef,
|
|
535
|
+
className: `${MENU_PANEL} max-w-[320px]`,
|
|
536
|
+
children: [
|
|
537
|
+
/* @__PURE__ */ jsx("div", { className: MENU_HEADER, children: "Model" }),
|
|
538
|
+
models.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-2.5 py-2 text-[13px] text-muted-foreground", children: "No models are available for this media type." }),
|
|
539
|
+
models.map((model) => /* @__PURE__ */ jsxs(
|
|
540
|
+
"button",
|
|
541
|
+
{
|
|
542
|
+
type: "button",
|
|
543
|
+
role: "menuitemradio",
|
|
544
|
+
"aria-checked": model.id === value,
|
|
545
|
+
disabled: model.status === "unavailable",
|
|
546
|
+
onClick: () => {
|
|
547
|
+
onSelect(model.id);
|
|
548
|
+
setOpen(false);
|
|
549
|
+
},
|
|
550
|
+
className: `${menuRowClass(model.id === value)} disabled:opacity-50`,
|
|
551
|
+
children: [
|
|
552
|
+
model.provider && /* @__PURE__ */ jsx(ProviderLogo, { provider: model.provider, size: 14 }),
|
|
553
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: model.name || model.id }),
|
|
554
|
+
model.status !== "available" && /* @__PURE__ */ jsx("span", { className: "ml-auto shrink-0 text-[11px] capitalize text-muted-foreground", children: model.status }),
|
|
555
|
+
model.id === value && /* @__PURE__ */ jsx(CheckGlyph, { className: `${model.status === "available" ? "ml-auto " : ""}h-3.5 w-3.5 shrink-0 text-primary` })
|
|
556
|
+
]
|
|
557
|
+
},
|
|
558
|
+
model.id
|
|
559
|
+
))
|
|
560
|
+
]
|
|
561
|
+
}
|
|
562
|
+
)
|
|
247
563
|
] });
|
|
248
564
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
565
|
+
function optionValueLabel(param, value) {
|
|
566
|
+
if (typeof value === "boolean") return value ? "On" : "Off";
|
|
567
|
+
const text = String(value);
|
|
568
|
+
if (param === "n") return `\xD7${text}`;
|
|
569
|
+
if (param === "speed") return `${text}\xD7`;
|
|
570
|
+
if (text === "auto") return "Auto";
|
|
571
|
+
if (param === "duration") return /^\d+(\.\d+)?$/.test(text) ? `${text}s` : text;
|
|
572
|
+
if (param === "size") {
|
|
573
|
+
const size = /^(\d+)x(\d+)$/.exec(text);
|
|
574
|
+
return size ? `${size[1]}\xD7${size[2]}` : text;
|
|
575
|
+
}
|
|
576
|
+
if (param === "resolution" || param === "aspect_ratio") return text;
|
|
577
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
258
578
|
}
|
|
259
579
|
|
|
260
|
-
// src/studio-react/composer
|
|
261
|
-
import { jsx as
|
|
262
|
-
var
|
|
263
|
-
|
|
264
|
-
{
|
|
265
|
-
|
|
266
|
-
type: "image",
|
|
267
|
-
prompt: "A vertical storyboard frame for a product launch teaser \u2014 dark cinematic lighting, sleek studio aesthetic, 9:16 composition."
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
label: "Launch teaser clip",
|
|
271
|
-
type: "video",
|
|
272
|
-
prompt: "A 6-second vertical launch teaser: animated product reveal, dynamic camera move, hero music swell."
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
label: "Scratch voiceover",
|
|
276
|
-
type: "speech",
|
|
277
|
-
prompt: "A confident 12-second scratch voiceover for a product launch teaser: warm tone, conversational pace."
|
|
278
|
-
}
|
|
580
|
+
// src/studio-react/studio-composer.tsx
|
|
581
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
582
|
+
var SEGMENTS = [
|
|
583
|
+
{ type: "image", label: "Image", icon: ImageIcon },
|
|
584
|
+
{ type: "video", label: "Video", icon: Video },
|
|
585
|
+
{ type: "speech", label: "Audio", icon: AudioLines }
|
|
279
586
|
];
|
|
280
|
-
|
|
587
|
+
var PILL_ORDER = {
|
|
588
|
+
image: ["size", "quality", "n"],
|
|
589
|
+
video: ["duration", "resolution", "aspect_ratio", "mode"],
|
|
590
|
+
speech: ["voice", "speed"]
|
|
591
|
+
};
|
|
592
|
+
var PARAM_LABELS = {
|
|
593
|
+
size: "Size",
|
|
594
|
+
quality: "Quality",
|
|
595
|
+
n: "Count",
|
|
596
|
+
duration: "Duration",
|
|
597
|
+
resolution: "Resolution",
|
|
598
|
+
aspect_ratio: "Aspect",
|
|
599
|
+
mode: "Mode",
|
|
600
|
+
voice: "Voice",
|
|
601
|
+
speed: "Speed"
|
|
602
|
+
};
|
|
603
|
+
var SPEED_PRESETS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 4];
|
|
604
|
+
function choicesFor(param, meta) {
|
|
605
|
+
const { min, max } = meta;
|
|
606
|
+
if (param === "speed" && !meta.values && min != null && max != null) {
|
|
607
|
+
return SPEED_PRESETS.filter((speed) => speed >= min && speed <= max);
|
|
608
|
+
}
|
|
609
|
+
return optionChoices(meta);
|
|
610
|
+
}
|
|
611
|
+
function visibleParams(type, options) {
|
|
612
|
+
if (!options) return [];
|
|
613
|
+
return PILL_ORDER[type].filter((param) => {
|
|
614
|
+
const meta = options[param];
|
|
615
|
+
return Boolean(meta) && meta?.supported !== false;
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
function defaultModelId(type, catalog, curated) {
|
|
619
|
+
const preferred = preferredModelId(type, catalog);
|
|
620
|
+
if (preferred && curated.some((model) => model.id === preferred)) return preferred;
|
|
621
|
+
return curated.find((model) => model.status !== "unavailable")?.id ?? curated[0]?.id ?? "";
|
|
622
|
+
}
|
|
623
|
+
function StudioComposer({
|
|
281
624
|
workspaceId,
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
625
|
+
onGenerated,
|
|
626
|
+
variant = "home",
|
|
627
|
+
pickReferenceImage,
|
|
628
|
+
className
|
|
285
629
|
}) {
|
|
286
|
-
const [type, setType] =
|
|
287
|
-
const [prompt, setPrompt] =
|
|
288
|
-
const [
|
|
289
|
-
const [
|
|
290
|
-
const [
|
|
291
|
-
const [
|
|
292
|
-
const [
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const [
|
|
298
|
-
const [
|
|
299
|
-
const [
|
|
300
|
-
const [mediaModelsError, setMediaModelsError] = useState2(null);
|
|
301
|
-
const [selectedModels, setSelectedModels] = useState2({});
|
|
630
|
+
const [type, setType] = useState3("image");
|
|
631
|
+
const [prompt, setPrompt] = useState3("");
|
|
632
|
+
const [catalog, setCatalog] = useState3(null);
|
|
633
|
+
const [catalogLoading, setCatalogLoading] = useState3(false);
|
|
634
|
+
const [catalogError, setCatalogError] = useState3(null);
|
|
635
|
+
const [selectedModels, setSelectedModels] = useState3({});
|
|
636
|
+
const [optionValues, setOptionValues] = useState3({
|
|
637
|
+
image: {},
|
|
638
|
+
video: {},
|
|
639
|
+
speech: {}
|
|
640
|
+
});
|
|
641
|
+
const [referenceImageUrl, setReferenceImageUrl] = useState3(null);
|
|
642
|
+
const [isSubmitting, setIsSubmitting] = useState3(false);
|
|
643
|
+
const [error, setError] = useState3(null);
|
|
302
644
|
const submitLockRef = useRef2(false);
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
const modelsForType = mediaModels?.models[type] ?? [];
|
|
306
|
-
const selectedModel = selectedModels[type] ?? preferredModelId(type, mediaModels) ?? "";
|
|
307
|
-
const selectedModelOption = modelsForType.find((model) => model.id === selectedModel);
|
|
308
|
-
const modelReady = Boolean(selectedModelOption) && selectedModelOption?.status !== "unavailable" && !mediaModelsLoading && !mediaModelsError;
|
|
309
|
-
const hasRequiredInput = Boolean(prompt.trim());
|
|
310
|
-
const canSubmit = Boolean(workspaceId) && modelReady && hasRequiredInput && !isSubmitting;
|
|
311
|
-
useEffect2(() => {
|
|
645
|
+
const bandRef = useRef2(null);
|
|
646
|
+
useEffect3(() => {
|
|
312
647
|
if (!workspaceId) return;
|
|
313
648
|
let cancelled = false;
|
|
314
|
-
|
|
315
|
-
|
|
649
|
+
setCatalogLoading(true);
|
|
650
|
+
setCatalogError(null);
|
|
316
651
|
fetch(`/api/media-models?workspaceId=${encodeURIComponent(workspaceId)}`).then(async (res) => {
|
|
317
652
|
const data = await res.json();
|
|
318
653
|
if (!res.ok) throw new Error(data.error ?? "Could not load media models");
|
|
319
654
|
return data;
|
|
320
655
|
}).then((data) => {
|
|
321
656
|
if (cancelled) return;
|
|
322
|
-
|
|
323
|
-
setSelectedModels((current) => selectedModelsWithDefaults(current, data));
|
|
657
|
+
setCatalog(data);
|
|
324
658
|
}).catch(() => {
|
|
325
659
|
if (cancelled) return;
|
|
326
|
-
|
|
327
|
-
|
|
660
|
+
setCatalog(null);
|
|
661
|
+
setCatalogError("Could not load media models");
|
|
328
662
|
}).finally(() => {
|
|
329
|
-
if (!cancelled)
|
|
663
|
+
if (!cancelled) setCatalogLoading(false);
|
|
330
664
|
});
|
|
331
665
|
return () => {
|
|
332
666
|
cancelled = true;
|
|
333
667
|
};
|
|
334
668
|
}, [workspaceId]);
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
669
|
+
const laneModels = useMemo2(() => catalog?.models[type] ?? [], [catalog, type]);
|
|
670
|
+
const curatedModels = useMemo2(() => curateComposerModels(type, laneModels), [laneModels, type]);
|
|
671
|
+
const retained = selectedModels[type];
|
|
672
|
+
const retainedUsable = retained !== void 0 && (!catalog || Boolean(textToVideoSibling(retained)) || curatedModels.some((model) => model.id === retained && model.status !== "unavailable"));
|
|
673
|
+
const modelId = retainedUsable ? retained : defaultModelId(type, catalog, curatedModels);
|
|
674
|
+
const modelOption = laneModels.find((model) => model.id === modelId);
|
|
675
|
+
const options = useMemo2(
|
|
676
|
+
() => modelId ? resolveComposerOptions({
|
|
677
|
+
type,
|
|
678
|
+
modelId,
|
|
679
|
+
provider: modelOption?.provider,
|
|
680
|
+
catalogOptions: modelOption?.options
|
|
681
|
+
}) : void 0,
|
|
682
|
+
[modelId, modelOption, type]
|
|
683
|
+
);
|
|
684
|
+
useEffect3(() => {
|
|
685
|
+
setOptionValues((current) => {
|
|
686
|
+
const next = reconcileOptionValues(options, current[type], {
|
|
687
|
+
allowCustomSize: supportsCustomImageSize(modelId)
|
|
688
|
+
});
|
|
689
|
+
const unchanged = Object.keys(next).length === Object.keys(current[type]).length && Object.entries(next).every(([key, value]) => current[type][key] === value);
|
|
690
|
+
return unchanged ? current : { ...current, [type]: next };
|
|
691
|
+
});
|
|
692
|
+
}, [modelId, options, type]);
|
|
693
|
+
const values = optionValues[type];
|
|
694
|
+
const params = visibleParams(type, options);
|
|
695
|
+
const audioMeta = type === "video" ? options?.audio : void 0;
|
|
696
|
+
const audioSupported = Boolean(audioMeta) && audioMeta?.supported !== false;
|
|
697
|
+
const unlistedSibling = !modelOption && Boolean(textToVideoSibling(modelId));
|
|
698
|
+
const referenceSupported = type === "video" && Boolean(imageToVideoSibling(modelId) ?? textToVideoSibling(modelId));
|
|
699
|
+
const modelReady = (Boolean(modelOption) || unlistedSibling) && modelOption?.status !== "unavailable" && !catalogLoading && !catalogError;
|
|
700
|
+
const canSubmit = Boolean(workspaceId) && modelReady && Boolean(prompt.trim()) && !isSubmitting;
|
|
701
|
+
function selectModel(id) {
|
|
702
|
+
setSelectedModels((current) => ({ ...current, [type]: id }));
|
|
703
|
+
}
|
|
704
|
+
function chooseModel(id) {
|
|
705
|
+
if (!referenceImageUrl || type !== "video") return selectModel(id);
|
|
706
|
+
const sibling = imageToVideoSibling(id);
|
|
707
|
+
if (sibling) return selectModel(sibling);
|
|
708
|
+
if (!textToVideoSibling(id)) setReferenceImageUrl(null);
|
|
709
|
+
return selectModel(id);
|
|
710
|
+
}
|
|
711
|
+
function setOption(param, value) {
|
|
712
|
+
setOptionValues((current) => ({ ...current, [type]: { ...current[type], [param]: value } }));
|
|
713
|
+
}
|
|
714
|
+
function attachReference(url) {
|
|
715
|
+
setReferenceImageUrl(url);
|
|
716
|
+
const sibling = imageToVideoSibling(modelId);
|
|
717
|
+
if (sibling) selectModel(sibling);
|
|
718
|
+
}
|
|
719
|
+
function removeReference() {
|
|
720
|
+
setReferenceImageUrl(null);
|
|
721
|
+
const sibling = textToVideoSibling(modelId);
|
|
722
|
+
if (sibling) selectModel(sibling);
|
|
338
723
|
}
|
|
339
724
|
async function generate() {
|
|
340
|
-
if (!workspaceId || submitLockRef.current) return;
|
|
725
|
+
if (!workspaceId || submitLockRef.current || isSubmitting) return;
|
|
341
726
|
const promptText = prompt.trim();
|
|
342
|
-
if (!promptText)
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
if (!selectedModelOption || selectedModelOption.status === "unavailable") {
|
|
347
|
-
setError(`Select an available ${typeConfigFor(type).label} model`);
|
|
727
|
+
if (!promptText) return;
|
|
728
|
+
if (!modelReady) {
|
|
729
|
+
if (!catalogLoading) setError("Select an available model");
|
|
348
730
|
return;
|
|
349
731
|
}
|
|
350
732
|
submitLockRef.current = true;
|
|
351
733
|
setIsSubmitting(true);
|
|
352
734
|
setError(null);
|
|
353
735
|
const clientRequestId = crypto.randomUUID();
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
const localGenerations = Array.from({ length: requestedImageCount }, (_, outputIndex) => optimisticGeneration({
|
|
736
|
+
const imageCount = type === "image" ? normalizeImageCount(values.n ?? 1) : 1;
|
|
737
|
+
const localGenerations = Array.from({ length: imageCount }, (_, outputIndex) => optimisticGeneration({
|
|
357
738
|
type,
|
|
358
739
|
prompt: promptText,
|
|
359
|
-
model:
|
|
740
|
+
model: modelId,
|
|
360
741
|
clientRequestId,
|
|
361
742
|
outputIndex: type === "image" ? outputIndex : void 0,
|
|
362
|
-
outputCount: type === "image" ?
|
|
743
|
+
outputCount: type === "image" ? imageCount : void 0
|
|
363
744
|
}));
|
|
364
745
|
localGenerations.slice().reverse().forEach(onGenerated);
|
|
365
746
|
setPrompt("");
|
|
@@ -369,13 +750,29 @@ function ComposerHero({
|
|
|
369
750
|
workspaceId,
|
|
370
751
|
clientRequestId,
|
|
371
752
|
type,
|
|
372
|
-
model:
|
|
373
|
-
prompt,
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
753
|
+
model: modelId,
|
|
754
|
+
prompt: promptText,
|
|
755
|
+
// `duration`, `audio` and `speed` go on the wire in the TYPE the model
|
|
756
|
+
// published them in — a `duration` of `'5'` that arrives as `5` is a
|
|
757
|
+
// different request, and the three providers behind this lane disagree
|
|
758
|
+
// about which is right. An absent value is omitted, never defaulted.
|
|
759
|
+
image: {
|
|
760
|
+
size: asText(values.size),
|
|
761
|
+
quality: asText(values.quality),
|
|
762
|
+
count: imageCount
|
|
763
|
+
},
|
|
764
|
+
video: {
|
|
765
|
+
duration: asWireScalar(values.duration),
|
|
766
|
+
resolution: asText(values.resolution),
|
|
767
|
+
aspectRatio: asText(values.aspect_ratio),
|
|
768
|
+
referenceImageUrl: referenceSupported && referenceImageUrl ? referenceImageUrl : void 0,
|
|
769
|
+
audio: audioSupported && typeof values.audio === "boolean" ? values.audio : void 0,
|
|
770
|
+
mode: asText(values.mode)
|
|
771
|
+
},
|
|
772
|
+
speech: {
|
|
773
|
+
voice: asText(values.voice),
|
|
774
|
+
speed: typeof values.speed === "number" ? values.speed : void 0
|
|
775
|
+
}
|
|
379
776
|
});
|
|
380
777
|
const res = await fetch("/api/generate", {
|
|
381
778
|
method: "POST",
|
|
@@ -397,192 +794,164 @@ function ComposerHero({
|
|
|
397
794
|
setIsSubmitting(false);
|
|
398
795
|
}
|
|
399
796
|
}
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const Icon = cfg.icon;
|
|
411
|
-
const active = type === key;
|
|
412
|
-
return /* @__PURE__ */ jsxs4(
|
|
413
|
-
"button",
|
|
414
|
-
{
|
|
415
|
-
type: "button",
|
|
416
|
-
role: "tab",
|
|
417
|
-
"aria-selected": active,
|
|
418
|
-
onClick: () => changeType(key),
|
|
419
|
-
className: `flex flex-col items-center gap-1.5 rounded-lg border px-1 pb-2 pt-2.5 text-xs transition-all ${active ? "border-primary/30 bg-primary/10 font-semibold text-primary" : "border-border bg-background font-medium text-muted-foreground hover:border-foreground/20 hover:text-foreground"}`,
|
|
420
|
-
children: [
|
|
421
|
-
/* @__PURE__ */ jsx5(Icon, { className: "h-[17px] w-[17px] shrink-0" }),
|
|
422
|
-
/* @__PURE__ */ jsx5("span", { className: "truncate", children: cfg.label })
|
|
423
|
-
]
|
|
424
|
-
},
|
|
425
|
-
key
|
|
426
|
-
);
|
|
427
|
-
}) }),
|
|
428
|
-
/* @__PURE__ */ jsxs4("div", { className: "mt-5", children: [
|
|
429
|
-
/* @__PURE__ */ jsx5(
|
|
430
|
-
Label2,
|
|
431
|
-
{
|
|
432
|
-
htmlFor: "studio-prompt",
|
|
433
|
-
className: "mb-1.5 block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground",
|
|
434
|
-
children: "Prompt"
|
|
435
|
-
}
|
|
436
|
-
),
|
|
437
|
-
/* @__PURE__ */ jsxs4("div", { className: "rounded-xl border border-border bg-background transition-colors focus-within:border-primary/30 focus-within:ring-[3px] focus-within:ring-primary/10", children: [
|
|
438
|
-
/* @__PURE__ */ jsx5(
|
|
797
|
+
const statusMessage = unlistedSibling ? null : modelMessage(modelOption, catalogLoading, curatedModels.length);
|
|
798
|
+
const notice = catalogError ?? error ?? statusMessage;
|
|
799
|
+
const noticeIsProblem = Boolean(catalogError || error || modelOption?.status === "unavailable");
|
|
800
|
+
return /* @__PURE__ */ jsxs2(
|
|
801
|
+
"section",
|
|
802
|
+
{
|
|
803
|
+
"data-variant": variant,
|
|
804
|
+
className: `rounded-[14px] border border-border bg-card px-2.5 pb-[9px] pt-2 shadow-sm transition focus-within:border-primary focus-within:ring-[3px] focus-within:ring-ring/30 ${className ?? ""}`,
|
|
805
|
+
children: [
|
|
806
|
+
/* @__PURE__ */ jsx2(
|
|
439
807
|
"textarea",
|
|
440
808
|
{
|
|
441
|
-
id: "studio-prompt",
|
|
442
809
|
value: prompt,
|
|
443
810
|
onChange: (event) => setPrompt(event.target.value),
|
|
444
811
|
onKeyDown: (event) => {
|
|
445
|
-
if (event.key
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
812
|
+
if (event.key !== "Enter" || event.shiftKey) return;
|
|
813
|
+
event.preventDefault();
|
|
814
|
+
void generate();
|
|
449
815
|
},
|
|
450
|
-
rows:
|
|
451
|
-
|
|
452
|
-
|
|
816
|
+
rows: 3,
|
|
817
|
+
"aria-label": "Prompt",
|
|
818
|
+
placeholder: "Describe what you want to generate\u2026",
|
|
819
|
+
className: "block min-h-[66px] w-full resize-none border-0 bg-transparent px-1.5 pb-1 pt-1.5 text-[14.5px] leading-relaxed outline-none placeholder:text-muted-foreground"
|
|
453
820
|
}
|
|
454
821
|
),
|
|
455
|
-
/* @__PURE__ */
|
|
456
|
-
|
|
457
|
-
{
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
/* @__PURE__ */ jsxs4(
|
|
544
|
-
Button,
|
|
545
|
-
{
|
|
546
|
-
onClick: generate,
|
|
547
|
-
disabled: !canSubmit,
|
|
548
|
-
size: "lg",
|
|
549
|
-
className: "mt-5 w-full disabled:bg-muted disabled:text-muted-foreground disabled:opacity-100",
|
|
550
|
-
children: [
|
|
551
|
-
/* @__PURE__ */ jsx5(Sparkles, { className: "mr-2 h-4 w-4" }),
|
|
552
|
-
"Generate",
|
|
553
|
-
/* @__PURE__ */ jsx5("span", { className: "ml-2 text-xs opacity-60", children: "\u2318\u21B5" })
|
|
554
|
-
]
|
|
555
|
-
}
|
|
556
|
-
)
|
|
557
|
-
] });
|
|
822
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
|
|
823
|
+
/* @__PURE__ */ jsx2(MediaTypeSegments, { value: type, segments: SEGMENTS, onChange: setType }),
|
|
824
|
+
/* @__PURE__ */ jsxs2(ComposerBand, { bandRef, resetKey: type, children: [
|
|
825
|
+
/* @__PURE__ */ jsx2(
|
|
826
|
+
ModelPill,
|
|
827
|
+
{
|
|
828
|
+
models: curatedModels,
|
|
829
|
+
value: modelId,
|
|
830
|
+
displayName: modelOption?.name || modelId || "Select a model",
|
|
831
|
+
provider: modelOption?.provider,
|
|
832
|
+
onSelect: chooseModel,
|
|
833
|
+
bandRef
|
|
834
|
+
}
|
|
835
|
+
),
|
|
836
|
+
params.map((param) => /* @__PURE__ */ jsx2("div", { className: "studio-pill-in flex-none", children: /* @__PURE__ */ jsx2(
|
|
837
|
+
OptionPill,
|
|
838
|
+
{
|
|
839
|
+
label: PARAM_LABELS[param] ?? param,
|
|
840
|
+
value: values[param],
|
|
841
|
+
choices: choicesForPill(param, options?.[param], values[param]),
|
|
842
|
+
onSelect: (value) => setOption(param, value),
|
|
843
|
+
bandRef,
|
|
844
|
+
custom: param === "size" && supportsCustomImageSize(modelId) ? {
|
|
845
|
+
label: "Custom\u2026",
|
|
846
|
+
render: ({ close }) => /* @__PURE__ */ jsx2(
|
|
847
|
+
CustomSizeForm,
|
|
848
|
+
{
|
|
849
|
+
initial: asText(values.size),
|
|
850
|
+
onApply: (size) => {
|
|
851
|
+
setOption("size", size);
|
|
852
|
+
close();
|
|
853
|
+
},
|
|
854
|
+
onCancel: close
|
|
855
|
+
}
|
|
856
|
+
)
|
|
857
|
+
} : void 0
|
|
858
|
+
}
|
|
859
|
+
) }, `${type}-${param}`)),
|
|
860
|
+
audioSupported && /* @__PURE__ */ jsx2("div", { className: "studio-pill-in flex-none", children: /* @__PURE__ */ jsx2(
|
|
861
|
+
AudioTogglePill,
|
|
862
|
+
{
|
|
863
|
+
on: values.audio === true,
|
|
864
|
+
onToggle: (on) => setOption("audio", on)
|
|
865
|
+
}
|
|
866
|
+
) }, `${type}-audio`),
|
|
867
|
+
referenceSupported && /* @__PURE__ */ jsx2("div", { className: "studio-pill-in flex-none", children: /* @__PURE__ */ jsx2(
|
|
868
|
+
ReferencePill,
|
|
869
|
+
{
|
|
870
|
+
url: referenceImageUrl,
|
|
871
|
+
onAttach: attachReference,
|
|
872
|
+
onRemove: removeReference,
|
|
873
|
+
pick: pickReferenceImage,
|
|
874
|
+
bandRef
|
|
875
|
+
}
|
|
876
|
+
) }, `${type}-reference`)
|
|
877
|
+
] }),
|
|
878
|
+
/* @__PURE__ */ jsx2(
|
|
879
|
+
"button",
|
|
880
|
+
{
|
|
881
|
+
type: "button",
|
|
882
|
+
"aria-label": "Generate",
|
|
883
|
+
title: "Generate",
|
|
884
|
+
disabled: !canSubmit,
|
|
885
|
+
onClick: () => void generate(),
|
|
886
|
+
className: "studio-send ml-auto inline-flex h-8 w-8 flex-none items-center justify-center rounded-full text-white transition disabled:opacity-40",
|
|
887
|
+
children: /* @__PURE__ */ jsx2(ArrowUp, { className: "h-4 w-4", strokeWidth: 2 })
|
|
888
|
+
}
|
|
889
|
+
)
|
|
890
|
+
] }),
|
|
891
|
+
notice && /* @__PURE__ */ jsx2("p", { className: `px-1.5 pt-1.5 text-[12px] ${noticeIsProblem ? "text-destructive" : "text-muted-foreground"}`, children: notice })
|
|
892
|
+
]
|
|
893
|
+
}
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
function asText(value) {
|
|
897
|
+
return typeof value === "string" ? value : typeof value === "number" ? String(value) : void 0;
|
|
898
|
+
}
|
|
899
|
+
function asWireScalar(value) {
|
|
900
|
+
return typeof value === "string" || typeof value === "number" ? value : void 0;
|
|
901
|
+
}
|
|
902
|
+
function choicesForPill(param, meta, value) {
|
|
903
|
+
if (!meta) return [];
|
|
904
|
+
const choices = choicesFor(param, meta).map((choice) => ({
|
|
905
|
+
value: choice,
|
|
906
|
+
label: optionValueLabel(param, choice)
|
|
907
|
+
}));
|
|
908
|
+
if (value === void 0 || choices.some((choice) => choice.value === value)) return choices;
|
|
909
|
+
return [...choices, { value, label: `${optionValueLabel(param, value)} \xB7 custom` }];
|
|
558
910
|
}
|
|
559
911
|
|
|
560
912
|
// src/studio-react/studio-header.tsx
|
|
561
|
-
import { Button
|
|
913
|
+
import { Button } from "@tangle-network/sandbox-ui/primitives";
|
|
562
914
|
import { Images } from "lucide-react";
|
|
563
|
-
import { jsx as
|
|
915
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
564
916
|
function StudioHeader({
|
|
565
917
|
count,
|
|
566
918
|
onOpenLibrary,
|
|
567
919
|
canGenerate
|
|
568
920
|
}) {
|
|
569
|
-
return /* @__PURE__ */
|
|
570
|
-
/* @__PURE__ */
|
|
571
|
-
/* @__PURE__ */
|
|
572
|
-
/* @__PURE__ */
|
|
921
|
+
return /* @__PURE__ */ jsx3("header", { className: "sticky top-0 z-20 border-b border-border bg-card", children: /* @__PURE__ */ jsxs3("div", { className: "mx-auto flex w-full max-w-3xl items-center justify-between gap-3 px-4 py-3 sm:px-6", children: [
|
|
922
|
+
/* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
|
|
923
|
+
/* @__PURE__ */ jsx3("h1", { className: "text-lg font-semibold tracking-tight text-foreground", children: "Studio" }),
|
|
924
|
+
/* @__PURE__ */ jsx3("p", { className: "truncate text-xs text-muted-foreground", children: canGenerate ? "Generate images, video, voice, avatars, and transcripts." : "Browse your team's generations." })
|
|
573
925
|
] }),
|
|
574
|
-
/* @__PURE__ */
|
|
575
|
-
/* @__PURE__ */
|
|
926
|
+
/* @__PURE__ */ jsxs3(Button, { size: "sm", variant: "outline", onClick: onOpenLibrary, className: "shrink-0", children: [
|
|
927
|
+
/* @__PURE__ */ jsx3(Images, { className: "mr-1.5 h-4 w-4" }),
|
|
576
928
|
"Library",
|
|
577
|
-
count > 0 && /* @__PURE__ */
|
|
929
|
+
count > 0 && /* @__PURE__ */ jsx3("span", { className: "ml-2 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary/15 px-1.5 text-xs font-semibold text-primary", children: count })
|
|
578
930
|
] })
|
|
579
931
|
] }) });
|
|
580
932
|
}
|
|
581
933
|
|
|
582
934
|
// src/studio-react/result-canvas.tsx
|
|
583
935
|
import { Badge } from "@tangle-network/sandbox-ui/primitives";
|
|
584
|
-
import { ArrowRight, Sparkles
|
|
585
|
-
|
|
936
|
+
import { ArrowRight, Sparkles } from "lucide-react";
|
|
937
|
+
|
|
938
|
+
// src/studio-react/type-config.ts
|
|
939
|
+
import { FileText, Image, Mic, User, Video as Video2 } from "lucide-react";
|
|
940
|
+
var MEDIA_SCRIM = "bg-black/60 text-white border-white/10";
|
|
941
|
+
var IMAGE = { label: "Image", icon: Image, color: MEDIA_SCRIM };
|
|
942
|
+
var TYPE_CONFIG = {
|
|
943
|
+
image: IMAGE,
|
|
944
|
+
video: { label: "Video", icon: Video2, color: MEDIA_SCRIM },
|
|
945
|
+
avatar: { label: "Avatar", icon: User, color: MEDIA_SCRIM },
|
|
946
|
+
speech: { label: "Audio", icon: Mic, color: MEDIA_SCRIM },
|
|
947
|
+
transcription: { label: "Transcript", icon: FileText, color: MEDIA_SCRIM }
|
|
948
|
+
};
|
|
949
|
+
function typeConfigFor(type) {
|
|
950
|
+
return TYPE_CONFIG[type] ?? IMAGE;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// src/studio-react/result-canvas.tsx
|
|
954
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
586
955
|
function ResultCanvas({
|
|
587
956
|
batch,
|
|
588
957
|
onOpenLibrary,
|
|
@@ -590,10 +959,10 @@ function ResultCanvas({
|
|
|
590
959
|
}) {
|
|
591
960
|
const cardClass = "rounded-xl border border-border bg-card shadow-sm";
|
|
592
961
|
if (batch.length === 0) {
|
|
593
|
-
return /* @__PURE__ */
|
|
594
|
-
/* @__PURE__ */
|
|
595
|
-
/* @__PURE__ */
|
|
596
|
-
/* @__PURE__ */
|
|
962
|
+
return /* @__PURE__ */ jsxs4("section", { className: `${cardClass} flex min-h-[180px] flex-col items-center justify-center gap-2 p-8 text-center`, children: [
|
|
963
|
+
/* @__PURE__ */ jsx4("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-muted", children: /* @__PURE__ */ jsx4(Sparkles, { className: "h-6 w-6 text-muted-foreground/40" }) }),
|
|
964
|
+
/* @__PURE__ */ jsx4("p", { className: "text-sm font-medium text-foreground", children: "Your creations appear here" }),
|
|
965
|
+
/* @__PURE__ */ jsx4("p", { className: "max-w-xs text-xs text-muted-foreground", children: "Generate above \u2014 results appear here and are saved to your library." })
|
|
597
966
|
] });
|
|
598
967
|
}
|
|
599
968
|
const cfg = typeConfigFor(batch[0]?.type ?? "image");
|
|
@@ -601,17 +970,17 @@ function ResultCanvas({
|
|
|
601
970
|
const statuses = batch.map(generationStatus);
|
|
602
971
|
const runLabel = statuses.some((s) => s === "pending" || s === "running") ? "Generating\u2026" : statuses.every((s) => s === "failed") ? "Last run failed" : "Latest creation";
|
|
603
972
|
const isWorking = statuses.some((s) => s === "pending" || s === "running");
|
|
604
|
-
return /* @__PURE__ */
|
|
605
|
-
/* @__PURE__ */
|
|
606
|
-
/* @__PURE__ */
|
|
607
|
-
/* @__PURE__ */
|
|
608
|
-
/* @__PURE__ */
|
|
973
|
+
return /* @__PURE__ */ jsxs4("section", { className: `${cardClass} p-5`, children: [
|
|
974
|
+
/* @__PURE__ */ jsxs4("div", { className: "mb-3 flex items-center justify-between gap-3", children: [
|
|
975
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
976
|
+
/* @__PURE__ */ jsxs4(Badge, { variant: "outline", className: `${cfg.color} gap-1 text-xs`, children: [
|
|
977
|
+
/* @__PURE__ */ jsx4(Icon, { className: "h-3 w-3" }),
|
|
609
978
|
cfg.label
|
|
610
979
|
] }),
|
|
611
|
-
/* @__PURE__ */
|
|
612
|
-
isWorking && /* @__PURE__ */
|
|
980
|
+
/* @__PURE__ */ jsx4("span", { className: "text-sm font-medium text-foreground", children: runLabel }),
|
|
981
|
+
isWorking && /* @__PURE__ */ jsx4("span", { className: "h-2 w-2 animate-pulse rounded-full bg-primary" })
|
|
613
982
|
] }),
|
|
614
|
-
/* @__PURE__ */
|
|
983
|
+
/* @__PURE__ */ jsxs4(
|
|
615
984
|
"button",
|
|
616
985
|
{
|
|
617
986
|
type: "button",
|
|
@@ -619,22 +988,22 @@ function ResultCanvas({
|
|
|
619
988
|
className: "flex items-center gap-1 text-xs font-medium text-primary transition-colors hover:text-primary/80",
|
|
620
989
|
children: [
|
|
621
990
|
"View in Library",
|
|
622
|
-
/* @__PURE__ */
|
|
991
|
+
/* @__PURE__ */ jsx4(ArrowRight, { className: "h-3.5 w-3.5" })
|
|
623
992
|
]
|
|
624
993
|
}
|
|
625
994
|
)
|
|
626
995
|
] }),
|
|
627
|
-
/* @__PURE__ */
|
|
996
|
+
/* @__PURE__ */ jsx4("div", { className: `grid gap-3 ${batch.length === 1 ? "grid-cols-1" : "grid-cols-2"}`, children: batch.map((generation) => /* @__PURE__ */ jsx4(
|
|
628
997
|
"button",
|
|
629
998
|
{
|
|
630
999
|
type: "button",
|
|
631
1000
|
onClick: () => onSelect(generation),
|
|
632
1001
|
className: "studio-bloom group relative aspect-video overflow-hidden rounded-lg border border-border bg-secondary transition-colors hover:border-primary/40",
|
|
633
|
-
children: /* @__PURE__ */
|
|
1002
|
+
children: /* @__PURE__ */ jsx4(CanvasTile, { generation })
|
|
634
1003
|
},
|
|
635
1004
|
generation.id
|
|
636
1005
|
)) }),
|
|
637
|
-
batch.length > 1 && !isWorking && /* @__PURE__ */
|
|
1006
|
+
batch.length > 1 && !isWorking && /* @__PURE__ */ jsxs4("p", { className: "mt-2 text-xs text-muted-foreground", children: [
|
|
638
1007
|
batch.length,
|
|
639
1008
|
" results from this run"
|
|
640
1009
|
] })
|
|
@@ -645,58 +1014,58 @@ function CanvasTile({ generation }) {
|
|
|
645
1014
|
const Icon = cfg.icon;
|
|
646
1015
|
const status = generationStatus(generation);
|
|
647
1016
|
if (status === "pending" || status === "running") {
|
|
648
|
-
return /* @__PURE__ */
|
|
1017
|
+
return /* @__PURE__ */ jsx4("div", { className: "studio-shimmer absolute inset-0 h-full w-full" });
|
|
649
1018
|
}
|
|
650
1019
|
if (status === "failed") {
|
|
651
|
-
return /* @__PURE__ */
|
|
652
|
-
/* @__PURE__ */
|
|
653
|
-
/* @__PURE__ */
|
|
1020
|
+
return /* @__PURE__ */ jsxs4("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-2 p-4 text-center", children: [
|
|
1021
|
+
/* @__PURE__ */ jsx4(Icon, { className: "h-6 w-6 text-destructive/50" }),
|
|
1022
|
+
/* @__PURE__ */ jsx4("p", { className: "line-clamp-3 text-xs text-destructive", children: generationError(generation) ?? "Generation failed" })
|
|
654
1023
|
] });
|
|
655
1024
|
}
|
|
656
1025
|
if (generation.type === "image" && generation.result) {
|
|
657
|
-
return /* @__PURE__ */
|
|
1026
|
+
return /* @__PURE__ */ jsx4("img", { src: generation.result, alt: generation.prompt, className: "absolute inset-0 h-full w-full object-contain" });
|
|
658
1027
|
}
|
|
659
1028
|
if ((generation.type === "video" || generation.type === "avatar") && generation.result) {
|
|
660
|
-
return /* @__PURE__ */
|
|
1029
|
+
return /* @__PURE__ */ jsx4("video", { src: generation.result, controls: true, className: "absolute inset-0 h-full w-full object-contain" });
|
|
661
1030
|
}
|
|
662
1031
|
if (generation.type === "speech" && generation.result) {
|
|
663
|
-
return /* @__PURE__ */
|
|
1032
|
+
return /* @__PURE__ */ jsx4("div", { className: "absolute inset-0 flex items-center justify-center p-4", children: /* @__PURE__ */ jsx4("audio", { src: generation.result, controls: true, className: "w-full" }) });
|
|
664
1033
|
}
|
|
665
1034
|
if (generation.type === "transcription" && generation.result) {
|
|
666
|
-
return /* @__PURE__ */
|
|
1035
|
+
return /* @__PURE__ */ jsx4("div", { className: "absolute inset-0 overflow-y-auto p-4", children: /* @__PURE__ */ jsx4("p", { className: "whitespace-pre-wrap text-xs text-foreground", children: generation.result }) });
|
|
667
1036
|
}
|
|
668
|
-
return /* @__PURE__ */
|
|
1037
|
+
return /* @__PURE__ */ jsx4("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx4(Icon, { className: "h-8 w-8 text-muted-foreground/20" }) });
|
|
669
1038
|
}
|
|
670
1039
|
|
|
671
1040
|
// src/studio-react/library-drawer.tsx
|
|
672
|
-
import { useMemo as
|
|
1041
|
+
import { useMemo as useMemo3 } from "react";
|
|
673
1042
|
import { Link as Link2 } from "react-router";
|
|
674
1043
|
import {
|
|
675
|
-
Button as
|
|
1044
|
+
Button as Button3,
|
|
676
1045
|
Tabs,
|
|
677
1046
|
TabsList,
|
|
678
1047
|
TabsTrigger
|
|
679
1048
|
} from "@tangle-network/sandbox-ui/primitives";
|
|
680
|
-
import { ArrowLeft, DollarSign, FolderOpen as FolderOpen2, X } from "lucide-react";
|
|
1049
|
+
import { ArrowLeft, DollarSign, FolderOpen as FolderOpen2, X as X2 } from "lucide-react";
|
|
681
1050
|
|
|
682
1051
|
// src/studio-react/studio-sheet.tsx
|
|
683
1052
|
import * as Dialog from "@radix-ui/react-dialog";
|
|
684
|
-
import { jsx as
|
|
1053
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
685
1054
|
function StudioSheet({
|
|
686
1055
|
open,
|
|
687
1056
|
onOpenChange,
|
|
688
1057
|
title,
|
|
689
1058
|
children
|
|
690
1059
|
}) {
|
|
691
|
-
return /* @__PURE__ */
|
|
692
|
-
/* @__PURE__ */
|
|
693
|
-
/* @__PURE__ */
|
|
1060
|
+
return /* @__PURE__ */ jsx5(Dialog.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs5(Dialog.Portal, { children: [
|
|
1061
|
+
/* @__PURE__ */ jsx5(Dialog.Overlay, { className: "studio-sheet-overlay fixed inset-0 z-50 bg-black/50" }),
|
|
1062
|
+
/* @__PURE__ */ jsxs5(
|
|
694
1063
|
Dialog.Content,
|
|
695
1064
|
{
|
|
696
1065
|
className: "studio-sheet-content fixed inset-y-0 right-0 z-50 flex w-[min(92vw,30rem)] flex-col border-l border-card-edge bg-popover shadow-[var(--shadow-overlay)] focus:outline-none",
|
|
697
1066
|
"aria-describedby": void 0,
|
|
698
1067
|
children: [
|
|
699
|
-
/* @__PURE__ */
|
|
1068
|
+
/* @__PURE__ */ jsx5(Dialog.Title, { className: "sr-only", children: title }),
|
|
700
1069
|
children
|
|
701
1070
|
]
|
|
702
1071
|
}
|
|
@@ -706,11 +1075,11 @@ function StudioSheet({
|
|
|
706
1075
|
|
|
707
1076
|
// src/studio-react/generation-grid.tsx
|
|
708
1077
|
import { EmptyState } from "@tangle-network/sandbox-ui/primitives";
|
|
709
|
-
import { Film, Sparkles as
|
|
1078
|
+
import { Film, Sparkles as Sparkles2 } from "lucide-react";
|
|
710
1079
|
|
|
711
1080
|
// src/studio-react/generation-card.tsx
|
|
712
1081
|
import { Card, CardContent, Badge as Badge2 } from "@tangle-network/sandbox-ui/primitives";
|
|
713
|
-
import { jsx as
|
|
1082
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
714
1083
|
function GenerationCard({
|
|
715
1084
|
generation,
|
|
716
1085
|
onSelect
|
|
@@ -718,26 +1087,26 @@ function GenerationCard({
|
|
|
718
1087
|
const cfg = typeConfigFor(generation.type);
|
|
719
1088
|
const Icon = cfg.icon;
|
|
720
1089
|
const status = generationStatus(generation);
|
|
721
|
-
return /* @__PURE__ */
|
|
722
|
-
/* @__PURE__ */
|
|
723
|
-
generation.type === "image" && generation.result ? /* @__PURE__ */
|
|
724
|
-
/* @__PURE__ */
|
|
725
|
-
/* @__PURE__ */
|
|
1090
|
+
return /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => onSelect(generation), className: "group text-left animate-row-in", children: /* @__PURE__ */ jsxs6(Card, { className: "overflow-hidden transition-all group-hover:border-primary/50 group-hover:shadow-md", children: [
|
|
1091
|
+
/* @__PURE__ */ jsxs6("div", { className: "relative aspect-video bg-secondary", children: [
|
|
1092
|
+
generation.type === "image" && generation.result ? /* @__PURE__ */ jsx6("img", { src: generation.result, alt: generation.prompt, className: "h-full w-full object-cover" }) : (generation.type === "video" || generation.type === "avatar") && generation.result ? /* @__PURE__ */ jsx6("video", { src: generation.result, className: "h-full w-full object-cover", muted: true }) : generation.type === "speech" && generation.result ? /* @__PURE__ */ jsx6("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx6(Icon, { className: "h-6 w-6 text-muted-foreground/40" }) }) : generation.type === "transcription" && generation.result ? /* @__PURE__ */ jsx6("div", { className: "flex h-full items-center justify-center p-4", children: /* @__PURE__ */ jsx6("p", { className: "line-clamp-5 text-xs text-muted-foreground", children: generation.result }) }) : status === "pending" || status === "running" ? /* @__PURE__ */ jsx6("div", { className: "shimmer h-full w-full" }) : /* @__PURE__ */ jsx6("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx6(Icon, { className: "h-8 w-8 text-muted-foreground/20" }) }),
|
|
1093
|
+
/* @__PURE__ */ jsx6("div", { className: "absolute left-2 top-2", children: /* @__PURE__ */ jsxs6(Badge2, { variant: "outline", className: `${cfg.color} gap-1 text-xs`, children: [
|
|
1094
|
+
/* @__PURE__ */ jsx6(Icon, { className: "h-3 w-3" }),
|
|
726
1095
|
cfg.label
|
|
727
1096
|
] }) }),
|
|
728
|
-
/* @__PURE__ */
|
|
1097
|
+
/* @__PURE__ */ jsx6(GenerationStatusBadge, { generation })
|
|
729
1098
|
] }),
|
|
730
|
-
/* @__PURE__ */
|
|
731
|
-
/* @__PURE__ */
|
|
732
|
-
status === "failed" && /* @__PURE__ */
|
|
733
|
-
/* @__PURE__ */
|
|
734
|
-
generation.model && /* @__PURE__ */
|
|
735
|
-
/* @__PURE__ */
|
|
736
|
-
generation.cost != null && /* @__PURE__ */
|
|
1099
|
+
/* @__PURE__ */ jsxs6(CardContent, { className: "p-3", children: [
|
|
1100
|
+
/* @__PURE__ */ jsx6("p", { className: "mb-2 line-clamp-2 text-sm font-medium text-foreground", children: generation.prompt }),
|
|
1101
|
+
status === "failed" && /* @__PURE__ */ jsx6("p", { className: "mb-2 line-clamp-2 text-xs text-destructive", children: generationError(generation) }),
|
|
1102
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
|
|
1103
|
+
generation.model && /* @__PURE__ */ jsx6("span", { className: "truncate text-xs text-muted-foreground", children: generation.model }),
|
|
1104
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1105
|
+
generation.cost != null && /* @__PURE__ */ jsxs6("span", { className: "text-xs text-muted-foreground", children: [
|
|
737
1106
|
"$",
|
|
738
1107
|
generation.cost.toFixed(3)
|
|
739
1108
|
] }),
|
|
740
|
-
/* @__PURE__ */
|
|
1109
|
+
/* @__PURE__ */ jsx6("span", { className: "text-xs text-muted-foreground", children: relativeTime(generation.createdAt) })
|
|
741
1110
|
] })
|
|
742
1111
|
] })
|
|
743
1112
|
] })
|
|
@@ -751,11 +1120,11 @@ function GenerationStatusBadge({
|
|
|
751
1120
|
if (status === "succeeded") return null;
|
|
752
1121
|
const label = status === "failed" ? "Failed" : status === "running" ? "Running" : "Pending";
|
|
753
1122
|
const className = status === "failed" ? "border-destructive/30 bg-[color-mix(in_srgb,hsl(var(--destructive))_10%,hsl(var(--card)))] text-destructive" : "border-warning/30 bg-[color-mix(in_srgb,hsl(var(--warning))_10%,hsl(var(--card)))] text-warning";
|
|
754
|
-
return /* @__PURE__ */
|
|
1123
|
+
return /* @__PURE__ */ jsx6("div", { className: inline ? "" : "absolute bottom-2 left-2", children: /* @__PURE__ */ jsx6(Badge2, { variant: "outline", className: `${className} text-xs`, children: label }) });
|
|
755
1124
|
}
|
|
756
1125
|
|
|
757
1126
|
// src/studio-react/generation-grid.tsx
|
|
758
|
-
import { jsx as
|
|
1127
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
759
1128
|
function filterGenerations(generations, typeFilter) {
|
|
760
1129
|
if (!typeFilter || !isGenerationType(typeFilter)) return generations;
|
|
761
1130
|
return generations.filter((generation) => generation.type === typeFilter);
|
|
@@ -766,24 +1135,24 @@ function GenerationGrid({
|
|
|
766
1135
|
onSelect
|
|
767
1136
|
}) {
|
|
768
1137
|
if (generations.length === 0) {
|
|
769
|
-
return /* @__PURE__ */
|
|
1138
|
+
return /* @__PURE__ */ jsx7(
|
|
770
1139
|
EmptyState,
|
|
771
1140
|
{
|
|
772
|
-
icon: typeFilter ? /* @__PURE__ */
|
|
1141
|
+
icon: typeFilter ? /* @__PURE__ */ jsx7(Film, { className: "h-8 w-8 text-muted-foreground/30" }) : /* @__PURE__ */ jsx7(Sparkles2, { className: "h-10 w-10 text-muted-foreground/30" }),
|
|
773
1142
|
title: typeFilter ? `No ${TYPE_CONFIG[typeFilter]?.label ?? typeFilter} generations` : "No generations yet",
|
|
774
1143
|
description: typeFilter ? "Try a different filter or change the type in the composer." : "Everything you and the agent create lives here.",
|
|
775
1144
|
className: "py-16"
|
|
776
1145
|
}
|
|
777
1146
|
);
|
|
778
1147
|
}
|
|
779
|
-
return /* @__PURE__ */
|
|
1148
|
+
return /* @__PURE__ */ jsx7("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: generations.map((generation) => /* @__PURE__ */ jsx7(GenerationCard, { generation, onSelect }, generation.id)) });
|
|
780
1149
|
}
|
|
781
1150
|
|
|
782
1151
|
// src/studio-react/generation-detail.tsx
|
|
783
1152
|
import { Link } from "react-router";
|
|
784
|
-
import { Badge as Badge3, Button as
|
|
1153
|
+
import { Badge as Badge3, Button as Button2 } from "@tangle-network/sandbox-ui/primitives";
|
|
785
1154
|
import { FolderOpen } from "lucide-react";
|
|
786
|
-
import { jsx as
|
|
1155
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
787
1156
|
function GenerationDetail({
|
|
788
1157
|
generation,
|
|
789
1158
|
vaultHref,
|
|
@@ -793,50 +1162,50 @@ function GenerationDetail({
|
|
|
793
1162
|
const Icon = cfg.icon;
|
|
794
1163
|
const vaultPath = generationVaultPath(generation);
|
|
795
1164
|
const href = vaultHref && vaultPath ? vaultHref(vaultPath) : null;
|
|
796
|
-
return /* @__PURE__ */
|
|
797
|
-
/* @__PURE__ */
|
|
798
|
-
/* @__PURE__ */
|
|
799
|
-
/* @__PURE__ */
|
|
800
|
-
/* @__PURE__ */
|
|
1165
|
+
return /* @__PURE__ */ jsxs7("div", { className: "space-y-4", children: [
|
|
1166
|
+
/* @__PURE__ */ jsx8("div", { className: "overflow-hidden rounded-lg bg-secondary", children: generation.type === "image" && generation.result ? /* @__PURE__ */ jsx8("img", { src: generation.result, alt: generation.prompt, className: "max-h-[420px] w-full object-contain" }) : (generation.type === "video" || generation.type === "avatar") && generation.result ? /* @__PURE__ */ jsx8("video", { src: generation.result, controls: true, className: "max-h-[420px] w-full" }) : generation.type === "speech" && generation.result ? /* @__PURE__ */ jsx8("div", { className: "p-6", children: /* @__PURE__ */ jsx8("audio", { src: generation.result, controls: true, className: "w-full" }) }) : /* @__PURE__ */ jsx8("div", { className: "flex h-32 items-center justify-center", children: /* @__PURE__ */ jsx8(Icon, { className: "h-8 w-8 text-muted-foreground/20" }) }) }),
|
|
1167
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1168
|
+
/* @__PURE__ */ jsxs7(Badge3, { variant: "outline", className: `${cfg.color} gap-1`, children: [
|
|
1169
|
+
/* @__PURE__ */ jsx8(Icon, { className: "h-3 w-3" }),
|
|
801
1170
|
cfg.label
|
|
802
1171
|
] }),
|
|
803
|
-
/* @__PURE__ */
|
|
804
|
-
generationError(generation) && /* @__PURE__ */
|
|
1172
|
+
/* @__PURE__ */ jsx8(GenerationStatusBadge, { generation, inline: true }),
|
|
1173
|
+
generationError(generation) && /* @__PURE__ */ jsx8("span", { className: "text-xs text-destructive", children: generationError(generation) })
|
|
805
1174
|
] }),
|
|
806
|
-
/* @__PURE__ */
|
|
807
|
-
/* @__PURE__ */
|
|
808
|
-
/* @__PURE__ */
|
|
1175
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
1176
|
+
/* @__PURE__ */ jsx8("span", { className: "mb-1 block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: "Prompt" }),
|
|
1177
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm text-foreground", children: generation.prompt })
|
|
809
1178
|
] }),
|
|
810
|
-
/* @__PURE__ */
|
|
811
|
-
generation.model && /* @__PURE__ */
|
|
812
|
-
/* @__PURE__ */
|
|
813
|
-
/* @__PURE__ */
|
|
1179
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-4", children: [
|
|
1180
|
+
generation.model && /* @__PURE__ */ jsxs7("div", { children: [
|
|
1181
|
+
/* @__PURE__ */ jsx8("span", { className: "block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: "Model" }),
|
|
1182
|
+
/* @__PURE__ */ jsx8("span", { className: "text-xs text-foreground", children: generation.model })
|
|
814
1183
|
] }),
|
|
815
|
-
generation.cost != null && /* @__PURE__ */
|
|
816
|
-
/* @__PURE__ */
|
|
817
|
-
/* @__PURE__ */
|
|
1184
|
+
generation.cost != null && /* @__PURE__ */ jsxs7("div", { children: [
|
|
1185
|
+
/* @__PURE__ */ jsx8("span", { className: "block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: "Cost" }),
|
|
1186
|
+
/* @__PURE__ */ jsxs7("span", { className: "text-xs text-foreground", children: [
|
|
818
1187
|
"$",
|
|
819
1188
|
generation.cost.toFixed(3)
|
|
820
1189
|
] })
|
|
821
1190
|
] }),
|
|
822
|
-
generation.createdAt && /* @__PURE__ */
|
|
823
|
-
/* @__PURE__ */
|
|
824
|
-
/* @__PURE__ */
|
|
1191
|
+
generation.createdAt && /* @__PURE__ */ jsxs7("div", { children: [
|
|
1192
|
+
/* @__PURE__ */ jsx8("span", { className: "block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: "Created" }),
|
|
1193
|
+
/* @__PURE__ */ jsx8("span", { className: "text-xs text-foreground", children: new Date(generation.createdAt).toLocaleString("en-US", { dateStyle: "medium", timeStyle: "short" }) })
|
|
825
1194
|
] })
|
|
826
1195
|
] }),
|
|
827
|
-
generation.type === "transcription" && generation.result && /* @__PURE__ */
|
|
828
|
-
/* @__PURE__ */
|
|
829
|
-
/* @__PURE__ */
|
|
1196
|
+
generation.type === "transcription" && generation.result && /* @__PURE__ */ jsxs7("div", { children: [
|
|
1197
|
+
/* @__PURE__ */ jsx8("span", { className: "mb-1 block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: "Transcription" }),
|
|
1198
|
+
/* @__PURE__ */ jsx8("pre", { className: "max-h-64 overflow-y-auto whitespace-pre-wrap rounded-lg bg-secondary p-4 font-mono text-[13px] tabular-nums text-foreground", children: generation.result })
|
|
830
1199
|
] }),
|
|
831
|
-
href && /* @__PURE__ */
|
|
832
|
-
/* @__PURE__ */
|
|
1200
|
+
href && /* @__PURE__ */ jsx8("div", { className: "border-t border-border pt-2", children: /* @__PURE__ */ jsx8(Link, { to: href, prefetch: "intent", onClick: onNavigate, children: /* @__PURE__ */ jsxs7(Button2, { size: "sm", variant: "outline", children: [
|
|
1201
|
+
/* @__PURE__ */ jsx8(FolderOpen, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
833
1202
|
"Open in Vault"
|
|
834
1203
|
] }) }) })
|
|
835
1204
|
] });
|
|
836
1205
|
}
|
|
837
1206
|
|
|
838
1207
|
// src/studio-react/library-drawer.tsx
|
|
839
|
-
import { Fragment, jsx as
|
|
1208
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
840
1209
|
function LibraryDrawer({
|
|
841
1210
|
open,
|
|
842
1211
|
onOpenChange,
|
|
@@ -848,71 +1217,71 @@ function LibraryDrawer({
|
|
|
848
1217
|
selected,
|
|
849
1218
|
onSelect
|
|
850
1219
|
}) {
|
|
851
|
-
const visible =
|
|
852
|
-
return /* @__PURE__ */
|
|
853
|
-
/* @__PURE__ */
|
|
854
|
-
selected ? /* @__PURE__ */
|
|
1220
|
+
const visible = useMemo3(() => filterGenerations(generations, typeFilter), [generations, typeFilter]);
|
|
1221
|
+
return /* @__PURE__ */ jsxs8(StudioSheet, { open, onOpenChange, title: "Asset library", children: [
|
|
1222
|
+
/* @__PURE__ */ jsxs8("header", { className: "flex shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-3", children: [
|
|
1223
|
+
selected ? /* @__PURE__ */ jsxs8(
|
|
855
1224
|
"button",
|
|
856
1225
|
{
|
|
857
1226
|
type: "button",
|
|
858
1227
|
onClick: () => onSelect(null),
|
|
859
1228
|
className: "flex items-center gap-1.5 text-sm font-medium text-foreground transition-colors hover:text-primary",
|
|
860
1229
|
children: [
|
|
861
|
-
/* @__PURE__ */
|
|
1230
|
+
/* @__PURE__ */ jsx9(ArrowLeft, { className: "h-4 w-4" }),
|
|
862
1231
|
"Back to library"
|
|
863
1232
|
]
|
|
864
1233
|
}
|
|
865
|
-
) : /* @__PURE__ */
|
|
866
|
-
/* @__PURE__ */
|
|
1234
|
+
) : /* @__PURE__ */ jsx9("h2", { className: "text-sm font-semibold text-foreground", children: "Asset library" }),
|
|
1235
|
+
/* @__PURE__ */ jsx9(
|
|
867
1236
|
"button",
|
|
868
1237
|
{
|
|
869
1238
|
type: "button",
|
|
870
1239
|
"aria-label": "Close library",
|
|
871
1240
|
onClick: () => onOpenChange(false),
|
|
872
1241
|
className: "rounded-md p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
873
|
-
children: /* @__PURE__ */
|
|
1242
|
+
children: /* @__PURE__ */ jsx9(X2, { className: "h-4 w-4" })
|
|
874
1243
|
}
|
|
875
1244
|
)
|
|
876
1245
|
] }),
|
|
877
|
-
selected ? /* @__PURE__ */
|
|
1246
|
+
selected ? /* @__PURE__ */ jsx9("div", { className: "flex-1 overflow-y-auto p-4", children: /* @__PURE__ */ jsx9(
|
|
878
1247
|
GenerationDetail,
|
|
879
1248
|
{
|
|
880
1249
|
generation: selected,
|
|
881
1250
|
vaultHref,
|
|
882
1251
|
onNavigate: () => onOpenChange(false)
|
|
883
1252
|
}
|
|
884
|
-
) }) : /* @__PURE__ */
|
|
885
|
-
/* @__PURE__ */
|
|
886
|
-
/* @__PURE__ */
|
|
887
|
-
/* @__PURE__ */
|
|
888
|
-
/* @__PURE__ */
|
|
1253
|
+
) }) : /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
1254
|
+
/* @__PURE__ */ jsxs8("div", { className: "shrink-0 border-b border-border px-4 pt-3", children: [
|
|
1255
|
+
/* @__PURE__ */ jsxs8("div", { className: "mb-2 flex items-center justify-between gap-3", children: [
|
|
1256
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
1257
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
889
1258
|
visible.length,
|
|
890
1259
|
" generation",
|
|
891
1260
|
visible.length !== 1 ? "s" : ""
|
|
892
1261
|
] }),
|
|
893
|
-
/* @__PURE__ */
|
|
894
|
-
/* @__PURE__ */
|
|
1262
|
+
/* @__PURE__ */ jsxs8("span", { className: "flex items-center gap-0.5", children: [
|
|
1263
|
+
/* @__PURE__ */ jsx9(DollarSign, { className: "h-3 w-3" }),
|
|
895
1264
|
totalCost.toFixed(2),
|
|
896
1265
|
" spent"
|
|
897
1266
|
] })
|
|
898
1267
|
] }),
|
|
899
|
-
vaultHref && /* @__PURE__ */
|
|
900
|
-
/* @__PURE__ */
|
|
1268
|
+
vaultHref && /* @__PURE__ */ jsx9(Link2, { to: vaultHref(), prefetch: "intent", onClick: () => onOpenChange(false), children: /* @__PURE__ */ jsxs8(Button3, { size: "sm", variant: "outline", children: [
|
|
1269
|
+
/* @__PURE__ */ jsx9(FolderOpen2, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
901
1270
|
"Vault"
|
|
902
1271
|
] }) })
|
|
903
1272
|
] }),
|
|
904
|
-
/* @__PURE__ */
|
|
905
|
-
/* @__PURE__ */
|
|
906
|
-
Object.entries(TYPE_CONFIG).map(([key, cfg]) => /* @__PURE__ */
|
|
1273
|
+
/* @__PURE__ */ jsx9(Tabs, { value: typeFilter ?? "all", onValueChange: (v) => onFilterChange(v === "all" ? null : v), children: /* @__PURE__ */ jsxs8(TabsList, { className: "h-9 w-full justify-start gap-1 overflow-x-auto bg-transparent", children: [
|
|
1274
|
+
/* @__PURE__ */ jsx9(TabsTrigger, { value: "all", className: "text-xs", children: "All" }),
|
|
1275
|
+
Object.entries(TYPE_CONFIG).map(([key, cfg]) => /* @__PURE__ */ jsx9(TabsTrigger, { value: key, className: "text-xs", children: cfg.label }, key))
|
|
907
1276
|
] }) })
|
|
908
1277
|
] }),
|
|
909
|
-
/* @__PURE__ */
|
|
1278
|
+
/* @__PURE__ */ jsx9("div", { className: "flex-1 overflow-y-auto p-4", children: /* @__PURE__ */ jsx9(GenerationGrid, { generations: visible, typeFilter, onSelect }) })
|
|
910
1279
|
] })
|
|
911
1280
|
] });
|
|
912
1281
|
}
|
|
913
1282
|
|
|
914
1283
|
// src/studio-react/studio-workspace.tsx
|
|
915
|
-
import { Fragment as
|
|
1284
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
916
1285
|
function StudioWorkspace({
|
|
917
1286
|
generations,
|
|
918
1287
|
totalCost,
|
|
@@ -923,8 +1292,8 @@ function StudioWorkspace({
|
|
|
923
1292
|
}) {
|
|
924
1293
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
925
1294
|
const typeFilter = searchParams.get("type");
|
|
926
|
-
const [drawerOpen, setDrawerOpen] =
|
|
927
|
-
const [selected, setSelected] =
|
|
1295
|
+
const [drawerOpen, setDrawerOpen] = useState4(false);
|
|
1296
|
+
const [selected, setSelected] = useState4(null);
|
|
928
1297
|
const canvasRef = useRef3(null);
|
|
929
1298
|
const canGenerate = role !== "viewer";
|
|
930
1299
|
const resolvedVaultHref = vaultHref ?? (workspaceId ? (filePath) => filePath ? `/app/${workspaceId}/vault?file=${encodeURIComponent(filePath)}` : `/app/${workspaceId}/vault` : void 0);
|
|
@@ -944,8 +1313,8 @@ function StudioWorkspace({
|
|
|
944
1313
|
setSelected(generation);
|
|
945
1314
|
setDrawerOpen(true);
|
|
946
1315
|
}
|
|
947
|
-
return /* @__PURE__ */
|
|
948
|
-
/* @__PURE__ */
|
|
1316
|
+
return /* @__PURE__ */ jsxs9("div", { className: "min-h-0 flex-1 overflow-y-auto bg-background", children: [
|
|
1317
|
+
/* @__PURE__ */ jsx10(
|
|
949
1318
|
StudioHeader,
|
|
950
1319
|
{
|
|
951
1320
|
count: mergedGenerations.length,
|
|
@@ -953,9 +1322,9 @@ function StudioWorkspace({
|
|
|
953
1322
|
onOpenLibrary: openLibrary
|
|
954
1323
|
}
|
|
955
1324
|
),
|
|
956
|
-
/* @__PURE__ */
|
|
957
|
-
/* @__PURE__ */
|
|
958
|
-
|
|
1325
|
+
/* @__PURE__ */ jsx10("div", { className: "mx-auto w-full max-w-3xl space-y-4 px-4 py-6 sm:px-6", children: canGenerate ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
1326
|
+
/* @__PURE__ */ jsx10(
|
|
1327
|
+
StudioComposer,
|
|
959
1328
|
{
|
|
960
1329
|
workspaceId,
|
|
961
1330
|
onGenerated: (generation) => {
|
|
@@ -966,7 +1335,7 @@ function StudioWorkspace({
|
|
|
966
1335
|
}
|
|
967
1336
|
}
|
|
968
1337
|
),
|
|
969
|
-
/* @__PURE__ */
|
|
1338
|
+
/* @__PURE__ */ jsx10("div", { ref: canvasRef, children: /* @__PURE__ */ jsx10(
|
|
970
1339
|
ResultCanvas,
|
|
971
1340
|
{
|
|
972
1341
|
batch: latestBatch,
|
|
@@ -974,16 +1343,16 @@ function StudioWorkspace({
|
|
|
974
1343
|
onSelect: openDetail
|
|
975
1344
|
}
|
|
976
1345
|
) })
|
|
977
|
-
] }) : /* @__PURE__ */
|
|
978
|
-
/* @__PURE__ */
|
|
979
|
-
/* @__PURE__ */
|
|
980
|
-
/* @__PURE__ */
|
|
981
|
-
/* @__PURE__ */
|
|
982
|
-
/* @__PURE__ */
|
|
1346
|
+
] }) : /* @__PURE__ */ jsxs9("section", { className: "flex flex-col items-center justify-center gap-3 rounded-xl border border-border bg-card p-10 text-center shadow-sm", children: [
|
|
1347
|
+
/* @__PURE__ */ jsx10("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-muted", children: /* @__PURE__ */ jsx10(Images2, { className: "h-6 w-6 text-muted-foreground/40" }) }),
|
|
1348
|
+
/* @__PURE__ */ jsx10("p", { className: "text-sm font-medium text-foreground", children: mergedGenerations.length > 0 ? `${mergedGenerations.length} asset${mergedGenerations.length !== 1 ? "s" : ""} in this workspace` : "No generations yet" }),
|
|
1349
|
+
/* @__PURE__ */ jsx10("p", { className: "max-w-sm text-xs text-muted-foreground", children: "Team generations appear in the library." }),
|
|
1350
|
+
/* @__PURE__ */ jsxs9(Button4, { size: "sm", variant: "outline", onClick: openLibrary, children: [
|
|
1351
|
+
/* @__PURE__ */ jsx10(Images2, { className: "mr-1.5 h-4 w-4" }),
|
|
983
1352
|
"Open library"
|
|
984
1353
|
] })
|
|
985
1354
|
] }) }),
|
|
986
|
-
/* @__PURE__ */
|
|
1355
|
+
/* @__PURE__ */ jsx10(
|
|
987
1356
|
LibraryDrawer,
|
|
988
1357
|
{
|
|
989
1358
|
open: drawerOpen,
|
|
@@ -1006,15 +1375,32 @@ function StudioWorkspace({
|
|
|
1006
1375
|
] });
|
|
1007
1376
|
}
|
|
1008
1377
|
|
|
1378
|
+
// src/studio-react/generation-notice.tsx
|
|
1379
|
+
import { Info } from "lucide-react";
|
|
1380
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1381
|
+
function GenerationNoticeChip({ className }) {
|
|
1382
|
+
return /* @__PURE__ */ jsxs10(
|
|
1383
|
+
"p",
|
|
1384
|
+
{
|
|
1385
|
+
className: `inline-block rounded-full border px-3.5 py-1.5 text-center text-[12.5px] font-medium leading-relaxed text-foreground shadow-sm ${className ?? ""}`,
|
|
1386
|
+
style: { background: "var(--studio-notice-bg)", borderColor: "var(--studio-notice-border)" },
|
|
1387
|
+
children: [
|
|
1388
|
+
/* @__PURE__ */ jsx11(Info, { className: "mr-1.5 inline-block h-[14px] w-[14px] align-[-2px] text-primary", strokeWidth: 1.5, "aria-hidden": true }),
|
|
1389
|
+
"Each prompt starts a new generation \u2014 this chat does not remember the last one."
|
|
1390
|
+
]
|
|
1391
|
+
}
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1009
1395
|
// src/studio-react/library-panel.tsx
|
|
1010
|
-
import { useMemo as
|
|
1396
|
+
import { useMemo as useMemo4 } from "react";
|
|
1011
1397
|
import {
|
|
1012
1398
|
Tabs as Tabs2,
|
|
1013
1399
|
TabsList as TabsList2,
|
|
1014
1400
|
TabsTrigger as TabsTrigger2
|
|
1015
1401
|
} from "@tangle-network/sandbox-ui/primitives";
|
|
1016
1402
|
import { DollarSign as DollarSign2 } from "lucide-react";
|
|
1017
|
-
import { jsx as
|
|
1403
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1018
1404
|
function LibraryPanel({
|
|
1019
1405
|
generations,
|
|
1020
1406
|
totalCost,
|
|
@@ -1022,11 +1408,11 @@ function LibraryPanel({
|
|
|
1022
1408
|
onFilterChange,
|
|
1023
1409
|
onSelect
|
|
1024
1410
|
}) {
|
|
1025
|
-
const visible =
|
|
1026
|
-
return /* @__PURE__ */
|
|
1027
|
-
/* @__PURE__ */
|
|
1028
|
-
/* @__PURE__ */
|
|
1029
|
-
/* @__PURE__ */
|
|
1411
|
+
const visible = useMemo4(() => filterGenerations(generations, typeFilter), [generations, typeFilter]);
|
|
1412
|
+
return /* @__PURE__ */ jsxs11("div", { className: "flex min-w-0 flex-col", children: [
|
|
1413
|
+
/* @__PURE__ */ jsxs11("div", { className: "mb-4 flex flex-wrap items-center justify-between gap-3", children: [
|
|
1414
|
+
/* @__PURE__ */ jsx12(Tabs2, { value: typeFilter ?? "all", onValueChange: (v) => onFilterChange(v === "all" ? null : v), children: /* @__PURE__ */ jsxs11(TabsList2, { className: "h-9 justify-start gap-1 overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-background p-1", children: [
|
|
1415
|
+
/* @__PURE__ */ jsx12(
|
|
1030
1416
|
TabsTrigger2,
|
|
1031
1417
|
{
|
|
1032
1418
|
value: "all",
|
|
@@ -1034,7 +1420,7 @@ function LibraryPanel({
|
|
|
1034
1420
|
children: "All"
|
|
1035
1421
|
}
|
|
1036
1422
|
),
|
|
1037
|
-
Object.entries(TYPE_CONFIG).map(([key, cfg]) => /* @__PURE__ */
|
|
1423
|
+
Object.entries(TYPE_CONFIG).map(([key, cfg]) => /* @__PURE__ */ jsx12(
|
|
1038
1424
|
TabsTrigger2,
|
|
1039
1425
|
{
|
|
1040
1426
|
value: key,
|
|
@@ -1044,158 +1430,54 @@ function LibraryPanel({
|
|
|
1044
1430
|
key
|
|
1045
1431
|
))
|
|
1046
1432
|
] }) }),
|
|
1047
|
-
/* @__PURE__ */
|
|
1048
|
-
/* @__PURE__ */
|
|
1433
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
1434
|
+
/* @__PURE__ */ jsxs11("span", { children: [
|
|
1049
1435
|
visible.length,
|
|
1050
1436
|
" generation",
|
|
1051
1437
|
visible.length !== 1 ? "s" : ""
|
|
1052
1438
|
] }),
|
|
1053
|
-
/* @__PURE__ */
|
|
1054
|
-
/* @__PURE__ */
|
|
1439
|
+
/* @__PURE__ */ jsxs11("span", { className: "flex items-center gap-0.5", children: [
|
|
1440
|
+
/* @__PURE__ */ jsx12(DollarSign2, { className: "h-3 w-3" }),
|
|
1055
1441
|
totalCost.toFixed(2),
|
|
1056
1442
|
" spent"
|
|
1057
1443
|
] })
|
|
1058
1444
|
] })
|
|
1059
1445
|
] }),
|
|
1060
|
-
/* @__PURE__ */
|
|
1446
|
+
/* @__PURE__ */ jsx12(GenerationGrid, { generations: visible, typeFilter, onSelect })
|
|
1061
1447
|
] });
|
|
1062
1448
|
}
|
|
1063
1449
|
|
|
1064
1450
|
// src/studio-react/generation-detail-modal.tsx
|
|
1065
1451
|
import { Dialog as Dialog2, DialogContent, DialogHeader, DialogTitle } from "@tangle-network/sandbox-ui/primitives";
|
|
1066
|
-
import { jsx as
|
|
1452
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1067
1453
|
function GenerationDetailModal({
|
|
1068
1454
|
generation,
|
|
1069
1455
|
vaultHref,
|
|
1070
1456
|
onClose
|
|
1071
1457
|
}) {
|
|
1072
1458
|
const cfg = generation ? typeConfigFor(generation.type) : null;
|
|
1073
|
-
return /* @__PURE__ */
|
|
1459
|
+
return /* @__PURE__ */ jsx13(Dialog2, { open: generation != null, onOpenChange: (open) => {
|
|
1074
1460
|
if (!open) onClose();
|
|
1075
|
-
}, children: /* @__PURE__ */
|
|
1076
|
-
/* @__PURE__ */
|
|
1077
|
-
generation && /* @__PURE__ */
|
|
1461
|
+
}, children: /* @__PURE__ */ jsxs12(DialogContent, { className: "max-h-[85vh] max-w-2xl overflow-y-auto border-card-edge bg-popover shadow-[var(--shadow-overlay)]", children: [
|
|
1462
|
+
/* @__PURE__ */ jsx13(DialogHeader, { children: /* @__PURE__ */ jsx13(DialogTitle, { className: "text-left text-[15px] font-semibold", children: cfg ? `${cfg.label} generation` : "Generation" }) }),
|
|
1463
|
+
generation && /* @__PURE__ */ jsx13(GenerationDetail, { generation, vaultHref, onNavigate: onClose })
|
|
1078
1464
|
] }) });
|
|
1079
1465
|
}
|
|
1080
|
-
|
|
1081
|
-
// src/studio-react/avatar-composer.tsx
|
|
1082
|
-
import { Input as Input5 } from "@tangle-network/sandbox-ui/primitives";
|
|
1083
|
-
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1084
|
-
function AvatarComposer({
|
|
1085
|
-
audioUrl,
|
|
1086
|
-
imageUrl,
|
|
1087
|
-
avatarId,
|
|
1088
|
-
onAudioUrlChange,
|
|
1089
|
-
onImageUrlChange,
|
|
1090
|
-
onAvatarIdChange
|
|
1091
|
-
}) {
|
|
1092
|
-
return /* @__PURE__ */ jsxs14("div", { className: "mt-4 grid gap-3 sm:grid-cols-2", children: [
|
|
1093
|
-
/* @__PURE__ */ jsx16(Field, { label: "Audio URL", htmlFor: "studio-audio-url", children: /* @__PURE__ */ jsx16(
|
|
1094
|
-
Input5,
|
|
1095
|
-
{
|
|
1096
|
-
id: "studio-audio-url",
|
|
1097
|
-
value: audioUrl,
|
|
1098
|
-
onChange: (event) => onAudioUrlChange(event.target.value),
|
|
1099
|
-
placeholder: "https://cdn.example.com/source-audio.mp3",
|
|
1100
|
-
className: "bg-background"
|
|
1101
|
-
}
|
|
1102
|
-
) }),
|
|
1103
|
-
/* @__PURE__ */ jsx16(Field, { label: "Image URL", htmlFor: "studio-avatar-image-url", children: /* @__PURE__ */ jsx16(
|
|
1104
|
-
Input5,
|
|
1105
|
-
{
|
|
1106
|
-
id: "studio-avatar-image-url",
|
|
1107
|
-
value: imageUrl,
|
|
1108
|
-
onChange: (event) => onImageUrlChange(event.target.value),
|
|
1109
|
-
placeholder: "https://cdn.example.com/portrait.png",
|
|
1110
|
-
className: "bg-background"
|
|
1111
|
-
}
|
|
1112
|
-
) }),
|
|
1113
|
-
/* @__PURE__ */ jsx16(Field, { label: "Avatar ID", htmlFor: "studio-avatar-id", children: /* @__PURE__ */ jsx16(
|
|
1114
|
-
Input5,
|
|
1115
|
-
{
|
|
1116
|
-
id: "studio-avatar-id",
|
|
1117
|
-
value: avatarId,
|
|
1118
|
-
onChange: (event) => onAvatarIdChange(event.target.value),
|
|
1119
|
-
placeholder: "Optional provider avatar id",
|
|
1120
|
-
className: "bg-background"
|
|
1121
|
-
}
|
|
1122
|
-
) })
|
|
1123
|
-
] });
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
// src/studio-react/transcription-composer.tsx
|
|
1127
|
-
import { Input as Input6 } from "@tangle-network/sandbox-ui/primitives";
|
|
1128
|
-
import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1129
|
-
function TranscriptionComposer({
|
|
1130
|
-
audioUrl,
|
|
1131
|
-
language,
|
|
1132
|
-
onAudioUrlChange,
|
|
1133
|
-
onLanguageChange
|
|
1134
|
-
}) {
|
|
1135
|
-
return /* @__PURE__ */ jsxs15("div", { className: "mt-4 grid gap-3 sm:grid-cols-2", children: [
|
|
1136
|
-
/* @__PURE__ */ jsx17(Field, { label: "Audio URL", htmlFor: "studio-audio-url", children: /* @__PURE__ */ jsx17(
|
|
1137
|
-
Input6,
|
|
1138
|
-
{
|
|
1139
|
-
id: "studio-audio-url",
|
|
1140
|
-
value: audioUrl,
|
|
1141
|
-
onChange: (event) => onAudioUrlChange(event.target.value),
|
|
1142
|
-
placeholder: "https://cdn.example.com/source-audio.mp3",
|
|
1143
|
-
className: "bg-background"
|
|
1144
|
-
}
|
|
1145
|
-
) }),
|
|
1146
|
-
/* @__PURE__ */ jsx17(Field, { label: "Language", htmlFor: "studio-transcription-language", children: /* @__PURE__ */ jsx17(
|
|
1147
|
-
Input6,
|
|
1148
|
-
{
|
|
1149
|
-
id: "studio-transcription-language",
|
|
1150
|
-
value: language,
|
|
1151
|
-
onChange: (event) => onLanguageChange(event.target.value),
|
|
1152
|
-
placeholder: "en",
|
|
1153
|
-
className: "bg-background"
|
|
1154
|
-
}
|
|
1155
|
-
) })
|
|
1156
|
-
] });
|
|
1157
|
-
}
|
|
1158
|
-
function TranscriptionOptions({
|
|
1159
|
-
responseFormat,
|
|
1160
|
-
temperature,
|
|
1161
|
-
onResponseFormatChange,
|
|
1162
|
-
onTemperatureChange
|
|
1163
|
-
}) {
|
|
1164
|
-
return /* @__PURE__ */ jsxs15(Fragment3, { children: [
|
|
1165
|
-
/* @__PURE__ */ jsx17(Field, { label: "Response format", children: /* @__PURE__ */ jsxs15(NativeSelect, { value: responseFormat, onChange: (event) => onResponseFormatChange(event.target.value), children: [
|
|
1166
|
-
/* @__PURE__ */ jsx17("option", { value: "json", children: "JSON" }),
|
|
1167
|
-
/* @__PURE__ */ jsx17("option", { value: "text", children: "Text" }),
|
|
1168
|
-
/* @__PURE__ */ jsx17("option", { value: "srt", children: "SRT" }),
|
|
1169
|
-
/* @__PURE__ */ jsx17("option", { value: "verbose_json", children: "Verbose JSON" }),
|
|
1170
|
-
/* @__PURE__ */ jsx17("option", { value: "vtt", children: "VTT" })
|
|
1171
|
-
] }) }),
|
|
1172
|
-
/* @__PURE__ */ jsx17(Field, { label: "Temperature", children: /* @__PURE__ */ jsx17(Input6, { type: "number", min: "0", max: "1", step: "0.1", value: temperature, onChange: (event) => onTemperatureChange(event.target.value), className: "bg-background" }) })
|
|
1173
|
-
] });
|
|
1174
|
-
}
|
|
1175
1466
|
export {
|
|
1176
|
-
AvatarComposer,
|
|
1177
|
-
ComposerDisclosure,
|
|
1178
|
-
ComposerHero,
|
|
1179
|
-
Field,
|
|
1180
1467
|
GenerationCard,
|
|
1181
1468
|
GenerationDetail,
|
|
1182
1469
|
GenerationDetailModal,
|
|
1183
1470
|
GenerationGrid,
|
|
1471
|
+
GenerationNoticeChip,
|
|
1184
1472
|
GenerationStatusBadge,
|
|
1185
|
-
ImageComposer,
|
|
1186
1473
|
LibraryDrawer,
|
|
1187
1474
|
LibraryPanel,
|
|
1188
|
-
NativeSelect,
|
|
1189
1475
|
ResultCanvas,
|
|
1190
|
-
|
|
1191
|
-
Stepper,
|
|
1476
|
+
StudioComposer,
|
|
1192
1477
|
StudioHeader,
|
|
1193
1478
|
StudioSheet,
|
|
1194
1479
|
StudioWorkspace,
|
|
1195
1480
|
TYPE_CONFIG,
|
|
1196
|
-
TranscriptionComposer,
|
|
1197
|
-
TranscriptionOptions,
|
|
1198
|
-
VideoComposer,
|
|
1199
1481
|
filterGenerations,
|
|
1200
1482
|
typeConfigFor,
|
|
1201
1483
|
useStudioGenerations
|