@templatical/core 0.48.0 → 0.49.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/dist/index.d.ts CHANGED
@@ -1,12 +1,115 @@
1
- import { i as useEditor, n as UseEditorOptions, r as UseEditorReturn, t as EditorState } from "./editor-Bl4WsPU3.js";
2
- import { Block, BlockDefaults, BlockType, Comment, CommentAuthor, CommentInput, CommentPatch, CommentsListParams, CommentsProvider, CustomBlock, CustomBlockDefinition, MediaProvider, SavedBlock, SavedBlockPatch, SavedBlocksListParams, SavedBlocksProvider, Template, TemplateContent, TemplateSettings, TemplateVersion, VersionHistoryListParams, VersionHistoryProvider } from "@templatical/types";
3
- import { ComputedRef, Ref } from "@vue/reactivity";
1
+ import { Block, BlockDefaults, BlockLock, BlockLockHolder, BlockType, CollaborationActor, CollaborationConflictCode, CollaborationConnectionState, CollaborationMember, CollaborationProvider, CollaborationRemoteChange, Comment, CommentAuthor, CommentInput, CommentPatch, CommentsListParams, CommentsProvider, CustomBlock, CustomBlockDefinition, MediaProvider, Operation, SavedBlock, SavedBlockPatch, SavedBlocksListParams, SavedBlocksProvider, Template, TemplateContent, TemplateDefaults, TemplateSaveTrigger, TemplateSettings, TemplateVersion, TemplatesProvider, UiTheme, VersionHistoryListParams, VersionHistoryProvider, ViewportSize } from "@templatical/types";
2
+ import { ComputedRef, DeepReadonly, Ref } from "@vue/reactivity";
4
3
  import { ComputedRef as ComputedRef$1, Ref as Ref$1 } from "vue";
4
+ //#region src/editor.d.ts
5
+ interface EditorState {
6
+ /**
7
+ * The template currently being edited, as the store last returned it — `null`
8
+ * until `create()` or `load()` resolves, and always `null` without a
9
+ * {@link TemplatesProvider}.
10
+ *
11
+ * Carries identity and name only for practical purposes: its `content` is the
12
+ * store's copy from the last round-trip, whereas `state.content` is what the
13
+ * user is editing.
14
+ */
15
+ template: Template | null;
16
+ content: TemplateContent;
17
+ selectedBlockId: string | null;
18
+ viewport: ViewportSize;
19
+ darkMode: boolean;
20
+ previewMode: boolean;
21
+ isDirty: boolean;
22
+ /** True for the duration of a `save()` call. */
23
+ isSaving: boolean;
24
+ /** True for the duration of a `create()` or `load()` call. */
25
+ isLoading: boolean;
26
+ uiTheme: UiTheme;
27
+ }
28
+ interface UseEditorOptions {
29
+ content: TemplateContent;
30
+ defaultFontFamily?: string;
31
+ templateDefaults?: TemplateDefaults;
32
+ /**
33
+ * Blocks other collaborators hold. A locked block can't be selected, and the
34
+ * editor's own mutators leave it alone.
35
+ */
36
+ lockedBlocks?: Readonly<Ref<ReadonlyMap<string, unknown>>>;
37
+ /**
38
+ * Storage backend for the template's save/load lifecycle. Omit it and
39
+ * `create()` / `load()` / `save()` reject — the editor keeps working as a
40
+ * purely local editing surface.
41
+ */
42
+ templates?: TemplatesProvider;
43
+ /**
44
+ * Called with any error a provider call rejects with, before the rejection is
45
+ * re-thrown to the caller.
46
+ */
47
+ onError?: (error: Error) => void;
48
+ }
49
+ interface UseEditorReturn {
50
+ state: DeepReadonly<EditorState>;
51
+ content: Ref<TemplateContent>;
52
+ selectedBlock: Ref<Block | null>;
53
+ setContent: (content: TemplateContent, markDirty?: boolean) => void;
54
+ selectBlock: (blockId: string | null) => void;
55
+ setViewport: (viewport: ViewportSize) => void;
56
+ setDarkMode: (darkMode: boolean) => void;
57
+ setPreviewMode: (previewMode: boolean) => void;
58
+ setUiTheme: (theme: UiTheme) => void;
59
+ updateBlock: (blockId: string, updates: Partial<Block>) => void;
60
+ updateSettings: (updates: Partial<TemplateSettings>) => void;
61
+ addBlock: (block: Block, targetSectionId?: string, columnIndex?: number, index?: number) => void;
62
+ removeBlock: (blockId: string) => void;
63
+ moveBlock: (blockId: string, newIndex: number, targetSectionId?: string, columnIndex?: number) => void;
64
+ isBlockLocked: (blockId: string) => boolean;
65
+ markDirty: () => void;
66
+ findBlockLocation: (blockId: string) => {
67
+ targetSectionId?: string;
68
+ columnIndex?: number;
69
+ index: number;
70
+ } | null;
71
+ /**
72
+ * Rename the loaded template locally and mark the editor dirty. The new name
73
+ * reaches the store on the next `save()`, in the same patch as the content —
74
+ * a rename is an ordinary unsaved change, not a side channel.
75
+ *
76
+ * This method only stages it. The editor's inline rename field commits by
77
+ * calling `setName()` and then saving immediately, because a rename reads as a
78
+ * discrete action rather than an edit to be batched — so in the editor the
79
+ * "next `save()`" is usually the one it triggers itself. A headless caller
80
+ * decides its own moment.
81
+ *
82
+ * No-op when no template is loaded: there is nothing to name.
83
+ */
84
+ setName: (name: string) => void;
85
+ /**
86
+ * Persist the current content as a new template. `input.content`, when given,
87
+ * replaces the editor's content first — so `create({ content })` both loads
88
+ * and stores in one step.
89
+ */
90
+ create: (input?: {
91
+ name?: string;
92
+ content?: TemplateContent;
93
+ }) => Promise<Template>;
94
+ /** Fetch a template and make it the editor's content. */
95
+ load: (templateId: string) => Promise<Template>;
96
+ /**
97
+ * Persist the loaded template's name + content as a patch.
98
+ *
99
+ * `trigger` is passed through to `TemplatesOptions.onSaved`. It defaults to
100
+ * `"api"`, which is the honest answer for a direct call — the editor package
101
+ * passes the value matching whichever affordance fired.
102
+ */
103
+ save: (trigger?: TemplateSaveTrigger) => Promise<Template>;
104
+ /** Whether a template has been created or loaded. */
105
+ hasTemplate: () => boolean;
106
+ }
107
+ export declare function useEditor(options: UseEditorOptions): UseEditorReturn;
108
+ //#endregion
5
109
  //#region src/history.d.ts
