@eldrajs/theme-vue 0.0.0 → 0.1.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.
@@ -0,0 +1,542 @@
1
+ import { InjectionKey, MaybeRefOrGetter, PropType, Ref, VNode, VNodeChild } from "vue";
2
+ import { ThemeMessages } from "@eldrajs/theme-core/i18n";
3
+ import { BlockFieldDefinition, BlockFieldsMap, BuildRichTextTreeOptions, DEFAULT_IMAGE_FRAMING, EldraClient, EntryDoc, EntryQuery, ImageFraming, PageLike, ReusableComponentProjection, RichTextFieldIdentity, RichTextMark, RichTextNode, RichTextRenderChild, RichTextRenderNode, RouteTemplateLike, ThemeDesignTokens, buildRichTextTree, imageFramingAttrs, imageFramingStyle, isBlockFieldLocalized, normalizeImageFraming, registerBlockFields } from "@eldrajs/theme-core";
4
+ import { LinkKind, LinkRouteContext, LinkTarget, LinkTargetInfo, LinkTargetInfo as LinkTargetInfo$1, LinkValue, ResolvedLink, ResolvedLink as ResolvedLink$1, linkTargetKeys, resolveLink, safeLinkHref } from "@eldrajs/theme-core/links";
5
+ import { BridgePayloads } from "@eldrajs/theme-core/bridge";
6
+ //#region src/context.d.ts
7
+ /**
8
+ * One rendered editor slot marker's geometry. Derives from the bridge payload
9
+ * type so the wire format stays the single source of truth.
10
+ */
11
+ type SlotGeometry = BridgePayloads['theme:slots-rendered']['slots'][number];
12
+ type SlotGeometryReporter = (slots: SlotGeometry[]) => void;
13
+ /**
14
+ * What `resolveLink` needs, as one reactive slice of the theme context: the
15
+ * site's pages and route templates, and what it knows about the objects its
16
+ * links point at, keyed `${_type}:${id}`. An adapter fills it where it already
17
+ * resolves the route — one place, one read per type — so a prerendered page
18
+ * carries every href in its payload.
19
+ */
20
+ interface EldraLinkState {
21
+ pages: PageLike[];
22
+ templates: RouteTemplateLike[];
23
+ targets: Map<string, LinkTargetInfo$1>;
24
+ }
25
+ /**
26
+ * Which content locale the page a visitor is looking at is, and what the others are — the slice an
27
+ * adapter fills from the site's own routing so a **block** can read it without reaching for the
28
+ * router or the runtime config.
29
+ *
30
+ * It lives on the theme context rather than in a framework composable for the same reason the link
31
+ * state does: a block must render in a Storybook story and a unit test with no Nuxt around it, so
32
+ * everything it needs arrives through one `inject`. `useEldraLocale()` is the accessor.
33
+ *
34
+ * `path` is the one every link goes through and it is **idempotent** — see the adapter's own
35
+ * implementation (`@eldrajs/theme-nuxt`'s `localeHref`): a destination can pass through more than
36
+ * one prefixer, and a second prefix would produce a path nothing on the site answers.
37
+ */
38
+ interface EldraLocaleState {
39
+ /**
40
+ * The active content locale, or `null` on a site that configures none. In a Studio preview this
41
+ * is the locale the editor is driving, which wins over the one the path names.
42
+ */
43
+ active: string | null;
44
+ /** The locale served at `/`, unprefixed. `null` on a site that configures none. */
45
+ defaultLocale: string | null;
46
+ /** Every locale the site serves, `defaultLocale` first. Empty on a site that configures none. */
47
+ supported: readonly string[];
48
+ /**
49
+ * One locale's name **in that locale** — "íslenska (Ísland)", "American English" — for a language
50
+ * switcher's option label, with the tag itself as the fallback.
51
+ *
52
+ * It is on the state rather than left to the caller because the answer is ICU data and a renderer
53
+ * and a browser do not always have the same of it: Node answers "íslenska (Ísland)" for `is-IS`
54
+ * where a reduced-ICU browser build answers "Icelandic (Iceland)". An adapter therefore resolves
55
+ * these **once, on the server**, and carries them to the browser, so a switcher that renders one
56
+ * does not hydrate into a mismatch and repaint.
57
+ */
58
+ name: (locale: string) => string;
59
+ /**
60
+ * One same-site destination (`/products/x`, `/search?q=mug`) under the active locale. Anything
61
+ * that is not a path on this site — an absolute URL, `mailto:`, `#main` — and anything already
62
+ * spelled in one of the site's locales comes back untouched.
63
+ */
64
+ path: (href: string) => string;
65
+ /** The page the visitor is on, spelled in `locale`. The default locale's spelling has no prefix. */
66
+ switchPath: (locale: string) => string;
67
+ /** Go to `switchPath(locale)` — what a language switcher calls when its value changes. */
68
+ select: (locale: string) => void;
69
+ }
70
+ interface EldraContext {
71
+ client: EldraClient;
72
+ links: EldraLinkState;
73
+ /**
74
+ * Optional so a context assembled before locales existed still type-checks and still renders:
75
+ * every reader treats its absence as "one unprefixed site, no switcher", which is what such a
76
+ * site is.
77
+ */
78
+ locales?: EldraLocaleState;
79
+ /**
80
+ * The theme's resolved message catalogue — the manifest's own `messages` block (or the fallback
81
+ * catalogue, `{ defaultLocale: 'en-US', locales: {} }`, for a theme that ships no `i18n/`
82
+ * directory), merged with the platform's own overrides and resolved over the organisation's
83
+ * locales at build time (`@eldrajs/theme-nuxt`'s module, `@eldrajs/theme-core/i18n`'s
84
+ * `resolveMessageCatalogue`). Flat dotted keys, one record per locale tag — the starter's
85
+ * `vue-i18n` plugin is the one consumer that unflattens it.
86
+ *
87
+ * Reactive so a live `editor:theme-messages` push from Studio's preview (`useEldraPreview`)
88
+ * updates every block reading a message through it with no re-render plumbing of its own.
89
+ */
90
+ messages: ThemeMessages;
91
+ preview: {
92
+ active: boolean;
93
+ mode: 'preview' | 'edit';
94
+ locale: string | null;
95
+ sourceDrafts: Record<string, Record<string, unknown>>;
96
+ drafts: Record<string, Record<string, unknown>>;
97
+ draftSchemaApiIds: Record<string, string>;
98
+ refreshRevision: number;
99
+ revision: number;
100
+ designTokensRevision: number;
101
+ /**
102
+ * The revision of the last `editor:theme-messages` push applied to `context.messages`.
103
+ * Mirrors `designTokensRevision`'s role: a push whose `revision` is not strictly greater than
104
+ * this is stale (an out-of-order delivery, a reconnect replay) and is ignored rather than
105
+ * rolling a live edit backwards.
106
+ */
107
+ messagesRevision: number;
108
+ /**
109
+ * Bumped every time `editor:init` carries a preview token that differs
110
+ * from the one before it — so a consumer can tell "the editor handed me a
111
+ * *new* token" apart from "the editor sent another content update"
112
+ * (`refreshRevision` bumps for both). `useEldraPage` reads it to decide
113
+ * whether a 401 is the first failure of a token, which the editor may
114
+ * still recover from, or the failure of a token it has already replaced,
115
+ * which is the operator's to see.
116
+ */
117
+ tokenRevision: number;
118
+ /**
119
+ * Negotiated from the editor's editor:hello capabilities (the
120
+ * `block-slots` capability), re-negotiated on every hello. Gates the
121
+ * editor-only slot markers, which `EldraLayout` renders — the one
122
+ * negotiated capability a Vue component has to read.
123
+ *
124
+ * The `image-framing` and `rich-text-inline` gates are deliberately not
125
+ * mirrored here: theme-core's preview message router negotiates them and
126
+ * forwards them straight to the overlay runtime
127
+ * (`setFramingEnabled`/`setRichTextEnabled`), so there is one owner per
128
+ * capability rather than one per framework binding.
129
+ */
130
+ editorSupportsSlots: boolean;
131
+ /**
132
+ * §18 v3: bumped once for every change theme-core reports through
133
+ * `onRichTextRenderState`, so a rich-text field re-evaluates whether its
134
+ * render is deferred. It is only a change signal — the answer itself is
135
+ * `isRichTextRenderDeferred`, which is core's, not a mirror of it.
136
+ */
137
+ richTextRenderRevision: number;
138
+ /**
139
+ * §18 v3: theme-core's predicate, installed by startEldraPreview. While it
140
+ * answers true for a field, the browser owns that field's DOM (a native
141
+ * text op is in flight) and the renderer must not replace it. Undefined in
142
+ * static output and outside a preview, where nothing is ever deferred.
143
+ */
144
+ isRichTextRenderDeferred?: (identity: RichTextFieldIdentity) => boolean;
145
+ /**
146
+ * Installed by startEldraPreview; posts bounded theme:slots-rendered
147
+ * geometry over the bridge. Undefined in static output.
148
+ */
149
+ slotGeometryReporter?: SlotGeometryReporter;
150
+ };
151
+ designTokens: ThemeDesignTokens;
152
+ }
153
+ export declare const ELDRA_KEY: InjectionKey<EldraContext>;
154
+ /**
155
+ * The preview slice of a fresh theme context, reactive and complete. Every
156
+ * adapter that assembles an `EldraContext` by hand — theme-nuxt's runtime
157
+ * plugin, or an equivalent entry for another framework — must build it from
158
+ * here: the hand-written literal had already drifted several fields behind
159
+ * this interface, and a missing field is a runtime error rather than a type
160
+ * error once the object is assembled elsewhere.
161
+ */
162
+ export declare function createEldraPreviewState(): EldraContext['preview'];
163
+ /**
164
+ * The link slice of a fresh theme context, reactive and empty. Built here for
165
+ * the same reason `createEldraPreviewState` is: a missing field is a runtime
166
+ * error rather than a type error once the object is assembled elsewhere.
167
+ */
168
+ /**
169
+ * The locale slice of a context with no router behind it — a Storybook story, a unit-test mount, a
170
+ * site whose organisation configures no locales. Every answer is the one-unprefixed-site answer,
171
+ * so a block written against `useEldraLocale()` renders identically in all three.
172
+ *
173
+ * An adapter that *has* routing builds its own (see `@eldrajs/theme-nuxt`'s runtime plugin): the
174
+ * active locale and the paths are derived from the route, which this layer knows nothing about.
175
+ */
176
+ export declare function createEldraLocaleState(): EldraLocaleState;
177
+ export declare function createEldraLinkState(): EldraLinkState;
178
+ export declare function provideEldra(opts: {
179
+ client: EldraClient;
180
+ designTokens?: unknown;
181
+ /** Defaults to the fallback catalogue (`{ defaultLocale: 'en-US', locales: {} }`) — a Storybook mount
182
+ * or a unit test that does not pass one renders exactly as a theme with no `i18n/` directory
183
+ * does. An adapter with a real build (`@eldrajs/theme-nuxt`'s runtime plugin) passes the
184
+ * resolved `virtual:eldra/messages` content instead. */
185
+ messages?: ThemeMessages;
186
+ }): EldraContext;
187
+ export declare function useEldra(): EldraContext;
188
+ //#endregion
189
+ //#region src/useEldraLink.d.ts
190
+ /**
191
+ * Resolve a `link` field's value against the site's own routes.
192
+ *
193
+ * Call it once per component and keep the returned function — it closes over
194
+ * the reactive link state, so every call reads whatever the adapter has filled
195
+ * in by then, and a row re-resolves when a target arrives or a preview draft
196
+ * changes the value.
197
+ *
198
+ * Outside a themed app (a block rendered in isolation, a story with no
199
+ * provider) it resolves nothing: every value comes back `null`, which a block
200
+ * already has to handle for a target that no longer exists.
201
+ *
202
+ * Every href it hands back is spelled in the **active content locale**
203
+ * (`useEldraLocale()`), so a navigation resolved on `/is-IS/about` points at
204
+ * `/is-IS/products/x` and a visitor cannot be dropped back into the default
205
+ * language by following a link. The rewrite is idempotent, which is what lets a
206
+ * theme's router-link component prefix as well without the two compounding.
207
+ */
208
+ export declare function useEldraLink(): (value: unknown) => ResolvedLink$1 | null;
209
+ //#endregion
210
+ //#region src/useEldraLocale.d.ts
211
+ /**
212
+ * The locale this page is in, the locales the site serves, and how to spell a destination in one
213
+ * of them.
214
+ *
215
+ * Call it once per component and keep what it returns — the state is reactive, so a template that
216
+ * reads `active` or calls `path()` re-renders when the visitor changes language.
217
+ *
218
+ * Outside a themed app, and on a site whose organisation configures no locales, it answers the
219
+ * one-unprefixed-site answer: `active` and `defaultLocale` `null`, `supported` empty, `path()` the
220
+ * identity. A block can therefore read it unconditionally and render the same markup in a
221
+ * Storybook story as on a single-locale site — which is also why a language switcher's own rule is
222
+ * `supported.length > 1`, never "is there a locale at all".
223
+ */
224
+ export declare function useEldraLocale(): EldraLocaleState;
225
+ //#endregion
226
+ //#region src/EldraBlockZone.d.ts
227
+ /** Resolve the schema API id across both supported gateway response shapes. */
228
+ export declare function getBlockSchemaApiId(entry: EntryDoc): string | null;
229
+ export declare const EldraBlockZone: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
230
+ blocks: {
231
+ type: PropType<EntryDoc[]>;
232
+ required: true;
233
+ };
234
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
235
+ [key: string]: any;
236
+ }>[], {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
237
+ blocks: {
238
+ type: PropType<EntryDoc[]>;
239
+ required: true;
240
+ };
241
+ }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
242
+ //#endregion
243
+ //#region src/EldraLayout.d.ts
244
+ /**
245
+ * Render a validated responsive layout with the same model and stylesheet used
246
+ * by static generation. Invalid documents and unresolved entry references fail
247
+ * closed before any customer block component is mounted.
248
+ */
249
+ export declare const EldraLayout: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
250
+ layout: {
251
+ type: PropType<unknown>;
252
+ required: true;
253
+ };
254
+ blocks: {
255
+ type: PropType<EntryDoc[]>;
256
+ required: true;
257
+ };
258
+ nonce: {
259
+ type: StringConstructor;
260
+ required: false;
261
+ default: undefined;
262
+ };
263
+ designTokens: {
264
+ type: PropType<unknown>;
265
+ required: false;
266
+ default: undefined;
267
+ };
268
+ reusableComponentProjection: {
269
+ type: PropType<ReusableComponentProjection | undefined>;
270
+ required: false;
271
+ default: undefined;
272
+ };
273
+ templateEntry: {
274
+ type: PropType<EntryDoc | undefined>;
275
+ required: false;
276
+ default: undefined;
277
+ };
278
+ }>, () => VNode<import("vue").RendererNode, import("vue").RendererElement, {
279
+ [key: string]: any;
280
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
281
+ layout: {
282
+ type: PropType<unknown>;
283
+ required: true;
284
+ };
285
+ blocks: {
286
+ type: PropType<EntryDoc[]>;
287
+ required: true;
288
+ };
289
+ nonce: {
290
+ type: StringConstructor;
291
+ required: false;
292
+ default: undefined;
293
+ };
294
+ designTokens: {
295
+ type: PropType<unknown>;
296
+ required: false;
297
+ default: undefined;
298
+ };
299
+ reusableComponentProjection: {
300
+ type: PropType<ReusableComponentProjection | undefined>;
301
+ required: false;
302
+ default: undefined;
303
+ };
304
+ templateEntry: {
305
+ type: PropType<EntryDoc | undefined>;
306
+ required: false;
307
+ default: undefined;
308
+ };
309
+ }>> & Readonly<{}>, {
310
+ nonce: string;
311
+ designTokens: undefined;
312
+ reusableComponentProjection: ReusableComponentProjection | undefined;
313
+ templateEntry: EntryDoc | undefined;
314
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
315
+ //#endregion
316
+ //#region src/EldraRichText.d.ts
317
+ /**
318
+ * Renders a §18 rich-text field's TipTap JSON document. Read render only —
319
+ * the theme SDK is a bridge, not an editor: it hosts no editing UI and
320
+ * depends on neither TipTap nor a component library.
321
+ *
322
+ * §18 v3: the operator edits this render natively — theme-core's overlay
323
+ * makes the root `contenteditable` in edit mode and reports selection, text
324
+ * ops and commands in ProseMirror document positions; Studio owns the one
325
+ * headless editor and the docked toolbar and echoes the document back
326
+ * through `editor:content-update`. While Studio is editing, the root is
327
+ * marked `data-eldra-rich-text-editing`; nothing is hidden or overlaid.
328
+ *
329
+ * The root carries the marking attributes the overlay's
330
+ * `decorateStegaTextNodes`/`applyEditable`/`reconcileExternalDrafts`
331
+ * recognize (`[data-eldra-rich-text]`) so the overlay skips per-leaf stega
332
+ * decoration here and hands click-to-select to `theme:field-clicked`
333
+ * instead (see theme-core's `overlay.ts`).
334
+ *
335
+ * Per-block toolbar: the `metadata.toolbar` data path is unchanged
336
+ * (`block.json` → manifest → Core field metadata → Studio), but the theme no
337
+ * longer reads it — Studio's docked toolbar does. The vite plugin's scanner
338
+ * still validates the control ids at build time.
339
+ *
340
+ * Locale: an omitted `locale` prop defaults to the preview's active content
341
+ * locale when the manifest marks the field `localized`, and to `null`
342
+ * otherwise. That resolved locale is what `data-eldra-locale` carries, so
343
+ * Studio edits the locale the render actually used.
344
+ *
345
+ * Heading outline: `minHeadingLevel` floors every heading tag at render time,
346
+ * so one stored document fits under two different page outlines without being
347
+ * rewritten (see that prop's own comment).
348
+ */
349
+ export declare const EldraRichText: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
350
+ entryId: {
351
+ type: StringConstructor;
352
+ required: true;
353
+ };
354
+ field: {
355
+ type: StringConstructor;
356
+ required: true;
357
+ };
358
+ locale: {
359
+ type: PropType<string | null>;
360
+ required: false;
361
+ default: null;
362
+ };
363
+ doc: {
364
+ type: PropType<unknown>;
365
+ required: true;
366
+ };
367
+ /** The owning block's schema id — a block component passes `getBlockSchemaApiId(entry)`.
368
+ * Used to resolve the field's `localized` flag from the block manifest. */
369
+ apiId: {
370
+ type: PropType<string | null | undefined>;
371
+ required: false;
372
+ default: undefined;
373
+ };
374
+ /**
375
+ * A floor for every heading in the document: a `heading` node renders as
376
+ * `h{max(minHeadingLevel, level)}`, still capped at 6. Defaults to 1 — the
377
+ * generic clamp, i.e. whatever level the document carries.
378
+ *
379
+ * A page owns its heading outline; a rich-text field does not. The same body
380
+ * document is legitimately an h2-and-down section in one block and an
381
+ * h3-and-down one in another (the starter's `article` body starts at h2, its
382
+ * `rich-text` block at h3), and nothing in the document or in `block.json`
383
+ * can say so — `metadata.toolbar`'s `heading` control id is one
384
+ * level-agnostic entry, so an editor can insert any level anywhere. Without
385
+ * this prop the only place to enforce it was a per-block transform over the
386
+ * TipTap JSON before handing it in (the starter shipped one); doing it here
387
+ * instead means the **document is never rewritten** — no copy, no
388
+ * reactive-state surgery, and an editor's own level survives a round trip
389
+ * through Studio untouched, because only the rendered tag moves.
390
+ *
391
+ * A floor, never an offset: an author who already wrote h3 under
392
+ * `minHeadingLevel: 3` keeps h3, and h4 stays h4 — only levels above the
393
+ * floor move. Every guard is in `@eldrajs/theme-core`'s own
394
+ * `clampHeadingLevel`, so an out-of-range or non-integer value can never
395
+ * produce an `<h0>`/`<h7>`.
396
+ *
397
+ * Rendering-only, deliberately: the value takes no part in position
398
+ * stamping (`data-eldra-pos` is the document's own numbering, which a
399
+ * rendered tag name cannot change), so native editing, selection reporting
400
+ * and `restampRichTextPositions` all behave exactly as they do without it.
401
+ */
402
+ minHeadingLevel: {
403
+ type: NumberConstructor;
404
+ required: false;
405
+ default: number;
406
+ };
407
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
408
+ [key: string]: any;
409
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
410
+ entryId: {
411
+ type: StringConstructor;
412
+ required: true;
413
+ };
414
+ field: {
415
+ type: StringConstructor;
416
+ required: true;
417
+ };
418
+ locale: {
419
+ type: PropType<string | null>;
420
+ required: false;
421
+ default: null;
422
+ };
423
+ doc: {
424
+ type: PropType<unknown>;
425
+ required: true;
426
+ };
427
+ /** The owning block's schema id — a block component passes `getBlockSchemaApiId(entry)`.
428
+ * Used to resolve the field's `localized` flag from the block manifest. */
429
+ apiId: {
430
+ type: PropType<string | null | undefined>;
431
+ required: false;
432
+ default: undefined;
433
+ };
434
+ /**
435
+ * A floor for every heading in the document: a `heading` node renders as
436
+ * `h{max(minHeadingLevel, level)}`, still capped at 6. Defaults to 1 — the
437
+ * generic clamp, i.e. whatever level the document carries.
438
+ *
439
+ * A page owns its heading outline; a rich-text field does not. The same body
440
+ * document is legitimately an h2-and-down section in one block and an
441
+ * h3-and-down one in another (the starter's `article` body starts at h2, its
442
+ * `rich-text` block at h3), and nothing in the document or in `block.json`
443
+ * can say so — `metadata.toolbar`'s `heading` control id is one
444
+ * level-agnostic entry, so an editor can insert any level anywhere. Without
445
+ * this prop the only place to enforce it was a per-block transform over the
446
+ * TipTap JSON before handing it in (the starter shipped one); doing it here
447
+ * instead means the **document is never rewritten** — no copy, no
448
+ * reactive-state surgery, and an editor's own level survives a round trip
449
+ * through Studio untouched, because only the rendered tag moves.
450
+ *
451
+ * A floor, never an offset: an author who already wrote h3 under
452
+ * `minHeadingLevel: 3` keeps h3, and h4 stays h4 — only levels above the
453
+ * floor move. Every guard is in `@eldrajs/theme-core`'s own
454
+ * `clampHeadingLevel`, so an out-of-range or non-integer value can never
455
+ * produce an `<h0>`/`<h7>`.
456
+ *
457
+ * Rendering-only, deliberately: the value takes no part in position
458
+ * stamping (`data-eldra-pos` is the document's own numbering, which a
459
+ * rendered tag name cannot change), so native editing, selection reporting
460
+ * and `restampRichTextPositions` all behave exactly as they do without it.
461
+ */
462
+ minHeadingLevel: {
463
+ type: NumberConstructor;
464
+ required: false;
465
+ default: number;
466
+ };
467
+ }>> & Readonly<{}>, {
468
+ locale: string | null;
469
+ apiId: string | null | undefined;
470
+ minHeadingLevel: number;
471
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
472
+ //#endregion
473
+ //#region src/richTextRender.d.ts
474
+ /**
475
+ * Vue binding for theme-core's framework-neutral rich-text render tree. Every
476
+ * node/mark rule, the `safeHref` gating and the attribute whitelist live in
477
+ * `buildRichTextTree`; this file only maps the resulting tree onto `h()` and
478
+ * supplies the vnode keys Vue wants (the tree itself carries none — keys are
479
+ * a framework concern).
480
+ */
481
+ type RenderRichTextOptions = BuildRichTextTreeOptions;
482
+ /**
483
+ * Render a TipTap JSON document's content as Vue VNodes. Non-doc input renders
484
+ * as an empty root.
485
+ *
486
+ * `generation` changes the key prefix, which makes Vue discard the subtree and
487
+ * build it again instead of patching. §18 v3's deferred rendering needs that
488
+ * exactly once per deferral: while a field is deferred the browser edits the
489
+ * DOM behind Vue's back, so the vnode tree Vue is holding no longer describes
490
+ * what is on screen, and an ordinary patch can skip a change it believes is
491
+ * already applied. Generation 0 keeps the original keys, so nothing about the
492
+ * ordinary render path changes.
493
+ */
494
+ export declare function renderRichTextDoc(doc: unknown, options: RenderRichTextOptions, generation?: number): VNodeChild[];
495
+ //#endregion
496
+ //#region src/useEldraEntry.d.ts
497
+ export declare function useEldraEntry(opts: {
498
+ schemaApiId: string;
499
+ entryId?: MaybeRefOrGetter<string | undefined>;
500
+ uniqueField?: {
501
+ fieldId: string;
502
+ value: MaybeRefOrGetter<string>;
503
+ };
504
+ query?: EntryQuery;
505
+ }): {
506
+ entry: Ref<EntryDoc | null>;
507
+ pending: Ref<boolean>;
508
+ error: Ref<string | null>;
509
+ refresh: () => Promise<void>;
510
+ };
511
+ //#endregion
512
+ //#region src/useEldraBlockField.d.ts
513
+ /**
514
+ * Vue-facing name for theme-core's block-field lookup. The registry and the
515
+ * `localized` resolution live in `@eldrajs/theme-core` so every framework
516
+ * binding shares one implementation; this wrapper exists only so themes keep
517
+ * calling a composable-shaped API.
518
+ *
519
+ * There is no `toolbar` here any more: §18 v3 keeps the rich-text toolbar in
520
+ * Studio, which reads `metadata.toolbar` from Core's field definitions. The
521
+ * vite plugin still validates the control ids at build time.
522
+ */
523
+ export declare function useEldraBlockField(apiId: string | undefined, fieldId: string): {
524
+ localized: boolean;
525
+ };
526
+ //#endregion
527
+ //#region src/useEldraPreview.d.ts
528
+ export declare function startEldraPreview(context: EldraContext, opts: {
529
+ allowedOrigins: string[];
530
+ onNavigate?: (path: string) => void;
531
+ }): {
532
+ active: Readonly<Ref<boolean>>;
533
+ destroy: () => void;
534
+ };
535
+ export declare function useEldraPreview(opts: {
536
+ allowedOrigins: string[];
537
+ onNavigate?: (path: string) => void;
538
+ }): {
539
+ active: Readonly<Ref<boolean>>;
540
+ };
541
+ //#endregion
542
+ export { type BlockFieldDefinition, type BlockFieldsMap, DEFAULT_IMAGE_FRAMING, type EldraContext, type EldraLinkState, type EldraLocaleState, type ImageFraming, type LinkKind, type LinkRouteContext, type LinkTarget, type LinkTargetInfo, type LinkValue, type RenderRichTextOptions, type ResolvedLink, type RichTextMark, type RichTextNode, type RichTextRenderChild, type RichTextRenderNode, type SlotGeometry, type SlotGeometryReporter, buildRichTextTree, imageFramingAttrs, imageFramingStyle, isBlockFieldLocalized, linkTargetKeys, normalizeImageFraming, registerBlockFields, resolveLink, safeLinkHref };