/** * Storyblok rich text <-> the pivot document. * * Both are ProseMirror documents, which is the whole reason this file is * short — and the reason it is embarrassing that it was the missing one. This * package calls the ProseMirror document "the pivot every CMS converts to and * from", ships converters for Portable Text, Contentful and Strapi, all three * of which are a different shape, and had nothing for the one CMS whose native * rich text already *is* the pivot. An integrator arriving with Storyblok * either rewrites these ninety lines or, much more likely, flattens the field * to a string and loses every mark, link and list on the first publish. * * The differences are three, and all mechanical: * * 1. Node naming. Storyblok writes `bullet_list`, the pivot writes * `bulletList`; same for ordered_list / list_item / code_block / * horizontal_rule / hard_break. Everything else — `paragraph`, * `heading`, `blockquote`, `text`, and every mark the pivot models — * is spelled identically in both. * 2. `blok`: a Storyblok node that embeds a whole component inside prose. * The pivot has a slot for exactly this, `avocadoUnknownBlock`, which * carries the source object under `attrs.data` and re-emits it unchanged. * Storyblok keeps the same payload under `attrs.body`, so the conversion * is a move, not a projection — nothing about the embedded component is * interpreted, which is what lets it survive a trip through the panel. * 3. Marks Storyblok has and the pivot does not model — `styled`, * `highlight`, `textStyle`, `anchor`, `subscript`, `superscript`. They * are carried through untouched rather than dropped; a span keeps its * unknown marks unless the span itself is edited. * * Round-trip is the contract: for any value in the space, * `toStoryblok(fromStoryblok(doc))` must equal `doc`. This implementation was * verified that way over 559 documents in a production tri-lingual space * before it was moved here. */ import { type RichTextDoc, type RichTextNode } from "./doc.ts"; /** A Storyblok rich-text value, as the Delivery API returns it. */ export type StoryblokRichText = { type: string; content?: unknown[]; [key: string]: unknown; }; export declare function isStoryblokRichText(value: unknown): value is StoryblokRichText; /** * True when the document holds no text and embeds nothing — Storyblok's * "empty" rich text, which is a `doc` with an empty paragraph in it rather * than an absent value. Worth asking before writing: publishing an empty * document over a field the CMS considers unset is a change, and it will show * up in the diff of every page nobody edited. */ export declare function isEmptyRichText(value: unknown): boolean; /** Storyblok rich text -> the ProseMirror document the property panel edits. */ export declare function fromStoryblok(doc: unknown): RichTextDoc; /** * The edited document -> Storyblok's rich text, ready to store. * * A plain string is accepted and wrapped in a paragraph. It should not happen * — the manifest pins the prop's schema to `type: "doc"`, and the panel gives * it a rich-text editor — but a planner adding a *new* list row writes the * value it would write for any other text field, and a string arriving at a * document field used to convert to an empty document. The row saved, the card * drew its title, and the sentence underneath it was gone, with nothing * logged. */ export declare function toStoryblok(doc: RichTextDoc | unknown): StoryblokRichText; export type { RichTextDoc, RichTextNode };