Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 2,656 Bytes
6277fde | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | /**
* Compute a human-readable, minimal diff between an edit's old and new values.
*
* For JSON fields (e.g. a scene's `remotion_code` descriptor) this walks both
* objects and returns only the leaf paths whose value changed β so the history
* shows "title.fontSize: 24 β 32" instead of the entire JSON blob. For plain
* scalar fields it returns a single change with an empty path.
*/
export interface LeafChange {
/** Dotted path to the changed leaf (empty for a scalar field). */
path: string;
old: string;
new: string;
}
type Json = unknown;
function tryParse(s: string | null): { ok: boolean; value: Json } {
if (s == null) return { ok: false, value: null };
const t = s.trim();
if (!(t.startsWith("{") || t.startsWith("["))) return { ok: false, value: null };
try {
return { ok: true, value: JSON.parse(t) };
} catch {
return { ok: false, value: null };
}
}
function scalarStr(v: Json): string {
if (v == null) return "β";
if (typeof v === "object") return JSON.stringify(v);
return String(v);
}
function walk(oldV: Json, newV: Json, path: string, out: LeafChange[]): void {
// Same reference/value β no change.
if (oldV === newV) return;
const bothObjects =
oldV != null && newV != null && typeof oldV === "object" && typeof newV === "object" &&
Array.isArray(oldV) === Array.isArray(newV);
if (bothObjects) {
const oldObj = oldV as Record<string, Json>;
const newObj = newV as Record<string, Json>;
const keys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
for (const k of keys) {
const childPath = path ? `${path}.${k}` : k;
walk(oldObj[k], newObj[k], childPath, out);
}
return;
}
// Leaf (or type changed) β record if the stringified values differ.
const o = scalarStr(oldV);
const n = scalarStr(newV);
if (o !== n) out.push({ path, old: o, new: n });
}
/**
* Diff a field's old/new string values. Returns the minimal set of changed leaves.
* A single non-JSON field yields one entry with `path === ""`.
*/
export function diffFieldValue(oldStr: string | null, newStr: string | null): LeafChange[] {
const a = tryParse(oldStr);
const b = tryParse(newStr);
if (a.ok && b.ok) {
const out: LeafChange[] = [];
walk(a.value, b.value, "", out);
// If the JSON changed but no leaf diff was found (rare β e.g. key reordering),
// fall back to whole-value so the change is never silently empty.
if (out.length === 0) {
return [{ path: "", old: oldStr ?? "β", new: newStr ?? "β" }];
}
return out;
}
return [{ path: "", old: oldStr ?? "β", new: newStr ?? "β" }];
}
|