MCPcopy Create free account
hub / github.com/heygen-com/hyperframes / createHistory

Function createHistory

packages/sdk/src/history.ts:44–145  ·  view source on GitHub ↗
(session: Composition, opts: HistoryOptions = {})

Source from the content-addressed store, hash-verified

42}
43
44export function createHistory(session: Composition, opts: HistoryOptions = {}): HistoryModule {
45 const coalesceMs = opts.coalesceMs ?? 300;
46 const maxEntries = opts.maxEntries ?? 100;
47 const { trackedOrigins } = opts;
48
49 const undoStack: HistoryEntry[] = [];
50 let redoStack: HistoryEntry[] = [];
51
52 function isTracked(origin: unknown): boolean {
53 if (origin === ORIGIN_APPLY_PATCHES) return false;
54 if (!trackedOrigins) return true;
55 return trackedOrigins.includes(origin);
56 }
57
58 function pathsKey(patches: readonly JsonPatchOp[]): string {
59 return patches
60 .map((p) => p.path)
61 .sort()
62 .join("\n");
63 }
64
65 function opTypesKey(opTypes: readonly string[]): string {
66 // Sorted: the same op-type SET coalesces regardless of dispatch order.
67 return [...opTypes].sort().join(",");
68 }
69
70 function shouldCoalesce(entry: HistoryEntry, incoming: PatchEvent): boolean {
71 if (coalesceMs <= 0) return false;
72 if (opTypesKey(entry.opTypes) !== opTypesKey(incoming.opTypes)) return false;
73 if (entry.origin !== incoming.origin) return false;
74 // Coalesce only when the SAME paths are touched (e.g. slider drag on one
75 // property). Without this, rapid edits to different elements would merge
76 // into one entry holding the second forward + first inverse — undo would
77 // then revert the wrong element.
78 if (pathsKey(entry.patches) !== pathsKey(incoming.patches)) return false;
79 const now = Date.now();
80 return now - entry.timestamp <= coalesceMs;
81 }
82
83 // fallow-ignore-next-line complexity
84 const unsubscribe = session.on("patch", (event: PatchEvent) => {
85 if (!isTracked(event.origin)) return;
86
87 const last = undoStack[undoStack.length - 1];
88 if (last && shouldCoalesce(last, event)) {
89 // Coalesce: keep first inverse (original prev), replace forward with latest value.
90 // Slide timestamp forward so rapid-fire edits keep coalescing.
91 const coalesced: HistoryEntry = {
92 patches: event.patches,
93 inversePatches: last.inversePatches,
94 opTypes: last.opTypes,
95 origin: last.origin,
96 timestamp: Date.now(),
97 };
98 undoStack[undoStack.length - 1] = coalesced;
99 } else {
100 undoStack.push({
101 patches: event.patches,

Callers 1

openCompositionFunction · 0.85

Calls 4

isTrackedFunction · 0.85
shouldCoalesceFunction · 0.85
nowMethod · 0.80
onMethod · 0.65

Tested by

no test coverage detected