fix(studio): a host can claim its writes under the key Studio's edit history uses (#5064)

* fix(studio): a host can claim its writes under the key Studio's edit history uses

* fix(studio): drop the claimKey comment, the comment share may not rise
This commit is contained in:
Miguel Ángel
2026-10-05 10:54:36 +00:00
committed by GitHub
parent 6791ea580c
commit 72d4cc3ef4
2 changed files with 29 additions and 3 deletions
@@ -62,7 +62,7 @@ async function studio({ withHistory = true } = {}) {
const file = () => readFileSync(join(dir, "index.html"), "utf8");
const save = (content: string) => writeFileSync(join(dir, "index.html"), content);
const readFile = async (path: string) => readFileSync(join(dir, path), "utf8");
return { dir, hook: () => hook, file, save, readFile };
return { dir, history, hook: () => hook, file, save, readFile };
}
it("an edit Studio saved is undone and redone by the project's history, with the preview's before and after", async () => {
@@ -411,6 +411,30 @@ it("a key one page holds does not join another page's edit under the same key",
expect(file()).toBe("B");
});
it("a host's write claimed under claimKey joins Studio's save under the same key: one undo takes back both", async () => {
const { dir, history, hook, file, save, readFile } = await studio();
const key = "drop:1";
writeFileSync(join(dir, "clip.mp4"), "media");
await history.claim({ kind: "person", name: "You" }, "Dropped on timeline", ["clip.mp4"], {
coalesceKey: hook().claimKey(key),
idleMs: Infinity,
});
save("B");
await act(() =>
hook().recordEdit({
label: "Dropped on timeline",
coalesceKey: key,
coalesceMs: Infinity,
files: { "index.html": { before: "A", after: "B" } },
}),
);
await act(() => hook().undo({ readFile }));
expect(file()).toBe("A");
expect(existsSync(join(dir, "clip.mp4"))).toBe(false);
});
it("an undo before the history view shows a drag's held claim still waits on the files it wrote", async () => {
const { hook, save, readFile } = await studio();
const server = globalThis.fetch;
@@ -209,6 +209,7 @@ export function usePersistentEditHistory({ projectId }: UsePersistentEditHistory
projectIdRef.current = projectId;
const [own] = useState(createOwnHistory);
const [pageKeyScope] = useState(generateId);
const claimKey = useCallback((key: string) => `${pageKeyScope}:${key}`, [pageKeyScope]);
const refresh = useCallback(async () => {
if (!projectId) return;
@@ -246,7 +247,7 @@ export function usePersistentEditHistory({ projectId }: UsePersistentEditHistory
paths,
overwrote: await overwroteVersions(files),
...(coalesceKey && {
coalesceKey: `${pageKeyScope}:${coalesceKey}`,
coalesceKey: claimKey(coalesceKey),
idleMs: coalesceMs ?? DEFAULT_COALESCE_MS,
}),
});
@@ -258,7 +259,7 @@ export function usePersistentEditHistory({ projectId }: UsePersistentEditHistory
heldClaimRef.current = claimed && coalesceKey ? { paths, at: Date.now() } : null;
await refresh();
},
[projectId, refresh, own, pageKeyScope],
[projectId, refresh, own, claimKey],
);
const step = useCallback(
@@ -341,6 +342,7 @@ export function usePersistentEditHistory({ projectId }: UsePersistentEditHistory
redoPaths: view.forward?.paths ?? [],
state,
recordEdit,
claimKey,
undo,
redo,
predict: own.predict,