6
110
  interface UseHistoryOptions {
7
111
  content: Ref<TemplateContent>;
8
112
  setContent: (content: TemplateContent, markDirty?: boolean) => void;
9
- isRemoteOperation?: () => boolean;
10
113
  maxSize?: number;
11
114
  }
12
115
  interface UseHistoryReturn {
@@ -136,11 +239,176 @@ interface HistoryInterceptorEditor {
136
239
  * Wraps editor mutation methods to record history snapshots before each
137
240
  * operation. Mutates the editor object in place.
138
241
  *
139
- * Must be applied **after** any collaboration broadcast wrapping so the
140
- * call chain is: history.record() → broadcast → original mutation.
242
+ * Not installed with a collaboration provider: its controller captures
243
+ * changes by diffing the content and keeps its own undo history.
141
244
  */
142
245
  export declare function useHistoryInterceptor(editor: HistoryInterceptorEditor, history: UseHistoryReturn): void;
143
246
  //#endregion
247
+ //#region src/diff-content.d.ts
248
+ /**
249
+ * The operations that turn `before` into `after`. Blocks are matched by id, so
250
+ * a drag comes out as a `moveBlock` and a layout change as a `columns` change
251
+ * plus moves, never as deletions and re-additions.
252
+ *
253
+ * `applyOperations(before, diffContent(before, after))` deep-equals `after`
254
+ * when both are well formed: unique ids, sections only at the top level, and
255
+ * each section's `children` as long as its layout's column count. The order is
256
+ * what the engine needs:
257
+ *
258
+ * 1. Deletions that take nothing still needed with them.
259
+ * 2. The top level: additions (a new section carries its new children) and moves.
260
+ * 3. `columns` growth, so new columns exist before blocks move into them.
261
+ * 4. Every section column: additions and moves, out of shrinking columns too.
262
+ * 5. `columns` shrinkage, once the dropped columns are empty.
263
+ * 6. Deletion of sections whose surviving blocks have moved out.
264
+ * 7. Key changes on blocks, then on settings.
265
+ *
266
+ * No operation carries `expect`: an ordinary edit applies over anything that
267
+ * doesn't conflict with it, and locks keep two people off one block.
268
+ */
269
+ export declare function diffContent(before: TemplateContent, after: TemplateContent): Operation[];
270
+ //#endregion
271
+ //#region src/invert-operations.d.ts
272
+ /**
273
+ * The batch that undoes `ops`, given the content they apply to. Each operation
274
+ * is inverted against the content just before it, and the inverses run in
275
+ * reverse order.
276
+ *
277
+ * Every inverse that can be guarded is: it expects what its operation left
278
+ * behind, so an undo that a later edit by someone else has overtaken is refused
279
+ * (`precondition_failed`, `anchor_missing`, `block_missing`) instead of
280
+ * overwriting that edit. A `setContent` inverse has no guard; the backend's
281
+ * `locked_by_others` rule is what stops it while others are editing.
282
+ *
283
+ * Throws the engine's `OperationError` when `ops` don't apply to `before`.
284
+ */
285
+ export declare function invertOperations(before: TemplateContent, ops: readonly Operation[]): Operation[];
286
+ //#endregion
287
+ //#region src/collaboration.d.ts
288
+ /** The template fields the controller reads, as the editor's readonly state holds them. */
289
+ type CollaborationTemplate = DeepReadonly<Pick<Template, "id" | "revision" | "content">>;
290
+ /** The slice of the editor the controller reads and writes. */
291
+ interface CollaborationEditor {
292
+ state: {
293
+ readonly template: CollaborationTemplate | null;
294
+ readonly selectedBlockId: string | null;
295
+ readonly isLoading: boolean;
296
+ };
297
+ /** The live content the user edits. */
298
+ content: Ref<TemplateContent>;
299
+ selectBlock: (blockId: string | null) => void;
300
+ setContent: (content: TemplateContent, markDirty?: boolean) => void;
301
+ markDirty: () => void;
302
+ }
303
+ /** Something to tell the person at this editor. */
304
+ type CollaborationNotice =
305
+ /** Someone else holds the block this session tried to select. */
306
+ {
307
+ type: "selectionRefused";
308
+ blockId: string;
309
+ holder: BlockLockHolder;
310
+ } |
311
+ /** A local change was refused and taken back. */
312
+ {
313
+ type: "editRefused";
314
+ code: CollaborationConflictCode;
315
+ holder?: BlockLockHolder;
316
+ } |
317
+ /** An undo or redo step no longer applies and was dropped. */
318
+ {
319
+ type: "undoSkipped";
320
+ code: CollaborationConflictCode;
321
+ };
322
+ /** A re-sync another session caused, as the backend announced it. */
323
+ interface CollaborationRemoteResync {
324
+ /** The revision the re-sync brings the template to. */
325
+ revision: number;
326
+ actor: CollaborationActor;
327
+ }
328
+ interface UseCollaborationOptions {
329
+ provider: CollaborationProvider;
330
+ /**
331
+ * Fetch a template when the backend says to re-sync: the templates
332
+ * provider's `load`. Its `revision` says which revision `content` is at.
333
+ */
334
+ loadTemplate: (templateId: string) => Promise<Template>;
335
+ /** This editor instance's session id. A new UUID when omitted. */
336
+ sessionId?: string;
337
+ /** How many undo steps to keep. */
338
+ maxUndo?: number;
339
+ /** A batch from another session was applied. */
340
+ onRemoteChange?: (change: CollaborationRemoteChange) => void;
341
+ /**
342
+ * Another session re-synced the template — a `setContent`, a restore, a
343
+ * batch too large to broadcast — and the backend said who. Called for a
344
+ * revision this session doesn't have yet, before it catches up; the
345
+ * catch-up's batches still reach `onRemoteChange`. The editor names an
346
+ * agent's re-sync with it.
347
+ */
348
+ onRemoteResync?: (resync: CollaborationRemoteResync) => void;
349
+ /**
350
+ * Rewrite another session's batch just before it is applied, given the
351
+ * content it applies to. Called for live batches and catch-up batches
352
+ * alike, never for this session's own. The editor uses it to turn merge-tag
353
+ * tokens into tags, as it does for loaded content. `onRemoteChange` still
354
+ * receives the batch as the backend sent it.
355
+ */
356
+ normalizeRemoteOperations?: (ops: readonly Operation[], content: TemplateContent) => Operation[];
357
+ onNotice?: (notice: CollaborationNotice) => void;
358
+ onError?: (error: Error) => void;
359
+ }
360
+ interface UseCollaborationReturn {
361
+ readonly sessionId: string;
362
+ /** Everyone with the template open, this session included; empty without presence. */
363
+ members: Readonly<Ref<readonly CollaborationMember[]>>;
364
+ /** Blocks other sessions hold, by block id. */
365
+ locks: Readonly<Ref<ReadonlyMap<string, BlockLock>>>;
366
+ /** `null` before a realtime join, and for a backend without realtime. */
367
+ connection: Readonly<Ref<CollaborationConnectionState | null>>;
368
+ /** The last revision this session confirmed. */
369
+ revision: Readonly<Ref<number>>;
370
+ canUndo: ComputedRef<boolean>;
371
+ canRedo: ComputedRef<boolean>;
372
+ /** Wire the controller to the editor. Call once, inside the editor's setup scope. */
373
+ attach: (editor: CollaborationEditor) => void;
374
+ undo: () => void;
375
+ redo: () => void;
376
+ clearHistory: () => void;
377
+ /** Turn edits not sent yet into a batch now. */
378
+ flush: () => void;
379
+ /** Resolves once every batch is confirmed; rejects when one fails in transit. */
380
+ settle: () => Promise<void>;
381
+ /** Stop reading the canvas (a version preview is on it). Batches keep arriving. */
382
+ suspend: () => void;
383
+ /** Put the current state back on the canvas and read it again. */
384
+ resume: () => void;
385
+ /** Fetch what this session missed, or reload when the backend says to. */
386
+ catchUp: () => Promise<void>;
387
+ /** Whether the block exists in the content the backend confirmed. */
388
+ hasConfirmedBlock: (blockId: string) => boolean;
389
+ destroy: () => void;
390
+ }
391
+ /**
392
+ * Real-time collaboration over a {@link CollaborationProvider}.
393
+ *
394
+ * The controller keeps the content the backend confirmed at revision R, plus
395
+ * this session's pending batches; the canvas shows the confirmed content with
396
+ * the pending batches applied. Local changes are captured by diffing the
397
+ * canvas against that view: structural changes, selection changes and blur
398
+ * send at once, typing into one block after a short pause. One batch is in
399
+ * flight at a time. Batches from other sessions apply strictly by revision,
400
+ * with this session's pending batches replayed on top; a gap that isn't
401
+ * filled within a second, a re-sync, a reconnect and a refusal all fetch what
402
+ * was missed.
403
+ *
404
+ * Selecting a block takes its lock (when the backend has locks), renewed
405
+ * while selected and released on deselect, template switch and page hide.
406
+ * Undo and redo send inverse batches guarded by preconditions; one that no
407
+ * longer applies is dropped with a notice. Changes from others never enter the
408
+ * stacks.
409
+ */
410
+ export declare function useCollaboration(options: UseCollaborationOptions): UseCollaborationReturn;
411
+ //#endregion
144
412
  //#region src/saved-blocks.d.ts
145
413
  interface UseSavedBlocksOptions {
146
414
  /**
@@ -419,5 +687,5 @@ interface UseCommentListenerOptions {
419
687
  */
420
688
  export declare function useCommentListener(options: UseCommentListenerOptions): void;
421
689
  //#endregion
422
- export { type EditorState, type LocalStorageMediaProviderOptions, type LocalStorageSavedBlocksOptions, type UseAutoSaveOptions, type UseAutoSaveReturn, type UseBlockActionsOptions, type UseBlockActionsReturn, type UseCommentListenerOptions, type UseCommentsOptions, type UseCommentsReturn, type UseConditionPreviewReturn, type UseEditorOptions, type UseEditorReturn, type UseHistoryOptions, type UseHistoryReturn, type UseSavedBlocksOptions, type UseSavedBlocksReturn, type UseVersionHistoryOptions, type UseVersionHistoryReturn, useEditor };
690
+ export type { CollaborationEditor, CollaborationNotice, CollaborationRemoteResync, CollaborationTemplate, EditorState, LocalStorageMediaProviderOptions, LocalStorageSavedBlocksOptions, UseAutoSaveOptions, UseAutoSaveReturn, UseBlockActionsOptions, UseBlockActionsReturn, UseCollaborationOptions, UseCollaborationReturn, UseCommentListenerOptions, UseCommentsOptions, UseCommentsReturn, UseConditionPreviewReturn, UseEditorOptions, UseEditorReturn, UseHistoryOptions, UseHistoryReturn, UseSavedBlocksOptions, UseSavedBlocksReturn, UseVersionHistoryOptions, UseVersionHistoryReturn };
423
691
  //# sourceMappingURL=index.d.ts.map