import type { TinyflowOptions } from '@tinyflow-ai/ui'; type TinyflowDataOption = Exclude< TinyflowOptions['data'], string | undefined >; type StructuredCloneFn = (value: T) => T; function createDataSignature(data: unknown) { if (data == null || typeof data === 'string') { return String(data ?? ''); } try { return JSON.stringify(data); } catch { return ''; } } function safeDeepClone(value: T): T { if (value === null || typeof value !== 'object') { return value; } try { const clone = (globalThis as { structuredClone?: StructuredCloneFn }) .structuredClone; if (clone) { return clone(value); } } catch { // Fall through to JSON cloning for browsers without native structuredClone. } try { return JSON.parse(JSON.stringify(value)); } catch { console.warn( 'Failed to clone object, returning original (may cause issues)', value, ); return value; } } /** * 维护 Vue 属性数据与 Tinyflow 原生数据之间的同步边界。 * * 原生画布回传的数据会被父组件再次作为属性传入。延迟到属性更新时再比较 * 两侧签名,可以阻止同一份画布状态被重新深拷贝并整图应用,同时避免在 * 每一帧提前序列化完整画布。 */ export function createTinyflowDataSync() { let lastAppliedDataSignature = ''; let lastNativeData: TinyflowOptions['data'] | null = null; return { cloneExternalDataIfChanged(data: TinyflowOptions['data']) { if (data == null || typeof data === 'string') { return null; } const signature = createDataSignature(data); if (lastNativeData != null) { const nativeSignature = createDataSignature(lastNativeData); lastNativeData = null; if (signature && signature === nativeSignature) { lastAppliedDataSignature = signature; return null; } lastAppliedDataSignature = ''; } if (signature && signature === lastAppliedDataSignature) { return null; } lastAppliedDataSignature = signature; return safeDeepClone(data as TinyflowDataOption); }, markNativeData(data: TinyflowOptions['data']) { lastNativeData = data; }, reset() { lastAppliedDataSignature = ''; lastNativeData = null; }, }; }