@tangle-network/agent-app 0.46.8 → 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;
|
|
@@ -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;
|