feat(studio): persist per-element-type pinned groups in studioUiPreferences

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Vance Ingalls
2026-07-14 15:51:53 -07:00
co-authored by Claude Sonnet 5
parent 384fe5934a
commit 0d7a60a525
2 changed files with 55 additions and 0 deletions
@@ -88,3 +88,48 @@ describe("timeline zoom pin persistence", () => {
expect(prefs.timelineManualZoomPercent).toBeUndefined();
});
});
function fakeStorage(): Storage {
const map = new Map<string, string>();
return {
getItem: (k) => map.get(k) ?? null,
setItem: (k, v) => void map.set(k, v),
removeItem: (k) => void map.delete(k),
clear: () => map.clear(),
key: () => null,
get length() {
return map.size;
},
} as Storage;
}
describe("pinnedGroupsByElementType", () => {
it("round-trips a per-element-type pin map", () => {
const storage = fakeStorage();
writeStudioUiPreferences(
{ pinnedGroupsByElementType: { text: ["motion"], media: ["grade"] } },
storage,
);
const read = readStudioUiPreferences(storage);
expect(read.pinnedGroupsByElementType).toEqual({ text: ["motion"], media: ["grade"] });
});
it("ignores a malformed pinnedGroupsByElementType (non-object, or non-string-array values)", () => {
const storage = fakeStorage();
storage.setItem(
"hf-studio-ui-preferences",
JSON.stringify({ pinnedGroupsByElementType: { text: "not-an-array", media: [1, 2] } }),
);
const read = readStudioUiPreferences(storage);
expect(read.pinnedGroupsByElementType).toEqual({ media: [] });
});
it("merges a pin-map patch without clobbering other preferences", () => {
const storage = fakeStorage();
writeStudioUiPreferences({ audioMuted: true }, storage);
writeStudioUiPreferences({ pinnedGroupsByElementType: { text: ["style"] } }, storage);
const read = readStudioUiPreferences(storage);
expect(read.audioMuted).toBe(true);
expect(read.pinnedGroupsByElementType).toEqual({ text: ["style"] });
});
});
@@ -28,6 +28,7 @@ export interface StudioUiPreferences {
timelineZoomMode?: "fit" | "manual";
/** Manual timeline zoom percent, paired with `timelineZoomMode: "manual"`. */
timelineManualZoomPercent?: number;
pinnedGroupsByElementType?: Record<string, string[]>;
}
const STUDIO_UI_PREFERENCES_KEY = "hf-studio-ui-preferences";
@@ -115,6 +116,15 @@ function readStorage(storage: Storage | null): StudioUiPreferences {
) {
preferences.timelineManualZoomPercent = parsed.timelineManualZoomPercent;
}
if (isRecord(parsed.pinnedGroupsByElementType)) {
const map: Record<string, string[]> = {};
for (const [kind, ids] of Object.entries(parsed.pinnedGroupsByElementType)) {
if (Array.isArray(ids)) {
map[kind] = ids.filter((id): id is string => typeof id === "string");
}
}
preferences.pinnedGroupsByElementType = map;
}
return preferences;
} catch {
return {};