@templatical/core 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cloud/index.d.ts +3 -100
- package/dist/cloud/index.js +1 -627
- package/dist/cloud/index.js.map +1 -1
- package/dist/index.d.ts +32 -21
- package/dist/index.js +125 -189
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["columnsOf","ref","computed","DEFAULT_KEY","ref","computed","ref","computed"],"sources":["../src/error-reporting.ts","../src/editor.ts","../src/history.ts","../src/block-actions.ts","../src/auto-save.ts","../src/condition-preview.ts","../src/data-source-fetch.ts","../src/history-interceptor.ts","../src/diff-content.ts","../src/invert-operations.ts","../src/collaboration.ts","../src/saved-blocks.ts","../src/saved-blocks-local.ts","../src/media-local.ts","../src/version-history.ts","../src/comments.ts"],"sourcesContent":["/**\n * Turn a thrown value into an `Error`.\n *\n * A `catch` clause's binding is `unknown` — a provider or a consumer's event\n * handler can throw a string, an object, or anything else. `onError` callbacks\n * across this package are typed to take an `Error`, so this is what makes that\n * contract honest at the one place values actually cross it.\n */\nexport function wrapReportedError(error: unknown): Error {\n return error instanceof Error\n ? error\n : new Error(String(error), { cause: error });\n}\n\n/**\n * Run a consumer's event handler without letting it fail the operation it\n * followed.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the caller has already succeeded by the time this runs. `onError` is taken\n * as a parameter rather than read from a shared reporter, so every feature\n * module keeps its own `options.onError` wiring and this stays reusable by\n * any module with the same fire-and-forget event shape.\n */\nexport function notifyHandler(\n onError: ((error: Error) => void) | undefined,\n run: () => void,\n): void {\n try {\n run();\n } catch (error) {\n onError?.(wrapReportedError(error));\n }\n}\n","import type {\n Block,\n Template,\n TemplateContent,\n TemplateDefaults,\n TemplatePatch,\n TemplateSaveTrigger,\n TemplateSettings,\n TemplatesProvider,\n UiTheme,\n ViewportSize,\n} from \"@templatical/types\";\nimport {\n assertNoSlotInContent,\n assertNoWrapperInContent,\n createDefaultTemplateContent,\n getColumnCount,\n SdkError,\n} from \"@templatical/types\";\nimport { notifyHandler } from \"./error-reporting\";\nimport {\n computed,\n reactive,\n readonly,\n type DeepReadonly,\n type Ref,\n} from \"@vue/reactivity\";\n\nexport interface EditorState {\n /**\n * The template currently being edited, as the store last returned it — `null`\n * until `create()` or `load()` resolves, and always `null` without a\n * {@link TemplatesProvider}.\n *\n * Carries identity and name only for practical purposes: its `content` is the\n * store's copy from the last round-trip, whereas `state.content` is what the\n * user is editing.\n */\n template: Template | null;\n content: TemplateContent;\n selectedBlockId: string | null;\n viewport: ViewportSize;\n darkMode: boolean;\n previewMode: boolean;\n isDirty: boolean;\n /** True for the duration of a `save()` call. */\n isSaving: boolean;\n /** True for the duration of a `create()` or `load()` call. */\n isLoading: boolean;\n uiTheme: UiTheme;\n}\n\nexport interface UseEditorOptions {\n content: TemplateContent;\n defaultFontFamily?: string;\n templateDefaults?: TemplateDefaults;\n /**\n * Blocks other collaborators hold. A locked block can't be selected, and the\n * editor's own mutators leave it alone.\n */\n lockedBlocks?: Readonly<Ref<ReadonlyMap<string, unknown>>>;\n /**\n * Storage backend for the template's save/load lifecycle. Omit it and\n * `create()` / `load()` / `save()` reject — the editor keeps working as a\n * purely local editing surface.\n */\n templates?: TemplatesProvider;\n /**\n * Called with any error a provider call rejects with, before the rejection is\n * re-thrown to the caller.\n */\n onError?: (error: Error) => void;\n}\n\nexport interface UseEditorReturn {\n state: DeepReadonly<EditorState>;\n content: Ref<TemplateContent>;\n selectedBlock: Ref<Block | null>;\n setContent: (content: TemplateContent, markDirty?: boolean) => void;\n selectBlock: (blockId: string | null) => void;\n setViewport: (viewport: ViewportSize) => void;\n setDarkMode: (darkMode: boolean) => void;\n setPreviewMode: (previewMode: boolean) => void;\n setUiTheme: (theme: UiTheme) => void;\n updateBlock: (blockId: string, updates: Partial<Block>) => void;\n updateSettings: (updates: Partial<TemplateSettings>) => void;\n addBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n index?: number,\n ) => void;\n removeBlock: (blockId: string) => void;\n moveBlock: (\n blockId: string,\n newIndex: number,\n targetSectionId?: string,\n columnIndex?: number,\n ) => void;\n isBlockLocked: (blockId: string) => boolean;\n markDirty: () => void;\n findBlockLocation: (blockId: string) => {\n targetSectionId?: string;\n columnIndex?: number;\n index: number;\n } | null;\n /**\n * Rename the loaded template locally and mark the editor dirty. The new name\n * reaches the store on the next `save()`, in the same patch as the content —\n * a rename is an ordinary unsaved change, not a side channel.\n *\n * This method only stages it. The editor's inline rename field commits by\n * calling `setName()` and then saving immediately, because a rename reads as a\n * discrete action rather than an edit to be batched — so in the editor the\n * \"next `save()`\" is usually the one it triggers itself. A headless caller\n * decides its own moment.\n *\n * No-op when no template is loaded: there is nothing to name.\n */\n setName: (name: string) => void;\n /**\n * Persist the current content as a new template. `input.content`, when given,\n * replaces the editor's content first — so `create({ content })` both loads\n * and stores in one step.\n */\n create: (input?: {\n name?: string;\n content?: TemplateContent;\n }) => Promise<Template>;\n /** Fetch a template and make it the editor's content. */\n load: (templateId: string) => Promise<Template>;\n /**\n * Persist the loaded template's name + content as a patch.\n *\n * `trigger` is passed through to `TemplatesOptions.onSaved`. It defaults to\n * `\"api\"`, which is the honest answer for a direct call — the editor package\n * passes the value matching whichever affordance fired.\n */\n save: (trigger?: TemplateSaveTrigger) => Promise<Template>;\n /** Whether a template has been created or loaded. */\n hasTemplate: () => boolean;\n}\n\nexport function useEditor(options: UseEditorOptions): UseEditorReturn {\n const seedContent =\n options.content ??\n createDefaultTemplateContent(\n options.defaultFontFamily,\n options.templateDefaults,\n );\n // Layout-only; authored content must not contain slot or wrapper.\n assertNoSlotInContent(seedContent);\n assertNoWrapperInContent(seedContent);\n\n const state = reactive<EditorState>({\n template: null,\n content: seedContent,\n selectedBlockId: null,\n viewport: \"desktop\",\n darkMode: false,\n previewMode: false,\n isDirty: false,\n isSaving: false,\n isLoading: false,\n uiTheme: \"auto\",\n });\n\n /**\n * Bumped by every content mutation. `save()` captures it before awaiting the\n * provider and only clears `isDirty` if it is unchanged afterwards, so an edit\n * made while a save is in flight is not reported as persisted — which would\n * also make autosave skip it, since that decides dirtiness at debounce time.\n */\n let revision = 0;\n\n function touch(): void {\n state.isDirty = true;\n revision++;\n }\n\n const content = computed({\n get: () => state.content,\n set: (value: TemplateContent) => {\n state.content = value;\n touch();\n },\n });\n\n const selectedBlock = computed(() => {\n if (!state.selectedBlockId) return null;\n return findBlockById(state.content.blocks, state.selectedBlockId);\n });\n\n function findBlockById(blocks: Block[], id: string): Block | null {\n for (const block of blocks) {\n if (block.id === id) return block;\n if (block.type === \"section\") {\n for (const column of block.children) {\n const found = findBlockById(column, id);\n if (found) return found;\n }\n }\n }\n return null;\n }\n\n function collectBlockIds(block: Block, ids: Set<string>): void {\n ids.add(block.id);\n if (block.type === \"section\") {\n for (const column of block.children) {\n for (const child of column) {\n collectBlockIds(child, ids);\n }\n }\n }\n }\n\n function findBlockParent(\n blocks: Block[],\n id: string,\n parent: {\n blocks: Block[];\n sectionId?: string;\n columnIndex?: number;\n } = { blocks },\n ): { blocks: Block[]; sectionId?: string; columnIndex?: number } | null {\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n if (block.id === id) return parent;\n if (block.type === \"section\") {\n for (let colIdx = 0; colIdx < block.children.length; colIdx++) {\n const result = findBlockParent(block.children[colIdx], id, {\n blocks: block.children[colIdx],\n sectionId: block.id,\n columnIndex: colIdx,\n });\n if (result) return result;\n }\n }\n }\n return null;\n }\n\n function isBlockLocked(blockId: string): boolean {\n return options.lockedBlocks?.value.has(blockId) ?? false;\n }\n\n function findBlockLocation(blockId: string): {\n targetSectionId?: string;\n columnIndex?: number;\n index: number;\n } | null {\n const parent = findBlockParent(state.content.blocks, blockId);\n if (!parent) return null;\n const index = parent.blocks.findIndex((b) => b.id === blockId);\n if (index === -1) return null;\n return {\n targetSectionId: parent.sectionId,\n columnIndex: parent.columnIndex,\n index,\n };\n }\n\n // TODO(collab): the lock checks in addBlock/moveBlock/removeBlock/updateBlock\n // are shallow — they only consider the directly-targeted block id. A section\n // can still be removed, moved, or have its `children` array rewritten while a\n // peer is editing one of its descendants, which silently disrupts that peer's\n // edit. Add a `hasLockedDescendant(blockId)` helper and gate section-level\n // operations on it (and on the parent of each affected child) so cascades\n // through the tree are also blocked.\n\n function setContent(newContent: TemplateContent, markDirty = true): void {\n assertNoSlotInContent(newContent);\n assertNoWrapperInContent(newContent);\n state.content = newContent;\n if (markDirty) {\n touch();\n }\n }\n\n function selectBlock(blockId: string | null): void {\n if (blockId && isBlockLocked(blockId)) {\n return;\n }\n state.selectedBlockId = blockId;\n }\n\n function setViewport(viewport: ViewportSize): void {\n state.viewport = viewport;\n }\n\n function setDarkMode(darkMode: boolean): void {\n state.darkMode = darkMode;\n }\n\n function setUiTheme(theme: UiTheme): void {\n state.uiTheme = theme;\n }\n\n function setPreviewMode(previewMode: boolean): void {\n state.previewMode = previewMode;\n if (previewMode) {\n state.selectedBlockId = null;\n }\n }\n\n function updateBlock(blockId: string, updates: Partial<Block>): void {\n if (isBlockLocked(blockId)) {\n return;\n }\n const block = findBlockById(state.content.blocks, blockId);\n if (block) {\n Object.assign(block, updates);\n touch();\n }\n }\n\n function updateSettings(updates: Partial<TemplateSettings>): void {\n state.content.settings = { ...state.content.settings, ...updates };\n touch();\n }\n\n function addBlock(\n block: Block,\n targetSectionId?: string,\n columnIndex = 0,\n index?: number,\n ): void {\n const candidate = {\n settings: state.content.settings,\n blocks: [block],\n };\n assertNoSlotInContent(candidate);\n assertNoWrapperInContent(candidate);\n // Sections cannot be nested inside a column — MJML forbids `mj-section`\n // inside `mj-column`, so the renderer drops them on export (issue #292).\n // Reject the nest up front rather than lose the content silently later.\n if (targetSectionId && block.type === \"section\") {\n return;\n }\n if (targetSectionId) {\n if (isBlockLocked(targetSectionId)) {\n return;\n }\n const section = findBlockById(state.content.blocks, targetSectionId);\n if (section && section.type === \"section\") {\n if (columnIndex < 0 || columnIndex >= getColumnCount(section.columns)) {\n return;\n }\n section.children[columnIndex] = section.children[columnIndex] || [];\n const targetArray = section.children[columnIndex];\n if (index !== undefined && index < targetArray.length) {\n targetArray.splice(index, 0, block);\n } else {\n targetArray.push(block);\n }\n }\n } else {\n if (index !== undefined && index < state.content.blocks.length) {\n state.content.blocks.splice(index, 0, block);\n } else {\n state.content.blocks.push(block);\n }\n }\n touch();\n }\n\n function removeBlock(blockId: string): void {\n if (isBlockLocked(blockId)) {\n return;\n }\n const parent = findBlockParent(state.content.blocks, blockId);\n if (parent) {\n const index = parent.blocks.findIndex((b) => b.id === blockId);\n if (index !== -1) {\n const [removed] = parent.blocks.splice(index, 1);\n if (state.selectedBlockId) {\n const removedIds = new Set<string>();\n collectBlockIds(removed, removedIds);\n if (removedIds.has(state.selectedBlockId)) {\n state.selectedBlockId = null;\n }\n }\n touch();\n }\n }\n }\n\n function moveBlock(\n blockId: string,\n newIndex: number,\n targetSectionId?: string,\n columnIndex = 0,\n ): void {\n if (isBlockLocked(blockId)) {\n return;\n }\n if (targetSectionId && isBlockLocked(targetSectionId)) {\n return;\n }\n\n const parent = findBlockParent(state.content.blocks, blockId);\n if (!parent) return;\n\n const oldIndex = parent.blocks.findIndex((b) => b.id === blockId);\n if (oldIndex === -1) return;\n\n // Sections cannot be nested inside a column (issue #292) — refuse to move\n // a section into a section column; MJML would drop it on export.\n if (targetSectionId && parent.blocks[oldIndex].type === \"section\") {\n return;\n }\n\n // Resolve target before mutating the source — otherwise an invalid\n // targetSectionId leaves the block spliced-out and unrecoverable.\n let targetArray: Block[];\n if (targetSectionId) {\n const section = findBlockById(state.content.blocks, targetSectionId);\n if (!section || section.type !== \"section\") return;\n if (columnIndex < 0 || columnIndex >= getColumnCount(section.columns)) {\n return;\n }\n section.children[columnIndex] = section.children[columnIndex] || [];\n targetArray = section.children[columnIndex];\n } else {\n targetArray = state.content.blocks;\n }\n\n const [block] = parent.blocks.splice(oldIndex, 1);\n targetArray.splice(newIndex, 0, block);\n\n touch();\n }\n\n function markDirty(): void {\n touch();\n }\n\n // -------------------------------------------------------------------------\n // Template lifecycle (over an optional TemplatesProvider)\n // -------------------------------------------------------------------------\n\n /**\n * A refused call throws rather than resolving, because a resolved promise\n * would read as \"saved\" to whoever awaited it.\n *\n * The message names the provider key the consumer set, not an internal\n * capability flag: neither `EditorCapabilities` nor its injection key is\n * exported, so neither a core nor an editor consumer can read one. Note the\n * editor hides its own save controls when `save` is withheld but has no\n * create affordance to hide at all, so `create()` is reachable by definition.\n */\n function refuse(action: string, reason: string): never {\n throw new SdkError(`[Templatical] Templates: ${action} ${reason}`);\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for something that landed.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n function requireProvider(action: string): TemplatesProvider {\n const { templates } = options;\n if (!templates) {\n refuse(\n action,\n \"needs a templates provider. Pass one as `init({ templates })` to enable saving and loading.\",\n );\n }\n return templates;\n }\n\n function setName(name: string): void {\n if (!state.template) return;\n state.template = { ...state.template, name };\n touch();\n }\n\n async function create(input?: {\n name?: string;\n content?: TemplateContent;\n }): Promise<Template> {\n const provider = requireProvider(\"create()\");\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") {\n refuse(\n \"create()\",\n \"is disabled by the provider — its `create` is `false`.\",\n );\n }\n\n let created: Template;\n state.isLoading = true;\n try {\n if (input?.content) {\n state.content = input.content;\n }\n // Omit `name` entirely when unset rather than sending `undefined` — a\n // provider serialising the input to JSON would otherwise receive a key it\n // never asked for.\n const revisionAtRequest = revision;\n created = await providerCreate(\n input?.name !== undefined\n ? { name: input.name, content: state.content }\n : { content: state.content },\n );\n state.template = created;\n if (revision === revisionAtRequest) {\n state.isDirty = false;\n }\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n state.isLoading = false;\n }\n // Below the `finally` on purpose: `isLoading` is cleared there, so a handler\n // must not observe the editor still loading. Unreachable on failure — the\n // catch rethrows.\n notify(() => provider.onCreated?.(created));\n return created;\n }\n\n async function load(templateId: string): Promise<Template> {\n const provider = requireProvider(\"load()\");\n\n let loaded: Template;\n state.isLoading = true;\n try {\n loaded = await provider.load(templateId);\n assertNoSlotInContent(loaded.content);\n assertNoWrapperInContent(loaded.content);\n state.template = loaded;\n state.content = loaded.content;\n state.isDirty = false;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n state.isLoading = false;\n }\n // Below the `finally` on purpose: `isLoading` is cleared there, so a handler\n // must not observe the editor still loading. Unreachable on failure — the\n // catch rethrows.\n notify(() => provider.onLoaded?.(loaded));\n return loaded;\n }\n\n async function save(trigger: TemplateSaveTrigger = \"api\"): Promise<Template> {\n const provider = requireProvider(\"save()\");\n const { save: providerSave } = provider;\n if (typeof providerSave !== \"function\") {\n refuse(\"save()\", \"is disabled by the provider — its `save` is `false`.\");\n }\n const current = state.template;\n if (!current) {\n refuse(\n \"save()\",\n \"has no template loaded. Call create() or load() first.\",\n );\n }\n\n // The name rides the same patch as the content: a rename is an ordinary\n // unsaved change, so one round-trip persists both.\n const patch: TemplatePatch =\n current.name !== undefined\n ? { name: current.name, content: state.content }\n : { content: state.content };\n\n let saved: Template;\n state.isSaving = true;\n try {\n const revisionAtRequest = revision;\n saved = await providerSave(current.id, patch);\n // Adopt the response only while it still describes what is open. A\n // `load()` that landed mid-flight has already replaced the template, so\n // the response is stale.\n if (state.template?.id === current.id) {\n // The patch was built before the await, so a rename that landed during\n // it is absent from the response. Taking the response's name verbatim\n // reverts what the user typed — and because the next save reads the\n // name back off `state.template`, it would then persist the stale one,\n // losing the rename outright rather than merely on screen.\n const localName = state.template.name;\n state.template =\n localName !== current.name ? { ...saved, name: localName } : saved;\n }\n // Deliberately conditional. An edit landing while the request was in\n // flight is not persisted by it, and clearing the flag would both claim it\n // was and make autosave — which checks dirtiness at debounce time — skip\n // the follow-up save.\n if (revision === revisionAtRequest) {\n state.isDirty = false;\n }\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n state.isSaving = false;\n }\n // Below the `finally` on purpose: `isSaving` is cleared there, and a handler\n // that navigates must observe a settled editor or the consumer's own dirty\n // guard blocks their navigation. Unreachable on failure — the catch rethrows.\n notify(() => provider.onSaved?.(saved, { trigger }));\n return saved;\n }\n\n function hasTemplate(): boolean {\n return state.template?.id !== undefined;\n }\n\n return {\n state: readonly(state),\n content,\n selectedBlock,\n isBlockLocked,\n setContent,\n selectBlock,\n setViewport,\n setDarkMode,\n setUiTheme,\n setPreviewMode,\n updateBlock,\n updateSettings,\n addBlock,\n removeBlock,\n moveBlock,\n markDirty,\n findBlockLocation,\n setName,\n create,\n load,\n save,\n hasTemplate,\n };\n}\n","import { safeClone, type TemplateContent } from \"@templatical/types\";\nimport { computed, ref, type ComputedRef, type Ref } from \"@vue/reactivity\";\n\nexport interface UseHistoryOptions {\n content: Ref<TemplateContent>;\n setContent: (content: TemplateContent, markDirty?: boolean) => void;\n maxSize?: number;\n}\n\nexport interface UseHistoryReturn {\n canUndo: ComputedRef<boolean>;\n canRedo: ComputedRef<boolean>;\n isNavigating: Ref<boolean>;\n undo: () => void;\n redo: () => void;\n record: () => void;\n recordDebounced: (blockId: string) => void;\n clear: () => void;\n destroy: () => void;\n}\n\ninterface DebouncedSnapshot {\n blockId: string;\n timeoutId: ReturnType<typeof setTimeout>;\n}\n\nconst MAX_STACK_SIZE = 50;\nconst DEBOUNCE_MS = 300;\nconst NAVIGATE_IDLE_MS = 1500;\n\nexport function useHistory(options: UseHistoryOptions): UseHistoryReturn {\n const { content, setContent, maxSize = MAX_STACK_SIZE } = options;\n\n const undoStack = ref<TemplateContent[]>([]);\n const redoStack = ref<TemplateContent[]>([]);\n const isNavigating = ref(false);\n let navigatingTimeoutId: ReturnType<typeof setTimeout> | null = null;\n let pendingDebounce: DebouncedSnapshot | null = null;\n\n const canUndo = computed(() => undoStack.value.length > 0);\n const canRedo = computed(() => redoStack.value.length > 0);\n\n function cloneContent(): TemplateContent {\n // Cycle-safe deep clone (see `safeClone`). A naked JSON.stringify throws\n // `Converting circular structure to JSON` if a DOM element with a Sortable\n // expando back-ref ever sneaks into block data via a drag handler — which\n // would freeze the undo stack, since every subsequent mutation calls\n // `record()`. safeClone drops the back-ref instead of throwing.\n return safeClone(content.value);\n }\n\n function pushToUndoStack(snapshot: TemplateContent): void {\n undoStack.value.push(snapshot);\n if (undoStack.value.length > maxSize) {\n undoStack.value.splice(0, undoStack.value.length - maxSize);\n }\n }\n\n function flushPendingDebounce(): void {\n if (pendingDebounce) {\n clearTimeout(pendingDebounce.timeoutId);\n pendingDebounce = null;\n }\n }\n\n function record(): void {\n flushPendingDebounce();\n pushToUndoStack(cloneContent());\n redoStack.value = [];\n }\n\n function recordDebounced(blockId: string): void {\n if (pendingDebounce && pendingDebounce.blockId === blockId) {\n clearTimeout(pendingDebounce.timeoutId);\n pendingDebounce.timeoutId = setTimeout(() => {\n pendingDebounce = null;\n }, DEBOUNCE_MS);\n return;\n }\n\n flushPendingDebounce();\n\n pushToUndoStack(cloneContent());\n redoStack.value = [];\n\n pendingDebounce = {\n blockId,\n timeoutId: setTimeout(() => {\n pendingDebounce = null;\n }, DEBOUNCE_MS),\n };\n }\n\n function setNavigating(): void {\n isNavigating.value = true;\n if (navigatingTimeoutId) {\n clearTimeout(navigatingTimeoutId);\n }\n navigatingTimeoutId = setTimeout(() => {\n isNavigating.value = false;\n navigatingTimeoutId = null;\n }, NAVIGATE_IDLE_MS);\n }\n\n function undo(): void {\n if (undoStack.value.length === 0) {\n return;\n }\n\n flushPendingDebounce();\n\n const snapshot = undoStack.value.pop()!;\n redoStack.value.push(cloneContent());\n setContent(snapshot, true);\n setNavigating();\n }\n\n function redo(): void {\n if (redoStack.value.length === 0) {\n return;\n }\n\n flushPendingDebounce();\n\n const snapshot = redoStack.value.pop()!;\n undoStack.value.push(cloneContent());\n setContent(snapshot, true);\n setNavigating();\n }\n\n function clear(): void {\n undoStack.value = [];\n redoStack.value = [];\n flushPendingDebounce();\n }\n\n function destroy(): void {\n clear();\n if (navigatingTimeoutId) {\n clearTimeout(navigatingTimeoutId);\n navigatingTimeoutId = null;\n }\n }\n\n return {\n canUndo,\n canRedo,\n isNavigating,\n undo,\n redo,\n record,\n recordDebounced,\n clear,\n destroy,\n };\n}\n","import type { Block, BlockDefaults, BlockType } from \"@templatical/types\";\nimport { cloneBlock, createBlock } from \"@templatical/types\";\n\nexport interface UseBlockActionsOptions {\n addBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n index?: number,\n ) => void;\n removeBlock: (blockId: string) => void;\n updateBlock: (blockId: string, updates: Partial<Block>) => void;\n selectBlock: (blockId: string | null) => void;\n /** Locate a block in the tree — used by `duplicateBlock` to insert the\n * clone right after the source instead of appending to the end. */\n findBlockLocation?: (blockId: string) => {\n targetSectionId?: string;\n columnIndex?: number;\n index: number;\n } | null;\n /**\n * Defaults merged onto every block `createAndAddBlock` creates.\n *\n * A **getter** is re-read on each insert; a plain object is captured once.\n * The editor passes a getter because half of these defaults track the\n * template's own `settings.locale`, which the author can change mid-session\n * — a snapshot would pin every later insert to the value at mount.\n */\n blockDefaults?: BlockDefaults | (() => BlockDefaults);\n}\n\nexport interface UseBlockActionsReturn {\n createAndAddBlock: (\n type: BlockType,\n targetSectionId?: string,\n columnIndex?: number,\n ) => Block;\n duplicateBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n ) => Block;\n deleteBlock: (blockId: string) => void;\n updateBlockProperty: <K extends keyof Block>(\n blockId: string,\n key: K,\n value: Block[K],\n ) => void;\n}\n\nexport function useBlockActions(\n options: UseBlockActionsOptions,\n): UseBlockActionsReturn {\n const { addBlock, removeBlock, updateBlock, selectBlock, findBlockLocation } =\n options;\n\n function createAndAddBlock(\n type: BlockType,\n targetSectionId?: string,\n columnIndex?: number,\n ): Block {\n const defaults =\n typeof options.blockDefaults === \"function\"\n ? options.blockDefaults()\n : options.blockDefaults;\n const block = createBlock(type, defaults);\n addBlock(block, targetSectionId, columnIndex);\n selectBlock(block.id);\n return block;\n }\n\n function duplicateBlock(\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n ): Block {\n const cloned = cloneBlock(block);\n\n // Insert directly after the source block. Explicit target args win;\n // otherwise, resolve the source's location and bump index by 1. Falls\n // back to appending at the end if location is unknown.\n if (targetSectionId !== undefined || columnIndex !== undefined) {\n addBlock(cloned, targetSectionId, columnIndex);\n } else {\n const sourceLocation = findBlockLocation?.(block.id) ?? null;\n if (sourceLocation) {\n addBlock(\n cloned,\n sourceLocation.targetSectionId,\n sourceLocation.columnIndex,\n sourceLocation.index + 1,\n );\n } else {\n addBlock(cloned, targetSectionId, columnIndex);\n }\n }\n selectBlock(cloned.id);\n return cloned;\n }\n\n function deleteBlock(blockId: string): void {\n removeBlock(blockId);\n }\n\n function updateBlockProperty<K extends keyof Block>(\n blockId: string,\n key: K,\n value: Block[K],\n ): void {\n updateBlock(blockId, { [key]: value } as Partial<Block>);\n }\n\n return {\n createAndAddBlock,\n duplicateBlock,\n deleteBlock,\n updateBlockProperty,\n };\n}\n","import type { TemplateContent } from \"@templatical/types\";\nimport { watch, type Ref } from \"@vue/reactivity\";\n\nexport interface UseAutoSaveOptions {\n content: Ref<TemplateContent>;\n isDirty: () => boolean;\n onChange: (content: TemplateContent) => void;\n debounce?: number;\n enabled?: boolean | (() => boolean);\n}\n\nexport interface UseAutoSaveReturn {\n flush: () => void;\n cancel: () => void;\n pause: () => void;\n resume: () => void;\n destroy: () => void;\n}\n\n/**\n * Trailing debounce, in ms, measured from the *last* content mutation.\n *\n * Typing is not debounced upstream — TipTap's `onUpdate` calls `updateBlock` per\n * keystroke — so this is the only thing between a keypress and a whole-document\n * write. 1000 was too eager: ordinary prose pauses for a second constantly\n * (word choice, re-reading, reaching for the mouse), so a single paragraph could\n * produce dozens of full-content saves. 2000 roughly halves that while still\n * landing well before a user wonders whether their work was kept.\n *\n * **The single default for both entry points.** Cloud imports this constant\n * rather than declaring its own — a second copy for one setting drifts silently,\n * since nothing links the two.\n */\nexport const DEFAULT_AUTO_SAVE_DEBOUNCE_MS = 2000;\n\nexport function useAutoSave(options: UseAutoSaveOptions): UseAutoSaveReturn {\n const {\n content,\n isDirty,\n onChange,\n debounce = DEFAULT_AUTO_SAVE_DEBOUNCE_MS,\n enabled = true,\n } = options;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let paused = false;\n\n function isEnabled(): boolean {\n return typeof enabled === \"function\" ? enabled() : enabled;\n }\n\n function pause(): void {\n paused = true;\n cancel();\n }\n\n function resume(): void {\n paused = false;\n }\n\n function cancel(): void {\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n }\n\n function flush(): void {\n cancel();\n if (isDirty()) {\n onChange(JSON.parse(JSON.stringify(content.value)));\n }\n }\n\n function scheduleOnChange(): void {\n if (!isEnabled() || paused) return;\n\n cancel();\n timeoutId = setTimeout(() => {\n timeoutId = null;\n if (isEnabled() && !paused && isDirty()) {\n onChange(JSON.parse(JSON.stringify(content.value)));\n }\n }, debounce);\n }\n\n // `watch` from `@vue/reactivity` has no scheduler, so this callback runs\n // *synchronously inside the mutation* — before the editor's trailing\n // `state.isDirty = true`, and `isDirty` sits outside the watched `content`\n // subtree so setting it never re-triggers. An `isDirty()` guard here would\n // therefore drop the first edit after every dirty-flag reset (issue #522).\n // Dirtiness is decided at debounce time instead, where the flag is settled;\n // `scheduleOnChange` still checks `enabled`/`paused` up front.\n const stopWatch = watch(\n content,\n () => {\n scheduleOnChange();\n },\n { deep: true },\n );\n\n function destroy(): void {\n stopWatch();\n cancel();\n }\n\n return {\n flush,\n cancel,\n pause,\n resume,\n destroy,\n };\n}\n","import { computed, reactive, type ComputedRef } from \"@vue/reactivity\";\n\nexport interface UseConditionPreviewReturn {\n isHidden: (blockId: string) => boolean;\n toggleBlock: (blockId: string) => void;\n reset: () => void;\n hasHiddenBlocks: ComputedRef<boolean>;\n}\n\n/**\n * The slice of an editor this needs — deliberately structural, so both the OSS\n * and the Cloud `useEditor` satisfy it without either having to grow toward the\n * other.\n */\nexport interface ConditionPreviewEditor {\n state: { readonly selectedBlockId: string | null };\n selectBlock: (blockId: string | null) => void;\n}\n\nexport function useConditionPreview(\n editor: ConditionPreviewEditor,\n): UseConditionPreviewReturn {\n const hiddenBlockIds = reactive(new Set<string>());\n\n const hasHiddenBlocks = computed(() => hiddenBlockIds.size > 0);\n\n function isHidden(blockId: string): boolean {\n return hiddenBlockIds.has(blockId);\n }\n\n function toggleBlock(blockId: string): void {\n if (hiddenBlockIds.has(blockId)) {\n hiddenBlockIds.delete(blockId);\n } else {\n hiddenBlockIds.add(blockId);\n\n if (editor.state.selectedBlockId === blockId) {\n editor.selectBlock(null);\n }\n }\n }\n\n function reset(): void {\n hiddenBlockIds.clear();\n }\n\n return {\n isHidden,\n toggleBlock,\n reset,\n hasHiddenBlocks,\n };\n}\n","import type { CustomBlock, CustomBlockDefinition } from \"@templatical/types\";\nimport type { ComputedRef, Ref } from \"@vue/reactivity\";\nimport { computed, ref } from \"@vue/reactivity\";\n\nexport function useDataSourceFetch(options: {\n definition: ComputedRef<CustomBlockDefinition | undefined>;\n block: ComputedRef<CustomBlock>;\n onUpdate: (fieldValues: Record<string, unknown>, fetched: boolean) => void;\n}): {\n isFetching: Ref<boolean>;\n fetchError: Ref<boolean>;\n fetch: () => Promise<void>;\n hasDataSource: ComputedRef<boolean>;\n needsFetch: ComputedRef<boolean>;\n} {\n const isFetching = ref(false);\n const fetchError = ref(false);\n\n const hasDataSource = computed(() => !!options.definition.value?.dataSource);\n\n const needsFetch = computed(\n () => hasDataSource.value && !options.block.value.dataSourceFetched,\n );\n\n async function fetch(): Promise<void> {\n const def = options.definition.value;\n if (!def?.dataSource) {\n return;\n }\n\n isFetching.value = true;\n fetchError.value = false;\n\n try {\n const result = await def.dataSource.onFetch({\n fieldValues: { ...options.block.value.fieldValues },\n blockId: options.block.value.id,\n });\n\n if (result == null) {\n return;\n }\n\n const merged = { ...options.block.value.fieldValues };\n for (const key of Object.keys(merged)) {\n if (key in result) {\n merged[key] = result[key];\n }\n }\n\n options.onUpdate(merged, true);\n } catch (error) {\n console.warn(\"[Templatical] Data source fetch error:\", error);\n fetchError.value = true;\n } finally {\n isFetching.value = false;\n }\n }\n\n return {\n isFetching,\n fetchError,\n fetch,\n hasDataSource,\n needsFetch,\n };\n}\n","import type { Block, TemplateSettings } from \"@templatical/types\";\nimport type { UseHistoryReturn } from \"./history\";\n\n/**\n * The mutable slice of an editor this wraps — structural, so the OSS and Cloud\n * `useEditor` returns both satisfy it without either having to grow toward the\n * other. Not `Readonly`: the whole point is to replace these members in place.\n */\nexport interface HistoryInterceptorEditor {\n addBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n index?: number,\n ) => void;\n removeBlock: (blockId: string) => void;\n moveBlock: (\n blockId: string,\n newIndex: number,\n targetSectionId?: string,\n columnIndex?: number,\n ) => void;\n updateBlock: (blockId: string, updates: Partial<Block>) => void;\n updateSettings: (updates: Partial<TemplateSettings>) => void;\n isBlockLocked: (blockId: string) => boolean;\n}\n\n/**\n * Wraps editor mutation methods to record history snapshots before each\n * operation. Mutates the editor object in place.\n *\n * Not installed with a collaboration provider: its controller captures\n * changes by diffing the content and keeps its own undo history.\n */\nexport function useHistoryInterceptor(\n editor: HistoryInterceptorEditor,\n history: UseHistoryReturn,\n): void {\n const originalAddBlock = editor.addBlock;\n const originalRemoveBlock = editor.removeBlock;\n const originalMoveBlock = editor.moveBlock;\n const originalUpdateBlock = editor.updateBlock;\n const originalUpdateSettings = editor.updateSettings;\n\n // Skip recording when the underlying op is a no-op (e.g., a peer-locked\n // block or section), otherwise the undo stack fills with snapshots that\n // are identical to current state and undo silently does nothing.\n editor.addBlock = (block, targetSectionId?, columnIndex?, index?) => {\n if (targetSectionId && editor.isBlockLocked(targetSectionId)) {\n return;\n }\n history.record();\n originalAddBlock(block, targetSectionId, columnIndex, index);\n };\n\n editor.removeBlock = (blockId) => {\n if (editor.isBlockLocked(blockId)) {\n return;\n }\n history.record();\n originalRemoveBlock(blockId);\n };\n\n editor.moveBlock = (blockId, newIndex, targetSectionId?, columnIndex?) => {\n if (editor.isBlockLocked(blockId)) {\n return;\n }\n if (targetSectionId && editor.isBlockLocked(targetSectionId)) {\n return;\n }\n history.record();\n originalMoveBlock(blockId, newIndex, targetSectionId, columnIndex);\n };\n\n editor.updateBlock = (blockId, updates) => {\n if (editor.isBlockLocked(blockId)) {\n return;\n }\n history.recordDebounced(blockId);\n originalUpdateBlock(blockId, updates);\n };\n\n editor.updateSettings = (updates) => {\n history.record();\n originalUpdateSettings(updates);\n };\n}\n","import type {\n Block,\n BlockParent,\n Operation,\n SectionBlock,\n TemplateContent,\n} from \"@templatical/types\";\nimport { deepEqual, safeClone } from \"@templatical/types\";\n\n/** Where a block sits, with its container as a comparable key. */\ninterface Placement {\n block: Block;\n key: string;\n index: number;\n}\n\ntype Json = Record<string, unknown>;\n\nconst ROOT_KEY = \"root\";\nconst BLOCK_SKIP: ReadonlySet<string> = new Set([\"id\", \"type\", \"children\"]);\nconst RESIZED_SECTION_SKIP: ReadonlySet<string> = new Set([\n \"id\",\n \"type\",\n \"children\",\n \"columns\",\n]);\nconst NO_SKIP: ReadonlySet<string> = new Set();\n\nfunction containerKey(parent: BlockParent): string {\n return parent === null\n ? ROOT_KEY\n : JSON.stringify([parent.sectionId, parent.column]);\n}\n\nfunction columnsOf(block: Block): Block[][] {\n const children = (block as { children?: unknown }).children;\n return block.type === \"section\" && Array.isArray(children)\n ? (children as Block[][])\n : [];\n}\n\n/** Every block by id, with its container. The first occurrence of an id wins. */\nfunction indexBlocks(blocks: Block[]): Map<string, Placement> {\n const placements = new Map<string, Placement>();\n const walk = (list: Block[], parent: BlockParent): void => {\n list.forEach((block, index) => {\n if (!placements.has(block.id)) {\n placements.set(block.id, { block, key: containerKey(parent), index });\n }\n columnsOf(block).forEach((column, columnIndex) => {\n if (Array.isArray(column)) {\n walk(column, { sectionId: block.id, column: columnIndex });\n }\n });\n });\n };\n walk(blocks, null);\n return placements;\n}\n\n/**\n * The positions in `values` of one longest strictly increasing run: the blocks\n * that can stay where they are while the others move around them.\n */\nfunction longestIncreasingRun(values: readonly number[]): Set<number> {\n const tails: number[] = [];\n const previous: number[] = new Array<number>(values.length).fill(-1);\n values.forEach((value, position) => {\n let low = 0;\n let high = tails.length;\n while (low < high) {\n const middle = (low + high) >> 1;\n if (values[tails[middle]] < value) low = middle + 1;\n else high = middle;\n }\n if (low > 0) previous[position] = tails[low - 1];\n tails[low] = position;\n });\n const run = new Set<number>();\n let position = tails.length > 0 ? tails[tails.length - 1] : -1;\n while (position !== -1) {\n run.add(position);\n position = previous[position];\n }\n return run;\n}\n\n/** Keys whose values differ, as `set` and `unset`. `undefined` counts as absent. */\nfunction keyChanges(\n before: Json,\n after: Json,\n skip: ReadonlySet<string>,\n): { set: Json; unset: string[] } {\n const set: Json = {};\n const unset: string[] = [];\n for (const key of Object.keys(after)) {\n if (skip.has(key) || after[key] === undefined) continue;\n if (!deepEqual(before[key], after[key])) set[key] = safeClone(after[key]);\n }\n for (const key of Object.keys(before)) {\n if (skip.has(key) || before[key] === undefined) continue;\n if (after[key] === undefined) unset.push(key);\n }\n return { set, unset };\n}\n\n/**\n * The operations that turn `before` into `after`. Blocks are matched by id, so\n * a drag comes out as a `moveBlock` and a layout change as a `columns` change\n * plus moves, never as deletions and re-additions.\n *\n * `applyOperations(before, diffContent(before, after))` deep-equals `after`\n * when both are well formed: unique ids, sections only at the top level, and\n * each section's `children` as long as its layout's column count. The order is\n * what the engine needs:\n *\n * 1. Deletions that take nothing still needed with them.\n * 2. The top level: additions (a new section carries its new children) and moves.\n * 3. `columns` growth, so new columns exist before blocks move into them.\n * 4. Every section column: additions and moves, out of shrinking columns too.\n * 5. `columns` shrinkage, once the dropped columns are empty.\n * 6. Deletion of sections whose surviving blocks have moved out.\n * 7. Key changes on blocks, then on settings.\n *\n * No operation carries `expect`: an ordinary edit applies over anything that\n * doesn't conflict with it, and locks keep two people off one block.\n */\nexport function diffContent(\n before: TemplateContent,\n after: TemplateContent,\n): Operation[] {\n const was = indexBlocks(before.blocks);\n const now = indexBlocks(after.blocks);\n const ops: Operation[] = [];\n\n const typeChanged = (id: string): boolean => {\n const next = now.get(id);\n const previous = was.get(id);\n return (\n next !== undefined &&\n previous !== undefined &&\n next.block.type !== previous.block.type\n );\n };\n const survives = (block: Block): boolean =>\n now.has(block.id) && !typeChanged(block.id);\n const hasSurvivor = (block: Block): boolean =>\n columnsOf(block).some(\n (column) =>\n Array.isArray(column) &&\n column.some(\n (child) =>\n survives(child) || (!typeChanged(child.id) && hasSurvivor(child)),\n ),\n );\n\n // 1. Every old block's fate: kept, deleted now (\"gone\"), or deleted once its\n // survivors have moved out (\"deferred\"). A block that changed type is\n // deleted and comes back as a new one.\n const gone = new Set<string>();\n const deferred: string[] = [];\n type Fate = \"kept\" | \"deferred\" | \"dropped\";\n const visit = (block: Block, fate: Fate): void => {\n let childFate: Fate = \"kept\";\n if (fate === \"dropped\") {\n gone.add(block.id);\n childFate = \"dropped\";\n } else if (typeChanged(block.id)) {\n ops.push({ op: \"deleteBlock\", blockId: block.id });\n gone.add(block.id);\n childFate = \"dropped\";\n } else if (!now.has(block.id)) {\n if (fate === \"deferred\") {\n childFate = \"deferred\";\n } else if (hasSurvivor(block)) {\n deferred.push(block.id);\n childFate = \"deferred\";\n } else {\n ops.push({ op: \"deleteBlock\", blockId: block.id });\n gone.add(block.id);\n childFate = \"dropped\";\n }\n }\n for (const column of columnsOf(block)) {\n if (Array.isArray(column)) {\n for (const child of column) visit(child, childFate);\n }\n }\n };\n for (const block of before.blocks) visit(block, \"kept\");\n\n const exists = (id: string): boolean => was.has(id) && !gone.has(id);\n\n // A new block arrives whole, except that a new section brings only its new\n // children: existing ones move in afterwards.\n const payloadOf = (block: Block): Block => {\n const payload = safeClone(block);\n if (payload.type === \"section\") {\n payload.children = columnsOf(block).map((column) =>\n Array.isArray(column)\n ? column\n .filter((child) => !exists(child.id))\n .map((child) => safeClone(child))\n : column,\n );\n }\n return payload;\n };\n\n // Lay out one final container: the longest run of blocks already in order\n // stays, and every other block is placed after its final predecessor.\n const place = (\n parent: BlockParent,\n finalList: Block[],\n arrivedWithParent: (block: Block) => boolean,\n ): void => {\n const key = containerKey(parent);\n const indexes: number[] = [];\n const positions: number[] = [];\n finalList.forEach((block, position) => {\n const previous = was.get(block.id);\n if (exists(block.id) && previous !== undefined && previous.key === key) {\n indexes.push(previous.index);\n positions.push(position);\n }\n });\n const staying = new Set(\n [...longestIncreasingRun(indexes)].map((run) => positions[run]),\n );\n finalList.forEach((block, position) => {\n if (staying.has(position) || arrivedWithParent(block)) return;\n const afterId = position > 0 ? finalList[position - 1].id : null;\n ops.push(\n exists(block.id)\n ? { op: \"moveBlock\", blockId: block.id, parent, after: afterId }\n : { op: \"addBlock\", block: payloadOf(block), parent, after: afterId },\n );\n });\n };\n\n // 2. The top level.\n place(null, after.blocks, () => false);\n\n const sections = after.blocks.filter(\n (block): block is SectionBlock => block.type === \"section\",\n );\n const resized = new Set<string>();\n const resize = (grow: boolean): void => {\n for (const section of sections) {\n if (!exists(section.id)) continue;\n const fromCount = columnsOf(was.get(section.id)!.block).length;\n const toCount = columnsOf(section).length;\n if (grow ? toCount > fromCount : toCount < fromCount) {\n ops.push({\n op: \"updateBlock\",\n blockId: section.id,\n set: { columns: section.columns },\n });\n resized.add(section.id);\n }\n }\n };\n\n // 3. Growth.\n resize(true);\n\n // 4. Every column of every section, new sections included.\n for (const section of sections) {\n const isNew = !exists(section.id);\n columnsOf(section).forEach((column, columnIndex) => {\n if (!Array.isArray(column)) return;\n place(\n { sectionId: section.id, column: columnIndex },\n column,\n (child) => isNew && !exists(child.id),\n );\n });\n }\n\n // 5. Shrinkage.\n resize(false);\n\n // 6. Sections whose survivors have moved out.\n for (const id of deferred) ops.push({ op: \"deleteBlock\", blockId: id });\n\n // 7. Key changes, in document order.\n for (const [id, placement] of now) {\n if (!exists(id)) continue;\n const { set, unset } = keyChanges(\n was.get(id)!.block as unknown as Json,\n placement.block as unknown as Json,\n resized.has(id) ? RESIZED_SECTION_SKIP : BLOCK_SKIP,\n );\n if (Object.keys(set).length === 0 && unset.length === 0) continue;\n ops.push({\n op: \"updateBlock\",\n blockId: id,\n ...(Object.keys(set).length > 0 ? { set } : {}),\n ...(unset.length > 0 ? { unset } : {}),\n });\n }\n\n const settings = keyChanges(\n before.settings as unknown as Json,\n after.settings as unknown as Json,\n NO_SKIP,\n );\n if (Object.keys(settings.set).length > 0 || settings.unset.length > 0) {\n ops.push({\n op: \"updateSettings\",\n ...(Object.keys(settings.set).length > 0 ? { set: settings.set } : {}),\n ...(settings.unset.length > 0 ? { unset: settings.unset } : {}),\n });\n }\n\n return ops;\n}\n","import type {\n Block,\n BlockParent,\n Operation,\n TemplateContent,\n} from \"@templatical/types\";\nimport {\n applyOperationsInPlace,\n OperationError,\n safeClone,\n} from \"@templatical/types\";\n\ntype Json = Record<string, unknown>;\n\ninterface Position {\n block: Block;\n parent: BlockParent;\n /** The previous sibling's id, or `null` when the block is first. */\n after: string | null;\n}\n\nfunction columnsOf(block: Block): Block[][] {\n const children = (block as { children?: unknown }).children;\n return block.type === \"section\" && Array.isArray(children)\n ? (children as Block[][])\n : [];\n}\n\nfunction positionOf(\n blocks: Block[],\n blockId: string,\n parent: BlockParent = null,\n): Position | null {\n for (let index = 0; index < blocks.length; index++) {\n const block = blocks[index];\n if (block.id === blockId) {\n return { block, parent, after: index > 0 ? blocks[index - 1].id : null };\n }\n const columns = columnsOf(block);\n for (let column = 0; column < columns.length; column++) {\n if (!Array.isArray(columns[column])) continue;\n const found = positionOf(columns[column], blockId, {\n sectionId: block.id,\n column,\n });\n if (found) return found;\n }\n }\n return null;\n}\n\nfunction requirePosition(\n state: TemplateContent,\n blockId: string,\n index: number,\n): Position {\n const position = positionOf(state.blocks, blockId);\n if (!position) {\n throw new OperationError(\n \"block_missing\",\n index,\n `No block with id \"${blockId}\".`,\n blockId,\n );\n }\n return position;\n}\n\n/** Put back the keys an update touched, expecting what the update left. */\nfunction restoreKeys(\n current: Json,\n set: Json | undefined,\n unset: readonly string[] | undefined,\n): { set: Json; unset: string[]; expect: Json } {\n const restore: Json = {};\n const remove: string[] = [];\n const expect: Json = {};\n const keys = [...new Set([...Object.keys(set ?? {}), ...(unset ?? [])])];\n for (const key of keys) {\n const previous = current[key];\n if (previous === undefined) remove.push(key);\n else restore[key] = safeClone(previous);\n const next = set && key in set ? set[key] : undefined;\n expect[key] = next === undefined ? null : safeClone(next);\n }\n return { set: restore, unset: remove, expect };\n}\n\nfunction invertOne(\n state: TemplateContent,\n op: Operation,\n index: number,\n): Operation {\n switch (op.op) {\n case \"addBlock\":\n return {\n op: \"deleteBlock\",\n blockId: op.block.id,\n expect: { block: safeClone(op.block) },\n };\n case \"deleteBlock\": {\n const position = requirePosition(state, op.blockId, index);\n return {\n op: \"addBlock\",\n block: safeClone(position.block),\n parent: position.parent,\n after: position.after,\n };\n }\n case \"moveBlock\": {\n const position = requirePosition(state, op.blockId, index);\n return {\n op: \"moveBlock\",\n blockId: op.blockId,\n parent: position.parent,\n after: position.after,\n expect: { parent: op.parent, after: op.after },\n };\n }\n case \"updateBlock\": {\n const block = requirePosition(state, op.blockId, index)\n .block as unknown as Json;\n const { set, unset, expect } = restoreKeys(block, op.set, op.unset);\n return {\n op: \"updateBlock\",\n blockId: op.blockId,\n ...(Object.keys(set).length > 0 ? { set } : {}),\n ...(unset.length > 0 ? { unset } : {}),\n expect,\n };\n }\n case \"updateSettings\": {\n const { set, unset, expect } = restoreKeys(\n state.settings as unknown as Json,\n op.set as Json | undefined,\n op.unset,\n );\n return {\n op: \"updateSettings\",\n ...(Object.keys(set).length > 0 ? { set } : {}),\n ...(unset.length > 0 ? { unset } : {}),\n expect,\n };\n }\n case \"setContent\":\n return {\n op: \"setContent\",\n content: {\n blocks: safeClone(state.blocks),\n settings: safeClone(state.settings),\n },\n };\n }\n}\n\n/**\n * The batch that undoes `ops`, given the content they apply to. Each operation\n * is inverted against the content just before it, and the inverses run in\n * reverse order.\n *\n * Every inverse that can be guarded is: it expects what its operation left\n * behind, so an undo that a later edit by someone else has overtaken is refused\n * (`precondition_failed`, `anchor_missing`, `block_missing`) instead of\n * overwriting that edit. A `setContent` inverse has no guard; the backend's\n * `locked_by_others` rule is what stops it while others are editing.\n *\n * Throws the engine's `OperationError` when `ops` don't apply to `before`.\n */\nexport function invertOperations(\n before: TemplateContent,\n ops: readonly Operation[],\n): Operation[] {\n const state = safeClone(before);\n const inverses: Operation[] = [];\n ops.forEach((op, index) => {\n inverses.push(invertOne(state, op, index));\n try {\n applyOperationsInPlace(state, [op]);\n } catch (error) {\n if (!(error instanceof OperationError)) throw error;\n // Applied one at a time, so the engine numbers it 0: give it its index.\n throw new OperationError(error.code, index, error.message, error.blockId);\n }\n });\n return inverses.reverse();\n}\n","import type {\n Block,\n BlockLock,\n BlockLockHolder,\n CollaborationActor,\n CollaborationConflictCode,\n CollaborationConnectionState,\n CollaborationHandlers,\n CollaborationMember,\n CollaborationProvider,\n CollaborationRemoteChange,\n Operation,\n OperationContext,\n RevisionedBatch,\n Template,\n TemplateContent,\n} from \"@templatical/types\";\nimport {\n applyOperations,\n applyOperationsInPlace,\n deepEqual,\n OperationError,\n safeClone,\n} from \"@templatical/types\";\nimport {\n computed,\n getCurrentScope,\n onScopeDispose,\n ref,\n shallowRef,\n watch,\n type ComputedRef,\n type DeepReadonly,\n type Ref,\n} from \"@vue/reactivity\";\nimport { diffContent } from \"./diff-content\";\nimport { invertOperations } from \"./invert-operations\";\n\n/** Typing into one block is sent this long after the last keystroke. */\nexport const COLLABORATION_TYPING_DEBOUNCE_MS = 300;\n/** Typing that never pauses is still sent at least this often. */\nexport const COLLABORATION_TYPING_MAX_WAIT_MS = 1000;\n/** A revision that hasn't arrived after this long is fetched. */\nexport const COLLABORATION_GAP_TIMEOUT_MS = 1000;\n/** A held lock is renewed this often; the backend lets it lapse after 30 s. */\nexport const COLLABORATION_LOCK_RENEW_MS = 10_000;\n/** Waits between attempts at a batch that failed in transit. */\nconst RETRY_DELAYS_MS = [1000, 2000, 5000, 10_000, 30_000];\n/** The snapshot history's depth, kept for the operation history too. */\nconst DEFAULT_MAX_UNDO = 50;\n\nconst REFUSAL_CODES: ReadonlySet<string> = new Set<CollaborationConflictCode>([\n \"block_exists\",\n \"block_missing\",\n \"anchor_missing\",\n \"precondition_failed\",\n \"lock_held\",\n \"locked_by_others\",\n \"invalid_operation\",\n \"lock_lost\",\n \"content_too_large\",\n]);\n\n/** The template fields the controller reads, as the editor's readonly state holds them. */\nexport type CollaborationTemplate = DeepReadonly<\n Pick<Template, \"id\" | \"revision\" | \"content\">\n>;\n\n/** The slice of the editor the controller reads and writes. */\nexport interface CollaborationEditor {\n state: {\n readonly template: CollaborationTemplate | null;\n readonly selectedBlockId: string | null;\n readonly isLoading: boolean;\n };\n /** The live content the user edits. */\n content: Ref<TemplateContent>;\n selectBlock: (blockId: string | null) => void;\n setContent: (content: TemplateContent, markDirty?: boolean) => void;\n markDirty: () => void;\n}\n\n/** Something to tell the person at this editor. */\nexport type CollaborationNotice =\n /** Someone else holds the block this session tried to select. */\n | { type: \"selectionRefused\"; blockId: string; holder: BlockLockHolder }\n /** A local change was refused and taken back. */\n | {\n type: \"editRefused\";\n code: CollaborationConflictCode;\n holder?: BlockLockHolder;\n }\n /** An undo or redo step no longer applies and was dropped. */\n | { type: \"undoSkipped\"; code: CollaborationConflictCode };\n\n/** A re-sync another session caused, as the backend announced it. */\nexport interface CollaborationRemoteResync {\n /** The revision the re-sync brings the template to. */\n revision: number;\n actor: CollaborationActor;\n}\n\nexport interface UseCollaborationOptions {\n provider: CollaborationProvider;\n /**\n * Fetch a template when the backend says to re-sync: the templates\n * provider's `load`. Its `revision` says which revision `content` is at.\n */\n loadTemplate: (templateId: string) => Promise<Template>;\n /** This editor instance's session id. A new UUID when omitted. */\n sessionId?: string;\n /** How many undo steps to keep. */\n maxUndo?: number;\n /** A batch from another session was applied. */\n onRemoteChange?: (change: CollaborationRemoteChange) => void;\n /**\n * Another session re-synced the template — a `setContent`, a restore, a\n * batch too large to broadcast — and the backend said who. Called for a\n * revision this session doesn't have yet, before it catches up; the\n * catch-up's batches still reach `onRemoteChange`. The editor names an\n * agent's re-sync with it.\n */\n onRemoteResync?: (resync: CollaborationRemoteResync) => void;\n /**\n * Rewrite another session's batch just before it is applied, given the\n * content it applies to. Called for live batches and catch-up batches\n * alike, never for this session's own. The editor uses it to turn merge-tag\n * tokens into tags, as it does for loaded content. `onRemoteChange` still\n * receives the batch as the backend sent it.\n */\n normalizeRemoteOperations?: (\n ops: readonly Operation[],\n content: TemplateContent,\n ) => Operation[];\n onNotice?: (notice: CollaborationNotice) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface UseCollaborationReturn {\n readonly sessionId: string;\n /** Everyone with the template open, this session included; empty without presence. */\n members: Readonly<Ref<readonly CollaborationMember[]>>;\n /** Blocks other sessions hold, by block id. */\n locks: Readonly<Ref<ReadonlyMap<string, BlockLock>>>;\n /** `null` before a realtime join, and for a backend without realtime. */\n connection: Readonly<Ref<CollaborationConnectionState | null>>;\n /** The last revision this session confirmed. */\n revision: Readonly<Ref<number>>;\n canUndo: ComputedRef<boolean>;\n canRedo: ComputedRef<boolean>;\n /** Wire the controller to the editor. Call once, inside the editor's setup scope. */\n attach: (editor: CollaborationEditor) => void;\n undo: () => void;\n redo: () => void;\n clearHistory: () => void;\n /** Turn edits not sent yet into a batch now. */\n flush: () => void;\n /** Resolves once every batch is confirmed; rejects when one fails in transit. */\n settle: () => Promise<void>;\n /** Stop reading the canvas (a version preview is on it). Batches keep arriving. */\n suspend: () => void;\n /** Put the current state back on the canvas and read it again. */\n resume: () => void;\n /** Fetch what this session missed, or reload when the backend says to. */\n catchUp: () => Promise<void>;\n /** Whether the block exists in the content the backend confirmed. */\n hasConfirmedBlock: (blockId: string) => boolean;\n destroy: () => void;\n}\n\ninterface HistoryEntry {\n ops: Operation[];\n}\n\ninterface PendingBatch {\n batchId: string;\n ops: Operation[];\n origin: \"edit\" | \"undo\" | \"redo\";\n /** The stack entry this batch pushed, taken back if the backend refuses it. */\n entry: HistoryEntry | null;\n sent: boolean;\n /** Set once the backend confirms it. */\n revision: number | null;\n}\n\ninterface Session {\n templateId: string;\n confirmed: TemplateContent;\n revision: number;\n pending: PendingBatch[];\n /** `confirmed` plus every pending batch that still applies on it. */\n view: TemplateContent;\n /** Batches that arrived ahead of a gap, by revision. */\n remote: Map<number, RevisionedBatch>;\n own: Map<number, PendingBatch>;\n inFlight: PendingBatch | null;\n gapTimer: ReturnType<typeof setTimeout> | null;\n catchingUp: Promise<void> | null;\n leave: (() => void) | null;\n retired: boolean;\n /** True while a reload runs: a drift found then is reported, not retried. */\n recovering: boolean;\n undo: HistoryEntry[];\n redo: HistoryEntry[];\n waiters: { resolve: () => void; reject: (error: Error) => void }[];\n}\n\nfunction refusalCode(error: unknown): CollaborationConflictCode | null {\n const code = (error as { code?: unknown } | null)?.code;\n return typeof code === \"string\" && REFUSAL_CODES.has(code)\n ? (code as CollaborationConflictCode)\n : null;\n}\n\nfunction refusalHolder(error: unknown): BlockLockHolder | undefined {\n const holder = (error as { holder?: unknown } | null)?.holder;\n return typeof holder === \"object\" &&\n holder !== null &&\n typeof (holder as BlockLockHolder).sessionId === \"string\"\n ? (holder as BlockLockHolder)\n : undefined;\n}\n\n/** Network failures, 5xx, 408 and 429 are worth retrying; other 4xx aren't. */\nfunction isTransient(error: unknown): boolean {\n const status = (error as { statusCode?: unknown } | null)?.statusCode;\n if (typeof status !== \"number\") return true;\n return status >= 500 || status === 408 || status === 429;\n}\n\nfunction containsBlock(blocks: Block[], blockId: string): boolean {\n return blocks.some((block) => {\n if (block.id === blockId) return true;\n const children = (block as { children?: unknown }).children;\n return (\n block.type === \"section\" &&\n Array.isArray(children) &&\n (children as Block[][]).some(\n (column) => Array.isArray(column) && containsBlock(column, blockId),\n )\n );\n });\n}\n\n/** Typing into one block, or one settings change: worth batching up. */\nfunction isCoalescible(ops: readonly Operation[]): boolean {\n if (ops.length === 1 && ops[0].op === \"updateSettings\") return true;\n const first = ops[0];\n return (\n first.op === \"updateBlock\" &&\n ops.every((op) => op.op === \"updateBlock\" && op.blockId === first.blockId)\n );\n}\n\n/**\n * A remote batch without its preconditions. The backend checked them against\n * its own copy before it accepted the batch. This copy can differ from that one\n * in merge-tag markup alone — tokens are normalized into tags on load and on\n * arrival — and an `expect` compares content exactly, so checking it here\n * refuses a batch the backend applied and reloads the template. Structural\n * checks still run, and still catch a copy that really drifted.\n */\nfunction withoutPreconditions(ops: readonly Operation[]): Operation[] {\n return ops.map((op) => {\n if (!(\"expect\" in op)) return op;\n const { expect: _expect, ...rest } = op;\n return rest as Operation;\n });\n}\n\n/**\n * Real-time collaboration over a {@link CollaborationProvider}.\n *\n * The controller keeps the content the backend confirmed at revision R, plus\n * this session's pending batches; the canvas shows the confirmed content with\n * the pending batches applied. Local changes are captured by diffing the\n * canvas against that view: structural changes, selection changes and blur\n * send at once, typing into one block after a short pause. One batch is in\n * flight at a time. Batches from other sessions apply strictly by revision,\n * with this session's pending batches replayed on top; a gap that isn't\n * filled within a second, a re-sync, a reconnect and a refusal all fetch what\n * was missed.\n *\n * Selecting a block takes its lock (when the backend has locks), renewed\n * while selected and released on deselect, template switch and page hide.\n * Undo and redo send inverse batches guarded by preconditions; one that no\n * longer applies is dropped with a notice. Changes from others never enter the\n * stacks.\n */\nexport function useCollaboration(\n options: UseCollaborationOptions,\n): UseCollaborationReturn {\n const { provider } = options;\n const sessionId = options.sessionId ?? crypto.randomUUID();\n const maxUndo = options.maxUndo ?? DEFAULT_MAX_UNDO;\n const locking =\n typeof provider.acquireLock === \"function\" &&\n typeof provider.renewLock === \"function\" &&\n typeof provider.releaseLock === \"function\";\n\n const members = shallowRef<readonly CollaborationMember[]>([]);\n const locks = shallowRef<ReadonlyMap<string, BlockLock>>(new Map());\n const connection = ref<CollaborationConnectionState | null>(null);\n const revision = ref(0);\n const historyVersion = ref(0);\n\n let editor: CollaborationEditor | null = null;\n let active: Session | null = null;\n let suspended = false;\n let writing = false;\n let captureQueued = false;\n let typingTimer: ReturnType<typeof setTimeout> | null = null;\n let typingSince = 0;\n let destroyed = false;\n /** The watchers `attach` set up; `destroy` stops them, scope or not. */\n const stopWatching: (() => void)[] = [];\n\n // Locks: `wanted` follows the selection, `held` is what the backend granted.\n let wanted: { templateId: string; blockId: string } | null = null;\n let held: { templateId: string; blockId: string } | null = null;\n let lockQueue: Promise<void> = Promise.resolve();\n let renewTimer: ReturnType<typeof setInterval> | null = null;\n\n const canUndo = computed(() => {\n void historyVersion.value;\n return (active?.undo.length ?? 0) > 0;\n });\n const canRedo = computed(() => {\n void historyVersion.value;\n return (active?.redo.length ?? 0) > 0;\n });\n\n function reportError(error: unknown): void {\n options.onError?.(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n\n function notify(\n notice: CollaborationNotice,\n session: Session | null = active,\n ): void {\n if (session !== active || destroyed) return;\n options.onNotice?.(notice);\n }\n\n function lockContext(): OperationContext {\n return {\n locks: [...locks.value.values()].map((lock) => ({\n blockId: lock.blockId,\n sessionId: lock.holder.sessionId,\n })),\n session: sessionId,\n };\n }\n\n function bumpHistory(): void {\n historyVersion.value++;\n }\n\n function pushCapped(stack: HistoryEntry[], entry: HistoryEntry): void {\n stack.push(entry);\n if (stack.length > maxUndo) stack.splice(0, stack.length - maxUndo);\n }\n\n // --- The canvas ----------------------------------------------------------\n\n /** Put `view` on the canvas, changing only what differs from it. */\n function renderView(view: TemplateContent): void {\n if (!editor || suspended) return;\n writing = true;\n try {\n const live = editor.content.value;\n const ops = diffContent(live, view);\n if (ops.length > 0) {\n try {\n applyOperationsInPlace(live, ops);\n } catch {\n editor.setContent(safeClone(view), false);\n }\n }\n if (!deepEqual(editor.content.value, view)) {\n editor.setContent(safeClone(view), false);\n }\n } finally {\n writing = false;\n }\n const selected = editor.state.selectedBlockId;\n if (selected && !containsBlock(view.blocks, selected)) {\n editor.selectBlock(null);\n }\n }\n\n // --- Local changes -------------------------------------------------------\n\n function scheduleCapture(): void {\n if (writing || captureQueued) return;\n captureQueued = true;\n queueMicrotask(() => {\n captureQueued = false;\n capture(false);\n });\n }\n\n function clearTypingTimer(): void {\n if (typingTimer) {\n clearTimeout(typingTimer);\n typingTimer = null;\n }\n }\n\n /**\n * Diff the canvas against the view and send what changed. `duringLoad` is\n * for the moment a load or create starts: the canvas still shows this\n * template, and its unsent typing must go before the content is replaced.\n */\n function capture(force: boolean, duringLoad = false): void {\n const session = active;\n if (!editor || !session || suspended || writing) return;\n if (editor.state.isLoading && !duringLoad) return;\n if (editor.state.template?.id !== session.templateId) return;\n const ops = diffContent(session.view, editor.content.value);\n if (ops.length === 0) {\n clearTypingTimer();\n return;\n }\n if (!force && isCoalescible(ops)) {\n const now = Date.now();\n if (typingTimer === null) typingSince = now;\n if (now - typingSince < COLLABORATION_TYPING_MAX_WAIT_MS) {\n if (typingTimer) clearTimeout(typingTimer);\n typingTimer = setTimeout(() => {\n typingTimer = null;\n capture(true);\n }, COLLABORATION_TYPING_DEBOUNCE_MS);\n return;\n }\n }\n clearTypingTimer();\n commit(session, ops);\n }\n\n function flush(): void {\n capture(true);\n }\n\n function commit(session: Session, proposed: Operation[]): void {\n let ops = proposed;\n let next: TemplateContent;\n try {\n next = applyOperations(session.view, ops, lockContext());\n } catch (error) {\n if (!(error instanceof OperationError)) throw error;\n // Refused here, before any request: take the change back.\n renderView(session.view);\n const holder =\n error.blockId === undefined\n ? undefined\n : locks.value.get(error.blockId)?.holder;\n notify({\n type: \"editRefused\",\n code: error.code,\n ...(holder ? { holder } : {}),\n });\n if (error.code === \"invalid_operation\") reportError(error);\n return;\n }\n const live = editor!.content.value;\n if (!deepEqual(next, live)) {\n // Content that isn't well formed can defeat the diff. Send it whole\n // rather than drift from it.\n reportError(\n new Error(\n \"[Templatical] Collaboration: a change couldn't be expressed as block operations, so the whole content was sent.\",\n ),\n );\n ops = [{ op: \"setContent\", content: safeClone(live) }];\n try {\n next = applyOperations(session.view, ops, lockContext());\n } catch (error) {\n if (!(error instanceof OperationError)) throw error;\n renderView(session.view);\n notify({ type: \"editRefused\", code: error.code });\n return;\n }\n }\n const entry: HistoryEntry = { ops: invertOperations(session.view, ops) };\n pushCapped(session.undo, entry);\n session.redo = [];\n bumpHistory();\n session.view = next;\n enqueue(session, ops, \"edit\", entry);\n }\n\n function enqueue(\n session: Session,\n ops: Operation[],\n origin: PendingBatch[\"origin\"],\n entry: HistoryEntry | null,\n ): void {\n session.pending.push({\n batchId: crypto.randomUUID(),\n ops,\n origin,\n entry,\n sent: false,\n revision: null,\n });\n pump(session);\n }\n\n // --- Sending ---------------------------------------------------------------\n\n function pump(session: Session): void {\n if (session.inFlight) return;\n const next = session.pending.find((batch) => !batch.sent);\n if (!next) {\n settleWaiters(session);\n return;\n }\n next.sent = true;\n session.inFlight = next;\n send(session, next, 0);\n }\n\n function send(session: Session, batch: PendingBatch, attempt: number): void {\n provider\n .submit(session.templateId, {\n batchId: batch.batchId,\n baseRevision: session.revision,\n ops: batch.ops,\n })\n .then(\n ({ revision: confirmedAt }) => {\n session.inFlight = null;\n confirmOwn(session, batch, confirmedAt);\n pump(session);\n },\n (error: unknown) => {\n if (refusalCode(error) !== null) {\n session.inFlight = null;\n refuse(session, batch, error);\n pump(session);\n return;\n }\n if (isTransient(error)) {\n // Same batch id, so a submit that did land applies once.\n rejectWaiters(session, error);\n const delay =\n RETRY_DELAYS_MS[Math.min(attempt, RETRY_DELAYS_MS.length - 1)];\n setTimeout(() => {\n if (!destroyed) send(session, batch, attempt + 1);\n }, delay);\n return;\n }\n session.inFlight = null;\n reportError(error);\n refuse(session, batch, error);\n pump(session);\n },\n );\n }\n\n function confirmOwn(session: Session, batch: PendingBatch, at: number): void {\n if (batch.revision !== null) return;\n batch.revision = at;\n if (at <= session.revision) {\n // A catch-up or a reload already brought it in.\n dropPending(session, batch);\n rebuild(session);\n return;\n }\n session.own.set(at, batch);\n drain(session);\n }\n\n function dropPending(session: Session, batch: PendingBatch): void {\n session.pending = session.pending.filter((pending) => pending !== batch);\n settleWaiters(session);\n }\n\n function takeBackEntry(session: Session, batch: PendingBatch): void {\n if (!batch.entry) return;\n for (const stack of [session.undo, session.redo]) {\n const position = stack.indexOf(batch.entry);\n if (position !== -1) stack.splice(position, 1);\n }\n bumpHistory();\n }\n\n function refuse(session: Session, batch: PendingBatch, error: unknown): void {\n dropPending(session, batch);\n takeBackEntry(session, batch);\n const code = refusalCode(error) ?? \"invalid_operation\";\n if (batch.origin === \"edit\") {\n const holder = refusalHolder(error);\n notify(\n { type: \"editRefused\", code, ...(holder ? { holder } : {}) },\n session,\n );\n } else {\n notify({ type: \"undoSkipped\", code }, session);\n }\n rebuild(session);\n if (session === active) void catchUp();\n }\n\n function settleWaiters(session: Session): void {\n if (session.pending.length > 0 || session.waiters.length === 0) return;\n for (const waiter of session.waiters.splice(0)) waiter.resolve();\n }\n\n function rejectWaiters(session: Session, error: unknown): void {\n const reason = error instanceof Error ? error : new Error(String(error));\n for (const waiter of session.waiters.splice(0)) waiter.reject(reason);\n }\n\n // --- Receiving -------------------------------------------------------------\n\n function receive(session: Session, batch: RevisionedBatch): void {\n if (batch.revision <= session.revision) return;\n const mine = session.pending.find(\n (pending) => pending.batchId === batch.batchId,\n );\n if (mine) {\n confirmOwn(session, mine, batch.revision);\n return;\n }\n session.remote.set(batch.revision, batch);\n drain(session);\n }\n\n /**\n * The ops another session's batch applies here: rewritten by\n * `normalizeRemoteOperations`, then without their preconditions. A\n * normalizer that throws is reported, and the batch applies as it came.\n */\n function arriving(session: Session, batch: RevisionedBatch): Operation[] {\n let ops: readonly Operation[] = batch.ops;\n if (options.normalizeRemoteOperations) {\n try {\n ops = options.normalizeRemoteOperations(batch.ops, session.confirmed);\n } catch (error) {\n reportError(error);\n }\n }\n return withoutPreconditions(ops);\n }\n\n /** Apply whatever is next in revision order, own and remote alike. */\n function drain(session: Session): void {\n const applied: RevisionedBatch[] = [];\n for (;;) {\n const at = session.revision + 1;\n const remote = session.remote.get(at);\n const own = session.own.get(at);\n if (!remote && !own) break;\n try {\n session.confirmed = applyOperations(\n session.confirmed,\n remote ? arriving(session, remote) : own!.ops,\n );\n } catch {\n if (session.recovering) {\n // Even a fresh copy doesn't take the backend's next batch: stop\n // rather than reload in a loop.\n session.remote.clear();\n session.own.clear();\n reportError(\n new Error(\n \"[Templatical] Collaboration: this editor can't follow the template's changes. Reload the page.\",\n ),\n );\n return;\n }\n // This copy has drifted from the backend's: start over from it.\n void reload(session);\n return;\n }\n session.revision = at;\n if (remote) {\n session.remote.delete(at);\n applied.push(remote);\n } else {\n session.own.delete(at);\n dropPending(session, own!);\n }\n }\n if (session === active) revision.value = session.revision;\n if (session.remote.size > 0 || session.own.size > 0) armGapTimer(session);\n else clearGapTimer(session);\n\n if (applied.length === 0) return;\n rebuild(session);\n if (session !== active) return;\n for (const batch of applied) {\n try {\n options.onRemoteChange?.({ actor: batch.actor, ops: batch.ops });\n } catch (error) {\n reportError(error);\n }\n }\n }\n\n /** Recompute the view from `confirmed`, replaying what is still pending. */\n function rebuild(session: Session): void {\n // Typing not sent yet becomes a batch first, so the canvas holds nothing\n // the replay below doesn't know about.\n if (session === active) flush();\n let view = session.confirmed;\n const kept: PendingBatch[] = [];\n for (const batch of session.pending) {\n try {\n view = applyOperations(view, batch.ops);\n kept.push(batch);\n } catch (error) {\n if (batch.sent) {\n // The backend decides this one; it will almost certainly refuse it.\n kept.push(batch);\n continue;\n }\n takeBackEntry(session, batch);\n const code =\n error instanceof OperationError ? error.code : \"invalid_operation\";\n notify(\n batch.origin === \"edit\"\n ? { type: \"editRefused\", code }\n : { type: \"undoSkipped\", code },\n session,\n );\n }\n }\n session.pending = kept;\n session.view = view;\n settleWaiters(session);\n if (session === active) renderView(view);\n }\n\n function armGapTimer(session: Session): void {\n if (session.gapTimer) return;\n session.gapTimer = setTimeout(() => {\n session.gapTimer = null;\n if (session === active) void catchUp();\n }, COLLABORATION_GAP_TIMEOUT_MS);\n }\n\n function clearGapTimer(session: Session): void {\n if (session.gapTimer) {\n clearTimeout(session.gapTimer);\n session.gapTimer = null;\n }\n }\n\n function catchUp(): Promise<void> {\n const session = active;\n if (!session) return Promise.resolve();\n if (session.catchingUp) return session.catchingUp;\n session.catchingUp = (async () => {\n try {\n const result = await provider.operationsAfter(\n session.templateId,\n session.revision,\n );\n if (session.retired) return;\n if (\"resync\" in result) {\n await reload(session);\n return;\n }\n for (const batch of result.batches) receive(session, batch);\n } catch (error) {\n reportError(error);\n } finally {\n session.catchingUp = null;\n }\n })();\n return session.catchingUp;\n }\n\n async function reload(session: Session): Promise<void> {\n session.recovering = true;\n try {\n const template = await options.loadTemplate(session.templateId);\n if (session.retired) return;\n const at = template.revision ?? session.revision;\n session.confirmed = safeClone(template.content);\n session.revision = at;\n session.remote = new Map(\n [...session.remote].filter(([remoteAt]) => remoteAt > at),\n );\n for (const [ownAt, batch] of [...session.own]) {\n if (ownAt <= at) {\n session.own.delete(ownAt);\n dropPending(session, batch);\n }\n }\n if (session === active) revision.value = at;\n rebuild(session);\n drain(session);\n } catch (error) {\n reportError(error);\n } finally {\n session.recovering = false;\n }\n }\n\n // --- Sessions --------------------------------------------------------------\n\n function handlersFor(session: Session): CollaborationHandlers {\n const live = (): boolean => session === active && !session.retired;\n return {\n onBatch: (batch) => {\n if (live()) receive(session, batch);\n },\n onResync: (at, actor) => {\n if (!live() || at <= session.revision) return;\n if (actor) {\n try {\n options.onRemoteResync?.({ revision: at, actor });\n } catch (error) {\n reportError(error);\n }\n }\n void catchUp();\n },\n onLock: (lock) => {\n if (live()) setLock(lock);\n },\n onUnlock: (blockId, holderSession) => {\n if (live()) removeLock(blockId, holderSession);\n },\n onMembers: (next) => {\n if (live()) setMembers(next);\n },\n onConnectionChange: (state) => {\n if (live()) changeConnection(session, state);\n },\n };\n }\n\n function join(session: Session, refresh: boolean): void {\n provider.join(session.templateId, { sessionId }, handlersFor(session)).then(\n (joined) => {\n if (session.retired || session !== active) {\n if (!refresh) joined.leave();\n return;\n }\n session.leave = joined.leave;\n replaceLocks(joined.locks, !refresh);\n setMembers(joined.members);\n if (joined.members.length > 0) connection.value = \"connected\";\n if (joined.revision > session.revision) void catchUp();\n if (refresh) reconcileLock();\n },\n (error: unknown) => reportError(error),\n );\n }\n\n function start(template: CollaborationTemplate): void {\n const content = safeClone(template.content) as TemplateContent;\n const session: Session = {\n templateId: template.id,\n confirmed: content,\n revision: template.revision ?? 0,\n pending: [],\n view: safeClone(content),\n remote: new Map(),\n own: new Map(),\n inFlight: null,\n gapTimer: null,\n catchingUp: null,\n leave: null,\n retired: false,\n recovering: false,\n undo: [],\n redo: [],\n waiters: [],\n };\n active = session;\n revision.value = session.revision;\n bumpHistory();\n join(session, false);\n if (editor?.state.selectedBlockId)\n onSelection(editor.state.selectedBlockId);\n scheduleCapture();\n }\n\n function retire(session: Session): void {\n session.retired = true;\n clearGapTimer(session);\n session.leave?.();\n session.leave = null;\n rejectWaiters(session, new Error(\"[Templatical] The template was closed.\"));\n }\n\n function changeConnection(\n session: Session,\n state: CollaborationConnectionState,\n ): void {\n const previous = connection.value;\n connection.value = state;\n if (\n state === \"connected\" &&\n previous !== null &&\n previous !== \"connected\"\n ) {\n // Back after a drop: refresh locks and members, and fetch what was missed.\n join(session, true);\n }\n }\n\n function switchTo(template: CollaborationTemplate | null): void {\n if (active && active.templateId === template?.id) return;\n if (active) {\n clearTypingTimer();\n retire(active);\n active = null;\n }\n members.value = [];\n locks.value = new Map();\n connection.value = null;\n wanted = null;\n reconcileLock();\n bumpHistory();\n if (template) start(template);\n }\n\n // --- Locks -----------------------------------------------------------------\n\n function setLock(lock: BlockLock): void {\n if (lock.holder.sessionId === sessionId) return;\n const next = new Map(\n [...locks.value].filter(\n ([, existing]) => existing.holder.sessionId !== lock.holder.sessionId,\n ),\n );\n next.set(lock.blockId, lock);\n locks.value = next;\n if (editor?.state.selectedBlockId === lock.blockId) {\n editor.selectBlock(null);\n notify({\n type: \"selectionRefused\",\n blockId: lock.blockId,\n holder: lock.holder,\n });\n }\n }\n\n function removeLock(blockId: string, holderSession: string): void {\n if (locks.value.get(blockId)?.holder.sessionId !== holderSession) return;\n const next = new Map(locks.value);\n next.delete(blockId);\n locks.value = next;\n }\n\n function replaceLocks(list: readonly BlockLock[], merge: boolean): void {\n const next = merge ? new Map(locks.value) : new Map<string, BlockLock>();\n for (const lock of list) {\n if (lock.holder.sessionId === sessionId) continue;\n if (merge && next.has(lock.blockId)) continue;\n next.set(lock.blockId, lock);\n }\n locks.value = next;\n }\n\n function setMembers(next: readonly CollaborationMember[]): void {\n const present = new Set(next.map((member) => member.sessionId));\n const departed = new Set(\n members.value\n .map((member) => member.sessionId)\n .filter((id) => !present.has(id)),\n );\n members.value = next;\n if (departed.size === 0) return;\n const remaining = new Map(\n [...locks.value].filter(\n ([, lock]) => !departed.has(lock.holder.sessionId),\n ),\n );\n if (remaining.size !== locks.value.size) locks.value = remaining;\n }\n\n function queueLock(task: () => Promise<void>): void {\n lockQueue = lockQueue.then(task).catch(reportError);\n }\n\n /** Move what the backend granted toward what is selected, one call at a time. */\n function reconcileLock(): void {\n if (!locking) return;\n queueLock(async () => {\n if (held && (wanted === null || held.templateId !== wanted.templateId)) {\n // Acquiring another block on the same template releases this one on\n // the backend, so only release explicitly when nothing replaces it.\n const releasing = held;\n held = null;\n stopRenewing();\n await provider.releaseLock!(releasing.templateId, releasing.blockId);\n }\n const target = wanted;\n if (\n !target ||\n (held?.templateId === target.templateId &&\n held.blockId === target.blockId)\n ) {\n return;\n }\n try {\n await provider.acquireLock!(target.templateId, target.blockId);\n held = target;\n startRenewing();\n forgetLock(target.blockId);\n } catch (error) {\n if (held?.templateId === target.templateId) {\n // Refused: the previous block is still this session's on the backend.\n const releasing = held;\n held = null;\n stopRenewing();\n await provider.releaseLock!(\n releasing.templateId,\n releasing.blockId,\n ).catch(() => {});\n }\n if (wanted !== target) return;\n refuseSelection(target.blockId, error);\n }\n });\n }\n\n function forgetLock(blockId: string): void {\n if (!locks.value.has(blockId)) return;\n const next = new Map(locks.value);\n next.delete(blockId);\n locks.value = next;\n }\n\n function refuseSelection(blockId: string, error: unknown): void {\n const code = refusalCode(error);\n if (code === \"lock_held\" || code === \"block_missing\") {\n wanted = null;\n const holder = refusalHolder(error);\n if (holder) {\n // Records the holder, deselects and says who it is.\n setLock({ blockId, holder, expiresAt: \"\" });\n } else if (editor?.state.selectedBlockId === blockId) {\n editor.selectBlock(null);\n }\n return;\n }\n // A failure in transit: keep the selection and try again shortly.\n reportError(error);\n setTimeout(() => {\n if (wanted?.blockId === blockId) reconcileLock();\n }, RETRY_DELAYS_MS[0]);\n }\n\n function startRenewing(): void {\n if (renewTimer) return;\n renewTimer = setInterval(() => {\n queueLock(async () => {\n const current = held;\n if (!current) return;\n try {\n await provider.renewLock!(current.templateId, current.blockId);\n } catch (error) {\n if (refusalCode(error) === \"lock_lost\") {\n held = null;\n stopRenewing();\n reconcileLock();\n return;\n }\n reportError(error);\n }\n });\n }, COLLABORATION_LOCK_RENEW_MS);\n }\n\n function stopRenewing(): void {\n if (renewTimer) {\n clearInterval(renewTimer);\n renewTimer = null;\n }\n }\n\n function onSelection(blockId: string | null): void {\n flush();\n if (!active) return;\n wanted = blockId ? { templateId: active.templateId, blockId } : null;\n reconcileLock();\n }\n\n /** The page is going away: send what's typed, let go of the lock, leave. */\n function onPageHide(): void {\n flush();\n if (held && locking) {\n void provider.releaseLock!(held.templateId, held.blockId).catch(() => {});\n held = null;\n stopRenewing();\n }\n active?.leave?.();\n if (active) active.leave = null;\n }\n\n // --- History ---------------------------------------------------------------\n\n function step(direction: \"undo\" | \"redo\"): void {\n const session = active;\n if (!session || suspended) return;\n flush();\n const entry = (direction === \"undo\" ? session.undo : session.redo).pop();\n if (!entry) return;\n bumpHistory();\n let next: TemplateContent;\n try {\n next = applyOperations(session.view, entry.ops, lockContext());\n } catch (error) {\n if (!(error instanceof OperationError)) throw error;\n notify({ type: \"undoSkipped\", code: error.code });\n return;\n }\n const counter: HistoryEntry = {\n ops: invertOperations(session.view, entry.ops),\n };\n pushCapped(direction === \"undo\" ? session.redo : session.undo, counter);\n session.view = next;\n renderView(next);\n editor?.markDirty();\n enqueue(session, entry.ops, direction, counter);\n }\n\n // --- Public surface --------------------------------------------------------\n\n function attach(target: CollaborationEditor): void {\n editor = target;\n stopWatching.push(\n watch(\n () => target.state.template,\n (template) => switchTo(template),\n {\n immediate: true,\n },\n ),\n watch(() => target.content.value, scheduleCapture, { deep: true }),\n watch(() => target.state.selectedBlockId, onSelection),\n watch(\n () => target.state.isLoading,\n (loading) => (loading ? capture(true, true) : scheduleCapture()),\n ),\n );\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"pagehide\", onPageHide);\n window.addEventListener(\"blur\", flush);\n }\n if (getCurrentScope()) onScopeDispose(destroy);\n }\n\n function settle(): Promise<void> {\n flush();\n const session = active;\n if (!session || session.pending.length === 0) return Promise.resolve();\n return new Promise((resolve, reject) =>\n session.waiters.push({ resolve, reject }),\n );\n }\n\n function suspend(): void {\n flush();\n suspended = true;\n }\n\n function resume(): void {\n suspended = false;\n if (active) renderView(active.view);\n }\n\n function destroy(): void {\n if (destroyed) return;\n flush();\n for (const stop of stopWatching.splice(0)) stop();\n clearTypingTimer();\n onPageHide();\n destroyed = true;\n if (active) retire(active);\n active = null;\n stopRenewing();\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"pagehide\", onPageHide);\n window.removeEventListener(\"blur\", flush);\n }\n }\n\n return {\n sessionId,\n members,\n locks,\n connection,\n revision,\n canUndo,\n canRedo,\n attach,\n undo: () => step(\"undo\"),\n redo: () => step(\"redo\"),\n clearHistory: () => {\n if (!active) return;\n active.undo = [];\n active.redo = [];\n bumpHistory();\n },\n flush,\n settle,\n suspend,\n resume,\n catchUp,\n hasConfirmedBlock: (blockId) =>\n active ? containsBlock(active.confirmed.blocks, blockId) : false,\n destroy,\n };\n}\n","import type {\n Block,\n SavedBlock,\n SavedBlockPatch,\n SavedBlocksListParams,\n SavedBlocksProvider,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport { computed, ref, type ComputedRef, type Ref } from \"vue\";\nimport { notifyHandler } from \"./error-reporting\";\n\nexport interface UseSavedBlocksOptions {\n /**\n * Storage backend. Supplied by the consumer via `init({ savedBlocks })`, or\n * by Cloud's own adapter — this composable is transport-agnostic and never\n * talks to a network itself.\n */\n provider: SavedBlocksProvider;\n onError?: (error: Error) => void;\n}\n\nexport interface UseSavedBlocksReturn {\n /** In-memory mirror of the provider's list, kept in sync after each call. */\n savedBlocks: Ref<SavedBlock[]>;\n isLoading: Ref<boolean>;\n /**\n * Distinct categories across the loaded entries, alphabetically sorted.\n *\n * Derived rather than stored: a category exists exactly as long as an entry\n * carries it. Sorting here is not a violation of \"the editor never re-sorts\"\n * — that rule protects the provider's ordering of *entries*, whereas these\n * are strings with no given order.\n */\n categories: ComputedRef<string[]>;\n /**\n * Whether the provider supplied each mutation at all — `false` on the\n * provider means the capability does not exist for this user, and the UI\n * hides the corresponding affordance entirely.\n *\n * Constant for the provider's lifetime, but exposed as computed refs so\n * consumers can bind them the same way as everything else here.\n */\n canCreate: ComputedRef<boolean>;\n canUpdate: ComputedRef<boolean>;\n canDelete: ComputedRef<boolean>;\n /**\n * Whether a *particular* entry may be edited / deleted: the capability must\n * exist AND the entry must not opt out via `canUpdate` / `canDelete`.\n *\n * Use these rather than reading the flags directly, so every surface asks the\n * question the same way and the \"absent means allowed\" default lives in one\n * place.\n */\n canUpdateBlock: (block: SavedBlock) => boolean;\n canDeleteBlock: (block: SavedBlock) => boolean;\n /**\n * Re-read from the provider. The editor always calls this bare and filters\n * in memory; `params` exists for headless callers that want the provider to\n * filter instead (see {@link SavedBlocksListParams}).\n */\n load: (params?: SavedBlocksListParams) => Promise<void>;\n /** Rejects with an {@link SdkError} when the provider disabled `create`. */\n create: (\n name: string,\n content: Block[],\n category?: string,\n ) => Promise<SavedBlock>;\n /** Rejects when `update` is disabled or the entry opts out of editing. */\n update: (id: string, patch: SavedBlockPatch) => Promise<SavedBlock>;\n /** Rejects when `delete` is disabled or the entry opts out of deletion. */\n remove: (id: string) => Promise<void>;\n}\n\n/**\n * Reactive state over a {@link SavedBlocksProvider}.\n *\n * Owns the list, the loading flag, and the local mutations that keep the list\n * consistent after each successful call, so a provider only has to implement\n * four promise-returning methods. Errors are reported through `onError` and\n * re-thrown, leaving the local list untouched on failure.\n */\nexport function useSavedBlocks(\n options: UseSavedBlocksOptions,\n): UseSavedBlocksReturn {\n const { provider } = options;\n\n const savedBlocks = ref<SavedBlock[]>([]);\n const isLoading = ref(false);\n\n const categories = computed(() => {\n const seen = new Set<string>();\n for (const block of savedBlocks.value) {\n const category = block.category?.trim();\n if (category) seen.add(category);\n }\n return [...seen].sort((a, b) => a.localeCompare(b));\n });\n\n const canCreate = computed(() => typeof provider.create === \"function\");\n const canUpdate = computed(() => typeof provider.update === \"function\");\n const canDelete = computed(() => typeof provider.delete === \"function\");\n\n /** Absent flag means allowed — the flags exist only to forbid. */\n function canUpdateBlock(block: SavedBlock): boolean {\n return canUpdate.value && block.canUpdate !== false;\n }\n\n function canDeleteBlock(block: SavedBlock): boolean {\n return canDelete.value && block.canDelete !== false;\n }\n\n /**\n * The UI hides disabled actions, so reaching one of these means a programmatic\n * caller went around it. Fail loudly rather than silently no-op: a resolved\n * promise would read as \"saved\" to whoever awaited it.\n */\n function refuse(action: string, reason: string): never {\n throw new SdkError(\n `[Templatical] Saved blocks: ${action} is ${reason}. Check the capability before calling — the editor's own UI hides the action.`,\n );\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for a saved block that was created.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n async function load(params?: SavedBlocksListParams): Promise<void> {\n isLoading.value = true;\n try {\n savedBlocks.value = await provider.list(params);\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n isLoading.value = false;\n }\n }\n\n async function create(\n name: string,\n content: Block[],\n category?: string,\n ): Promise<SavedBlock> {\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") {\n refuse(\"create\", \"disabled by the provider\");\n }\n try {\n // Omit the key entirely when unset, rather than sending `undefined` —\n // a provider serialising the input to JSON would otherwise be handed an\n // explicit null-ish field it never asked for.\n const created = await providerCreate(\n category ? { name, content, category } : { name, content },\n );\n savedBlocks.value = [created, ...savedBlocks.value];\n notify(() => provider.onCreated?.(created));\n return created;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function update(\n id: string,\n patch: SavedBlockPatch,\n ): Promise<SavedBlock> {\n const { update: providerUpdate } = provider;\n if (typeof providerUpdate !== \"function\") {\n refuse(\"update\", \"disabled by the provider\");\n }\n // An id absent from the local list isn't refused — a headless caller may\n // legitimately patch something it never loaded. Only a loaded entry that\n // explicitly opted out is blocked.\n const local = savedBlocks.value.find((b) => b.id === id);\n if (local && local.canUpdate === false) {\n refuse(\"update\", `not permitted for entry \"${id}\"`);\n }\n try {\n const updated = await providerUpdate(id, patch);\n savedBlocks.value = savedBlocks.value.map((b) =>\n b.id === id ? updated : b,\n );\n notify(() => provider.onUpdated?.(updated));\n return updated;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function remove(id: string): Promise<void> {\n const { delete: providerDelete } = provider;\n if (typeof providerDelete !== \"function\") {\n refuse(\"delete\", \"disabled by the provider\");\n }\n // Captured before the delete resolves: `onDeleted` carries the removed\n // entry itself, and there is nothing left to read once the list is\n // filtered.\n const removed = savedBlocks.value.find((b) => b.id === id) ?? null;\n if (removed && removed.canDelete === false) {\n refuse(\"delete\", `not permitted for entry \"${id}\"`);\n }\n try {\n await providerDelete(id);\n savedBlocks.value = savedBlocks.value.filter((b) => b.id !== id);\n if (removed) {\n notify(() => provider.onDeleted?.(removed));\n }\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n return {\n savedBlocks,\n isLoading,\n categories,\n canCreate,\n canUpdate,\n canDelete,\n canUpdateBlock,\n canDeleteBlock,\n load,\n create,\n update,\n remove,\n };\n}\n","import type {\n SavedBlock,\n SavedBlockInput,\n SavedBlockPatch,\n SavedBlocksListParams,\n SavedBlocksProvider,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\n\nexport interface LocalStorageSavedBlocksOptions {\n /**\n * `localStorage` key holding the serialized array.\n *\n * @default \"templatical:saved-blocks\"\n */\n key?: string;\n}\n\nconst DEFAULT_KEY = \"templatical:saved-blocks\";\n\n/**\n * Browser-local {@link SavedBlocksProvider} backed by `localStorage`.\n *\n * Opt-in: pass it explicitly as `init({ savedBlocks: createLocalStorageSavedBlocksProvider() })`.\n * The editor never falls back to it, so consumers without a provider keep the\n * feature — and its UI — entirely off.\n *\n * Intended for demos, prototypes, and single-device use. Entries live in one\n * browser profile only: they don't sync across devices or users, and clearing\n * site data removes them. Back saved blocks with your own API for anything\n * that needs to outlive a browser profile.\n */\nexport function createLocalStorageSavedBlocksProvider(\n options: LocalStorageSavedBlocksOptions = {},\n): SavedBlocksProvider {\n const storageKey = options.key ?? DEFAULT_KEY;\n\n function getStorage(): Storage {\n // Core targets a neutral platform, so `localStorage` isn't guaranteed —\n // fail with an actionable message rather than a bare ReferenceError.\n if (typeof localStorage === \"undefined\") {\n throw new Error(\n \"[Templatical] createLocalStorageSavedBlocksProvider requires a browser environment with localStorage. Supply your own SavedBlocksProvider for server-side or non-browser use.\",\n );\n }\n return localStorage;\n }\n\n function readAll(): SavedBlock[] {\n const raw = getStorage().getItem(storageKey);\n if (!raw) return [];\n try {\n const parsed: unknown = JSON.parse(raw);\n // Tolerate a corrupted or hand-edited value rather than breaking the\n // editor: treat anything that isn't an array as \"no saved blocks\".\n return Array.isArray(parsed) ? (parsed as SavedBlock[]) : [];\n } catch {\n return [];\n }\n }\n\n function writeAll(blocks: SavedBlock[]): void {\n getStorage().setItem(storageKey, JSON.stringify(blocks));\n }\n\n // Every method is `async` so a synchronous failure (a missing\n // `localStorage`, a quota error from `setItem`) surfaces as a rejected\n // promise rather than a sync throw — callers of a `Promise`-returning\n // contract must be able to rely on `.catch()`.\n return {\n // The editor calls this bare and filters in memory; the params are honored\n // for headless callers that would rather the provider did the work.\n async list(params?: SavedBlocksListParams): Promise<SavedBlock[]> {\n const all = readAll();\n const search = params?.search?.trim().toLowerCase();\n const category = params?.category?.trim();\n return all.filter((b) => {\n if (search && !b.name.toLowerCase().includes(search)) return false;\n if (category && b.category !== category) return false;\n return true;\n });\n },\n\n // All three mutations are enabled: a browser-local store has no notion of\n // permissions, so there is nothing to forbid.\n async create(input: SavedBlockInput): Promise<SavedBlock> {\n const now = new Date().toISOString();\n const created: SavedBlock = {\n id: generateId(),\n name: input.name,\n content: input.content,\n ...(input.category ? { category: input.category } : {}),\n createdAt: now,\n updatedAt: now,\n };\n // Newest-first, matching the order the composable applies locally after\n // a create — so a reload preserves what the user just saw.\n writeAll([created, ...readAll()]);\n return created;\n },\n\n async update(id: string, patch: SavedBlockPatch): Promise<SavedBlock> {\n const all = readAll();\n const index = all.findIndex((b) => b.id === id);\n // Mirrors a REST 404 so provider behavior is consistent across adapters.\n if (index === -1) {\n throw new Error(`[Templatical] Saved block not found: ${id}`);\n }\n const updated: SavedBlock = {\n ...all[index],\n ...patch,\n updatedAt: new Date().toISOString(),\n };\n const next = [...all];\n next[index] = updated;\n writeAll(next);\n return updated;\n },\n\n async delete(id: string): Promise<void> {\n const all = readAll();\n if (!all.some((b) => b.id === id)) {\n throw new Error(`[Templatical] Saved block not found: ${id}`);\n }\n writeAll(all.filter((b) => b.id !== id));\n },\n };\n}\n","import type {\n MediaAsset,\n MediaAssetPatch,\n MediaCreateInput,\n MediaListPage,\n MediaListParams,\n MediaProvider,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\n\nexport interface LocalStorageMediaProviderOptions {\n /**\n * `localStorage` key holding the serialized array.\n *\n * @default \"templatical:media\"\n */\n key?: string;\n}\n\nconst DEFAULT_KEY = \"templatical:media\";\nconst PAGE_SIZE = 50;\n\n/**\n * Browser-local {@link MediaProvider} backed by `localStorage`.\n *\n * Opt-in: pass it explicitly as `init({ media: createLocalStorageMediaProvider() })`.\n * The editor never falls back to it, so consumers without a provider keep\n * image fields URL-only.\n *\n * `create` stores the file as a data URL. That is a demo-sized persistence\n * model — `localStorage` quotas are typically ~5 MB, and a few large\n * images will fill it. Back a real gallery with your own API.\n *\n * Folders, replace, import, usage, frequently-used and quota are `false`:\n * this adapter is a flat list of assets.\n */\nexport function createLocalStorageMediaProvider(\n options: LocalStorageMediaProviderOptions = {},\n): MediaProvider {\n const storageKey = options.key ?? DEFAULT_KEY;\n\n function getStorage(): Storage {\n // Core targets a neutral platform, so `localStorage` isn't guaranteed —\n // fail with an actionable message rather than a bare ReferenceError.\n if (typeof localStorage === \"undefined\") {\n throw new Error(\n \"[Templatical] createLocalStorageMediaProvider requires a browser environment with localStorage. Supply your own MediaProvider for server-side or non-browser use.\",\n );\n }\n return localStorage;\n }\n\n function readAll(): MediaAsset[] {\n const raw = getStorage().getItem(storageKey);\n if (!raw) return [];\n try {\n const parsed: unknown = JSON.parse(raw);\n // Tolerate a corrupted or hand-edited value rather than breaking the\n // editor: treat anything that isn't an array as \"no assets\".\n return Array.isArray(parsed) ? (parsed as MediaAsset[]) : [];\n } catch {\n return [];\n }\n }\n\n function writeAll(assets: MediaAsset[]): void {\n getStorage().setItem(storageKey, JSON.stringify(assets));\n }\n\n function readFileAsDataURL(file: File): Promise<string> {\n if (typeof FileReader === \"undefined\") {\n return Promise.reject(\n new Error(\n \"[Templatical] createLocalStorageMediaProvider requires a browser environment with FileReader.\",\n ),\n );\n }\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (typeof reader.result !== \"string\") {\n reject(new Error(\"[Templatical] Failed to read file as a data URL.\"));\n return;\n }\n resolve(reader.result);\n };\n reader.onerror = () => {\n reject(\n reader.error ??\n new Error(\"[Templatical] Failed to read file as a data URL.\"),\n );\n };\n reader.readAsDataURL(file);\n });\n }\n\n // Every method is `async` so a synchronous failure (a missing\n // `localStorage`, a quota error from `setItem`) surfaces as a rejected\n // promise rather than a sync throw — callers of a `Promise`-returning\n // contract must be able to rely on `.catch()`.\n return {\n async list(params?: MediaListParams): Promise<MediaListPage> {\n const all = readAll();\n const search = params?.search?.trim().toLowerCase();\n const filtered = search\n ? all.filter((asset) => {\n const filename = asset.filename?.toLowerCase() ?? \"\";\n const alt = asset.alt?.toLowerCase() ?? \"\";\n return filename.includes(search) || alt.includes(search);\n })\n : all;\n const parsed = params?.cursor ? Number.parseInt(params.cursor, 10) : 0;\n const offset = Number.isFinite(parsed) && parsed > 0 ? parsed : 0;\n const items = filtered.slice(offset, offset + PAGE_SIZE);\n const page: MediaListPage = { items };\n const nextOffset = offset + items.length;\n if (nextOffset < filtered.length) {\n page.nextCursor = String(nextOffset);\n }\n return page;\n },\n\n async create(input: MediaCreateInput): Promise<MediaAsset> {\n const url = await readFileAsDataURL(input.file);\n const now = new Date().toISOString();\n const created: MediaAsset = {\n id: generateId(),\n url,\n filename: input.filename || input.file.name,\n createdAt: now,\n updatedAt: now,\n size: input.file.size,\n };\n if (input.alt !== undefined) created.alt = input.alt;\n if (input.file.type) created.mimeType = input.file.type;\n if (input.folderId !== undefined) created.folderId = input.folderId;\n // Newest-first, matching the order the composable applies locally after\n // a create — so a reload preserves what the user just saw.\n writeAll([created, ...readAll()]);\n return created;\n },\n\n async update(id: string, patch: MediaAssetPatch): Promise<MediaAsset> {\n const all = readAll();\n const index = all.findIndex((asset) => asset.id === id);\n // Mirrors a REST 404 so provider behavior is consistent across adapters.\n if (index === -1) {\n throw new Error(`[Templatical] Media asset not found: ${id}`);\n }\n const current = all[index];\n const updated: MediaAsset = {\n ...current,\n ...patch,\n id: current.id,\n url: current.url,\n updatedAt: new Date().toISOString(),\n };\n const next = [...all];\n next[index] = updated;\n writeAll(next);\n return updated;\n },\n\n async delete(ids: string[]): Promise<void> {\n const all = readAll();\n for (const id of ids) {\n if (!all.some((asset) => asset.id === id)) {\n throw new Error(`[Templatical] Media asset not found: ${id}`);\n }\n }\n const remove = new Set(ids);\n writeAll(all.filter((asset) => !remove.has(asset.id)));\n },\n\n folders: false,\n replace: false,\n importFromUrl: false,\n checkUsage: false,\n frequentlyUsed: false,\n storage: false,\n };\n}\n","import type {\n Template,\n TemplateContent,\n TemplateVersion,\n VersionHistoryListParams,\n VersionHistoryProvider,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport { computed, ref, type ComputedRef, type Ref } from \"vue\";\nimport { notifyHandler } from \"./error-reporting\";\n\nexport interface UseVersionHistoryOptions {\n /**\n * Storage backend. Supplied by the consumer via `init({ versionHistory })`,\n * or by Cloud's own adapter — this composable is transport-agnostic and never\n * talks to a network itself.\n */\n provider: VersionHistoryProvider;\n /**\n * Which template's history this is. A getter rather than a value because a\n * session can outlive the template: `initCloud()` constructs the feature at\n * setup and only learns the id once `create()` / `load()` resolves.\n */\n getTemplateId: () => string | null;\n onError?: (error: Error) => void;\n}\n\nexport interface UseVersionHistoryReturn {\n /** The provider's list, in the provider's order. Never re-sorted. */\n versions: Ref<TemplateVersion[]>;\n isLoading: Ref<boolean>;\n isRestoring: Ref<boolean>;\n /**\n * Cursor for the page after the one currently held, or `undefined` when the\n * provider signalled there is no more. The editor loads one page and ignores\n * this; it is here so a headless caller can page without reaching past the\n * composable.\n */\n nextCursor: Ref<string | undefined>;\n /** Whether the provider supplied each mutation at all. */\n canCreate: ComputedRef<boolean>;\n canRestore: ComputedRef<boolean>;\n /** Re-read the list. The editor calls this bare; `params` is for headless callers. */\n load: (params?: VersionHistoryListParams) => Promise<void>;\n /**\n * The version's content **if it is already in hand** — the provider's\n * `content` hint, or a previous `get` this composable cached. `null` means a\n * round-trip is required.\n *\n * Callers that must not block (scrubbing through history swaps the canvas in\n * the same tick) check this first and only await {@link resolveContent} when\n * it comes back null.\n */\n peekContent: (version: TemplateVersion) => TemplateContent | null;\n /**\n * `version.content ?? await provider.get(...)`, cached per version id, so a\n * lazily-loaded version costs one round-trip on its first visit and nothing\n * afterwards.\n */\n resolveContent: (version: TemplateVersion) => Promise<TemplateContent>;\n /** Rejects with an {@link SdkError} when the provider disabled `create`. */\n create: (\n content: TemplateContent,\n meta?: { label?: string },\n ) => Promise<TemplateVersion>;\n /** Rejects with an {@link SdkError} when the provider disabled `restore`. */\n restore: (versionId: string) => Promise<Template>;\n}\n\n/**\n * Reactive state over a {@link VersionHistoryProvider}.\n *\n * Owns the list, the loading flags and the per-version content cache that keeps\n * scrubbing synchronous. Errors are reported through `onError` and re-thrown,\n * leaving the list untouched on failure.\n *\n * It deliberately does **not** create versions of its own accord. The editor\n * never records a version on a save — whoever implements `TemplatesProvider.save`\n * decides that, because they are the side that pays for the storage.\n */\nexport function useVersionHistory(\n options: UseVersionHistoryOptions,\n): UseVersionHistoryReturn {\n const { provider, getTemplateId } = options;\n\n const versions = ref<TemplateVersion[]>([]);\n const isLoading = ref(false);\n const nextCursor = ref<string | undefined>(undefined);\n const isRestoring = ref(false);\n\n /**\n * Content fetched through `get`, keyed by version id. A version that carried\n * a `content` hint never lands here — the hint is read straight off the entry,\n * so a provider that eager-loads pays no second copy.\n */\n const fetched = new Map<string, TemplateContent>();\n\n const canCreate = computed(() => typeof provider.create === \"function\");\n const canRestore = computed(() => typeof provider.restore === \"function\");\n\n /**\n * The UI hides disabled actions and renders nothing before a template exists,\n * so reaching one of these means a programmatic caller went around it. Fail\n * loudly rather than silently no-op: a resolved promise reads as \"done\".\n */\n function requireTemplateId(action: string): string {\n const templateId = getTemplateId();\n if (!templateId) {\n throw new SdkError(\n `[Templatical] Version history: ${action} needs a template. Call create() or load() first.`,\n );\n }\n return templateId;\n }\n\n function refuse(action: string): never {\n throw new SdkError(\n `[Templatical] Version history: ${action} is disabled by the provider. Check the capability before calling — the editor's own UI hides the action.`,\n );\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for a version that was recorded or restored.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n async function load(params?: VersionHistoryListParams): Promise<void> {\n const templateId = requireTemplateId(\"list\");\n isLoading.value = true;\n try {\n const page = await provider.list(templateId, params);\n versions.value = page.versions;\n nextCursor.value = page.nextCursor;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n isLoading.value = false;\n }\n }\n\n function peekContent(version: TemplateVersion): TemplateContent | null {\n return version.content ?? fetched.get(version.id) ?? null;\n }\n\n async function resolveContent(\n version: TemplateVersion,\n ): Promise<TemplateContent> {\n const known = peekContent(version);\n if (known) return known;\n\n const templateId = requireTemplateId(\"get\");\n try {\n const content = await provider.get(templateId, version.id);\n fetched.set(version.id, content);\n return content;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function create(\n content: TemplateContent,\n meta?: { label?: string },\n ): Promise<TemplateVersion> {\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") refuse(\"create\");\n const templateId = requireTemplateId(\"create\");\n try {\n const created = await providerCreate(templateId, content, meta);\n versions.value = [created, ...versions.value];\n notify(() => provider.onCreated?.(created));\n return created;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function restore(versionId: string): Promise<Template> {\n const { restore: providerRestore } = provider;\n if (typeof providerRestore !== \"function\") refuse(\"restore\");\n const templateId = requireTemplateId(\"restore\");\n\n let restored: Template;\n isRestoring.value = true;\n try {\n restored = await providerRestore(templateId, versionId);\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n isRestoring.value = false;\n }\n // Below the `finally` on purpose: `isRestoring` is cleared there, so a\n // handler must not observe the composable still reporting itself\n // mid-restore. Unreachable on failure — the catch rethrows.\n notify(() => provider.onRestored?.(restored));\n return restored;\n }\n\n return {\n nextCursor,\n versions,\n isLoading,\n isRestoring,\n canCreate,\n canRestore,\n load,\n peekContent,\n resolveContent,\n create,\n restore,\n };\n}\n","import type {\n Comment,\n CommentAuthor,\n CommentChange,\n CommentInput,\n CommentPatch,\n CommentsListParams,\n CommentsProvider,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport {\n computed,\n onScopeDispose,\n ref,\n watch,\n type ComputedRef,\n type Ref,\n} from \"vue\";\nimport { notifyHandler, wrapReportedError } from \"./error-reporting\";\n\nexport interface UseCommentsOptions {\n /**\n * Storage backend. Supplied by the consumer via `init({ comments })`, or by\n * Cloud's own adapter — this composable is transport-agnostic and never talks\n * to a network itself.\n */\n provider: CommentsProvider;\n /**\n * Which template's conversation this is. A getter rather than a value because a\n * session can outlive the template: `initCloud()` constructs the feature at\n * setup and only learns the id once `create()` / `load()` resolves.\n */\n getTemplateId: () => string | null;\n /**\n * Who is commenting, read at call time. `null` means the editor has no\n * identity, and every mutation refuses — an unattributable comment is worse\n * than no comment feature.\n */\n getUser: () => CommentAuthor | null;\n onError?: (error: Error) => void;\n}\n\nexport interface UseCommentsReturn {\n /** Thread roots in the provider's order. Never re-sorted. */\n comments: Ref<Comment[]>;\n isLoading: Ref<boolean>;\n isSubmitting: Ref<boolean>;\n /** Whether the provider supplied each mutation at all. */\n canCreate: ComputedRef<boolean>;\n canUpdate: ComputedRef<boolean>;\n canDelete: ComputedRef<boolean>;\n canResolve: ComputedRef<boolean>;\n /** Roots plus replies. */\n totalCount: ComputedRef<number>;\n /** Unresolved roots — the badge on the trigger. */\n unresolvedCount: ComputedRef<number>;\n /** Comments per anchored block id, roots plus their replies. */\n commentCountByBlock: ComputedRef<Map<string, number>>;\n /** Re-read the list. The editor calls this bare; `params` is for headless callers. */\n load: (params?: CommentsListParams) => Promise<void>;\n /** Rejects with an {@link SdkError} when `create` is disabled or there is no user. */\n create: (input: CommentInput) => Promise<Comment>;\n /** Rejects with an {@link SdkError} when `update` is disabled or there is no user. */\n update: (commentId: string, patch: CommentPatch) => Promise<Comment>;\n /** Rejects with an {@link SdkError} when `delete` is disabled or there is no user. */\n remove: (commentId: string) => Promise<void>;\n /** Rejects with an {@link SdkError} when `setResolved` is disabled or there is no user. */\n setResolved: (commentId: string, resolved: boolean) => Promise<Comment>;\n /** Look one up by id, root or reply. `null` when it isn't loaded. */\n find: (commentId: string) => Comment | null;\n /** Whether this comment was written by the current user. */\n isOwn: (comment: Comment) => boolean;\n /**\n * Apply a change that arrived from {@link CommentsProvider.subscribe}. Public\n * so a consumer driving this headlessly can push their own transport into it.\n */\n applyRemoteCreate: (comment: Comment) => void;\n applyRemoteUpdate: (comment: Comment) => void;\n applyRemoteDelete: (commentId: string, parentId?: string | null) => void;\n}\n\n/**\n * Reactive state over a {@link CommentsProvider}.\n *\n * Owns the thread list, the loading flags, the local mutations that keep the list\n * consistent, and the derived counts the chrome renders. Errors are reported\n * through `onError` and **re-thrown**, leaving the list untouched on failure —\n * the same discipline as `useSavedBlocks` and `useVersionHistory`.\n *\n * Mutations **reject** when the provider withheld them rather than resolving to\n * `null`: a resolved promise reads as \"saved\" to whoever awaited it. Ask through\n * `canCreate` / `canUpdate` / `canDelete` / `canResolve` first — which is what the\n * editor's own UI does, hiding each action rather than disabling it.\n */\nexport function useComments(options: UseCommentsOptions): UseCommentsReturn {\n const { provider, getTemplateId, getUser } = options;\n\n const comments = ref<Comment[]>([]);\n const isLoading = ref(false);\n const isSubmitting = ref(false);\n\n const canCreate = computed(() => typeof provider.create === \"function\");\n const canUpdate = computed(() => typeof provider.update === \"function\");\n const canDelete = computed(() => typeof provider.delete === \"function\");\n const canResolve = computed(() => typeof provider.setResolved === \"function\");\n\n const totalCount = computed(() =>\n comments.value.reduce(\n (sum, thread) => sum + 1 + (thread.replies?.length ?? 0),\n 0,\n ),\n );\n\n const unresolvedCount = computed(\n () => comments.value.filter((thread) => !thread.resolvedAt).length,\n );\n\n const commentCountByBlock = computed(() => {\n const map = new Map<string, number>();\n for (const thread of comments.value) {\n if (!thread.blockId) continue;\n map.set(\n thread.blockId,\n (map.get(thread.blockId) ?? 0) + 1 + (thread.replies?.length ?? 0),\n );\n }\n return map;\n });\n\n /**\n * The UI hides disabled actions and renders nothing before a template exists,\n * so reaching one of these means a programmatic caller went around it. Fail\n * loudly rather than silently no-op.\n */\n function requireTemplateId(action: string): string {\n const templateId = getTemplateId();\n if (!templateId) {\n throw new SdkError(\n `[Templatical] Comments: ${action} needs a template. Call create() or load() first.`,\n );\n }\n return templateId;\n }\n\n function requireUser(action: string): CommentAuthor {\n const user = getUser();\n if (!user) {\n throw new SdkError(\n `[Templatical] Comments: ${action} needs to know who is commenting. Pass init({ user: { id, name } }) — an unattributed comment is not written.`,\n );\n }\n return user;\n }\n\n function refuse(action: string): never {\n throw new SdkError(\n `[Templatical] Comments: ${action} is disabled by the provider. Check the capability before calling — the editor's own UI hides the action.`,\n );\n }\n\n function find(commentId: string): Comment | null {\n for (const thread of comments.value) {\n if (thread.id === commentId) return thread;\n for (const reply of thread.replies ?? []) {\n if (reply.id === commentId) return reply;\n }\n }\n return null;\n }\n\n function isOwn(comment: Comment): boolean {\n const user = getUser();\n return user !== null && comment.author.id === user.id;\n }\n\n function report(error: unknown): never {\n const wrapped = wrapReportedError(error);\n options.onError?.(wrapped);\n throw wrapped;\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for a comment that was created.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n function replaceAt<T>(list: T[], index: number, next: T): T[] {\n return [...list.slice(0, index), next, ...list.slice(index + 1)];\n }\n\n /**\n * Key-order sensitive: identical fields serialized in a different order\n * compare unequal, so an echo whose transport frame orders keys\n * differently than the mutation response it echoes would not be\n * suppressed. Accepted rather than a defect — not worth a deep-equal\n * dependency for it.\n */\n function sameComment(a: Comment | undefined, b: Comment): boolean {\n return a !== undefined && JSON.stringify(a) === JSON.stringify(b);\n }\n\n /**\n * Insert a comment, replacing an entry with the same id rather than\n * duplicating it, and report whether the list actually changed.\n *\n * The **remote** apply paths (`applyRemoteCreate` / `applyRemoteUpdate`)\n * emit their event only when this returns `true`. A transport's own\n * broadcast can come back to the sender — `CommentsProvider.subscribe`'s\n * contract permits it — and emitting unconditionally fires the handler\n * twice for one comment. The comparison below runs against the **merged**\n * result, never the raw payload: the root branch keeps already-loaded\n * `replies` when an update payload carries none, so comparing the payload\n * directly would report a change for an echo that altered nothing.\n * Deleting the `sameComment` check as a redundant no-op reinstates the\n * double-fire.\n */\n function upsert(comment: Comment): boolean {\n if (comment.parentId) {\n let changed = false;\n const next = comments.value.map((thread) => {\n if (thread.id !== comment.parentId) return thread;\n const replies = thread.replies ?? [];\n const at = replies.findIndex((reply) => reply.id === comment.id);\n if (at !== -1 && sameComment(replies[at], comment)) return thread;\n changed = true;\n return {\n ...thread,\n replies:\n at === -1 ? [...replies, comment] : replaceAt(replies, at, comment),\n };\n });\n // Assigned only when something actually changed: `.map()` always returns\n // a new array, and reassigning `comments.value` for a no-op echo would\n // still invalidate every computed reading it (`commentCountByBlock`\n // returns a new `Map` on each recompute) for nothing.\n if (changed) comments.value = next;\n return changed;\n }\n\n const at = comments.value.findIndex((thread) => thread.id === comment.id);\n if (at === -1) {\n comments.value = [...comments.value, comment];\n return true;\n }\n // A root arriving again keeps the replies already loaded: the update payload\n // for a body edit carries none, and dropping them would empty the thread.\n const next: Comment = {\n ...comment,\n replies: comment.replies ?? comments.value[at].replies,\n };\n if (sameComment(comments.value[at], next)) return false;\n comments.value = replaceAt(comments.value, at, next);\n return true;\n }\n\n function drop(commentId: string, parentId?: string | null): Comment | null {\n const existing = find(commentId);\n // `parentId` from the caller wins so a delete can be applied without the\n // comment being loaded — the subscribe path, on a thread never opened.\n const parent = parentId ?? existing?.parentId ?? null;\n\n if (parent) {\n comments.value = comments.value.map((thread) =>\n thread.id === parent\n ? {\n ...thread,\n replies: (thread.replies ?? []).filter((r) => r.id !== commentId),\n }\n : thread,\n );\n } else {\n comments.value = comments.value.filter((c) => c.id !== commentId);\n }\n return existing;\n }\n\n async function load(params?: CommentsListParams): Promise<void> {\n const templateId = requireTemplateId(\"list\");\n isLoading.value = true;\n try {\n comments.value = await provider.list(templateId, params);\n } catch (error) {\n report(error);\n } finally {\n isLoading.value = false;\n }\n }\n\n async function create(input: CommentInput): Promise<Comment> {\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") refuse(\"create\");\n const templateId = requireTemplateId(\"create\");\n requireUser(\"create\");\n isSubmitting.value = true;\n try {\n const created = await providerCreate(templateId, input);\n upsert(created);\n notify(() => provider.onCreated?.(created, { origin: \"local\" }));\n return created;\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n async function update(\n commentId: string,\n patch: CommentPatch,\n ): Promise<Comment> {\n const { update: providerUpdate } = provider;\n if (typeof providerUpdate !== \"function\") refuse(\"update\");\n const templateId = requireTemplateId(\"update\");\n requireUser(\"update\");\n isSubmitting.value = true;\n try {\n const updated = await providerUpdate(templateId, commentId, patch);\n upsert(updated);\n notify(() => provider.onUpdated?.(updated, { origin: \"local\" }));\n return updated;\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n async function remove(commentId: string): Promise<void> {\n const { delete: providerDelete } = provider;\n if (typeof providerDelete !== \"function\") refuse(\"delete\");\n const templateId = requireTemplateId(\"delete\");\n requireUser(\"delete\");\n // Snapshotted before the request: the event carries what was deleted, and\n // there is nothing to read afterwards.\n const existing = find(commentId);\n isSubmitting.value = true;\n try {\n await providerDelete(templateId, commentId);\n drop(commentId);\n if (existing) {\n notify(() => provider.onDeleted?.(existing, { origin: \"local\" }));\n }\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n async function setResolved(\n commentId: string,\n resolved: boolean,\n ): Promise<Comment> {\n const { setResolved: providerSetResolved } = provider;\n if (typeof providerSetResolved !== \"function\") refuse(\"setResolved\");\n const templateId = requireTemplateId(\"setResolved\");\n requireUser(\"setResolved\");\n isSubmitting.value = true;\n try {\n const updated = await providerSetResolved(\n templateId,\n commentId,\n resolved,\n );\n upsert(updated);\n // The stored result decides which event fires, not the requested state: a\n // store may refuse to reopen, and the consumer should hear what happened.\n notify(() =>\n (updated.resolvedAt ? provider.onResolved : provider.onUnresolved)?.(\n updated,\n { origin: \"local\" },\n ),\n );\n return updated;\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n function applyRemoteCreate(comment: Comment): void {\n // A reply whose parent thread isn't loaded reports nothing: `upsert`\n // finds no matching thread to attach it to, so the list doesn't change.\n if (upsert(comment)) {\n notify(() => provider.onCreated?.(comment, { origin: \"remote\" }));\n }\n }\n\n function applyRemoteUpdate(comment: Comment): void {\n if (!upsert(comment)) return;\n notify(() =>\n (comment.resolvedAt\n ? provider.onResolved\n : // An unresolve and a body edit are both \"not resolved\"; only a payload\n // that used to be resolved is an unresolve, and the transport doesn't\n // say. `updated` is the honest answer for a plain change.\n provider.onUpdated)?.(comment, { origin: \"remote\" }),\n );\n }\n\n function applyRemoteDelete(\n commentId: string,\n parentId?: string | null,\n ): void {\n const existing = drop(commentId, parentId);\n if (existing) {\n notify(() => provider.onDeleted?.(existing, { origin: \"remote\" }));\n }\n }\n\n return {\n comments,\n isLoading,\n isSubmitting,\n canCreate,\n canUpdate,\n canDelete,\n canResolve,\n totalCount,\n unresolvedCount,\n commentCountByBlock,\n load,\n create,\n update,\n remove,\n setResolved,\n find,\n isOwn,\n applyRemoteCreate,\n applyRemoteUpdate,\n applyRemoteDelete,\n };\n}\n\nexport interface UseCommentListenerOptions {\n comments: UseCommentsReturn;\n provider: CommentsProvider;\n getTemplateId: () => string | null;\n}\n\n/**\n * Wire {@link CommentsProvider.subscribe} into the three `applyRemote*` paths.\n *\n * A no-op when the provider has no `subscribe` — realtime is optional, and\n * comments without it work exactly the same except that a colleague's comment\n * only appears on the next read. Nothing here knows about a transport: Cloud's\n * Pusher channel lives inside `createCloudCommentsProvider`, and an SSE or\n * long-poll implementation is the same three lines on the consumer's side.\n *\n * Re-subscribes when the template id changes, and unsubscribes on scope dispose —\n * so a session that loads a second template does not keep receiving the first\n * one's comments.\n */\nexport function useCommentListener(options: UseCommentListenerOptions): void {\n const { comments, provider, getTemplateId } = options;\n const { subscribe } = provider;\n if (typeof subscribe !== \"function\") return;\n\n let unsubscribe: (() => void) | null = null;\n\n function stop(): void {\n unsubscribe?.();\n unsubscribe = null;\n }\n\n watch(\n () => getTemplateId(),\n (templateId) => {\n stop();\n if (!templateId) return;\n unsubscribe = subscribe(templateId, (change: CommentChange) => {\n switch (change.type) {\n case \"created\":\n comments.applyRemoteCreate(change.comment);\n break;\n case \"updated\":\n comments.applyRemoteUpdate(change.comment);\n break;\n case \"deleted\":\n comments.applyRemoteDelete(change.commentId, change.parentId);\n break;\n }\n });\n },\n { immediate: true },\n );\n\n onScopeDispose(stop);\n}\n"],"mappings":";;;;;;;;;;;;AAQA,SAAgB,kBAAkB,OAAuB;CACvD,OAAO,iBAAiB,QACpB,QACA,IAAI,MAAM,OAAO,KAAK,GAAG,EAAE,OAAO,MAAM,CAAC;AAC/C;;;;;;;;;;;AAYA,SAAgB,cACd,SACA,KACM;CACN,IAAI;EACF,IAAI;CACN,SAAS,OAAO;EACd,UAAU,kBAAkB,KAAK,CAAC;CACpC;AACF;;;AC8GA,SAAgB,UAAU,SAA4C;CACpE,MAAM,cACJ,QAAQ,WACR,6BACE,QAAQ,mBACR,QAAQ,gBACV;CAEF,sBAAsB,WAAW;CACjC,yBAAyB,WAAW;CAEpC,MAAM,QAAQ,SAAsB;EAClC,UAAU;EACV,SAAS;EACT,iBAAiB;EACjB,UAAU;EACV,UAAU;EACV,aAAa;EACb,SAAS;EACT,UAAU;EACV,WAAW;EACX,SAAS;CACX,CAAC;;;;;;;CAQD,IAAI,WAAW;CAEf,SAAS,QAAc;EACrB,MAAM,UAAU;EAChB;CACF;CAEA,MAAM,UAAU,SAAS;EACvB,WAAW,MAAM;EACjB,MAAM,UAA2B;GAC/B,MAAM,UAAU;GAChB,MAAM;EACR;CACF,CAAC;CAED,MAAM,gBAAgB,eAAe;EACnC,IAAI,CAAC,MAAM,iBAAiB,OAAO;EACnC,OAAO,cAAc,MAAM,QAAQ,QAAQ,MAAM,eAAe;CAClE,CAAC;CAED,SAAS,cAAc,QAAiB,IAA0B;EAChE,KAAK,MAAM,SAAS,QAAQ;GAC1B,IAAI,MAAM,OAAO,IAAI,OAAO;GAC5B,IAAI,MAAM,SAAS,WACjB,KAAK,MAAM,UAAU,MAAM,UAAU;IACnC,MAAM,QAAQ,cAAc,QAAQ,EAAE;IACtC,IAAI,OAAO,OAAO;GACpB;EAEJ;EACA,OAAO;CACT;CAEA,SAAS,gBAAgB,OAAc,KAAwB;EAC7D,IAAI,IAAI,MAAM,EAAE;EAChB,IAAI,MAAM,SAAS,WACjB,KAAK,MAAM,UAAU,MAAM,UACzB,KAAK,MAAM,SAAS,QAClB,gBAAgB,OAAO,GAAG;CAIlC;CAEA,SAAS,gBACP,QACA,IACA,SAII,EAAE,OAAO,GACyD;EACtE,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;GACtC,MAAM,QAAQ,OAAO;GACrB,IAAI,MAAM,OAAO,IAAI,OAAO;GAC5B,IAAI,MAAM,SAAS,WACjB,KAAK,IAAI,SAAS,GAAG,SAAS,MAAM,SAAS,QAAQ,UAAU;IAC7D,MAAM,SAAS,gBAAgB,MAAM,SAAS,SAAS,IAAI;KACzD,QAAQ,MAAM,SAAS;KACvB,WAAW,MAAM;KACjB,aAAa;IACf,CAAC;IACD,IAAI,QAAQ,OAAO;GACrB;EAEJ;EACA,OAAO;CACT;CAEA,SAAS,cAAc,SAA0B;EAC/C,OAAO,QAAQ,cAAc,MAAM,IAAI,OAAO,KAAK;CACrD;CAEA,SAAS,kBAAkB,SAIlB;EACP,MAAM,SAAS,gBAAgB,MAAM,QAAQ,QAAQ,OAAO;EAC5D,IAAI,CAAC,QAAQ,OAAO;EACpB,MAAM,QAAQ,OAAO,OAAO,WAAW,MAAM,EAAE,OAAO,OAAO;EAC7D,IAAI,UAAU,IAAI,OAAO;EACzB,OAAO;GACL,iBAAiB,OAAO;GACxB,aAAa,OAAO;GACpB;EACF;CACF;CAUA,SAAS,WAAW,YAA6B,YAAY,MAAY;EACvE,sBAAsB,UAAU;EAChC,yBAAyB,UAAU;EACnC,MAAM,UAAU;EAChB,IAAI,WACF,MAAM;CAEV;CAEA,SAAS,YAAY,SAA8B;EACjD,IAAI,WAAW,cAAc,OAAO,GAClC;EAEF,MAAM,kBAAkB;CAC1B;CAEA,SAAS,YAAY,UAA8B;EACjD,MAAM,WAAW;CACnB;CAEA,SAAS,YAAY,UAAyB;EAC5C,MAAM,WAAW;CACnB;CAEA,SAAS,WAAW,OAAsB;EACxC,MAAM,UAAU;CAClB;CAEA,SAAS,eAAe,aAA4B;EAClD,MAAM,cAAc;EACpB,IAAI,aACF,MAAM,kBAAkB;CAE5B;CAEA,SAAS,YAAY,SAAiB,SAA+B;EACnE,IAAI,cAAc,OAAO,GACvB;EAEF,MAAM,QAAQ,cAAc,MAAM,QAAQ,QAAQ,OAAO;EACzD,IAAI,OAAO;GACT,OAAO,OAAO,OAAO,OAAO;GAC5B,MAAM;EACR;CACF;CAEA,SAAS,eAAe,SAA0C;EAChE,MAAM,QAAQ,WAAW;GAAE,GAAG,MAAM,QAAQ;GAAU,GAAG;EAAQ;EACjE,MAAM;CACR;CAEA,SAAS,SACP,OACA,iBACA,cAAc,GACd,OACM;EACN,MAAM,YAAY;GAChB,UAAU,MAAM,QAAQ;GACxB,QAAQ,CAAC,KAAK;EAChB;EACA,sBAAsB,SAAS;EAC/B,yBAAyB,SAAS;EAIlC,IAAI,mBAAmB,MAAM,SAAS,WACpC;EAEF,IAAI,iBAAiB;GACnB,IAAI,cAAc,eAAe,GAC/B;GAEF,MAAM,UAAU,cAAc,MAAM,QAAQ,QAAQ,eAAe;GACnE,IAAI,WAAW,QAAQ,SAAS,WAAW;IACzC,IAAI,cAAc,KAAK,eAAe,eAAe,QAAQ,OAAO,GAClE;IAEF,QAAQ,SAAS,eAAe,QAAQ,SAAS,gBAAgB,CAAC;IAClE,MAAM,cAAc,QAAQ,SAAS;IACrC,IAAI,UAAU,KAAA,KAAa,QAAQ,YAAY,QAC7C,YAAY,OAAO,OAAO,GAAG,KAAK;SAElC,YAAY,KAAK,KAAK;GAE1B;EACF,OACE,IAAI,UAAU,KAAA,KAAa,QAAQ,MAAM,QAAQ,OAAO,QACtD,MAAM,QAAQ,OAAO,OAAO,OAAO,GAAG,KAAK;OAE3C,MAAM,QAAQ,OAAO,KAAK,KAAK;EAGnC,MAAM;CACR;CAEA,SAAS,YAAY,SAAuB;EAC1C,IAAI,cAAc,OAAO,GACvB;EAEF,MAAM,SAAS,gBAAgB,MAAM,QAAQ,QAAQ,OAAO;EAC5D,IAAI,QAAQ;GACV,MAAM,QAAQ,OAAO,OAAO,WAAW,MAAM,EAAE,OAAO,OAAO;GAC7D,IAAI,UAAU,IAAI;IAChB,MAAM,CAAC,WAAW,OAAO,OAAO,OAAO,OAAO,CAAC;IAC/C,IAAI,MAAM,iBAAiB;KACzB,MAAM,6BAAa,IAAI,IAAY;KACnC,gBAAgB,SAAS,UAAU;KACnC,IAAI,WAAW,IAAI,MAAM,eAAe,GACtC,MAAM,kBAAkB;IAE5B;IACA,MAAM;GACR;EACF;CACF;CAEA,SAAS,UACP,SACA,UACA,iBACA,cAAc,GACR;EACN,IAAI,cAAc,OAAO,GACvB;EAEF,IAAI,mBAAmB,cAAc,eAAe,GAClD;EAGF,MAAM,SAAS,gBAAgB,MAAM,QAAQ,QAAQ,OAAO;EAC5D,IAAI,CAAC,QAAQ;EAEb,MAAM,WAAW,OAAO,OAAO,WAAW,MAAM,EAAE,OAAO,OAAO;EAChE,IAAI,aAAa,IAAI;EAIrB,IAAI,mBAAmB,OAAO,OAAO,SAAS,CAAC,SAAS,WACtD;EAKF,IAAI;EACJ,IAAI,iBAAiB;GACnB,MAAM,UAAU,cAAc,MAAM,QAAQ,QAAQ,eAAe;GACnE,IAAI,CAAC,WAAW,QAAQ,SAAS,WAAW;GAC5C,IAAI,cAAc,KAAK,eAAe,eAAe,QAAQ,OAAO,GAClE;GAEF,QAAQ,SAAS,eAAe,QAAQ,SAAS,gBAAgB,CAAC;GAClE,cAAc,QAAQ,SAAS;EACjC,OACE,cAAc,MAAM,QAAQ;EAG9B,MAAM,CAAC,SAAS,OAAO,OAAO,OAAO,UAAU,CAAC;EAChD,YAAY,OAAO,UAAU,GAAG,KAAK;EAErC,MAAM;CACR;CAEA,SAAS,YAAkB;EACzB,MAAM;CACR;;;;;;;;;;;CAgBA,SAAS,OAAO,QAAgB,QAAuB;EACrD,MAAM,IAAI,SAAS,4BAA4B,OAAO,GAAG,QAAQ;CACnE;;;;;;;CAQA,SAAS,OAAO,KAAuB;EACrC,cAAc,QAAQ,SAAS,GAAG;CACpC;CAEA,SAAS,gBAAgB,QAAmC;EAC1D,MAAM,EAAE,cAAc;EACtB,IAAI,CAAC,WACH,OACE,QACA,6FACF;EAEF,OAAO;CACT;CAEA,SAAS,QAAQ,MAAoB;EACnC,IAAI,CAAC,MAAM,UAAU;EACrB,MAAM,WAAW;GAAE,GAAG,MAAM;GAAU;EAAK;EAC3C,MAAM;CACR;CAEA,eAAe,OAAO,OAGA;EACpB,MAAM,WAAW,gBAAgB,UAAU;EAC3C,MAAM,EAAE,QAAQ,mBAAmB;EACnC,IAAI,OAAO,mBAAmB,YAC5B,OACE,YACA,wDACF;EAGF,IAAI;EACJ,MAAM,YAAY;EAClB,IAAI;GACF,IAAI,OAAO,SACT,MAAM,UAAU,MAAM;GAKxB,MAAM,oBAAoB;GAC1B,UAAU,MAAM,eACd,OAAO,SAAS,KAAA,IACZ;IAAE,MAAM,MAAM;IAAM,SAAS,MAAM;GAAQ,IAC3C,EAAE,SAAS,MAAM,QAAQ,CAC/B;GACA,MAAM,WAAW;GACjB,IAAI,aAAa,mBACf,MAAM,UAAU;EAEpB,SAAS,OAAO;GACd,QAAQ,UAAU,KAAc;GAChC,MAAM;EACR,UAAU;GACR,MAAM,YAAY;EACpB;EAIA,aAAa,SAAS,YAAY,OAAO,CAAC;EAC1C,OAAO;CACT;CAEA,eAAe,KAAK,YAAuC;EACzD,MAAM,WAAW,gBAAgB,QAAQ;EAEzC,IAAI;EACJ,MAAM,YAAY;EAClB,IAAI;GACF,SAAS,MAAM,SAAS,KAAK,UAAU;GACvC,sBAAsB,OAAO,OAAO;GACpC,yBAAyB,OAAO,OAAO;GACvC,MAAM,WAAW;GACjB,MAAM,UAAU,OAAO;GACvB,MAAM,UAAU;EAClB,SAAS,OAAO;GACd,QAAQ,UAAU,KAAc;GAChC,MAAM;EACR,UAAU;GACR,MAAM,YAAY;EACpB;EAIA,aAAa,SAAS,WAAW,MAAM,CAAC;EACxC,OAAO;CACT;CAEA,eAAe,KAAK,UAA+B,OAA0B;EAC3E,MAAM,WAAW,gBAAgB,QAAQ;EACzC,MAAM,EAAE,MAAM,iBAAiB;EAC/B,IAAI,OAAO,iBAAiB,YAC1B,OAAO,UAAU,sDAAsD;EAEzE,MAAM,UAAU,MAAM;EACtB,IAAI,CAAC,SACH,OACE,UACA,wDACF;EAKF,MAAM,QACJ,QAAQ,SAAS,KAAA,IACb;GAAE,MAAM,QAAQ;GAAM,SAAS,MAAM;EAAQ,IAC7C,EAAE,SAAS,MAAM,QAAQ;EAE/B,IAAI;EACJ,MAAM,WAAW;EACjB,IAAI;GACF,MAAM,oBAAoB;GAC1B,QAAQ,MAAM,aAAa,QAAQ,IAAI,KAAK;GAI5C,IAAI,MAAM,UAAU,OAAO,QAAQ,IAAI;IAMrC,MAAM,YAAY,MAAM,SAAS;IACjC,MAAM,WACJ,cAAc,QAAQ,OAAO;KAAE,GAAG;KAAO,MAAM;IAAU,IAAI;GACjE;GAKA,IAAI,aAAa,mBACf,MAAM,UAAU;EAEpB,SAAS,OAAO;GACd,QAAQ,UAAU,KAAc;GAChC,MAAM;EACR,UAAU;GACR,MAAM,WAAW;EACnB;EAIA,aAAa,SAAS,UAAU,OAAO,EAAE,QAAQ,CAAC,CAAC;EACnD,OAAO;CACT;CAEA,SAAS,cAAuB;EAC9B,OAAO,MAAM,UAAU,OAAO,KAAA;CAChC;CAEA,OAAO;EACL,OAAO,SAAS,KAAK;EACrB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF;;;ACtmBA,MAAM,iBAAiB;AACvB,MAAM,cAAc;AACpB,MAAM,mBAAmB;AAEzB,SAAgB,WAAW,SAA8C;CACvE,MAAM,EAAE,SAAS,YAAY,UAAU,mBAAmB;CAE1D,MAAM,YAAY,IAAuB,CAAC,CAAC;CAC3C,MAAM,YAAY,IAAuB,CAAC,CAAC;CAC3C,MAAM,eAAe,IAAI,KAAK;CAC9B,IAAI,sBAA4D;CAChE,IAAI,kBAA4C;CAEhD,MAAM,UAAU,eAAe,UAAU,MAAM,SAAS,CAAC;CACzD,MAAM,UAAU,eAAe,UAAU,MAAM,SAAS,CAAC;CAEzD,SAAS,eAAgC;EAMvC,OAAO,UAAU,QAAQ,KAAK;CAChC;CAEA,SAAS,gBAAgB,UAAiC;EACxD,UAAU,MAAM,KAAK,QAAQ;EAC7B,IAAI,UAAU,MAAM,SAAS,SAC3B,UAAU,MAAM,OAAO,GAAG,UAAU,MAAM,SAAS,OAAO;CAE9D;CAEA,SAAS,uBAA6B;EACpC,IAAI,iBAAiB;GACnB,aAAa,gBAAgB,SAAS;GACtC,kBAAkB;EACpB;CACF;CAEA,SAAS,SAAe;EACtB,qBAAqB;EACrB,gBAAgB,aAAa,CAAC;EAC9B,UAAU,QAAQ,CAAC;CACrB;CAEA,SAAS,gBAAgB,SAAuB;EAC9C,IAAI,mBAAmB,gBAAgB,YAAY,SAAS;GAC1D,aAAa,gBAAgB,SAAS;GACtC,gBAAgB,YAAY,iBAAiB;IAC3C,kBAAkB;GACpB,GAAG,WAAW;GACd;EACF;EAEA,qBAAqB;EAErB,gBAAgB,aAAa,CAAC;EAC9B,UAAU,QAAQ,CAAC;EAEnB,kBAAkB;GAChB;GACA,WAAW,iBAAiB;IAC1B,kBAAkB;GACpB,GAAG,WAAW;EAChB;CACF;CAEA,SAAS,gBAAsB;EAC7B,aAAa,QAAQ;EACrB,IAAI,qBACF,aAAa,mBAAmB;EAElC,sBAAsB,iBAAiB;GACrC,aAAa,QAAQ;GACrB,sBAAsB;EACxB,GAAG,gBAAgB;CACrB;CAEA,SAAS,OAAa;EACpB,IAAI,UAAU,MAAM,WAAW,GAC7B;EAGF,qBAAqB;EAErB,MAAM,WAAW,UAAU,MAAM,IAAI;EACrC,UAAU,MAAM,KAAK,aAAa,CAAC;EACnC,WAAW,UAAU,IAAI;EACzB,cAAc;CAChB;CAEA,SAAS,OAAa;EACpB,IAAI,UAAU,MAAM,WAAW,GAC7B;EAGF,qBAAqB;EAErB,MAAM,WAAW,UAAU,MAAM,IAAI;EACrC,UAAU,MAAM,KAAK,aAAa,CAAC;EACnC,WAAW,UAAU,IAAI;EACzB,cAAc;CAChB;CAEA,SAAS,QAAc;EACrB,UAAU,QAAQ,CAAC;EACnB,UAAU,QAAQ,CAAC;EACnB,qBAAqB;CACvB;CAEA,SAAS,UAAgB;EACvB,MAAM;EACN,IAAI,qBAAqB;GACvB,aAAa,mBAAmB;GAChC,sBAAsB;EACxB;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF;;;ACzGA,SAAgB,gBACd,SACuB;CACvB,MAAM,EAAE,UAAU,aAAa,aAAa,aAAa,sBACvD;CAEF,SAAS,kBACP,MACA,iBACA,aACO;EACP,MAAM,WACJ,OAAO,QAAQ,kBAAkB,aAC7B,QAAQ,cAAc,IACtB,QAAQ;EACd,MAAM,QAAQ,YAAY,MAAM,QAAQ;EACxC,SAAS,OAAO,iBAAiB,WAAW;EAC5C,YAAY,MAAM,EAAE;EACpB,OAAO;CACT;CAEA,SAAS,eACP,OACA,iBACA,aACO;EACP,MAAM,SAAS,WAAW,KAAK;EAK/B,IAAI,oBAAoB,KAAA,KAAa,gBAAgB,KAAA,GACnD,SAAS,QAAQ,iBAAiB,WAAW;OACxC;GACL,MAAM,iBAAiB,oBAAoB,MAAM,EAAE,KAAK;GACxD,IAAI,gBACF,SACE,QACA,eAAe,iBACf,eAAe,aACf,eAAe,QAAQ,CACzB;QAEA,SAAS,QAAQ,iBAAiB,WAAW;EAEjD;EACA,YAAY,OAAO,EAAE;EACrB,OAAO;CACT;CAEA,SAAS,YAAY,SAAuB;EAC1C,YAAY,OAAO;CACrB;CAEA,SAAS,oBACP,SACA,KACA,OACM;EACN,YAAY,SAAS,GAAG,MAAM,MAAM,CAAmB;CACzD;CAEA,OAAO;EACL;EACA;EACA;EACA;CACF;AACF;;;;;;;;;;;;;;;;;ACrFA,MAAa,gCAAgC;AAE7C,SAAgB,YAAY,SAAgD;CAC1E,MAAM,EACJ,SACA,SACA,UACA,WAAW,+BACX,UAAU,SACR;CAEJ,IAAI,YAAkD;CACtD,IAAI,SAAS;CAEb,SAAS,YAAqB;EAC5B,OAAO,OAAO,YAAY,aAAa,QAAQ,IAAI;CACrD;CAEA,SAAS,QAAc;EACrB,SAAS;EACT,OAAO;CACT;CAEA,SAAS,SAAe;EACtB,SAAS;CACX;CAEA,SAAS,SAAe;EACtB,IAAI,WAAW;GACb,aAAa,SAAS;GACtB,YAAY;EACd;CACF;CAEA,SAAS,QAAc;EACrB,OAAO;EACP,IAAI,QAAQ,GACV,SAAS,KAAK,MAAM,KAAK,UAAU,QAAQ,KAAK,CAAC,CAAC;CAEtD;CAEA,SAAS,mBAAyB;EAChC,IAAI,CAAC,UAAU,KAAK,QAAQ;EAE5B,OAAO;EACP,YAAY,iBAAiB;GAC3B,YAAY;GACZ,IAAI,UAAU,KAAK,CAAC,UAAU,QAAQ,GACpC,SAAS,KAAK,MAAM,KAAK,UAAU,QAAQ,KAAK,CAAC,CAAC;EAEtD,GAAG,QAAQ;CACb;CASA,MAAM,YAAY,MAChB,eACM;EACJ,iBAAiB;CACnB,GACA,EAAE,MAAM,KAAK,CACf;CAEA,SAAS,UAAgB;EACvB,UAAU;EACV,OAAO;CACT;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF;;;AC9FA,SAAgB,oBACd,QAC2B;CAC3B,MAAM,iBAAiB,yBAAS,IAAI,IAAY,CAAC;CAEjD,MAAM,kBAAkB,eAAe,eAAe,OAAO,CAAC;CAE9D,SAAS,SAAS,SAA0B;EAC1C,OAAO,eAAe,IAAI,OAAO;CACnC;CAEA,SAAS,YAAY,SAAuB;EAC1C,IAAI,eAAe,IAAI,OAAO,GAC5B,eAAe,OAAO,OAAO;OACxB;GACL,eAAe,IAAI,OAAO;GAE1B,IAAI,OAAO,MAAM,oBAAoB,SACnC,OAAO,YAAY,IAAI;EAE3B;CACF;CAEA,SAAS,QAAc;EACrB,eAAe,MAAM;CACvB;CAEA,OAAO;EACL;EACA;EACA;EACA;CACF;AACF;;;AChDA,SAAgB,mBAAmB,SAUjC;CACA,MAAM,aAAa,IAAI,KAAK;CAC5B,MAAM,aAAa,IAAI,KAAK;CAE5B,MAAM,gBAAgB,eAAe,CAAC,CAAC,QAAQ,WAAW,OAAO,UAAU;CAE3E,MAAM,aAAa,eACX,cAAc,SAAS,CAAC,QAAQ,MAAM,MAAM,iBACpD;CAEA,eAAe,QAAuB;EACpC,MAAM,MAAM,QAAQ,WAAW;EAC/B,IAAI,CAAC,KAAK,YACR;EAGF,WAAW,QAAQ;EACnB,WAAW,QAAQ;EAEnB,IAAI;GACF,MAAM,SAAS,MAAM,IAAI,WAAW,QAAQ;IAC1C,aAAa,EAAE,GAAG,QAAQ,MAAM,MAAM,YAAY;IAClD,SAAS,QAAQ,MAAM,MAAM;GAC/B,CAAC;GAED,IAAI,UAAU,MACZ;GAGF,MAAM,SAAS,EAAE,GAAG,QAAQ,MAAM,MAAM,YAAY;GACpD,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAClC,IAAI,OAAO,QACT,OAAO,OAAO,OAAO;GAIzB,QAAQ,SAAS,QAAQ,IAAI;EAC/B,SAAS,OAAO;GACd,QAAQ,KAAK,0CAA0C,KAAK;GAC5D,WAAW,QAAQ;EACrB,UAAU;GACR,WAAW,QAAQ;EACrB;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF;;;;;;;;;;AChCA,SAAgB,sBACd,QACA,SACM;CACN,MAAM,mBAAmB,OAAO;CAChC,MAAM,sBAAsB,OAAO;CACnC,MAAM,oBAAoB,OAAO;CACjC,MAAM,sBAAsB,OAAO;CACnC,MAAM,yBAAyB,OAAO;CAKtC,OAAO,YAAY,OAAO,iBAAkB,aAAc,UAAW;EACnE,IAAI,mBAAmB,OAAO,cAAc,eAAe,GACzD;EAEF,QAAQ,OAAO;EACf,iBAAiB,OAAO,iBAAiB,aAAa,KAAK;CAC7D;CAEA,OAAO,eAAe,YAAY;EAChC,IAAI,OAAO,cAAc,OAAO,GAC9B;EAEF,QAAQ,OAAO;EACf,oBAAoB,OAAO;CAC7B;CAEA,OAAO,aAAa,SAAS,UAAU,iBAAkB,gBAAiB;EACxE,IAAI,OAAO,cAAc,OAAO,GAC9B;EAEF,IAAI,mBAAmB,OAAO,cAAc,eAAe,GACzD;EAEF,QAAQ,OAAO;EACf,kBAAkB,SAAS,UAAU,iBAAiB,WAAW;CACnE;CAEA,OAAO,eAAe,SAAS,YAAY;EACzC,IAAI,OAAO,cAAc,OAAO,GAC9B;EAEF,QAAQ,gBAAgB,OAAO;EAC/B,oBAAoB,SAAS,OAAO;CACtC;CAEA,OAAO,kBAAkB,YAAY;EACnC,QAAQ,OAAO;EACf,uBAAuB,OAAO;CAChC;AACF;;;ACpEA,MAAM,WAAW;AACjB,MAAM,6BAAkC,IAAI,IAAI;CAAC;CAAM;CAAQ;AAAU,CAAC;AAC1E,MAAM,uCAA4C,IAAI,IAAI;CACxD;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,0BAA+B,IAAI,IAAI;AAE7C,SAAS,aAAa,QAA6B;CACjD,OAAO,WAAW,OACd,WACA,KAAK,UAAU,CAAC,OAAO,WAAW,OAAO,MAAM,CAAC;AACtD;AAEA,SAASA,YAAU,OAAyB;CAC1C,MAAM,WAAY,MAAiC;CACnD,OAAO,MAAM,SAAS,aAAa,MAAM,QAAQ,QAAQ,IACpD,WACD,CAAC;AACP;;AAGA,SAAS,YAAY,QAAyC;CAC5D,MAAM,6BAAa,IAAI,IAAuB;CAC9C,MAAM,QAAQ,MAAe,WAA8B;EACzD,KAAK,SAAS,OAAO,UAAU;GAC7B,IAAI,CAAC,WAAW,IAAI,MAAM,EAAE,GAC1B,WAAW,IAAI,MAAM,IAAI;IAAE;IAAO,KAAK,aAAa,MAAM;IAAG;GAAM,CAAC;GAEtE,YAAU,KAAK,CAAC,CAAC,SAAS,QAAQ,gBAAgB;IAChD,IAAI,MAAM,QAAQ,MAAM,GACtB,KAAK,QAAQ;KAAE,WAAW,MAAM;KAAI,QAAQ;IAAY,CAAC;GAE7D,CAAC;EACH,CAAC;CACH;CACA,KAAK,QAAQ,IAAI;CACjB,OAAO;AACT;;;;;AAMA,SAAS,qBAAqB,QAAwC;CACpE,MAAM,QAAkB,CAAC;CACzB,MAAM,WAAqB,IAAI,MAAc,OAAO,MAAM,CAAC,CAAC,KAAK,EAAE;CACnE,OAAO,SAAS,OAAO,aAAa;EAClC,IAAI,MAAM;EACV,IAAI,OAAO,MAAM;EACjB,OAAO,MAAM,MAAM;GACjB,MAAM,SAAU,MAAM,QAAS;GAC/B,IAAI,OAAO,MAAM,WAAW,OAAO,MAAM,SAAS;QAC7C,OAAO;EACd;EACA,IAAI,MAAM,GAAG,SAAS,YAAY,MAAM,MAAM;EAC9C,MAAM,OAAO;CACf,CAAC;CACD,MAAM,sBAAM,IAAI,IAAY;CAC5B,IAAI,WAAW,MAAM,SAAS,IAAI,MAAM,MAAM,SAAS,KAAK;CAC5D,OAAO,aAAa,IAAI;EACtB,IAAI,IAAI,QAAQ;EAChB,WAAW,SAAS;CACtB;CACA,OAAO;AACT;;AAGA,SAAS,WACP,QACA,OACA,MACgC;CAChC,MAAM,MAAY,CAAC;CACnB,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,OAAO,OAAO,KAAK,KAAK,GAAG;EACpC,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,KAAA,GAAW;EAC/C,IAAI,CAAC,UAAU,OAAO,MAAM,MAAM,IAAI,GAAG,IAAI,OAAO,UAAU,MAAM,IAAI;CAC1E;CACA,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAAG;EACrC,IAAI,KAAK,IAAI,GAAG,KAAK,OAAO,SAAS,KAAA,GAAW;EAChD,IAAI,MAAM,SAAS,KAAA,GAAW,MAAM,KAAK,GAAG;CAC9C;CACA,OAAO;EAAE;EAAK;CAAM;AACtB;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,YACd,QACA,OACa;CACb,MAAM,MAAM,YAAY,OAAO,MAAM;CACrC,MAAM,MAAM,YAAY,MAAM,MAAM;CACpC,MAAM,MAAmB,CAAC;CAE1B,MAAM,eAAe,OAAwB;EAC3C,MAAM,OAAO,IAAI,IAAI,EAAE;EACvB,MAAM,WAAW,IAAI,IAAI,EAAE;EAC3B,OACE,SAAS,KAAA,KACT,aAAa,KAAA,KACb,KAAK,MAAM,SAAS,SAAS,MAAM;CAEvC;CACA,MAAM,YAAY,UAChB,IAAI,IAAI,MAAM,EAAE,KAAK,CAAC,YAAY,MAAM,EAAE;CAC5C,MAAM,eAAe,UACnBA,YAAU,KAAK,CAAC,CAAC,MACd,WACC,MAAM,QAAQ,MAAM,KACpB,OAAO,MACJ,UACC,SAAS,KAAK,KAAM,CAAC,YAAY,MAAM,EAAE,KAAK,YAAY,KAAK,CACnE,CACJ;CAKF,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,WAAqB,CAAC;CAE5B,MAAM,SAAS,OAAc,SAAqB;EAChD,IAAI,YAAkB;EACtB,IAAI,SAAS,WAAW;GACtB,KAAK,IAAI,MAAM,EAAE;GACjB,YAAY;EACd,OAAO,IAAI,YAAY,MAAM,EAAE,GAAG;GAChC,IAAI,KAAK;IAAE,IAAI;IAAe,SAAS,MAAM;GAAG,CAAC;GACjD,KAAK,IAAI,MAAM,EAAE;GACjB,YAAY;EACd,OAAO,IAAI,CAAC,IAAI,IAAI,MAAM,EAAE,GAAG;GAC7B,IAAI,SAAS,YACX,YAAY;QACP,IAAI,YAAY,KAAK,GAAG;IAC7B,SAAS,KAAK,MAAM,EAAE;IACtB,YAAY;GACd,OAAO;IACL,IAAI,KAAK;KAAE,IAAI;KAAe,SAAS,MAAM;IAAG,CAAC;IACjD,KAAK,IAAI,MAAM,EAAE;IACjB,YAAY;GACd;EACF;EACA,KAAK,MAAM,UAAUA,YAAU,KAAK,GAClC,IAAI,MAAM,QAAQ,MAAM,GACtB,KAAK,MAAM,SAAS,QAAQ,MAAM,OAAO,SAAS;CAGxD;CACA,KAAK,MAAM,SAAS,OAAO,QAAQ,MAAM,OAAO,MAAM;CAEtD,MAAM,UAAU,OAAwB,IAAI,IAAI,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE;CAInE,MAAM,aAAa,UAAwB;EACzC,MAAM,UAAU,UAAU,KAAK;EAC/B,IAAI,QAAQ,SAAS,WACnB,QAAQ,WAAWA,YAAU,KAAK,CAAC,CAAC,KAAK,WACvC,MAAM,QAAQ,MAAM,IAChB,OACG,QAAQ,UAAU,CAAC,OAAO,MAAM,EAAE,CAAC,CAAC,CACpC,KAAK,UAAU,UAAU,KAAK,CAAC,IAClC,MACN;EAEF,OAAO;CACT;CAIA,MAAM,SACJ,QACA,WACA,sBACS;EACT,MAAM,MAAM,aAAa,MAAM;EAC/B,MAAM,UAAoB,CAAC;EAC3B,MAAM,YAAsB,CAAC;EAC7B,UAAU,SAAS,OAAO,aAAa;GACrC,MAAM,WAAW,IAAI,IAAI,MAAM,EAAE;GACjC,IAAI,OAAO,MAAM,EAAE,KAAK,aAAa,KAAA,KAAa,SAAS,QAAQ,KAAK;IACtE,QAAQ,KAAK,SAAS,KAAK;IAC3B,UAAU,KAAK,QAAQ;GACzB;EACF,CAAC;EACD,MAAM,UAAU,IAAI,IAClB,CAAC,GAAG,qBAAqB,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,UAAU,IAAI,CAChE;EACA,UAAU,SAAS,OAAO,aAAa;GACrC,IAAI,QAAQ,IAAI,QAAQ,KAAK,kBAAkB,KAAK,GAAG;GACvD,MAAM,UAAU,WAAW,IAAI,UAAU,WAAW,EAAE,CAAC,KAAK;GAC5D,IAAI,KACF,OAAO,MAAM,EAAE,IACX;IAAE,IAAI;IAAa,SAAS,MAAM;IAAI;IAAQ,OAAO;GAAQ,IAC7D;IAAE,IAAI;IAAY,OAAO,UAAU,KAAK;IAAG;IAAQ,OAAO;GAAQ,CACxE;EACF,CAAC;CACH;CAGA,MAAM,MAAM,MAAM,cAAc,KAAK;CAErC,MAAM,WAAW,MAAM,OAAO,QAC3B,UAAiC,MAAM,SAAS,SACnD;CACA,MAAM,0BAAU,IAAI,IAAY;CAChC,MAAM,UAAU,SAAwB;EACtC,KAAK,MAAM,WAAW,UAAU;GAC9B,IAAI,CAAC,OAAO,QAAQ,EAAE,GAAG;GACzB,MAAM,YAAYA,YAAU,IAAI,IAAI,QAAQ,EAAE,CAAC,CAAE,KAAK,CAAC,CAAC;GACxD,MAAM,UAAUA,YAAU,OAAO,CAAC,CAAC;GACnC,IAAI,OAAO,UAAU,YAAY,UAAU,WAAW;IACpD,IAAI,KAAK;KACP,IAAI;KACJ,SAAS,QAAQ;KACjB,KAAK,EAAE,SAAS,QAAQ,QAAQ;IAClC,CAAC;IACD,QAAQ,IAAI,QAAQ,EAAE;GACxB;EACF;CACF;CAGA,OAAO,IAAI;CAGX,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,QAAQ,CAAC,OAAO,QAAQ,EAAE;EAChC,YAAU,OAAO,CAAC,CAAC,SAAS,QAAQ,gBAAgB;GAClD,IAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;GAC5B,MACE;IAAE,WAAW,QAAQ;IAAI,QAAQ;GAAY,GAC7C,SACC,UAAU,SAAS,CAAC,OAAO,MAAM,EAAE,CACtC;EACF,CAAC;CACH;CAGA,OAAO,KAAK;CAGZ,KAAK,MAAM,MAAM,UAAU,IAAI,KAAK;EAAE,IAAI;EAAe,SAAS;CAAG,CAAC;CAGtE,KAAK,MAAM,CAAC,IAAI,cAAc,KAAK;EACjC,IAAI,CAAC,OAAO,EAAE,GAAG;EACjB,MAAM,EAAE,KAAK,UAAU,WACrB,IAAI,IAAI,EAAE,CAAC,CAAE,OACb,UAAU,OACV,QAAQ,IAAI,EAAE,IAAI,uBAAuB,UAC3C;EACA,IAAI,OAAO,KAAK,GAAG,CAAC,CAAC,WAAW,KAAK,MAAM,WAAW,GAAG;EACzD,IAAI,KAAK;GACP,IAAI;GACJ,SAAS;GACT,GAAI,OAAO,KAAK,GAAG,CAAC,CAAC,SAAS,IAAI,EAAE,IAAI,IAAI,CAAC;GAC7C,GAAI,MAAM,SAAS,IAAI,EAAE,MAAM,IAAI,CAAC;EACtC,CAAC;CACH;CAEA,MAAM,WAAW,WACf,OAAO,UACP,MAAM,UACN,OACF;CACA,IAAI,OAAO,KAAK,SAAS,GAAG,CAAC,CAAC,SAAS,KAAK,SAAS,MAAM,SAAS,GAClE,IAAI,KAAK;EACP,IAAI;EACJ,GAAI,OAAO,KAAK,SAAS,GAAG,CAAC,CAAC,SAAS,IAAI,EAAE,KAAK,SAAS,IAAI,IAAI,CAAC;EACpE,GAAI,SAAS,MAAM,SAAS,IAAI,EAAE,OAAO,SAAS,MAAM,IAAI,CAAC;CAC/D,CAAC;CAGH,OAAO;AACT;;;ACvSA,SAAS,UAAU,OAAyB;CAC1C,MAAM,WAAY,MAAiC;CACnD,OAAO,MAAM,SAAS,aAAa,MAAM,QAAQ,QAAQ,IACpD,WACD,CAAC;AACP;AAEA,SAAS,WACP,QACA,SACA,SAAsB,MACL;CACjB,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS;EAClD,MAAM,QAAQ,OAAO;EACrB,IAAI,MAAM,OAAO,SACf,OAAO;GAAE;GAAO;GAAQ,OAAO,QAAQ,IAAI,OAAO,QAAQ,EAAE,CAAC,KAAK;EAAK;EAEzE,MAAM,UAAU,UAAU,KAAK;EAC/B,KAAK,IAAI,SAAS,GAAG,SAAS,QAAQ,QAAQ,UAAU;GACtD,IAAI,CAAC,MAAM,QAAQ,QAAQ,OAAO,GAAG;GACrC,MAAM,QAAQ,WAAW,QAAQ,SAAS,SAAS;IACjD,WAAW,MAAM;IACjB;GACF,CAAC;GACD,IAAI,OAAO,OAAO;EACpB;CACF;CACA,OAAO;AACT;AAEA,SAAS,gBACP,OACA,SACA,OACU;CACV,MAAM,WAAW,WAAW,MAAM,QAAQ,OAAO;CACjD,IAAI,CAAC,UACH,MAAM,IAAI,eACR,iBACA,OACA,qBAAqB,QAAQ,KAC7B,OACF;CAEF,OAAO;AACT;;AAGA,SAAS,YACP,SACA,KACA,OAC8C;CAC9C,MAAM,UAAgB,CAAC;CACvB,MAAM,SAAmB,CAAC;CAC1B,MAAM,SAAe,CAAC;CACtB,MAAM,OAAO,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,GAAG,GAAI,SAAS,CAAC,CAAE,CAAC,CAAC;CACvE,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,WAAW,QAAQ;EACzB,IAAI,aAAa,KAAA,GAAW,OAAO,KAAK,GAAG;OACtC,QAAQ,OAAO,UAAU,QAAQ;EACtC,MAAM,OAAO,OAAO,OAAO,MAAM,IAAI,OAAO,KAAA;EAC5C,OAAO,OAAO,SAAS,KAAA,IAAY,OAAO,UAAU,IAAI;CAC1D;CACA,OAAO;EAAE,KAAK;EAAS,OAAO;EAAQ;CAAO;AAC/C;AAEA,SAAS,UACP,OACA,IACA,OACW;CACX,QAAQ,GAAG,IAAX;EACE,KAAK,YACH,OAAO;GACL,IAAI;GACJ,SAAS,GAAG,MAAM;GAClB,QAAQ,EAAE,OAAO,UAAU,GAAG,KAAK,EAAE;EACvC;EACF,KAAK,eAAe;GAClB,MAAM,WAAW,gBAAgB,OAAO,GAAG,SAAS,KAAK;GACzD,OAAO;IACL,IAAI;IACJ,OAAO,UAAU,SAAS,KAAK;IAC/B,QAAQ,SAAS;IACjB,OAAO,SAAS;GAClB;EACF;EACA,KAAK,aAAa;GAChB,MAAM,WAAW,gBAAgB,OAAO,GAAG,SAAS,KAAK;GACzD,OAAO;IACL,IAAI;IACJ,SAAS,GAAG;IACZ,QAAQ,SAAS;IACjB,OAAO,SAAS;IAChB,QAAQ;KAAE,QAAQ,GAAG;KAAQ,OAAO,GAAG;IAAM;GAC/C;EACF;EACA,KAAK,eAAe;GAClB,MAAM,QAAQ,gBAAgB,OAAO,GAAG,SAAS,KAAK,CAAC,CACpD;GACH,MAAM,EAAE,KAAK,OAAO,WAAW,YAAY,OAAO,GAAG,KAAK,GAAG,KAAK;GAClE,OAAO;IACL,IAAI;IACJ,SAAS,GAAG;IACZ,GAAI,OAAO,KAAK,GAAG,CAAC,CAAC,SAAS,IAAI,EAAE,IAAI,IAAI,CAAC;IAC7C,GAAI,MAAM,SAAS,IAAI,EAAE,MAAM,IAAI,CAAC;IACpC;GACF;EACF;EACA,KAAK,kBAAkB;GACrB,MAAM,EAAE,KAAK,OAAO,WAAW,YAC7B,MAAM,UACN,GAAG,KACH,GAAG,KACL;GACA,OAAO;IACL,IAAI;IACJ,GAAI,OAAO,KAAK,GAAG,CAAC,CAAC,SAAS,IAAI,EAAE,IAAI,IAAI,CAAC;IAC7C,GAAI,MAAM,SAAS,IAAI,EAAE,MAAM,IAAI,CAAC;IACpC;GACF;EACF;EACA,KAAK,cACH,OAAO;GACL,IAAI;GACJ,SAAS;IACP,QAAQ,UAAU,MAAM,MAAM;IAC9B,UAAU,UAAU,MAAM,QAAQ;GACpC;EACF;CACJ;AACF;;;;;;;;;;;;;;AAeA,SAAgB,iBACd,QACA,KACa;CACb,MAAM,QAAQ,UAAU,MAAM;CAC9B,MAAM,WAAwB,CAAC;CAC/B,IAAI,SAAS,IAAI,UAAU;EACzB,SAAS,KAAK,UAAU,OAAO,IAAI,KAAK,CAAC;EACzC,IAAI;GACF,uBAAuB,OAAO,CAAC,EAAE,CAAC;EACpC,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,iBAAiB,MAAM;GAE9C,MAAM,IAAI,eAAe,MAAM,MAAM,OAAO,MAAM,SAAS,MAAM,OAAO;EAC1E;CACF,CAAC;CACD,OAAO,SAAS,QAAQ;AAC1B;;AC9IA,MAAa,+BAA+B;;AAE5C,MAAa,8BAA8B;;AAE3C,MAAM,kBAAkB;CAAC;CAAM;CAAM;CAAM;CAAQ;AAAM;;AAEzD,MAAM,mBAAmB;AAEzB,MAAM,gCAAqC,IAAI,IAA+B;CAC5E;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAkJD,SAAS,YAAY,OAAkD;CACrE,MAAM,OAAQ,OAAqC;CACnD,OAAO,OAAO,SAAS,YAAY,cAAc,IAAI,IAAI,IACpD,OACD;AACN;AAEA,SAAS,cAAc,OAA6C;CAClE,MAAM,SAAU,OAAuC;CACvD,OAAO,OAAO,WAAW,YACvB,WAAW,QACX,OAAQ,OAA2B,cAAc,WAC9C,SACD,KAAA;AACN;;AAGA,SAAS,YAAY,OAAyB;CAC5C,MAAM,SAAU,OAA2C;CAC3D,IAAI,OAAO,WAAW,UAAU,OAAO;CACvC,OAAO,UAAU,OAAO,WAAW,OAAO,WAAW;AACvD;AAEA,SAAS,cAAc,QAAiB,SAA0B;CAChE,OAAO,OAAO,MAAM,UAAU;EAC5B,IAAI,MAAM,OAAO,SAAS,OAAO;EACjC,MAAM,WAAY,MAAiC;EACnD,OACE,MAAM,SAAS,aACf,MAAM,QAAQ,QAAQ,KACrB,SAAuB,MACrB,WAAW,MAAM,QAAQ,MAAM,KAAK,cAAc,QAAQ,OAAO,CACpE;CAEJ,CAAC;AACH;;AAGA,SAAS,cAAc,KAAoC;CACzD,IAAI,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC,OAAO,kBAAkB,OAAO;CAC/D,MAAM,QAAQ,IAAI;CAClB,OACE,MAAM,OAAO,iBACb,IAAI,OAAO,OAAO,GAAG,OAAO,iBAAiB,GAAG,YAAY,MAAM,OAAO;AAE7E;;;;;;;;;AAUA,SAAS,qBAAqB,KAAwC;CACpE,OAAO,IAAI,KAAK,OAAO;EACrB,IAAI,EAAE,YAAY,KAAK,OAAO;EAC9B,MAAM,EAAE,QAAQ,SAAS,GAAG,SAAS;EACrC,OAAO;CACT,CAAC;AACH;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,iBACd,SACwB;CACxB,MAAM,EAAE,aAAa;CACrB,MAAM,YAAY,QAAQ,aAAa,OAAO,WAAW;CACzD,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,UACJ,OAAO,SAAS,gBAAgB,cAChC,OAAO,SAAS,cAAc,cAC9B,OAAO,SAAS,gBAAgB;CAElC,MAAM,UAAU,WAA2C,CAAC,CAAC;CAC7D,MAAM,QAAQ,2BAA2C,IAAI,IAAI,CAAC;CAClE,MAAM,aAAa,IAAyC,IAAI;CAChE,MAAM,WAAW,IAAI,CAAC;CACtB,MAAM,iBAAiB,IAAI,CAAC;CAE5B,IAAI,SAAqC;CACzC,IAAI,SAAyB;CAC7B,IAAI,YAAY;CAChB,IAAI,UAAU;CACd,IAAI,gBAAgB;CACpB,IAAI,cAAoD;CACxD,IAAI,cAAc;CAClB,IAAI,YAAY;;CAEhB,MAAM,eAA+B,CAAC;CAGtC,IAAI,SAAyD;CAC7D,IAAI,OAAuD;CAC3D,IAAI,YAA2B,QAAQ,QAAQ;CAC/C,IAAI,aAAoD;CAExD,MAAM,UAAU,eAAe;EAC7B,eAAoB;EACpB,QAAQ,QAAQ,KAAK,UAAU,KAAK;CACtC,CAAC;CACD,MAAM,UAAU,eAAe;EAC7B,eAAoB;EACpB,QAAQ,QAAQ,KAAK,UAAU,KAAK;CACtC,CAAC;CAED,SAAS,YAAY,OAAsB;EACzC,QAAQ,UACN,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAC1D;CACF;CAEA,SAAS,OACP,QACA,UAA0B,QACpB;EACN,IAAI,YAAY,UAAU,WAAW;EACrC,QAAQ,WAAW,MAAM;CAC3B;CAEA,SAAS,cAAgC;EACvC,OAAO;GACL,OAAO,CAAC,GAAG,MAAM,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,UAAU;IAC9C,SAAS,KAAK;IACd,WAAW,KAAK,OAAO;GACzB,EAAE;GACF,SAAS;EACX;CACF;CAEA,SAAS,cAAoB;EAC3B,eAAe;CACjB;CAEA,SAAS,WAAW,OAAuB,OAA2B;EACpE,MAAM,KAAK,KAAK;EAChB,IAAI,MAAM,SAAS,SAAS,MAAM,OAAO,GAAG,MAAM,SAAS,OAAO;CACpE;;CAKA,SAAS,WAAW,MAA6B;EAC/C,IAAI,CAAC,UAAU,WAAW;EAC1B,UAAU;EACV,IAAI;GACF,MAAM,OAAO,OAAO,QAAQ;GAC5B,MAAM,MAAM,YAAY,MAAM,IAAI;GAClC,IAAI,IAAI,SAAS,GACf,IAAI;IACF,uBAAuB,MAAM,GAAG;GAClC,QAAQ;IACN,OAAO,WAAW,UAAU,IAAI,GAAG,KAAK;GAC1C;GAEF,IAAI,CAAC,UAAU,OAAO,QAAQ,OAAO,IAAI,GACvC,OAAO,WAAW,UAAU,IAAI,GAAG,KAAK;EAE5C,UAAU;GACR,UAAU;EACZ;EACA,MAAM,WAAW,OAAO,MAAM;EAC9B,IAAI,YAAY,CAAC,cAAc,KAAK,QAAQ,QAAQ,GAClD,OAAO,YAAY,IAAI;CAE3B;CAIA,SAAS,kBAAwB;EAC/B,IAAI,WAAW,eAAe;EAC9B,gBAAgB;EAChB,qBAAqB;GACnB,gBAAgB;GAChB,QAAQ,KAAK;EACf,CAAC;CACH;CAEA,SAAS,mBAAyB;EAChC,IAAI,aAAa;GACf,aAAa,WAAW;GACxB,cAAc;EAChB;CACF;;;;;;CAOA,SAAS,QAAQ,OAAgB,aAAa,OAAa;EACzD,MAAM,UAAU;EAChB,IAAI,CAAC,UAAU,CAAC,WAAW,aAAa,SAAS;EACjD,IAAI,OAAO,MAAM,aAAa,CAAC,YAAY;EAC3C,IAAI,OAAO,MAAM,UAAU,OAAO,QAAQ,YAAY;EACtD,MAAM,MAAM,YAAY,QAAQ,MAAM,OAAO,QAAQ,KAAK;EAC1D,IAAI,IAAI,WAAW,GAAG;GACpB,iBAAiB;GACjB;EACF;EACA,IAAI,CAAC,SAAS,cAAc,GAAG,GAAG;GAChC,MAAM,MAAM,KAAK,IAAI;GACrB,IAAI,gBAAgB,MAAM,cAAc;GACxC,IAAI,MAAM,cAAA,KAAgD;IACxD,IAAI,aAAa,aAAa,WAAW;IACzC,cAAc,iBAAiB;KAC7B,cAAc;KACd,QAAQ,IAAI;IACd,GAAA,GAAmC;IACnC;GACF;EACF;EACA,iBAAiB;EACjB,OAAO,SAAS,GAAG;CACrB;CAEA,SAAS,QAAc;EACrB,QAAQ,IAAI;CACd;CAEA,SAAS,OAAO,SAAkB,UAA6B;EAC7D,IAAI,MAAM;EACV,IAAI;EACJ,IAAI;GACF,OAAO,gBAAgB,QAAQ,MAAM,KAAK,YAAY,CAAC;EACzD,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,iBAAiB,MAAM;GAE9C,WAAW,QAAQ,IAAI;GACvB,MAAM,SACJ,MAAM,YAAY,KAAA,IACd,KAAA,IACA,MAAM,MAAM,IAAI,MAAM,OAAO,CAAC,EAAE;GACtC,OAAO;IACL,MAAM;IACN,MAAM,MAAM;IACZ,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;GAC7B,CAAC;GACD,IAAI,MAAM,SAAS,qBAAqB,YAAY,KAAK;GACzD;EACF;EACA,MAAM,OAAO,OAAQ,QAAQ;EAC7B,IAAI,CAAC,UAAU,MAAM,IAAI,GAAG;GAG1B,4BACE,IAAI,MACF,iHACF,CACF;GACA,MAAM,CAAC;IAAE,IAAI;IAAc,SAAS,UAAU,IAAI;GAAE,CAAC;GACrD,IAAI;IACF,OAAO,gBAAgB,QAAQ,MAAM,KAAK,YAAY,CAAC;GACzD,SAAS,OAAO;IACd,IAAI,EAAE,iBAAiB,iBAAiB,MAAM;IAC9C,WAAW,QAAQ,IAAI;IACvB,OAAO;KAAE,MAAM;KAAe,MAAM,MAAM;IAAK,CAAC;IAChD;GACF;EACF;EACA,MAAM,QAAsB,EAAE,KAAK,iBAAiB,QAAQ,MAAM,GAAG,EAAE;EACvE,WAAW,QAAQ,MAAM,KAAK;EAC9B,QAAQ,OAAO,CAAC;EAChB,YAAY;EACZ,QAAQ,OAAO;EACf,QAAQ,SAAS,KAAK,QAAQ,KAAK;CACrC;CAEA,SAAS,QACP,SACA,KACA,QACA,OACM;EACN,QAAQ,QAAQ,KAAK;GACnB,SAAS,OAAO,WAAW;GAC3B;GACA;GACA;GACA,MAAM;GACN,UAAU;EACZ,CAAC;EACD,KAAK,OAAO;CACd;CAIA,SAAS,KAAK,SAAwB;EACpC,IAAI,QAAQ,UAAU;EACtB,MAAM,OAAO,QAAQ,QAAQ,MAAM,UAAU,CAAC,MAAM,IAAI;EACxD,IAAI,CAAC,MAAM;GACT,cAAc,OAAO;GACrB;EACF;EACA,KAAK,OAAO;EACZ,QAAQ,WAAW;EACnB,KAAK,SAAS,MAAM,CAAC;CACvB;CAEA,SAAS,KAAK,SAAkB,OAAqB,SAAuB;EAC1E,SACG,OAAO,QAAQ,YAAY;GAC1B,SAAS,MAAM;GACf,cAAc,QAAQ;GACtB,KAAK,MAAM;EACb,CAAC,CAAC,CACD,MACE,EAAE,UAAU,kBAAkB;GAC7B,QAAQ,WAAW;GACnB,WAAW,SAAS,OAAO,WAAW;GACtC,KAAK,OAAO;EACd,IACC,UAAmB;GAClB,IAAI,YAAY,KAAK,MAAM,MAAM;IAC/B,QAAQ,WAAW;IACnB,OAAO,SAAS,OAAO,KAAK;IAC5B,KAAK,OAAO;IACZ;GACF;GACA,IAAI,YAAY,KAAK,GAAG;IAEtB,cAAc,SAAS,KAAK;IAC5B,MAAM,QACJ,gBAAgB,KAAK,IAAI,SAAS,gBAAgB,SAAS,CAAC;IAC9D,iBAAiB;KACf,IAAI,CAAC,WAAW,KAAK,SAAS,OAAO,UAAU,CAAC;IAClD,GAAG,KAAK;IACR;GACF;GACA,QAAQ,WAAW;GACnB,YAAY,KAAK;GACjB,OAAO,SAAS,OAAO,KAAK;GAC5B,KAAK,OAAO;EACd,CACF;CACJ;CAEA,SAAS,WAAW,SAAkB,OAAqB,IAAkB;EAC3E,IAAI,MAAM,aAAa,MAAM;EAC7B,MAAM,WAAW;EACjB,IAAI,MAAM,QAAQ,UAAU;GAE1B,YAAY,SAAS,KAAK;GAC1B,QAAQ,OAAO;GACf;EACF;EACA,QAAQ,IAAI,IAAI,IAAI,KAAK;EACzB,MAAM,OAAO;CACf;CAEA,SAAS,YAAY,SAAkB,OAA2B;EAChE,QAAQ,UAAU,QAAQ,QAAQ,QAAQ,YAAY,YAAY,KAAK;EACvE,cAAc,OAAO;CACvB;CAEA,SAAS,cAAc,SAAkB,OAA2B;EAClE,IAAI,CAAC,MAAM,OAAO;EAClB,KAAK,MAAM,SAAS,CAAC,QAAQ,MAAM,QAAQ,IAAI,GAAG;GAChD,MAAM,WAAW,MAAM,QAAQ,MAAM,KAAK;GAC1C,IAAI,aAAa,IAAI,MAAM,OAAO,UAAU,CAAC;EAC/C;EACA,YAAY;CACd;CAEA,SAAS,OAAO,SAAkB,OAAqB,OAAsB;EAC3E,YAAY,SAAS,KAAK;EAC1B,cAAc,SAAS,KAAK;EAC5B,MAAM,OAAO,YAAY,KAAK,KAAK;EACnC,IAAI,MAAM,WAAW,QAAQ;GAC3B,MAAM,SAAS,cAAc,KAAK;GAClC,OACE;IAAE,MAAM;IAAe;IAAM,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;GAAG,GAC3D,OACF;EACF,OACE,OAAO;GAAE,MAAM;GAAe;EAAK,GAAG,OAAO;EAE/C,QAAQ,OAAO;EACf,IAAI,YAAY,QAAQ,QAAa;CACvC;CAEA,SAAS,cAAc,SAAwB;EAC7C,IAAI,QAAQ,QAAQ,SAAS,KAAK,QAAQ,QAAQ,WAAW,GAAG;EAChE,KAAK,MAAM,UAAU,QAAQ,QAAQ,OAAO,CAAC,GAAG,OAAO,QAAQ;CACjE;CAEA,SAAS,cAAc,SAAkB,OAAsB;EAC7D,MAAM,SAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;EACvE,KAAK,MAAM,UAAU,QAAQ,QAAQ,OAAO,CAAC,GAAG,OAAO,OAAO,MAAM;CACtE;CAIA,SAAS,QAAQ,SAAkB,OAA8B;EAC/D,IAAI,MAAM,YAAY,QAAQ,UAAU;EACxC,MAAM,OAAO,QAAQ,QAAQ,MAC1B,YAAY,QAAQ,YAAY,MAAM,OACzC;EACA,IAAI,MAAM;GACR,WAAW,SAAS,MAAM,MAAM,QAAQ;GACxC;EACF;EACA,QAAQ,OAAO,IAAI,MAAM,UAAU,KAAK;EACxC,MAAM,OAAO;CACf;;;;;;CAOA,SAAS,SAAS,SAAkB,OAAqC;EACvE,IAAI,MAA4B,MAAM;EACtC,IAAI,QAAQ,2BACV,IAAI;GACF,MAAM,QAAQ,0BAA0B,MAAM,KAAK,QAAQ,SAAS;EACtE,SAAS,OAAO;GACd,YAAY,KAAK;EACnB;EAEF,OAAO,qBAAqB,GAAG;CACjC;;CAGA,SAAS,MAAM,SAAwB;EACrC,MAAM,UAA6B,CAAC;EACpC,SAAS;GACP,MAAM,KAAK,QAAQ,WAAW;GAC9B,MAAM,SAAS,QAAQ,OAAO,IAAI,EAAE;GACpC,MAAM,MAAM,QAAQ,IAAI,IAAI,EAAE;GAC9B,IAAI,CAAC,UAAU,CAAC,KAAK;GACrB,IAAI;IACF,QAAQ,YAAY,gBAClB,QAAQ,WACR,SAAS,SAAS,SAAS,MAAM,IAAI,IAAK,GAC5C;GACF,QAAQ;IACN,IAAI,QAAQ,YAAY;KAGtB,QAAQ,OAAO,MAAM;KACrB,QAAQ,IAAI,MAAM;KAClB,4BACE,IAAI,MACF,gGACF,CACF;KACA;IACF;IAEA,OAAY,OAAO;IACnB;GACF;GACA,QAAQ,WAAW;GACnB,IAAI,QAAQ;IACV,QAAQ,OAAO,OAAO,EAAE;IACxB,QAAQ,KAAK,MAAM;GACrB,OAAO;IACL,QAAQ,IAAI,OAAO,EAAE;IACrB,YAAY,SAAS,GAAI;GAC3B;EACF;EACA,IAAI,YAAY,QAAQ,SAAS,QAAQ,QAAQ;EACjD,IAAI,QAAQ,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,GAAG,YAAY,OAAO;OACnE,cAAc,OAAO;EAE1B,IAAI,QAAQ,WAAW,GAAG;EAC1B,QAAQ,OAAO;EACf,IAAI,YAAY,QAAQ;EACxB,KAAK,MAAM,SAAS,SAClB,IAAI;GACF,QAAQ,iBAAiB;IAAE,OAAO,MAAM;IAAO,KAAK,MAAM;GAAI,CAAC;EACjE,SAAS,OAAO;GACd,YAAY,KAAK;EACnB;CAEJ;;CAGA,SAAS,QAAQ,SAAwB;EAGvC,IAAI,YAAY,QAAQ,MAAM;EAC9B,IAAI,OAAO,QAAQ;EACnB,MAAM,OAAuB,CAAC;EAC9B,KAAK,MAAM,SAAS,QAAQ,SAC1B,IAAI;GACF,OAAO,gBAAgB,MAAM,MAAM,GAAG;GACtC,KAAK,KAAK,KAAK;EACjB,SAAS,OAAO;GACd,IAAI,MAAM,MAAM;IAEd,KAAK,KAAK,KAAK;IACf;GACF;GACA,cAAc,SAAS,KAAK;GAC5B,MAAM,OACJ,iBAAiB,iBAAiB,MAAM,OAAO;GACjD,OACE,MAAM,WAAW,SACb;IAAE,MAAM;IAAe;GAAK,IAC5B;IAAE,MAAM;IAAe;GAAK,GAChC,OACF;EACF;EAEF,QAAQ,UAAU;EAClB,QAAQ,OAAO;EACf,cAAc,OAAO;EACrB,IAAI,YAAY,QAAQ,WAAW,IAAI;CACzC;CAEA,SAAS,YAAY,SAAwB;EAC3C,IAAI,QAAQ,UAAU;EACtB,QAAQ,WAAW,iBAAiB;GAClC,QAAQ,WAAW;GACnB,IAAI,YAAY,QAAQ,QAAa;EACvC,GAAG,4BAA4B;CACjC;CAEA,SAAS,cAAc,SAAwB;EAC7C,IAAI,QAAQ,UAAU;GACpB,aAAa,QAAQ,QAAQ;GAC7B,QAAQ,WAAW;EACrB;CACF;CAEA,SAAS,UAAyB;EAChC,MAAM,UAAU;EAChB,IAAI,CAAC,SAAS,OAAO,QAAQ,QAAQ;EACrC,IAAI,QAAQ,YAAY,OAAO,QAAQ;EACvC,QAAQ,cAAc,YAAY;GAChC,IAAI;IACF,MAAM,SAAS,MAAM,SAAS,gBAC5B,QAAQ,YACR,QAAQ,QACV;IACA,IAAI,QAAQ,SAAS;IACrB,IAAI,YAAY,QAAQ;KACtB,MAAM,OAAO,OAAO;KACpB;IACF;IACA,KAAK,MAAM,SAAS,OAAO,SAAS,QAAQ,SAAS,KAAK;GAC5D,SAAS,OAAO;IACd,YAAY,KAAK;GACnB,UAAU;IACR,QAAQ,aAAa;GACvB;EACF,EAAA,CAAG;EACH,OAAO,QAAQ;CACjB;CAEA,eAAe,OAAO,SAAiC;EACrD,QAAQ,aAAa;EACrB,IAAI;GACF,MAAM,WAAW,MAAM,QAAQ,aAAa,QAAQ,UAAU;GAC9D,IAAI,QAAQ,SAAS;GACrB,MAAM,KAAK,SAAS,YAAY,QAAQ;GACxC,QAAQ,YAAY,UAAU,SAAS,OAAO;GAC9C,QAAQ,WAAW;GACnB,QAAQ,SAAS,IAAI,IACnB,CAAC,GAAG,QAAQ,MAAM,CAAC,CAAC,QAAQ,CAAC,cAAc,WAAW,EAAE,CAC1D;GACA,KAAK,MAAM,CAAC,OAAO,UAAU,CAAC,GAAG,QAAQ,GAAG,GAC1C,IAAI,SAAS,IAAI;IACf,QAAQ,IAAI,OAAO,KAAK;IACxB,YAAY,SAAS,KAAK;GAC5B;GAEF,IAAI,YAAY,QAAQ,SAAS,QAAQ;GACzC,QAAQ,OAAO;GACf,MAAM,OAAO;EACf,SAAS,OAAO;GACd,YAAY,KAAK;EACnB,UAAU;GACR,QAAQ,aAAa;EACvB;CACF;CAIA,SAAS,YAAY,SAAyC;EAC5D,MAAM,aAAsB,YAAY,UAAU,CAAC,QAAQ;EAC3D,OAAO;GACL,UAAU,UAAU;IAClB,IAAI,KAAK,GAAG,QAAQ,SAAS,KAAK;GACpC;GACA,WAAW,IAAI,UAAU;IACvB,IAAI,CAAC,KAAK,KAAK,MAAM,QAAQ,UAAU;IACvC,IAAI,OACF,IAAI;KACF,QAAQ,iBAAiB;MAAE,UAAU;MAAI;KAAM,CAAC;IAClD,SAAS,OAAO;KACd,YAAY,KAAK;IACnB;IAEF,QAAa;GACf;GACA,SAAS,SAAS;IAChB,IAAI,KAAK,GAAG,QAAQ,IAAI;GAC1B;GACA,WAAW,SAAS,kBAAkB;IACpC,IAAI,KAAK,GAAG,WAAW,SAAS,aAAa;GAC/C;GACA,YAAY,SAAS;IACnB,IAAI,KAAK,GAAG,WAAW,IAAI;GAC7B;GACA,qBAAqB,UAAU;IAC7B,IAAI,KAAK,GAAG,iBAAiB,SAAS,KAAK;GAC7C;EACF;CACF;CAEA,SAAS,KAAK,SAAkB,SAAwB;EACtD,SAAS,KAAK,QAAQ,YAAY,EAAE,UAAU,GAAG,YAAY,OAAO,CAAC,CAAC,CAAC,MACpE,WAAW;GACV,IAAI,QAAQ,WAAW,YAAY,QAAQ;IACzC,IAAI,CAAC,SAAS,OAAO,MAAM;IAC3B;GACF;GACA,QAAQ,QAAQ,OAAO;GACvB,aAAa,OAAO,OAAO,CAAC,OAAO;GACnC,WAAW,OAAO,OAAO;GACzB,IAAI,OAAO,QAAQ,SAAS,GAAG,WAAW,QAAQ;GAClD,IAAI,OAAO,WAAW,QAAQ,UAAU,QAAa;GACrD,IAAI,SAAS,cAAc;EAC7B,IACC,UAAmB,YAAY,KAAK,CACvC;CACF;CAEA,SAAS,MAAM,UAAuC;EACpD,MAAM,UAAU,UAAU,SAAS,OAAO;EAC1C,MAAM,UAAmB;GACvB,YAAY,SAAS;GACrB,WAAW;GACX,UAAU,SAAS,YAAY;GAC/B,SAAS,CAAC;GACV,MAAM,UAAU,OAAO;GACvB,wBAAQ,IAAI,IAAI;GAChB,qBAAK,IAAI,IAAI;GACb,UAAU;GACV,UAAU;GACV,YAAY;GACZ,OAAO;GACP,SAAS;GACT,YAAY;GACZ,MAAM,CAAC;GACP,MAAM,CAAC;GACP,SAAS,CAAC;EACZ;EACA,SAAS;EACT,SAAS,QAAQ,QAAQ;EACzB,YAAY;EACZ,KAAK,SAAS,KAAK;EACnB,IAAI,QAAQ,MAAM,iBAChB,YAAY,OAAO,MAAM,eAAe;EAC1C,gBAAgB;CAClB;CAEA,SAAS,OAAO,SAAwB;EACtC,QAAQ,UAAU;EAClB,cAAc,OAAO;EACrB,QAAQ,QAAQ;EAChB,QAAQ,QAAQ;EAChB,cAAc,yBAAS,IAAI,MAAM,wCAAwC,CAAC;CAC5E;CAEA,SAAS,iBACP,SACA,OACM;EACN,MAAM,WAAW,WAAW;EAC5B,WAAW,QAAQ;EACnB,IACE,UAAU,eACV,aAAa,QACb,aAAa,aAGb,KAAK,SAAS,IAAI;CAEtB;CAEA,SAAS,SAAS,UAA8C;EAC9D,IAAI,UAAU,OAAO,eAAe,UAAU,IAAI;EAClD,IAAI,QAAQ;GACV,iBAAiB;GACjB,OAAO,MAAM;GACb,SAAS;EACX;EACA,QAAQ,QAAQ,CAAC;EACjB,MAAM,wBAAQ,IAAI,IAAI;EACtB,WAAW,QAAQ;EACnB,SAAS;EACT,cAAc;EACd,YAAY;EACZ,IAAI,UAAU,MAAM,QAAQ;CAC9B;CAIA,SAAS,QAAQ,MAAuB;EACtC,IAAI,KAAK,OAAO,cAAc,WAAW;EACzC,MAAM,OAAO,IAAI,IACf,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,QACd,GAAG,cAAc,SAAS,OAAO,cAAc,KAAK,OAAO,SAC9D,CACF;EACA,KAAK,IAAI,KAAK,SAAS,IAAI;EAC3B,MAAM,QAAQ;EACd,IAAI,QAAQ,MAAM,oBAAoB,KAAK,SAAS;GAClD,OAAO,YAAY,IAAI;GACvB,OAAO;IACL,MAAM;IACN,SAAS,KAAK;IACd,QAAQ,KAAK;GACf,CAAC;EACH;CACF;CAEA,SAAS,WAAW,SAAiB,eAA6B;EAChE,IAAI,MAAM,MAAM,IAAI,OAAO,CAAC,EAAE,OAAO,cAAc,eAAe;EAClE,MAAM,OAAO,IAAI,IAAI,MAAM,KAAK;EAChC,KAAK,OAAO,OAAO;EACnB,MAAM,QAAQ;CAChB;CAEA,SAAS,aAAa,MAA4B,OAAsB;EACtE,MAAM,OAAO,QAAQ,IAAI,IAAI,MAAM,KAAK,oBAAI,IAAI,IAAuB;EACvE,KAAK,MAAM,QAAQ,MAAM;GACvB,IAAI,KAAK,OAAO,cAAc,WAAW;GACzC,IAAI,SAAS,KAAK,IAAI,KAAK,OAAO,GAAG;GACrC,KAAK,IAAI,KAAK,SAAS,IAAI;EAC7B;EACA,MAAM,QAAQ;CAChB;CAEA,SAAS,WAAW,MAA4C;EAC9D,MAAM,UAAU,IAAI,IAAI,KAAK,KAAK,WAAW,OAAO,SAAS,CAAC;EAC9D,MAAM,WAAW,IAAI,IACnB,QAAQ,MACL,KAAK,WAAW,OAAO,SAAS,CAAC,CACjC,QAAQ,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CACpC;EACA,QAAQ,QAAQ;EAChB,IAAI,SAAS,SAAS,GAAG;EACzB,MAAM,YAAY,IAAI,IACpB,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,QACd,GAAG,UAAU,CAAC,SAAS,IAAI,KAAK,OAAO,SAAS,CACnD,CACF;EACA,IAAI,UAAU,SAAS,MAAM,MAAM,MAAM,MAAM,QAAQ;CACzD;CAEA,SAAS,UAAU,MAAiC;EAClD,YAAY,UAAU,KAAK,IAAI,CAAC,CAAC,MAAM,WAAW;CACpD;;CAGA,SAAS,gBAAsB;EAC7B,IAAI,CAAC,SAAS;EACd,UAAU,YAAY;GACpB,IAAI,SAAS,WAAW,QAAQ,KAAK,eAAe,OAAO,aAAa;IAGtE,MAAM,YAAY;IAClB,OAAO;IACP,aAAa;IACb,MAAM,SAAS,YAAa,UAAU,YAAY,UAAU,OAAO;GACrE;GACA,MAAM,SAAS;GACf,IACE,CAAC,UACA,MAAM,eAAe,OAAO,cAC3B,KAAK,YAAY,OAAO,SAE1B;GAEF,IAAI;IACF,MAAM,SAAS,YAAa,OAAO,YAAY,OAAO,OAAO;IAC7D,OAAO;IACP,cAAc;IACd,WAAW,OAAO,OAAO;GAC3B,SAAS,OAAO;IACd,IAAI,MAAM,eAAe,OAAO,YAAY;KAE1C,MAAM,YAAY;KAClB,OAAO;KACP,aAAa;KACb,MAAM,SAAS,YACb,UAAU,YACV,UAAU,OACZ,CAAC,CAAC,YAAY,CAAC,CAAC;IAClB;IACA,IAAI,WAAW,QAAQ;IACvB,gBAAgB,OAAO,SAAS,KAAK;GACvC;EACF,CAAC;CACH;CAEA,SAAS,WAAW,SAAuB;EACzC,IAAI,CAAC,MAAM,MAAM,IAAI,OAAO,GAAG;EAC/B,MAAM,OAAO,IAAI,IAAI,MAAM,KAAK;EAChC,KAAK,OAAO,OAAO;EACnB,MAAM,QAAQ;CAChB;CAEA,SAAS,gBAAgB,SAAiB,OAAsB;EAC9D,MAAM,OAAO,YAAY,KAAK;EAC9B,IAAI,SAAS,eAAe,SAAS,iBAAiB;GACpD,SAAS;GACT,MAAM,SAAS,cAAc,KAAK;GAClC,IAAI,QAEF,QAAQ;IAAE;IAAS;IAAQ,WAAW;GAAG,CAAC;QACrC,IAAI,QAAQ,MAAM,oBAAoB,SAC3C,OAAO,YAAY,IAAI;GAEzB;EACF;EAEA,YAAY,KAAK;EACjB,iBAAiB;GACf,IAAI,QAAQ,YAAY,SAAS,cAAc;EACjD,GAAG,gBAAgB,EAAE;CACvB;CAEA,SAAS,gBAAsB;EAC7B,IAAI,YAAY;EAChB,aAAa,kBAAkB;GAC7B,UAAU,YAAY;IACpB,MAAM,UAAU;IAChB,IAAI,CAAC,SAAS;IACd,IAAI;KACF,MAAM,SAAS,UAAW,QAAQ,YAAY,QAAQ,OAAO;IAC/D,SAAS,OAAO;KACd,IAAI,YAAY,KAAK,MAAM,aAAa;MACtC,OAAO;MACP,aAAa;MACb,cAAc;MACd;KACF;KACA,YAAY,KAAK;IACnB;GACF,CAAC;EACH,GAAG,2BAA2B;CAChC;CAEA,SAAS,eAAqB;EAC5B,IAAI,YAAY;GACd,cAAc,UAAU;GACxB,aAAa;EACf;CACF;CAEA,SAAS,YAAY,SAA8B;EACjD,MAAM;EACN,IAAI,CAAC,QAAQ;EACb,SAAS,UAAU;GAAE,YAAY,OAAO;GAAY;EAAQ,IAAI;EAChE,cAAc;CAChB;;CAGA,SAAS,aAAmB;EAC1B,MAAM;EACN,IAAI,QAAQ,SAAS;GACnB,SAAc,YAAa,KAAK,YAAY,KAAK,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;GACxE,OAAO;GACP,aAAa;EACf;EACA,QAAQ,QAAQ;EAChB,IAAI,QAAQ,OAAO,QAAQ;CAC7B;CAIA,SAAS,KAAK,WAAkC;EAC9C,MAAM,UAAU;EAChB,IAAI,CAAC,WAAW,WAAW;EAC3B,MAAM;EACN,MAAM,SAAS,cAAc,SAAS,QAAQ,OAAO,QAAQ,KAAA,CAAM,IAAI;EACvE,IAAI,CAAC,OAAO;EACZ,YAAY;EACZ,IAAI;EACJ,IAAI;GACF,OAAO,gBAAgB,QAAQ,MAAM,MAAM,KAAK,YAAY,CAAC;EAC/D,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,iBAAiB,MAAM;GAC9C,OAAO;IAAE,MAAM;IAAe,MAAM,MAAM;GAAK,CAAC;GAChD;EACF;EACA,MAAM,UAAwB,EAC5B,KAAK,iBAAiB,QAAQ,MAAM,MAAM,GAAG,EAC/C;EACA,WAAW,cAAc,SAAS,QAAQ,OAAO,QAAQ,MAAM,OAAO;EACtE,QAAQ,OAAO;EACf,WAAW,IAAI;EACf,QAAQ,UAAU;EAClB,QAAQ,SAAS,MAAM,KAAK,WAAW,OAAO;CAChD;CAIA,SAAS,OAAO,QAAmC;EACjD,SAAS;EACT,aAAa,KACX,YACQ,OAAO,MAAM,WAClB,aAAa,SAAS,QAAQ,GAC/B,EACE,WAAW,KACb,CACF,GACA,YAAY,OAAO,QAAQ,OAAO,iBAAiB,EAAE,MAAM,KAAK,CAAC,GACjE,YAAY,OAAO,MAAM,iBAAiB,WAAW,GACrD,YACQ,OAAO,MAAM,YAClB,YAAa,UAAU,QAAQ,MAAM,IAAI,IAAI,gBAAgB,CAChE,CACF;EACA,IAAI,OAAO,WAAW,aAAa;GACjC,OAAO,iBAAiB,YAAY,UAAU;GAC9C,OAAO,iBAAiB,QAAQ,KAAK;EACvC;EACA,IAAI,gBAAgB,GAAG,eAAe,OAAO;CAC/C;CAEA,SAAS,SAAwB;EAC/B,MAAM;EACN,MAAM,UAAU;EAChB,IAAI,CAAC,WAAW,QAAQ,QAAQ,WAAW,GAAG,OAAO,QAAQ,QAAQ;EACrE,OAAO,IAAI,SAAS,SAAS,WAC3B,QAAQ,QAAQ,KAAK;GAAE;GAAS;EAAO,CAAC,CAC1C;CACF;CAEA,SAAS,UAAgB;EACvB,MAAM;EACN,YAAY;CACd;CAEA,SAAS,SAAe;EACtB,YAAY;EACZ,IAAI,QAAQ,WAAW,OAAO,IAAI;CACpC;CAEA,SAAS,UAAgB;EACvB,IAAI,WAAW;EACf,MAAM;EACN,KAAK,MAAM,QAAQ,aAAa,OAAO,CAAC,GAAG,KAAK;EAChD,iBAAiB;EACjB,WAAW;EACX,YAAY;EACZ,IAAI,QAAQ,OAAO,MAAM;EACzB,SAAS;EACT,aAAa;EACb,IAAI,OAAO,WAAW,aAAa;GACjC,OAAO,oBAAoB,YAAY,UAAU;GACjD,OAAO,oBAAoB,QAAQ,KAAK;EAC1C;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YAAY,KAAK,MAAM;EACvB,YAAY,KAAK,MAAM;EACvB,oBAAoB;GAClB,IAAI,CAAC,QAAQ;GACb,OAAO,OAAO,CAAC;GACf,OAAO,OAAO,CAAC;GACf,YAAY;EACd;EACA;EACA;EACA;EACA;EACA;EACA,oBAAoB,YAClB,SAAS,cAAc,OAAO,UAAU,QAAQ,OAAO,IAAI;EAC7D;CACF;AACF;;;;;;;;;;;AC9mCA,SAAgB,eACd,SACsB;CACtB,MAAM,EAAE,aAAa;CAErB,MAAM,cAAcC,MAAkB,CAAC,CAAC;CACxC,MAAM,YAAYA,MAAI,KAAK;CAE3B,MAAM,aAAaC,iBAAe;EAChC,MAAM,uBAAO,IAAI,IAAY;EAC7B,KAAK,MAAM,SAAS,YAAY,OAAO;GACrC,MAAM,WAAW,MAAM,UAAU,KAAK;GACtC,IAAI,UAAU,KAAK,IAAI,QAAQ;EACjC;EACA,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;CACpD,CAAC;CAED,MAAM,YAAYA,iBAAe,OAAO,SAAS,WAAW,UAAU;CACtE,MAAM,YAAYA,iBAAe,OAAO,SAAS,WAAW,UAAU;CACtE,MAAM,YAAYA,iBAAe,OAAO,SAAS,WAAW,UAAU;;CAGtE,SAAS,eAAe,OAA4B;EAClD,OAAO,UAAU,SAAS,MAAM,cAAc;CAChD;CAEA,SAAS,eAAe,OAA4B;EAClD,OAAO,UAAU,SAAS,MAAM,cAAc;CAChD;;;;;;CAOA,SAAS,OAAO,QAAgB,QAAuB;EACrD,MAAM,IAAI,SACR,+BAA+B,OAAO,MAAM,OAAO,8EACrD;CACF;;;;;;;CAQA,SAAS,OAAO,KAAuB;EACrC,cAAc,QAAQ,SAAS,GAAG;CACpC;CAEA,eAAe,KAAK,QAA+C;EACjE,UAAU,QAAQ;EAClB,IAAI;GACF,YAAY,QAAQ,MAAM,SAAS,KAAK,MAAM;EAChD,SAAS,OAAO;GACd,QAAQ,UAAU,KAAc;GAChC,MAAM;EACR,UAAU;GACR,UAAU,QAAQ;EACpB;CACF;CAEA,eAAe,OACb,MACA,SACA,UACqB;EACrB,MAAM,EAAE,QAAQ,mBAAmB;EACnC,IAAI,OAAO,mBAAmB,YAC5B,OAAO,UAAU,0BAA0B;EAE7C,IAAI;GAIF,MAAM,UAAU,MAAM,eACpB,WAAW;IAAE;IAAM;IAAS;GAAS,IAAI;IAAE;IAAM;GAAQ,CAC3D;GACA,YAAY,QAAQ,CAAC,SAAS,GAAG,YAAY,KAAK;GAClD,aAAa,SAAS,YAAY,OAAO,CAAC;GAC1C,OAAO;EACT,SAAS,OAAO;GACd,QAAQ,UAAU,KAAc;GAChC,MAAM;EACR;CACF;CAEA,eAAe,OACb,IACA,OACqB;EACrB,MAAM,EAAE,QAAQ,mBAAmB;EACnC,IAAI,OAAO,mBAAmB,YAC5B,OAAO,UAAU,0BAA0B;EAK7C,MAAM,QAAQ,YAAY,MAAM,MAAM,MAAM,EAAE,OAAO,EAAE;EACvD,IAAI,SAAS,MAAM,cAAc,OAC/B,OAAO,UAAU,4BAA4B,GAAG,EAAE;EAEpD,IAAI;GACF,MAAM,UAAU,MAAM,eAAe,IAAI,KAAK;GAC9C,YAAY,QAAQ,YAAY,MAAM,KAAK,MACzC,EAAE,OAAO,KAAK,UAAU,CAC1B;GACA,aAAa,SAAS,YAAY,OAAO,CAAC;GAC1C,OAAO;EACT,SAAS,OAAO;GACd,QAAQ,UAAU,KAAc;GAChC,MAAM;EACR;CACF;CAEA,eAAe,OAAO,IAA2B;EAC/C,MAAM,EAAE,QAAQ,mBAAmB;EACnC,IAAI,OAAO,mBAAmB,YAC5B,OAAO,UAAU,0BAA0B;EAK7C,MAAM,UAAU,YAAY,MAAM,MAAM,MAAM,EAAE,OAAO,EAAE,KAAK;EAC9D,IAAI,WAAW,QAAQ,cAAc,OACnC,OAAO,UAAU,4BAA4B,GAAG,EAAE;EAEpD,IAAI;GACF,MAAM,eAAe,EAAE;GACvB,YAAY,QAAQ,YAAY,MAAM,QAAQ,MAAM,EAAE,OAAO,EAAE;GAC/D,IAAI,SACF,aAAa,SAAS,YAAY,OAAO,CAAC;EAE9C,SAAS,OAAO;GACd,QAAQ,UAAU,KAAc;GAChC,MAAM;EACR;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF;;;ACzNA,MAAMC,gBAAc;;;;;;;;;;;;;AAcpB,SAAgB,sCACd,UAA0C,CAAC,GACtB;CACrB,MAAM,aAAa,QAAQ,OAAOA;CAElC,SAAS,aAAsB;EAG7B,IAAI,OAAO,iBAAiB,aAC1B,MAAM,IAAI,MACR,+KACF;EAEF,OAAO;CACT;CAEA,SAAS,UAAwB;EAC/B,MAAM,MAAM,WAAW,CAAC,CAAC,QAAQ,UAAU;EAC3C,IAAI,CAAC,KAAK,OAAO,CAAC;EAClB,IAAI;GACF,MAAM,SAAkB,KAAK,MAAM,GAAG;GAGtC,OAAO,MAAM,QAAQ,MAAM,IAAK,SAA0B,CAAC;EAC7D,QAAQ;GACN,OAAO,CAAC;EACV;CACF;CAEA,SAAS,SAAS,QAA4B;EAC5C,WAAW,CAAC,CAAC,QAAQ,YAAY,KAAK,UAAU,MAAM,CAAC;CACzD;CAMA,OAAO;EAGL,MAAM,KAAK,QAAuD;GAChE,MAAM,MAAM,QAAQ;GACpB,MAAM,SAAS,QAAQ,QAAQ,KAAK,CAAC,CAAC,YAAY;GAClD,MAAM,WAAW,QAAQ,UAAU,KAAK;GACxC,OAAO,IAAI,QAAQ,MAAM;IACvB,IAAI,UAAU,CAAC,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,OAAO;IAC7D,IAAI,YAAY,EAAE,aAAa,UAAU,OAAO;IAChD,OAAO;GACT,CAAC;EACH;EAIA,MAAM,OAAO,OAA6C;GACxD,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;GACnC,MAAM,UAAsB;IAC1B,IAAI,WAAW;IACf,MAAM,MAAM;IACZ,SAAS,MAAM;IACf,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;IACrD,WAAW;IACX,WAAW;GACb;GAGA,SAAS,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC;GAChC,OAAO;EACT;EAEA,MAAM,OAAO,IAAY,OAA6C;GACpE,MAAM,MAAM,QAAQ;GACpB,MAAM,QAAQ,IAAI,WAAW,MAAM,EAAE,OAAO,EAAE;GAE9C,IAAI,UAAU,IACZ,MAAM,IAAI,MAAM,wCAAwC,IAAI;GAE9D,MAAM,UAAsB;IAC1B,GAAG,IAAI;IACP,GAAG;IACH,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;GACpC;GACA,MAAM,OAAO,CAAC,GAAG,GAAG;GACpB,KAAK,SAAS;GACd,SAAS,IAAI;GACb,OAAO;EACT;EAEA,MAAM,OAAO,IAA2B;GACtC,MAAM,MAAM,QAAQ;GACpB,IAAI,CAAC,IAAI,MAAM,MAAM,EAAE,OAAO,EAAE,GAC9B,MAAM,IAAI,MAAM,wCAAwC,IAAI;GAE9D,SAAS,IAAI,QAAQ,MAAM,EAAE,OAAO,EAAE,CAAC;EACzC;CACF;AACF;;;AC5GA,MAAM,cAAc;AACpB,MAAM,YAAY;;;;;;;;;;;;;;;AAgBlB,SAAgB,gCACd,UAA4C,CAAC,GAC9B;CACf,MAAM,aAAa,QAAQ,OAAO;CAElC,SAAS,aAAsB;EAG7B,IAAI,OAAO,iBAAiB,aAC1B,MAAM,IAAI,MACR,mKACF;EAEF,OAAO;CACT;CAEA,SAAS,UAAwB;EAC/B,MAAM,MAAM,WAAW,CAAC,CAAC,QAAQ,UAAU;EAC3C,IAAI,CAAC,KAAK,OAAO,CAAC;EAClB,IAAI;GACF,MAAM,SAAkB,KAAK,MAAM,GAAG;GAGtC,OAAO,MAAM,QAAQ,MAAM,IAAK,SAA0B,CAAC;EAC7D,QAAQ;GACN,OAAO,CAAC;EACV;CACF;CAEA,SAAS,SAAS,QAA4B;EAC5C,WAAW,CAAC,CAAC,QAAQ,YAAY,KAAK,UAAU,MAAM,CAAC;CACzD;CAEA,SAAS,kBAAkB,MAA6B;EACtD,IAAI,OAAO,eAAe,aACxB,OAAO,QAAQ,uBACb,IAAI,MACF,+FACF,CACF;EAEF,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,SAAS,IAAI,WAAW;GAC9B,OAAO,eAAe;IACpB,IAAI,OAAO,OAAO,WAAW,UAAU;KACrC,uBAAO,IAAI,MAAM,kDAAkD,CAAC;KACpE;IACF;IACA,QAAQ,OAAO,MAAM;GACvB;GACA,OAAO,gBAAgB;IACrB,OACE,OAAO,yBACL,IAAI,MAAM,kDAAkD,CAChE;GACF;GACA,OAAO,cAAc,IAAI;EAC3B,CAAC;CACH;CAMA,OAAO;EACL,MAAM,KAAK,QAAkD;GAC3D,MAAM,MAAM,QAAQ;GACpB,MAAM,SAAS,QAAQ,QAAQ,KAAK,CAAC,CAAC,YAAY;GAClD,MAAM,WAAW,SACb,IAAI,QAAQ,UAAU;IACpB,MAAM,WAAW,MAAM,UAAU,YAAY,KAAK;IAClD,MAAM,MAAM,MAAM,KAAK,YAAY,KAAK;IACxC,OAAO,SAAS,SAAS,MAAM,KAAK,IAAI,SAAS,MAAM;GACzD,CAAC,IACD;GACJ,MAAM,SAAS,QAAQ,SAAS,OAAO,SAAS,OAAO,QAAQ,EAAE,IAAI;GACrE,MAAM,SAAS,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;GAChE,MAAM,QAAQ,SAAS,MAAM,QAAQ,SAAS,SAAS;GACvD,MAAM,OAAsB,EAAE,MAAM;GACpC,MAAM,aAAa,SAAS,MAAM;GAClC,IAAI,aAAa,SAAS,QACxB,KAAK,aAAa,OAAO,UAAU;GAErC,OAAO;EACT;EAEA,MAAM,OAAO,OAA8C;GACzD,MAAM,MAAM,MAAM,kBAAkB,MAAM,IAAI;GAC9C,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;GACnC,MAAM,UAAsB;IAC1B,IAAI,WAAW;IACf;IACA,UAAU,MAAM,YAAY,MAAM,KAAK;IACvC,WAAW;IACX,WAAW;IACX,MAAM,MAAM,KAAK;GACnB;GACA,IAAI,MAAM,QAAQ,KAAA,GAAW,QAAQ,MAAM,MAAM;GACjD,IAAI,MAAM,KAAK,MAAM,QAAQ,WAAW,MAAM,KAAK;GACnD,IAAI,MAAM,aAAa,KAAA,GAAW,QAAQ,WAAW,MAAM;GAG3D,SAAS,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC;GAChC,OAAO;EACT;EAEA,MAAM,OAAO,IAAY,OAA6C;GACpE,MAAM,MAAM,QAAQ;GACpB,MAAM,QAAQ,IAAI,WAAW,UAAU,MAAM,OAAO,EAAE;GAEtD,IAAI,UAAU,IACZ,MAAM,IAAI,MAAM,wCAAwC,IAAI;GAE9D,MAAM,UAAU,IAAI;GACpB,MAAM,UAAsB;IAC1B,GAAG;IACH,GAAG;IACH,IAAI,QAAQ;IACZ,KAAK,QAAQ;IACb,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;GACpC;GACA,MAAM,OAAO,CAAC,GAAG,GAAG;GACpB,KAAK,SAAS;GACd,SAAS,IAAI;GACb,OAAO;EACT;EAEA,MAAM,OAAO,KAA8B;GACzC,MAAM,MAAM,QAAQ;GACpB,KAAK,MAAM,MAAM,KACf,IAAI,CAAC,IAAI,MAAM,UAAU,MAAM,OAAO,EAAE,GACtC,MAAM,IAAI,MAAM,wCAAwC,IAAI;GAGhE,MAAM,SAAS,IAAI,IAAI,GAAG;GAC1B,SAAS,IAAI,QAAQ,UAAU,CAAC,OAAO,IAAI,MAAM,EAAE,CAAC,CAAC;EACvD;EAEA,SAAS;EACT,SAAS;EACT,eAAe;EACf,YAAY;EACZ,gBAAgB;EAChB,SAAS;CACX;AACF;;;;;;;;;;;;;;ACrGA,SAAgB,kBACd,SACyB;CACzB,MAAM,EAAE,UAAU,kBAAkB;CAEpC,MAAM,WAAWC,MAAuB,CAAC,CAAC;CAC1C,MAAM,YAAYA,MAAI,KAAK;CAC3B,MAAM,aAAaA,MAAwB,KAAA,CAAS;CACpD,MAAM,cAAcA,MAAI,KAAK;;;;;;CAO7B,MAAM,0BAAU,IAAI,IAA6B;CAEjD,MAAM,YAAYC,iBAAe,OAAO,SAAS,WAAW,UAAU;CACtE,MAAM,aAAaA,iBAAe,OAAO,SAAS,YAAY,UAAU;;;;;;CAOxE,SAAS,kBAAkB,QAAwB;EACjD,MAAM,aAAa,cAAc;EACjC,IAAI,CAAC,YACH,MAAM,IAAI,SACR,kCAAkC,OAAO,kDAC3C;EAEF,OAAO;CACT;CAEA,SAAS,OAAO,QAAuB;EACrC,MAAM,IAAI,SACR,kCAAkC,OAAO,0GAC3C;CACF;;;;;;;CAQA,SAAS,OAAO,KAAuB;EACrC,cAAc,QAAQ,SAAS,GAAG;CACpC;CAEA,eAAe,KAAK,QAAkD;EACpE,MAAM,aAAa,kBAAkB,MAAM;EAC3C,UAAU,QAAQ;EAClB,IAAI;GACF,MAAM,OAAO,MAAM,SAAS,KAAK,YAAY,MAAM;GACnD,SAAS,QAAQ,KAAK;GACtB,WAAW,QAAQ,KAAK;EAC1B,SAAS,OAAO;GACd,QAAQ,UAAU,KAAc;GAChC,MAAM;EACR,UAAU;GACR,UAAU,QAAQ;EACpB;CACF;CAEA,SAAS,YAAY,SAAkD;EACrE,OAAO,QAAQ,WAAW,QAAQ,IAAI,QAAQ,EAAE,KAAK;CACvD;CAEA,eAAe,eACb,SAC0B;EAC1B,MAAM,QAAQ,YAAY,OAAO;EACjC,IAAI,OAAO,OAAO;EAElB,MAAM,aAAa,kBAAkB,KAAK;EAC1C,IAAI;GACF,MAAM,UAAU,MAAM,SAAS,IAAI,YAAY,QAAQ,EAAE;GACzD,QAAQ,IAAI,QAAQ,IAAI,OAAO;GAC/B,OAAO;EACT,SAAS,OAAO;GACd,QAAQ,UAAU,KAAc;GAChC,MAAM;EACR;CACF;CAEA,eAAe,OACb,SACA,MAC0B;EAC1B,MAAM,EAAE,QAAQ,mBAAmB;EACnC,IAAI,OAAO,mBAAmB,YAAY,OAAO,QAAQ;EACzD,MAAM,aAAa,kBAAkB,QAAQ;EAC7C,IAAI;GACF,MAAM,UAAU,MAAM,eAAe,YAAY,SAAS,IAAI;GAC9D,SAAS,QAAQ,CAAC,SAAS,GAAG,SAAS,KAAK;GAC5C,aAAa,SAAS,YAAY,OAAO,CAAC;GAC1C,OAAO;EACT,SAAS,OAAO;GACd,QAAQ,UAAU,KAAc;GAChC,MAAM;EACR;CACF;CAEA,eAAe,QAAQ,WAAsC;EAC3D,MAAM,EAAE,SAAS,oBAAoB;EACrC,IAAI,OAAO,oBAAoB,YAAY,OAAO,SAAS;EAC3D,MAAM,aAAa,kBAAkB,SAAS;EAE9C,IAAI;EACJ,YAAY,QAAQ;EACpB,IAAI;GACF,WAAW,MAAM,gBAAgB,YAAY,SAAS;EACxD,SAAS,OAAO;GACd,QAAQ,UAAU,KAAc;GAChC,MAAM;EACR,UAAU;GACR,YAAY,QAAQ;EACtB;EAIA,aAAa,SAAS,aAAa,QAAQ,CAAC;EAC5C,OAAO;CACT;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF;;;;;;;;;;;;;;;;AC9HA,SAAgB,YAAY,SAAgD;CAC1E,MAAM,EAAE,UAAU,eAAe,YAAY;CAE7C,MAAM,WAAWC,MAAe,CAAC,CAAC;CAClC,MAAM,YAAYA,MAAI,KAAK;CAC3B,MAAM,eAAeA,MAAI,KAAK;CAE9B,MAAM,YAAYC,iBAAe,OAAO,SAAS,WAAW,UAAU;CACtE,MAAM,YAAYA,iBAAe,OAAO,SAAS,WAAW,UAAU;CACtE,MAAM,YAAYA,iBAAe,OAAO,SAAS,WAAW,UAAU;CACtE,MAAM,aAAaA,iBAAe,OAAO,SAAS,gBAAgB,UAAU;CAE5E,MAAM,aAAaA,iBACjB,SAAS,MAAM,QACZ,KAAK,WAAW,MAAM,KAAK,OAAO,SAAS,UAAU,IACtD,CACF,CACF;CAEA,MAAM,kBAAkBA,iBAChB,SAAS,MAAM,QAAQ,WAAW,CAAC,OAAO,UAAU,CAAC,CAAC,MAC9D;CAEA,MAAM,sBAAsBA,iBAAe;EACzC,MAAM,sBAAM,IAAI,IAAoB;EACpC,KAAK,MAAM,UAAU,SAAS,OAAO;GACnC,IAAI,CAAC,OAAO,SAAS;GACrB,IAAI,IACF,OAAO,UACN,IAAI,IAAI,OAAO,OAAO,KAAK,KAAK,KAAK,OAAO,SAAS,UAAU,EAClE;EACF;EACA,OAAO;CACT,CAAC;;;;;;CAOD,SAAS,kBAAkB,QAAwB;EACjD,MAAM,aAAa,cAAc;EACjC,IAAI,CAAC,YACH,MAAM,IAAI,SACR,2BAA2B,OAAO,kDACpC;EAEF,OAAO;CACT;CAEA,SAAS,YAAY,QAA+B;EAClD,MAAM,OAAO,QAAQ;EACrB,IAAI,CAAC,MACH,MAAM,IAAI,SACR,2BAA2B,OAAO,8GACpC;EAEF,OAAO;CACT;CAEA,SAAS,OAAO,QAAuB;EACrC,MAAM,IAAI,SACR,2BAA2B,OAAO,0GACpC;CACF;CAEA,SAAS,KAAK,WAAmC;EAC/C,KAAK,MAAM,UAAU,SAAS,OAAO;GACnC,IAAI,OAAO,OAAO,WAAW,OAAO;GACpC,KAAK,MAAM,SAAS,OAAO,WAAW,CAAC,GACrC,IAAI,MAAM,OAAO,WAAW,OAAO;EAEvC;EACA,OAAO;CACT;CAEA,SAAS,MAAM,SAA2B;EACxC,MAAM,OAAO,QAAQ;EACrB,OAAO,SAAS,QAAQ,QAAQ,OAAO,OAAO,KAAK;CACrD;CAEA,SAAS,OAAO,OAAuB;EACrC,MAAM,UAAU,kBAAkB,KAAK;EACvC,QAAQ,UAAU,OAAO;EACzB,MAAM;CACR;;;;;;;CAQA,SAAS,OAAO,KAAuB;EACrC,cAAc,QAAQ,SAAS,GAAG;CACpC;CAEA,SAAS,UAAa,MAAW,OAAe,MAAc;EAC5D,OAAO;GAAC,GAAG,KAAK,MAAM,GAAG,KAAK;GAAG;GAAM,GAAG,KAAK,MAAM,QAAQ,CAAC;EAAC;CACjE;;;;;;;;CASA,SAAS,YAAY,GAAwB,GAAqB;EAChE,OAAO,MAAM,KAAA,KAAa,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;CAClE;;;;;;;;;;;;;;;;CAiBA,SAAS,OAAO,SAA2B;EACzC,IAAI,QAAQ,UAAU;GACpB,IAAI,UAAU;GACd,MAAM,OAAO,SAAS,MAAM,KAAK,WAAW;IAC1C,IAAI,OAAO,OAAO,QAAQ,UAAU,OAAO;IAC3C,MAAM,UAAU,OAAO,WAAW,CAAC;IACnC,MAAM,KAAK,QAAQ,WAAW,UAAU,MAAM,OAAO,QAAQ,EAAE;IAC/D,IAAI,OAAO,MAAM,YAAY,QAAQ,KAAK,OAAO,GAAG,OAAO;IAC3D,UAAU;IACV,OAAO;KACL,GAAG;KACH,SACE,OAAO,KAAK,CAAC,GAAG,SAAS,OAAO,IAAI,UAAU,SAAS,IAAI,OAAO;IACtE;GACF,CAAC;GAKD,IAAI,SAAS,SAAS,QAAQ;GAC9B,OAAO;EACT;EAEA,MAAM,KAAK,SAAS,MAAM,WAAW,WAAW,OAAO,OAAO,QAAQ,EAAE;EACxE,IAAI,OAAO,IAAI;GACb,SAAS,QAAQ,CAAC,GAAG,SAAS,OAAO,OAAO;GAC5C,OAAO;EACT;EAGA,MAAM,OAAgB;GACpB,GAAG;GACH,SAAS,QAAQ,WAAW,SAAS,MAAM,GAAG,CAAC;EACjD;EACA,IAAI,YAAY,SAAS,MAAM,KAAK,IAAI,GAAG,OAAO;EAClD,SAAS,QAAQ,UAAU,SAAS,OAAO,IAAI,IAAI;EACnD,OAAO;CACT;CAEA,SAAS,KAAK,WAAmB,UAA0C;EACzE,MAAM,WAAW,KAAK,SAAS;EAG/B,MAAM,SAAS,YAAY,UAAU,YAAY;EAEjD,IAAI,QACF,SAAS,QAAQ,SAAS,MAAM,KAAK,WACnC,OAAO,OAAO,SACV;GACE,GAAG;GACH,UAAU,OAAO,WAAW,CAAC,EAAA,CAAG,QAAQ,MAAM,EAAE,OAAO,SAAS;EAClE,IACA,MACN;OAEA,SAAS,QAAQ,SAAS,MAAM,QAAQ,MAAM,EAAE,OAAO,SAAS;EAElE,OAAO;CACT;CAEA,eAAe,KAAK,QAA4C;EAC9D,MAAM,aAAa,kBAAkB,MAAM;EAC3C,UAAU,QAAQ;EAClB,IAAI;GACF,SAAS,QAAQ,MAAM,SAAS,KAAK,YAAY,MAAM;EACzD,SAAS,OAAO;GACd,OAAO,KAAK;EACd,UAAU;GACR,UAAU,QAAQ;EACpB;CACF;CAEA,eAAe,OAAO,OAAuC;EAC3D,MAAM,EAAE,QAAQ,mBAAmB;EACnC,IAAI,OAAO,mBAAmB,YAAY,OAAO,QAAQ;EACzD,MAAM,aAAa,kBAAkB,QAAQ;EAC7C,YAAY,QAAQ;EACpB,aAAa,QAAQ;EACrB,IAAI;GACF,MAAM,UAAU,MAAM,eAAe,YAAY,KAAK;GACtD,OAAO,OAAO;GACd,aAAa,SAAS,YAAY,SAAS,EAAE,QAAQ,QAAQ,CAAC,CAAC;GAC/D,OAAO;EACT,SAAS,OAAO;GACd,OAAO,KAAK;EACd,UAAU;GACR,aAAa,QAAQ;EACvB;CACF;CAEA,eAAe,OACb,WACA,OACkB;EAClB,MAAM,EAAE,QAAQ,mBAAmB;EACnC,IAAI,OAAO,mBAAmB,YAAY,OAAO,QAAQ;EACzD,MAAM,aAAa,kBAAkB,QAAQ;EAC7C,YAAY,QAAQ;EACpB,aAAa,QAAQ;EACrB,IAAI;GACF,MAAM,UAAU,MAAM,eAAe,YAAY,WAAW,KAAK;GACjE,OAAO,OAAO;GACd,aAAa,SAAS,YAAY,SAAS,EAAE,QAAQ,QAAQ,CAAC,CAAC;GAC/D,OAAO;EACT,SAAS,OAAO;GACd,OAAO,KAAK;EACd,UAAU;GACR,aAAa,QAAQ;EACvB;CACF;CAEA,eAAe,OAAO,WAAkC;EACtD,MAAM,EAAE,QAAQ,mBAAmB;EACnC,IAAI,OAAO,mBAAmB,YAAY,OAAO,QAAQ;EACzD,MAAM,aAAa,kBAAkB,QAAQ;EAC7C,YAAY,QAAQ;EAGpB,MAAM,WAAW,KAAK,SAAS;EAC/B,aAAa,QAAQ;EACrB,IAAI;GACF,MAAM,eAAe,YAAY,SAAS;GAC1C,KAAK,SAAS;GACd,IAAI,UACF,aAAa,SAAS,YAAY,UAAU,EAAE,QAAQ,QAAQ,CAAC,CAAC;EAEpE,SAAS,OAAO;GACd,OAAO,KAAK;EACd,UAAU;GACR,aAAa,QAAQ;EACvB;CACF;CAEA,eAAe,YACb,WACA,UACkB;EAClB,MAAM,EAAE,aAAa,wBAAwB;EAC7C,IAAI,OAAO,wBAAwB,YAAY,OAAO,aAAa;EACnE,MAAM,aAAa,kBAAkB,aAAa;EAClD,YAAY,aAAa;EACzB,aAAa,QAAQ;EACrB,IAAI;GACF,MAAM,UAAU,MAAM,oBACpB,YACA,WACA,QACF;GACA,OAAO,OAAO;GAGd,cACG,QAAQ,aAAa,SAAS,aAAa,SAAS,aAAA,GACnD,SACA,EAAE,QAAQ,QAAQ,CACpB,CACF;GACA,OAAO;EACT,SAAS,OAAO;GACd,OAAO,KAAK;EACd,UAAU;GACR,aAAa,QAAQ;EACvB;CACF;CAEA,SAAS,kBAAkB,SAAwB;EAGjD,IAAI,OAAO,OAAO,GAChB,aAAa,SAAS,YAAY,SAAS,EAAE,QAAQ,SAAS,CAAC,CAAC;CAEpE;CAEA,SAAS,kBAAkB,SAAwB;EACjD,IAAI,CAAC,OAAO,OAAO,GAAG;EACtB,cACG,QAAQ,aACL,SAAS,aAIT,SAAS,UAAA,GAAa,SAAS,EAAE,QAAQ,SAAS,CAAC,CACzD;CACF;CAEA,SAAS,kBACP,WACA,UACM;EACN,MAAM,WAAW,KAAK,WAAW,QAAQ;EACzC,IAAI,UACF,aAAa,SAAS,YAAY,UAAU,EAAE,QAAQ,SAAS,CAAC,CAAC;CAErE;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF;;;;;;;;;;;;;;AAqBA,SAAgB,mBAAmB,SAA0C;CAC3E,MAAM,EAAE,UAAU,UAAU,kBAAkB;CAC9C,MAAM,EAAE,cAAc;CACtB,IAAI,OAAO,cAAc,YAAY;CAErC,IAAI,cAAmC;CAEvC,SAAS,OAAa;EACpB,cAAc;EACd,cAAc;CAChB;CAEA,cACQ,cAAc,IACnB,eAAe;EACd,KAAK;EACL,IAAI,CAAC,YAAY;EACjB,cAAc,UAAU,aAAa,WAA0B;GAC7D,QAAQ,OAAO,MAAf;IACE,KAAK;KACH,SAAS,kBAAkB,OAAO,OAAO;KACzC;IACF,KAAK;KACH,SAAS,kBAAkB,OAAO,OAAO;KACzC;IACF,KAAK,WACH,SAAS,kBAAkB,OAAO,WAAW,OAAO,QAAQ;GAEhE;EACF,CAAC;CACH,GACA,EAAE,WAAW,KAAK,CACpB;CAEA,iBAAe,IAAI;AACrB"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["invertOperations","ref","computed","DEFAULT_KEY","ref","computed","ref","computed"],"sources":["../src/error-reporting.ts","../src/editor.ts","../src/history.ts","../src/block-actions.ts","../src/auto-save.ts","../src/condition-preview.ts","../src/data-source-fetch.ts","../src/history-interceptor.ts","../src/diff-content.ts","../src/collaboration.ts","../src/saved-blocks.ts","../src/saved-blocks-local.ts","../src/media-local.ts","../src/version-history.ts","../src/comments.ts"],"sourcesContent":["/**\n * Turn a thrown value into an `Error`.\n *\n * A `catch` clause's binding is `unknown` — a provider or a consumer's event\n * handler can throw a string, an object, or anything else. `onError` callbacks\n * across this package are typed to take an `Error`, so this is what makes that\n * contract honest at the one place values actually cross it.\n */\nexport function wrapReportedError(error: unknown): Error {\n return error instanceof Error\n ? error\n : new Error(String(error), { cause: error });\n}\n\n/**\n * Run a consumer's event handler without letting it fail the operation it\n * followed.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the caller has already succeeded by the time this runs. `onError` is taken\n * as a parameter rather than read from a shared reporter, so every feature\n * module keeps its own `options.onError` wiring and this stays reusable by\n * any module with the same fire-and-forget event shape.\n */\nexport function notifyHandler(\n onError: ((error: Error) => void) | undefined,\n run: () => void,\n): void {\n try {\n run();\n } catch (error) {\n onError?.(wrapReportedError(error));\n }\n}\n","import type {\n Block,\n Template,\n TemplateContent,\n TemplateDefaults,\n TemplatePatch,\n TemplateSaveTrigger,\n TemplateSettings,\n TemplatesProvider,\n UiTheme,\n ViewportSize,\n} from \"@templatical/types\";\nimport {\n assertNoSlotInContent,\n assertNoWrapperInContent,\n createDefaultTemplateContent,\n getColumnCount,\n SdkError,\n} from \"@templatical/types\";\nimport { notifyHandler } from \"./error-reporting\";\nimport {\n computed,\n reactive,\n readonly,\n ref,\n type DeepReadonly,\n type Ref,\n} from \"@vue/reactivity\";\n\nexport interface EditorState {\n /**\n * The template currently being edited, as the store last returned it — `null`\n * until `create()` or `load()` resolves, and always `null` without a\n * {@link TemplatesProvider}.\n *\n * Carries identity and name only for practical purposes: its `content` is the\n * store's copy from the last round-trip, whereas `state.content` is what the\n * user is editing.\n */\n template: Template | null;\n content: TemplateContent;\n selectedBlockId: string | null;\n viewport: ViewportSize;\n darkMode: boolean;\n previewMode: boolean;\n isDirty: boolean;\n /** True for the duration of a `save()` call. */\n isSaving: boolean;\n /** True while the latest `create()` or `load()` request is pending. */\n isLoading: boolean;\n uiTheme: UiTheme;\n}\n\nexport interface UseEditorOptions {\n content: TemplateContent;\n defaultFontFamily?: string;\n templateDefaults?: TemplateDefaults;\n /**\n * Blocks other collaborators hold. A locked block can't be selected, and the\n * editor's own mutators leave it alone.\n */\n lockedBlocks?: Readonly<Ref<ReadonlyMap<string, unknown>>>;\n /**\n * Storage backend for the template's save/load lifecycle. Omit it and\n * `create()` / `load()` / `save()` reject — the editor keeps working as a\n * purely local editing surface.\n */\n templates?: TemplatesProvider;\n /**\n * Called with any error a provider call rejects with, before the rejection is\n * re-thrown to the caller.\n */\n onError?: (error: Error) => void;\n}\n\nexport interface UseEditorReturn {\n state: DeepReadonly<EditorState>;\n /** Increments when create/load commits, including a reload of the same id. */\n loadVersion: Readonly<Ref<number>>;\n content: Ref<TemplateContent>;\n selectedBlock: Ref<Block | null>;\n setContent: (content: TemplateContent, markDirty?: boolean) => void;\n selectBlock: (blockId: string | null) => void;\n setViewport: (viewport: ViewportSize) => void;\n setDarkMode: (darkMode: boolean) => void;\n setPreviewMode: (previewMode: boolean) => void;\n setUiTheme: (theme: UiTheme) => void;\n updateBlock: (blockId: string, updates: Partial<Block>) => void;\n updateSettings: (updates: Partial<TemplateSettings>) => void;\n addBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n index?: number,\n ) => void;\n removeBlock: (blockId: string) => void;\n moveBlock: (\n blockId: string,\n newIndex: number,\n targetSectionId?: string,\n columnIndex?: number,\n ) => void;\n isBlockLocked: (blockId: string) => boolean;\n markDirty: () => void;\n findBlockLocation: (blockId: string) => {\n targetSectionId?: string;\n columnIndex?: number;\n index: number;\n } | null;\n /**\n * Rename the loaded template locally and mark the editor dirty. The new name\n * reaches the store on the next `save()`, in the same patch as the content —\n * a rename is an ordinary unsaved change, not a side channel.\n *\n * This method only stages it. The editor's inline rename field commits by\n * calling `setName()` and then saving immediately, because a rename reads as a\n * discrete action rather than an edit to be batched — so in the editor the\n * \"next `save()`\" is usually the one it triggers itself. A headless caller\n * decides its own moment.\n *\n * No-op when no template is loaded: there is nothing to name.\n */\n setName: (name: string) => void;\n /**\n * Persist the current content as a new template. `input.content`, when given,\n * replaces the editor's content first — so `create({ content })` both loads\n * and stores in one step.\n */\n create: (input?: {\n name?: string;\n content?: TemplateContent;\n }) => Promise<Template>;\n /** Fetch a template; an optional guard can discard a stale response. */\n load: (templateId: string, shouldApply?: () => boolean) => Promise<Template>;\n /**\n * Persist the loaded template's name + content as a patch.\n *\n * `trigger` is passed through to `TemplatesOptions.onSaved`. It defaults to\n * `\"api\"`, which is the honest answer for a direct call — the editor package\n * passes the value matching whichever affordance fired.\n */\n save: (trigger?: TemplateSaveTrigger) => Promise<Template>;\n /** Whether a template has been created or loaded. */\n hasTemplate: () => boolean;\n}\n\nexport function useEditor(options: UseEditorOptions): UseEditorReturn {\n const seedContent =\n options.content ??\n createDefaultTemplateContent(\n options.defaultFontFamily,\n options.templateDefaults,\n );\n // Layout-only; authored content must not contain slot or wrapper.\n assertNoSlotInContent(seedContent);\n assertNoWrapperInContent(seedContent);\n\n const state = reactive<EditorState>({\n template: null,\n content: seedContent,\n selectedBlockId: null,\n viewport: \"desktop\",\n darkMode: false,\n previewMode: false,\n isDirty: false,\n isSaving: false,\n isLoading: false,\n uiTheme: \"auto\",\n });\n const loadVersion = ref(0);\n let loadingRequest = 0;\n\n /**\n * Bumped by every content mutation. `save()` captures it before awaiting the\n * provider and only clears `isDirty` if it is unchanged afterwards, so an edit\n * made while a save is in flight is not reported as persisted — which would\n * also make autosave skip it, since that decides dirtiness at debounce time.\n */\n let revision = 0;\n\n function touch(): void {\n state.isDirty = true;\n revision++;\n }\n\n const content = computed({\n get: () => state.content,\n set: (value: TemplateContent) => {\n state.content = value;\n touch();\n },\n });\n\n const selectedBlock = computed(() => {\n if (!state.selectedBlockId) return null;\n return findBlockById(state.content.blocks, state.selectedBlockId);\n });\n\n function findBlockById(blocks: Block[], id: string): Block | null {\n for (const block of blocks) {\n if (block.id === id) return block;\n if (block.type === \"section\") {\n for (const column of block.children) {\n const found = findBlockById(column, id);\n if (found) return found;\n }\n }\n }\n return null;\n }\n\n function collectBlockIds(block: Block, ids: Set<string>): void {\n ids.add(block.id);\n if (block.type === \"section\") {\n for (const column of block.children) {\n for (const child of column) {\n collectBlockIds(child, ids);\n }\n }\n }\n }\n\n function findBlockParent(\n blocks: Block[],\n id: string,\n parent: {\n blocks: Block[];\n sectionId?: string;\n columnIndex?: number;\n } = { blocks },\n ): { blocks: Block[]; sectionId?: string; columnIndex?: number } | null {\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n if (block.id === id) return parent;\n if (block.type === \"section\") {\n for (let colIdx = 0; colIdx < block.children.length; colIdx++) {\n const result = findBlockParent(block.children[colIdx], id, {\n blocks: block.children[colIdx],\n sectionId: block.id,\n columnIndex: colIdx,\n });\n if (result) return result;\n }\n }\n }\n return null;\n }\n\n function isBlockLocked(blockId: string): boolean {\n return options.lockedBlocks?.value.has(blockId) ?? false;\n }\n\n function findBlockLocation(blockId: string): {\n targetSectionId?: string;\n columnIndex?: number;\n index: number;\n } | null {\n const parent = findBlockParent(state.content.blocks, blockId);\n if (!parent) return null;\n const index = parent.blocks.findIndex((b) => b.id === blockId);\n if (index === -1) return null;\n return {\n targetSectionId: parent.sectionId,\n columnIndex: parent.columnIndex,\n index,\n };\n }\n\n // TODO(collab): the lock checks in addBlock/moveBlock/removeBlock/updateBlock\n // are shallow — they only consider the directly-targeted block id. A section\n // can still be removed, moved, or have its `children` array rewritten while a\n // peer is editing one of its descendants, which silently disrupts that peer's\n // edit. Add a `hasLockedDescendant(blockId)` helper and gate section-level\n // operations on it (and on the parent of each affected child) so cascades\n // through the tree are also blocked.\n\n function setContent(newContent: TemplateContent, markDirty = true): void {\n assertNoSlotInContent(newContent);\n assertNoWrapperInContent(newContent);\n state.content = newContent;\n if (markDirty) {\n touch();\n }\n }\n\n function selectBlock(blockId: string | null): void {\n if (blockId && isBlockLocked(blockId)) {\n return;\n }\n state.selectedBlockId = blockId;\n }\n\n function setViewport(viewport: ViewportSize): void {\n state.viewport = viewport;\n }\n\n function setDarkMode(darkMode: boolean): void {\n state.darkMode = darkMode;\n }\n\n function setUiTheme(theme: UiTheme): void {\n state.uiTheme = theme;\n }\n\n function setPreviewMode(previewMode: boolean): void {\n state.previewMode = previewMode;\n if (previewMode) {\n state.selectedBlockId = null;\n }\n }\n\n function updateBlock(blockId: string, updates: Partial<Block>): void {\n if (isBlockLocked(blockId)) {\n return;\n }\n const block = findBlockById(state.content.blocks, blockId);\n if (block) {\n Object.assign(block, updates);\n touch();\n }\n }\n\n function updateSettings(updates: Partial<TemplateSettings>): void {\n state.content.settings = { ...state.content.settings, ...updates };\n touch();\n }\n\n function addBlock(\n block: Block,\n targetSectionId?: string,\n columnIndex = 0,\n index?: number,\n ): void {\n const candidate = {\n settings: state.content.settings,\n blocks: [block],\n };\n assertNoSlotInContent(candidate);\n assertNoWrapperInContent(candidate);\n // Sections cannot be nested inside a column — MJML forbids `mj-section`\n // inside `mj-column`, so the renderer drops them on export (issue #292).\n // Reject the nest up front rather than lose the content silently later.\n if (targetSectionId && block.type === \"section\") {\n return;\n }\n if (targetSectionId) {\n if (isBlockLocked(targetSectionId)) {\n return;\n }\n const section = findBlockById(state.content.blocks, targetSectionId);\n if (section && section.type === \"section\") {\n if (columnIndex < 0 || columnIndex >= getColumnCount(section.columns)) {\n return;\n }\n section.children[columnIndex] = section.children[columnIndex] || [];\n const targetArray = section.children[columnIndex];\n if (index !== undefined && index < targetArray.length) {\n targetArray.splice(index, 0, block);\n } else {\n targetArray.push(block);\n }\n }\n } else {\n if (index !== undefined && index < state.content.blocks.length) {\n state.content.blocks.splice(index, 0, block);\n } else {\n state.content.blocks.push(block);\n }\n }\n touch();\n }\n\n function removeBlock(blockId: string): void {\n if (isBlockLocked(blockId)) {\n return;\n }\n const parent = findBlockParent(state.content.blocks, blockId);\n if (parent) {\n const index = parent.blocks.findIndex((b) => b.id === blockId);\n if (index !== -1) {\n const [removed] = parent.blocks.splice(index, 1);\n if (state.selectedBlockId) {\n const removedIds = new Set<string>();\n collectBlockIds(removed, removedIds);\n if (removedIds.has(state.selectedBlockId)) {\n state.selectedBlockId = null;\n }\n }\n touch();\n }\n }\n }\n\n function moveBlock(\n blockId: string,\n newIndex: number,\n targetSectionId?: string,\n columnIndex = 0,\n ): void {\n if (isBlockLocked(blockId)) {\n return;\n }\n if (targetSectionId && isBlockLocked(targetSectionId)) {\n return;\n }\n\n const parent = findBlockParent(state.content.blocks, blockId);\n if (!parent) return;\n\n const oldIndex = parent.blocks.findIndex((b) => b.id === blockId);\n if (oldIndex === -1) return;\n\n // Sections cannot be nested inside a column (issue #292) — refuse to move\n // a section into a section column; MJML would drop it on export.\n if (targetSectionId && parent.blocks[oldIndex].type === \"section\") {\n return;\n }\n\n // Resolve target before mutating the source — otherwise an invalid\n // targetSectionId leaves the block spliced-out and unrecoverable.\n let targetArray: Block[];\n if (targetSectionId) {\n const section = findBlockById(state.content.blocks, targetSectionId);\n if (!section || section.type !== \"section\") return;\n if (columnIndex < 0 || columnIndex >= getColumnCount(section.columns)) {\n return;\n }\n section.children[columnIndex] = section.children[columnIndex] || [];\n targetArray = section.children[columnIndex];\n } else {\n targetArray = state.content.blocks;\n }\n\n const [block] = parent.blocks.splice(oldIndex, 1);\n targetArray.splice(newIndex, 0, block);\n\n touch();\n }\n\n function markDirty(): void {\n touch();\n }\n\n // -------------------------------------------------------------------------\n // Template lifecycle (over an optional TemplatesProvider)\n // -------------------------------------------------------------------------\n\n /**\n * A refused call throws rather than resolving, because a resolved promise\n * would read as \"saved\" to whoever awaited it.\n *\n * The message names the provider key the consumer set, not an internal\n * capability flag: neither `EditorCapabilities` nor its injection key is\n * exported, so neither a core nor an editor consumer can read one. Note the\n * editor hides its own save controls when `save` is withheld but has no\n * create affordance to hide at all, so `create()` is reachable by definition.\n */\n function refuse(action: string, reason: string): never {\n throw new SdkError(`[Templatical] Templates: ${action} ${reason}`);\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for something that landed.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n function requireProvider(action: string): TemplatesProvider {\n const { templates } = options;\n if (!templates) {\n refuse(\n action,\n \"needs a templates provider. Pass one as `init({ templates })` to enable saving and loading.\",\n );\n }\n return templates;\n }\n\n function setName(name: string): void {\n if (!state.template) return;\n state.template = { ...state.template, name };\n touch();\n }\n\n async function create(input?: {\n name?: string;\n content?: TemplateContent;\n }): Promise<Template> {\n const provider = requireProvider(\"create()\");\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") {\n refuse(\n \"create()\",\n \"is disabled by the provider — its `create` is `false`.\",\n );\n }\n\n let created: Template;\n const request = ++loadingRequest;\n state.isLoading = true;\n try {\n if (input?.content) {\n state.content = input.content;\n }\n // Omit `name` entirely when unset rather than sending `undefined` — a\n // provider serialising the input to JSON would otherwise receive a key it\n // never asked for.\n const revisionAtRequest = revision;\n created = await providerCreate(\n input?.name !== undefined\n ? { name: input.name, content: state.content }\n : { content: state.content },\n );\n state.template = created;\n if (revision === revisionAtRequest) {\n state.isDirty = false;\n }\n loadVersion.value++;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n if (loadingRequest === request) state.isLoading = false;\n }\n // Release this request's loading flag before notifying the consumer. A\n // newer request, if any, retains ownership. Unreachable on failure.\n notify(() => provider.onCreated?.(created));\n return created;\n }\n\n async function load(\n templateId: string,\n shouldApply?: () => boolean,\n ): Promise<Template> {\n const provider = requireProvider(\"load()\");\n\n let loaded: Template;\n const request = ++loadingRequest;\n state.isLoading = true;\n try {\n loaded = await provider.load(templateId);\n assertNoSlotInContent(loaded.content);\n assertNoWrapperInContent(loaded.content);\n if (shouldApply && !shouldApply()) return loaded;\n state.template = loaded;\n state.content = loaded.content;\n state.isDirty = false;\n loadVersion.value++;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n if (loadingRequest === request) state.isLoading = false;\n }\n // Release this request's loading flag before notifying the consumer. A\n // newer request, if any, retains ownership. Unreachable on failure.\n notify(() => provider.onLoaded?.(loaded));\n return loaded;\n }\n\n async function save(trigger: TemplateSaveTrigger = \"api\"): Promise<Template> {\n const provider = requireProvider(\"save()\");\n const { save: providerSave } = provider;\n if (typeof providerSave !== \"function\") {\n refuse(\"save()\", \"is disabled by the provider — its `save` is `false`.\");\n }\n const current = state.template;\n if (!current) {\n refuse(\n \"save()\",\n \"has no template loaded. Call create() or load() first.\",\n );\n }\n\n // The name rides the same patch as the content: a rename is an ordinary\n // unsaved change, so one round-trip persists both.\n const patch: TemplatePatch =\n current.name !== undefined\n ? { name: current.name, content: state.content }\n : { content: state.content };\n\n let saved: Template;\n state.isSaving = true;\n try {\n const revisionAtRequest = revision;\n saved = await providerSave(current.id, patch);\n // Adopt the response only while it still describes what is open. A\n // `load()` that landed mid-flight has already replaced the template, so\n // the response is stale.\n if (state.template?.id === current.id) {\n // The patch was built before the await, so a rename that landed during\n // it is absent from the response. Taking the response's name verbatim\n // reverts what the user typed — and because the next save reads the\n // name back off `state.template`, it would then persist the stale one,\n // losing the rename outright rather than merely on screen.\n const localName = state.template.name;\n state.template =\n localName !== current.name ? { ...saved, name: localName } : saved;\n }\n // Deliberately conditional. An edit landing while the request was in\n // flight is not persisted by it, and clearing the flag would both claim it\n // was and make autosave — which checks dirtiness at debounce time — skip\n // the follow-up save.\n if (revision === revisionAtRequest) {\n state.isDirty = false;\n }\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n state.isSaving = false;\n }\n // Below the `finally` on purpose: `isSaving` is cleared there, and a handler\n // that navigates must observe a settled editor or the consumer's own dirty\n // guard blocks their navigation. Unreachable on failure — the catch rethrows.\n notify(() => provider.onSaved?.(saved, { trigger }));\n return saved;\n }\n\n function hasTemplate(): boolean {\n return state.template?.id !== undefined;\n }\n\n return {\n state: readonly(state),\n loadVersion: readonly(loadVersion),\n content,\n selectedBlock,\n isBlockLocked,\n setContent,\n selectBlock,\n setViewport,\n setDarkMode,\n setUiTheme,\n setPreviewMode,\n updateBlock,\n updateSettings,\n addBlock,\n removeBlock,\n moveBlock,\n markDirty,\n findBlockLocation,\n setName,\n create,\n load,\n save,\n hasTemplate,\n };\n}\n","import { safeClone, type TemplateContent } from \"@templatical/types\";\nimport { computed, ref, type ComputedRef, type Ref } from \"@vue/reactivity\";\n\nexport interface UseHistoryOptions {\n content: Ref<TemplateContent>;\n setContent: (content: TemplateContent, markDirty?: boolean) => void;\n maxSize?: number;\n}\n\nexport interface UseHistoryReturn {\n canUndo: ComputedRef<boolean>;\n canRedo: ComputedRef<boolean>;\n isNavigating: Ref<boolean>;\n undo: () => void;\n redo: () => void;\n record: () => void;\n recordDebounced: (blockId: string) => void;\n clear: () => void;\n destroy: () => void;\n}\n\ninterface DebouncedSnapshot {\n blockId: string;\n timeoutId: ReturnType<typeof setTimeout>;\n}\n\nconst MAX_STACK_SIZE = 50;\nconst DEBOUNCE_MS = 300;\nconst NAVIGATE_IDLE_MS = 1500;\n\nexport function useHistory(options: UseHistoryOptions): UseHistoryReturn {\n const { content, setContent, maxSize = MAX_STACK_SIZE } = options;\n\n const undoStack = ref<TemplateContent[]>([]);\n const redoStack = ref<TemplateContent[]>([]);\n const isNavigating = ref(false);\n let navigatingTimeoutId: ReturnType<typeof setTimeout> | null = null;\n let pendingDebounce: DebouncedSnapshot | null = null;\n\n const canUndo = computed(() => undoStack.value.length > 0);\n const canRedo = computed(() => redoStack.value.length > 0);\n\n function cloneContent(): TemplateContent {\n // Cycle-safe deep clone (see `safeClone`). A naked JSON.stringify throws\n // `Converting circular structure to JSON` if a DOM element with a Sortable\n // expando back-ref ever sneaks into block data via a drag handler — which\n // would freeze the undo stack, since every subsequent mutation calls\n // `record()`. safeClone drops the back-ref instead of throwing.\n return safeClone(content.value);\n }\n\n function pushToUndoStack(snapshot: TemplateContent): void {\n undoStack.value.push(snapshot);\n if (undoStack.value.length > maxSize) {\n undoStack.value.splice(0, undoStack.value.length - maxSize);\n }\n }\n\n function flushPendingDebounce(): void {\n if (pendingDebounce) {\n clearTimeout(pendingDebounce.timeoutId);\n pendingDebounce = null;\n }\n }\n\n function record(): void {\n flushPendingDebounce();\n pushToUndoStack(cloneContent());\n redoStack.value = [];\n }\n\n function recordDebounced(blockId: string): void {\n if (pendingDebounce && pendingDebounce.blockId === blockId) {\n clearTimeout(pendingDebounce.timeoutId);\n pendingDebounce.timeoutId = setTimeout(() => {\n pendingDebounce = null;\n }, DEBOUNCE_MS);\n return;\n }\n\n flushPendingDebounce();\n\n pushToUndoStack(cloneContent());\n redoStack.value = [];\n\n pendingDebounce = {\n blockId,\n timeoutId: setTimeout(() => {\n pendingDebounce = null;\n }, DEBOUNCE_MS),\n };\n }\n\n function setNavigating(): void {\n isNavigating.value = true;\n if (navigatingTimeoutId) {\n clearTimeout(navigatingTimeoutId);\n }\n navigatingTimeoutId = setTimeout(() => {\n isNavigating.value = false;\n navigatingTimeoutId = null;\n }, NAVIGATE_IDLE_MS);\n }\n\n function undo(): void {\n if (undoStack.value.length === 0) {\n return;\n }\n\n flushPendingDebounce();\n\n const snapshot = undoStack.value.pop()!;\n redoStack.value.push(cloneContent());\n setContent(snapshot, true);\n setNavigating();\n }\n\n function redo(): void {\n if (redoStack.value.length === 0) {\n return;\n }\n\n flushPendingDebounce();\n\n const snapshot = redoStack.value.pop()!;\n undoStack.value.push(cloneContent());\n setContent(snapshot, true);\n setNavigating();\n }\n\n function clear(): void {\n undoStack.value = [];\n redoStack.value = [];\n flushPendingDebounce();\n }\n\n function destroy(): void {\n clear();\n if (navigatingTimeoutId) {\n clearTimeout(navigatingTimeoutId);\n navigatingTimeoutId = null;\n }\n }\n\n return {\n canUndo,\n canRedo,\n isNavigating,\n undo,\n redo,\n record,\n recordDebounced,\n clear,\n destroy,\n };\n}\n","import type { Block, BlockDefaults, BlockType } from \"@templatical/types\";\nimport { cloneBlock, createBlock } from \"@templatical/types\";\n\nexport interface UseBlockActionsOptions {\n addBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n index?: number,\n ) => void;\n removeBlock: (blockId: string) => void;\n updateBlock: (blockId: string, updates: Partial<Block>) => void;\n selectBlock: (blockId: string | null) => void;\n /** Locate a block in the tree — used by `duplicateBlock` to insert the\n * clone right after the source instead of appending to the end. */\n findBlockLocation?: (blockId: string) => {\n targetSectionId?: string;\n columnIndex?: number;\n index: number;\n } | null;\n /**\n * Defaults merged onto every block `createAndAddBlock` creates.\n *\n * A **getter** is re-read on each insert; a plain object is captured once.\n * The editor passes a getter because half of these defaults track the\n * template's own `settings.locale`, which the author can change mid-session\n * — a snapshot would pin every later insert to the value at mount.\n */\n blockDefaults?: BlockDefaults | (() => BlockDefaults);\n}\n\nexport interface UseBlockActionsReturn {\n createAndAddBlock: (\n type: BlockType,\n targetSectionId?: string,\n columnIndex?: number,\n ) => Block;\n duplicateBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n ) => Block;\n deleteBlock: (blockId: string) => void;\n updateBlockProperty: <K extends keyof Block>(\n blockId: string,\n key: K,\n value: Block[K],\n ) => void;\n}\n\nexport function useBlockActions(\n options: UseBlockActionsOptions,\n): UseBlockActionsReturn {\n const { addBlock, removeBlock, updateBlock, selectBlock, findBlockLocation } =\n options;\n\n function createAndAddBlock(\n type: BlockType,\n targetSectionId?: string,\n columnIndex?: number,\n ): Block {\n const defaults =\n typeof options.blockDefaults === \"function\"\n ? options.blockDefaults()\n : options.blockDefaults;\n const block = createBlock(type, defaults);\n addBlock(block, targetSectionId, columnIndex);\n selectBlock(block.id);\n return block;\n }\n\n function duplicateBlock(\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n ): Block {\n const cloned = cloneBlock(block);\n\n // Insert directly after the source block. Explicit target args win;\n // otherwise, resolve the source's location and bump index by 1. Falls\n // back to appending at the end if location is unknown.\n if (targetSectionId !== undefined || columnIndex !== undefined) {\n addBlock(cloned, targetSectionId, columnIndex);\n } else {\n const sourceLocation = findBlockLocation?.(block.id) ?? null;\n if (sourceLocation) {\n addBlock(\n cloned,\n sourceLocation.targetSectionId,\n sourceLocation.columnIndex,\n sourceLocation.index + 1,\n );\n } else {\n addBlock(cloned, targetSectionId, columnIndex);\n }\n }\n selectBlock(cloned.id);\n return cloned;\n }\n\n function deleteBlock(blockId: string): void {\n removeBlock(blockId);\n }\n\n function updateBlockProperty<K extends keyof Block>(\n blockId: string,\n key: K,\n value: Block[K],\n ): void {\n updateBlock(blockId, { [key]: value } as Partial<Block>);\n }\n\n return {\n createAndAddBlock,\n duplicateBlock,\n deleteBlock,\n updateBlockProperty,\n };\n}\n","import type { TemplateContent } from \"@templatical/types\";\nimport { watch, type Ref } from \"@vue/reactivity\";\n\nexport interface UseAutoSaveOptions {\n content: Ref<TemplateContent>;\n isDirty: () => boolean;\n onChange: (content: TemplateContent) => void;\n debounce?: number;\n enabled?: boolean | (() => boolean);\n}\n\nexport interface UseAutoSaveReturn {\n flush: () => void;\n cancel: () => void;\n pause: () => void;\n resume: () => void;\n destroy: () => void;\n}\n\n/**\n * Trailing debounce, in ms, measured from the *last* content mutation.\n *\n * Typing is not debounced upstream — TipTap's `onUpdate` calls `updateBlock` per\n * keystroke — so this is the only thing between a keypress and a whole-document\n * write. 1000 was too eager: ordinary prose pauses for a second constantly\n * (word choice, re-reading, reaching for the mouse), so a single paragraph could\n * produce dozens of full-content saves. 2000 roughly halves that while still\n * landing well before a user wonders whether their work was kept.\n *\n * **The single default for both entry points.** Cloud imports this constant\n * rather than declaring its own — a second copy for one setting drifts silently,\n * since nothing links the two.\n */\nexport const DEFAULT_AUTO_SAVE_DEBOUNCE_MS = 2000;\n\nexport function useAutoSave(options: UseAutoSaveOptions): UseAutoSaveReturn {\n const {\n content,\n isDirty,\n onChange,\n debounce = DEFAULT_AUTO_SAVE_DEBOUNCE_MS,\n enabled = true,\n } = options;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let paused = false;\n\n function isEnabled(): boolean {\n return typeof enabled === \"function\" ? enabled() : enabled;\n }\n\n function pause(): void {\n paused = true;\n cancel();\n }\n\n function resume(): void {\n paused = false;\n }\n\n function cancel(): void {\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n }\n\n function flush(): void {\n cancel();\n if (isDirty()) {\n onChange(JSON.parse(JSON.stringify(content.value)));\n }\n }\n\n function scheduleOnChange(): void {\n if (!isEnabled() || paused) return;\n\n cancel();\n timeoutId = setTimeout(() => {\n timeoutId = null;\n if (isEnabled() && !paused && isDirty()) {\n onChange(JSON.parse(JSON.stringify(content.value)));\n }\n }, debounce);\n }\n\n // `watch` from `@vue/reactivity` has no scheduler, so this callback runs\n // *synchronously inside the mutation* — before the editor's trailing\n // `state.isDirty = true`, and `isDirty` sits outside the watched `content`\n // subtree so setting it never re-triggers. An `isDirty()` guard here would\n // therefore drop the first edit after every dirty-flag reset (issue #522).\n // Dirtiness is decided at debounce time instead, where the flag is settled;\n // `scheduleOnChange` still checks `enabled`/`paused` up front.\n const stopWatch = watch(\n content,\n () => {\n scheduleOnChange();\n },\n { deep: true },\n );\n\n function destroy(): void {\n stopWatch();\n cancel();\n }\n\n return {\n flush,\n cancel,\n pause,\n resume,\n destroy,\n };\n}\n","import { computed, reactive, type ComputedRef } from \"@vue/reactivity\";\n\nexport interface UseConditionPreviewReturn {\n isHidden: (blockId: string) => boolean;\n toggleBlock: (blockId: string) => void;\n reset: () => void;\n hasHiddenBlocks: ComputedRef<boolean>;\n}\n\n/**\n * The slice of an editor this needs — deliberately structural, so both the OSS\n * and the Cloud `useEditor` satisfy it without either having to grow toward the\n * other.\n */\nexport interface ConditionPreviewEditor {\n state: { readonly selectedBlockId: string | null };\n selectBlock: (blockId: string | null) => void;\n}\n\nexport function useConditionPreview(\n editor: ConditionPreviewEditor,\n): UseConditionPreviewReturn {\n const hiddenBlockIds = reactive(new Set<string>());\n\n const hasHiddenBlocks = computed(() => hiddenBlockIds.size > 0);\n\n function isHidden(blockId: string): boolean {\n return hiddenBlockIds.has(blockId);\n }\n\n function toggleBlock(blockId: string): void {\n if (hiddenBlockIds.has(blockId)) {\n hiddenBlockIds.delete(blockId);\n } else {\n hiddenBlockIds.add(blockId);\n\n if (editor.state.selectedBlockId === blockId) {\n editor.selectBlock(null);\n }\n }\n }\n\n function reset(): void {\n hiddenBlockIds.clear();\n }\n\n return {\n isHidden,\n toggleBlock,\n reset,\n hasHiddenBlocks,\n };\n}\n","import type { CustomBlock, CustomBlockDefinition } from \"@templatical/types\";\nimport type { ComputedRef, Ref } from \"@vue/reactivity\";\nimport { computed, ref } from \"@vue/reactivity\";\n\nexport function useDataSourceFetch(options: {\n definition: ComputedRef<CustomBlockDefinition | undefined>;\n block: ComputedRef<CustomBlock>;\n onUpdate: (fieldValues: Record<string, unknown>, fetched: boolean) => void;\n}): {\n isFetching: Ref<boolean>;\n fetchError: Ref<boolean>;\n fetch: () => Promise<void>;\n hasDataSource: ComputedRef<boolean>;\n needsFetch: ComputedRef<boolean>;\n} {\n const isFetching = ref(false);\n const fetchError = ref(false);\n\n const hasDataSource = computed(() => !!options.definition.value?.dataSource);\n\n const needsFetch = computed(\n () => hasDataSource.value && !options.block.value.dataSourceFetched,\n );\n\n async function fetch(): Promise<void> {\n const def = options.definition.value;\n if (!def?.dataSource) {\n return;\n }\n\n isFetching.value = true;\n fetchError.value = false;\n\n try {\n const result = await def.dataSource.onFetch({\n fieldValues: { ...options.block.value.fieldValues },\n blockId: options.block.value.id,\n });\n\n if (result == null) {\n return;\n }\n\n const merged = { ...options.block.value.fieldValues };\n for (const key of Object.keys(merged)) {\n if (key in result) {\n merged[key] = result[key];\n }\n }\n\n options.onUpdate(merged, true);\n } catch (error) {\n console.warn(\"[Templatical] Data source fetch error:\", error);\n fetchError.value = true;\n } finally {\n isFetching.value = false;\n }\n }\n\n return {\n isFetching,\n fetchError,\n fetch,\n hasDataSource,\n needsFetch,\n };\n}\n","import type { Block, TemplateSettings } from \"@templatical/types\";\nimport type { UseHistoryReturn } from \"./history\";\n\n/**\n * The mutable slice of an editor this wraps — structural, so the OSS and Cloud\n * `useEditor` returns both satisfy it without either having to grow toward the\n * other. Not `Readonly`: the whole point is to replace these members in place.\n */\nexport interface HistoryInterceptorEditor {\n addBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n index?: number,\n ) => void;\n removeBlock: (blockId: string) => void;\n moveBlock: (\n blockId: string,\n newIndex: number,\n targetSectionId?: string,\n columnIndex?: number,\n ) => void;\n updateBlock: (blockId: string, updates: Partial<Block>) => void;\n updateSettings: (updates: Partial<TemplateSettings>) => void;\n isBlockLocked: (blockId: string) => boolean;\n}\n\n/**\n * Wraps editor mutation methods to record history snapshots before each\n * operation. Mutates the editor object in place.\n *\n * Not installed with a collaboration provider: its controller captures\n * changes by diffing the content and keeps its own undo history.\n */\nexport function useHistoryInterceptor(\n editor: HistoryInterceptorEditor,\n history: UseHistoryReturn,\n): void {\n const originalAddBlock = editor.addBlock;\n const originalRemoveBlock = editor.removeBlock;\n const originalMoveBlock = editor.moveBlock;\n const originalUpdateBlock = editor.updateBlock;\n const originalUpdateSettings = editor.updateSettings;\n\n // Skip recording when the underlying op is a no-op (e.g., a peer-locked\n // block or section), otherwise the undo stack fills with snapshots that\n // are identical to current state and undo silently does nothing.\n editor.addBlock = (block, targetSectionId?, columnIndex?, index?) => {\n if (targetSectionId && editor.isBlockLocked(targetSectionId)) {\n return;\n }\n history.record();\n originalAddBlock(block, targetSectionId, columnIndex, index);\n };\n\n editor.removeBlock = (blockId) => {\n if (editor.isBlockLocked(blockId)) {\n return;\n }\n history.record();\n originalRemoveBlock(blockId);\n };\n\n editor.moveBlock = (blockId, newIndex, targetSectionId?, columnIndex?) => {\n if (editor.isBlockLocked(blockId)) {\n return;\n }\n if (targetSectionId && editor.isBlockLocked(targetSectionId)) {\n return;\n }\n history.record();\n originalMoveBlock(blockId, newIndex, targetSectionId, columnIndex);\n };\n\n editor.updateBlock = (blockId, updates) => {\n if (editor.isBlockLocked(blockId)) {\n return;\n }\n history.recordDebounced(blockId);\n originalUpdateBlock(blockId, updates);\n };\n\n editor.updateSettings = (updates) => {\n history.record();\n originalUpdateSettings(updates);\n };\n}\n","import type {\n Block,\n BlockParent,\n Operation,\n SectionBlock,\n TemplateContent,\n} from \"@templatical/types\";\nimport { deepEqual, safeClone } from \"@templatical/types\";\n\n/** Where a block sits, with its container as a comparable key. */\ninterface Placement {\n block: Block;\n key: string;\n index: number;\n}\n\ntype Json = Record<string, unknown>;\n\nconst ROOT_KEY = \"root\";\nconst BLOCK_SKIP: ReadonlySet<string> = new Set([\"id\", \"type\", \"children\"]);\nconst RESIZED_SECTION_SKIP: ReadonlySet<string> = new Set([\n \"id\",\n \"type\",\n \"children\",\n \"columns\",\n]);\nconst NO_SKIP: ReadonlySet<string> = new Set();\n\nfunction containerKey(parent: BlockParent): string {\n return parent === null\n ? ROOT_KEY\n : JSON.stringify([parent.sectionId, parent.column]);\n}\n\nfunction columnsOf(block: Block): Block[][] {\n const children = (block as { children?: unknown }).children;\n return block.type === \"section\" && Array.isArray(children)\n ? (children as Block[][])\n : [];\n}\n\n/** Every block by id, with its container. The first occurrence of an id wins. */\nfunction indexBlocks(blocks: Block[]): Map<string, Placement> {\n const placements = new Map<string, Placement>();\n const walk = (list: Block[], parent: BlockParent): void => {\n list.forEach((block, index) => {\n if (!placements.has(block.id)) {\n placements.set(block.id, { block, key: containerKey(parent), index });\n }\n columnsOf(block).forEach((column, columnIndex) => {\n if (Array.isArray(column)) {\n walk(column, { sectionId: block.id, column: columnIndex });\n }\n });\n });\n };\n walk(blocks, null);\n return placements;\n}\n\n/**\n * The positions in `values` of one longest strictly increasing run: the blocks\n * that can stay where they are while the others move around them.\n */\nfunction longestIncreasingRun(values: readonly number[]): Set<number> {\n const tails: number[] = [];\n const previous: number[] = new Array<number>(values.length).fill(-1);\n values.forEach((value, position) => {\n let low = 0;\n let high = tails.length;\n while (low < high) {\n const middle = (low + high) >> 1;\n if (values[tails[middle]] < value) low = middle + 1;\n else high = middle;\n }\n if (low > 0) previous[position] = tails[low - 1];\n tails[low] = position;\n });\n const run = new Set<number>();\n let position = tails.length > 0 ? tails[tails.length - 1] : -1;\n while (position !== -1) {\n run.add(position);\n position = previous[position];\n }\n return run;\n}\n\n/** Keys whose values differ, as `set` and `unset`. `undefined` counts as absent. */\nfunction keyChanges(\n before: Json,\n after: Json,\n skip: ReadonlySet<string>,\n): { set: Json; unset: string[] } {\n const set: Json = {};\n const unset: string[] = [];\n for (const key of Object.keys(after)) {\n if (skip.has(key) || after[key] === undefined) continue;\n if (!deepEqual(before[key], after[key])) set[key] = safeClone(after[key]);\n }\n for (const key of Object.keys(before)) {\n if (skip.has(key) || before[key] === undefined) continue;\n if (after[key] === undefined) unset.push(key);\n }\n return { set, unset };\n}\n\n/**\n * The operations that turn `before` into `after`. Blocks are matched by id, so\n * a drag comes out as a `moveBlock` and a layout change as a `columns` change\n * plus moves, never as deletions and re-additions.\n *\n * `applyOperations(before, diffContent(before, after))` deep-equals `after`\n * when both are well formed: unique ids, sections only at the top level, and\n * each section's `children` as long as its layout's column count. The order is\n * what the engine needs:\n *\n * 1. Deletions that take nothing still needed with them.\n * 2. The top level: additions (a new section carries its new children) and moves.\n * 3. `columns` growth, so new columns exist before blocks move into them.\n * 4. Every section column: additions and moves, out of shrinking columns too.\n * 5. `columns` shrinkage, once the dropped columns are empty.\n * 6. Deletion of sections whose surviving blocks have moved out.\n * 7. Key changes on blocks, then on settings.\n *\n * No operation carries `expect`: an ordinary edit applies over anything that\n * doesn't conflict with it, and locks keep two people off one block.\n */\nexport function diffContent(\n before: TemplateContent,\n after: TemplateContent,\n): Operation[] {\n const was = indexBlocks(before.blocks);\n const now = indexBlocks(after.blocks);\n const ops: Operation[] = [];\n\n const typeChanged = (id: string): boolean => {\n const next = now.get(id);\n const previous = was.get(id);\n return (\n next !== undefined &&\n previous !== undefined &&\n next.block.type !== previous.block.type\n );\n };\n const survives = (block: Block): boolean =>\n now.has(block.id) && !typeChanged(block.id);\n const hasSurvivor = (block: Block): boolean =>\n columnsOf(block).some(\n (column) =>\n Array.isArray(column) &&\n column.some(\n (child) =>\n survives(child) || (!typeChanged(child.id) && hasSurvivor(child)),\n ),\n );\n\n // 1. Every old block's fate: kept, deleted now (\"gone\"), or deleted once its\n // survivors have moved out (\"deferred\"). A block that changed type is\n // deleted and comes back as a new one.\n const gone = new Set<string>();\n const deferred: string[] = [];\n type Fate = \"kept\" | \"deferred\" | \"dropped\";\n const visit = (block: Block, fate: Fate): void => {\n let childFate: Fate = \"kept\";\n if (fate === \"dropped\") {\n gone.add(block.id);\n childFate = \"dropped\";\n } else if (typeChanged(block.id)) {\n ops.push({ op: \"deleteBlock\", blockId: block.id });\n gone.add(block.id);\n childFate = \"dropped\";\n } else if (!now.has(block.id)) {\n if (fate === \"deferred\") {\n childFate = \"deferred\";\n } else if (hasSurvivor(block)) {\n deferred.push(block.id);\n childFate = \"deferred\";\n } else {\n ops.push({ op: \"deleteBlock\", blockId: block.id });\n gone.add(block.id);\n childFate = \"dropped\";\n }\n }\n for (const column of columnsOf(block)) {\n if (Array.isArray(column)) {\n for (const child of column) visit(child, childFate);\n }\n }\n };\n for (const block of before.blocks) visit(block, \"kept\");\n\n const exists = (id: string): boolean => was.has(id) && !gone.has(id);\n\n // A new block arrives whole, except that a new section brings only its new\n // children: existing ones move in afterwards.\n const payloadOf = (block: Block): Block => {\n const payload = safeClone(block);\n if (payload.type === \"section\") {\n payload.children = columnsOf(block).map((column) =>\n Array.isArray(column)\n ? column\n .filter((child) => !exists(child.id))\n .map((child) => safeClone(child))\n : column,\n );\n }\n return payload;\n };\n\n // Lay out one final container: the longest run of blocks already in order\n // stays, and every other block is placed after its final predecessor.\n const place = (\n parent: BlockParent,\n finalList: Block[],\n arrivedWithParent: (block: Block) => boolean,\n ): void => {\n const key = containerKey(parent);\n const indexes: number[] = [];\n const positions: number[] = [];\n finalList.forEach((block, position) => {\n const previous = was.get(block.id);\n if (exists(block.id) && previous !== undefined && previous.key === key) {\n indexes.push(previous.index);\n positions.push(position);\n }\n });\n const staying = new Set(\n [...longestIncreasingRun(indexes)].map((run) => positions[run]),\n );\n finalList.forEach((block, position) => {\n if (staying.has(position) || arrivedWithParent(block)) return;\n const afterId = position > 0 ? finalList[position - 1].id : null;\n ops.push(\n exists(block.id)\n ? { op: \"moveBlock\", blockId: block.id, parent, after: afterId }\n : { op: \"addBlock\", block: payloadOf(block), parent, after: afterId },\n );\n });\n };\n\n // 2. The top level.\n place(null, after.blocks, () => false);\n\n const sections = after.blocks.filter(\n (block): block is SectionBlock => block.type === \"section\",\n );\n const resized = new Set<string>();\n const resize = (grow: boolean): void => {\n for (const section of sections) {\n if (!exists(section.id)) continue;\n const fromCount = columnsOf(was.get(section.id)!.block).length;\n const toCount = columnsOf(section).length;\n if (grow ? toCount > fromCount : toCount < fromCount) {\n ops.push({\n op: \"updateBlock\",\n blockId: section.id,\n set: { columns: section.columns },\n });\n resized.add(section.id);\n }\n }\n };\n\n // 3. Growth.\n resize(true);\n\n // 4. Every column of every section, new sections included.\n for (const section of sections) {\n const isNew = !exists(section.id);\n columnsOf(section).forEach((column, columnIndex) => {\n if (!Array.isArray(column)) return;\n place(\n { sectionId: section.id, column: columnIndex },\n column,\n (child) => isNew && !exists(child.id),\n );\n });\n }\n\n // 5. Shrinkage.\n resize(false);\n\n // 6. Sections whose survivors have moved out.\n for (const id of deferred) ops.push({ op: \"deleteBlock\", blockId: id });\n\n // 7. Key changes, in document order.\n for (const [id, placement] of now) {\n if (!exists(id)) continue;\n const { set, unset } = keyChanges(\n was.get(id)!.block as unknown as Json,\n placement.block as unknown as Json,\n resized.has(id) ? RESIZED_SECTION_SKIP : BLOCK_SKIP,\n );\n if (Object.keys(set).length === 0 && unset.length === 0) continue;\n ops.push({\n op: \"updateBlock\",\n blockId: id,\n ...(Object.keys(set).length > 0 ? { set } : {}),\n ...(unset.length > 0 ? { unset } : {}),\n });\n }\n\n const settings = keyChanges(\n before.settings as unknown as Json,\n after.settings as unknown as Json,\n NO_SKIP,\n );\n if (Object.keys(settings.set).length > 0 || settings.unset.length > 0) {\n ops.push({\n op: \"updateSettings\",\n ...(Object.keys(settings.set).length > 0 ? { set: settings.set } : {}),\n ...(settings.unset.length > 0 ? { unset: settings.unset } : {}),\n });\n }\n\n return ops;\n}\n","import type {\n Block,\n BlockLock,\n BlockLockHolder,\n CollaborationActor,\n CollaborationConflictCode,\n CollaborationConnectionState,\n CollaborationHandlers,\n CollaborationMember,\n CollaborationProvider,\n CollaborationRemoteChange,\n Operation,\n OperationContext,\n RevisionedBatch,\n Template,\n TemplateContent,\n} from \"@templatical/types\";\nimport {\n applyOperations,\n applyOperationsInPlace,\n deepEqual,\n invertOperations,\n OperationError,\n safeClone,\n} from \"@templatical/types\";\nimport {\n computed,\n getCurrentScope,\n onScopeDispose,\n ref,\n shallowRef,\n watch,\n type ComputedRef,\n type DeepReadonly,\n type Ref,\n} from \"@vue/reactivity\";\nimport { diffContent } from \"./diff-content\";\n\n/** Typing into one block is sent this long after the last keystroke. */\nexport const COLLABORATION_TYPING_DEBOUNCE_MS = 300;\n/** Typing that never pauses is still sent at least this often. */\nexport const COLLABORATION_TYPING_MAX_WAIT_MS = 1000;\n/** A revision that hasn't arrived after this long is fetched. */\nexport const COLLABORATION_GAP_TIMEOUT_MS = 1000;\n/** A held lock is renewed this often; the backend lets it lapse after 30 s. */\nexport const COLLABORATION_LOCK_RENEW_MS = 10_000;\n/** Waits between attempts at a batch that failed in transit. */\nconst RETRY_DELAYS_MS = [1000, 2000, 5000, 10_000, 30_000];\n/** The snapshot history's depth, kept for the operation history too. */\nconst DEFAULT_MAX_UNDO = 50;\n/** OperationBatch allows 1–100 operations. */\nconst MAX_BATCH_OPS = 100;\n\nconst REFUSAL_CODES: ReadonlySet<string> = new Set<CollaborationConflictCode>([\n \"block_exists\",\n \"block_missing\",\n \"anchor_missing\",\n \"precondition_failed\",\n \"lock_held\",\n \"locked_by_others\",\n \"invalid_operation\",\n \"lock_lost\",\n \"content_too_large\",\n]);\n\n/** The template fields the controller reads, as the editor's readonly state holds them. */\nexport type CollaborationTemplate = DeepReadonly<\n Pick<Template, \"id\" | \"revision\" | \"content\">\n>;\n\n/** The slice of the editor the controller reads and writes. */\nexport interface CollaborationEditor {\n state: {\n readonly template: CollaborationTemplate | null;\n readonly selectedBlockId: string | null;\n readonly isLoading: boolean;\n };\n /** The live content the user edits. */\n content: Ref<TemplateContent>;\n selectBlock: (blockId: string | null) => void;\n setContent: (content: TemplateContent, markDirty?: boolean) => void;\n markDirty: () => void;\n}\n\n/** Something to tell the person at this editor. */\nexport type CollaborationNotice =\n /** Someone else holds the block this session tried to select. */\n | { type: \"selectionRefused\"; blockId: string; holder: BlockLockHolder }\n /** A local change was refused and taken back. */\n | {\n type: \"editRefused\";\n code: CollaborationConflictCode;\n holder?: BlockLockHolder;\n }\n /** An undo or redo step no longer applies and was dropped. */\n | { type: \"undoSkipped\"; code: CollaborationConflictCode };\n\n/** A re-sync another session caused, as the backend announced it. */\nexport interface CollaborationRemoteResync {\n /** The revision the re-sync brings the template to. */\n revision: number;\n actor: CollaborationActor;\n}\n\n/** How {@link UseCollaborationReturn.applyOperations} sends a batch. */\nexport interface CollaborationApplyOptions {\n /** Sent as the batch's `origin`: an AI turn produced it (BY9). */\n origin: \"ai\";\n /**\n * The turn the batch belongs to. Batches of one turn share an undo step\n * while that step is the newest one.\n */\n turn: string;\n}\n\n/** What {@link UseCollaborationReturn.applyOperations} answers. */\nexport type CollaborationApplyResult =\n | { applied: true; inverse: Operation[] }\n | { applied: false; error: OperationError };\n\nexport interface UseCollaborationOptions {\n provider: CollaborationProvider;\n /**\n * Fetch a template when the backend says to re-sync: the templates\n * provider's `load`. Its `revision` says which revision `content` is at.\n */\n loadTemplate: (templateId: string) => Promise<Template>;\n /** This editor instance's session id. A new UUID when omitted. */\n sessionId?: string;\n /** How many undo steps to keep. */\n maxUndo?: number;\n /** A batch from another session was applied. */\n onRemoteChange?: (change: CollaborationRemoteChange) => void;\n /**\n * Another session re-synced the template — a `setContent`, a restore, a\n * batch too large to broadcast — and the backend said who. Called for a\n * revision this session doesn't have yet, before it catches up; the\n * catch-up's batches still reach `onRemoteChange`. The editor names an\n * agent's re-sync with it.\n */\n onRemoteResync?: (resync: CollaborationRemoteResync) => void;\n /**\n * Rewrite another session's batch just before it is applied, given the\n * content it applies to. Called for live batches and catch-up batches\n * alike, never for this session's own. The editor uses it to turn merge-tag\n * tokens into tags, as it does for loaded content. `onRemoteChange` still\n * receives the batch as the backend sent it.\n */\n normalizeRemoteOperations?: (\n ops: readonly Operation[],\n content: TemplateContent,\n ) => Operation[];\n onNotice?: (notice: CollaborationNotice) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface UseCollaborationReturn {\n readonly sessionId: string;\n /** Everyone with the template open, this session included; empty without presence. */\n members: Readonly<Ref<readonly CollaborationMember[]>>;\n /** Blocks other sessions hold, by block id. */\n locks: Readonly<Ref<ReadonlyMap<string, BlockLock>>>;\n /** `null` before a realtime join, and for a backend without realtime. */\n connection: Readonly<Ref<CollaborationConnectionState | null>>;\n /** The last revision this session confirmed. */\n revision: Readonly<Ref<number>>;\n canUndo: ComputedRef<boolean>;\n canRedo: ComputedRef<boolean>;\n /** Wire the controller to the editor. Call once, inside the editor's setup scope. */\n attach: (editor: CollaborationEditor) => void;\n undo: () => void;\n redo: () => void;\n clearHistory: () => void;\n /** Turn edits not sent yet into a batch now. */\n flush: () => void;\n /** Resolves once every batch is confirmed; rejects when one fails in transit. */\n settle: () => Promise<void>;\n /** Stop reading the canvas (a version preview is on it). Batches keep arriving. */\n suspend: () => void;\n /** Put the current state back on the canvas and read it again. */\n resume: () => void;\n /** Fetch what this session missed, or reload when the backend says to. */\n catchUp: () => Promise<void>;\n /** Whether the block exists in the content the backend confirmed. */\n hasConfirmedBlock: (blockId: string) => boolean;\n /**\n * Apply a batch this editor was handed (an AI turn's) as this session's\n * own: checked against the view with this session's locks, put on the\n * canvas, sent with its `origin`, and undone with the rest of its turn. A\n * refusal changes nothing and sends nothing.\n */\n applyOperations: (\n ops: Operation[],\n options: CollaborationApplyOptions,\n ) => CollaborationApplyResult;\n destroy: () => void;\n}\n\ninterface HistoryEntry {\n ops: Operation[];\n /** The AI turn whose batches this step undoes, if it is one. */\n turn?: string;\n}\n\ninterface PendingBatch {\n batchId: string;\n ops: Operation[];\n origin: \"edit\" | \"undo\" | \"redo\" | \"ai\";\n /** The stack entry this batch pushed, taken back if the backend refuses it. */\n entry: HistoryEntry | null;\n /** Chunks from the same enqueue call share refusal cleanup. */\n group: { dropped: boolean };\n sent: boolean;\n /** Set once the backend confirms it. */\n revision: number | null;\n}\n\ninterface Session {\n templateId: string;\n confirmed: TemplateContent;\n revision: number;\n pending: PendingBatch[];\n /** `confirmed` plus every pending batch that still applies on it. */\n view: TemplateContent;\n /** Batches that arrived ahead of a gap, by revision. */\n remote: Map<number, RevisionedBatch>;\n own: Map<number, PendingBatch>;\n inFlight: PendingBatch | null;\n gapTimer: ReturnType<typeof setTimeout> | null;\n catchingUp: Promise<void> | null;\n leave: (() => void) | null;\n retired: boolean;\n /** True while a reload runs: a drift found then is reported, not retried. */\n recovering: boolean;\n undo: HistoryEntry[];\n redo: HistoryEntry[];\n waiters: { resolve: () => void; reject: (error: Error) => void }[];\n}\n\nfunction refusalCode(error: unknown): CollaborationConflictCode | null {\n const code = (error as { code?: unknown } | null)?.code;\n return typeof code === \"string\" && REFUSAL_CODES.has(code)\n ? (code as CollaborationConflictCode)\n : null;\n}\n\nfunction refusalHolder(error: unknown): BlockLockHolder | undefined {\n const holder = (error as { holder?: unknown } | null)?.holder;\n return typeof holder === \"object\" &&\n holder !== null &&\n typeof (holder as BlockLockHolder).sessionId === \"string\"\n ? (holder as BlockLockHolder)\n : undefined;\n}\n\n/** Network failures, 5xx, 408 and 429 are worth retrying; other 4xx aren't. */\nfunction isTransient(error: unknown): boolean {\n const status = (error as { statusCode?: unknown } | null)?.statusCode;\n if (typeof status !== \"number\") return true;\n return status >= 500 || status === 408 || status === 429;\n}\n\nfunction containsBlock(blocks: Block[], blockId: string): boolean {\n return blocks.some((block) => {\n if (block.id === blockId) return true;\n const children = (block as { children?: unknown }).children;\n return (\n block.type === \"section\" &&\n Array.isArray(children) &&\n (children as Block[][]).some(\n (column) => Array.isArray(column) && containsBlock(column, blockId),\n )\n );\n });\n}\n\n/** Typing into one block, or one settings change: worth batching up. */\nfunction isCoalescible(ops: readonly Operation[]): boolean {\n if (ops.length === 1 && ops[0].op === \"updateSettings\") return true;\n const first = ops[0];\n return (\n first.op === \"updateBlock\" &&\n ops.every((op) => op.op === \"updateBlock\" && op.blockId === first.blockId)\n );\n}\n\n/**\n * A remote batch without its preconditions. The backend checked them against\n * its own copy before it accepted the batch. This copy can differ from that one\n * in merge-tag markup alone — tokens are normalized into tags on load and on\n * arrival — and an `expect` compares content exactly, so checking it here\n * refuses a batch the backend applied and reloads the template. Structural\n * checks still run, and still catch a copy that really drifted.\n */\nfunction withoutPreconditions(ops: readonly Operation[]): Operation[] {\n return ops.map((op) => {\n if (!(\"expect\" in op)) return op;\n const { expect: _expect, ...rest } = op;\n return rest as Operation;\n });\n}\n\n/**\n * Real-time collaboration over a {@link CollaborationProvider}.\n *\n * The controller keeps the content the backend confirmed at revision R, plus\n * this session's pending batches; the canvas shows the confirmed content with\n * the pending batches applied. Local changes are captured by diffing the\n * canvas against that view: structural changes, selection changes and blur\n * send at once, typing into one block after a short pause. One batch is in\n * flight at a time. Batches from other sessions apply strictly by revision,\n * with this session's pending batches replayed on top; a gap that isn't\n * filled within a second, a re-sync, a reconnect and a refusal all fetch what\n * was missed.\n *\n * Selecting a block takes its lock (when the backend has locks), renewed\n * while selected and released on deselect, template switch and page hide.\n * Undo and redo send inverse batches guarded by preconditions; one that no\n * longer applies is dropped with a notice. Changes from others never enter the\n * stacks.\n */\nexport function useCollaboration(\n options: UseCollaborationOptions,\n): UseCollaborationReturn {\n const { provider } = options;\n const sessionId = options.sessionId ?? crypto.randomUUID();\n const maxUndo = options.maxUndo ?? DEFAULT_MAX_UNDO;\n const locking =\n typeof provider.acquireLock === \"function\" &&\n typeof provider.renewLock === \"function\" &&\n typeof provider.releaseLock === \"function\";\n\n const members = shallowRef<readonly CollaborationMember[]>([]);\n const locks = shallowRef<ReadonlyMap<string, BlockLock>>(new Map());\n const connection = ref<CollaborationConnectionState | null>(null);\n const revision = ref(0);\n const historyVersion = ref(0);\n\n let editor: CollaborationEditor | null = null;\n let active: Session | null = null;\n let suspended = false;\n let writing = false;\n let captureQueued = false;\n let typingTimer: ReturnType<typeof setTimeout> | null = null;\n let typingSince = 0;\n let destroyed = false;\n /** The watchers `attach` set up; `destroy` stops them, scope or not. */\n const stopWatching: (() => void)[] = [];\n\n // Locks: `wanted` follows the selection, `held` is what the backend granted.\n let wanted: { templateId: string; blockId: string } | null = null;\n let held: { templateId: string; blockId: string } | null = null;\n let lockQueue: Promise<void> = Promise.resolve();\n let renewTimer: ReturnType<typeof setInterval> | null = null;\n\n const canUndo = computed(() => {\n void historyVersion.value;\n return (active?.undo.length ?? 0) > 0;\n });\n const canRedo = computed(() => {\n void historyVersion.value;\n return (active?.redo.length ?? 0) > 0;\n });\n\n function reportError(error: unknown): void {\n options.onError?.(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n\n function notify(\n notice: CollaborationNotice,\n session: Session | null = active,\n ): void {\n if (session !== active || destroyed) return;\n options.onNotice?.(notice);\n }\n\n function lockContext(): OperationContext {\n return {\n locks: [...locks.value.values()].map((lock) => ({\n blockId: lock.blockId,\n sessionId: lock.holder.sessionId,\n })),\n session: sessionId,\n };\n }\n\n function bumpHistory(): void {\n historyVersion.value++;\n }\n\n function pushCapped(stack: HistoryEntry[], entry: HistoryEntry): void {\n stack.push(entry);\n if (stack.length > maxUndo) stack.splice(0, stack.length - maxUndo);\n }\n\n // --- The canvas ----------------------------------------------------------\n\n /** Put `view` on the canvas, changing only what differs from it. */\n function renderView(view: TemplateContent): void {\n if (!editor || suspended) return;\n writing = true;\n try {\n const live = editor.content.value;\n const ops = diffContent(live, view);\n if (ops.length > 0) {\n try {\n applyOperationsInPlace(live, ops);\n } catch {\n editor.setContent(safeClone(view), false);\n }\n }\n if (!deepEqual(editor.content.value, view)) {\n editor.setContent(safeClone(view), false);\n }\n } finally {\n writing = false;\n }\n const selected = editor.state.selectedBlockId;\n if (selected && !containsBlock(view.blocks, selected)) {\n editor.selectBlock(null);\n }\n }\n\n // --- Local changes -------------------------------------------------------\n\n function scheduleCapture(): void {\n if (writing || captureQueued) return;\n captureQueued = true;\n queueMicrotask(() => {\n captureQueued = false;\n capture(false);\n });\n }\n\n function clearTypingTimer(): void {\n if (typingTimer) {\n clearTimeout(typingTimer);\n typingTimer = null;\n }\n }\n\n /**\n * Diff the canvas against the view and send what changed. `duringLoad` is\n * for the moment a load or create starts: the canvas still shows this\n * template, and its unsent typing must go before the content is replaced.\n */\n function capture(force: boolean, duringLoad = false): void {\n const session = active;\n if (!editor || !session || suspended || writing) return;\n if (editor.state.isLoading && !duringLoad) return;\n if (editor.state.template?.id !== session.templateId) return;\n const ops = diffContent(session.view, editor.content.value);\n if (ops.length === 0) {\n clearTypingTimer();\n return;\n }\n if (!force && isCoalescible(ops)) {\n const now = Date.now();\n if (typingTimer === null) typingSince = now;\n if (now - typingSince < COLLABORATION_TYPING_MAX_WAIT_MS) {\n if (typingTimer) clearTimeout(typingTimer);\n typingTimer = setTimeout(() => {\n typingTimer = null;\n capture(true);\n }, COLLABORATION_TYPING_DEBOUNCE_MS);\n return;\n }\n }\n clearTypingTimer();\n commit(session, ops);\n }\n\n function flush(): void {\n capture(true);\n }\n\n function commit(session: Session, proposed: Operation[]): void {\n let ops = proposed;\n let next: TemplateContent;\n try {\n next = applyOperations(session.view, ops, lockContext());\n } catch (error) {\n if (!(error instanceof OperationError)) throw error;\n // Refused here, before any request: take the change back.\n renderView(session.view);\n const holder =\n error.blockId === undefined\n ? undefined\n : locks.value.get(error.blockId)?.holder;\n notify({\n type: \"editRefused\",\n code: error.code,\n ...(holder ? { holder } : {}),\n });\n if (error.code === \"invalid_operation\") reportError(error);\n return;\n }\n const live = editor!.content.value;\n if (!deepEqual(next, live)) {\n // Content that isn't well formed can defeat the diff. Send it whole\n // rather than drift from it.\n reportError(\n new Error(\n \"[Templatical] Collaboration: a change couldn't be expressed as block operations, so the whole content was sent.\",\n ),\n );\n ops = [{ op: \"setContent\", content: safeClone(live) }];\n try {\n next = applyOperations(session.view, ops, lockContext());\n } catch (error) {\n if (!(error instanceof OperationError)) throw error;\n renderView(session.view);\n notify({ type: \"editRefused\", code: error.code });\n return;\n }\n }\n const entry: HistoryEntry = { ops: invertOperations(session.view, ops) };\n pushCapped(session.undo, entry);\n session.redo = [];\n bumpHistory();\n session.view = next;\n enqueue(session, ops, \"edit\", entry);\n }\n\n function enqueue(\n session: Session,\n ops: Operation[],\n origin: PendingBatch[\"origin\"],\n entry: HistoryEntry | null,\n ): void {\n const group = { dropped: false };\n let offset = 0;\n do {\n session.pending.push({\n batchId: crypto.randomUUID(),\n ops: ops.slice(offset, offset + MAX_BATCH_OPS),\n origin,\n entry,\n group,\n sent: false,\n revision: null,\n });\n offset += MAX_BATCH_OPS;\n } while (offset < ops.length);\n pump(session);\n }\n\n // --- Sending ---------------------------------------------------------------\n\n function pump(session: Session): void {\n if (session.inFlight) return;\n const next = session.pending.find((batch) => !batch.sent);\n if (!next) {\n settleWaiters(session);\n return;\n }\n next.sent = true;\n session.inFlight = next;\n send(session, next, 0);\n }\n\n function send(session: Session, batch: PendingBatch, attempt: number): void {\n provider\n .submit(session.templateId, {\n batchId: batch.batchId,\n baseRevision: session.revision,\n ops: batch.ops,\n ...(batch.origin === \"ai\" ? { origin: \"ai\" as const } : {}),\n })\n .then(\n ({ revision: confirmedAt }) => {\n session.inFlight = null;\n confirmOwn(session, batch, confirmedAt);\n pump(session);\n },\n (error: unknown) => {\n if (refusalCode(error) !== null) {\n session.inFlight = null;\n refuse(session, batch, error);\n pump(session);\n return;\n }\n if (isTransient(error)) {\n // Same batch id, so a submit that did land applies once.\n rejectWaiters(session, error);\n const delay =\n RETRY_DELAYS_MS[Math.min(attempt, RETRY_DELAYS_MS.length - 1)];\n setTimeout(() => {\n if (!destroyed) send(session, batch, attempt + 1);\n }, delay);\n return;\n }\n session.inFlight = null;\n reportError(error);\n refuse(session, batch, error);\n pump(session);\n },\n );\n }\n\n function confirmOwn(session: Session, batch: PendingBatch, at: number): void {\n if (batch.revision !== null) return;\n batch.revision = at;\n if (at <= session.revision) {\n // A catch-up or a reload already brought it in.\n dropPending(session, batch);\n rebuild(session);\n return;\n }\n session.own.set(at, batch);\n drain(session);\n }\n\n function dropPending(session: Session, batch: PendingBatch): void {\n session.pending = session.pending.filter((pending) => pending !== batch);\n settleWaiters(session);\n }\n\n function takeBackEntry(session: Session, batch: PendingBatch): void {\n if (!batch.entry) return;\n for (const stack of [session.undo, session.redo]) {\n const position = stack.indexOf(batch.entry);\n if (position !== -1) stack.splice(position, 1);\n }\n bumpHistory();\n }\n\n function dropRefusedGroup(session: Session, batch: PendingBatch): boolean {\n const firstRefusal = !batch.group.dropped;\n batch.group.dropped = true;\n // Confirmed chunks stay applied; sent chunks still await their outcome.\n // A split step is not atomic at the backend.\n session.pending = session.pending.filter(\n (pending) => pending.group !== batch.group || pending.sent,\n );\n if (firstRefusal) takeBackEntry(session, batch);\n settleWaiters(session);\n return firstRefusal;\n }\n\n function refuse(session: Session, batch: PendingBatch, error: unknown): void {\n dropPending(session, batch);\n if (dropRefusedGroup(session, batch)) {\n const code = refusalCode(error) ?? \"invalid_operation\";\n if (batch.origin === \"edit\" || batch.origin === \"ai\") {\n const holder = refusalHolder(error);\n notify(\n { type: \"editRefused\", code, ...(holder ? { holder } : {}) },\n session,\n );\n } else {\n notify({ type: \"undoSkipped\", code }, session);\n }\n }\n rebuild(session);\n if (session === active) void catchUp();\n }\n\n function settleWaiters(session: Session): void {\n if (session.pending.length > 0 || session.waiters.length === 0) return;\n for (const waiter of session.waiters.splice(0)) waiter.resolve();\n }\n\n function rejectWaiters(session: Session, error: unknown): void {\n const reason = error instanceof Error ? error : new Error(String(error));\n for (const waiter of session.waiters.splice(0)) waiter.reject(reason);\n }\n\n // --- Receiving -------------------------------------------------------------\n\n function receive(session: Session, batch: RevisionedBatch): void {\n if (batch.revision <= session.revision) return;\n const mine = session.pending.find(\n (pending) => pending.batchId === batch.batchId,\n );\n if (mine) {\n confirmOwn(session, mine, batch.revision);\n return;\n }\n session.remote.set(batch.revision, batch);\n drain(session);\n }\n\n /**\n * The ops another session's batch applies here: rewritten by\n * `normalizeRemoteOperations`, then without their preconditions. A\n * normalizer that throws is reported, and the batch applies as it came.\n */\n function arriving(session: Session, batch: RevisionedBatch): Operation[] {\n let ops: readonly Operation[] = batch.ops;\n if (options.normalizeRemoteOperations) {\n try {\n ops = options.normalizeRemoteOperations(batch.ops, session.confirmed);\n } catch (error) {\n reportError(error);\n }\n }\n return withoutPreconditions(ops);\n }\n\n /** Apply whatever is next in revision order, own and remote alike. */\n function drain(session: Session): void {\n const applied: RevisionedBatch[] = [];\n let confirmedDroppedChunk = false;\n for (;;) {\n const at = session.revision + 1;\n const remote = session.remote.get(at);\n const own = session.own.get(at);\n if (!remote && !own) break;\n try {\n session.confirmed = applyOperations(\n session.confirmed,\n remote ? arriving(session, remote) : own!.ops,\n );\n } catch {\n if (session.recovering) {\n // Even a fresh copy doesn't take the backend's next batch: stop\n // rather than reload in a loop.\n session.remote.clear();\n session.own.clear();\n reportError(\n new Error(\n \"[Templatical] Collaboration: this editor can't follow the template's changes. Reload the page.\",\n ),\n );\n return;\n }\n // This copy has drifted from the backend's: start over from it.\n void reload(session);\n return;\n }\n session.revision = at;\n if (remote) {\n session.remote.delete(at);\n applied.push(remote);\n } else {\n session.own.delete(at);\n if (own!.group.dropped) confirmedDroppedChunk = true;\n dropPending(session, own!);\n }\n }\n if (session === active) revision.value = session.revision;\n if (session.remote.size > 0 || session.own.size > 0) armGapTimer(session);\n else clearGapTimer(session);\n\n if (applied.length === 0 && !confirmedDroppedChunk) return;\n rebuild(session);\n if (session !== active) return;\n for (const batch of applied) {\n try {\n options.onRemoteChange?.({ actor: batch.actor, ops: batch.ops });\n } catch (error) {\n reportError(error);\n }\n }\n }\n\n /** Recompute the view from `confirmed`, replaying what is still pending. */\n function rebuild(session: Session): void {\n // Typing not sent yet becomes a batch first, so the canvas holds nothing\n // the replay below doesn't know about.\n if (session === active) flush();\n let view = session.confirmed;\n for (let index = 0; index < session.pending.length; index++) {\n const batch = session.pending[index];\n try {\n view = applyOperations(view, batch.ops);\n } catch (error) {\n if (batch.sent) {\n // The backend decides this one; it will almost certainly refuse it.\n continue;\n }\n if (dropRefusedGroup(session, batch)) {\n const code =\n error instanceof OperationError ? error.code : \"invalid_operation\";\n notify(\n batch.origin === \"edit\" || batch.origin === \"ai\"\n ? { type: \"editRefused\", code }\n : { type: \"undoSkipped\", code },\n session,\n );\n }\n // Earlier pending chunks of that group may already be in this view.\n view = session.confirmed;\n index = -1;\n }\n }\n session.view = view;\n settleWaiters(session);\n if (session === active) renderView(view);\n }\n\n function armGapTimer(session: Session): void {\n if (session.gapTimer) return;\n session.gapTimer = setTimeout(() => {\n session.gapTimer = null;\n if (session === active) void catchUp();\n }, COLLABORATION_GAP_TIMEOUT_MS);\n }\n\n function clearGapTimer(session: Session): void {\n if (session.gapTimer) {\n clearTimeout(session.gapTimer);\n session.gapTimer = null;\n }\n }\n\n function catchUp(): Promise<void> {\n const session = active;\n if (!session) return Promise.resolve();\n if (session.catchingUp) return session.catchingUp;\n session.catchingUp = (async () => {\n try {\n const result = await provider.operationsAfter(\n session.templateId,\n session.revision,\n );\n if (session.retired) return;\n if (\"resync\" in result) {\n await reload(session);\n return;\n }\n for (const batch of result.batches) receive(session, batch);\n } catch (error) {\n reportError(error);\n } finally {\n session.catchingUp = null;\n }\n })();\n return session.catchingUp;\n }\n\n async function reload(session: Session): Promise<void> {\n session.recovering = true;\n try {\n const template = await options.loadTemplate(session.templateId);\n if (session.retired) return;\n const at = template.revision ?? session.revision;\n session.confirmed = safeClone(template.content);\n session.revision = at;\n session.remote = new Map(\n [...session.remote].filter(([remoteAt]) => remoteAt > at),\n );\n for (const [ownAt, batch] of [...session.own]) {\n if (ownAt <= at) {\n session.own.delete(ownAt);\n dropPending(session, batch);\n }\n }\n if (session === active) revision.value = at;\n rebuild(session);\n drain(session);\n } catch (error) {\n reportError(error);\n } finally {\n session.recovering = false;\n }\n }\n\n // --- Sessions --------------------------------------------------------------\n\n function handlersFor(session: Session): CollaborationHandlers {\n const live = (): boolean => session === active && !session.retired;\n return {\n onBatch: (batch) => {\n if (live()) receive(session, batch);\n },\n onResync: (at, actor) => {\n if (!live() || at <= session.revision) return;\n if (actor) {\n try {\n options.onRemoteResync?.({ revision: at, actor });\n } catch (error) {\n reportError(error);\n }\n }\n void catchUp();\n },\n onLock: (lock) => {\n if (live()) setLock(lock);\n },\n onUnlock: (blockId, holderSession) => {\n if (live()) removeLock(blockId, holderSession);\n },\n onMembers: (next) => {\n if (live()) setMembers(next);\n },\n onConnectionChange: (state) => {\n if (live()) changeConnection(session, state);\n },\n };\n }\n\n function join(session: Session, refresh: boolean): void {\n provider.join(session.templateId, { sessionId }, handlersFor(session)).then(\n (joined) => {\n if (session.retired || session !== active) {\n if (!refresh) joined.leave();\n return;\n }\n session.leave = joined.leave;\n replaceLocks(joined.locks, !refresh);\n setMembers(joined.members);\n if (joined.members.length > 0) connection.value = \"connected\";\n if (joined.revision > session.revision) void catchUp();\n if (refresh) reconcileLock();\n },\n (error: unknown) => reportError(error),\n );\n }\n\n function start(template: CollaborationTemplate): void {\n const content = safeClone(template.content) as TemplateContent;\n const session: Session = {\n templateId: template.id,\n confirmed: content,\n revision: template.revision ?? 0,\n pending: [],\n view: safeClone(content),\n remote: new Map(),\n own: new Map(),\n inFlight: null,\n gapTimer: null,\n catchingUp: null,\n leave: null,\n retired: false,\n recovering: false,\n undo: [],\n redo: [],\n waiters: [],\n };\n active = session;\n revision.value = session.revision;\n bumpHistory();\n join(session, false);\n if (editor?.state.selectedBlockId)\n onSelection(editor.state.selectedBlockId);\n scheduleCapture();\n }\n\n function retire(session: Session): void {\n session.retired = true;\n clearGapTimer(session);\n session.leave?.();\n session.leave = null;\n rejectWaiters(session, new Error(\"[Templatical] The template was closed.\"));\n }\n\n function changeConnection(\n session: Session,\n state: CollaborationConnectionState,\n ): void {\n const previous = connection.value;\n connection.value = state;\n if (\n state === \"connected\" &&\n previous !== null &&\n previous !== \"connected\"\n ) {\n // Back after a drop: refresh locks and members, and fetch what was missed.\n join(session, true);\n }\n }\n\n function switchTo(template: CollaborationTemplate | null): void {\n if (active && active.templateId === template?.id) return;\n if (active) {\n clearTypingTimer();\n retire(active);\n active = null;\n }\n members.value = [];\n locks.value = new Map();\n connection.value = null;\n wanted = null;\n reconcileLock();\n bumpHistory();\n if (template) start(template);\n }\n\n // --- Locks -----------------------------------------------------------------\n\n function setLock(lock: BlockLock): void {\n if (lock.holder.sessionId === sessionId) return;\n const next = new Map(\n [...locks.value].filter(\n ([, existing]) => existing.holder.sessionId !== lock.holder.sessionId,\n ),\n );\n next.set(lock.blockId, lock);\n locks.value = next;\n if (editor?.state.selectedBlockId === lock.blockId) {\n editor.selectBlock(null);\n notify({\n type: \"selectionRefused\",\n blockId: lock.blockId,\n holder: lock.holder,\n });\n }\n }\n\n function removeLock(blockId: string, holderSession: string): void {\n if (locks.value.get(blockId)?.holder.sessionId !== holderSession) return;\n const next = new Map(locks.value);\n next.delete(blockId);\n locks.value = next;\n }\n\n function replaceLocks(list: readonly BlockLock[], merge: boolean): void {\n const next = merge ? new Map(locks.value) : new Map<string, BlockLock>();\n for (const lock of list) {\n if (lock.holder.sessionId === sessionId) continue;\n if (merge && next.has(lock.blockId)) continue;\n next.set(lock.blockId, lock);\n }\n locks.value = next;\n }\n\n function setMembers(next: readonly CollaborationMember[]): void {\n const present = new Set(next.map((member) => member.sessionId));\n const departed = new Set(\n members.value\n .map((member) => member.sessionId)\n .filter((id) => !present.has(id)),\n );\n members.value = next;\n if (departed.size === 0) return;\n const remaining = new Map(\n [...locks.value].filter(\n ([, lock]) => !departed.has(lock.holder.sessionId),\n ),\n );\n if (remaining.size !== locks.value.size) locks.value = remaining;\n }\n\n function queueLock(task: () => Promise<void>): void {\n lockQueue = lockQueue.then(task).catch(reportError);\n }\n\n /** Move what the backend granted toward what is selected, one call at a time. */\n function reconcileLock(): void {\n if (!locking) return;\n queueLock(async () => {\n if (held && (wanted === null || held.templateId !== wanted.templateId)) {\n // Acquiring another block on the same template releases this one on\n // the backend, so only release explicitly when nothing replaces it.\n const releasing = held;\n held = null;\n stopRenewing();\n await provider.releaseLock!(releasing.templateId, releasing.blockId);\n }\n const target = wanted;\n if (\n !target ||\n (held?.templateId === target.templateId &&\n held.blockId === target.blockId)\n ) {\n return;\n }\n try {\n await provider.acquireLock!(target.templateId, target.blockId);\n held = target;\n startRenewing();\n forgetLock(target.blockId);\n } catch (error) {\n if (held?.templateId === target.templateId) {\n // Refused: the previous block is still this session's on the backend.\n const releasing = held;\n held = null;\n stopRenewing();\n await provider.releaseLock!(\n releasing.templateId,\n releasing.blockId,\n ).catch(() => {});\n }\n if (wanted !== target) return;\n refuseSelection(target.blockId, error);\n }\n });\n }\n\n function forgetLock(blockId: string): void {\n if (!locks.value.has(blockId)) return;\n const next = new Map(locks.value);\n next.delete(blockId);\n locks.value = next;\n }\n\n function refuseSelection(blockId: string, error: unknown): void {\n const code = refusalCode(error);\n if (code === \"lock_held\" || code === \"block_missing\") {\n wanted = null;\n const holder = refusalHolder(error);\n if (holder) {\n // Records the holder, deselects and says who it is.\n setLock({ blockId, holder, expiresAt: \"\" });\n } else if (editor?.state.selectedBlockId === blockId) {\n editor.selectBlock(null);\n }\n return;\n }\n // A failure in transit: keep the selection and try again shortly.\n reportError(error);\n setTimeout(() => {\n if (wanted?.blockId === blockId) reconcileLock();\n }, RETRY_DELAYS_MS[0]);\n }\n\n function startRenewing(): void {\n if (renewTimer) return;\n renewTimer = setInterval(() => {\n queueLock(async () => {\n const current = held;\n if (!current) return;\n try {\n await provider.renewLock!(current.templateId, current.blockId);\n } catch (error) {\n if (refusalCode(error) === \"lock_lost\") {\n held = null;\n stopRenewing();\n reconcileLock();\n return;\n }\n reportError(error);\n }\n });\n }, COLLABORATION_LOCK_RENEW_MS);\n }\n\n function stopRenewing(): void {\n if (renewTimer) {\n clearInterval(renewTimer);\n renewTimer = null;\n }\n }\n\n function onSelection(blockId: string | null): void {\n flush();\n if (!active) return;\n wanted = blockId ? { templateId: active.templateId, blockId } : null;\n reconcileLock();\n }\n\n /** The page is going away: send what's typed, let go of the lock, leave. */\n function onPageHide(): void {\n flush();\n if (held && locking) {\n void provider.releaseLock!(held.templateId, held.blockId).catch(() => {});\n held = null;\n stopRenewing();\n }\n active?.leave?.();\n if (active) active.leave = null;\n }\n\n // --- History ---------------------------------------------------------------\n\n function step(direction: \"undo\" | \"redo\"): void {\n const session = active;\n if (!session || suspended) return;\n flush();\n const entry = (direction === \"undo\" ? session.undo : session.redo).pop();\n if (!entry) return;\n bumpHistory();\n let next: TemplateContent;\n try {\n next = applyOperations(session.view, entry.ops, lockContext());\n } catch (error) {\n if (!(error instanceof OperationError)) throw error;\n notify({ type: \"undoSkipped\", code: error.code });\n return;\n }\n const counter: HistoryEntry = {\n ops: invertOperations(session.view, entry.ops),\n };\n pushCapped(direction === \"undo\" ? session.redo : session.undo, counter);\n session.view = next;\n renderView(next);\n editor?.markDirty();\n enqueue(session, entry.ops, direction, counter);\n }\n\n function applyFromTurn(\n ops: Operation[],\n options: CollaborationApplyOptions,\n ): CollaborationApplyResult {\n const session = active;\n if (!editor || !session || suspended) {\n return {\n applied: false,\n error: new OperationError(\n \"invalid_operation\",\n 0,\n \"No template is open for editing.\",\n ),\n };\n }\n // Typing not sent yet becomes its own batch, and its own undo step, first.\n flush();\n let next: TemplateContent;\n let inverse: Operation[];\n try {\n next = applyOperations(session.view, ops, lockContext());\n inverse = invertOperations(session.view, ops);\n } catch (error) {\n if (error instanceof OperationError) return { applied: false, error };\n throw error;\n }\n const top = session.undo.at(-1);\n let entry: HistoryEntry;\n if (top && top.turn === options.turn) {\n // Undoing the turn undoes this batch first, then the earlier ones.\n top.ops = [...inverse, ...top.ops];\n entry = top;\n } else {\n entry = { ops: inverse, turn: options.turn };\n pushCapped(session.undo, entry);\n }\n session.redo = [];\n bumpHistory();\n session.view = next;\n renderView(next);\n editor.markDirty();\n // Like an edit, a batch the backend refuses takes its undo entry back.\n // If it joined a turn, that turn's whole local undo entry goes.\n enqueue(session, ops, options.origin, entry);\n return { applied: true, inverse };\n }\n\n // --- Public surface --------------------------------------------------------\n\n function attach(target: CollaborationEditor): void {\n editor = target;\n stopWatching.push(\n watch(\n () => target.state.template,\n (template) => switchTo(template),\n {\n immediate: true,\n },\n ),\n watch(() => target.content.value, scheduleCapture, { deep: true }),\n watch(() => target.state.selectedBlockId, onSelection),\n watch(\n () => target.state.isLoading,\n (loading) => (loading ? capture(true, true) : scheduleCapture()),\n ),\n );\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"pagehide\", onPageHide);\n window.addEventListener(\"blur\", flush);\n }\n if (getCurrentScope()) onScopeDispose(destroy);\n }\n\n function settle(): Promise<void> {\n flush();\n const session = active;\n if (!session || session.pending.length === 0) return Promise.resolve();\n return new Promise((resolve, reject) =>\n session.waiters.push({ resolve, reject }),\n );\n }\n\n function suspend(): void {\n flush();\n suspended = true;\n }\n\n function resume(): void {\n suspended = false;\n if (active) renderView(active.view);\n }\n\n function destroy(): void {\n if (destroyed) return;\n flush();\n for (const stop of stopWatching.splice(0)) stop();\n clearTypingTimer();\n onPageHide();\n destroyed = true;\n if (active) retire(active);\n active = null;\n stopRenewing();\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"pagehide\", onPageHide);\n window.removeEventListener(\"blur\", flush);\n }\n }\n\n return {\n sessionId,\n members,\n locks,\n connection,\n revision,\n canUndo,\n canRedo,\n attach,\n undo: () => step(\"undo\"),\n redo: () => step(\"redo\"),\n clearHistory: () => {\n if (!active) return;\n active.undo = [];\n active.redo = [];\n bumpHistory();\n },\n flush,\n settle,\n suspend,\n resume,\n catchUp,\n hasConfirmedBlock: (blockId) =>\n active ? containsBlock(active.confirmed.blocks, blockId) : false,\n applyOperations: applyFromTurn,\n destroy,\n };\n}\n","import type {\n Block,\n SavedBlock,\n SavedBlockPatch,\n SavedBlocksListParams,\n SavedBlocksProvider,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport { computed, ref, type ComputedRef, type Ref } from \"vue\";\nimport { notifyHandler } from \"./error-reporting\";\n\nexport interface UseSavedBlocksOptions {\n /**\n * Storage backend. Supplied by the consumer via `init({ savedBlocks })`, or\n * by Cloud's own adapter — this composable is transport-agnostic and never\n * talks to a network itself.\n */\n provider: SavedBlocksProvider;\n onError?: (error: Error) => void;\n}\n\nexport interface UseSavedBlocksReturn {\n /** In-memory mirror of the provider's list, kept in sync after each call. */\n savedBlocks: Ref<SavedBlock[]>;\n isLoading: Ref<boolean>;\n /**\n * Distinct categories across the loaded entries, alphabetically sorted.\n *\n * Derived rather than stored: a category exists exactly as long as an entry\n * carries it. Sorting here is not a violation of \"the editor never re-sorts\"\n * — that rule protects the provider's ordering of *entries*, whereas these\n * are strings with no given order.\n */\n categories: ComputedRef<string[]>;\n /**\n * Whether the provider supplied each mutation at all — `false` on the\n * provider means the capability does not exist for this user, and the UI\n * hides the corresponding affordance entirely.\n *\n * Constant for the provider's lifetime, but exposed as computed refs so\n * consumers can bind them the same way as everything else here.\n */\n canCreate: ComputedRef<boolean>;\n canUpdate: ComputedRef<boolean>;\n canDelete: ComputedRef<boolean>;\n /**\n * Whether a *particular* entry may be edited / deleted: the capability must\n * exist AND the entry must not opt out via `canUpdate` / `canDelete`.\n *\n * Use these rather than reading the flags directly, so every surface asks the\n * question the same way and the \"absent means allowed\" default lives in one\n * place.\n */\n canUpdateBlock: (block: SavedBlock) => boolean;\n canDeleteBlock: (block: SavedBlock) => boolean;\n /**\n * Re-read from the provider. The editor always calls this bare and filters\n * in memory; `params` exists for headless callers that want the provider to\n * filter instead (see {@link SavedBlocksListParams}).\n */\n load: (params?: SavedBlocksListParams) => Promise<void>;\n /** Rejects with an {@link SdkError} when the provider disabled `create`. */\n create: (\n name: string,\n content: Block[],\n category?: string,\n ) => Promise<SavedBlock>;\n /** Rejects when `update` is disabled or the entry opts out of editing. */\n update: (id: string, patch: SavedBlockPatch) => Promise<SavedBlock>;\n /** Rejects when `delete` is disabled or the entry opts out of deletion. */\n remove: (id: string) => Promise<void>;\n}\n\n/**\n * Reactive state over a {@link SavedBlocksProvider}.\n *\n * Owns the list, the loading flag, and the local mutations that keep the list\n * consistent after each successful call, so a provider only has to implement\n * four promise-returning methods. Errors are reported through `onError` and\n * re-thrown, leaving the local list untouched on failure.\n */\nexport function useSavedBlocks(\n options: UseSavedBlocksOptions,\n): UseSavedBlocksReturn {\n const { provider } = options;\n\n const savedBlocks = ref<SavedBlock[]>([]);\n const isLoading = ref(false);\n\n const categories = computed(() => {\n const seen = new Set<string>();\n for (const block of savedBlocks.value) {\n const category = block.category?.trim();\n if (category) seen.add(category);\n }\n return [...seen].sort((a, b) => a.localeCompare(b));\n });\n\n const canCreate = computed(() => typeof provider.create === \"function\");\n const canUpdate = computed(() => typeof provider.update === \"function\");\n const canDelete = computed(() => typeof provider.delete === \"function\");\n\n /** Absent flag means allowed — the flags exist only to forbid. */\n function canUpdateBlock(block: SavedBlock): boolean {\n return canUpdate.value && block.canUpdate !== false;\n }\n\n function canDeleteBlock(block: SavedBlock): boolean {\n return canDelete.value && block.canDelete !== false;\n }\n\n /**\n * The UI hides disabled actions, so reaching one of these means a programmatic\n * caller went around it. Fail loudly rather than silently no-op: a resolved\n * promise would read as \"saved\" to whoever awaited it.\n */\n function refuse(action: string, reason: string): never {\n throw new SdkError(\n `[Templatical] Saved blocks: ${action} is ${reason}. Check the capability before calling — the editor's own UI hides the action.`,\n );\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for a saved block that was created.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n async function load(params?: SavedBlocksListParams): Promise<void> {\n isLoading.value = true;\n try {\n savedBlocks.value = await provider.list(params);\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n isLoading.value = false;\n }\n }\n\n async function create(\n name: string,\n content: Block[],\n category?: string,\n ): Promise<SavedBlock> {\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") {\n refuse(\"create\", \"disabled by the provider\");\n }\n try {\n // Omit the key entirely when unset, rather than sending `undefined` —\n // a provider serialising the input to JSON would otherwise be handed an\n // explicit null-ish field it never asked for.\n const created = await providerCreate(\n category ? { name, content, category } : { name, content },\n );\n savedBlocks.value = [created, ...savedBlocks.value];\n notify(() => provider.onCreated?.(created));\n return created;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function update(\n id: string,\n patch: SavedBlockPatch,\n ): Promise<SavedBlock> {\n const { update: providerUpdate } = provider;\n if (typeof providerUpdate !== \"function\") {\n refuse(\"update\", \"disabled by the provider\");\n }\n // An id absent from the local list isn't refused — a headless caller may\n // legitimately patch something it never loaded. Only a loaded entry that\n // explicitly opted out is blocked.\n const local = savedBlocks.value.find((b) => b.id === id);\n if (local && local.canUpdate === false) {\n refuse(\"update\", `not permitted for entry \"${id}\"`);\n }\n try {\n const updated = await providerUpdate(id, patch);\n savedBlocks.value = savedBlocks.value.map((b) =>\n b.id === id ? updated : b,\n );\n notify(() => provider.onUpdated?.(updated));\n return updated;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function remove(id: string): Promise<void> {\n const { delete: providerDelete } = provider;\n if (typeof providerDelete !== \"function\") {\n refuse(\"delete\", \"disabled by the provider\");\n }\n // Captured before the delete resolves: `onDeleted` carries the removed\n // entry itself, and there is nothing left to read once the list is\n // filtered.\n const removed = savedBlocks.value.find((b) => b.id === id) ?? null;\n if (removed && removed.canDelete === false) {\n refuse(\"delete\", `not permitted for entry \"${id}\"`);\n }\n try {\n await providerDelete(id);\n savedBlocks.value = savedBlocks.value.filter((b) => b.id !== id);\n if (removed) {\n notify(() => provider.onDeleted?.(removed));\n }\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n return {\n savedBlocks,\n isLoading,\n categories,\n canCreate,\n canUpdate,\n canDelete,\n canUpdateBlock,\n canDeleteBlock,\n load,\n create,\n update,\n remove,\n };\n}\n","import type {\n SavedBlock,\n SavedBlockInput,\n SavedBlockPatch,\n SavedBlocksListParams,\n SavedBlocksProvider,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\n\nexport interface LocalStorageSavedBlocksOptions {\n /**\n * `localStorage` key holding the serialized array.\n *\n * @default \"templatical:saved-blocks\"\n */\n key?: string;\n}\n\nconst DEFAULT_KEY = \"templatical:saved-blocks\";\n\n/**\n * Browser-local {@link SavedBlocksProvider} backed by `localStorage`.\n *\n * Opt-in: pass it explicitly as `init({ savedBlocks: createLocalStorageSavedBlocksProvider() })`.\n * The editor never falls back to it, so consumers without a provider keep the\n * feature — and its UI — entirely off.\n *\n * Intended for demos, prototypes, and single-device use. Entries live in one\n * browser profile only: they don't sync across devices or users, and clearing\n * site data removes them. Back saved blocks with your own API for anything\n * that needs to outlive a browser profile.\n */\nexport function createLocalStorageSavedBlocksProvider(\n options: LocalStorageSavedBlocksOptions = {},\n): SavedBlocksProvider {\n const storageKey = options.key ?? DEFAULT_KEY;\n\n function getStorage(): Storage {\n // Core targets a neutral platform, so `localStorage` isn't guaranteed —\n // fail with an actionable message rather than a bare ReferenceError.\n if (typeof localStorage === \"undefined\") {\n throw new Error(\n \"[Templatical] createLocalStorageSavedBlocksProvider requires a browser environment with localStorage. Supply your own SavedBlocksProvider for server-side or non-browser use.\",\n );\n }\n return localStorage;\n }\n\n function readAll(): SavedBlock[] {\n const raw = getStorage().getItem(storageKey);\n if (!raw) return [];\n try {\n const parsed: unknown = JSON.parse(raw);\n // Tolerate a corrupted or hand-edited value rather than breaking the\n // editor: treat anything that isn't an array as \"no saved blocks\".\n return Array.isArray(parsed) ? (parsed as SavedBlock[]) : [];\n } catch {\n return [];\n }\n }\n\n function writeAll(blocks: SavedBlock[]): void {\n getStorage().setItem(storageKey, JSON.stringify(blocks));\n }\n\n // Every method is `async` so a synchronous failure (a missing\n // `localStorage`, a quota error from `setItem`) surfaces as a rejected\n // promise rather than a sync throw — callers of a `Promise`-returning\n // contract must be able to rely on `.catch()`.\n return {\n // The editor calls this bare and filters in memory; the params are honored\n // for headless callers that would rather the provider did the work.\n async list(params?: SavedBlocksListParams): Promise<SavedBlock[]> {\n const all = readAll();\n const search = params?.search?.trim().toLowerCase();\n const category = params?.category?.trim();\n return all.filter((b) => {\n if (search && !b.name.toLowerCase().includes(search)) return false;\n if (category && b.category !== category) return false;\n return true;\n });\n },\n\n // All three mutations are enabled: a browser-local store has no notion of\n // permissions, so there is nothing to forbid.\n async create(input: SavedBlockInput): Promise<SavedBlock> {\n const now = new Date().toISOString();\n const created: SavedBlock = {\n id: generateId(),\n name: input.name,\n content: input.content,\n ...(input.category ? { category: input.category } : {}),\n createdAt: now,\n updatedAt: now,\n };\n // Newest-first, matching the order the composable applies locally after\n // a create — so a reload preserves what the user just saw.\n writeAll([created, ...readAll()]);\n return created;\n },\n\n async update(id: string, patch: SavedBlockPatch): Promise<SavedBlock> {\n const all = readAll();\n const index = all.findIndex((b) => b.id === id);\n // Mirrors a REST 404 so provider behavior is consistent across adapters.\n if (index === -1) {\n throw new Error(`[Templatical] Saved block not found: ${id}`);\n }\n const updated: SavedBlock = {\n ...all[index],\n ...patch,\n updatedAt: new Date().toISOString(),\n };\n const next = [...all];\n next[index] = updated;\n writeAll(next);\n return updated;\n },\n\n async delete(id: string): Promise<void> {\n const all = readAll();\n if (!all.some((b) => b.id === id)) {\n throw new Error(`[Templatical] Saved block not found: ${id}`);\n }\n writeAll(all.filter((b) => b.id !== id));\n },\n };\n}\n","import type {\n MediaAsset,\n MediaAssetPatch,\n MediaCreateInput,\n MediaListPage,\n MediaListParams,\n MediaProvider,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\n\nexport interface LocalStorageMediaProviderOptions {\n /**\n * `localStorage` key holding the serialized array.\n *\n * @default \"templatical:media\"\n */\n key?: string;\n}\n\nconst DEFAULT_KEY = \"templatical:media\";\nconst PAGE_SIZE = 50;\n\n/**\n * Browser-local {@link MediaProvider} backed by `localStorage`.\n *\n * Opt-in: pass it explicitly as `init({ media: createLocalStorageMediaProvider() })`.\n * The editor never falls back to it, so consumers without a provider keep\n * image fields URL-only.\n *\n * `create` stores the file as a data URL. That is a demo-sized persistence\n * model — `localStorage` quotas are typically ~5 MB, and a few large\n * images will fill it. Back a real gallery with your own API.\n *\n * Folders, replace, import, usage, frequently-used and quota are `false`:\n * this adapter is a flat list of assets.\n */\nexport function createLocalStorageMediaProvider(\n options: LocalStorageMediaProviderOptions = {},\n): MediaProvider {\n const storageKey = options.key ?? DEFAULT_KEY;\n\n function getStorage(): Storage {\n // Core targets a neutral platform, so `localStorage` isn't guaranteed —\n // fail with an actionable message rather than a bare ReferenceError.\n if (typeof localStorage === \"undefined\") {\n throw new Error(\n \"[Templatical] createLocalStorageMediaProvider requires a browser environment with localStorage. Supply your own MediaProvider for server-side or non-browser use.\",\n );\n }\n return localStorage;\n }\n\n function readAll(): MediaAsset[] {\n const raw = getStorage().getItem(storageKey);\n if (!raw) return [];\n try {\n const parsed: unknown = JSON.parse(raw);\n // Tolerate a corrupted or hand-edited value rather than breaking the\n // editor: treat anything that isn't an array as \"no assets\".\n return Array.isArray(parsed) ? (parsed as MediaAsset[]) : [];\n } catch {\n return [];\n }\n }\n\n function writeAll(assets: MediaAsset[]): void {\n getStorage().setItem(storageKey, JSON.stringify(assets));\n }\n\n function readFileAsDataURL(file: File): Promise<string> {\n if (typeof FileReader === \"undefined\") {\n return Promise.reject(\n new Error(\n \"[Templatical] createLocalStorageMediaProvider requires a browser environment with FileReader.\",\n ),\n );\n }\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (typeof reader.result !== \"string\") {\n reject(new Error(\"[Templatical] Failed to read file as a data URL.\"));\n return;\n }\n resolve(reader.result);\n };\n reader.onerror = () => {\n reject(\n reader.error ??\n new Error(\"[Templatical] Failed to read file as a data URL.\"),\n );\n };\n reader.readAsDataURL(file);\n });\n }\n\n // Every method is `async` so a synchronous failure (a missing\n // `localStorage`, a quota error from `setItem`) surfaces as a rejected\n // promise rather than a sync throw — callers of a `Promise`-returning\n // contract must be able to rely on `.catch()`.\n return {\n async list(params?: MediaListParams): Promise<MediaListPage> {\n const all = readAll();\n const search = params?.search?.trim().toLowerCase();\n const filtered = search\n ? all.filter((asset) => {\n const filename = asset.filename?.toLowerCase() ?? \"\";\n const alt = asset.alt?.toLowerCase() ?? \"\";\n return filename.includes(search) || alt.includes(search);\n })\n : all;\n const parsed = params?.cursor ? Number.parseInt(params.cursor, 10) : 0;\n const offset = Number.isFinite(parsed) && parsed > 0 ? parsed : 0;\n const items = filtered.slice(offset, offset + PAGE_SIZE);\n const page: MediaListPage = { items };\n const nextOffset = offset + items.length;\n if (nextOffset < filtered.length) {\n page.nextCursor = String(nextOffset);\n }\n return page;\n },\n\n async create(input: MediaCreateInput): Promise<MediaAsset> {\n const url = await readFileAsDataURL(input.file);\n const now = new Date().toISOString();\n const created: MediaAsset = {\n id: generateId(),\n url,\n filename: input.filename || input.file.name,\n createdAt: now,\n updatedAt: now,\n size: input.file.size,\n };\n if (input.alt !== undefined) created.alt = input.alt;\n if (input.file.type) created.mimeType = input.file.type;\n if (input.folderId !== undefined) created.folderId = input.folderId;\n // Newest-first, matching the order the composable applies locally after\n // a create — so a reload preserves what the user just saw.\n writeAll([created, ...readAll()]);\n return created;\n },\n\n async update(id: string, patch: MediaAssetPatch): Promise<MediaAsset> {\n const all = readAll();\n const index = all.findIndex((asset) => asset.id === id);\n // Mirrors a REST 404 so provider behavior is consistent across adapters.\n if (index === -1) {\n throw new Error(`[Templatical] Media asset not found: ${id}`);\n }\n const current = all[index];\n const updated: MediaAsset = {\n ...current,\n ...patch,\n id: current.id,\n url: current.url,\n updatedAt: new Date().toISOString(),\n };\n const next = [...all];\n next[index] = updated;\n writeAll(next);\n return updated;\n },\n\n async delete(ids: string[]): Promise<void> {\n const all = readAll();\n for (const id of ids) {\n if (!all.some((asset) => asset.id === id)) {\n throw new Error(`[Templatical] Media asset not found: ${id}`);\n }\n }\n const remove = new Set(ids);\n writeAll(all.filter((asset) => !remove.has(asset.id)));\n },\n\n folders: false,\n replace: false,\n importFromUrl: false,\n checkUsage: false,\n frequentlyUsed: false,\n storage: false,\n };\n}\n","import type {\n Template,\n TemplateContent,\n TemplateVersion,\n VersionHistoryListParams,\n VersionHistoryProvider,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport { computed, ref, type ComputedRef, type Ref } from \"vue\";\nimport { notifyHandler } from \"./error-reporting\";\n\nexport interface UseVersionHistoryOptions {\n /**\n * Storage backend. Supplied by the consumer via `init({ versionHistory })`,\n * or by Cloud's own adapter — this composable is transport-agnostic and never\n * talks to a network itself.\n */\n provider: VersionHistoryProvider;\n /**\n * Which template's history this is. A getter rather than a value because a\n * session can outlive the template: `initCloud()` constructs the feature at\n * setup and only learns the id once `create()` / `load()` resolves.\n */\n getTemplateId: () => string | null;\n onError?: (error: Error) => void;\n}\n\nexport interface UseVersionHistoryReturn {\n /** The provider's list, in the provider's order. Never re-sorted. */\n versions: Ref<TemplateVersion[]>;\n isLoading: Ref<boolean>;\n isRestoring: Ref<boolean>;\n /**\n * Cursor for the page after the one currently held, or `undefined` when the\n * provider signalled there is no more. The editor loads one page and ignores\n * this; it is here so a headless caller can page without reaching past the\n * composable.\n */\n nextCursor: Ref<string | undefined>;\n /** Whether the provider supplied each mutation at all. */\n canCreate: ComputedRef<boolean>;\n canRestore: ComputedRef<boolean>;\n /** Re-read the list. The editor calls this bare; `params` is for headless callers. */\n load: (params?: VersionHistoryListParams) => Promise<void>;\n /**\n * The version's content **if it is already in hand** — the provider's\n * `content` hint, or a previous `get` this composable cached. `null` means a\n * round-trip is required.\n *\n * Callers that must not block (scrubbing through history swaps the canvas in\n * the same tick) check this first and only await {@link resolveContent} when\n * it comes back null.\n */\n peekContent: (version: TemplateVersion) => TemplateContent | null;\n /**\n * `version.content ?? await provider.get(...)`, cached per version id, so a\n * lazily-loaded version costs one round-trip on its first visit and nothing\n * afterwards.\n */\n resolveContent: (version: TemplateVersion) => Promise<TemplateContent>;\n /** Rejects with an {@link SdkError} when the provider disabled `create`. */\n create: (\n content: TemplateContent,\n meta?: { label?: string },\n ) => Promise<TemplateVersion>;\n /** Rejects with an {@link SdkError} when the provider disabled `restore`. */\n restore: (versionId: string) => Promise<Template>;\n}\n\n/**\n * Reactive state over a {@link VersionHistoryProvider}.\n *\n * Owns the list, the loading flags and the per-version content cache that keeps\n * scrubbing synchronous. Errors are reported through `onError` and re-thrown,\n * leaving the list untouched on failure.\n *\n * It deliberately does **not** create versions of its own accord. The editor\n * never records a version on a save — whoever implements `TemplatesProvider.save`\n * decides that, because they are the side that pays for the storage.\n */\nexport function useVersionHistory(\n options: UseVersionHistoryOptions,\n): UseVersionHistoryReturn {\n const { provider, getTemplateId } = options;\n\n const versions = ref<TemplateVersion[]>([]);\n const isLoading = ref(false);\n const nextCursor = ref<string | undefined>(undefined);\n const isRestoring = ref(false);\n\n /**\n * Content fetched through `get`, keyed by version id. A version that carried\n * a `content` hint never lands here — the hint is read straight off the entry,\n * so a provider that eager-loads pays no second copy.\n */\n const fetched = new Map<string, TemplateContent>();\n\n const canCreate = computed(() => typeof provider.create === \"function\");\n const canRestore = computed(() => typeof provider.restore === \"function\");\n\n /**\n * The UI hides disabled actions and renders nothing before a template exists,\n * so reaching one of these means a programmatic caller went around it. Fail\n * loudly rather than silently no-op: a resolved promise reads as \"done\".\n */\n function requireTemplateId(action: string): string {\n const templateId = getTemplateId();\n if (!templateId) {\n throw new SdkError(\n `[Templatical] Version history: ${action} needs a template. Call create() or load() first.`,\n );\n }\n return templateId;\n }\n\n function refuse(action: string): never {\n throw new SdkError(\n `[Templatical] Version history: ${action} is disabled by the provider. Check the capability before calling — the editor's own UI hides the action.`,\n );\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for a version that was recorded or restored.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n async function load(params?: VersionHistoryListParams): Promise<void> {\n const templateId = requireTemplateId(\"list\");\n isLoading.value = true;\n try {\n const page = await provider.list(templateId, params);\n versions.value = page.versions;\n nextCursor.value = page.nextCursor;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n isLoading.value = false;\n }\n }\n\n function peekContent(version: TemplateVersion): TemplateContent | null {\n return version.content ?? fetched.get(version.id) ?? null;\n }\n\n async function resolveContent(\n version: TemplateVersion,\n ): Promise<TemplateContent> {\n const known = peekContent(version);\n if (known) return known;\n\n const templateId = requireTemplateId(\"get\");\n try {\n const content = await provider.get(templateId, version.id);\n fetched.set(version.id, content);\n return content;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function create(\n content: TemplateContent,\n meta?: { label?: string },\n ): Promise<TemplateVersion> {\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") refuse(\"create\");\n const templateId = requireTemplateId(\"create\");\n try {\n const created = await providerCreate(templateId, content, meta);\n versions.value = [created, ...versions.value];\n notify(() => provider.onCreated?.(created));\n return created;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function restore(versionId: string): Promise<Template> {\n const { restore: providerRestore } = provider;\n if (typeof providerRestore !== \"function\") refuse(\"restore\");\n const templateId = requireTemplateId(\"restore\");\n\n let restored: Template;\n isRestoring.value = true;\n try {\n restored = await providerRestore(templateId, versionId);\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n isRestoring.value = false;\n }\n // Below the `finally` on purpose: `isRestoring` is cleared there, so a\n // handler must not observe the composable still reporting itself\n // mid-restore. Unreachable on failure — the catch rethrows.\n notify(() => provider.onRestored?.(restored));\n return restored;\n }\n\n return {\n nextCursor,\n versions,\n isLoading,\n isRestoring,\n canCreate,\n canRestore,\n load,\n peekContent,\n resolveContent,\n create,\n restore,\n };\n}\n","import type {\n Comment,\n CommentAuthor,\n CommentChange,\n CommentInput,\n CommentPatch,\n CommentsListParams,\n CommentsProvider,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport {\n computed,\n onScopeDispose,\n ref,\n watch,\n type ComputedRef,\n type Ref,\n} from \"vue\";\nimport { notifyHandler, wrapReportedError } from \"./error-reporting\";\n\nexport interface UseCommentsOptions {\n /**\n * Storage backend. Supplied by the consumer via `init({ comments })`, or by\n * Cloud's own adapter — this composable is transport-agnostic and never talks\n * to a network itself.\n */\n provider: CommentsProvider;\n /**\n * Which template's conversation this is. A getter rather than a value because a\n * session can outlive the template: `initCloud()` constructs the feature at\n * setup and only learns the id once `create()` / `load()` resolves.\n */\n getTemplateId: () => string | null;\n /**\n * Who is commenting, read at call time. `null` means the editor has no\n * identity, and every mutation refuses — an unattributable comment is worse\n * than no comment feature.\n */\n getUser: () => CommentAuthor | null;\n onError?: (error: Error) => void;\n}\n\nexport interface UseCommentsReturn {\n /** Thread roots in the provider's order. Never re-sorted. */\n comments: Ref<Comment[]>;\n isLoading: Ref<boolean>;\n isSubmitting: Ref<boolean>;\n /** Whether the provider supplied each mutation at all. */\n canCreate: ComputedRef<boolean>;\n canUpdate: ComputedRef<boolean>;\n canDelete: ComputedRef<boolean>;\n canResolve: ComputedRef<boolean>;\n /** Roots plus replies. */\n totalCount: ComputedRef<number>;\n /** Unresolved roots — the badge on the trigger. */\n unresolvedCount: ComputedRef<number>;\n /** Comments per anchored block id, roots plus their replies. */\n commentCountByBlock: ComputedRef<Map<string, number>>;\n /** Re-read the list. The editor calls this bare; `params` is for headless callers. */\n load: (params?: CommentsListParams) => Promise<void>;\n /** Rejects with an {@link SdkError} when `create` is disabled or there is no user. */\n create: (input: CommentInput) => Promise<Comment>;\n /** Rejects with an {@link SdkError} when `update` is disabled or there is no user. */\n update: (commentId: string, patch: CommentPatch) => Promise<Comment>;\n /** Rejects with an {@link SdkError} when `delete` is disabled or there is no user. */\n remove: (commentId: string) => Promise<void>;\n /** Rejects with an {@link SdkError} when `setResolved` is disabled or there is no user. */\n setResolved: (commentId: string, resolved: boolean) => Promise<Comment>;\n /** Look one up by id, root or reply. `null` when it isn't loaded. */\n find: (commentId: string) => Comment | null;\n /** Whether this comment was written by the current user. */\n isOwn: (comment: Comment) => boolean;\n /**\n * Apply a change that arrived from {@link CommentsProvider.subscribe}. Public\n * so a consumer driving this headlessly can push their own transport into it.\n */\n applyRemoteCreate: (comment: Comment) => void;\n applyRemoteUpdate: (comment: Comment) => void;\n applyRemoteDelete: (commentId: string, parentId?: string | null) => void;\n}\n\n/**\n * Reactive state over a {@link CommentsProvider}.\n *\n * Owns the thread list, the loading flags, the local mutations that keep the list\n * consistent, and the derived counts the chrome renders. Errors are reported\n * through `onError` and **re-thrown**, leaving the list untouched on failure —\n * the same discipline as `useSavedBlocks` and `useVersionHistory`.\n *\n * Mutations **reject** when the provider withheld them rather than resolving to\n * `null`: a resolved promise reads as \"saved\" to whoever awaited it. Ask through\n * `canCreate` / `canUpdate` / `canDelete` / `canResolve` first — which is what the\n * editor's own UI does, hiding each action rather than disabling it.\n */\nexport function useComments(options: UseCommentsOptions): UseCommentsReturn {\n const { provider, getTemplateId, getUser } = options;\n\n const comments = ref<Comment[]>([]);\n const isLoading = ref(false);\n const isSubmitting = ref(false);\n\n const canCreate = computed(() => typeof provider.create === \"function\");\n const canUpdate = computed(() => typeof provider.update === \"function\");\n const canDelete = computed(() => typeof provider.delete === \"function\");\n const canResolve = computed(() => typeof provider.setResolved === \"function\");\n\n const totalCount = computed(() =>\n comments.value.reduce(\n (sum, thread) => sum + 1 + (thread.replies?.length ?? 0),\n 0,\n ),\n );\n\n const unresolvedCount = computed(\n () => comments.value.filter((thread) => !thread.resolvedAt).length,\n );\n\n const commentCountByBlock = computed(() => {\n const map = new Map<string, number>();\n for (const thread of comments.value) {\n if (!thread.blockId) continue;\n map.set(\n thread.blockId,\n (map.get(thread.blockId) ?? 0) + 1 + (thread.replies?.length ?? 0),\n );\n }\n return map;\n });\n\n /**\n * The UI hides disabled actions and renders nothing before a template exists,\n * so reaching one of these means a programmatic caller went around it. Fail\n * loudly rather than silently no-op.\n */\n function requireTemplateId(action: string): string {\n const templateId = getTemplateId();\n if (!templateId) {\n throw new SdkError(\n `[Templatical] Comments: ${action} needs a template. Call create() or load() first.`,\n );\n }\n return templateId;\n }\n\n function requireUser(action: string): CommentAuthor {\n const user = getUser();\n if (!user) {\n throw new SdkError(\n `[Templatical] Comments: ${action} needs to know who is commenting. Pass init({ user: { id, name } }) — an unattributed comment is not written.`,\n );\n }\n return user;\n }\n\n function refuse(action: string): never {\n throw new SdkError(\n `[Templatical] Comments: ${action} is disabled by the provider. Check the capability before calling — the editor's own UI hides the action.`,\n );\n }\n\n function find(commentId: string): Comment | null {\n for (const thread of comments.value) {\n if (thread.id === commentId) return thread;\n for (const reply of thread.replies ?? []) {\n if (reply.id === commentId) return reply;\n }\n }\n return null;\n }\n\n function isOwn(comment: Comment): boolean {\n const user = getUser();\n return user !== null && comment.author.id === user.id;\n }\n\n function report(error: unknown): never {\n const wrapped = wrapReportedError(error);\n options.onError?.(wrapped);\n throw wrapped;\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for a comment that was created.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n function replaceAt<T>(list: T[], index: number, next: T): T[] {\n return [...list.slice(0, index), next, ...list.slice(index + 1)];\n }\n\n /**\n * Key-order sensitive: identical fields serialized in a different order\n * compare unequal, so an echo whose transport frame orders keys\n * differently than the mutation response it echoes would not be\n * suppressed. Accepted rather than a defect — not worth a deep-equal\n * dependency for it.\n */\n function sameComment(a: Comment | undefined, b: Comment): boolean {\n return a !== undefined && JSON.stringify(a) === JSON.stringify(b);\n }\n\n /**\n * Insert a comment, replacing an entry with the same id rather than\n * duplicating it, and report whether the list actually changed.\n *\n * The **remote** apply paths (`applyRemoteCreate` / `applyRemoteUpdate`)\n * emit their event only when this returns `true`. A transport's own\n * broadcast can come back to the sender — `CommentsProvider.subscribe`'s\n * contract permits it — and emitting unconditionally fires the handler\n * twice for one comment. The comparison below runs against the **merged**\n * result, never the raw payload: the root branch keeps already-loaded\n * `replies` when an update payload carries none, so comparing the payload\n * directly would report a change for an echo that altered nothing.\n * Deleting the `sameComment` check as a redundant no-op reinstates the\n * double-fire.\n */\n function upsert(comment: Comment): boolean {\n if (comment.parentId) {\n let changed = false;\n const next = comments.value.map((thread) => {\n if (thread.id !== comment.parentId) return thread;\n const replies = thread.replies ?? [];\n const at = replies.findIndex((reply) => reply.id === comment.id);\n if (at !== -1 && sameComment(replies[at], comment)) return thread;\n changed = true;\n return {\n ...thread,\n replies:\n at === -1 ? [...replies, comment] : replaceAt(replies, at, comment),\n };\n });\n // Assigned only when something actually changed: `.map()` always returns\n // a new array, and reassigning `comments.value` for a no-op echo would\n // still invalidate every computed reading it (`commentCountByBlock`\n // returns a new `Map` on each recompute) for nothing.\n if (changed) comments.value = next;\n return changed;\n }\n\n const at = comments.value.findIndex((thread) => thread.id === comment.id);\n if (at === -1) {\n comments.value = [...comments.value, comment];\n return true;\n }\n // A root arriving again keeps the replies already loaded: the update payload\n // for a body edit carries none, and dropping them would empty the thread.\n const next: Comment = {\n ...comment,\n replies: comment.replies ?? comments.value[at].replies,\n };\n if (sameComment(comments.value[at], next)) return false;\n comments.value = replaceAt(comments.value, at, next);\n return true;\n }\n\n function drop(commentId: string, parentId?: string | null): Comment | null {\n const existing = find(commentId);\n // `parentId` from the caller wins so a delete can be applied without the\n // comment being loaded — the subscribe path, on a thread never opened.\n const parent = parentId ?? existing?.parentId ?? null;\n\n if (parent) {\n comments.value = comments.value.map((thread) =>\n thread.id === parent\n ? {\n ...thread,\n replies: (thread.replies ?? []).filter((r) => r.id !== commentId),\n }\n : thread,\n );\n } else {\n comments.value = comments.value.filter((c) => c.id !== commentId);\n }\n return existing;\n }\n\n async function load(params?: CommentsListParams): Promise<void> {\n const templateId = requireTemplateId(\"list\");\n isLoading.value = true;\n try {\n comments.value = await provider.list(templateId, params);\n } catch (error) {\n report(error);\n } finally {\n isLoading.value = false;\n }\n }\n\n async function create(input: CommentInput): Promise<Comment> {\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") refuse(\"create\");\n const templateId = requireTemplateId(\"create\");\n requireUser(\"create\");\n isSubmitting.value = true;\n try {\n const created = await providerCreate(templateId, input);\n upsert(created);\n notify(() => provider.onCreated?.(created, { origin: \"local\" }));\n return created;\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n async function update(\n commentId: string,\n patch: CommentPatch,\n ): Promise<Comment> {\n const { update: providerUpdate } = provider;\n if (typeof providerUpdate !== \"function\") refuse(\"update\");\n const templateId = requireTemplateId(\"update\");\n requireUser(\"update\");\n isSubmitting.value = true;\n try {\n const updated = await providerUpdate(templateId, commentId, patch);\n upsert(updated);\n notify(() => provider.onUpdated?.(updated, { origin: \"local\" }));\n return updated;\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n async function remove(commentId: string): Promise<void> {\n const { delete: providerDelete } = provider;\n if (typeof providerDelete !== \"function\") refuse(\"delete\");\n const templateId = requireTemplateId(\"delete\");\n requireUser(\"delete\");\n // Snapshotted before the request: the event carries what was deleted, and\n // there is nothing to read afterwards.\n const existing = find(commentId);\n isSubmitting.value = true;\n try {\n await providerDelete(templateId, commentId);\n drop(commentId);\n if (existing) {\n notify(() => provider.onDeleted?.(existing, { origin: \"local\" }));\n }\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n async function setResolved(\n commentId: string,\n resolved: boolean,\n ): Promise<Comment> {\n const { setResolved: providerSetResolved } = provider;\n if (typeof providerSetResolved !== \"function\") refuse(\"setResolved\");\n const templateId = requireTemplateId(\"setResolved\");\n requireUser(\"setResolved\");\n isSubmitting.value = true;\n try {\n const updated = await providerSetResolved(\n templateId,\n commentId,\n resolved,\n );\n upsert(updated);\n // The stored result decides which event fires, not the requested state: a\n // store may refuse to reopen, and the consumer should hear what happened.\n notify(() =>\n (updated.resolvedAt ? provider.onResolved : provider.onUnresolved)?.(\n updated,\n { origin: \"local\" },\n ),\n );\n return updated;\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n function applyRemoteCreate(comment: Comment): void {\n // A reply whose parent thread isn't loaded reports nothing: `upsert`\n // finds no matching thread to attach it to, so the list doesn't change.\n if (upsert(comment)) {\n notify(() => provider.onCreated?.(comment, { origin: \"remote\" }));\n }\n }\n\n function applyRemoteUpdate(comment: Comment): void {\n if (!upsert(comment)) return;\n notify(() =>\n (comment.resolvedAt\n ? provider.onResolved\n : // An unresolve and a body edit are both \"not resolved\"; only a payload\n // that used to be resolved is an unresolve, and the transport doesn't\n // say. `updated` is the honest answer for a plain change.\n provider.onUpdated)?.(comment, { origin: \"remote\" }),\n );\n }\n\n function applyRemoteDelete(\n commentId: string,\n parentId?: string | null,\n ): void {\n const existing = drop(commentId, parentId);\n if (existing) {\n notify(() => provider.onDeleted?.(existing, { origin: \"remote\" }));\n }\n }\n\n return {\n comments,\n isLoading,\n isSubmitting,\n canCreate,\n canUpdate,\n canDelete,\n canResolve,\n totalCount,\n unresolvedCount,\n commentCountByBlock,\n load,\n create,\n update,\n remove,\n setResolved,\n find,\n isOwn,\n applyRemoteCreate,\n applyRemoteUpdate,\n applyRemoteDelete,\n };\n}\n\nexport interface UseCommentListenerOptions {\n comments: UseCommentsReturn;\n provider: CommentsProvider;\n getTemplateId: () => string | null;\n}\n\n/**\n * Wire {@link CommentsProvider.subscribe} into the three `applyRemote*` paths.\n *\n * A no-op when the provider has no `subscribe` — realtime is optional, and\n * comments without it work exactly the same except that a colleague's comment\n * only appears on the next read. Nothing here knows about a transport: Cloud's\n * Pusher channel lives inside `createCloudCommentsProvider`, and an SSE or\n * long-poll implementation is the same three lines on the consumer's side.\n *\n * Re-subscribes when the template id changes, and unsubscribes on scope dispose —\n * so a session that loads a second template does not keep receiving the first\n * one's comments.\n */\nexport function useCommentListener(options: UseCommentListenerOptions): void {\n const { comments, provider, getTemplateId } = options;\n const { subscribe } = provider;\n if (typeof subscribe !== \"function\") return;\n\n let unsubscribe: (() => void) | null = null;\n\n function stop(): void {\n unsubscribe?.();\n unsubscribe = null;\n }\n\n watch(\n () => getTemplateId(),\n (templateId) => {\n stop();\n if (!templateId) return;\n unsubscribe = subscribe(templateId, (change: CommentChange) => {\n switch (change.type) {\n case \"created\":\n comments.applyRemoteCreate(change.comment);\n break;\n case \"updated\":\n comments.applyRemoteUpdate(change.comment);\n break;\n case \"deleted\":\n comments.applyRemoteDelete(change.commentId, change.parentId);\n break;\n }\n });\n },\n { immediate: true },\n );\n\n onScopeDispose(stop);\n}\n"],"mappings":"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"}
|