@bloklabs/vue 1.1.1

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.
@@ -0,0 +1,502 @@
1
+ import type { Component, DefineComponent, InjectionKey, MaybeRefOrGetter, Ref, ShallowRef } from 'vue';
2
+ import type {
3
+ API,
4
+ Blok,
5
+ BlockAPI,
6
+ BlockMutationEvent,
7
+ BlockToolConstructable,
8
+ BlockToolData,
9
+ BlokConfig,
10
+ EditorWidth,
11
+ OutputBlockData,
12
+ OutputData,
13
+ ResolvedTheme,
14
+ ToolboxConfig,
15
+ } from '@bloklabs/core';
16
+ import type { BlockTuneData } from '@bloklabs/core';
17
+ import type { MarkdownImportConfig } from '@bloklabs/core/markdown';
18
+
19
+ /**
20
+ * Configuration for the `useBlok` composable and `<BlokEditor>`.
21
+ * Accepts all `BlokConfig` properties except `holder` (managed by the adapter),
22
+ * plus a reactive `width` prop.
23
+ *
24
+ * Reactive props (sync after mount without recreation):
25
+ * - `readOnly` — calls `editor.readOnly.set(value)`
26
+ * - `autofocus` — calls `editor.focus()` when changed to true
27
+ * - `theme` — calls `editor.theme.set(value)`
28
+ * - `width` — calls `editor.width.set(value)`
29
+ * - `placeholder` — calls `editor.placeholder.set(value)`
30
+ * - `data` — re-renders via `editor.render(value)` when content changes
31
+ * (deep-equal–deduped and serialized; seeds the initial content at creation)
32
+ *
33
+ * All other config is consumed once at editor creation.
34
+ */
35
+ export interface UseBlokConfig extends Omit<BlokConfig, 'holder'> {
36
+ /** Editor content width mode. Synced reactively after mount via `editor.width.set()`. */
37
+ width?: EditorWidth;
38
+ }
39
+
40
+ /** Props for the `BlokContent` component. */
41
+ export interface BlokContentProps {
42
+ /** The Blok editor instance from `useBlok`. Pass null before it is ready. */
43
+ editor: Blok | null;
44
+ }
45
+
46
+ /**
47
+ * Composable that creates and manages a Blok editor instance.
48
+ *
49
+ * @param config - reactive config source (ref/getter), all `BlokConfig` props except `holder`
50
+ * @param recreateKey - reactive value whose identity change destroys and recreates the editor
51
+ * @returns a ref to the live Blok instance, or null before ready / after destroy
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * const editor = useBlok(() => ({ tools, data, readOnly: false }));
56
+ * ```
57
+ */
58
+ export declare function useBlok(
59
+ config: MaybeRefOrGetter<UseBlokConfig>,
60
+ recreateKey?: MaybeRefOrGetter<unknown>
61
+ ): Ref<Blok | null>;
62
+
63
+ /**
64
+ * Component that provides the DOM mount point for a Blok editor. Renders a
65
+ * `<div>` and adopts the editor's detached holder into it.
66
+ */
67
+ export declare const BlokContent: DefineComponent<BlokContentProps>;
68
+
69
+ /**
70
+ * Props for `<BlokEditor>` — every `UseBlokConfig` option except the callbacks
71
+ * surfaced as emits (`onReady`/`onChange`/`onSave`/`onAfterRender`/`onThemeChange`),
72
+ * plus `recreateKey`. The return-valued transform hooks (`onBeforeRender`,
73
+ * `onBeforePaste`) stay props. Any extra attribute falls through to the container.
74
+ */
75
+ export interface BlokEditorProps
76
+ extends Partial<Omit<UseBlokConfig, 'onReady' | 'onChange' | 'onSave' | 'onAfterRender' | 'onThemeChange'>> {
77
+ /** Changing this prop's identity destroys and recreates the editor. */
78
+ recreateKey?: unknown;
79
+ }
80
+
81
+ /** Events emitted by `<BlokEditor>`. */
82
+ export interface BlokEditorEmits {
83
+ /** The editor became ready — fires once with the live instance. */
84
+ (event: 'ready', instance: Blok): void;
85
+ /** Raw block mutation channel (core `onChange`). */
86
+ (event: 'change', payload: { api: API; event: BlockMutationEvent | BlockMutationEvent[] }): void;
87
+ /** Full serialized content on every change (notification half). */
88
+ (event: 'save', data: OutputData): void;
89
+ /** Two-way `v-model:data` update half. */
90
+ (event: 'update:data', data: OutputData): void;
91
+ /** Fires after the editor finishes (re-)rendering (core `onAfterRender`). */
92
+ (event: 'after-render', api: API): void;
93
+ /** Fires when the resolved theme changes (core `onThemeChange`). */
94
+ (event: 'theme-change', resolvedTheme: ResolvedTheme): void;
95
+ /** Fires after a batch render completes (core `blocks:rendered`). */
96
+ (event: 'blocks-rendered', payload: unknown): void;
97
+ /** Fires for each block rendered into the DOM (core `block:rendered`). */
98
+ (event: 'block-rendered', payload: unknown): void;
99
+ }
100
+
101
+ /** The instance surface exposed via a template ref on `<BlokEditor>`. */
102
+ export interface BlokEditorExposed {
103
+ /** The live Blok instance, or null before ready. */
104
+ instance: Blok | null;
105
+ /** Serialize the current content (undefined until ready). */
106
+ save(): Promise<OutputData> | undefined;
107
+ /** Move the caret into the editor. */
108
+ focus(atEnd?: boolean): void;
109
+ /** Replace the editor content (undefined until ready). */
110
+ render(data: OutputData): Promise<void> | undefined;
111
+ }
112
+
113
+ /**
114
+ * The blessed all-in-one component for embedding Blok in Vue. Wires `useBlok`
115
+ * and `BlokContent`, exposes the live instance (see {@link BlokEditorExposed}),
116
+ * and forwards fallthrough attributes to the editor container.
117
+ *
118
+ * @example
119
+ * ```vue
120
+ * <BlokEditor :tools="tools" v-model:data="data" theme="dark" @ready="onReady" />
121
+ * ```
122
+ */
123
+ export declare const BlokEditor: DefineComponent<
124
+ BlokEditorProps,
125
+ BlokEditorExposed,
126
+ unknown,
127
+ Record<string, never>,
128
+ Record<string, never>,
129
+ unknown,
130
+ unknown,
131
+ BlokEditorEmits
132
+ >;
133
+
134
+ /** Injection key holding app-wide Blok defaults (merged under per-instance props). */
135
+ export declare const BLOK_DEFAULT_CONFIG: InjectionKey<Partial<UseBlokConfig>>;
136
+
137
+ /**
138
+ * Registers app-wide Blok defaults in the current component's provide scope
139
+ * (call inside a parent component's `setup`). Mirrors Angular's `provideBlok`.
140
+ */
141
+ export declare function provideBlok(defaults: Partial<UseBlokConfig>): void;
142
+
143
+ /**
144
+ * Reads the app-wide Blok defaults from the nearest `provideBlok` (or `{}`).
145
+ * Mirrors React's `useBlokDefaults`. Call inside `setup`.
146
+ */
147
+ export declare function useBlokDefaults(): Partial<UseBlokConfig>;
148
+
149
+ /**
150
+ * A plain, serializable view of one block in the tree. Snapshot-volatile: every
151
+ * read allocates a fresh node and `contentIds` is derived per read — depend on
152
+ * the `id`, don't stash the node.
153
+ */
154
+ export interface BlockNode {
155
+ id: string;
156
+ type: string;
157
+ parentId: string | null;
158
+ contentIds: readonly string[];
159
+ }
160
+
161
+ /** Where to place the caret within a block. */
162
+ export interface CaretTarget {
163
+ position?: 'start' | 'end' | 'default';
164
+ offset?: number;
165
+ }
166
+
167
+ /** Where to place a block among its siblings. */
168
+ export type InsertPosition = 'start' | 'end' | { before: string } | { after: string };
169
+
170
+ /** Spec for {@link UseBlocksApi.insert}. */
171
+ export interface InsertSpec {
172
+ type?: string;
173
+ data?: BlockToolData;
174
+ parentId?: string | null;
175
+ position?: InsertPosition;
176
+ focus?: boolean;
177
+ replace?: boolean;
178
+ id?: string;
179
+ tunes?: { [name: string]: BlockTuneData };
180
+ caret?: CaretTarget;
181
+ }
182
+
183
+ /**
184
+ * One node of a pre-built nested subtree for {@link UseBlocksApi.insertTree}.
185
+ *
186
+ * Each node maps to one block; `children` are inserted nested under it (their
187
+ * `parentId` set to this node's id) in array order, recursively. Placement
188
+ * options (`parentId`/`position`) are ROOT-ONLY — they position the whole
189
+ * subtree among existing blocks and are ignored on nested children, whose parent
190
+ * is always their enclosing node.
191
+ */
192
+ export interface TreeInsertSpec {
193
+ type?: string;
194
+ data?: BlockToolData;
195
+ tunes?: { [name: string]: BlockTuneData };
196
+ /**
197
+ * Explicit id for this node (generated when omitted). Unlike `insert`, this is
198
+ * NOT insert-if-absent: a tree insert always creates fresh blocks. A colliding
199
+ * id — one that already exists in the document, or is reused by another node
200
+ * in the same spec — is REJECTED up front: nothing is inserted and `insertTree`
201
+ * returns `null` (a duplicate id would corrupt every id-keyed lookup).
202
+ */
203
+ id?: string;
204
+ /** Direct children, inserted nested under this node, in array order. */
205
+ children?: TreeInsertSpec[];
206
+ /** Root-only: where to place the whole subtree. Ignored on nested children. */
207
+ parentId?: string | null;
208
+ /** Root-only: slot among siblings of `parentId`. Ignored on nested children. */
209
+ position?: InsertPosition;
210
+ }
211
+
212
+ /** Where to move an existing block. */
213
+ export type MoveTarget = { before: string } | { after: string } | { toIndex: number };
214
+
215
+ /**
216
+ * Reactive, id/parentId-relative view of the block tree returned by
217
+ * {@link useBlocks}. Reads are reactive (re-run inside a `computed`/template on
218
+ * `block changed`); mutators delegate to the editor's `blocks` API.
219
+ *
220
+ * This is the SAME 28-method surface React's `useBlocks` exposes — both adapters
221
+ * are thin reactivity wrappers over one shared `createBlocksApiForEditor` core,
222
+ * so the surfaces are identical and cannot drift.
223
+ */
224
+ export interface UseBlocksApi {
225
+ getById(id: string): BlockNode | null;
226
+ getChildren(parentId: string | null): BlockNode[];
227
+ /**
228
+ * Insert one block; returns the created node or null when rejected (unknown
229
+ * tool type, dangling `parentId`, or a `replace` whose target is missing). An
230
+ * explicit `id` that already exists is insert-if-absent (returns the existing
231
+ * node, creates nothing). Atomic — one undo step. The returned node is
232
+ * {@link BlockNode}-volatile; read it now, don't put it in a dep array.
233
+ *
234
+ * `replace: true` is a positional "turn into": it REQUIRES a `before`/`after`
235
+ * `position` whose ref IS the block to overwrite. It returns `null` if
236
+ * `replace` is set without an object `position`, or if that target ref does
237
+ * not exist. The replacement keeps the overwritten block's own parent (the
238
+ * caller's `parentId` is ignored under `replace`).
239
+ */
240
+ insert(spec?: InsertSpec): BlockNode | null;
241
+ /**
242
+ * Insert several blocks atomically, in array order, as ONE undo step. Each
243
+ * spec is a full {@link InsertSpec} (own type/data/parentId/position), routed
244
+ * through the same single-`insert` path, so per-spec parent assertion and
245
+ * positioning still apply. Specs that fail to insert (e.g. a dangling
246
+ * parentId, or a replace whose target is missing) are dropped; the returned
247
+ * array holds only the successfully created nodes. An empty input is a no-op
248
+ * (returns `[]`, opens no transaction).
249
+ */
250
+ insertMany(specs: InsertSpec[]): BlockNode[];
251
+ /**
252
+ * Insert a pre-built NESTED subtree in ONE atomic operation (one undo step).
253
+ * Each {@link TreeInsertSpec} node becomes a block; its `children` are inserted
254
+ * nested under it (recursively, in array order). Placement is root-only: the
255
+ * root node's `parentId`/`position` position the whole subtree; nested children
256
+ * ignore those fields. A dangling root `parentId` (or a colliding explicit id)
257
+ * is rejected — nothing is inserted and `null` is returned. Returns the root
258
+ * {@link BlockNode}.
259
+ */
260
+ insertTree(spec: TreeInsertSpec): BlockNode | null;
261
+ /**
262
+ * Convert a Markdown string to blocks and insert them ADDITIVELY at a position,
263
+ * WITHOUT clearing the document. Async: the converter is lazy-loaded, so
264
+ * `await` the returned promise. The whole batch is one atomic undo step.
265
+ * `parentId` nests the import (top-level converted blocks are reparented under
266
+ * it); a dangling `parentId` (re-checked after the async convert) and a
267
+ * converter failure both return `[]`. Returns all created {@link BlockNode}s in
268
+ * document order. `config` is forwarded to the converter (gfm/toolMap/extensions).
269
+ */
270
+ insertMarkdown(
271
+ markdown: string,
272
+ options?: { parentId?: string | null; position?: InsertPosition; config?: MarkdownImportConfig }
273
+ ): Promise<BlockNode[]>;
274
+ /**
275
+ * Serialize the WHOLE document to a Markdown string — the outbound twin of
276
+ * core's `blocks.exportMarkdown` (and the read-side counterpart of the additive
277
+ * {@link insertMarkdown}). Async: the serializer is lazy-loaded. Markdown cannot
278
+ * express every block, so some structure is dropped (table `colspan`/`rowspan`,
279
+ * heading columns). Returns `''` for an empty document. Pre-ready: `''`.
280
+ */
281
+ exportMarkdown(): Promise<string>;
282
+ /**
283
+ * Move `id` to a flat slot, as a single operation. No-op when `id` is unknown.
284
+ * For a relative `{ before|after }` target it is also a no-op when the ref is
285
+ * `id` itself, a descendant of `id`, OR does not exist. A `{ toIndex }` target
286
+ * is an absolute flat index, clamped into range. Subtree-aware: a block with
287
+ * descendants relocates its WHOLE subtree (relative targets only — an absolute
288
+ * `{ toIndex }` of a block with descendants is a graceful no-op).
289
+ *
290
+ * Parent-adoption side effect: `before`/`after` make the moved block a SIBLING
291
+ * of the ref — it adopts the ref's parent. Use {@link nest}/{@link unnest} to
292
+ * change the parent without choosing a sibling slot. Returns void.
293
+ */
294
+ move(id: string, target: MoveTarget): void;
295
+ /**
296
+ * Nest `id` (and its whole subtree) under `parentId`, as one undo step.
297
+ * Graceful no-op when either id is unknown, when `parentId === id`, or when
298
+ * `parentId` is a DESCENDANT of `id`. Column boundary caveat: nesting a block
299
+ * that lives inside a `column` (or into one) is a GRACEFUL no-op. Returns void.
300
+ */
301
+ nest(id: string, parentId: string): void;
302
+ /**
303
+ * Promote `id` (and its subtree) to root, as one undo step. Graceful no-op when
304
+ * the id is unknown or already at root. Same column-boundary caveat as
305
+ * {@link nest}. Returns void.
306
+ */
307
+ unnest(id: string): void;
308
+ /**
309
+ * Remove a block by id. DESTRUCTIVE: deletes the block AND its entire subtree
310
+ * (all transitive descendants), deepest-first so a container's children aren't
311
+ * promoted to root. The whole cascade is one undo step. Unknown id is a silent
312
+ * no-op. Returns void.
313
+ */
314
+ remove(id: string): void;
315
+ /**
316
+ * Update a block's data and/or tunes by id. Delegates to core's async
317
+ * `blocks.update`, which forms its OWN undo step (not wrapped in `transact`).
318
+ * Unknown id is a silent no-op; a rejected update is swallowed. Returns void.
319
+ */
320
+ update(id: string, data?: BlockToolData, tunes?: { [name: string]: BlockTuneData }): void;
321
+ /**
322
+ * Convert a block to another type ("turn into") by id. Delegates to core's
323
+ * async `blocks.convert`; both tools must provide a `conversionConfig` or core
324
+ * rejects (swallowed → graceful no-op). Unknown id is a silent no-op. Returns void.
325
+ */
326
+ convert(
327
+ id: string,
328
+ newType: string,
329
+ dataOverrides?: BlockToolData,
330
+ options?: { caret?: CaretTarget }
331
+ ): void;
332
+ /**
333
+ * Run `fn` as a single atomic undo step: every mutation it makes is grouped
334
+ * into one history entry. (`insert`/`insertMany`/`nest`/`unnest`/`remove`
335
+ * already wrap themselves; use this to group several calls.) Returns void.
336
+ */
337
+ transact(fn: () => void): void;
338
+ /**
339
+ * Run `fn` as one atomic operation NOT captured in the undo history — for
340
+ * silent auto-repair a user's undo should never step through. Mutations inside
341
+ * still emit reactively. Pre-ready it just runs `fn`.
342
+ */
343
+ transactWithoutCapture(fn: () => void): void;
344
+ /** The current block count. Reactive (re-reads on 'block changed'). Pre-ready: 0. */
345
+ getBlocksCount(): number;
346
+ /** The flat index of the block holding the caret, or -1 when none. Pre-ready: -1. */
347
+ getCurrentBlockIndex(): number;
348
+ /** The block at a flat index as a snapshot {@link BlockNode}, or null. */
349
+ getBlockByIndex(index: number): BlockNode | null;
350
+ /**
351
+ * The absolute flat index of a block by id, or null when unknown. Unknown ids
352
+ * return null silently (no console warn). Pre-ready: null.
353
+ */
354
+ getBlockIndex(id: string): number | null;
355
+ /**
356
+ * Read a block's current `data` and `tunes` by id WITHOUT mutating anything —
357
+ * the synchronous last-extracted view. Unknown id returns null. Pre-ready: null.
358
+ */
359
+ getBlockData(id: string): { data: BlockToolData; tunes: { [name: string]: BlockTuneData } } | null;
360
+ /**
361
+ * The block whose holder contains/equals `element`, as a snapshot
362
+ * {@link BlockNode}, or null. Maps a DOM event target back to a block.
363
+ */
364
+ getBlockByElement(element: HTMLElement): BlockNode | null;
365
+ /**
366
+ * Read a tool's default empty data WITHOUT inserting anything — delegates to
367
+ * core's `composeBlockData`. Async; rejects for an unknown tool. Pre-ready: `{}`.
368
+ */
369
+ composeBlockData(toolName: string): Promise<BlockToolData>;
370
+ /**
371
+ * Replace the WHOLE document with blocks parsed from an HTML string — delegates
372
+ * to core's `renderFromHTML`. Unlike {@link insertMarkdown} (additive), this
373
+ * CLEARS existing content first. Async. Pre-ready: resolves immediately (no-op).
374
+ */
375
+ renderFromHTML(html: string): Promise<void>;
376
+ /**
377
+ * Insert a flat array of already-serialized {@link OutputBlockData} (the
378
+ * `save()` shape) directly, honoring each block's `parent`/`content` links. One
379
+ * atomic undo step. Returns the created nodes; pre-ready: `[]` (no insert).
380
+ */
381
+ insertOutputData(blocks: OutputBlockData[], options?: { index?: number }): BlockNode[];
382
+ /**
383
+ * Atomically split a block: update `currentBlockId` with `currentBlockData` and
384
+ * insert a new `newBlockType` block at `insertIndex`, as ONE undo step.
385
+ * Delegates to core's `splitBlock`. Returns the new node, or null pre-ready.
386
+ */
387
+ splitBlock(
388
+ currentBlockId: string,
389
+ currentBlockData: Partial<BlockToolData>,
390
+ newBlockType: string,
391
+ newBlockData: BlockToolData,
392
+ insertIndex: number
393
+ ): BlockNode | null;
394
+ /**
395
+ * Insert a single child block under `parentId` at flat `insertIndex`, atomically
396
+ * (block creation AND parent assignment in ONE undo step) — delegates to core's
397
+ * `blocks.insertInsideParent`. A dangling `parentId` is a no-op returning `null`;
398
+ * `childData` defaults to an empty paragraph. Returns the created node. Pre-ready: `null`.
399
+ */
400
+ insertInsideParent(parentId: string, insertIndex: number, childData?: BlockToolData): BlockNode | null;
401
+ /**
402
+ * Replace the WHOLE document with blocks parsed from saved {@link OutputData} —
403
+ * delegates to core's `blocks.render`. CLEARS existing content first (a
404
+ * document-LOAD primitive). Async. Pre-ready: resolves immediately (no-op).
405
+ */
406
+ render(data: OutputData): Promise<void>;
407
+ /**
408
+ * Remove EVERY block from the document — delegates to core's `blocks.clear`. A
409
+ * document-reset primitive (pairs with {@link render}). Async. Pre-ready:
410
+ * resolves immediately (no-op).
411
+ */
412
+ clear(): Promise<void>;
413
+ /**
414
+ * Whether a Yjs sync (undo/redo) is currently in progress — the Vue mirror of
415
+ * core's `blocks.isSyncingFromYjs`. A METHOD (not a property) so it reads the
416
+ * LIVE flag at call time. Pre-ready: `false`.
417
+ */
418
+ isSyncingFromYjs(): boolean;
419
+ }
420
+
421
+ /**
422
+ * Composable exposing a reactive, id/parentId-relative view of the block tree
423
+ * for connection ops (insert/move/nest/unnest/remove) — the "everything is a
424
+ * block" tree, manipulated without flat-index math. Mirrors React's `useBlocks`.
425
+ *
426
+ * @param editor - the Blok instance (value/ref/getter), or null before ready
427
+ *
428
+ * @example
429
+ * ```ts
430
+ * const editor = useBlok(() => ({ tools }));
431
+ * const blocks = useBlocks(editor);
432
+ * blocks.transact(() => {
433
+ * const created = blocks.insert({ type: 'paragraph', parentId: 'toggle-1', position: 'end' });
434
+ * blocks.move(created!.id, { after: 'sibling-2' });
435
+ * });
436
+ * ```
437
+ */
438
+ export declare function useBlocks(editor: MaybeRefOrGetter<Blok | null>): UseBlocksApi;
439
+
440
+ /** One field of a {@link CreateVueBlockSpec.propSchema}. */
441
+ export interface PropSchemaEntry {
442
+ default: unknown;
443
+ values?: readonly unknown[];
444
+ }
445
+
446
+ /**
447
+ * Declarative data shape. Its keys are EXACTLY the keys `save()` returns to Yjs
448
+ * (a cleared field is written as its default, never dropped).
449
+ */
450
+ export type PropSchema = Record<string, PropSchemaEntry>;
451
+
452
+ /** Context handed to a Vue block's `setup` (the only data write path is `commit`). */
453
+ export interface VueBlockRenderProps<Data> {
454
+ /** Reactive, frozen snapshot of the block data. Read `data.value`; never mutate. */
455
+ data: ShallowRef<Readonly<Data>>;
456
+ /** The ONLY data write path: merge a partial patch and sync once. */
457
+ commit: (patch: Partial<Data>) => void;
458
+ /** This block's per-block API. */
459
+ block: BlockAPI;
460
+ /**
461
+ * Reactive read-only flag. Read `readOnly.value` in render to disable editing.
462
+ * Toggled IN PLACE by core's read-only switch (no remount, ephemeral state
463
+ * survives). Honor it — a block that ignores it stays interactive when the
464
+ * editor is read-only.
465
+ */
466
+ readOnly: Readonly<ShallowRef<boolean>>;
467
+ /** Engine-owned child slot — render `h(BlockChildren)` for a container block. */
468
+ BlockChildren: Component;
469
+ }
470
+
471
+ /** Spec for {@link createVueBlock}. Authored as a `.ts` render function (no SFC). */
472
+ export interface CreateVueBlockSpec<Data extends BlockToolData = BlockToolData> {
473
+ type: string;
474
+ toolbox?: ToolboxConfig;
475
+ propSchema: PropSchema;
476
+ setup: (props: VueBlockRenderProps<Data>) => () => unknown;
477
+ onRendered?: (block: BlockAPI) => void;
478
+ onMoved?: (block: BlockAPI) => void;
479
+ onRemoved?: (block: BlockAPI) => void;
480
+ }
481
+
482
+ /**
483
+ * Author a first-party Vue block: returns a `BlockToolConstructable` registered
484
+ * exactly like a vanilla tool (`tools: { type: { class: createVueBlock(...) } }`).
485
+ * The factory owns the mutation-free host, a frozen defaults-filled data mirror,
486
+ * and a reactive snapshot the component reads; it bridges Blok's block lifecycle
487
+ * to Vue (in-place `setData`, complete-mirror `save`, deterministic unmount).
488
+ *
489
+ * @example
490
+ * ```ts
491
+ * const Callout = createVueBlock<{ text: string }>({
492
+ * type: 'callout',
493
+ * propSchema: { text: { default: '' } },
494
+ * setup({ data, commit }) {
495
+ * return () => h('aside', { onClick: () => commit({ text: 'hi' }) }, data.value.text);
496
+ * },
497
+ * });
498
+ * ```
499
+ */
500
+ export declare function createVueBlock<Data extends BlockToolData = BlockToolData>(
501
+ spec: CreateVueBlockSpec<Data>
502
+ ): BlockToolConstructable;