import { useEffect, useRef, useState } from 'react' import { IconVersions } from '@tabler/icons-react' import { type TLEditorSnapshot, type TLStore, createTLStore, defaultBindingUtils, defaultShapeUtils } from 'tldraw' import { describeNewerWriter, sequencesAhead } from '@/lib/scratchpad-skew' import type { ScratchpadWriter } from '@/lib/types' export type ScratchpadFetch = { document: TLEditorSnapshot['document'] | null writer?: ScratchpadWriter } export type ScratchpadSkew = { newer: boolean writer?: ScratchpadWriter detail: string } let runtimeSchemaCache: TLStore['schema'] | null = null function runtimeSchema(): TLStore['schema'] { runtimeSchemaCache ??= createTLStore({ shapeUtils: defaultShapeUtils, bindingUtils: defaultBindingUtils }).schema return runtimeSchemaCache } export function detectScratchpadSkew( document: NonNullable, writer: ScratchpadWriter | undefined ): ScratchpadSkew | null { const schema = runtimeSchema() try { if (schema.migrateStoreSnapshot(document).type === 'success') return null } catch {} const ahead = sequencesAhead(document.schema, schema.serialize().sequences) if (ahead.length > 0) { return { newer: true, writer, detail: describeNewerWriter(writer, ahead) } } return { newer: false, writer, detail: 'The saved snapshot failed to load.' } } export function useScratchpadSnapshot(workspaceId: string): { loaded: boolean snapshot: TLEditorSnapshot['document'] | undefined skew: ScratchpadSkew | null flagSkew: (skew: ScratchpadSkew) => void } { const [loaded, setLoaded] = useState(false) const [skew, setSkew] = useState(null) const snapshot = useRef(undefined) useEffect(() => { let cancelled = false setLoaded(false) setSkew(null) snapshot.current = undefined fetch(`/api/workspaces/${workspaceId}/scratchpad`) .then(response => response.json()) .then((data: ScratchpadFetch) => { if (cancelled) return if (data?.document) { const found = detectScratchpadSkew(data.document, data.writer) if (found) setSkew(found) else snapshot.current = data.document } setLoaded(true) }) .catch(() => { if (!cancelled) setLoaded(true) }) return () => { cancelled = true } }, [workspaceId]) return { loaded, snapshot: snapshot.current, skew, flagSkew: setSkew } } export function ScratchpadSkewNotice({ skew }: { skew: ScratchpadSkew }) { return (

{skew.newer ? 'This canvas needs a newer moi' : 'This canvas couldn’t be loaded'}

{skew.newer ? ( <>

This scratchpad was saved by a newer version of moi {skew.writer ? ` (v${skew.writer.moi})` : ''}.
Please update moi and restart the server:

bun install -g moi-computer@latest ) : (

{skew.detail}

)}
) }