@tangle-network/agent-app 0.60.153 → 0.60.155
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.
|
@@ -91,6 +91,32 @@ export declare function OptionPill({ label, value, choices, onSelect, bandRef, c
|
|
|
91
91
|
}) => ReactNode;
|
|
92
92
|
};
|
|
93
93
|
}): JSX.Element;
|
|
94
|
+
export interface GenerationSetting {
|
|
95
|
+
key: string;
|
|
96
|
+
label: string;
|
|
97
|
+
summary: string;
|
|
98
|
+
value: ModelOptionValue | undefined;
|
|
99
|
+
choices: readonly OptionChoice[];
|
|
100
|
+
onSelect: (value: ModelOptionValue) => void;
|
|
101
|
+
icon?: LucideIcon;
|
|
102
|
+
/** A binary setting acts immediately instead of opening a two-row menu. */
|
|
103
|
+
onToggle?: () => void;
|
|
104
|
+
custom?: {
|
|
105
|
+
label: string;
|
|
106
|
+
render: (args: {
|
|
107
|
+
close: () => void;
|
|
108
|
+
}) => ReactNode;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** The model's current defaults and optional parameters in one compact menu.
|
|
112
|
+
* The value list opens inside the panel, so no nested portal can be clipped or
|
|
113
|
+
* mistaken for an outside click. A long duration or voice list scrolls within
|
|
114
|
+
* 16rem while the rest of the settings stay in place. */
|
|
115
|
+
export declare function GenerationSettings({ settings, bandRef, resetKey, }: {
|
|
116
|
+
settings: readonly GenerationSetting[];
|
|
117
|
+
bandRef: RefObject<HTMLDivElement | null>;
|
|
118
|
+
resetKey: string;
|
|
119
|
+
}): JSX.Element;
|
|
94
120
|
/**
|
|
95
121
|
* The custom-size form the Size menu swaps to.
|
|
96
122
|
*
|
|
@@ -127,7 +127,7 @@ import {
|
|
|
127
127
|
Monitor,
|
|
128
128
|
Ratio,
|
|
129
129
|
Scaling,
|
|
130
|
-
SlidersHorizontal,
|
|
130
|
+
SlidersHorizontal as SlidersHorizontal2,
|
|
131
131
|
Sparkles,
|
|
132
132
|
TriangleAlert,
|
|
133
133
|
Video
|
|
@@ -140,7 +140,7 @@ import {
|
|
|
140
140
|
useId,
|
|
141
141
|
useState as useState2
|
|
142
142
|
} from "react";
|
|
143
|
-
import { ImagePlus, Volume2, VolumeX, X } from "lucide-react";
|
|
143
|
+
import { ImagePlus, SlidersHorizontal, Volume2, VolumeX, X } from "lucide-react";
|
|
144
144
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
145
145
|
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-[14px] font-medium text-foreground transition hover:bg-accent";
|
|
146
146
|
var PILL_LABEL = "[text-box:trim-both_cap_alphabetic]";
|
|
@@ -403,6 +403,166 @@ function OptionPill({
|
|
|
403
403
|
)
|
|
404
404
|
] });
|
|
405
405
|
}
|
|
406
|
+
function GenerationSettings({
|
|
407
|
+
settings,
|
|
408
|
+
bandRef,
|
|
409
|
+
resetKey
|
|
410
|
+
}) {
|
|
411
|
+
const [open, setOpen] = useState2(false);
|
|
412
|
+
const [more, setMore] = useState2(false);
|
|
413
|
+
const [active, setActive] = useState2(null);
|
|
414
|
+
const [customOpen, setCustomOpen] = useState2(false);
|
|
415
|
+
const [focusAfterSelect, setFocusAfterSelect] = useState2(null);
|
|
416
|
+
const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen);
|
|
417
|
+
const panelId = useId();
|
|
418
|
+
useCloseWhenScrolledOut(open, setOpen, triggerRef, bandRef);
|
|
419
|
+
useEffect2(() => {
|
|
420
|
+
setOpen(false);
|
|
421
|
+
setMore(false);
|
|
422
|
+
setActive(null);
|
|
423
|
+
setCustomOpen(false);
|
|
424
|
+
setFocusAfterSelect(null);
|
|
425
|
+
}, [resetKey]);
|
|
426
|
+
useEffect2(() => {
|
|
427
|
+
if (open) panelRef.current?.querySelector("[data-setting-key]")?.focus();
|
|
428
|
+
}, [open, panelRef]);
|
|
429
|
+
useEffect2(() => {
|
|
430
|
+
if (!focusAfterSelect) return;
|
|
431
|
+
const rows = panelRef.current?.querySelectorAll("[data-setting-key]");
|
|
432
|
+
Array.from(rows ?? []).find((row) => row.dataset.settingKey === focusAfterSelect)?.focus();
|
|
433
|
+
setFocusAfterSelect(null);
|
|
434
|
+
}, [focusAfterSelect, panelRef]);
|
|
435
|
+
const primary = settings.slice(0, 3);
|
|
436
|
+
const visible = more ? settings : primary;
|
|
437
|
+
const summary = primary.map((setting) => setting.summary).join(" \xB7 ");
|
|
438
|
+
const fullSummary = settings.map((setting) => `${setting.label}: ${setting.summary}`).join(", ");
|
|
439
|
+
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative inline-flex flex-none", children: [
|
|
440
|
+
/* @__PURE__ */ jsxs(
|
|
441
|
+
"button",
|
|
442
|
+
{
|
|
443
|
+
type: "button",
|
|
444
|
+
...triggerProps,
|
|
445
|
+
"aria-haspopup": "dialog",
|
|
446
|
+
"aria-controls": open ? panelId : void 0,
|
|
447
|
+
"aria-label": `Generation settings: ${fullSummary}`,
|
|
448
|
+
title: "Generation settings",
|
|
449
|
+
onClick: () => setOpen(!open),
|
|
450
|
+
className: PILL,
|
|
451
|
+
children: [
|
|
452
|
+
/* @__PURE__ */ jsx(SlidersHorizontal, { "aria-hidden": true, className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", strokeWidth: 1.5 }),
|
|
453
|
+
/* @__PURE__ */ jsx("span", { className: PILL_LABEL, children: "Settings" }),
|
|
454
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "hidden max-w-[150px] truncate text-muted-foreground @[640px]/studio-composer:inline", children: summary }),
|
|
455
|
+
/* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true, className: "h-3 w-3 shrink-0 text-muted-foreground" })
|
|
456
|
+
]
|
|
457
|
+
}
|
|
458
|
+
),
|
|
459
|
+
/* @__PURE__ */ jsxs(
|
|
460
|
+
PopoverSurface,
|
|
461
|
+
{
|
|
462
|
+
open,
|
|
463
|
+
id: panelId,
|
|
464
|
+
role: "dialog",
|
|
465
|
+
"aria-label": "Generation settings",
|
|
466
|
+
triggerRef,
|
|
467
|
+
panelRef,
|
|
468
|
+
contentKey: `${active}:${more}:${customOpen}`,
|
|
469
|
+
className: `${MENU_PANEL} w-[min(320px,calc(100vw-24px))] gap-0.5`,
|
|
470
|
+
children: [
|
|
471
|
+
/* @__PURE__ */ jsx("div", { className: MENU_HEADER, children: "Generation settings" }),
|
|
472
|
+
visible.map((setting) => {
|
|
473
|
+
const Icon = setting.icon;
|
|
474
|
+
const expanded = active === setting.key;
|
|
475
|
+
return /* @__PURE__ */ jsx("div", { children: setting.onToggle ? /* @__PURE__ */ jsxs(
|
|
476
|
+
"button",
|
|
477
|
+
{
|
|
478
|
+
type: "button",
|
|
479
|
+
role: "switch",
|
|
480
|
+
"data-setting-key": setting.key,
|
|
481
|
+
"aria-checked": setting.value === true,
|
|
482
|
+
"aria-label": setting.label,
|
|
483
|
+
onClick: setting.onToggle,
|
|
484
|
+
className: menuRowClass(false),
|
|
485
|
+
children: [
|
|
486
|
+
Icon && /* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: "h-3.5 w-3.5 shrink-0", strokeWidth: 1.5 }),
|
|
487
|
+
/* @__PURE__ */ jsx("span", { children: setting.label }),
|
|
488
|
+
/* @__PURE__ */ jsx("span", { className: "ml-auto text-muted-foreground", children: setting.summary })
|
|
489
|
+
]
|
|
490
|
+
}
|
|
491
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
492
|
+
/* @__PURE__ */ jsxs(
|
|
493
|
+
"button",
|
|
494
|
+
{
|
|
495
|
+
type: "button",
|
|
496
|
+
"data-setting-key": setting.key,
|
|
497
|
+
"aria-expanded": expanded,
|
|
498
|
+
"aria-label": `${setting.label}: ${setting.summary}`,
|
|
499
|
+
onClick: () => {
|
|
500
|
+
setActive(expanded ? null : setting.key);
|
|
501
|
+
setCustomOpen(false);
|
|
502
|
+
},
|
|
503
|
+
className: menuRowClass(expanded),
|
|
504
|
+
children: [
|
|
505
|
+
Icon && /* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: "h-3.5 w-3.5 shrink-0", strokeWidth: 1.5 }),
|
|
506
|
+
/* @__PURE__ */ jsx("span", { children: setting.label }),
|
|
507
|
+
/* @__PURE__ */ jsx("span", { className: "ml-auto truncate text-muted-foreground", children: setting.summary }),
|
|
508
|
+
/* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true, className: "h-3 w-3 shrink-0 text-muted-foreground" })
|
|
509
|
+
]
|
|
510
|
+
}
|
|
511
|
+
),
|
|
512
|
+
expanded && /* @__PURE__ */ jsx("div", { role: "group", "aria-label": `${setting.label} choices`, className: "ml-5 max-h-64 overflow-y-auto overscroll-contain border-l border-border pl-1", children: customOpen && setting.custom ? setting.custom.render({ close: () => {
|
|
513
|
+
setActive(null);
|
|
514
|
+
setCustomOpen(false);
|
|
515
|
+
setFocusAfterSelect(setting.key);
|
|
516
|
+
} }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
517
|
+
setting.choices.map((choice, index) => /* @__PURE__ */ jsxs(
|
|
518
|
+
"label",
|
|
519
|
+
{
|
|
520
|
+
className: `${menuRowClass(choice.value === setting.value)} relative focus-within:outline focus-within:outline-2 focus-within:outline-ring`,
|
|
521
|
+
children: [
|
|
522
|
+
/* @__PURE__ */ jsx(
|
|
523
|
+
"input",
|
|
524
|
+
{
|
|
525
|
+
type: "radio",
|
|
526
|
+
name: `${panelId}-${setting.key}`,
|
|
527
|
+
value: String(choice.value),
|
|
528
|
+
checked: choice.value === setting.value,
|
|
529
|
+
onChange: () => {
|
|
530
|
+
setting.onSelect(choice.value);
|
|
531
|
+
setActive(null);
|
|
532
|
+
setFocusAfterSelect(setting.key);
|
|
533
|
+
},
|
|
534
|
+
className: "absolute inset-0 h-full w-full cursor-pointer opacity-0"
|
|
535
|
+
}
|
|
536
|
+
),
|
|
537
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: choice.label }),
|
|
538
|
+
choice.value === setting.value && /* @__PURE__ */ jsx(CheckGlyph, { "aria-hidden": true, className: "ml-auto h-3.5 w-3.5 shrink-0 text-primary" })
|
|
539
|
+
]
|
|
540
|
+
},
|
|
541
|
+
`${String(choice.value)}-${index}`
|
|
542
|
+
)),
|
|
543
|
+
setting.custom && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCustomOpen(true), className: `${menuRowClass(false)} text-muted-foreground`, children: setting.custom.label })
|
|
544
|
+
] }) })
|
|
545
|
+
] }) }, setting.key);
|
|
546
|
+
}),
|
|
547
|
+
settings.length > 3 && /* @__PURE__ */ jsx(
|
|
548
|
+
"button",
|
|
549
|
+
{
|
|
550
|
+
type: "button",
|
|
551
|
+
"aria-expanded": more,
|
|
552
|
+
onClick: () => {
|
|
553
|
+
setMore(!more);
|
|
554
|
+
setActive(null);
|
|
555
|
+
setCustomOpen(false);
|
|
556
|
+
},
|
|
557
|
+
className: `${menuRowClass(false)} text-primary`,
|
|
558
|
+
children: more ? "Less" : `More settings (${settings.length - 3})`
|
|
559
|
+
}
|
|
560
|
+
)
|
|
561
|
+
]
|
|
562
|
+
}
|
|
563
|
+
)
|
|
564
|
+
] });
|
|
565
|
+
}
|
|
406
566
|
function CustomSizeForm({
|
|
407
567
|
initial,
|
|
408
568
|
onApply,
|
|
@@ -681,7 +841,7 @@ var SEGMENTS = [
|
|
|
681
841
|
{ type: "video", label: "Video", icon: Video },
|
|
682
842
|
{ type: "speech", label: "Audio", icon: AudioLines }
|
|
683
843
|
];
|
|
684
|
-
var
|
|
844
|
+
var PARAM_ORDER = {
|
|
685
845
|
image: ["size", "quality", "n"],
|
|
686
846
|
video: ["duration", "resolution", "aspect_ratio", "mode"],
|
|
687
847
|
speech: ["voice", "speed"]
|
|
@@ -704,7 +864,7 @@ var PARAM_ICONS = {
|
|
|
704
864
|
duration: Clock,
|
|
705
865
|
resolution: Monitor,
|
|
706
866
|
aspect_ratio: Ratio,
|
|
707
|
-
mode:
|
|
867
|
+
mode: SlidersHorizontal2,
|
|
708
868
|
voice: Mic,
|
|
709
869
|
speed: Gauge
|
|
710
870
|
};
|
|
@@ -743,7 +903,7 @@ function pickerModel(model, type) {
|
|
|
743
903
|
}
|
|
744
904
|
function visibleParams(type, options) {
|
|
745
905
|
if (!options) return [];
|
|
746
|
-
return
|
|
906
|
+
return PARAM_ORDER[type].filter((param) => {
|
|
747
907
|
const meta = options[param];
|
|
748
908
|
return Boolean(meta) && meta?.supported !== false;
|
|
749
909
|
});
|
|
@@ -1019,6 +1179,50 @@ function StudioComposer({
|
|
|
1019
1179
|
const modelDisplayName = laneDown ? "Unavailable" : modelOption?.name || modelId || "Select a model";
|
|
1020
1180
|
const modelUnavailable = laneDown || modelOption?.status === "unavailable";
|
|
1021
1181
|
const canReloadModels = Boolean(workspaceId) && !loadingModels && Boolean(catalogError || catalogReady && curatedModels.length === 0);
|
|
1182
|
+
const settingParams = type === "video" ? ["aspect_ratio", "duration", ...params.filter((param) => param !== "aspect_ratio" && param !== "duration")].filter((param) => params.includes(param)) : params;
|
|
1183
|
+
const settings = settingParams.map((param) => {
|
|
1184
|
+
const value = values[param] ?? options?.[param]?.default;
|
|
1185
|
+
const choices = choicesForSetting(param, options?.[param], value);
|
|
1186
|
+
const label = PARAM_LABELS[param] ?? param;
|
|
1187
|
+
return {
|
|
1188
|
+
key: param,
|
|
1189
|
+
label,
|
|
1190
|
+
summary: choices.find((choice) => choice.value === value)?.label ?? "\u2014",
|
|
1191
|
+
value,
|
|
1192
|
+
choices,
|
|
1193
|
+
onSelect: (next) => setOption(param, next),
|
|
1194
|
+
icon: PARAM_ICONS[param] ?? SlidersHorizontal2,
|
|
1195
|
+
...param === "size" && supportsCustomImageSize(modelId) ? {
|
|
1196
|
+
custom: {
|
|
1197
|
+
label: "Custom\u2026",
|
|
1198
|
+
render: ({ close }) => /* @__PURE__ */ jsx2(
|
|
1199
|
+
CustomSizeForm,
|
|
1200
|
+
{
|
|
1201
|
+
initial: asText(value),
|
|
1202
|
+
onApply: (size) => {
|
|
1203
|
+
setOption("size", size);
|
|
1204
|
+
close();
|
|
1205
|
+
},
|
|
1206
|
+
onCancel: close
|
|
1207
|
+
}
|
|
1208
|
+
)
|
|
1209
|
+
}
|
|
1210
|
+
} : {}
|
|
1211
|
+
};
|
|
1212
|
+
});
|
|
1213
|
+
if (audioSupported) {
|
|
1214
|
+
const audioOn = (values.audio ?? audioMeta?.default) === true;
|
|
1215
|
+
settings.splice(Math.min(2, settings.length), 0, {
|
|
1216
|
+
key: "audio",
|
|
1217
|
+
label: "Audio",
|
|
1218
|
+
summary: audioOn ? "Audio on" : "Audio off",
|
|
1219
|
+
value: audioOn,
|
|
1220
|
+
choices: [],
|
|
1221
|
+
onSelect: (next) => setOption("audio", next),
|
|
1222
|
+
onToggle: () => setOption("audio", !audioOn),
|
|
1223
|
+
icon: AudioLines
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1022
1226
|
function reloadModels() {
|
|
1023
1227
|
if (!workspaceId || catalogLoading || catalogPendingRef.current) return;
|
|
1024
1228
|
catalogPendingRef.current = true;
|
|
@@ -1073,38 +1277,7 @@ function StudioComposer({
|
|
|
1073
1277
|
] })
|
|
1074
1278
|
}
|
|
1075
1279
|
) }),
|
|
1076
|
-
|
|
1077
|
-
OptionPill,
|
|
1078
|
-
{
|
|
1079
|
-
label: PARAM_LABELS[param] ?? param,
|
|
1080
|
-
icon: PARAM_ICONS[param] ?? SlidersHorizontal,
|
|
1081
|
-
value: values[param],
|
|
1082
|
-
choices: choicesForPill(param, options?.[param], values[param]),
|
|
1083
|
-
onSelect: (value) => setOption(param, value),
|
|
1084
|
-
bandRef,
|
|
1085
|
-
custom: param === "size" && supportsCustomImageSize(modelId) ? {
|
|
1086
|
-
label: "Custom\u2026",
|
|
1087
|
-
render: ({ close }) => /* @__PURE__ */ jsx2(
|
|
1088
|
-
CustomSizeForm,
|
|
1089
|
-
{
|
|
1090
|
-
initial: asText(values.size),
|
|
1091
|
-
onApply: (size) => {
|
|
1092
|
-
setOption("size", size);
|
|
1093
|
-
close();
|
|
1094
|
-
},
|
|
1095
|
-
onCancel: close
|
|
1096
|
-
}
|
|
1097
|
-
)
|
|
1098
|
-
} : void 0
|
|
1099
|
-
}
|
|
1100
|
-
) }, `${type}-${param}`)),
|
|
1101
|
-
audioSupported && /* @__PURE__ */ jsx2("div", { className: "studio-pill-in flex-none", children: /* @__PURE__ */ jsx2(
|
|
1102
|
-
AudioTogglePill,
|
|
1103
|
-
{
|
|
1104
|
-
on: values.audio === true,
|
|
1105
|
-
onToggle: (on) => setOption("audio", on)
|
|
1106
|
-
}
|
|
1107
|
-
) }, `${type}-audio`),
|
|
1280
|
+
settings.length > 0 && /* @__PURE__ */ jsx2("div", { className: "studio-pill-in flex-none", children: /* @__PURE__ */ jsx2(GenerationSettings, { settings, bandRef, resetKey: `${type}:${modelId}` }) }),
|
|
1108
1281
|
referenceSupported && /* @__PURE__ */ jsx2("div", { className: "studio-pill-in flex-none", children: /* @__PURE__ */ jsx2(
|
|
1109
1282
|
ReferencePill,
|
|
1110
1283
|
{
|
|
@@ -1140,7 +1313,7 @@ function asText(value) {
|
|
|
1140
1313
|
function asWireScalar(value) {
|
|
1141
1314
|
return typeof value === "string" || typeof value === "number" ? value : void 0;
|
|
1142
1315
|
}
|
|
1143
|
-
function
|
|
1316
|
+
function choicesForSetting(param, meta, value) {
|
|
1144
1317
|
if (!meta) return [];
|
|
1145
1318
|
const choices = choicesFor(param, meta).map((choice) => ({
|
|
1146
1319
|
value: choice,
|
|
@@ -3334,6 +3507,7 @@ export {
|
|
|
3334
3507
|
ComposerBand,
|
|
3335
3508
|
CustomSizeForm,
|
|
3336
3509
|
GenerationNoticeChip,
|
|
3510
|
+
GenerationSettings,
|
|
3337
3511
|
MediaTile,
|
|
3338
3512
|
MediaTypeSegments,
|
|
3339
3513
|
MediaViewerModal,
|