@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.
- package/dist/index.cjs +1 -0
- package/dist/index.mjs +602 -0
- package/package.json +39 -0
- package/src/BlockPortalHost.ts +46 -0
- package/src/BlokContent.ts +80 -0
- package/src/BlokEditor.ts +197 -0
- package/src/block-portal-registry.ts +79 -0
- package/src/blocks-snapshot.ts +17 -0
- package/src/config-keys.ts +66 -0
- package/src/createVueBlock.ts +319 -0
- package/src/holder-map.ts +19 -0
- package/src/index.ts +22 -0
- package/src/provide-blok.ts +65 -0
- package/src/registry-map.ts +21 -0
- package/src/types.ts +30 -0
- package/src/useBlocks.ts +147 -0
- package/src/useBlok.ts +299 -0
- package/types/index.d.ts +502 -0
package/types/index.d.ts
ADDED
|
@@ -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;
|