@tangle-network/agent-app 0.45.65 → 0.46.1
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/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/stories/studio/fixtures.d.ts +0 -4
- package/dist/studio/generation.d.ts +16 -74
- package/dist/studio/index.d.ts +2 -2
- package/dist/studio/index.js +27 -25
- 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 +6 -12
- package/dist/studio-react/index.js +902 -827
- package/dist/studio-react/index.js.map +1 -1
- package/dist/studio-react/studio-composer.d.ts +34 -0
- package/dist/studio-react/studio-workspace.d.ts +2 -4
- 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 +1 -1
- package/dist/chunk-23J72UUA.js.map +0 -1
- package/dist/chunk-HSBJB46C.js +0 -305
- package/dist/chunk-HSBJB46C.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 -12
- package/dist/studio-react/composer-shell.d.ts +0 -18
- package/dist/studio-react/image-composer.d.ts +0 -8
- package/dist/studio-react/publish-package-composer.d.ts +0 -18
- 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,44 +1,43 @@
|
|
|
1
1
|
import {
|
|
2
|
-
CADENCES,
|
|
3
|
-
DESTINATIONS,
|
|
4
|
-
IMAGE_QUALITIES,
|
|
5
|
-
IMAGE_SIZE_HINT,
|
|
6
|
-
IMAGE_SIZE_PATTERN,
|
|
7
|
-
MAX_IMAGE_COUNT,
|
|
8
|
-
MIN_IMAGE_COUNT,
|
|
9
|
-
VIDEO_ASPECT_RATIOS,
|
|
10
|
-
VIDEO_DURATIONS,
|
|
11
|
-
VIDEO_RESOLUTIONS,
|
|
12
2
|
buildGenerationRequestBody,
|
|
13
|
-
|
|
3
|
+
curateComposerModels,
|
|
14
4
|
failedOptimisticGeneration,
|
|
15
5
|
generationError,
|
|
16
6
|
generationStatus,
|
|
17
7
|
generationVaultPath,
|
|
18
|
-
|
|
8
|
+
imageToVideoSibling,
|
|
19
9
|
isGenerationType,
|
|
20
10
|
isLocalGeneration,
|
|
21
|
-
isPublishPackage,
|
|
22
11
|
latestBatchOf,
|
|
23
12
|
mergeLiveGeneration,
|
|
24
13
|
mergeLoaderAndLive,
|
|
25
14
|
modelMessage,
|
|
26
15
|
normalizeImageCount,
|
|
27
16
|
optimisticGeneration,
|
|
28
|
-
|
|
17
|
+
optionChoices,
|
|
29
18
|
preferredModelId,
|
|
19
|
+
reconcileOptionValues,
|
|
30
20
|
relativeTime,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
resolveComposerOptions,
|
|
22
|
+
supportsCustomImageSize,
|
|
23
|
+
textToVideoSibling,
|
|
24
|
+
userSafeGenerationMessage,
|
|
25
|
+
validateCustomImageSize
|
|
26
|
+
} from "../chunk-2RTYQU4W.js";
|
|
34
27
|
import {
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
CheckGlyph,
|
|
29
|
+
ChevronDown,
|
|
30
|
+
OVERLAY_SHADOW,
|
|
31
|
+
POPOVER_OPTION_FOCUS,
|
|
32
|
+
PopoverSurface,
|
|
33
|
+
ProviderLogo,
|
|
34
|
+
usePopover
|
|
35
|
+
} from "../chunk-YDOQE47G.js";
|
|
37
36
|
|
|
38
37
|
// src/studio-react/studio-workspace.tsx
|
|
39
|
-
import { useRef as useRef3, useState as
|
|
38
|
+
import { useRef as useRef3, useState as useState4 } from "react";
|
|
40
39
|
import { useSearchParams } from "react-router";
|
|
41
|
-
import { Button as
|
|
40
|
+
import { Button as Button4 } from "@tangle-network/sandbox-ui/primitives";
|
|
42
41
|
import { Images as Images2 } from "lucide-react";
|
|
43
42
|
|
|
44
43
|
// src/studio-react/use-studio-generations.ts
|
|
@@ -97,477 +96,651 @@ function useStudioGenerations(loaderGenerations, options = {}) {
|
|
|
97
96
|
return { mergedGenerations, latestBatch, onGenerated };
|
|
98
97
|
}
|
|
99
98
|
|
|
100
|
-
// src/studio-react/composer
|
|
101
|
-
import { useEffect as
|
|
102
|
-
import {
|
|
103
|
-
Badge as Badge2,
|
|
104
|
-
Button as Button2,
|
|
105
|
-
Input as Input7,
|
|
106
|
-
Label as Label2,
|
|
107
|
-
Textarea as Textarea2,
|
|
108
|
-
Select,
|
|
109
|
-
SelectContent,
|
|
110
|
-
SelectItem,
|
|
111
|
-
SelectTrigger,
|
|
112
|
-
SelectValue
|
|
113
|
-
} from "@tangle-network/sandbox-ui/primitives";
|
|
114
|
-
import { useIntegrations } from "@tangle-network/sandbox-ui/integrations";
|
|
115
|
-
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";
|
|
116
102
|
|
|
117
|
-
// src/studio-react/
|
|
118
|
-
import {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
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"}`;
|
|
130
117
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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,
|
|
140
155
|
children
|
|
141
156
|
}) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
]
|
|
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
|
+
);
|
|
153
194
|
}
|
|
154
|
-
function
|
|
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]);
|
|
210
|
+
}
|
|
211
|
+
function OptionPill({
|
|
212
|
+
label,
|
|
155
213
|
value,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
214
|
+
choices,
|
|
215
|
+
onSelect,
|
|
216
|
+
bandRef,
|
|
217
|
+
custom
|
|
159
218
|
}) {
|
|
160
|
-
|
|
161
|
-
|
|
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(
|
|
162
231
|
"button",
|
|
163
232
|
{
|
|
164
233
|
type: "button",
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
+
]
|
|
170
247
|
}
|
|
171
248
|
),
|
|
172
|
-
/* @__PURE__ */
|
|
173
|
-
|
|
174
|
-
"button",
|
|
249
|
+
/* @__PURE__ */ jsxs(
|
|
250
|
+
PopoverSurface,
|
|
175
251
|
{
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
+
]
|
|
182
290
|
}
|
|
183
291
|
)
|
|
184
292
|
] });
|
|
185
293
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
summary
|
|
191
|
-
] }),
|
|
192
|
-
/* @__PURE__ */ jsx("div", { className: "border-t border-border px-3 py-3", children })
|
|
193
|
-
] });
|
|
194
|
-
}
|
|
195
|
-
function NativeSelect(props) {
|
|
196
|
-
return /* @__PURE__ */ jsx("select", { ...props, className: "h-9 w-full rounded-md border border-input bg-background px-3 text-sm" });
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// src/studio-react/image-composer.tsx
|
|
200
|
-
import { Input } from "@tangle-network/sandbox-ui/primitives";
|
|
201
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
202
|
-
function ImageComposer({
|
|
203
|
-
size,
|
|
204
|
-
quality,
|
|
205
|
-
imageCount,
|
|
206
|
-
onSizeChange,
|
|
207
|
-
onQualityChange,
|
|
208
|
-
onImageCountChange
|
|
294
|
+
function CustomSizeForm({
|
|
295
|
+
initial,
|
|
296
|
+
onApply,
|
|
297
|
+
onCancel
|
|
209
298
|
}) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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",
|
|
214
330
|
{
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
|
220
339
|
}
|
|
221
|
-
)
|
|
222
|
-
/* @__PURE__ */ jsx2(Field, { label: "Quality", children: /* @__PURE__ */ jsx2(NativeSelect, { value: quality, onChange: (event) => onQualityChange(event.target.value), children: IMAGE_QUALITIES.map((option) => /* @__PURE__ */ jsx2("option", { value: option, children: option.charAt(0).toUpperCase() + option.slice(1) }, option)) }) })
|
|
340
|
+
)
|
|
223
341
|
] }),
|
|
224
|
-
/* @__PURE__ */
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
onResolutionChange,
|
|
246
|
-
onAspectRatioChange,
|
|
247
|
-
onReferenceImageUrlChange
|
|
248
|
-
}) {
|
|
249
|
-
return /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
250
|
-
/* @__PURE__ */ jsx3(Field, { label: "Duration (s)", children: /* @__PURE__ */ jsx3(NativeSelect, { value: duration, onChange: (event) => onDurationChange(event.target.value), children: VIDEO_DURATIONS.map((option) => /* @__PURE__ */ jsx3("option", { value: option, children: option }, option)) }) }),
|
|
251
|
-
/* @__PURE__ */ jsx3(Field, { label: "Resolution", children: /* @__PURE__ */ jsx3(NativeSelect, { value: resolution, onChange: (event) => onResolutionChange(event.target.value), children: VIDEO_RESOLUTIONS.map((option) => /* @__PURE__ */ jsx3("option", { value: option, children: option }, option)) }) }),
|
|
252
|
-
/* @__PURE__ */ jsx3(Field, { label: "Aspect ratio", children: /* @__PURE__ */ jsx3(NativeSelect, { value: aspectRatio, onChange: (event) => onAspectRatioChange(event.target.value), children: VIDEO_ASPECT_RATIOS.map((option) => /* @__PURE__ */ jsx3("option", { value: option, children: option }, option)) }) }),
|
|
253
|
-
/* @__PURE__ */ jsx3(Field, { label: "Reference image URL", children: /* @__PURE__ */ jsx3(Input2, { value: referenceImageUrl, onChange: (event) => onReferenceImageUrlChange(event.target.value), className: "bg-background" }) })
|
|
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
|
+
] })
|
|
254
363
|
] });
|
|
255
364
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
+
);
|
|
265
380
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
imageUrl,
|
|
273
|
-
avatarId,
|
|
274
|
-
onAudioUrlChange,
|
|
275
|
-
onImageUrlChange,
|
|
276
|
-
onAvatarIdChange
|
|
381
|
+
function ReferencePill({
|
|
382
|
+
url,
|
|
383
|
+
onAttach,
|
|
384
|
+
onRemove,
|
|
385
|
+
pick,
|
|
386
|
+
bandRef
|
|
277
387
|
}) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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",
|
|
400
|
+
{
|
|
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 })
|
|
406
|
+
}
|
|
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",
|
|
291
424
|
{
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
+
]
|
|
297
443
|
}
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
|
|
444
|
+
),
|
|
445
|
+
!pick && /* @__PURE__ */ jsxs(
|
|
446
|
+
PopoverSurface,
|
|
301
447
|
{
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
className:
|
|
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
|
+
]
|
|
307
493
|
}
|
|
308
|
-
)
|
|
494
|
+
)
|
|
309
495
|
] });
|
|
310
496
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
onAudioUrlChange,
|
|
319
|
-
onLanguageChange
|
|
497
|
+
function ModelPill({
|
|
498
|
+
models,
|
|
499
|
+
value,
|
|
500
|
+
displayName,
|
|
501
|
+
provider,
|
|
502
|
+
onSelect,
|
|
503
|
+
bandRef
|
|
320
504
|
}) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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",
|
|
324
512
|
{
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
+
]
|
|
330
525
|
}
|
|
331
|
-
)
|
|
332
|
-
/* @__PURE__ */
|
|
333
|
-
|
|
526
|
+
),
|
|
527
|
+
/* @__PURE__ */ jsxs(
|
|
528
|
+
PopoverSurface,
|
|
334
529
|
{
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
+
]
|
|
340
561
|
}
|
|
341
|
-
)
|
|
342
|
-
] });
|
|
343
|
-
}
|
|
344
|
-
function TranscriptionOptions({
|
|
345
|
-
responseFormat,
|
|
346
|
-
temperature,
|
|
347
|
-
onResponseFormatChange,
|
|
348
|
-
onTemperatureChange
|
|
349
|
-
}) {
|
|
350
|
-
return /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
351
|
-
/* @__PURE__ */ jsx6(Field, { label: "Response format", children: /* @__PURE__ */ jsxs5(NativeSelect, { value: responseFormat, onChange: (event) => onResponseFormatChange(event.target.value), children: [
|
|
352
|
-
/* @__PURE__ */ jsx6("option", { value: "json", children: "JSON" }),
|
|
353
|
-
/* @__PURE__ */ jsx6("option", { value: "text", children: "Text" }),
|
|
354
|
-
/* @__PURE__ */ jsx6("option", { value: "srt", children: "SRT" }),
|
|
355
|
-
/* @__PURE__ */ jsx6("option", { value: "verbose_json", children: "Verbose JSON" }),
|
|
356
|
-
/* @__PURE__ */ jsx6("option", { value: "vtt", children: "VTT" })
|
|
357
|
-
] }) }),
|
|
358
|
-
/* @__PURE__ */ jsx6(Field, { label: "Temperature", children: /* @__PURE__ */ jsx6(Input5, { type: "number", min: "0", max: "1", step: "0.1", value: temperature, onChange: (event) => onTemperatureChange(event.target.value), className: "bg-background" }) })
|
|
562
|
+
)
|
|
359
563
|
] });
|
|
360
564
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
connections,
|
|
375
|
-
connectionError,
|
|
376
|
-
connectionsLoading,
|
|
377
|
-
integrationsHref,
|
|
378
|
-
canManageIntegrations,
|
|
379
|
-
onCaptionChange,
|
|
380
|
-
onDescriptionChange,
|
|
381
|
-
onMentionsChange,
|
|
382
|
-
onCadenceChange,
|
|
383
|
-
onDestinationToggle
|
|
384
|
-
}) {
|
|
385
|
-
return /* @__PURE__ */ jsxs6("div", { className: "space-y-3", children: [
|
|
386
|
-
/* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between gap-3", children: [
|
|
387
|
-
connectionError ? /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 rounded-md border border-warning/25 bg-warning/10 px-2.5 py-1.5 text-xs text-warning", children: [
|
|
388
|
-
/* @__PURE__ */ jsx7(AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
389
|
-
"Couldn't load connected apps. Check Integrations."
|
|
390
|
-
] }) : /* @__PURE__ */ jsx7("p", { className: "text-xs text-muted-foreground", children: connectionsLoading ? "Checking connected apps\u2026" : "Saved with the generated asset." }),
|
|
391
|
-
integrationsHref && canManageIntegrations && /* @__PURE__ */ jsx7(Link, { to: integrationsHref, prefetch: "intent", className: "shrink-0", children: /* @__PURE__ */ jsx7(Button, { variant: "outline", size: "sm", children: "Connect" }) })
|
|
392
|
-
] }),
|
|
393
|
-
/* @__PURE__ */ jsx7("div", { className: "grid gap-2", children: DESTINATIONS.map((destination) => {
|
|
394
|
-
const connected = isDestinationConnected(destination, connections);
|
|
395
|
-
const active = connected && selectedDestinations.includes(destination.id);
|
|
396
|
-
return /* @__PURE__ */ jsxs6(
|
|
397
|
-
"button",
|
|
398
|
-
{
|
|
399
|
-
type: "button",
|
|
400
|
-
disabled: !connected,
|
|
401
|
-
"aria-pressed": active,
|
|
402
|
-
onClick: () => onDestinationToggle(destination.id),
|
|
403
|
-
className: `rounded-md border p-2 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60 ${active ? "border-primary bg-primary/10 ring-1 ring-primary/20" : "border-border bg-background hover:bg-accent"}`,
|
|
404
|
-
children: [
|
|
405
|
-
/* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between gap-2", children: [
|
|
406
|
-
/* @__PURE__ */ jsxs6("span", { className: "flex items-center gap-1.5 text-xs font-medium text-foreground", children: [
|
|
407
|
-
active && /* @__PURE__ */ jsx7(Check, { className: "h-3.5 w-3.5 text-primary" }),
|
|
408
|
-
/* @__PURE__ */ jsx7(ProviderIcon, { id: destination.providerIds[0] ?? destination.id, displayName: destination.label, size: 16, className: "rounded" }),
|
|
409
|
-
destination.label
|
|
410
|
-
] }),
|
|
411
|
-
/* @__PURE__ */ jsx7(Badge, { variant: active || connected ? "default" : "outline", children: active ? "Selected" : connected ? "Ready" : "Not connected" })
|
|
412
|
-
] }),
|
|
413
|
-
/* @__PURE__ */ jsx7("p", { className: "mt-1 text-xs text-muted-foreground", children: destination.fields })
|
|
414
|
-
]
|
|
415
|
-
},
|
|
416
|
-
destination.id
|
|
417
|
-
);
|
|
418
|
-
}) }),
|
|
419
|
-
/* @__PURE__ */ jsx7(Field, { label: "Caption", className: "space-y-2", children: /* @__PURE__ */ jsx7(Textarea, { value: caption, onChange: (event) => onCaptionChange(event.target.value), rows: 3, placeholder: "Write or generate the caption for selected destinations...", className: "bg-background" }) }),
|
|
420
|
-
/* @__PURE__ */ jsx7(Field, { label: "Description / CTA", className: "space-y-2", children: /* @__PURE__ */ jsx7(Textarea, { value: postDescription, onChange: (event) => onDescriptionChange(event.target.value), rows: 2, placeholder: "Release note, product context, link, or approval instruction...", className: "bg-background" }) }),
|
|
421
|
-
/* @__PURE__ */ jsxs6("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
422
|
-
/* @__PURE__ */ jsx7(Field, { label: "Mentions", className: "space-y-2", children: /* @__PURE__ */ jsx7(Input6, { value: mentions, onChange: (event) => onMentionsChange(event.target.value), placeholder: "@creator, @partner", className: "bg-background" }) }),
|
|
423
|
-
/* @__PURE__ */ jsx7(Field, { label: "Cadence", className: "space-y-2", children: /* @__PURE__ */ jsx7(NativeSelect, { value: cadence, onChange: (event) => onCadenceChange(event.target.value), children: CADENCES.map((option) => /* @__PURE__ */ jsx7("option", { value: option, children: option }, option)) }) })
|
|
424
|
-
] })
|
|
425
|
-
] });
|
|
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);
|
|
426
578
|
}
|
|
427
579
|
|
|
428
|
-
// src/studio-react/composer
|
|
429
|
-
import {
|
|
430
|
-
var
|
|
431
|
-
|
|
432
|
-
{
|
|
433
|
-
|
|
434
|
-
type: "image",
|
|
435
|
-
prompt: "A vertical storyboard frame for a product launch teaser \u2014 dark cinematic lighting, sleek studio aesthetic, 9:16 composition."
|
|
436
|
-
},
|
|
437
|
-
{
|
|
438
|
-
label: "Launch teaser clip",
|
|
439
|
-
type: "video",
|
|
440
|
-
prompt: "A 6-second vertical launch teaser: animated product reveal, dynamic camera move, hero music swell."
|
|
441
|
-
},
|
|
442
|
-
{
|
|
443
|
-
label: "Scratch voiceover",
|
|
444
|
-
type: "speech",
|
|
445
|
-
prompt: "A confident 12-second scratch voiceover for a product launch teaser: warm tone, conversational pace."
|
|
446
|
-
},
|
|
447
|
-
{
|
|
448
|
-
label: "Transcribe dailies",
|
|
449
|
-
type: "transcription",
|
|
450
|
-
prompt: "Transcribe the latest dailies recording with timestamps, speaker labels, and key story beats highlighted."
|
|
451
|
-
}
|
|
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 }
|
|
452
586
|
];
|
|
453
|
-
|
|
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({
|
|
454
624
|
workspaceId,
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
onGenerated
|
|
625
|
+
onGenerated,
|
|
626
|
+
variant = "home",
|
|
627
|
+
pickReferenceImage,
|
|
628
|
+
className
|
|
460
629
|
}) {
|
|
461
|
-
const [type, setType] =
|
|
462
|
-
const [prompt, setPrompt] =
|
|
463
|
-
const [
|
|
464
|
-
const [
|
|
465
|
-
const [
|
|
466
|
-
const [
|
|
467
|
-
const [
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const [
|
|
473
|
-
const [
|
|
474
|
-
const [
|
|
475
|
-
const [aspectRatio, setAspectRatio] = useState2("16:9");
|
|
476
|
-
const [referenceImageUrl, setReferenceImageUrl] = useState2("");
|
|
477
|
-
const [voice, setVoice] = useState2("alloy");
|
|
478
|
-
const [avatarAudioUrl, setAvatarAudioUrl] = useState2("");
|
|
479
|
-
const [avatarImageUrl, setAvatarImageUrl] = useState2("");
|
|
480
|
-
const [avatarId, setAvatarId] = useState2("");
|
|
481
|
-
const [transcriptionAudioUrl, setTranscriptionAudioUrl] = useState2("");
|
|
482
|
-
const [transcriptionLanguage, setTranscriptionLanguage] = useState2("");
|
|
483
|
-
const [transcriptionResponseFormat, setTranscriptionResponseFormat] = useState2("json");
|
|
484
|
-
const [transcriptionTemperature, setTranscriptionTemperature] = useState2("0");
|
|
485
|
-
const [mediaModels, setMediaModels] = useState2(null);
|
|
486
|
-
const [mediaModelsLoading, setMediaModelsLoading] = useState2(false);
|
|
487
|
-
const [mediaModelsError, setMediaModelsError] = useState2(null);
|
|
488
|
-
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);
|
|
489
644
|
const submitLockRef = useRef2(false);
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
const modelsForType = mediaModels?.models[type] ?? [];
|
|
493
|
-
const selectedModel = selectedModels[type] ?? preferredModelId(type, mediaModels) ?? "";
|
|
494
|
-
const selectedModelOption = modelsForType.find((model) => model.id === selectedModel);
|
|
495
|
-
const modelReady = Boolean(selectedModelOption) && selectedModelOption?.status !== "unavailable" && !mediaModelsLoading && !mediaModelsError;
|
|
496
|
-
const hasRequiredInput = type === "transcription" ? Boolean(transcriptionAudioUrl.trim()) : type === "avatar" ? Boolean(avatarAudioUrl.trim()) : Boolean(prompt.trim());
|
|
497
|
-
const canSubmit = Boolean(workspaceId) && modelReady && hasRequiredInput && !isSubmitting;
|
|
498
|
-
const integrations = useIntegrations({ apiBaseUrl: HUB_BASE_URL });
|
|
499
|
-
const selectedConnectedDestinations = useMemo2(() => selectedDestinations.filter((destinationId) => {
|
|
500
|
-
const destination = DESTINATIONS.find((item) => item.id === destinationId);
|
|
501
|
-
return Boolean(destination && isDestinationConnected(destination, integrations.connections));
|
|
502
|
-
}), [integrations.connections, selectedDestinations]);
|
|
503
|
-
useEffect2(() => {
|
|
645
|
+
const bandRef = useRef2(null);
|
|
646
|
+
useEffect3(() => {
|
|
504
647
|
if (!workspaceId) return;
|
|
505
648
|
let cancelled = false;
|
|
506
|
-
|
|
507
|
-
|
|
649
|
+
setCatalogLoading(true);
|
|
650
|
+
setCatalogError(null);
|
|
508
651
|
fetch(`/api/media-models?workspaceId=${encodeURIComponent(workspaceId)}`).then(async (res) => {
|
|
509
652
|
const data = await res.json();
|
|
510
653
|
if (!res.ok) throw new Error(data.error ?? "Could not load media models");
|
|
511
654
|
return data;
|
|
512
655
|
}).then((data) => {
|
|
513
656
|
if (cancelled) return;
|
|
514
|
-
|
|
515
|
-
setSelectedModels((current) => selectedModelsWithDefaults(current, data));
|
|
657
|
+
setCatalog(data);
|
|
516
658
|
}).catch(() => {
|
|
517
659
|
if (cancelled) return;
|
|
518
|
-
|
|
519
|
-
|
|
660
|
+
setCatalog(null);
|
|
661
|
+
setCatalogError("Could not load media models");
|
|
520
662
|
}).finally(() => {
|
|
521
|
-
if (!cancelled)
|
|
663
|
+
if (!cancelled) setCatalogLoading(false);
|
|
522
664
|
});
|
|
523
665
|
return () => {
|
|
524
666
|
cancelled = true;
|
|
525
667
|
};
|
|
526
668
|
}, [workspaceId]);
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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);
|
|
530
723
|
}
|
|
531
724
|
async function generate() {
|
|
532
|
-
if (!workspaceId || submitLockRef.current) return;
|
|
725
|
+
if (!workspaceId || submitLockRef.current || isSubmitting) return;
|
|
533
726
|
const promptText = prompt.trim();
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
setError("prompt is required");
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
if (type === "avatar" && !avatarAudioUrlText) {
|
|
541
|
-
setError("audioUrl is required");
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
if (type === "transcription" && !transcriptionAudioUrlText) {
|
|
545
|
-
setError("audioUrl is required");
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
if (!selectedModelOption || selectedModelOption.status === "unavailable") {
|
|
549
|
-
setError(`Select an available ${typeConfigFor(type).label} model`);
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
|
-
if (cadence === "Publish now" && selectedConnectedDestinations.length === 0) {
|
|
553
|
-
setError("Select a connected destination to publish now");
|
|
727
|
+
if (!promptText) return;
|
|
728
|
+
if (!modelReady) {
|
|
729
|
+
if (!catalogLoading) setError("Select an available model");
|
|
554
730
|
return;
|
|
555
731
|
}
|
|
556
732
|
submitLockRef.current = true;
|
|
557
733
|
setIsSubmitting(true);
|
|
558
734
|
setError(null);
|
|
559
735
|
const clientRequestId = crypto.randomUUID();
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
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({
|
|
563
738
|
type,
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
prompt: type === "avatar" ? "" : promptText,
|
|
567
|
-
model: selectedModel,
|
|
739
|
+
prompt: promptText,
|
|
740
|
+
model: modelId,
|
|
568
741
|
clientRequestId,
|
|
569
742
|
outputIndex: type === "image" ? outputIndex : void 0,
|
|
570
|
-
outputCount: type === "image" ?
|
|
743
|
+
outputCount: type === "image" ? imageCount : void 0
|
|
571
744
|
}));
|
|
572
745
|
localGenerations.slice().reverse().forEach(onGenerated);
|
|
573
746
|
setPrompt("");
|
|
@@ -577,26 +750,28 @@ function ComposerHero({
|
|
|
577
750
|
workspaceId,
|
|
578
751
|
clientRequestId,
|
|
579
752
|
type,
|
|
580
|
-
model:
|
|
581
|
-
prompt,
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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
|
|
600
775
|
}
|
|
601
776
|
});
|
|
602
777
|
const res = await fetch("/api/generate", {
|
|
@@ -619,257 +794,164 @@ function ComposerHero({
|
|
|
619
794
|
setIsSubmitting(false);
|
|
620
795
|
}
|
|
621
796
|
}
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
const Icon = cfg.icon;
|
|
633
|
-
const active = type === key;
|
|
634
|
-
return /* @__PURE__ */ jsxs7(
|
|
635
|
-
"button",
|
|
636
|
-
{
|
|
637
|
-
type: "button",
|
|
638
|
-
role: "tab",
|
|
639
|
-
"aria-selected": active,
|
|
640
|
-
onClick: () => changeType(key),
|
|
641
|
-
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"}`,
|
|
642
|
-
children: [
|
|
643
|
-
/* @__PURE__ */ jsx8(Icon, { className: "h-[17px] w-[17px] shrink-0" }),
|
|
644
|
-
/* @__PURE__ */ jsx8("span", { className: "truncate", children: cfg.label })
|
|
645
|
-
]
|
|
646
|
-
},
|
|
647
|
-
key
|
|
648
|
-
);
|
|
649
|
-
}) }),
|
|
650
|
-
type !== "avatar" && /* @__PURE__ */ jsxs7("div", { className: "mt-5", children: [
|
|
651
|
-
/* @__PURE__ */ jsx8(
|
|
652
|
-
Label2,
|
|
653
|
-
{
|
|
654
|
-
htmlFor: "studio-prompt",
|
|
655
|
-
className: "mb-1.5 block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground",
|
|
656
|
-
children: "Prompt"
|
|
657
|
-
}
|
|
658
|
-
),
|
|
659
|
-
/* @__PURE__ */ jsxs7("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: [
|
|
660
|
-
/* @__PURE__ */ jsx8(
|
|
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(
|
|
661
807
|
"textarea",
|
|
662
808
|
{
|
|
663
|
-
id: "studio-prompt",
|
|
664
809
|
value: prompt,
|
|
665
810
|
onChange: (event) => setPrompt(event.target.value),
|
|
666
811
|
onKeyDown: (event) => {
|
|
667
|
-
if (event.key
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
}
|
|
812
|
+
if (event.key !== "Enter" || event.shiftKey) return;
|
|
813
|
+
event.preventDefault();
|
|
814
|
+
void generate();
|
|
671
815
|
},
|
|
672
|
-
rows:
|
|
673
|
-
|
|
674
|
-
|
|
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"
|
|
675
820
|
}
|
|
676
821
|
),
|
|
677
|
-
/* @__PURE__ */
|
|
678
|
-
|
|
679
|
-
{
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
/* @__PURE__ */ jsxs7("span", { className: "flex min-w-0 items-center gap-2", children: [
|
|
733
|
-
model.provider && /* @__PURE__ */ jsx8(ProviderLogo, { provider: model.provider, size: 14 }),
|
|
734
|
-
/* @__PURE__ */ jsxs7("span", { className: "truncate", children: [
|
|
735
|
-
model.name || model.id,
|
|
736
|
-
model.provider ? ` \xB7 ${model.provider}` : ""
|
|
737
|
-
] })
|
|
738
|
-
] }),
|
|
739
|
-
model.status !== "available" && /* @__PURE__ */ jsx8("span", { className: "shrink-0 text-xs capitalize text-muted-foreground", children: model.status })
|
|
740
|
-
] }) }, model.id)) })
|
|
741
|
-
]
|
|
742
|
-
}
|
|
743
|
-
) }),
|
|
744
|
-
(mediaModelsError || modelMessage(selectedModelOption, mediaModelsLoading, modelsForType.length)) && /* @__PURE__ */ jsx8("p", { className: `text-xs ${mediaModelsError || selectedModelOption?.status === "unavailable" ? "text-destructive" : "text-muted-foreground"}`, children: mediaModelsError ?? modelMessage(selectedModelOption, mediaModelsLoading, modelsForType.length) })
|
|
745
|
-
] }),
|
|
746
|
-
type === "image" && /* @__PURE__ */ jsx8(
|
|
747
|
-
ImageComposer,
|
|
748
|
-
{
|
|
749
|
-
size,
|
|
750
|
-
quality,
|
|
751
|
-
imageCount,
|
|
752
|
-
onSizeChange: setSize,
|
|
753
|
-
onQualityChange: setQuality,
|
|
754
|
-
onImageCountChange: setImageCount
|
|
755
|
-
}
|
|
756
|
-
),
|
|
757
|
-
type === "video" && /* @__PURE__ */ jsx8(
|
|
758
|
-
VideoComposer,
|
|
759
|
-
{
|
|
760
|
-
duration,
|
|
761
|
-
resolution,
|
|
762
|
-
aspectRatio,
|
|
763
|
-
referenceImageUrl,
|
|
764
|
-
onDurationChange: setDuration,
|
|
765
|
-
onResolutionChange: setResolution,
|
|
766
|
-
onAspectRatioChange: setAspectRatio,
|
|
767
|
-
onReferenceImageUrlChange: setReferenceImageUrl
|
|
768
|
-
}
|
|
769
|
-
),
|
|
770
|
-
type === "speech" && /* @__PURE__ */ jsx8(SpeechComposer, { voice, onVoiceChange: setVoice })
|
|
771
|
-
] }),
|
|
772
|
-
/* @__PURE__ */ jsxs7("div", { className: "mt-4 space-y-2", children: [
|
|
773
|
-
/* @__PURE__ */ jsx8(ComposerDisclosure, { summary: "Advanced options", children: /* @__PURE__ */ jsxs7("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
774
|
-
/* @__PURE__ */ jsx8(Field, { label: "Negative prompt", children: /* @__PURE__ */ jsx8(
|
|
775
|
-
Textarea2,
|
|
776
|
-
{
|
|
777
|
-
value: negativePrompt,
|
|
778
|
-
onChange: (event) => setNegativePrompt(event.target.value),
|
|
779
|
-
rows: 2,
|
|
780
|
-
placeholder: "Avoid artifacts, off-style composition...",
|
|
781
|
-
className: "bg-background"
|
|
782
|
-
}
|
|
783
|
-
) }),
|
|
784
|
-
/* @__PURE__ */ jsx8(Field, { label: "Save to", children: /* @__PURE__ */ jsx8(Input7, { value: outputPath, onChange: (event) => setOutputPath(event.target.value), className: "bg-background" }) }),
|
|
785
|
-
type === "transcription" && /* @__PURE__ */ jsx8(
|
|
786
|
-
TranscriptionOptions,
|
|
787
|
-
{
|
|
788
|
-
responseFormat: transcriptionResponseFormat,
|
|
789
|
-
temperature: transcriptionTemperature,
|
|
790
|
-
onResponseFormatChange: setTranscriptionResponseFormat,
|
|
791
|
-
onTemperatureChange: setTranscriptionTemperature
|
|
792
|
-
}
|
|
793
|
-
)
|
|
794
|
-
] }) }),
|
|
795
|
-
/* @__PURE__ */ jsx8(
|
|
796
|
-
ComposerDisclosure,
|
|
797
|
-
{
|
|
798
|
-
summary: /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
799
|
-
"Schedule a post",
|
|
800
|
-
selectedConnectedDestinations.length > 0 && /* @__PURE__ */ jsx8(Badge2, { variant: "outline", className: "ml-1 text-xs", children: selectedConnectedDestinations.length })
|
|
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`)
|
|
801
877
|
] }),
|
|
802
|
-
|
|
803
|
-
|
|
878
|
+
/* @__PURE__ */ jsx2(
|
|
879
|
+
"button",
|
|
804
880
|
{
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
connectionsLoading: integrations.isLoading,
|
|
813
|
-
integrationsHref,
|
|
814
|
-
canManageIntegrations,
|
|
815
|
-
onCaptionChange: setCaption,
|
|
816
|
-
onDescriptionChange: setPostDescription,
|
|
817
|
-
onMentionsChange: setMentions,
|
|
818
|
-
onCadenceChange: setCadence,
|
|
819
|
-
onDestinationToggle: (destination) => {
|
|
820
|
-
const destinationConfig = DESTINATIONS.find((item) => item.id === destination);
|
|
821
|
-
if (!destinationConfig || !isDestinationConnected(destinationConfig, integrations.connections)) return;
|
|
822
|
-
setSelectedDestinations((current) => current.includes(destination) ? current.filter((item) => item !== destination) : [...current, destination]);
|
|
823
|
-
}
|
|
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 })
|
|
824
888
|
}
|
|
825
889
|
)
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
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` }];
|
|
845
910
|
}
|
|
846
911
|
|
|
847
912
|
// src/studio-react/studio-header.tsx
|
|
848
|
-
import { Button
|
|
913
|
+
import { Button } from "@tangle-network/sandbox-ui/primitives";
|
|
849
914
|
import { Images } from "lucide-react";
|
|
850
|
-
import { jsx as
|
|
915
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
851
916
|
function StudioHeader({
|
|
852
917
|
count,
|
|
853
918
|
onOpenLibrary,
|
|
854
919
|
canGenerate
|
|
855
920
|
}) {
|
|
856
|
-
return /* @__PURE__ */
|
|
857
|
-
/* @__PURE__ */
|
|
858
|
-
/* @__PURE__ */
|
|
859
|
-
/* @__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." })
|
|
860
925
|
] }),
|
|
861
|
-
/* @__PURE__ */
|
|
862
|
-
/* @__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" }),
|
|
863
928
|
"Library",
|
|
864
|
-
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 })
|
|
865
930
|
] })
|
|
866
931
|
] }) });
|
|
867
932
|
}
|
|
868
933
|
|
|
869
934
|
// src/studio-react/result-canvas.tsx
|
|
870
|
-
import { Badge
|
|
871
|
-
import { ArrowRight, Sparkles
|
|
872
|
-
|
|
935
|
+
import { Badge } from "@tangle-network/sandbox-ui/primitives";
|
|
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";
|
|
873
955
|
function ResultCanvas({
|
|
874
956
|
batch,
|
|
875
957
|
onOpenLibrary,
|
|
@@ -877,10 +959,10 @@ function ResultCanvas({
|
|
|
877
959
|
}) {
|
|
878
960
|
const cardClass = "rounded-xl border border-border bg-card shadow-sm";
|
|
879
961
|
if (batch.length === 0) {
|
|
880
|
-
return /* @__PURE__ */
|
|
881
|
-
/* @__PURE__ */
|
|
882
|
-
/* @__PURE__ */
|
|
883
|
-
/* @__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." })
|
|
884
966
|
] });
|
|
885
967
|
}
|
|
886
968
|
const cfg = typeConfigFor(batch[0]?.type ?? "image");
|
|
@@ -888,17 +970,17 @@ function ResultCanvas({
|
|
|
888
970
|
const statuses = batch.map(generationStatus);
|
|
889
971
|
const runLabel = statuses.some((s) => s === "pending" || s === "running") ? "Generating\u2026" : statuses.every((s) => s === "failed") ? "Last run failed" : "Latest creation";
|
|
890
972
|
const isWorking = statuses.some((s) => s === "pending" || s === "running");
|
|
891
|
-
return /* @__PURE__ */
|
|
892
|
-
/* @__PURE__ */
|
|
893
|
-
/* @__PURE__ */
|
|
894
|
-
/* @__PURE__ */
|
|
895
|
-
/* @__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" }),
|
|
896
978
|
cfg.label
|
|
897
979
|
] }),
|
|
898
|
-
/* @__PURE__ */
|
|
899
|
-
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" })
|
|
900
982
|
] }),
|
|
901
|
-
/* @__PURE__ */
|
|
983
|
+
/* @__PURE__ */ jsxs4(
|
|
902
984
|
"button",
|
|
903
985
|
{
|
|
904
986
|
type: "button",
|
|
@@ -906,22 +988,22 @@ function ResultCanvas({
|
|
|
906
988
|
className: "flex items-center gap-1 text-xs font-medium text-primary transition-colors hover:text-primary/80",
|
|
907
989
|
children: [
|
|
908
990
|
"View in Library",
|
|
909
|
-
/* @__PURE__ */
|
|
991
|
+
/* @__PURE__ */ jsx4(ArrowRight, { className: "h-3.5 w-3.5" })
|
|
910
992
|
]
|
|
911
993
|
}
|
|
912
994
|
)
|
|
913
995
|
] }),
|
|
914
|
-
/* @__PURE__ */
|
|
996
|
+
/* @__PURE__ */ jsx4("div", { className: `grid gap-3 ${batch.length === 1 ? "grid-cols-1" : "grid-cols-2"}`, children: batch.map((generation) => /* @__PURE__ */ jsx4(
|
|
915
997
|
"button",
|
|
916
998
|
{
|
|
917
999
|
type: "button",
|
|
918
1000
|
onClick: () => onSelect(generation),
|
|
919
1001
|
className: "studio-bloom group relative aspect-video overflow-hidden rounded-lg border border-border bg-secondary transition-colors hover:border-primary/40",
|
|
920
|
-
children: /* @__PURE__ */
|
|
1002
|
+
children: /* @__PURE__ */ jsx4(CanvasTile, { generation })
|
|
921
1003
|
},
|
|
922
1004
|
generation.id
|
|
923
1005
|
)) }),
|
|
924
|
-
batch.length > 1 && !isWorking && /* @__PURE__ */
|
|
1006
|
+
batch.length > 1 && !isWorking && /* @__PURE__ */ jsxs4("p", { className: "mt-2 text-xs text-muted-foreground", children: [
|
|
925
1007
|
batch.length,
|
|
926
1008
|
" results from this run"
|
|
927
1009
|
] })
|
|
@@ -932,58 +1014,58 @@ function CanvasTile({ generation }) {
|
|
|
932
1014
|
const Icon = cfg.icon;
|
|
933
1015
|
const status = generationStatus(generation);
|
|
934
1016
|
if (status === "pending" || status === "running") {
|
|
935
|
-
return /* @__PURE__ */
|
|
1017
|
+
return /* @__PURE__ */ jsx4("div", { className: "studio-shimmer absolute inset-0 h-full w-full" });
|
|
936
1018
|
}
|
|
937
1019
|
if (status === "failed") {
|
|
938
|
-
return /* @__PURE__ */
|
|
939
|
-
/* @__PURE__ */
|
|
940
|
-
/* @__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" })
|
|
941
1023
|
] });
|
|
942
1024
|
}
|
|
943
1025
|
if (generation.type === "image" && generation.result) {
|
|
944
|
-
return /* @__PURE__ */
|
|
1026
|
+
return /* @__PURE__ */ jsx4("img", { src: generation.result, alt: generation.prompt, className: "absolute inset-0 h-full w-full object-contain" });
|
|
945
1027
|
}
|
|
946
1028
|
if ((generation.type === "video" || generation.type === "avatar") && generation.result) {
|
|
947
|
-
return /* @__PURE__ */
|
|
1029
|
+
return /* @__PURE__ */ jsx4("video", { src: generation.result, controls: true, className: "absolute inset-0 h-full w-full object-contain" });
|
|
948
1030
|
}
|
|
949
1031
|
if (generation.type === "speech" && generation.result) {
|
|
950
|
-
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" }) });
|
|
951
1033
|
}
|
|
952
1034
|
if (generation.type === "transcription" && generation.result) {
|
|
953
|
-
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 }) });
|
|
954
1036
|
}
|
|
955
|
-
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" }) });
|
|
956
1038
|
}
|
|
957
1039
|
|
|
958
1040
|
// src/studio-react/library-drawer.tsx
|
|
959
1041
|
import { useMemo as useMemo3 } from "react";
|
|
960
|
-
import { Link as
|
|
1042
|
+
import { Link as Link2 } from "react-router";
|
|
961
1043
|
import {
|
|
962
|
-
Button as
|
|
1044
|
+
Button as Button3,
|
|
963
1045
|
Tabs,
|
|
964
1046
|
TabsList,
|
|
965
1047
|
TabsTrigger
|
|
966
1048
|
} from "@tangle-network/sandbox-ui/primitives";
|
|
967
|
-
import { ArrowLeft, DollarSign, FolderOpen as FolderOpen2, X } from "lucide-react";
|
|
1049
|
+
import { ArrowLeft, DollarSign, FolderOpen as FolderOpen2, X as X2 } from "lucide-react";
|
|
968
1050
|
|
|
969
1051
|
// src/studio-react/studio-sheet.tsx
|
|
970
1052
|
import * as Dialog from "@radix-ui/react-dialog";
|
|
971
|
-
import { jsx as
|
|
1053
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
972
1054
|
function StudioSheet({
|
|
973
1055
|
open,
|
|
974
1056
|
onOpenChange,
|
|
975
1057
|
title,
|
|
976
1058
|
children
|
|
977
1059
|
}) {
|
|
978
|
-
return /* @__PURE__ */
|
|
979
|
-
/* @__PURE__ */
|
|
980
|
-
/* @__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(
|
|
981
1063
|
Dialog.Content,
|
|
982
1064
|
{
|
|
983
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",
|
|
984
1066
|
"aria-describedby": void 0,
|
|
985
1067
|
children: [
|
|
986
|
-
/* @__PURE__ */
|
|
1068
|
+
/* @__PURE__ */ jsx5(Dialog.Title, { className: "sr-only", children: title }),
|
|
987
1069
|
children
|
|
988
1070
|
]
|
|
989
1071
|
}
|
|
@@ -993,12 +1075,11 @@ function StudioSheet({
|
|
|
993
1075
|
|
|
994
1076
|
// src/studio-react/generation-grid.tsx
|
|
995
1077
|
import { EmptyState } from "@tangle-network/sandbox-ui/primitives";
|
|
996
|
-
import { Film, Sparkles as
|
|
1078
|
+
import { Film, Sparkles as Sparkles2 } from "lucide-react";
|
|
997
1079
|
|
|
998
1080
|
// src/studio-react/generation-card.tsx
|
|
999
|
-
import { Card, CardContent, Badge as
|
|
1000
|
-
import {
|
|
1001
|
-
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1081
|
+
import { Card, CardContent, Badge as Badge2 } from "@tangle-network/sandbox-ui/primitives";
|
|
1082
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1002
1083
|
function GenerationCard({
|
|
1003
1084
|
generation,
|
|
1004
1085
|
onSelect
|
|
@@ -1006,35 +1087,27 @@ function GenerationCard({
|
|
|
1006
1087
|
const cfg = typeConfigFor(generation.type);
|
|
1007
1088
|
const Icon = cfg.icon;
|
|
1008
1089
|
const status = generationStatus(generation);
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
/* @__PURE__ */ jsx12(Icon, { className: "h-3 w-3" }),
|
|
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" }),
|
|
1015
1095
|
cfg.label
|
|
1016
1096
|
] }) }),
|
|
1017
|
-
/* @__PURE__ */
|
|
1097
|
+
/* @__PURE__ */ jsx6(GenerationStatusBadge, { generation })
|
|
1018
1098
|
] }),
|
|
1019
|
-
/* @__PURE__ */
|
|
1020
|
-
/* @__PURE__ */
|
|
1021
|
-
status === "failed" && /* @__PURE__ */
|
|
1022
|
-
/* @__PURE__ */
|
|
1023
|
-
generation.model && /* @__PURE__ */
|
|
1024
|
-
/* @__PURE__ */
|
|
1025
|
-
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: [
|
|
1026
1106
|
"$",
|
|
1027
1107
|
generation.cost.toFixed(3)
|
|
1028
1108
|
] }),
|
|
1029
|
-
/* @__PURE__ */
|
|
1109
|
+
/* @__PURE__ */ jsx6("span", { className: "text-xs text-muted-foreground", children: relativeTime(generation.createdAt) })
|
|
1030
1110
|
] })
|
|
1031
|
-
] }),
|
|
1032
|
-
isPublishPackage(publishPackage) && /* @__PURE__ */ jsxs11("div", { className: "mt-3 rounded-md border border-border bg-secondary p-2", children: [
|
|
1033
|
-
/* @__PURE__ */ jsxs11("div", { className: "mb-1 flex items-center gap-1.5 text-xs font-medium text-foreground", children: [
|
|
1034
|
-
/* @__PURE__ */ jsx12(Send, { className: "h-3 w-3" }),
|
|
1035
|
-
(publishPackage.destinations ?? []).map((id) => DESTINATIONS.find((destination) => destination.id === id)?.label ?? id).join(", ") || "Publish package"
|
|
1036
|
-
] }),
|
|
1037
|
-
/* @__PURE__ */ jsx12("p", { className: "line-clamp-2 text-xs text-muted-foreground", children: publishPackage.caption || "Caption pending" })
|
|
1038
1111
|
] })
|
|
1039
1112
|
] })
|
|
1040
1113
|
] }) });
|
|
@@ -1047,11 +1120,11 @@ function GenerationStatusBadge({
|
|
|
1047
1120
|
if (status === "succeeded") return null;
|
|
1048
1121
|
const label = status === "failed" ? "Failed" : status === "running" ? "Running" : "Pending";
|
|
1049
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";
|
|
1050
|
-
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 }) });
|
|
1051
1124
|
}
|
|
1052
1125
|
|
|
1053
1126
|
// src/studio-react/generation-grid.tsx
|
|
1054
|
-
import { jsx as
|
|
1127
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1055
1128
|
function filterGenerations(generations, typeFilter) {
|
|
1056
1129
|
if (!typeFilter || !isGenerationType(typeFilter)) return generations;
|
|
1057
1130
|
return generations.filter((generation) => generation.type === typeFilter);
|
|
@@ -1062,24 +1135,24 @@ function GenerationGrid({
|
|
|
1062
1135
|
onSelect
|
|
1063
1136
|
}) {
|
|
1064
1137
|
if (generations.length === 0) {
|
|
1065
|
-
return /* @__PURE__ */
|
|
1138
|
+
return /* @__PURE__ */ jsx7(
|
|
1066
1139
|
EmptyState,
|
|
1067
1140
|
{
|
|
1068
|
-
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" }),
|
|
1069
1142
|
title: typeFilter ? `No ${TYPE_CONFIG[typeFilter]?.label ?? typeFilter} generations` : "No generations yet",
|
|
1070
1143
|
description: typeFilter ? "Try a different filter or change the type in the composer." : "Everything you and the agent create lives here.",
|
|
1071
1144
|
className: "py-16"
|
|
1072
1145
|
}
|
|
1073
1146
|
);
|
|
1074
1147
|
}
|
|
1075
|
-
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)) });
|
|
1076
1149
|
}
|
|
1077
1150
|
|
|
1078
1151
|
// src/studio-react/generation-detail.tsx
|
|
1079
|
-
import { Link
|
|
1080
|
-
import { Badge as
|
|
1152
|
+
import { Link } from "react-router";
|
|
1153
|
+
import { Badge as Badge3, Button as Button2 } from "@tangle-network/sandbox-ui/primitives";
|
|
1081
1154
|
import { FolderOpen } from "lucide-react";
|
|
1082
|
-
import { jsx as
|
|
1155
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1083
1156
|
function GenerationDetail({
|
|
1084
1157
|
generation,
|
|
1085
1158
|
vaultHref,
|
|
@@ -1089,50 +1162,50 @@ function GenerationDetail({
|
|
|
1089
1162
|
const Icon = cfg.icon;
|
|
1090
1163
|
const vaultPath = generationVaultPath(generation);
|
|
1091
1164
|
const href = vaultHref && vaultPath ? vaultHref(vaultPath) : null;
|
|
1092
|
-
return /* @__PURE__ */
|
|
1093
|
-
/* @__PURE__ */
|
|
1094
|
-
/* @__PURE__ */
|
|
1095
|
-
/* @__PURE__ */
|
|
1096
|
-
/* @__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" }),
|
|
1097
1170
|
cfg.label
|
|
1098
1171
|
] }),
|
|
1099
|
-
/* @__PURE__ */
|
|
1100
|
-
generationError(generation) && /* @__PURE__ */
|
|
1172
|
+
/* @__PURE__ */ jsx8(GenerationStatusBadge, { generation, inline: true }),
|
|
1173
|
+
generationError(generation) && /* @__PURE__ */ jsx8("span", { className: "text-xs text-destructive", children: generationError(generation) })
|
|
1101
1174
|
] }),
|
|
1102
|
-
/* @__PURE__ */
|
|
1103
|
-
/* @__PURE__ */
|
|
1104
|
-
/* @__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 })
|
|
1105
1178
|
] }),
|
|
1106
|
-
/* @__PURE__ */
|
|
1107
|
-
generation.model && /* @__PURE__ */
|
|
1108
|
-
/* @__PURE__ */
|
|
1109
|
-
/* @__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 })
|
|
1110
1183
|
] }),
|
|
1111
|
-
generation.cost != null && /* @__PURE__ */
|
|
1112
|
-
/* @__PURE__ */
|
|
1113
|
-
/* @__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: [
|
|
1114
1187
|
"$",
|
|
1115
1188
|
generation.cost.toFixed(3)
|
|
1116
1189
|
] })
|
|
1117
1190
|
] }),
|
|
1118
|
-
generation.createdAt && /* @__PURE__ */
|
|
1119
|
-
/* @__PURE__ */
|
|
1120
|
-
/* @__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" }) })
|
|
1121
1194
|
] })
|
|
1122
1195
|
] }),
|
|
1123
|
-
generation.type === "transcription" && generation.result && /* @__PURE__ */
|
|
1124
|
-
/* @__PURE__ */
|
|
1125
|
-
/* @__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 })
|
|
1126
1199
|
] }),
|
|
1127
|
-
href && /* @__PURE__ */
|
|
1128
|
-
/* @__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" }),
|
|
1129
1202
|
"Open in Vault"
|
|
1130
1203
|
] }) }) })
|
|
1131
1204
|
] });
|
|
1132
1205
|
}
|
|
1133
1206
|
|
|
1134
1207
|
// src/studio-react/library-drawer.tsx
|
|
1135
|
-
import { Fragment as
|
|
1208
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1136
1209
|
function LibraryDrawer({
|
|
1137
1210
|
open,
|
|
1138
1211
|
onOpenChange,
|
|
@@ -1145,88 +1218,85 @@ function LibraryDrawer({
|
|
|
1145
1218
|
onSelect
|
|
1146
1219
|
}) {
|
|
1147
1220
|
const visible = useMemo3(() => filterGenerations(generations, typeFilter), [generations, typeFilter]);
|
|
1148
|
-
return /* @__PURE__ */
|
|
1149
|
-
/* @__PURE__ */
|
|
1150
|
-
selected ? /* @__PURE__ */
|
|
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(
|
|
1151
1224
|
"button",
|
|
1152
1225
|
{
|
|
1153
1226
|
type: "button",
|
|
1154
1227
|
onClick: () => onSelect(null),
|
|
1155
1228
|
className: "flex items-center gap-1.5 text-sm font-medium text-foreground transition-colors hover:text-primary",
|
|
1156
1229
|
children: [
|
|
1157
|
-
/* @__PURE__ */
|
|
1230
|
+
/* @__PURE__ */ jsx9(ArrowLeft, { className: "h-4 w-4" }),
|
|
1158
1231
|
"Back to library"
|
|
1159
1232
|
]
|
|
1160
1233
|
}
|
|
1161
|
-
) : /* @__PURE__ */
|
|
1162
|
-
/* @__PURE__ */
|
|
1234
|
+
) : /* @__PURE__ */ jsx9("h2", { className: "text-sm font-semibold text-foreground", children: "Asset library" }),
|
|
1235
|
+
/* @__PURE__ */ jsx9(
|
|
1163
1236
|
"button",
|
|
1164
1237
|
{
|
|
1165
1238
|
type: "button",
|
|
1166
1239
|
"aria-label": "Close library",
|
|
1167
1240
|
onClick: () => onOpenChange(false),
|
|
1168
1241
|
className: "rounded-md p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
1169
|
-
children: /* @__PURE__ */
|
|
1242
|
+
children: /* @__PURE__ */ jsx9(X2, { className: "h-4 w-4" })
|
|
1170
1243
|
}
|
|
1171
1244
|
)
|
|
1172
1245
|
] }),
|
|
1173
|
-
selected ? /* @__PURE__ */
|
|
1246
|
+
selected ? /* @__PURE__ */ jsx9("div", { className: "flex-1 overflow-y-auto p-4", children: /* @__PURE__ */ jsx9(
|
|
1174
1247
|
GenerationDetail,
|
|
1175
1248
|
{
|
|
1176
1249
|
generation: selected,
|
|
1177
1250
|
vaultHref,
|
|
1178
1251
|
onNavigate: () => onOpenChange(false)
|
|
1179
1252
|
}
|
|
1180
|
-
) }) : /* @__PURE__ */
|
|
1181
|
-
/* @__PURE__ */
|
|
1182
|
-
/* @__PURE__ */
|
|
1183
|
-
/* @__PURE__ */
|
|
1184
|
-
/* @__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: [
|
|
1185
1258
|
visible.length,
|
|
1186
1259
|
" generation",
|
|
1187
1260
|
visible.length !== 1 ? "s" : ""
|
|
1188
1261
|
] }),
|
|
1189
|
-
/* @__PURE__ */
|
|
1190
|
-
/* @__PURE__ */
|
|
1262
|
+
/* @__PURE__ */ jsxs8("span", { className: "flex items-center gap-0.5", children: [
|
|
1263
|
+
/* @__PURE__ */ jsx9(DollarSign, { className: "h-3 w-3" }),
|
|
1191
1264
|
totalCost.toFixed(2),
|
|
1192
1265
|
" spent"
|
|
1193
1266
|
] })
|
|
1194
1267
|
] }),
|
|
1195
|
-
vaultHref && /* @__PURE__ */
|
|
1196
|
-
/* @__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" }),
|
|
1197
1270
|
"Vault"
|
|
1198
1271
|
] }) })
|
|
1199
1272
|
] }),
|
|
1200
|
-
/* @__PURE__ */
|
|
1201
|
-
/* @__PURE__ */
|
|
1202
|
-
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))
|
|
1203
1276
|
] }) })
|
|
1204
1277
|
] }),
|
|
1205
|
-
/* @__PURE__ */
|
|
1278
|
+
/* @__PURE__ */ jsx9("div", { className: "flex-1 overflow-y-auto p-4", children: /* @__PURE__ */ jsx9(GenerationGrid, { generations: visible, typeFilter, onSelect }) })
|
|
1206
1279
|
] })
|
|
1207
1280
|
] });
|
|
1208
1281
|
}
|
|
1209
1282
|
|
|
1210
1283
|
// src/studio-react/studio-workspace.tsx
|
|
1211
|
-
import { Fragment as
|
|
1284
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1212
1285
|
function StudioWorkspace({
|
|
1213
1286
|
generations,
|
|
1214
1287
|
totalCost,
|
|
1215
1288
|
workspaceId,
|
|
1216
1289
|
role,
|
|
1217
1290
|
generationsEndpoint,
|
|
1218
|
-
vaultHref
|
|
1219
|
-
integrationsHref
|
|
1291
|
+
vaultHref
|
|
1220
1292
|
}) {
|
|
1221
1293
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
1222
1294
|
const typeFilter = searchParams.get("type");
|
|
1223
|
-
const [drawerOpen, setDrawerOpen] =
|
|
1224
|
-
const [selected, setSelected] =
|
|
1295
|
+
const [drawerOpen, setDrawerOpen] = useState4(false);
|
|
1296
|
+
const [selected, setSelected] = useState4(null);
|
|
1225
1297
|
const canvasRef = useRef3(null);
|
|
1226
1298
|
const canGenerate = role !== "viewer";
|
|
1227
|
-
const canManageIntegrations = role === "owner" || role === "admin";
|
|
1228
1299
|
const resolvedVaultHref = vaultHref ?? (workspaceId ? (filePath) => filePath ? `/app/${workspaceId}/vault?file=${encodeURIComponent(filePath)}` : `/app/${workspaceId}/vault` : void 0);
|
|
1229
|
-
const resolvedIntegrationsHref = integrationsHref ?? (workspaceId ? `/app/${workspaceId}/integrations` : void 0);
|
|
1230
1300
|
const { mergedGenerations, latestBatch, onGenerated } = useStudioGenerations(generations, {
|
|
1231
1301
|
workspaceId,
|
|
1232
1302
|
generationsEndpoint
|
|
@@ -1243,8 +1313,8 @@ function StudioWorkspace({
|
|
|
1243
1313
|
setSelected(generation);
|
|
1244
1314
|
setDrawerOpen(true);
|
|
1245
1315
|
}
|
|
1246
|
-
return /* @__PURE__ */
|
|
1247
|
-
/* @__PURE__ */
|
|
1316
|
+
return /* @__PURE__ */ jsxs9("div", { className: "min-h-0 flex-1 overflow-y-auto bg-background", children: [
|
|
1317
|
+
/* @__PURE__ */ jsx10(
|
|
1248
1318
|
StudioHeader,
|
|
1249
1319
|
{
|
|
1250
1320
|
count: mergedGenerations.length,
|
|
@@ -1252,13 +1322,11 @@ function StudioWorkspace({
|
|
|
1252
1322
|
onOpenLibrary: openLibrary
|
|
1253
1323
|
}
|
|
1254
1324
|
),
|
|
1255
|
-
/* @__PURE__ */
|
|
1256
|
-
/* @__PURE__ */
|
|
1257
|
-
|
|
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,
|
|
1258
1328
|
{
|
|
1259
1329
|
workspaceId,
|
|
1260
|
-
integrationsHref: resolvedIntegrationsHref,
|
|
1261
|
-
canManageIntegrations,
|
|
1262
1330
|
onGenerated: (generation) => {
|
|
1263
1331
|
onGenerated(generation);
|
|
1264
1332
|
requestAnimationFrame(() => {
|
|
@@ -1267,7 +1335,7 @@ function StudioWorkspace({
|
|
|
1267
1335
|
}
|
|
1268
1336
|
}
|
|
1269
1337
|
),
|
|
1270
|
-
/* @__PURE__ */
|
|
1338
|
+
/* @__PURE__ */ jsx10("div", { ref: canvasRef, children: /* @__PURE__ */ jsx10(
|
|
1271
1339
|
ResultCanvas,
|
|
1272
1340
|
{
|
|
1273
1341
|
batch: latestBatch,
|
|
@@ -1275,16 +1343,16 @@ function StudioWorkspace({
|
|
|
1275
1343
|
onSelect: openDetail
|
|
1276
1344
|
}
|
|
1277
1345
|
) })
|
|
1278
|
-
] }) : /* @__PURE__ */
|
|
1279
|
-
/* @__PURE__ */
|
|
1280
|
-
/* @__PURE__ */
|
|
1281
|
-
/* @__PURE__ */
|
|
1282
|
-
/* @__PURE__ */
|
|
1283
|
-
/* @__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" }),
|
|
1284
1352
|
"Open library"
|
|
1285
1353
|
] })
|
|
1286
1354
|
] }) }),
|
|
1287
|
-
/* @__PURE__ */
|
|
1355
|
+
/* @__PURE__ */ jsx10(
|
|
1288
1356
|
LibraryDrawer,
|
|
1289
1357
|
{
|
|
1290
1358
|
open: drawerOpen,
|
|
@@ -1307,6 +1375,23 @@ function StudioWorkspace({
|
|
|
1307
1375
|
] });
|
|
1308
1376
|
}
|
|
1309
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
|
+
|
|
1310
1395
|
// src/studio-react/library-panel.tsx
|
|
1311
1396
|
import { useMemo as useMemo4 } from "react";
|
|
1312
1397
|
import {
|
|
@@ -1315,7 +1400,7 @@ import {
|
|
|
1315
1400
|
TabsTrigger as TabsTrigger2
|
|
1316
1401
|
} from "@tangle-network/sandbox-ui/primitives";
|
|
1317
1402
|
import { DollarSign as DollarSign2 } from "lucide-react";
|
|
1318
|
-
import { jsx as
|
|
1403
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1319
1404
|
function LibraryPanel({
|
|
1320
1405
|
generations,
|
|
1321
1406
|
totalCost,
|
|
@@ -1324,10 +1409,10 @@ function LibraryPanel({
|
|
|
1324
1409
|
onSelect
|
|
1325
1410
|
}) {
|
|
1326
1411
|
const visible = useMemo4(() => filterGenerations(generations, typeFilter), [generations, typeFilter]);
|
|
1327
|
-
return /* @__PURE__ */
|
|
1328
|
-
/* @__PURE__ */
|
|
1329
|
-
/* @__PURE__ */
|
|
1330
|
-
/* @__PURE__ */
|
|
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(
|
|
1331
1416
|
TabsTrigger2,
|
|
1332
1417
|
{
|
|
1333
1418
|
value: "all",
|
|
@@ -1335,7 +1420,7 @@ function LibraryPanel({
|
|
|
1335
1420
|
children: "All"
|
|
1336
1421
|
}
|
|
1337
1422
|
),
|
|
1338
|
-
Object.entries(TYPE_CONFIG).map(([key, cfg]) => /* @__PURE__ */
|
|
1423
|
+
Object.entries(TYPE_CONFIG).map(([key, cfg]) => /* @__PURE__ */ jsx12(
|
|
1339
1424
|
TabsTrigger2,
|
|
1340
1425
|
{
|
|
1341
1426
|
value: key,
|
|
@@ -1345,64 +1430,54 @@ function LibraryPanel({
|
|
|
1345
1430
|
key
|
|
1346
1431
|
))
|
|
1347
1432
|
] }) }),
|
|
1348
|
-
/* @__PURE__ */
|
|
1349
|
-
/* @__PURE__ */
|
|
1433
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
1434
|
+
/* @__PURE__ */ jsxs11("span", { children: [
|
|
1350
1435
|
visible.length,
|
|
1351
1436
|
" generation",
|
|
1352
1437
|
visible.length !== 1 ? "s" : ""
|
|
1353
1438
|
] }),
|
|
1354
|
-
/* @__PURE__ */
|
|
1355
|
-
/* @__PURE__ */
|
|
1439
|
+
/* @__PURE__ */ jsxs11("span", { className: "flex items-center gap-0.5", children: [
|
|
1440
|
+
/* @__PURE__ */ jsx12(DollarSign2, { className: "h-3 w-3" }),
|
|
1356
1441
|
totalCost.toFixed(2),
|
|
1357
1442
|
" spent"
|
|
1358
1443
|
] })
|
|
1359
1444
|
] })
|
|
1360
1445
|
] }),
|
|
1361
|
-
/* @__PURE__ */
|
|
1446
|
+
/* @__PURE__ */ jsx12(GenerationGrid, { generations: visible, typeFilter, onSelect })
|
|
1362
1447
|
] });
|
|
1363
1448
|
}
|
|
1364
1449
|
|
|
1365
1450
|
// src/studio-react/generation-detail-modal.tsx
|
|
1366
1451
|
import { Dialog as Dialog2, DialogContent, DialogHeader, DialogTitle } from "@tangle-network/sandbox-ui/primitives";
|
|
1367
|
-
import { jsx as
|
|
1452
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1368
1453
|
function GenerationDetailModal({
|
|
1369
1454
|
generation,
|
|
1370
1455
|
vaultHref,
|
|
1371
1456
|
onClose
|
|
1372
1457
|
}) {
|
|
1373
1458
|
const cfg = generation ? typeConfigFor(generation.type) : null;
|
|
1374
|
-
return /* @__PURE__ */
|
|
1459
|
+
return /* @__PURE__ */ jsx13(Dialog2, { open: generation != null, onOpenChange: (open) => {
|
|
1375
1460
|
if (!open) onClose();
|
|
1376
|
-
}, children: /* @__PURE__ */
|
|
1377
|
-
/* @__PURE__ */
|
|
1378
|
-
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 })
|
|
1379
1464
|
] }) });
|
|
1380
1465
|
}
|
|
1381
1466
|
export {
|
|
1382
|
-
AvatarComposer,
|
|
1383
|
-
ComposerDisclosure,
|
|
1384
|
-
ComposerHero,
|
|
1385
|
-
Field,
|
|
1386
1467
|
GenerationCard,
|
|
1387
1468
|
GenerationDetail,
|
|
1388
1469
|
GenerationDetailModal,
|
|
1389
1470
|
GenerationGrid,
|
|
1471
|
+
GenerationNoticeChip,
|
|
1390
1472
|
GenerationStatusBadge,
|
|
1391
|
-
ImageComposer,
|
|
1392
1473
|
LibraryDrawer,
|
|
1393
1474
|
LibraryPanel,
|
|
1394
|
-
NativeSelect,
|
|
1395
|
-
PublishPackageComposer,
|
|
1396
1475
|
ResultCanvas,
|
|
1397
|
-
|
|
1398
|
-
Stepper,
|
|
1476
|
+
StudioComposer,
|
|
1399
1477
|
StudioHeader,
|
|
1400
1478
|
StudioSheet,
|
|
1401
1479
|
StudioWorkspace,
|
|
1402
1480
|
TYPE_CONFIG,
|
|
1403
|
-
TranscriptionComposer,
|
|
1404
|
-
TranscriptionOptions,
|
|
1405
|
-
VideoComposer,
|
|
1406
1481
|
filterGenerations,
|
|
1407
1482
|
typeConfigFor,
|
|
1408
1483
|
useStudioGenerations
|