@tangle-network/agent-app 0.46.7 → 0.46.9
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.
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ModelOptionValue } from '../studio';
|
|
2
|
+
import type { ComposerType } from './studio-composer';
|
|
3
|
+
export interface PersistedComposerSelections {
|
|
4
|
+
v: 1;
|
|
5
|
+
type: ComposerType;
|
|
6
|
+
selectedModels: Partial<Record<ComposerType, string>>;
|
|
7
|
+
/** Option values are model-specific: two models in one lane may publish
|
|
8
|
+
* different enums for the same parameter. */
|
|
9
|
+
optionsByModel: Record<string, Record<string, ModelOptionValue>>;
|
|
10
|
+
}
|
|
11
|
+
export declare function loadComposerSelections(workspaceId: string): PersistedComposerSelections | null;
|
|
12
|
+
export declare function saveComposerSelections(workspaceId: string, snapshot: PersistedComposerSelections): void;
|
|
@@ -625,7 +625,7 @@ function ModelPill({
|
|
|
625
625
|
children: [
|
|
626
626
|
unavailable && /* @__PURE__ */ jsx(TriangleAlert, { "aria-hidden": true, className: "h-3.5 w-3.5 shrink-0 text-warning", strokeWidth: 2 }),
|
|
627
627
|
provider && /* @__PURE__ */ jsx(ProviderLogo, { provider, size: 14 }),
|
|
628
|
-
/* @__PURE__ */ jsx("span", { className:
|
|
628
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-[168px] truncate leading-normal", children: displayName }),
|
|
629
629
|
/* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 shrink-0 text-muted-foreground" })
|
|
630
630
|
]
|
|
631
631
|
}
|
|
@@ -685,6 +685,58 @@ function optionValueLabel(param, value) {
|
|
|
685
685
|
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
686
686
|
}
|
|
687
687
|
|
|
688
|
+
// src/studio-react/composer-persistence.ts
|
|
689
|
+
var COMPOSER_TYPES = ["image", "video", "speech"];
|
|
690
|
+
function storageKey(workspaceId) {
|
|
691
|
+
return `studio-composer:${workspaceId}`;
|
|
692
|
+
}
|
|
693
|
+
function isPlainObject(value) {
|
|
694
|
+
if (value === null || typeof value !== "object") return false;
|
|
695
|
+
const prototype = Object.getPrototypeOf(value);
|
|
696
|
+
return prototype === Object.prototype || prototype === null;
|
|
697
|
+
}
|
|
698
|
+
function isComposerType(value) {
|
|
699
|
+
return COMPOSER_TYPES.some((type) => type === value);
|
|
700
|
+
}
|
|
701
|
+
function isOptionValue(value) {
|
|
702
|
+
return typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value);
|
|
703
|
+
}
|
|
704
|
+
function parseSelections(value) {
|
|
705
|
+
if (!isPlainObject(value) || value.v !== 1 || !isComposerType(value.type)) return null;
|
|
706
|
+
if (!isPlainObject(value.selectedModels) || !isPlainObject(value.optionsByModel)) return null;
|
|
707
|
+
const selectedModels = {};
|
|
708
|
+
for (const type of COMPOSER_TYPES) {
|
|
709
|
+
const modelId = value.selectedModels[type];
|
|
710
|
+
if (typeof modelId === "string") selectedModels[type] = modelId;
|
|
711
|
+
}
|
|
712
|
+
const optionsByModel = {};
|
|
713
|
+
for (const [modelId, rawOptions] of Object.entries(value.optionsByModel)) {
|
|
714
|
+
if (!isPlainObject(rawOptions)) continue;
|
|
715
|
+
const options = {};
|
|
716
|
+
for (const [param, optionValue] of Object.entries(rawOptions)) {
|
|
717
|
+
if (isOptionValue(optionValue)) options[param] = optionValue;
|
|
718
|
+
}
|
|
719
|
+
optionsByModel[modelId] = options;
|
|
720
|
+
}
|
|
721
|
+
return { v: 1, type: value.type, selectedModels, optionsByModel };
|
|
722
|
+
}
|
|
723
|
+
function loadComposerSelections(workspaceId) {
|
|
724
|
+
if (typeof window === "undefined") return null;
|
|
725
|
+
try {
|
|
726
|
+
const raw = window.localStorage.getItem(storageKey(workspaceId));
|
|
727
|
+
return raw === null ? null : parseSelections(JSON.parse(raw));
|
|
728
|
+
} catch {
|
|
729
|
+
return null;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
function saveComposerSelections(workspaceId, snapshot) {
|
|
733
|
+
if (typeof window === "undefined") return;
|
|
734
|
+
try {
|
|
735
|
+
window.localStorage.setItem(storageKey(workspaceId), JSON.stringify(snapshot));
|
|
736
|
+
} catch {
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
688
740
|
// src/studio-react/studio-composer.tsx
|
|
689
741
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
690
742
|
var SEGMENTS = [
|
|
@@ -760,8 +812,26 @@ function StudioComposer({
|
|
|
760
812
|
const [referenceImageUrl, setReferenceImageUrl] = useState3(null);
|
|
761
813
|
const [isSubmitting, setIsSubmitting] = useState3(false);
|
|
762
814
|
const [error, setError] = useState3(null);
|
|
815
|
+
const [hydratedWorkspaceId, setHydratedWorkspaceId] = useState3(null);
|
|
763
816
|
const submitLockRef = useRef2(false);
|
|
764
817
|
const bandRef = useRef2(null);
|
|
818
|
+
const hydratedRef = useRef2(false);
|
|
819
|
+
const persistedOptionsRef = useRef2({});
|
|
820
|
+
useEffect3(() => {
|
|
821
|
+
hydratedRef.current = false;
|
|
822
|
+
setHydratedWorkspaceId(null);
|
|
823
|
+
if (!workspaceId) return;
|
|
824
|
+
const persisted = loadComposerSelections(workspaceId);
|
|
825
|
+
if (persisted) {
|
|
826
|
+
setType(persisted.type);
|
|
827
|
+
setSelectedModels(persisted.selectedModels);
|
|
828
|
+
persistedOptionsRef.current = persisted.optionsByModel;
|
|
829
|
+
} else {
|
|
830
|
+
persistedOptionsRef.current = {};
|
|
831
|
+
}
|
|
832
|
+
hydratedRef.current = true;
|
|
833
|
+
setHydratedWorkspaceId(workspaceId);
|
|
834
|
+
}, [workspaceId]);
|
|
765
835
|
useEffect3(() => {
|
|
766
836
|
if (!workspaceId) return;
|
|
767
837
|
let cancelled = false;
|
|
@@ -814,13 +884,35 @@ function StudioComposer({
|
|
|
814
884
|
);
|
|
815
885
|
useEffect3(() => {
|
|
816
886
|
setOptionValues((current) => {
|
|
817
|
-
const
|
|
887
|
+
const seed = { ...persistedOptionsRef.current[modelId], ...current[type] };
|
|
888
|
+
const next = reconcileOptionValues(options, seed, {
|
|
818
889
|
allowCustomSize: supportsCustomImageSize(modelId)
|
|
819
890
|
});
|
|
820
891
|
const unchanged = Object.keys(next).length === Object.keys(current[type]).length && Object.entries(next).every(([key, value]) => current[type][key] === value);
|
|
821
892
|
return unchanged ? current : { ...current, [type]: next };
|
|
822
893
|
});
|
|
823
894
|
}, [modelId, options, type]);
|
|
895
|
+
useEffect3(() => {
|
|
896
|
+
if (!workspaceId || !hydratedRef.current || hydratedWorkspaceId !== workspaceId) return;
|
|
897
|
+
if (!catalog && modelId && persistedOptionsRef.current[modelId] !== void 0 && !options) return;
|
|
898
|
+
const seed = { ...persistedOptionsRef.current[modelId], ...optionValues[type] };
|
|
899
|
+
const reconciled = reconcileOptionValues(options, seed, {
|
|
900
|
+
allowCustomSize: supportsCustomImageSize(modelId)
|
|
901
|
+
});
|
|
902
|
+
const reconciledReady = Object.keys(reconciled).length === Object.keys(optionValues[type]).length && Object.entries(reconciled).every(([key, value]) => optionValues[type][key] === value);
|
|
903
|
+
if (!reconciledReady) return;
|
|
904
|
+
const optionsByModel = {
|
|
905
|
+
...persistedOptionsRef.current,
|
|
906
|
+
...modelId ? { [modelId]: optionValues[type] } : {}
|
|
907
|
+
};
|
|
908
|
+
saveComposerSelections(workspaceId, {
|
|
909
|
+
v: 1,
|
|
910
|
+
type,
|
|
911
|
+
selectedModels,
|
|
912
|
+
optionsByModel
|
|
913
|
+
});
|
|
914
|
+
persistedOptionsRef.current = optionsByModel;
|
|
915
|
+
}, [catalog, hydratedWorkspaceId, modelId, optionValues, options, selectedModels, type, workspaceId]);
|
|
824
916
|
const values = optionValues[type];
|
|
825
917
|
const params = visibleParams(type, options);
|
|
826
918
|
const audioMeta = type === "video" ? options?.audio : void 0;
|
|
@@ -1023,7 +1115,7 @@ function StudioComposer({
|
|
|
1023
1115
|
title: "Generate",
|
|
1024
1116
|
disabled: !canSubmit,
|
|
1025
1117
|
onClick: () => void generate(),
|
|
1026
|
-
className: "
|
|
1118
|
+
className: "ml-auto inline-flex h-8 w-8 flex-none items-center justify-center rounded-full bg-foreground text-background transition hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-card",
|
|
1027
1119
|
children: /* @__PURE__ */ jsx2(ArrowUp, { className: "h-4 w-4", strokeWidth: 2 })
|
|
1028
1120
|
}
|
|
1029
1121
|
)
|
|
@@ -2490,7 +2582,7 @@ function StudioHomeScreen({
|
|
|
2490
2582
|
}
|
|
2491
2583
|
|
|
2492
2584
|
// src/studio-react/studio-generation-screen.tsx
|
|
2493
|
-
import { ArrowLeft } from "lucide-react";
|
|
2585
|
+
import { ArrowLeft, CircleAlert } from "lucide-react";
|
|
2494
2586
|
import {
|
|
2495
2587
|
useCallback as useCallback9,
|
|
2496
2588
|
useEffect as useEffect12,
|
|
@@ -2618,12 +2710,17 @@ function StudioGenerationScreen({
|
|
|
2618
2710
|
return /* @__PURE__ */ jsx11("div", { className: "studio-skeleton", style: { "--r": ratio } }, row.id);
|
|
2619
2711
|
}
|
|
2620
2712
|
if (generationStatus(row) === "failed") {
|
|
2621
|
-
|
|
2713
|
+
const reason = generationError(row);
|
|
2714
|
+
return /* @__PURE__ */ jsxs10(
|
|
2622
2715
|
"div",
|
|
2623
2716
|
{
|
|
2624
|
-
className: "
|
|
2717
|
+
className: "flex flex-col items-center justify-center gap-1.5 bg-accent p-3 text-center",
|
|
2625
2718
|
style: { aspectRatio: ratio },
|
|
2626
|
-
children:
|
|
2719
|
+
children: [
|
|
2720
|
+
/* @__PURE__ */ jsx11(CircleAlert, { "aria-hidden": true, className: "h-4 w-4 flex-none text-destructive", strokeWidth: 2 }),
|
|
2721
|
+
/* @__PURE__ */ jsx11("p", { className: "text-[13px] font-medium text-foreground", children: "Generation failed" }),
|
|
2722
|
+
reason && reason !== "Generation failed" && /* @__PURE__ */ jsx11("p", { className: "line-clamp-3 text-[12px] text-muted-foreground", children: reason })
|
|
2723
|
+
]
|
|
2627
2724
|
},
|
|
2628
2725
|
row.id
|
|
2629
2726
|
);
|