@bettercms-ai/kit 2.0.0-next.4 → 2.0.0-next.6
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/controls/reference-services.d.ts +23 -2
- package/dist/controls/reference.js +74 -24
- package/dist/controls/reference.js.map +1 -1
- package/dist/controls/rich-text/article-shell.js +4 -1
- package/dist/controls/rich-text/article-shell.js.map +1 -1
- package/dist/controls/rich-text/blocks.js +2 -1
- package/dist/controls/rich-text/blocks.js.map +1 -1
- package/dist/controls/rich-text/codec.d.ts +2 -0
- package/dist/controls/rich-text/codec.js.map +1 -1
- package/dist/controls/rich-text/related.d.ts +7 -2
- package/dist/controls/rich-text/related.js +14 -5
- package/dist/controls/rich-text/related.js.map +1 -1
- package/dist/schema/types.d.ts +1 -0
- package/dist/schema/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blocks.js","names":[],"sources":["../../../src/controls/rich-text/blocks.tsx"],"sourcesContent":["/**\n * RICH TEXT · rich blocks (\"islands\", V2 HEAD controls/rich-text/blocks.tsx). Each renders through a portal into a\n * contenteditable=false element inside the editor surface; its model lives on that element (`el.__rt`) so serialize()\n * can write it back as static HTML.\n *\n * The image block is V2's: the toolbar (Replace · Align · Size S/M/L/Full · Link · Alt text · Remove) and the caption\n * under it, each stored on production's image block (see dom.ts). The callout (tone · title · message), toggle, video,\n * file and embedded component blocks are V2's too, limited to what their stored blocks keep: a callout's message and a\n * toggle's body are plain text (production stores them as text), a component is a linked card (its fields are edited\n * on the component itself).\n *\n * The table block is V2's TableBlock: the kit's table editor on the island's table value. The pull quote is V2's article\n * block (article-blocks.tsx ArticleBlock + PullQuotePreview): drawn as the reader sees it, edited in the floating\n * properties panel (4 settings → panel, CPO D54) through the kit SchemaForm. Key takeaways are V2's article block too,\n * written in the text itself (article-blocks.tsx TakeawaysEditor, surface \"inline\"): a title line and a line per point.\n * The embed island draws embed.tsx's block (V2's article Embed: Link, Title, Shape), keeping the keys it lacks absent.\n */\nimport * as React from \"react\";\nimport {\n IconAlertTriangle, IconAlt, IconArrowsHorizontal,\n IconBulb, IconCheck, IconChevronDown, IconChevronRight, IconComponents, IconCopy, IconFileText, IconFileUpload,\n IconInfoCircle, IconLayoutAlignCenter, IconLayoutAlignLeft, IconLayoutAlignRight, IconLibraryPhoto,\n IconLink, IconPhoto, IconQuote, IconReplace, IconTrash, IconWorld,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../../lib/cn\";\nimport { useKitServices } from \"../../context\";\nimport { isCropRect } from \"../../adapters/values\";\nimport { Button, IconButton } from \"../../ui/button\";\nimport { Input, Textarea } from \"../../ui/input\";\nimport { Menu, MenuContent, MenuItem, MenuTrigger, Popover, PopoverContent, PopoverTrigger, Tooltip } from \"../../ui/overlay\";\nimport type { TypeSchema } from \"../../schema/types\";\nimport type { TableFieldOptions } from \"../../schema/table\";\nimport { TableEditor } from \"../table/table-editor\";\nimport { VideoPreview } from \"../media\";\nimport {\n CALLOUT_TONES, placeCaret, type CalloutModel, type CalloutTone, type ComponentModel, type EmbedModel, type FileModel, type GalleryIslandModel, type TocIslandModel, type ColumnsIslandModel, type AuthorIslandModel, type RelatedIslandModel, type ImageAlign, type ImageModel, type ImageSize,\n type IslandEl, type IslandKind, type IslandModels, type PullQuoteModel, type TableModel, type TakeawaysModel, type ToggleModel, type VideoModel,\n} from \"./dom\";\nimport { LinkForm } from \"./link-form\";\nimport { EmbedBlock } from \"./embed\";\nimport { GalleryBlock, type GalleryModel } from \"./gallery\";\nimport { TocBlock, type OutlineStore } from \"./toc\";\nimport { ColumnsBlock } from \"./columns\";\nimport { AuthorBlock } from \"./author\";\nimport { RelatedBlock, type RelatedModel } from \"./related\";\nimport type { RichTextRead } from \"./codec\";\nimport type { RichTextFeature } from \"./codec\";\nimport { ArticleActions, ArticleCard, ArticleForm, ArticlePanel, articleCardKeyDown, useArticleOpen } from \"./article-shell\";\nimport { ComponentCard } from \"./component-card\";\n\nexport interface IslandCtx {\n readOnly?: boolean;\n dense?: boolean;\n emit: () => void;\n remove: (el: IslandEl) => void;\n /** Leave the block: caret to the start of the next line. */\n exit: (el: IslandEl) => void;\n /** CPO D54: an island write joins the body field's ONE undo history (`before` = the model it replaces) */\n record?: (el: IslandEl, before: IslandModels[IslandKind], label: string, coalesce?: boolean) => void;\n /** a refusal the author must see (an address the image cannot use) */\n notify: (message: string, description?: string) => void;\n duplicate?: (el: IslandEl) => void;\n move?: (el: IslandEl, dir: -1 | 1) => void;\n /** the body field's one history (D54): ⌘Z on a focused block or in its panel */\n undo?: () => boolean;\n /**\n * One action as ONE undo step, in the host's history when it keeps one (the dock's toolbar Undo), else the body's\n * own. `write` makes the change and answers the island it changed; `before` is that island's model before it. Answers\n * the step's undo (an Undo toast): true, or the sentence saying why nothing was undone.\n */\n step?: (label: string, write: () => IslandEl | null, before?: IslandModels[IslandKind]) => () => true | string;\n redo?: () => boolean;\n /** Drag the block to a new position (the gutter grip's drag), for a grip drawn on the block itself */\n drag?: (el: IslandEl, e: React.PointerEvent) => void;\n /** what the field's stored shape keeps (the host codec's features): a setting it cannot store is not drawn */\n allows?: (f: RichTextFeature) => boolean;\n /** the body's live outline (its top-level H2/H3), which a table of contents lists */\n outline?: OutlineStore;\n /** where an author bio and related posts point (the field's config, from the host codec) */\n references?: RichTextRead[\"references\"];\n}\n\n/* ── Island host ───────────────────────────────────────────────────── */\n/** `label` names the step in the editor's one undo history (\"Edit alt text\"). */\nexport type Setter<K extends IslandKind> = (patch: Partial<IslandModels[K]> | ((m: IslandModels[K]) => IslandModels[K]), label?: string, record?: boolean) => void;\nexport interface BlockProps<K extends IslandKind> {\n m: IslandModels[K];\n set: Setter<K>;\n el: IslandEl;\n ctx: IslandCtx;\n autoFocus: boolean;\n}\n\nexport function Island({ el, ctx }: { el: IslandEl; ctx: IslandCtx }) {\n const kind = el.getAttribute(\"data-rt-island\") as IslandKind;\n const [model, setModel] = React.useState(() => el.__rt!);\n const [autoFocus] = React.useState(() => !!el.__rtFocus);\n React.useEffect(() => {\n el.__rtFocus = false;\n }, [el]);\n const set = React.useCallback(\n (patch: unknown, label = \"Edit block\", record = true) => {\n const prev = el.__rt as IslandModels[IslandKind];\n const next = typeof patch === \"function\" ? (patch as (m: object) => object)(prev) : { ...prev, ...(patch as object) };\n // `record` false: the caller records the step itself (IslandCtx.step)\n if (record) ctx.record?.(el, prev, label, typeof patch !== \"function\");\n el.__rt = next as IslandModels[IslandKind];\n setModel(next as IslandModels[IslandKind]);\n ctx.emit();\n },\n [el, ctx],\n );\n React.useEffect(() => {\n el.__rtSet = (m) => {\n el.__rt = m;\n setModel(m);\n ctx.emit();\n };\n return () => {\n if (el.__rtSet) delete el.__rtSet;\n };\n }, [el, ctx]);\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (!e.currentTarget.contains(e.target as Node)) return;\n if (e.key === \"Escape\" && (e.target as HTMLElement).isContentEditable) {\n e.preventDefault();\n e.stopPropagation();\n ctx.exit(el);\n }\n };\n return (\n <div className=\"relative select-text\" onKeyDown={onKeyDown} data-island-kind={kind}>\n {kind === \"image\" ? <ImageBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as ImageModel} set={set as Setter<\"image\">} />\n : kind === \"table\" ? <TableBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as TableModel} set={set as Setter<\"table\">} />\n : kind === \"pullquote\" ? <PullQuoteBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as PullQuoteModel} set={set as Setter<\"pullquote\">} />\n : kind === \"takeaways\" ? <TakeawaysBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as TakeawaysModel} set={set as Setter<\"takeaways\">} />\n : kind === \"callout\" ? <CalloutBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as CalloutModel} set={set as Setter<\"callout\">} />\n : kind === \"toggle\" ? <ToggleBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as ToggleModel} set={set as Setter<\"toggle\">} />\n : kind === \"video\" ? <VideoBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as VideoModel} set={set as Setter<\"video\">} />\n : kind === \"embed\" ? <EmbedIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as EmbedModel} set={set as Setter<\"embed\">} />\n : kind === \"gallery\" ? <GalleryIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as GalleryIslandModel} set={set as Setter<\"gallery\">} />\n : kind === \"toc\" ? <TocIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as TocIslandModel} set={set as Setter<\"toc\">} />\n : kind === \"columns\" ? <ColumnsIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as ColumnsIslandModel} set={set as Setter<\"columns\">} />\n : kind === \"author\" ? <AuthorIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as AuthorIslandModel} set={set as Setter<\"author\">} />\n : kind === \"related\" ? <RelatedIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as RelatedIslandModel} set={set as Setter<\"related\">} />\n : kind === \"file\" ? <FileBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as FileModel} set={set as Setter<\"file\">} />\n : kind === \"component\" ? <ComponentBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as ComponentModel} set={set as Setter<\"component\">} />\n : null}\n </div>\n );\n}\n\n/** The island's stored embed (keys absent when the block lacks them) through embed.tsx's block (V2's model). */\nfunction EmbedIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"embed\">) {\n return (\n <EmbedBlock\n m={{ url: m.url ?? \"\", title: m.title ?? \"\", aspect: m.aspect ?? \"16:9\" }}\n set={(fn, label) =>\n set((prev) => {\n const next = fn({ url: prev.url ?? \"\", title: prev.title ?? \"\", aspect: prev.aspect ?? \"16:9\" });\n // a key the block never had stays absent until it is given one (an untouched block reads back as stored)\n const out: EmbedModel = { ...prev };\n if (next.url !== (prev.url ?? \"\")) out.url = next.url;\n if (next.title !== (prev.title ?? \"\")) out.title = next.title;\n if (next.aspect !== (prev.aspect ?? \"16:9\")) out.aspect = next.aspect;\n return out;\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n shape={ctx.allows?.(\"embedAspect\") ?? false}\n />\n );\n}\n\n/** The island's stored gallery (src, alt, caption per image) through gallery.tsx's block (V2's model, an image value each). */\n/** RT9: a gallery image's framing, image-url's 0–1 focal point ⇄ the kit image value's percent */\nconst toGallery = (m: GalleryIslandModel): GalleryModel => ({\n layout: m.layout ?? \"grid\",\n images: m.images.map((x) => ({\n _key: x._key,\n image: x.src\n ? { url: x.src, ...(x.alt !== undefined ? { alt: x.alt } : {}), ...(x.crop ? { crop: x.crop } : {}), ...(x.focal ? { focal: { x: x.focal.x * 100, y: x.focal.y * 100 } } : {}) }\n : null,\n caption: x.caption ?? \"\",\n })),\n});\nconst cropOf = (c: unknown) => (isCropRect(c) ? { top: c.top, right: c.right, bottom: c.bottom, left: c.left } : undefined);\nconst focalOf = (f: unknown) => {\n const p = f as { x?: unknown; y?: unknown } | undefined;\n return typeof p?.x === \"number\" && typeof p?.y === \"number\" ? { x: Math.round(p.x) / 100, y: Math.round(p.y) / 100 } : undefined;\n};\nfunction GalleryIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"gallery\">) {\n return (\n <GalleryBlock\n m={toGallery(m)}\n set={(fn, label) =>\n set((prev) => {\n const next = fn(toGallery(prev));\n const out: GalleryIslandModel = {\n ...prev,\n images: next.images.map((x) => {\n const crop = cropOf(x.image?.crop);\n const focal = focalOf(x.image?.focal);\n return { _key: x._key, src: x.image?.url ?? \"\", ...(x.image?.alt !== undefined ? { alt: x.image.alt } : {}), ...(x.caption ? { caption: x.caption } : {}), ...(crop ? { crop } : {}), ...(focal ? { focal } : {}) };\n }),\n };\n if (next.layout !== (prev.layout ?? \"grid\")) out.layout = next.layout;\n return out;\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n />\n );\n}\n\n/** The island's stored table of contents (title, depth) through toc.tsx's block, which lists the editor's live outline. */\nfunction TocIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"toc\">) {\n return (\n <TocBlock\n m={{ title: m.title ?? \"\", depth: m.depth ?? \"3\" }}\n set={(fn, label) =>\n set((prev) => {\n const next = fn({ title: prev.title ?? \"\", depth: prev.depth ?? \"3\" });\n const out: TocIslandModel = { ...prev };\n if (next.title !== (prev.title ?? \"\")) out.title = next.title;\n if (next.depth !== (prev.depth ?? \"3\")) out.depth = next.depth;\n return out;\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n />\n );\n}\n\n/** The island's stored columns (gap, each column's HTML) through columns.tsx's block. */\nfunction ColumnsIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"columns\">) {\n return (\n <ColumnsBlock\n m={{ gap: m.gap ?? \"m\", cols: m.cols }}\n set={(fn, label) =>\n set((prev) => {\n const next = fn({ gap: prev.gap ?? \"m\", cols: prev.cols });\n const out: ColumnsIslandModel = { ...prev, cols: next.cols };\n if (next.gap !== (prev.gap ?? \"m\")) out.gap = next.gap;\n return out;\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n />\n );\n}\n\n/** The island's stored author reference through author.tsx's block, picking from the field's authors collection. */\nfunction AuthorIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"author\">) {\n return (\n <AuthorBlock\n m={{ author: m.author ?? \"\" }}\n set={(fn, label) =>\n set((prev) => {\n const next = fn({ author: prev.author ?? \"\" });\n return next.author === (prev.author ?? \"\") ? prev : { ...prev, author: next.author };\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n collection={ctx.references?.authors ?? \"\"}\n />\n );\n}\n\n/** The island's stored related posts through related.tsx's block, over the field's posts collection. */\nconst toRelated = (m: RelatedIslandModel): RelatedModel => ({ title: m.title ?? \"\", mode: m.mode ?? \"topic\", count: m.count ?? 3, posts: m.posts });\nfunction RelatedIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"related\">) {\n return (\n <RelatedBlock\n m={toRelated(m)}\n set={(fn, label) =>\n set((prev) => {\n const was = toRelated(prev);\n const next = fn(was);\n const out: RelatedIslandModel = { ...prev, posts: next.posts };\n if (next.title !== was.title) out.title = next.title;\n if (next.mode !== was.mode) out.mode = next.mode;\n if (next.count !== was.count) out.count = next.count;\n return out;\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n collection={ctx.references?.posts ?? \"\"}\n excludeId={ctx.references?.entryId}\n topic={ctx.references?.topic !== false}\n />\n );\n}\n\nconst Sep = () => <span aria-hidden className=\"mx-0.5 h-4 w-px shrink-0 bg-kit-line\" />;\n/** An address an image can be shown from: http(s) or a site path. */\nconst imageUrlOk = (u: string) => /^(https?:)?\\/\\/[^\\s]+$/i.test(u) || /^\\/(?!\\/)\\S*$/.test(u);\n\n/* ── plain text inside a block (V2 Editable, `plain`): a caption, a title, a message ── */\nfunction PlainEditable({\n value,\n onChange,\n label,\n placeholder,\n readOnly,\n autoFocus,\n multiline,\n onEnter,\n onBackspaceEmpty,\n className,\n}: {\n value: string;\n onChange: (v: string) => void;\n label: string;\n placeholder?: string;\n readOnly?: boolean;\n autoFocus?: boolean;\n /** Enter adds a line (else Enter calls `onEnter`) */\n multiline?: boolean;\n onEnter?: () => void;\n /** Backspace in an empty line (V2 PlainLine: removes the line) */\n onBackspaceEmpty?: () => void;\n className?: string;\n}) {\n const ref = React.useRef<HTMLDivElement>(null);\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el || document.activeElement === el) return;\n if (el.textContent !== value) el.textContent = value;\n }, [value]);\n React.useEffect(() => {\n if (!autoFocus || !ref.current || readOnly) return;\n ref.current.focus({ preventScroll: false });\n placeCaret(ref.current);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n return (\n <div\n ref={ref}\n role=\"textbox\"\n aria-label={label}\n aria-multiline={multiline || undefined}\n aria-readonly={readOnly || undefined}\n tabIndex={readOnly ? -1 : 0}\n contentEditable={readOnly ? false : \"plaintext-only\"}\n suppressContentEditableWarning\n spellCheck\n data-placeholder={placeholder}\n onInput={(e) => onChange((e.currentTarget.textContent ?? \"\").replace(/\\u200b/g, \"\"))}\n onPaste={(e) => {\n e.preventDefault();\n const t = e.clipboardData.getData(\"text/plain\");\n document.execCommand(\"insertText\", false, multiline ? t : t.replace(/\\s*\\n\\s*/g, \" \"));\n }}\n onKeyDown={(e) => {\n if (e.key === \"Backspace\" && onBackspaceEmpty && !(e.currentTarget.textContent ?? \"\").replace(/\\u200b/g, \"\")) {\n e.preventDefault();\n onBackspaceEmpty();\n return;\n }\n if (e.key !== \"Enter\") return;\n // in a multi-line field Enter adds a line; ⌘↵ leaves it\n if (multiline && !(e.metaKey || e.ctrlKey)) return;\n e.preventDefault();\n onEnter?.();\n }}\n className={cn(\n \"min-w-0 cursor-text whitespace-pre-wrap break-words outline-none focus-visible:shadow-none empty:before:pointer-events-none empty:before:text-kit-fg-4 empty:before:content-[attr(data-placeholder)]\",\n className,\n )}\n />\n );\n}\n\nconst RemoveBtn = ({ onClick, label = \"Remove block\" }: { onClick: () => void; label?: string }) => (\n <IconButton size=\"xs\" label={label} onClick={onClick} className=\"hover:text-kit-danger-fg\">\n <IconTrash stroke={1.5} />\n </IconButton>\n);\n\n/* ── Image ─────────────────────────────────────────────────────────── */\n/** Production's image layout (lib/richtext/imageLayout.ts): a size sets the column share; with none, the alignment does. */\nconst SIZES: { v: ImageSize; label: string; w: string }[] = [\n { v: \"s\", label: \"S\", w: \"33%\" },\n { v: \"m\", label: \"M\", w: \"50%\" },\n { v: \"l\", label: \"L\", w: \"75%\" },\n { v: \"full\", label: \"Full\", w: \"100%\" },\n];\nconst ALIGN_WIDTH: Record<ImageAlign, string> = { full: \"100%\", center: \"78%\", left: \"62%\", right: \"62%\" };\nconst ALIGNS: { v: ImageAlign; label: string; icon: React.ReactNode }[] = [\n { v: \"full\", label: \"Full width\", icon: <IconArrowsHorizontal stroke={1.5} /> },\n { v: \"center\", label: \"Center\", icon: <IconLayoutAlignCenter stroke={1.5} /> },\n { v: \"left\", label: \"Left\", icon: <IconLayoutAlignLeft stroke={1.5} /> },\n { v: \"right\", label: \"Right\", icon: <IconLayoutAlignRight stroke={1.5} /> },\n];\n\nfunction ImageBlock({ m, set, el, ctx }: BlockProps<\"image\">) {\n const { MediaPicker } = useKitServices();\n const [picker, setPicker] = React.useState(false);\n const [urlMode, setUrlMode] = React.useState(false);\n const [url, setUrl] = React.useState(\"\");\n const [busy, setBusy] = React.useState(false);\n const [linkOpen, setLinkOpen] = React.useState(false);\n const ro = ctx.readOnly;\n const pickerEl = MediaPicker ? (\n <MediaPicker\n open={picker}\n onOpenChange={setPicker}\n accept={[\"image\", \"svg\"]}\n onPick={(a) => a[0] && set({ src: a[0].url, alt: a[0].alt || m.alt || \"\" }, \"Replace image\")}\n />\n ) : null;\n\n if (!m.src)\n return (\n <div className=\"group/blk rounded-kit-lg border border-dashed border-kit-line-strong bg-kit-subtle px-4 py-4\">\n <div className=\"flex items-start gap-3\">\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-kit-md border border-kit-line bg-kit-control-bg text-kit-fg-2\">\n <IconPhoto className=\"size-4\" stroke={1.5} aria-hidden />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-kit-sm font-medium text-kit-fg\">Add an image</div>\n <p className=\"text-kit-xs text-kit-fg-3\">{MediaPicker ? \"Pick one from the media library or paste a link.\" : \"Paste a link to the image.\"}</p>\n </div>\n {ro ? null : <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove image\" />}\n </div>\n {ro ? null : urlMode || !MediaPicker ? (\n <form\n className=\"mt-3 flex items-center gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n const u = url.trim();\n if (!imageUrlOk(u)) return ctx.notify(\"Paste a full image link that starts with https://\", \"Or a path on this site, like /images/hero.jpg.\");\n set({ src: u, alt: m.alt ?? \"\" }, \"Add image\");\n ctx.exit(el);\n }}\n >\n <Input autoFocus size=\"sm\" value={url} onChange={(e) => setUrl(e.target.value)} placeholder=\"https://…\" aria-label=\"Image URL\" leading={<IconLink stroke={1.5} />} className=\"flex-1\" />\n <Button size=\"sm\" variant=\"primary\" type=\"submit\" disabled={!url.trim()}>\n Add\n </Button>\n {MediaPicker ? (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setUrlMode(false)}>\n Cancel\n </Button>\n ) : null}\n </form>\n ) : (\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <Button size=\"sm\" leftIcon={<IconLibraryPhoto stroke={1.5} />} onClick={() => setPicker(true)}>\n Media library\n </Button>\n <Button size=\"sm\" variant=\"ghost\" leftIcon={<IconLink stroke={1.5} />} onClick={() => setUrlMode(true)}>\n Paste URL\n </Button>\n </div>\n )}\n {pickerEl}\n </div>\n );\n\n const align: ImageAlign = m.align ?? \"full\";\n const size = m.size;\n const width = size ? (SIZES.find((x) => x.v === size)?.w ?? \"100%\") : ALIGN_WIDTH[align];\n const justify = align === \"left\" ? \"justify-start\" : align === \"right\" ? \"justify-end\" : \"justify-center\";\n const alignItem = ALIGNS.find((a) => a.v === align)!;\n const toolBtn =\n \"flex h-7 shrink-0 items-center gap-1 rounded-kit-md px-1.5 text-kit-xs text-kit-fg-2 hover:bg-kit-hover hover:text-kit-fg data-[popup-open]:bg-kit-hover data-[popup-open]:text-kit-fg [&_svg]:size-3.5\";\n return (\n <figure className=\"group/blk relative m-0\">\n {ro ? null : (\n <div\n role=\"toolbar\"\n aria-label=\"Image tools\"\n className={cn(\n \"glass-strong absolute left-1/2 top-2 z-[3] flex h-8 -translate-x-1/2 items-center gap-0.5 rounded-kit-lg border border-kit-line px-1 opacity-0 transition-opacity duration-150\",\n \"group-hover/blk:opacity-100 group-focus-within/blk:opacity-100 pointer-coarse:opacity-100\",\n busy && \"opacity-100\",\n )}\n >\n {MediaPicker ? (\n <>\n <Tooltip content=\"Replace image\">\n <button type=\"button\" aria-label=\"Replace image\" className={toolBtn} onClick={() => setPicker(true)}>\n <IconReplace stroke={1.5} aria-hidden />\n {ctx.dense ? null : \"Replace\"}\n </button>\n </Tooltip>\n <Sep />\n </>\n ) : null}\n <Menu onOpenChange={setBusy}>\n <MenuTrigger\n render={\n <button type=\"button\" aria-label={`Align: ${alignItem.label}`} className={toolBtn}>\n {alignItem.icon}\n <IconChevronDown className=\"!size-3 text-kit-fg-3\" stroke={1.5} aria-hidden />\n </button>\n }\n />\n <MenuContent className=\"w-40\">\n {ALIGNS.map((a) => (\n <MenuItem\n key={a.v}\n icon={a.icon}\n onClick={() => set({ align: a.v, ...(a.v === \"full\" ? { size: \"full\" as const } : size === \"full\" || !size ? { size: \"l\" as const } : {}) }, \"Align image\")}\n >\n <span className=\"flex w-full items-center justify-between\">\n {a.label}\n {align === a.v ? <IconCheck className=\"size-3.5 text-kit-fg-2\" stroke={2} aria-hidden /> : null}\n </span>\n </MenuItem>\n ))}\n </MenuContent>\n </Menu>\n <div role=\"group\" aria-label=\"Size\" className=\"flex items-center\">\n {SIZES.map((x) => {\n const on = (size ?? (align === \"full\" ? \"full\" : null)) === x.v;\n return (\n <button\n key={x.v}\n type=\"button\"\n aria-pressed={on}\n aria-label={`Size ${x.label}`}\n onClick={() => set({ size: x.v, ...(x.v === \"full\" ? { align: \"full\" as const } : align === \"full\" ? { align: \"center\" as const } : {}) }, \"Resize image\")}\n className={cn(\"h-7 min-w-6 rounded-kit-md px-1 text-kit-xs text-kit-fg-3 hover:bg-kit-hover hover:text-kit-fg\", on && \"bg-kit-selected font-medium text-kit-fg\")}\n >\n {x.label}\n </button>\n );\n })}\n </div>\n <Sep />\n <Popover\n open={linkOpen}\n onOpenChange={(o) => {\n setLinkOpen(o);\n setBusy(o);\n }}\n >\n <Tooltip content={m.href ? `Links to ${m.href}` : \"Link the image\"}>\n <PopoverTrigger aria-label=\"Link the image\" className={cn(toolBtn, m.href && \"text-kit-accent-fg\")}>\n <IconLink stroke={1.5} aria-hidden />\n </PopoverTrigger>\n </Tooltip>\n <PopoverContent className=\"w-[340px] rounded-kit-lg p-3\" finalFocus={false}>\n <p className=\"mb-2 text-kit-xs font-medium text-kit-fg-2\">Link the image to</p>\n <LinkForm\n initial={{ href: m.href ?? \"\" }}\n allowNewTab={false}\n onSave={(v) => {\n set({ href: v.href }, \"Link image\");\n setLinkOpen(false);\n setBusy(false);\n }}\n onCancel={() => {\n setLinkOpen(false);\n setBusy(false);\n }}\n onDetach={\n m.href\n ? () => {\n set((x) => {\n const { href: _gone, ...rest } = x;\n void _gone;\n return rest;\n }, \"Unlink image\");\n setLinkOpen(false);\n setBusy(false);\n }\n : undefined\n }\n />\n </PopoverContent>\n </Popover>\n <AltPopover alt={m.alt ?? \"\"} onSave={(alt) => set({ alt }, \"Edit alt text\")} onOpen={setBusy} btnClass={toolBtn} />\n <Sep />\n <Tooltip content=\"Remove image\">\n <button type=\"button\" aria-label=\"Remove image\" className={cn(toolBtn, \"hover:text-kit-danger-fg\")} onClick={() => ctx.remove(el)}>\n <IconTrash stroke={1.5} aria-hidden />\n </button>\n </Tooltip>\n </div>\n )}\n <div className={cn(\"flex\", justify)}>\n <img src={m.src} alt={m.alt ?? \"\"} style={{ width }} className=\"block h-auto max-w-full rounded-kit-lg border border-kit-line\" draggable={false} />\n </div>\n {ro && !m.caption ? null : (\n <PlainEditable\n value={m.caption ?? \"\"}\n label=\"Image caption\"\n readOnly={ro}\n placeholder={ro ? \"\" : \"Add a caption…\"}\n onChange={(caption) => set({ caption }, \"Edit caption\")}\n onEnter={() => ctx.exit(el)}\n className=\"mt-1.5 text-center text-kit-sm text-kit-fg-3 empty:before:text-kit-fg-4\"\n />\n )}\n {pickerEl}\n </figure>\n );\n}\n\nfunction AltPopover({ alt, onSave, onOpen, btnClass }: { alt: string; onSave: (a: string) => void; onOpen: (o: boolean) => void; btnClass: string }) {\n const [open, setOpen] = React.useState(false);\n const [v, setV] = React.useState(alt);\n const id = React.useId();\n return (\n <Popover\n open={open}\n onOpenChange={(o) => {\n setOpen(o);\n onOpen(o);\n if (o) setV(alt);\n }}\n >\n <Tooltip content={alt ? \"Alt text\" : \"Add alt text\"}>\n <PopoverTrigger aria-label=\"Alt text\" className={cn(btnClass, !alt && \"text-kit-warning-fg\")}>\n <IconAlt stroke={1.5} aria-hidden />\n </PopoverTrigger>\n </Tooltip>\n <PopoverContent className=\"w-72\" finalFocus={false}>\n <form\n onSubmit={(e) => {\n e.preventDefault();\n onSave(v.trim());\n setOpen(false);\n onOpen(false);\n }}\n className=\"flex flex-col gap-2\"\n >\n <label className=\"text-kit-xs font-medium text-kit-fg-2\" htmlFor={id}>\n Alt text\n </label>\n <Textarea id={id} autoFocus value={v} onChange={(e) => setV(e.target.value)} placeholder=\"Describe the image\" rows={2} />\n <p className=\"text-kit-2xs text-kit-fg-3\">Read aloud by screen readers and used by search engines.</p>\n <div className=\"flex justify-end\">\n <Button size=\"xs\" variant=\"primary\" type=\"submit\">\n Done\n </Button>\n </div>\n </form>\n </PopoverContent>\n </Popover>\n );\n}\n\n/* ── Callout (V2 CalloutBlock): tone · title · message ─────────────── */\nconst TONES: Record<CalloutTone, { label: string; icon: React.ReactNode; box: string; chip: string }> = {\n info: { label: \"Info\", icon: <IconInfoCircle stroke={1.5} />, box: \"border-kit-info-line bg-kit-info-soft\", chip: \"text-kit-info-fg\" },\n tip: { label: \"Tip\", icon: <IconBulb stroke={1.5} />, box: \"border-kit-success-line bg-kit-success-soft\", chip: \"text-kit-success-fg\" },\n warning: { label: \"Warning\", icon: <IconAlertTriangle stroke={1.5} />, box: \"border-kit-warning-line bg-kit-warning-soft\", chip: \"text-kit-warning-fg\" },\n};\nfunction CalloutBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"callout\">) {\n // a stored tone the editor does not offer is kept as it is, and drawn as Info\n const tone: CalloutTone = (CALLOUT_TONES as readonly string[]).includes(m.tone ?? \"\") ? (m.tone as CalloutTone) : \"info\";\n const t = TONES[tone];\n const body = React.useRef<HTMLDivElement>(null);\n const ro = ctx.readOnly;\n return (\n <div className={cn(\"group/blk relative rounded-kit-lg border px-3.5 py-3\", t.box)} data-rt-isolate=\"\">\n <div className=\"flex items-center gap-1\">\n <Menu>\n <MenuTrigger\n disabled={ro}\n render={\n <button\n type=\"button\"\n aria-label={`Callout tone: ${t.label}`}\n className={cn(\"-ml-1 flex h-6 items-center gap-1 rounded-kit-md px-1.5 text-kit-xs font-medium hover:bg-kit-hover data-[popup-open]:bg-kit-hover [&_svg]:size-3.5\", t.chip)}\n >\n {t.icon}\n {t.label}\n {ro ? null : <IconChevronDown className=\"!size-3 opacity-70\" stroke={1.5} aria-hidden />}\n </button>\n }\n />\n <MenuContent className=\"w-36\">\n {CALLOUT_TONES.map((k) => (\n <MenuItem key={k} icon={TONES[k].icon} onClick={() => set({ tone: k }, \"Change callout tone\")}>\n <span className=\"flex w-full items-center justify-between\">\n {TONES[k].label}\n {tone === k ? <IconCheck className=\"size-3.5 text-kit-fg-2\" stroke={2} aria-hidden /> : null}\n </span>\n </MenuItem>\n ))}\n </MenuContent>\n </Menu>\n <span className=\"flex-1\" />\n {ro ? null : (\n <div className=\"opacity-0 transition-opacity group-hover/blk:opacity-100 group-focus-within/blk:opacity-100 pointer-coarse:opacity-100\">\n <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove callout\" />\n </div>\n )}\n </div>\n <PlainEditable\n value={m.title ?? \"\"}\n label=\"Callout title\"\n readOnly={ro}\n autoFocus={autoFocus}\n placeholder=\"Title\"\n onChange={(title) => set({ title }, \"Edit callout title\")}\n onEnter={() => {\n const b = body.current?.querySelector<HTMLElement>(\"[role=textbox]\");\n b?.focus();\n if (b) placeCaret(b, false);\n }}\n className=\"mt-1 font-medium text-kit-fg\"\n />\n <div ref={body}>\n <PlainEditable\n multiline\n value={m.body ?? \"\"}\n label=\"Callout message\"\n readOnly={ro}\n placeholder=\"Write a message…\"\n onChange={(b) => set({ body: b }, \"Edit callout message\")}\n className=\"mt-0.5 text-kit-fg-2 [&_p+p]:mt-2\"\n />\n </div>\n </div>\n );\n}\n\n/* ── Toggle (V2 ToggleBlock): title · body. A saved toggle shows closed, like V2; a just-inserted one shows open. Open or closed is the editor's view only. ── */\nfunction ToggleBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"toggle\">) {\n const [open, setOpen] = React.useState(!!autoFocus);\n const body = React.useRef<HTMLDivElement>(null);\n const bodyId = React.useId();\n const ro = ctx.readOnly;\n return (\n <div className=\"group/blk relative rounded-kit-md\" data-rt-isolate=\"\">\n <div className=\"flex items-start gap-1\">\n <button\n type=\"button\"\n aria-expanded={open}\n aria-controls={bodyId}\n aria-label={open ? \"Collapse toggle\" : \"Expand toggle\"}\n onClick={() => setOpen((o) => !o)}\n className=\"mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-kit-md text-kit-fg-2 hover:bg-kit-hover hover:text-kit-fg\"\n >\n <IconChevronRight className={cn(\"size-4 transition-transform duration-150\", open && \"rotate-90\")} stroke={1.5} aria-hidden />\n </button>\n <PlainEditable\n value={m.title ?? \"\"}\n label=\"Toggle title\"\n readOnly={ro}\n autoFocus={autoFocus}\n placeholder=\"Toggle title\"\n onChange={(title) => set({ title }, \"Edit toggle title\")}\n onEnter={() => {\n setOpen(true);\n requestAnimationFrame(() => {\n const b = body.current?.querySelector<HTMLElement>(\"[role=textbox]\");\n b?.focus();\n if (b) placeCaret(b, false);\n });\n }}\n className=\"flex-1 py-0.5 font-medium text-kit-fg\"\n />\n {ro ? null : (\n <div className=\"opacity-0 transition-opacity group-hover/blk:opacity-100 group-focus-within/blk:opacity-100 pointer-coarse:opacity-100\">\n <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove toggle\" />\n </div>\n )}\n </div>\n <div ref={body} id={bodyId} hidden={!open} className=\"ml-3 mt-1 border-l border-kit-line pl-4\">\n <PlainEditable\n multiline\n value={m.body ?? \"\"}\n label=\"Toggle body\"\n readOnly={ro}\n placeholder=\"Hidden content. Click to write…\"\n onChange={(b) => set({ body: b }, \"Edit toggle body\")}\n className=\"py-0.5 text-kit-fg-2 [&_p+p]:mt-2\"\n />\n </div>\n </div>\n );\n}\n\n/* ── Video (V2 VideoBlock): a link to a video file or a provider's page ─── */\nconst videoUrlOk = (u: string) => /^https?:\\/\\/\\S+$/i.test(u) || /^\\/(?!\\/)\\S*$/.test(u);\nfunction VideoBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"video\">) {\n const [draft, setDraft] = React.useState(m.url ?? \"\");\n React.useEffect(() => setDraft(m.url ?? \"\"), [m.url]);\n const [error, setError] = React.useState<string | null>(null);\n const errId = React.useId();\n const commit = () => {\n const u = draft.trim();\n if (u === (m.url ?? \"\")) return setError(null);\n if (u && !videoUrlOk(u)) return setError(\"Paste a full link that starts with https://, or a path on this site.\");\n setError(null);\n set({ url: u }, \"Set video link\");\n };\n const ro = ctx.readOnly;\n return (\n <div className=\"group/blk rounded-kit-lg border border-kit-line bg-kit-subtle p-2.5\" data-rt-isolate=\"\">\n {ro ? null : (\n <div className=\"mb-2 flex items-center gap-1.5\">\n <Input\n size=\"sm\"\n autoFocus={autoFocus}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={commit}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n commit();\n }\n }}\n placeholder=\"Paste a YouTube, Vimeo or video file link\"\n aria-label=\"Video URL\"\n aria-invalid={!!error || undefined}\n aria-describedby={error ? errId : undefined}\n spellCheck={false}\n autoComplete=\"off\"\n leading={<IconWorld stroke={1.5} />}\n className=\"min-w-0 flex-1\"\n />\n <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove video\" />\n </div>\n )}\n {error ? (\n <p id={errId} role=\"alert\" className=\"mb-2 px-1 text-kit-xs text-kit-danger-fg\">\n {error}\n </p>\n ) : null}\n {/* D5: V2's click-to-play facade (VideoPreview: poster, provider badge, Play), no third-party frame until Play */}\n {m.url ? <VideoPreview url={m.url} title=\"Video\" /> : <p className=\"px-1 pb-0.5 text-kit-xs text-kit-fg-3\">No video yet.</p>}\n </div>\n );\n}\n\n/* ── File (V2 FileBlock): a downloadable document ─────────────────── */\nconst formatBytes = (n: number) => (n < 1024 ? `${n} B` : n < 1024 * 1024 ? `${(n / 1024).toFixed(0)} KB` : `${(n / 1024 / 1024).toFixed(1)} MB`);\nfunction FileBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"file\">) {\n const { MediaPicker } = useKitServices();\n const [picker, setPicker] = React.useState(false);\n const [url, setUrl] = React.useState(\"\");\n const [name, setName] = React.useState(\"\");\n const ro = ctx.readOnly;\n const has = !!(m.url && m.name);\n const pickerEl = MediaPicker ? (\n <MediaPicker open={picker} onOpenChange={setPicker} accept={[\"file\"]} onPick={(a) => a[0] && set({ url: a[0].url, name: a[0].name ?? a[0].url.split(\"/\").pop() ?? \"Download\", ...(a[0].size ? { size: a[0].size } : {}) }, \"Choose file\")} />\n ) : null;\n if (has)\n return (\n <div className=\"group/blk\" data-rt-isolate=\"\">\n <div className=\"flex items-center gap-3 rounded-kit-lg border border-kit-line bg-kit-surface px-3 py-2.5\">\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-kit-md bg-kit-muted text-kit-fg-2\">\n <IconFileText className=\"size-[18px]\" stroke={1.5} aria-hidden />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-kit-sm font-medium text-kit-fg\">{m.name}</div>\n <div className=\"truncate text-kit-xs text-kit-fg-3\">\n {m.size ? `${formatBytes(m.size)} · ` : \"\"}\n {(m.name!.includes(\".\") ? m.name!.split(\".\").pop()! : \"file\").toUpperCase()}\n </div>\n </div>\n {ro ? null : (\n <>\n {MediaPicker ? (\n <Button size=\"xs\" variant=\"ghost\" onClick={() => setPicker(true)}>\n Replace\n </Button>\n ) : null}\n <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove file\" />\n </>\n )}\n </div>\n {pickerEl}\n </div>\n );\n return (\n <div className=\"group/blk\" data-rt-isolate=\"\">\n <div className=\"flex items-center gap-3 rounded-kit-lg border border-dashed border-kit-line-strong bg-kit-subtle px-3 py-2.5\">\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-kit-md border border-kit-line bg-kit-control-bg text-kit-fg-3\">\n <IconFileText className=\"size-[18px]\" stroke={1.5} aria-hidden />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-kit-sm font-medium text-kit-fg\">File</div>\n <p className=\"text-kit-xs text-kit-fg-3\">No file yet.</p>\n </div>\n {ro ? null : (\n <>\n {MediaPicker ? (\n <Button size=\"sm\" leftIcon={<IconFileUpload stroke={1.5} />} onClick={() => setPicker(true)}>\n Choose file\n </Button>\n ) : null}\n <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove file\" />\n </>\n )}\n </div>\n {ro ? null : (\n <form\n className=\"mt-2.5 flex flex-wrap items-center gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n const u = url.trim();\n if (!/^https?:\\/\\/\\S+$/i.test(u) && !/^\\/(?!\\/)\\S*$/.test(u)) return ctx.notify(\"Paste a full file link that starts with https://\", \"Or a path on this site, like /files/spec.pdf.\");\n set({ url: u, name: name.trim() || u.split(\"/\").pop() || \"Download\" }, \"Add file\");\n ctx.exit(el);\n }}\n >\n <Input autoFocus={autoFocus && !MediaPicker} size=\"sm\" value={url} onChange={(e) => setUrl(e.target.value)} placeholder=\"https://…/guide.pdf\" aria-label=\"File URL\" spellCheck={false} autoComplete=\"off\" leading={<IconLink stroke={1.5} />} className=\"min-w-40 flex-1\" />\n <Input size=\"sm\" value={name} onChange={(e) => setName(e.target.value)} placeholder=\"Link text\" aria-label=\"File name shown to readers\" className=\"min-w-28 flex-1\" />\n <Button size=\"sm\" variant=\"primary\" type=\"submit\" disabled={!url.trim()}>\n Add\n </Button>\n </form>\n )}\n {pickerEl}\n </div>\n );\n}\n\n/* ── Embedded component (CPO D28): a linked instance, shown as its card ─────────── */\nfunction ComponentBlock({ m, el, ctx, set }: BlockProps<\"component\">) {\n const { useComponentInfo } = useKitServices();\n const overrides = React.useMemo<Record<string, unknown>>(() => {\n try {\n const v = m.overrides ? (JSON.parse(m.overrides) as unknown) : {};\n return v && typeof v === \"object\" && !Array.isArray(v) ? (v as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n }, [m.overrides]);\n // the host's component library answers → V2's instance card; none → the compact linked row (no state made up)\n if (useComponentInfo) return <ComponentCard m={m} el={el} ctx={ctx} use={useComponentInfo} overrides={overrides} set={set} />;\n const count = Object.keys(overrides).length;\n const name = m.name || \"Block\";\n return (\n <div\n role=\"group\"\n aria-label={`${name}, ${m.detached ? \"separate copy\" : \"linked block\"}`}\n className=\"group/blk flex items-center gap-3 rounded-kit-lg border border-kit-line bg-kit-surface px-3 py-2.5\"\n data-rt-isolate=\"\"\n >\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-kit-md bg-kit-muted text-kit-fg-2\">\n <IconComponents className=\"size-[18px]\" stroke={1.5} aria-hidden />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-kit-sm font-medium text-kit-fg\">{name}</div>\n <div className=\"truncate text-kit-xs text-kit-fg-3\">\n {m.detached ? (\n \"Separate copy · edits to the block don't reach it\"\n ) : (\n <>\n {m.ref ? \"Linked block\" : \"Block with no source\"}\n {count ? ` · ${count} ${count === 1 ? \"override\" : \"overrides\"}` : \"\"} · edits to the component show here too\n </>\n )}\n </div>\n </div>\n {ctx.readOnly ? null : (\n <div className=\"flex items-center gap-0.5 opacity-0 transition-opacity motion-reduce:transition-none group-hover/blk:opacity-100 group-focus-within/blk:opacity-100 pointer-coarse:opacity-100\">\n {ctx.duplicate ? (\n <IconButton size=\"xs\" label={`Duplicate ${name}`} onClick={() => ctx.duplicate?.(el)}>\n <IconCopy stroke={1.5} />\n </IconButton>\n ) : null}\n <RemoveBtn onClick={() => ctx.remove(el)} label={`Remove ${name}`} />\n </div>\n )}\n </div>\n );\n}\n\n/* ── Table (V2 TableBlock, CPO D42: the table value, edited with the shared table editor) ─────────── */\n/**\n * What production's rich-text table keeps: typed columns (V2's Text, Number, Price, Yes / No, Link and Rating with their unit,\n * currency, decimals, alignment, yes / no words, scale and description; a row's and a cell's type and a cell's value: a\n * first-row cell's `column`, a row's / cell's `type`, a cell's `value`), the header row and row headers, an optional caption,\n * the note under the table, row groups, the totals row and the row and column highlights (`caption`, `notes`,\n * `rowGroups`, a row's `footer` / `highlight`, a first-row cell's `highlight` / `sortable`, the table's `filterable`).\n * the table's `jsonLd` and `design` (On phones, Keep column names in view)). Footnotes are stored as the table's `footnotes` and a cell's `note` (V2's default, allowFootnotes on). No setting is\n * pinned: V2's island passes no options, so its editor offers GENERAL_TABLE (sort, search and the JSON-LD choice on,\n * everything else at its default), and so does this.\n */\nconst STORED_TABLE: TableFieldOptions = {\n // No `columnTypes`: V2's island passes no options and no `developer` (blocks.tsx:411-421), so its editor offers every type\n // TYPE_UI does not mark `dev` (table-editor.tsx:80-86: Text, Number, Price, Yes / No, Link, Rating), and so does this.\n // V2's default, as V2's island draws it: its editor never reads requireCaption (table-editor.tsx:1030-1033, no hint beside\n // Title) and its island runs no table validation (validate.ts:112 is the table FIELD's). Ours draws a \"Required\" hint for\n // the table field lane, whose backend refuses a captionless table (table_caption_required); a rich-text table is never\n // refused for it, so the island shows no hint, exactly as V2's.\n requireCaption: false,\n allowGroups: true,\n allowNote: true,\n settings: { sort: true, search: true },\n // \"Help search engines read this as\": V2's GENERAL_TABLE offers all three (schema/table.ts:170)\n jsonLd: [\"ItemList\", \"Dataset\", \"Offers\"],\n};\nfunction TableBlock({ m, set, el, ctx }: BlockProps<\"table\">) {\n return (\n <div className=\"group/blk\" data-rt-isolate=\"\">\n <TableEditor\n value={m}\n onChange={(next) => set(() => next, \"Edit table\")}\n readOnly={ctx.readOnly}\n options={STORED_TABLE}\n label=\"Table\"\n actions={ctx.readOnly ? null : <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove table\" />}\n />\n </div>\n );\n}\n\n/* ── Pull quote (V2 article-blocks.tsx ArticleBlock, surface \"panel\") ─────────── */\nconst PULL_QUOTE_LABEL = \"Pull quote\";\n/** V2 article.ts ARTICLE_SCHEMAS.pullquote: the properties panel is built from it. */\nconst PULL_QUOTE_SCHEMA: TypeSchema = {\n name: \"pullQuote\",\n title: PULL_QUOTE_LABEL,\n kind: \"object\",\n icon: \"quote\",\n fields: [\n { name: \"quote\", title: \"Quote\", type: \"text\", rows: 3, validation: { required: true, recommendedMax: 200 } },\n { name: \"name\", title: \"Name\", type: \"string\", validation: { max: 60 } },\n { name: \"role\", title: \"Role or source\", type: \"string\", validation: { max: 80 } },\n { name: \"link\", title: \"Source link\", type: \"url\", description: \"The name links here.\" },\n ],\n};\n\nfunction PullQuoteBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"pullquote\">) {\n const label = PULL_QUOTE_LABEL;\n const card = React.useRef<HTMLDivElement | null>(null);\n const [cardEl, setCardEl] = React.useState<HTMLDivElement | null>(null);\n const ro = !!ctx.readOnly;\n const { open, editing, openPanel, close } = useArticleOpen({ ro, autoFocus });\n // a stable ref: a new callback each render would pass null for a moment and remount the panel\n const cardRef = React.useCallback((n: HTMLDivElement | null) => {\n card.current = n;\n if (n) setCardEl(n);\n }, []);\n const modelRef = React.useRef(m);\n modelRef.current = m;\n const focusCard = React.useCallback(() => requestAnimationFrame(() => card.current?.focus({ preventScroll: true })), []);\n\n /** a form edit joins the body's one undo history (D54); typing coalesces per field */\n const commit = (next: PullQuoteModel) => {\n const prev = modelRef.current;\n const field = (Object.keys(next) as (keyof PullQuoteModel)[]).find((k) => next[k] !== prev[k]) ?? \"\";\n set(() => next, `Edit ${label.toLowerCase()} ${field}`);\n };\n\n return (\n <>\n <ArticleCard\n ref={cardRef}\n label={label}\n kind=\"pullquote\"\n ro={ro}\n editing={editing}\n onOpen={openPanel}\n onKeyDown={articleCardKeyDown({ card, ctx, el, ro, editing, onOpen: openPanel, onClose: close, focusCard })}\n >\n <PullQuotePreview m={m} />\n <ArticleActions label={label} ctx={ctx} el={el} editing={editing} onEdit={() => (editing ? close() : openPanel())} />\n </ArticleCard>\n {editing ? (\n <ArticlePanel label={label} anchor={cardEl} nonce={open!.nonce} focusKey={open!.focusKey} onClose={close} focusBack={focusCard} ctx={ctx} el={el}>\n <ArticleForm schema={PULL_QUOTE_SCHEMA} label={label} value={m as unknown as Record<string, unknown>} onChange={(v) => commit(v as unknown as PullQuoteModel)} ro={ro} />\n </ArticlePanel>\n ) : null}\n </>\n );\n}\n\n/* ── Key takeaways (V2 article-blocks.tsx ArticleBlock, surface \"inline\" + TakeawaysEditor) ─────────── */\nconst TAKEAWAYS_LABEL = \"Key takeaways\";\n/** V2 article.ts ARTICLE_SCHEMAS.takeaways `points` max (the stored block's limit, key_takeaways_limit_exceeded) */\nconst MAX_POINTS = 8;\n/** V2 article.ts ARTICLE_SCHEMAS.takeaways `title` max */\nconst MAX_TITLE = 40;\n\nfunction TakeawaysBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"takeaways\">) {\n const label = TAKEAWAYS_LABEL;\n const card = React.useRef<HTMLDivElement>(null);\n const box = React.useRef<HTMLUListElement>(null);\n const ro = !!ctx.readOnly;\n const focusCard = () => requestAnimationFrame(() => card.current?.focus({ preventScroll: true }));\n const focusPoint = (i: number, atEnd = true) =>\n requestAnimationFrame(() => {\n const t = box.current?.querySelectorAll<HTMLElement>(\"[role=textbox]\")[i];\n if (!t) return;\n t.focus();\n placeCaret(t, atEnd);\n });\n const setPoint = (i: number, v: string) => set({ points: m.points.map((p, j) => (j === i ? v : p)) }, `Edit key takeaways point ${i + 1}`);\n\n /** V2 ArticleBlock onKeyDown, on the block itself (its lines handle their own keys) */\n const onKeyDown = articleCardKeyDown({ card, ctx, el, ro, focusCard });\n\n return (\n <ArticleCard ref={card} label={label} kind=\"takeaways\" ro={ro} onKeyDown={onKeyDown}>\n <aside className=\"rounded-kit-lg border border-kit-line bg-kit-subtle px-4 py-3\" aria-label={m.title || label}>\n <PlainEditable\n label=\"Key takeaways title\"\n value={m.title}\n placeholder=\"Key takeaways\"\n readOnly={ro}\n onChange={(title) => {\n // V2 ARTICLE_SCHEMAS.takeaways title max: said the moment it is crossed, not first at save (key_takeaways_limit_exceeded)\n if (title.length > MAX_TITLE && m.title.length <= MAX_TITLE)\n ctx.notify(`A key takeaways title holds at most ${MAX_TITLE} characters.`, \"Shorten it, or the change will not save.\");\n set({ title }, \"Edit key takeaways title\");\n }}\n onEnter={() => focusPoint(0, false)}\n className=\"text-kit-sm font-semibold text-kit-fg\"\n />\n <ul ref={box} className=\"m-0 mt-1.5 flex flex-col gap-1 p-0\">\n {m.points.map((p, i) => (\n <li key={i} className=\"flex list-none items-baseline gap-2 text-[15px] leading-relaxed text-kit-fg\">\n <span aria-hidden className=\"mt-[0.55em] size-1.5 shrink-0 self-start rounded-full bg-kit-accent\" />\n <PlainEditable\n label={`Point ${i + 1}`}\n value={p}\n placeholder={i === 0 ? \"The first thing to remember\" : \"Another point\"}\n readOnly={ro}\n autoFocus={autoFocus && i === 0}\n onChange={(v) => setPoint(i, v)}\n onEnter={() => {\n if (m.points.length >= MAX_POINTS) {\n ctx.notify(`Key takeaways hold at most ${MAX_POINTS} points.`, \"Merge two points, or move this one into the text.\");\n return;\n }\n set((x) => ({ ...x, points: [...x.points.slice(0, i + 1), \"\", ...x.points.slice(i + 1)] }), \"Add a key takeaways point\");\n focusPoint(i + 1);\n }}\n onBackspaceEmpty={\n m.points.length > 1\n ? () => {\n set((x) => ({ ...x, points: x.points.filter((_, j) => j !== i) }), \"Remove a key takeaways point\");\n focusPoint(Math.max(0, i - 1));\n }\n : undefined\n }\n className=\"flex-1\"\n />\n </li>\n ))}\n </ul>\n </aside>\n <ArticleActions label={label} ctx={ctx} el={el} />\n </ArticleCard>\n );\n}\n\n/* ── Preview (what the reader sees; V2 PullQuotePreview) ───────────── */\nfunction PullQuotePreview({ m }: { m: PullQuoteModel }) {\n return (\n <figure className=\"m-0 py-2 pl-5 pr-10\">\n <IconQuote className=\"mb-1 size-6 text-kit-accent-fg\" stroke={1.5} aria-hidden />\n <blockquote data-field=\"quote\" className={cn(\"m-0 text-[22px] font-medium leading-snug tracking-[-0.01em]\", m.quote ? \"text-kit-fg\" : \"text-kit-fg-4\")}>\n {m.quote || \"Write the quote\"}\n </blockquote>\n {m.name || m.role ? (\n <figcaption className=\"mt-3 text-kit-sm text-kit-fg-2\">\n {m.name ? (\n <span data-field=\"name\" className={cn(\"font-medium text-kit-fg\", m.link && \"underline decoration-kit-line-strong underline-offset-2\")}>\n {m.name}\n </span>\n ) : null}\n {m.name && m.role ? \", \" : null}\n {m.role ? <span data-field=\"role\">{m.role}</span> : null}\n </figcaption>\n ) : (\n <figcaption data-field=\"name\" className=\"mt-3 text-kit-sm text-kit-fg-4\">Who said it</figcaption>\n )}\n </figure>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"blocks.js","names":[],"sources":["../../../src/controls/rich-text/blocks.tsx"],"sourcesContent":["/**\n * RICH TEXT · rich blocks (\"islands\", V2 HEAD controls/rich-text/blocks.tsx). Each renders through a portal into a\n * contenteditable=false element inside the editor surface; its model lives on that element (`el.__rt`) so serialize()\n * can write it back as static HTML.\n *\n * The image block is V2's: the toolbar (Replace · Align · Size S/M/L/Full · Link · Alt text · Remove) and the caption\n * under it, each stored on production's image block (see dom.ts). The callout (tone · title · message), toggle, video,\n * file and embedded component blocks are V2's too, limited to what their stored blocks keep: a callout's message and a\n * toggle's body are plain text (production stores them as text), a component is a linked card (its fields are edited\n * on the component itself).\n *\n * The table block is V2's TableBlock: the kit's table editor on the island's table value. The pull quote is V2's article\n * block (article-blocks.tsx ArticleBlock + PullQuotePreview): drawn as the reader sees it, edited in the floating\n * properties panel (4 settings → panel, CPO D54) through the kit SchemaForm. Key takeaways are V2's article block too,\n * written in the text itself (article-blocks.tsx TakeawaysEditor, surface \"inline\"): a title line and a line per point.\n * The embed island draws embed.tsx's block (V2's article Embed: Link, Title, Shape), keeping the keys it lacks absent.\n */\nimport * as React from \"react\";\nimport {\n IconAlertTriangle, IconAlt, IconArrowsHorizontal,\n IconBulb, IconCheck, IconChevronDown, IconChevronRight, IconComponents, IconCopy, IconFileText, IconFileUpload,\n IconInfoCircle, IconLayoutAlignCenter, IconLayoutAlignLeft, IconLayoutAlignRight, IconLibraryPhoto,\n IconLink, IconPhoto, IconQuote, IconReplace, IconTrash, IconWorld,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../../lib/cn\";\nimport { useKitServices } from \"../../context\";\nimport { isCropRect } from \"../../adapters/values\";\nimport { Button, IconButton } from \"../../ui/button\";\nimport { Input, Textarea } from \"../../ui/input\";\nimport { Menu, MenuContent, MenuItem, MenuTrigger, Popover, PopoverContent, PopoverTrigger, Tooltip } from \"../../ui/overlay\";\nimport type { TypeSchema } from \"../../schema/types\";\nimport type { TableFieldOptions } from \"../../schema/table\";\nimport { TableEditor } from \"../table/table-editor\";\nimport { VideoPreview } from \"../media\";\nimport {\n CALLOUT_TONES, placeCaret, type CalloutModel, type CalloutTone, type ComponentModel, type EmbedModel, type FileModel, type GalleryIslandModel, type TocIslandModel, type ColumnsIslandModel, type AuthorIslandModel, type RelatedIslandModel, type ImageAlign, type ImageModel, type ImageSize,\n type IslandEl, type IslandKind, type IslandModels, type PullQuoteModel, type TableModel, type TakeawaysModel, type ToggleModel, type VideoModel,\n} from \"./dom\";\nimport { LinkForm } from \"./link-form\";\nimport { EmbedBlock } from \"./embed\";\nimport { GalleryBlock, type GalleryModel } from \"./gallery\";\nimport { TocBlock, type OutlineStore } from \"./toc\";\nimport { ColumnsBlock } from \"./columns\";\nimport { AuthorBlock } from \"./author\";\nimport { RelatedBlock, type RelatedModel } from \"./related\";\nimport type { RichTextRead } from \"./codec\";\nimport type { RichTextFeature } from \"./codec\";\nimport { ArticleActions, ArticleCard, ArticleForm, ArticlePanel, articleCardKeyDown, useArticleOpen } from \"./article-shell\";\nimport { ComponentCard } from \"./component-card\";\n\nexport interface IslandCtx {\n readOnly?: boolean;\n dense?: boolean;\n emit: () => void;\n remove: (el: IslandEl) => void;\n /** Leave the block: caret to the start of the next line. */\n exit: (el: IslandEl) => void;\n /** CPO D54: an island write joins the body field's ONE undo history (`before` = the model it replaces) */\n record?: (el: IslandEl, before: IslandModels[IslandKind], label: string, coalesce?: boolean) => void;\n /** a refusal the author must see (an address the image cannot use) */\n notify: (message: string, description?: string) => void;\n duplicate?: (el: IslandEl) => void;\n move?: (el: IslandEl, dir: -1 | 1) => void;\n /** the body field's one history (D54): ⌘Z on a focused block or in its panel */\n undo?: () => boolean;\n /**\n * One action as ONE undo step, in the host's history when it keeps one (the dock's toolbar Undo), else the body's\n * own. `write` makes the change and answers the island it changed; `before` is that island's model before it. Answers\n * the step's undo (an Undo toast): true, or the sentence saying why nothing was undone.\n */\n step?: (label: string, write: () => IslandEl | null, before?: IslandModels[IslandKind]) => () => true | string;\n redo?: () => boolean;\n /** Drag the block to a new position (the gutter grip's drag), for a grip drawn on the block itself */\n drag?: (el: IslandEl, e: React.PointerEvent) => void;\n /** what the field's stored shape keeps (the host codec's features): a setting it cannot store is not drawn */\n allows?: (f: RichTextFeature) => boolean;\n /** the body's live outline (its top-level H2/H3), which a table of contents lists */\n outline?: OutlineStore;\n /** where an author bio and related posts point (the field's config, from the host codec) */\n references?: RichTextRead[\"references\"];\n}\n\n/* ── Island host ───────────────────────────────────────────────────── */\n/** `label` names the step in the editor's one undo history (\"Edit alt text\"). */\nexport type Setter<K extends IslandKind> = (patch: Partial<IslandModels[K]> | ((m: IslandModels[K]) => IslandModels[K]), label?: string, record?: boolean) => void;\nexport interface BlockProps<K extends IslandKind> {\n m: IslandModels[K];\n set: Setter<K>;\n el: IslandEl;\n ctx: IslandCtx;\n autoFocus: boolean;\n}\n\nexport function Island({ el, ctx }: { el: IslandEl; ctx: IslandCtx }) {\n const kind = el.getAttribute(\"data-rt-island\") as IslandKind;\n const [model, setModel] = React.useState(() => el.__rt!);\n const [autoFocus] = React.useState(() => !!el.__rtFocus);\n React.useEffect(() => {\n el.__rtFocus = false;\n }, [el]);\n const set = React.useCallback(\n (patch: unknown, label = \"Edit block\", record = true) => {\n const prev = el.__rt as IslandModels[IslandKind];\n const next = typeof patch === \"function\" ? (patch as (m: object) => object)(prev) : { ...prev, ...(patch as object) };\n // `record` false: the caller records the step itself (IslandCtx.step)\n if (record) ctx.record?.(el, prev, label, typeof patch !== \"function\");\n el.__rt = next as IslandModels[IslandKind];\n setModel(next as IslandModels[IslandKind]);\n ctx.emit();\n },\n [el, ctx],\n );\n React.useEffect(() => {\n el.__rtSet = (m) => {\n el.__rt = m;\n setModel(m);\n ctx.emit();\n };\n return () => {\n if (el.__rtSet) delete el.__rtSet;\n };\n }, [el, ctx]);\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (!e.currentTarget.contains(e.target as Node)) return;\n if (e.key === \"Escape\" && (e.target as HTMLElement).isContentEditable) {\n e.preventDefault();\n e.stopPropagation();\n ctx.exit(el);\n }\n };\n return (\n <div className=\"relative select-text\" onKeyDown={onKeyDown} data-island-kind={kind}>\n {kind === \"image\" ? <ImageBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as ImageModel} set={set as Setter<\"image\">} />\n : kind === \"table\" ? <TableBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as TableModel} set={set as Setter<\"table\">} />\n : kind === \"pullquote\" ? <PullQuoteBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as PullQuoteModel} set={set as Setter<\"pullquote\">} />\n : kind === \"takeaways\" ? <TakeawaysBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as TakeawaysModel} set={set as Setter<\"takeaways\">} />\n : kind === \"callout\" ? <CalloutBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as CalloutModel} set={set as Setter<\"callout\">} />\n : kind === \"toggle\" ? <ToggleBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as ToggleModel} set={set as Setter<\"toggle\">} />\n : kind === \"video\" ? <VideoBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as VideoModel} set={set as Setter<\"video\">} />\n : kind === \"embed\" ? <EmbedIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as EmbedModel} set={set as Setter<\"embed\">} />\n : kind === \"gallery\" ? <GalleryIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as GalleryIslandModel} set={set as Setter<\"gallery\">} />\n : kind === \"toc\" ? <TocIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as TocIslandModel} set={set as Setter<\"toc\">} />\n : kind === \"columns\" ? <ColumnsIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as ColumnsIslandModel} set={set as Setter<\"columns\">} />\n : kind === \"author\" ? <AuthorIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as AuthorIslandModel} set={set as Setter<\"author\">} />\n : kind === \"related\" ? <RelatedIsland el={el} ctx={ctx} autoFocus={autoFocus} m={model as RelatedIslandModel} set={set as Setter<\"related\">} />\n : kind === \"file\" ? <FileBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as FileModel} set={set as Setter<\"file\">} />\n : kind === \"component\" ? <ComponentBlock el={el} ctx={ctx} autoFocus={autoFocus} m={model as ComponentModel} set={set as Setter<\"component\">} />\n : null}\n </div>\n );\n}\n\n/** The island's stored embed (keys absent when the block lacks them) through embed.tsx's block (V2's model). */\nfunction EmbedIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"embed\">) {\n return (\n <EmbedBlock\n m={{ url: m.url ?? \"\", title: m.title ?? \"\", aspect: m.aspect ?? \"16:9\" }}\n set={(fn, label) =>\n set((prev) => {\n const next = fn({ url: prev.url ?? \"\", title: prev.title ?? \"\", aspect: prev.aspect ?? \"16:9\" });\n // a key the block never had stays absent until it is given one (an untouched block reads back as stored)\n const out: EmbedModel = { ...prev };\n if (next.url !== (prev.url ?? \"\")) out.url = next.url;\n if (next.title !== (prev.title ?? \"\")) out.title = next.title;\n if (next.aspect !== (prev.aspect ?? \"16:9\")) out.aspect = next.aspect;\n return out;\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n shape={ctx.allows?.(\"embedAspect\") ?? false}\n />\n );\n}\n\n/** The island's stored gallery (src, alt, caption per image) through gallery.tsx's block (V2's model, an image value each). */\n/** RT9: a gallery image's framing, image-url's 0–1 focal point ⇄ the kit image value's percent */\nconst toGallery = (m: GalleryIslandModel): GalleryModel => ({\n layout: m.layout ?? \"grid\",\n images: m.images.map((x) => ({\n _key: x._key,\n image: x.src\n ? { url: x.src, ...(x.alt !== undefined ? { alt: x.alt } : {}), ...(x.crop ? { crop: x.crop } : {}), ...(x.focal ? { focal: { x: x.focal.x * 100, y: x.focal.y * 100 } } : {}) }\n : null,\n caption: x.caption ?? \"\",\n })),\n});\nconst cropOf = (c: unknown) => (isCropRect(c) ? { top: c.top, right: c.right, bottom: c.bottom, left: c.left } : undefined);\nconst focalOf = (f: unknown) => {\n const p = f as { x?: unknown; y?: unknown } | undefined;\n return typeof p?.x === \"number\" && typeof p?.y === \"number\" ? { x: Math.round(p.x) / 100, y: Math.round(p.y) / 100 } : undefined;\n};\nfunction GalleryIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"gallery\">) {\n return (\n <GalleryBlock\n m={toGallery(m)}\n set={(fn, label) =>\n set((prev) => {\n const next = fn(toGallery(prev));\n const out: GalleryIslandModel = {\n ...prev,\n images: next.images.map((x) => {\n const crop = cropOf(x.image?.crop);\n const focal = focalOf(x.image?.focal);\n return { _key: x._key, src: x.image?.url ?? \"\", ...(x.image?.alt !== undefined ? { alt: x.image.alt } : {}), ...(x.caption ? { caption: x.caption } : {}), ...(crop ? { crop } : {}), ...(focal ? { focal } : {}) };\n }),\n };\n if (next.layout !== (prev.layout ?? \"grid\")) out.layout = next.layout;\n return out;\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n />\n );\n}\n\n/** The island's stored table of contents (title, depth) through toc.tsx's block, which lists the editor's live outline. */\nfunction TocIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"toc\">) {\n return (\n <TocBlock\n m={{ title: m.title ?? \"\", depth: m.depth ?? \"3\" }}\n set={(fn, label) =>\n set((prev) => {\n const next = fn({ title: prev.title ?? \"\", depth: prev.depth ?? \"3\" });\n const out: TocIslandModel = { ...prev };\n if (next.title !== (prev.title ?? \"\")) out.title = next.title;\n if (next.depth !== (prev.depth ?? \"3\")) out.depth = next.depth;\n return out;\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n />\n );\n}\n\n/** The island's stored columns (gap, each column's HTML) through columns.tsx's block. */\nfunction ColumnsIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"columns\">) {\n return (\n <ColumnsBlock\n m={{ gap: m.gap ?? \"m\", cols: m.cols }}\n set={(fn, label) =>\n set((prev) => {\n const next = fn({ gap: prev.gap ?? \"m\", cols: prev.cols });\n const out: ColumnsIslandModel = { ...prev, cols: next.cols };\n if (next.gap !== (prev.gap ?? \"m\")) out.gap = next.gap;\n return out;\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n />\n );\n}\n\n/** The island's stored author reference through author.tsx's block, picking from the field's authors collection. */\nfunction AuthorIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"author\">) {\n return (\n <AuthorBlock\n m={{ author: m.author ?? \"\" }}\n set={(fn, label) =>\n set((prev) => {\n const next = fn({ author: prev.author ?? \"\" });\n return next.author === (prev.author ?? \"\") ? prev : { ...prev, author: next.author };\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n collection={ctx.references?.authors ?? \"\"}\n />\n );\n}\n\n/** The island's stored related posts through related.tsx's block, over the field's posts collection. */\nconst toRelated = (m: RelatedIslandModel): RelatedModel => ({ title: m.title ?? \"\", mode: m.mode ?? \"topic\", count: m.count ?? 3, posts: m.posts });\nfunction RelatedIsland({ m, set, el, ctx, autoFocus }: BlockProps<\"related\">) {\n return (\n <RelatedBlock\n m={toRelated(m)}\n set={(fn, label) =>\n set((prev) => {\n const was = toRelated(prev);\n const next = fn(was);\n const out: RelatedIslandModel = { ...prev, posts: next.posts };\n if (next.title !== was.title) out.title = next.title;\n if (next.mode !== was.mode) out.mode = next.mode;\n if (next.count !== was.count) out.count = next.count;\n return out;\n }, label)\n }\n el={el}\n ctx={ctx}\n autoFocus={autoFocus}\n collection={ctx.references?.posts ?? \"\"}\n excludeId={ctx.references?.entryId}\n topic={ctx.references?.topic !== false}\n noun={ctx.references?.postsNoun}\n />\n );\n}\n\nconst Sep = () => <span aria-hidden className=\"mx-0.5 h-4 w-px shrink-0 bg-kit-line\" />;\n/** An address an image can be shown from: http(s) or a site path. */\nconst imageUrlOk = (u: string) => /^(https?:)?\\/\\/[^\\s]+$/i.test(u) || /^\\/(?!\\/)\\S*$/.test(u);\n\n/* ── plain text inside a block (V2 Editable, `plain`): a caption, a title, a message ── */\nfunction PlainEditable({\n value,\n onChange,\n label,\n placeholder,\n readOnly,\n autoFocus,\n multiline,\n onEnter,\n onBackspaceEmpty,\n className,\n}: {\n value: string;\n onChange: (v: string) => void;\n label: string;\n placeholder?: string;\n readOnly?: boolean;\n autoFocus?: boolean;\n /** Enter adds a line (else Enter calls `onEnter`) */\n multiline?: boolean;\n onEnter?: () => void;\n /** Backspace in an empty line (V2 PlainLine: removes the line) */\n onBackspaceEmpty?: () => void;\n className?: string;\n}) {\n const ref = React.useRef<HTMLDivElement>(null);\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el || document.activeElement === el) return;\n if (el.textContent !== value) el.textContent = value;\n }, [value]);\n React.useEffect(() => {\n if (!autoFocus || !ref.current || readOnly) return;\n ref.current.focus({ preventScroll: false });\n placeCaret(ref.current);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n return (\n <div\n ref={ref}\n role=\"textbox\"\n aria-label={label}\n aria-multiline={multiline || undefined}\n aria-readonly={readOnly || undefined}\n tabIndex={readOnly ? -1 : 0}\n contentEditable={readOnly ? false : \"plaintext-only\"}\n suppressContentEditableWarning\n spellCheck\n data-placeholder={placeholder}\n onInput={(e) => onChange((e.currentTarget.textContent ?? \"\").replace(/\\u200b/g, \"\"))}\n onPaste={(e) => {\n e.preventDefault();\n const t = e.clipboardData.getData(\"text/plain\");\n document.execCommand(\"insertText\", false, multiline ? t : t.replace(/\\s*\\n\\s*/g, \" \"));\n }}\n onKeyDown={(e) => {\n if (e.key === \"Backspace\" && onBackspaceEmpty && !(e.currentTarget.textContent ?? \"\").replace(/\\u200b/g, \"\")) {\n e.preventDefault();\n onBackspaceEmpty();\n return;\n }\n if (e.key !== \"Enter\") return;\n // in a multi-line field Enter adds a line; ⌘↵ leaves it\n if (multiline && !(e.metaKey || e.ctrlKey)) return;\n e.preventDefault();\n onEnter?.();\n }}\n className={cn(\n \"min-w-0 cursor-text whitespace-pre-wrap break-words outline-none focus-visible:shadow-none empty:before:pointer-events-none empty:before:text-kit-fg-4 empty:before:content-[attr(data-placeholder)]\",\n className,\n )}\n />\n );\n}\n\nconst RemoveBtn = ({ onClick, label = \"Remove block\" }: { onClick: () => void; label?: string }) => (\n <IconButton size=\"xs\" label={label} onClick={onClick} className=\"hover:text-kit-danger-fg\">\n <IconTrash stroke={1.5} />\n </IconButton>\n);\n\n/* ── Image ─────────────────────────────────────────────────────────── */\n/** Production's image layout (lib/richtext/imageLayout.ts): a size sets the column share; with none, the alignment does. */\nconst SIZES: { v: ImageSize; label: string; w: string }[] = [\n { v: \"s\", label: \"S\", w: \"33%\" },\n { v: \"m\", label: \"M\", w: \"50%\" },\n { v: \"l\", label: \"L\", w: \"75%\" },\n { v: \"full\", label: \"Full\", w: \"100%\" },\n];\nconst ALIGN_WIDTH: Record<ImageAlign, string> = { full: \"100%\", center: \"78%\", left: \"62%\", right: \"62%\" };\nconst ALIGNS: { v: ImageAlign; label: string; icon: React.ReactNode }[] = [\n { v: \"full\", label: \"Full width\", icon: <IconArrowsHorizontal stroke={1.5} /> },\n { v: \"center\", label: \"Center\", icon: <IconLayoutAlignCenter stroke={1.5} /> },\n { v: \"left\", label: \"Left\", icon: <IconLayoutAlignLeft stroke={1.5} /> },\n { v: \"right\", label: \"Right\", icon: <IconLayoutAlignRight stroke={1.5} /> },\n];\n\nfunction ImageBlock({ m, set, el, ctx }: BlockProps<\"image\">) {\n const { MediaPicker } = useKitServices();\n const [picker, setPicker] = React.useState(false);\n const [urlMode, setUrlMode] = React.useState(false);\n const [url, setUrl] = React.useState(\"\");\n const [busy, setBusy] = React.useState(false);\n const [linkOpen, setLinkOpen] = React.useState(false);\n const ro = ctx.readOnly;\n const pickerEl = MediaPicker ? (\n <MediaPicker\n open={picker}\n onOpenChange={setPicker}\n accept={[\"image\", \"svg\"]}\n onPick={(a) => a[0] && set({ src: a[0].url, alt: a[0].alt || m.alt || \"\" }, \"Replace image\")}\n />\n ) : null;\n\n if (!m.src)\n return (\n <div className=\"group/blk rounded-kit-lg border border-dashed border-kit-line-strong bg-kit-subtle px-4 py-4\">\n <div className=\"flex items-start gap-3\">\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-kit-md border border-kit-line bg-kit-control-bg text-kit-fg-2\">\n <IconPhoto className=\"size-4\" stroke={1.5} aria-hidden />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-kit-sm font-medium text-kit-fg\">Add an image</div>\n <p className=\"text-kit-xs text-kit-fg-3\">{MediaPicker ? \"Pick one from the media library or paste a link.\" : \"Paste a link to the image.\"}</p>\n </div>\n {ro ? null : <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove image\" />}\n </div>\n {ro ? null : urlMode || !MediaPicker ? (\n <form\n className=\"mt-3 flex items-center gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n const u = url.trim();\n if (!imageUrlOk(u)) return ctx.notify(\"Paste a full image link that starts with https://\", \"Or a path on this site, like /images/hero.jpg.\");\n set({ src: u, alt: m.alt ?? \"\" }, \"Add image\");\n ctx.exit(el);\n }}\n >\n <Input autoFocus size=\"sm\" value={url} onChange={(e) => setUrl(e.target.value)} placeholder=\"https://…\" aria-label=\"Image URL\" leading={<IconLink stroke={1.5} />} className=\"flex-1\" />\n <Button size=\"sm\" variant=\"primary\" type=\"submit\" disabled={!url.trim()}>\n Add\n </Button>\n {MediaPicker ? (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setUrlMode(false)}>\n Cancel\n </Button>\n ) : null}\n </form>\n ) : (\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <Button size=\"sm\" leftIcon={<IconLibraryPhoto stroke={1.5} />} onClick={() => setPicker(true)}>\n Media library\n </Button>\n <Button size=\"sm\" variant=\"ghost\" leftIcon={<IconLink stroke={1.5} />} onClick={() => setUrlMode(true)}>\n Paste URL\n </Button>\n </div>\n )}\n {pickerEl}\n </div>\n );\n\n const align: ImageAlign = m.align ?? \"full\";\n const size = m.size;\n const width = size ? (SIZES.find((x) => x.v === size)?.w ?? \"100%\") : ALIGN_WIDTH[align];\n const justify = align === \"left\" ? \"justify-start\" : align === \"right\" ? \"justify-end\" : \"justify-center\";\n const alignItem = ALIGNS.find((a) => a.v === align)!;\n const toolBtn =\n \"flex h-7 shrink-0 items-center gap-1 rounded-kit-md px-1.5 text-kit-xs text-kit-fg-2 hover:bg-kit-hover hover:text-kit-fg data-[popup-open]:bg-kit-hover data-[popup-open]:text-kit-fg [&_svg]:size-3.5\";\n return (\n <figure className=\"group/blk relative m-0\">\n {ro ? null : (\n <div\n role=\"toolbar\"\n aria-label=\"Image tools\"\n className={cn(\n \"glass-strong absolute left-1/2 top-2 z-[3] flex h-8 -translate-x-1/2 items-center gap-0.5 rounded-kit-lg border border-kit-line px-1 opacity-0 transition-opacity duration-150\",\n \"group-hover/blk:opacity-100 group-focus-within/blk:opacity-100 pointer-coarse:opacity-100\",\n busy && \"opacity-100\",\n )}\n >\n {MediaPicker ? (\n <>\n <Tooltip content=\"Replace image\">\n <button type=\"button\" aria-label=\"Replace image\" className={toolBtn} onClick={() => setPicker(true)}>\n <IconReplace stroke={1.5} aria-hidden />\n {ctx.dense ? null : \"Replace\"}\n </button>\n </Tooltip>\n <Sep />\n </>\n ) : null}\n <Menu onOpenChange={setBusy}>\n <MenuTrigger\n render={\n <button type=\"button\" aria-label={`Align: ${alignItem.label}`} className={toolBtn}>\n {alignItem.icon}\n <IconChevronDown className=\"!size-3 text-kit-fg-3\" stroke={1.5} aria-hidden />\n </button>\n }\n />\n <MenuContent className=\"w-40\">\n {ALIGNS.map((a) => (\n <MenuItem\n key={a.v}\n icon={a.icon}\n onClick={() => set({ align: a.v, ...(a.v === \"full\" ? { size: \"full\" as const } : size === \"full\" || !size ? { size: \"l\" as const } : {}) }, \"Align image\")}\n >\n <span className=\"flex w-full items-center justify-between\">\n {a.label}\n {align === a.v ? <IconCheck className=\"size-3.5 text-kit-fg-2\" stroke={2} aria-hidden /> : null}\n </span>\n </MenuItem>\n ))}\n </MenuContent>\n </Menu>\n <div role=\"group\" aria-label=\"Size\" className=\"flex items-center\">\n {SIZES.map((x) => {\n const on = (size ?? (align === \"full\" ? \"full\" : null)) === x.v;\n return (\n <button\n key={x.v}\n type=\"button\"\n aria-pressed={on}\n aria-label={`Size ${x.label}`}\n onClick={() => set({ size: x.v, ...(x.v === \"full\" ? { align: \"full\" as const } : align === \"full\" ? { align: \"center\" as const } : {}) }, \"Resize image\")}\n className={cn(\"h-7 min-w-6 rounded-kit-md px-1 text-kit-xs text-kit-fg-3 hover:bg-kit-hover hover:text-kit-fg\", on && \"bg-kit-selected font-medium text-kit-fg\")}\n >\n {x.label}\n </button>\n );\n })}\n </div>\n <Sep />\n <Popover\n open={linkOpen}\n onOpenChange={(o) => {\n setLinkOpen(o);\n setBusy(o);\n }}\n >\n <Tooltip content={m.href ? `Links to ${m.href}` : \"Link the image\"}>\n <PopoverTrigger aria-label=\"Link the image\" className={cn(toolBtn, m.href && \"text-kit-accent-fg\")}>\n <IconLink stroke={1.5} aria-hidden />\n </PopoverTrigger>\n </Tooltip>\n <PopoverContent className=\"w-[340px] rounded-kit-lg p-3\" finalFocus={false}>\n <p className=\"mb-2 text-kit-xs font-medium text-kit-fg-2\">Link the image to</p>\n <LinkForm\n initial={{ href: m.href ?? \"\" }}\n allowNewTab={false}\n onSave={(v) => {\n set({ href: v.href }, \"Link image\");\n setLinkOpen(false);\n setBusy(false);\n }}\n onCancel={() => {\n setLinkOpen(false);\n setBusy(false);\n }}\n onDetach={\n m.href\n ? () => {\n set((x) => {\n const { href: _gone, ...rest } = x;\n void _gone;\n return rest;\n }, \"Unlink image\");\n setLinkOpen(false);\n setBusy(false);\n }\n : undefined\n }\n />\n </PopoverContent>\n </Popover>\n <AltPopover alt={m.alt ?? \"\"} onSave={(alt) => set({ alt }, \"Edit alt text\")} onOpen={setBusy} btnClass={toolBtn} />\n <Sep />\n <Tooltip content=\"Remove image\">\n <button type=\"button\" aria-label=\"Remove image\" className={cn(toolBtn, \"hover:text-kit-danger-fg\")} onClick={() => ctx.remove(el)}>\n <IconTrash stroke={1.5} aria-hidden />\n </button>\n </Tooltip>\n </div>\n )}\n <div className={cn(\"flex\", justify)}>\n <img src={m.src} alt={m.alt ?? \"\"} style={{ width }} className=\"block h-auto max-w-full rounded-kit-lg border border-kit-line\" draggable={false} />\n </div>\n {ro && !m.caption ? null : (\n <PlainEditable\n value={m.caption ?? \"\"}\n label=\"Image caption\"\n readOnly={ro}\n placeholder={ro ? \"\" : \"Add a caption…\"}\n onChange={(caption) => set({ caption }, \"Edit caption\")}\n onEnter={() => ctx.exit(el)}\n className=\"mt-1.5 text-center text-kit-sm text-kit-fg-3 empty:before:text-kit-fg-4\"\n />\n )}\n {pickerEl}\n </figure>\n );\n}\n\nfunction AltPopover({ alt, onSave, onOpen, btnClass }: { alt: string; onSave: (a: string) => void; onOpen: (o: boolean) => void; btnClass: string }) {\n const [open, setOpen] = React.useState(false);\n const [v, setV] = React.useState(alt);\n const id = React.useId();\n return (\n <Popover\n open={open}\n onOpenChange={(o) => {\n setOpen(o);\n onOpen(o);\n if (o) setV(alt);\n }}\n >\n <Tooltip content={alt ? \"Alt text\" : \"Add alt text\"}>\n <PopoverTrigger aria-label=\"Alt text\" className={cn(btnClass, !alt && \"text-kit-warning-fg\")}>\n <IconAlt stroke={1.5} aria-hidden />\n </PopoverTrigger>\n </Tooltip>\n <PopoverContent className=\"w-72\" finalFocus={false}>\n <form\n onSubmit={(e) => {\n e.preventDefault();\n onSave(v.trim());\n setOpen(false);\n onOpen(false);\n }}\n className=\"flex flex-col gap-2\"\n >\n <label className=\"text-kit-xs font-medium text-kit-fg-2\" htmlFor={id}>\n Alt text\n </label>\n <Textarea id={id} autoFocus value={v} onChange={(e) => setV(e.target.value)} placeholder=\"Describe the image\" rows={2} />\n <p className=\"text-kit-2xs text-kit-fg-3\">Read aloud by screen readers and used by search engines.</p>\n <div className=\"flex justify-end\">\n <Button size=\"xs\" variant=\"primary\" type=\"submit\">\n Done\n </Button>\n </div>\n </form>\n </PopoverContent>\n </Popover>\n );\n}\n\n/* ── Callout (V2 CalloutBlock): tone · title · message ─────────────── */\nconst TONES: Record<CalloutTone, { label: string; icon: React.ReactNode; box: string; chip: string }> = {\n info: { label: \"Info\", icon: <IconInfoCircle stroke={1.5} />, box: \"border-kit-info-line bg-kit-info-soft\", chip: \"text-kit-info-fg\" },\n tip: { label: \"Tip\", icon: <IconBulb stroke={1.5} />, box: \"border-kit-success-line bg-kit-success-soft\", chip: \"text-kit-success-fg\" },\n warning: { label: \"Warning\", icon: <IconAlertTriangle stroke={1.5} />, box: \"border-kit-warning-line bg-kit-warning-soft\", chip: \"text-kit-warning-fg\" },\n};\nfunction CalloutBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"callout\">) {\n // a stored tone the editor does not offer is kept as it is, and drawn as Info\n const tone: CalloutTone = (CALLOUT_TONES as readonly string[]).includes(m.tone ?? \"\") ? (m.tone as CalloutTone) : \"info\";\n const t = TONES[tone];\n const body = React.useRef<HTMLDivElement>(null);\n const ro = ctx.readOnly;\n return (\n <div className={cn(\"group/blk relative rounded-kit-lg border px-3.5 py-3\", t.box)} data-rt-isolate=\"\">\n <div className=\"flex items-center gap-1\">\n <Menu>\n <MenuTrigger\n disabled={ro}\n render={\n <button\n type=\"button\"\n aria-label={`Callout tone: ${t.label}`}\n className={cn(\"-ml-1 flex h-6 items-center gap-1 rounded-kit-md px-1.5 text-kit-xs font-medium hover:bg-kit-hover data-[popup-open]:bg-kit-hover [&_svg]:size-3.5\", t.chip)}\n >\n {t.icon}\n {t.label}\n {ro ? null : <IconChevronDown className=\"!size-3 opacity-70\" stroke={1.5} aria-hidden />}\n </button>\n }\n />\n <MenuContent className=\"w-36\">\n {CALLOUT_TONES.map((k) => (\n <MenuItem key={k} icon={TONES[k].icon} onClick={() => set({ tone: k }, \"Change callout tone\")}>\n <span className=\"flex w-full items-center justify-between\">\n {TONES[k].label}\n {tone === k ? <IconCheck className=\"size-3.5 text-kit-fg-2\" stroke={2} aria-hidden /> : null}\n </span>\n </MenuItem>\n ))}\n </MenuContent>\n </Menu>\n <span className=\"flex-1\" />\n {ro ? null : (\n <div className=\"opacity-0 transition-opacity group-hover/blk:opacity-100 group-focus-within/blk:opacity-100 pointer-coarse:opacity-100\">\n <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove callout\" />\n </div>\n )}\n </div>\n <PlainEditable\n value={m.title ?? \"\"}\n label=\"Callout title\"\n readOnly={ro}\n autoFocus={autoFocus}\n placeholder=\"Title\"\n onChange={(title) => set({ title }, \"Edit callout title\")}\n onEnter={() => {\n const b = body.current?.querySelector<HTMLElement>(\"[role=textbox]\");\n b?.focus();\n if (b) placeCaret(b, false);\n }}\n className=\"mt-1 font-medium text-kit-fg\"\n />\n <div ref={body}>\n <PlainEditable\n multiline\n value={m.body ?? \"\"}\n label=\"Callout message\"\n readOnly={ro}\n placeholder=\"Write a message…\"\n onChange={(b) => set({ body: b }, \"Edit callout message\")}\n className=\"mt-0.5 text-kit-fg-2 [&_p+p]:mt-2\"\n />\n </div>\n </div>\n );\n}\n\n/* ── Toggle (V2 ToggleBlock): title · body. A saved toggle shows closed, like V2; a just-inserted one shows open. Open or closed is the editor's view only. ── */\nfunction ToggleBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"toggle\">) {\n const [open, setOpen] = React.useState(!!autoFocus);\n const body = React.useRef<HTMLDivElement>(null);\n const bodyId = React.useId();\n const ro = ctx.readOnly;\n return (\n <div className=\"group/blk relative rounded-kit-md\" data-rt-isolate=\"\">\n <div className=\"flex items-start gap-1\">\n <button\n type=\"button\"\n aria-expanded={open}\n aria-controls={bodyId}\n aria-label={open ? \"Collapse toggle\" : \"Expand toggle\"}\n onClick={() => setOpen((o) => !o)}\n className=\"mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-kit-md text-kit-fg-2 hover:bg-kit-hover hover:text-kit-fg\"\n >\n <IconChevronRight className={cn(\"size-4 transition-transform duration-150\", open && \"rotate-90\")} stroke={1.5} aria-hidden />\n </button>\n <PlainEditable\n value={m.title ?? \"\"}\n label=\"Toggle title\"\n readOnly={ro}\n autoFocus={autoFocus}\n placeholder=\"Toggle title\"\n onChange={(title) => set({ title }, \"Edit toggle title\")}\n onEnter={() => {\n setOpen(true);\n requestAnimationFrame(() => {\n const b = body.current?.querySelector<HTMLElement>(\"[role=textbox]\");\n b?.focus();\n if (b) placeCaret(b, false);\n });\n }}\n className=\"flex-1 py-0.5 font-medium text-kit-fg\"\n />\n {ro ? null : (\n <div className=\"opacity-0 transition-opacity group-hover/blk:opacity-100 group-focus-within/blk:opacity-100 pointer-coarse:opacity-100\">\n <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove toggle\" />\n </div>\n )}\n </div>\n <div ref={body} id={bodyId} hidden={!open} className=\"ml-3 mt-1 border-l border-kit-line pl-4\">\n <PlainEditable\n multiline\n value={m.body ?? \"\"}\n label=\"Toggle body\"\n readOnly={ro}\n placeholder=\"Hidden content. Click to write…\"\n onChange={(b) => set({ body: b }, \"Edit toggle body\")}\n className=\"py-0.5 text-kit-fg-2 [&_p+p]:mt-2\"\n />\n </div>\n </div>\n );\n}\n\n/* ── Video (V2 VideoBlock): a link to a video file or a provider's page ─── */\nconst videoUrlOk = (u: string) => /^https?:\\/\\/\\S+$/i.test(u) || /^\\/(?!\\/)\\S*$/.test(u);\nfunction VideoBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"video\">) {\n const [draft, setDraft] = React.useState(m.url ?? \"\");\n React.useEffect(() => setDraft(m.url ?? \"\"), [m.url]);\n const [error, setError] = React.useState<string | null>(null);\n const errId = React.useId();\n const commit = () => {\n const u = draft.trim();\n if (u === (m.url ?? \"\")) return setError(null);\n if (u && !videoUrlOk(u)) return setError(\"Paste a full link that starts with https://, or a path on this site.\");\n setError(null);\n set({ url: u }, \"Set video link\");\n };\n const ro = ctx.readOnly;\n return (\n <div className=\"group/blk rounded-kit-lg border border-kit-line bg-kit-subtle p-2.5\" data-rt-isolate=\"\">\n {ro ? null : (\n <div className=\"mb-2 flex items-center gap-1.5\">\n <Input\n size=\"sm\"\n autoFocus={autoFocus}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={commit}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n commit();\n }\n }}\n placeholder=\"Paste a YouTube, Vimeo or video file link\"\n aria-label=\"Video URL\"\n aria-invalid={!!error || undefined}\n aria-describedby={error ? errId : undefined}\n spellCheck={false}\n autoComplete=\"off\"\n leading={<IconWorld stroke={1.5} />}\n className=\"min-w-0 flex-1\"\n />\n <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove video\" />\n </div>\n )}\n {error ? (\n <p id={errId} role=\"alert\" className=\"mb-2 px-1 text-kit-xs text-kit-danger-fg\">\n {error}\n </p>\n ) : null}\n {/* D5: V2's click-to-play facade (VideoPreview: poster, provider badge, Play), no third-party frame until Play */}\n {m.url ? <VideoPreview url={m.url} title=\"Video\" /> : <p className=\"px-1 pb-0.5 text-kit-xs text-kit-fg-3\">No video yet.</p>}\n </div>\n );\n}\n\n/* ── File (V2 FileBlock): a downloadable document ─────────────────── */\nconst formatBytes = (n: number) => (n < 1024 ? `${n} B` : n < 1024 * 1024 ? `${(n / 1024).toFixed(0)} KB` : `${(n / 1024 / 1024).toFixed(1)} MB`);\nfunction FileBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"file\">) {\n const { MediaPicker } = useKitServices();\n const [picker, setPicker] = React.useState(false);\n const [url, setUrl] = React.useState(\"\");\n const [name, setName] = React.useState(\"\");\n const ro = ctx.readOnly;\n const has = !!(m.url && m.name);\n const pickerEl = MediaPicker ? (\n <MediaPicker open={picker} onOpenChange={setPicker} accept={[\"file\"]} onPick={(a) => a[0] && set({ url: a[0].url, name: a[0].name ?? a[0].url.split(\"/\").pop() ?? \"Download\", ...(a[0].size ? { size: a[0].size } : {}) }, \"Choose file\")} />\n ) : null;\n if (has)\n return (\n <div className=\"group/blk\" data-rt-isolate=\"\">\n <div className=\"flex items-center gap-3 rounded-kit-lg border border-kit-line bg-kit-surface px-3 py-2.5\">\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-kit-md bg-kit-muted text-kit-fg-2\">\n <IconFileText className=\"size-[18px]\" stroke={1.5} aria-hidden />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-kit-sm font-medium text-kit-fg\">{m.name}</div>\n <div className=\"truncate text-kit-xs text-kit-fg-3\">\n {m.size ? `${formatBytes(m.size)} · ` : \"\"}\n {(m.name!.includes(\".\") ? m.name!.split(\".\").pop()! : \"file\").toUpperCase()}\n </div>\n </div>\n {ro ? null : (\n <>\n {MediaPicker ? (\n <Button size=\"xs\" variant=\"ghost\" onClick={() => setPicker(true)}>\n Replace\n </Button>\n ) : null}\n <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove file\" />\n </>\n )}\n </div>\n {pickerEl}\n </div>\n );\n return (\n <div className=\"group/blk\" data-rt-isolate=\"\">\n <div className=\"flex items-center gap-3 rounded-kit-lg border border-dashed border-kit-line-strong bg-kit-subtle px-3 py-2.5\">\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-kit-md border border-kit-line bg-kit-control-bg text-kit-fg-3\">\n <IconFileText className=\"size-[18px]\" stroke={1.5} aria-hidden />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-kit-sm font-medium text-kit-fg\">File</div>\n <p className=\"text-kit-xs text-kit-fg-3\">No file yet.</p>\n </div>\n {ro ? null : (\n <>\n {MediaPicker ? (\n <Button size=\"sm\" leftIcon={<IconFileUpload stroke={1.5} />} onClick={() => setPicker(true)}>\n Choose file\n </Button>\n ) : null}\n <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove file\" />\n </>\n )}\n </div>\n {ro ? null : (\n <form\n className=\"mt-2.5 flex flex-wrap items-center gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n const u = url.trim();\n if (!/^https?:\\/\\/\\S+$/i.test(u) && !/^\\/(?!\\/)\\S*$/.test(u)) return ctx.notify(\"Paste a full file link that starts with https://\", \"Or a path on this site, like /files/spec.pdf.\");\n set({ url: u, name: name.trim() || u.split(\"/\").pop() || \"Download\" }, \"Add file\");\n ctx.exit(el);\n }}\n >\n <Input autoFocus={autoFocus && !MediaPicker} size=\"sm\" value={url} onChange={(e) => setUrl(e.target.value)} placeholder=\"https://…/guide.pdf\" aria-label=\"File URL\" spellCheck={false} autoComplete=\"off\" leading={<IconLink stroke={1.5} />} className=\"min-w-40 flex-1\" />\n <Input size=\"sm\" value={name} onChange={(e) => setName(e.target.value)} placeholder=\"Link text\" aria-label=\"File name shown to readers\" className=\"min-w-28 flex-1\" />\n <Button size=\"sm\" variant=\"primary\" type=\"submit\" disabled={!url.trim()}>\n Add\n </Button>\n </form>\n )}\n {pickerEl}\n </div>\n );\n}\n\n/* ── Embedded component (CPO D28): a linked instance, shown as its card ─────────── */\nfunction ComponentBlock({ m, el, ctx, set }: BlockProps<\"component\">) {\n const { useComponentInfo } = useKitServices();\n const overrides = React.useMemo<Record<string, unknown>>(() => {\n try {\n const v = m.overrides ? (JSON.parse(m.overrides) as unknown) : {};\n return v && typeof v === \"object\" && !Array.isArray(v) ? (v as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n }, [m.overrides]);\n // the host's component library answers → V2's instance card; none → the compact linked row (no state made up)\n if (useComponentInfo) return <ComponentCard m={m} el={el} ctx={ctx} use={useComponentInfo} overrides={overrides} set={set} />;\n const count = Object.keys(overrides).length;\n const name = m.name || \"Block\";\n return (\n <div\n role=\"group\"\n aria-label={`${name}, ${m.detached ? \"separate copy\" : \"linked block\"}`}\n className=\"group/blk flex items-center gap-3 rounded-kit-lg border border-kit-line bg-kit-surface px-3 py-2.5\"\n data-rt-isolate=\"\"\n >\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-kit-md bg-kit-muted text-kit-fg-2\">\n <IconComponents className=\"size-[18px]\" stroke={1.5} aria-hidden />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-kit-sm font-medium text-kit-fg\">{name}</div>\n <div className=\"truncate text-kit-xs text-kit-fg-3\">\n {m.detached ? (\n \"Separate copy · edits to the block don't reach it\"\n ) : (\n <>\n {m.ref ? \"Linked block\" : \"Block with no source\"}\n {count ? ` · ${count} ${count === 1 ? \"override\" : \"overrides\"}` : \"\"} · edits to the component show here too\n </>\n )}\n </div>\n </div>\n {ctx.readOnly ? null : (\n <div className=\"flex items-center gap-0.5 opacity-0 transition-opacity motion-reduce:transition-none group-hover/blk:opacity-100 group-focus-within/blk:opacity-100 pointer-coarse:opacity-100\">\n {ctx.duplicate ? (\n <IconButton size=\"xs\" label={`Duplicate ${name}`} onClick={() => ctx.duplicate?.(el)}>\n <IconCopy stroke={1.5} />\n </IconButton>\n ) : null}\n <RemoveBtn onClick={() => ctx.remove(el)} label={`Remove ${name}`} />\n </div>\n )}\n </div>\n );\n}\n\n/* ── Table (V2 TableBlock, CPO D42: the table value, edited with the shared table editor) ─────────── */\n/**\n * What production's rich-text table keeps: typed columns (V2's Text, Number, Price, Yes / No, Link and Rating with their unit,\n * currency, decimals, alignment, yes / no words, scale and description; a row's and a cell's type and a cell's value: a\n * first-row cell's `column`, a row's / cell's `type`, a cell's `value`), the header row and row headers, an optional caption,\n * the note under the table, row groups, the totals row and the row and column highlights (`caption`, `notes`,\n * `rowGroups`, a row's `footer` / `highlight`, a first-row cell's `highlight` / `sortable`, the table's `filterable`).\n * the table's `jsonLd` and `design` (On phones, Keep column names in view)). Footnotes are stored as the table's `footnotes` and a cell's `note` (V2's default, allowFootnotes on). No setting is\n * pinned: V2's island passes no options, so its editor offers GENERAL_TABLE (sort, search and the JSON-LD choice on,\n * everything else at its default), and so does this.\n */\nconst STORED_TABLE: TableFieldOptions = {\n // No `columnTypes`: V2's island passes no options and no `developer` (blocks.tsx:411-421), so its editor offers every type\n // TYPE_UI does not mark `dev` (table-editor.tsx:80-86: Text, Number, Price, Yes / No, Link, Rating), and so does this.\n // V2's default, as V2's island draws it: its editor never reads requireCaption (table-editor.tsx:1030-1033, no hint beside\n // Title) and its island runs no table validation (validate.ts:112 is the table FIELD's). Ours draws a \"Required\" hint for\n // the table field lane, whose backend refuses a captionless table (table_caption_required); a rich-text table is never\n // refused for it, so the island shows no hint, exactly as V2's.\n requireCaption: false,\n allowGroups: true,\n allowNote: true,\n settings: { sort: true, search: true },\n // \"Help search engines read this as\": V2's GENERAL_TABLE offers all three (schema/table.ts:170)\n jsonLd: [\"ItemList\", \"Dataset\", \"Offers\"],\n};\nfunction TableBlock({ m, set, el, ctx }: BlockProps<\"table\">) {\n return (\n <div className=\"group/blk\" data-rt-isolate=\"\">\n <TableEditor\n value={m}\n onChange={(next) => set(() => next, \"Edit table\")}\n readOnly={ctx.readOnly}\n options={STORED_TABLE}\n label=\"Table\"\n actions={ctx.readOnly ? null : <RemoveBtn onClick={() => ctx.remove(el)} label=\"Remove table\" />}\n />\n </div>\n );\n}\n\n/* ── Pull quote (V2 article-blocks.tsx ArticleBlock, surface \"panel\") ─────────── */\nconst PULL_QUOTE_LABEL = \"Pull quote\";\n/** V2 article.ts ARTICLE_SCHEMAS.pullquote: the properties panel is built from it. */\nconst PULL_QUOTE_SCHEMA: TypeSchema = {\n name: \"pullQuote\",\n title: PULL_QUOTE_LABEL,\n kind: \"object\",\n icon: \"quote\",\n fields: [\n { name: \"quote\", title: \"Quote\", type: \"text\", rows: 3, validation: { required: true, recommendedMax: 200 } },\n { name: \"name\", title: \"Name\", type: \"string\", validation: { max: 60 } },\n { name: \"role\", title: \"Role or source\", type: \"string\", validation: { max: 80 } },\n { name: \"link\", title: \"Source link\", type: \"url\", description: \"The name links here.\" },\n ],\n};\n\nfunction PullQuoteBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"pullquote\">) {\n const label = PULL_QUOTE_LABEL;\n const card = React.useRef<HTMLDivElement | null>(null);\n const [cardEl, setCardEl] = React.useState<HTMLDivElement | null>(null);\n const ro = !!ctx.readOnly;\n const { open, editing, openPanel, close } = useArticleOpen({ ro, autoFocus });\n // a stable ref: a new callback each render would pass null for a moment and remount the panel\n const cardRef = React.useCallback((n: HTMLDivElement | null) => {\n card.current = n;\n if (n) setCardEl(n);\n }, []);\n const modelRef = React.useRef(m);\n modelRef.current = m;\n const focusCard = React.useCallback(() => requestAnimationFrame(() => card.current?.focus({ preventScroll: true })), []);\n\n /** a form edit joins the body's one undo history (D54); typing coalesces per field */\n const commit = (next: PullQuoteModel) => {\n const prev = modelRef.current;\n const field = (Object.keys(next) as (keyof PullQuoteModel)[]).find((k) => next[k] !== prev[k]) ?? \"\";\n set(() => next, `Edit ${label.toLowerCase()} ${field}`);\n };\n\n return (\n <>\n <ArticleCard\n ref={cardRef}\n label={label}\n kind=\"pullquote\"\n ro={ro}\n editing={editing}\n onOpen={openPanel}\n onKeyDown={articleCardKeyDown({ card, ctx, el, ro, editing, onOpen: openPanel, onClose: close, focusCard })}\n >\n <PullQuotePreview m={m} />\n <ArticleActions label={label} ctx={ctx} el={el} editing={editing} onEdit={() => (editing ? close() : openPanel())} />\n </ArticleCard>\n {editing ? (\n <ArticlePanel label={label} anchor={cardEl} nonce={open!.nonce} focusKey={open!.focusKey} onClose={close} focusBack={focusCard} ctx={ctx} el={el}>\n <ArticleForm schema={PULL_QUOTE_SCHEMA} label={label} value={m as unknown as Record<string, unknown>} onChange={(v) => commit(v as unknown as PullQuoteModel)} ro={ro} />\n </ArticlePanel>\n ) : null}\n </>\n );\n}\n\n/* ── Key takeaways (V2 article-blocks.tsx ArticleBlock, surface \"inline\" + TakeawaysEditor) ─────────── */\nconst TAKEAWAYS_LABEL = \"Key takeaways\";\n/** V2 article.ts ARTICLE_SCHEMAS.takeaways `points` max (the stored block's limit, key_takeaways_limit_exceeded) */\nconst MAX_POINTS = 8;\n/** V2 article.ts ARTICLE_SCHEMAS.takeaways `title` max */\nconst MAX_TITLE = 40;\n\nfunction TakeawaysBlock({ m, set, el, ctx, autoFocus }: BlockProps<\"takeaways\">) {\n const label = TAKEAWAYS_LABEL;\n const card = React.useRef<HTMLDivElement>(null);\n const box = React.useRef<HTMLUListElement>(null);\n const ro = !!ctx.readOnly;\n const focusCard = () => requestAnimationFrame(() => card.current?.focus({ preventScroll: true }));\n const focusPoint = (i: number, atEnd = true) =>\n requestAnimationFrame(() => {\n const t = box.current?.querySelectorAll<HTMLElement>(\"[role=textbox]\")[i];\n if (!t) return;\n t.focus();\n placeCaret(t, atEnd);\n });\n const setPoint = (i: number, v: string) => set({ points: m.points.map((p, j) => (j === i ? v : p)) }, `Edit key takeaways point ${i + 1}`);\n\n /** V2 ArticleBlock onKeyDown, on the block itself (its lines handle their own keys) */\n const onKeyDown = articleCardKeyDown({ card, ctx, el, ro, focusCard });\n\n return (\n <ArticleCard ref={card} label={label} kind=\"takeaways\" ro={ro} onKeyDown={onKeyDown}>\n <aside className=\"rounded-kit-lg border border-kit-line bg-kit-subtle px-4 py-3\" aria-label={m.title || label}>\n <PlainEditable\n label=\"Key takeaways title\"\n value={m.title}\n placeholder=\"Key takeaways\"\n readOnly={ro}\n onChange={(title) => {\n // V2 ARTICLE_SCHEMAS.takeaways title max: said the moment it is crossed, not first at save (key_takeaways_limit_exceeded)\n if (title.length > MAX_TITLE && m.title.length <= MAX_TITLE)\n ctx.notify(`A key takeaways title holds at most ${MAX_TITLE} characters.`, \"Shorten it, or the change will not save.\");\n set({ title }, \"Edit key takeaways title\");\n }}\n onEnter={() => focusPoint(0, false)}\n className=\"text-kit-sm font-semibold text-kit-fg\"\n />\n <ul ref={box} className=\"m-0 mt-1.5 flex flex-col gap-1 p-0\">\n {m.points.map((p, i) => (\n <li key={i} className=\"flex list-none items-baseline gap-2 text-[15px] leading-relaxed text-kit-fg\">\n <span aria-hidden className=\"mt-[0.55em] size-1.5 shrink-0 self-start rounded-full bg-kit-accent\" />\n <PlainEditable\n label={`Point ${i + 1}`}\n value={p}\n placeholder={i === 0 ? \"The first thing to remember\" : \"Another point\"}\n readOnly={ro}\n autoFocus={autoFocus && i === 0}\n onChange={(v) => setPoint(i, v)}\n onEnter={() => {\n if (m.points.length >= MAX_POINTS) {\n ctx.notify(`Key takeaways hold at most ${MAX_POINTS} points.`, \"Merge two points, or move this one into the text.\");\n return;\n }\n set((x) => ({ ...x, points: [...x.points.slice(0, i + 1), \"\", ...x.points.slice(i + 1)] }), \"Add a key takeaways point\");\n focusPoint(i + 1);\n }}\n onBackspaceEmpty={\n m.points.length > 1\n ? () => {\n set((x) => ({ ...x, points: x.points.filter((_, j) => j !== i) }), \"Remove a key takeaways point\");\n focusPoint(Math.max(0, i - 1));\n }\n : undefined\n }\n className=\"flex-1\"\n />\n </li>\n ))}\n </ul>\n </aside>\n <ArticleActions label={label} ctx={ctx} el={el} />\n </ArticleCard>\n );\n}\n\n/* ── Preview (what the reader sees; V2 PullQuotePreview) ───────────── */\nfunction PullQuotePreview({ m }: { m: PullQuoteModel }) {\n return (\n <figure className=\"m-0 py-2 pl-5 pr-10\">\n <IconQuote className=\"mb-1 size-6 text-kit-accent-fg\" stroke={1.5} aria-hidden />\n <blockquote data-field=\"quote\" className={cn(\"m-0 text-[22px] font-medium leading-snug tracking-[-0.01em]\", m.quote ? \"text-kit-fg\" : \"text-kit-fg-4\")}>\n {m.quote || \"Write the quote\"}\n </blockquote>\n {m.name || m.role ? (\n <figcaption className=\"mt-3 text-kit-sm text-kit-fg-2\">\n {m.name ? (\n <span data-field=\"name\" className={cn(\"font-medium text-kit-fg\", m.link && \"underline decoration-kit-line-strong underline-offset-2\")}>\n {m.name}\n </span>\n ) : null}\n {m.name && m.role ? \", \" : null}\n {m.role ? <span data-field=\"role\">{m.role}</span> : null}\n </figcaption>\n ) : (\n <figcaption data-field=\"name\" className=\"mt-3 text-kit-sm text-kit-fg-4\">Who said it</figcaption>\n )}\n </figure>\n );\n}\n"],"mappings":"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"}
|
|
@@ -39,11 +39,13 @@ export interface RichTextRead {
|
|
|
39
39
|
* `topic: false`: the posts collection has no tags or category field, so "Same topic" cannot rank there (the entry
|
|
40
40
|
* write refuses it, relatedPosts.no_topic_field); a new block is Newest and Same topic is not offered. Absent = has one.
|
|
41
41
|
*/
|
|
42
|
+
/** `postsNoun`: what one post is called (the posts collection's singular, "story"), so Hand-picked says "Create new story" */
|
|
42
43
|
references?: {
|
|
43
44
|
authors?: string;
|
|
44
45
|
posts?: string;
|
|
45
46
|
entryId?: string;
|
|
46
47
|
topic?: boolean;
|
|
48
|
+
postsNoun?: string;
|
|
47
49
|
};
|
|
48
50
|
}
|
|
49
51
|
export interface RichTextWrite {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.js","names":[],"sources":["../../../src/controls/rich-text/codec.ts"],"sourcesContent":["/**\n * RICH TEXT · the storage contract between the kit editor and its host.\n *\n * The V2 editor edits HTML (contentEditable). Production stores Portable Text, in the shape the classic editor of the\n * same surface writes (a block array for an entry field, the `{ content, html }` envelope for a component prop). The\n * conversion lives in the host, which owns the real converters, schema conformance and the field's Accept config; the\n * kit never imports them. A host with no codec gets its own control for rich text (LegacyFallback), never a guess.\n *\n * The control opens a value only when it survives the trip editor → codec unchanged (`same`), so nothing the HTML\n * editor cannot hold (a checklist's ticks, a link's rel, an image's layout) is dropped by a first keystroke: such a\n * value is edited by the host's own control instead, with the reason named.\n */\nimport type { FieldPath, FieldSchema } from \"../../schema/types\";\nimport type { UnknownValue } from \"../unknown-value\";\n\n/** What a field may hold, in the editor's vocabulary. The editor offers nothing outside it. */\nexport type RichTextFeature =\n | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\"\n | \"blockquote\" | \"ul\" | \"ol\" | \"pre\" | \"hr\"\n | \"bold\" | \"italic\" | \"underline\" | \"strike\" | \"sup\" | \"sub\" | \"code\"\n | \"link\" | \"image\"\n /* blocks, each stored as its own Portable Text block (see dom.ts) */\n | \"table\" | \"pullquote\" | \"takeaways\" | \"callout\" | \"toggle\" | \"video\" | \"file\"\n /* a task list (PT listItem \"check\" + checked) and a call-to-action link (the link markDef with button: true) */\n | \"checklist\" | \"button\"\n /* V2's article blocks, each a Portable Text block of its own (plan RT3–RT5): embed, gallery, table of contents */\n | \"embed\" | \"gallery\" | \"toc\"\n /* plan RT6–RT8: a footnote (inline, in the text), columns of text, an author bio and related posts (references) */\n | \"footnote\" | \"columns\" | \"author\" | \"related\"\n /* an embed's Shape (aspect): the host declares it once its stored embed keeps one (plan RT3) */\n | \"embedAspect\";\n\nexport const ALL_FEATURES: readonly RichTextFeature[] = [\n \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", \"blockquote\", \"ul\", \"ol\", \"pre\", \"hr\", \"bold\", \"italic\", \"underline\", \"strike\", \"sup\", \"sub\", \"code\", \"link\", \"image\",\n \"table\", \"pullquote\", \"takeaways\", \"callout\", \"toggle\", \"video\", \"file\", \"checklist\", \"button\", \"embed\", \"gallery\", \"toc\", \"footnote\", \"columns\", \"author\", \"related\", \"embedAspect\",\n];\n\nexport interface RichTextRead {\n /** the HTML the editor opens on */\n html: string;\n /** the stored value as the classic editor would open it (compared against the editor's round trip) */\n seed: unknown;\n /** what this value already cannot carry from an earlier editor, in words (\"2 links\"); shown before the first edit */\n losses: string[];\n /** what the field accepts */\n features: RichTextFeature[];\n /** one paragraph, stored inline (allowMultipleParagraphs: false) */\n single: boolean;\n /** links may open in a new tab */\n newTab: boolean;\n /**\n * the styles, marks and lists the value holds that the field no longer declares: kept by every write, flagged above\n * the editor with change-to and remove (H4; see `remap`). Absent or empty = none.\n */\n undeclared?: UnknownValue[];\n /**\n * Where an author bio and related posts point (plan RT8), from the field's config: the authors and posts collections,\n * and the entry being edited (left out of the automatic related modes). Author bio is offered only with `authors`.\n * `topic: false`: the posts collection has no tags or category field, so \"Same topic\" cannot rank there (the entry\n * write refuses it, relatedPosts.no_topic_field); a new block is Newest and Same topic is not offered. Absent = has one.\n */\n references?: { authors?: string; posts?: string; entryId?: string; topic?: boolean };\n}\n\nexport interface RichTextWrite {\n /** the value to store, in the surface's own shape */\n value: unknown;\n /** what the stored shape would drop from this HTML, in words; non-empty = the write is refused */\n losses: string[];\n}\n\nexport interface RichTextAt {\n field: FieldSchema;\n path: FieldPath;\n}\n\nexport interface KitRichTextCodec {\n read(value: unknown, at: RichTextAt): RichTextRead;\n /** `prev` is the stored value being replaced (its keys are reused so an edit does not re-key the document) */\n write(html: string, at: RichTextAt, prev: unknown): RichTextWrite;\n /** the same document, ignoring keys, span splits and storage wrapper */\n same(a: unknown, b: unknown): boolean;\n /** the stored value with one `undeclared` value (its `key`) changed to `to`, or removed (`to` null); the words stay */\n remap?(value: unknown, at: RichTextAt, key: string, to: string | null): unknown;\n}\n\ndeclare module \"../../context\" {\n interface KitServices {\n /**\n * Stored rich text ↔ the HTML the kit editor edits (see controls/rich-text/codec.ts). Absent → a rich text field\n * draws the host's own control for it (LegacyControl).\n */\n richText?: KitRichTextCodec;\n }\n}\n"],"mappings":";AAgCA,IAAa,eAA2C;CACtD;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAc;CAAM;CAAM;CAAO;CAAM;CAAQ;CAAU;CAAa;CAAU;CAAO;CAAO;CAAQ;CAAQ;CAClJ;CAAS;CAAa;CAAa;CAAW;CAAU;CAAS;CAAQ;CAAa;CAAU;CAAS;CAAW;CAAO;CAAY;CAAW;CAAU;CAAW;AACzK"}
|
|
1
|
+
{"version":3,"file":"codec.js","names":[],"sources":["../../../src/controls/rich-text/codec.ts"],"sourcesContent":["/**\n * RICH TEXT · the storage contract between the kit editor and its host.\n *\n * The V2 editor edits HTML (contentEditable). Production stores Portable Text, in the shape the classic editor of the\n * same surface writes (a block array for an entry field, the `{ content, html }` envelope for a component prop). The\n * conversion lives in the host, which owns the real converters, schema conformance and the field's Accept config; the\n * kit never imports them. A host with no codec gets its own control for rich text (LegacyFallback), never a guess.\n *\n * The control opens a value only when it survives the trip editor → codec unchanged (`same`), so nothing the HTML\n * editor cannot hold (a checklist's ticks, a link's rel, an image's layout) is dropped by a first keystroke: such a\n * value is edited by the host's own control instead, with the reason named.\n */\nimport type { FieldPath, FieldSchema } from \"../../schema/types\";\nimport type { UnknownValue } from \"../unknown-value\";\n\n/** What a field may hold, in the editor's vocabulary. The editor offers nothing outside it. */\nexport type RichTextFeature =\n | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\"\n | \"blockquote\" | \"ul\" | \"ol\" | \"pre\" | \"hr\"\n | \"bold\" | \"italic\" | \"underline\" | \"strike\" | \"sup\" | \"sub\" | \"code\"\n | \"link\" | \"image\"\n /* blocks, each stored as its own Portable Text block (see dom.ts) */\n | \"table\" | \"pullquote\" | \"takeaways\" | \"callout\" | \"toggle\" | \"video\" | \"file\"\n /* a task list (PT listItem \"check\" + checked) and a call-to-action link (the link markDef with button: true) */\n | \"checklist\" | \"button\"\n /* V2's article blocks, each a Portable Text block of its own (plan RT3–RT5): embed, gallery, table of contents */\n | \"embed\" | \"gallery\" | \"toc\"\n /* plan RT6–RT8: a footnote (inline, in the text), columns of text, an author bio and related posts (references) */\n | \"footnote\" | \"columns\" | \"author\" | \"related\"\n /* an embed's Shape (aspect): the host declares it once its stored embed keeps one (plan RT3) */\n | \"embedAspect\";\n\nexport const ALL_FEATURES: readonly RichTextFeature[] = [\n \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", \"blockquote\", \"ul\", \"ol\", \"pre\", \"hr\", \"bold\", \"italic\", \"underline\", \"strike\", \"sup\", \"sub\", \"code\", \"link\", \"image\",\n \"table\", \"pullquote\", \"takeaways\", \"callout\", \"toggle\", \"video\", \"file\", \"checklist\", \"button\", \"embed\", \"gallery\", \"toc\", \"footnote\", \"columns\", \"author\", \"related\", \"embedAspect\",\n];\n\nexport interface RichTextRead {\n /** the HTML the editor opens on */\n html: string;\n /** the stored value as the classic editor would open it (compared against the editor's round trip) */\n seed: unknown;\n /** what this value already cannot carry from an earlier editor, in words (\"2 links\"); shown before the first edit */\n losses: string[];\n /** what the field accepts */\n features: RichTextFeature[];\n /** one paragraph, stored inline (allowMultipleParagraphs: false) */\n single: boolean;\n /** links may open in a new tab */\n newTab: boolean;\n /**\n * the styles, marks and lists the value holds that the field no longer declares: kept by every write, flagged above\n * the editor with change-to and remove (H4; see `remap`). Absent or empty = none.\n */\n undeclared?: UnknownValue[];\n /**\n * Where an author bio and related posts point (plan RT8), from the field's config: the authors and posts collections,\n * and the entry being edited (left out of the automatic related modes). Author bio is offered only with `authors`.\n * `topic: false`: the posts collection has no tags or category field, so \"Same topic\" cannot rank there (the entry\n * write refuses it, relatedPosts.no_topic_field); a new block is Newest and Same topic is not offered. Absent = has one.\n */\n /** `postsNoun`: what one post is called (the posts collection's singular, \"story\"), so Hand-picked says \"Create new story\" */\n references?: { authors?: string; posts?: string; entryId?: string; topic?: boolean; postsNoun?: string };\n}\n\nexport interface RichTextWrite {\n /** the value to store, in the surface's own shape */\n value: unknown;\n /** what the stored shape would drop from this HTML, in words; non-empty = the write is refused */\n losses: string[];\n}\n\nexport interface RichTextAt {\n field: FieldSchema;\n path: FieldPath;\n}\n\nexport interface KitRichTextCodec {\n read(value: unknown, at: RichTextAt): RichTextRead;\n /** `prev` is the stored value being replaced (its keys are reused so an edit does not re-key the document) */\n write(html: string, at: RichTextAt, prev: unknown): RichTextWrite;\n /** the same document, ignoring keys, span splits and storage wrapper */\n same(a: unknown, b: unknown): boolean;\n /** the stored value with one `undeclared` value (its `key`) changed to `to`, or removed (`to` null); the words stay */\n remap?(value: unknown, at: RichTextAt, key: string, to: string | null): unknown;\n}\n\ndeclare module \"../../context\" {\n interface KitServices {\n /**\n * Stored rich text ↔ the HTML the kit editor edits (see controls/rich-text/codec.ts). Absent → a rich text field\n * draws the host's own control for it (LegacyControl).\n */\n richText?: KitRichTextCodec;\n }\n}\n"],"mappings":";AAgCA,IAAa,eAA2C;CACtD;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAc;CAAM;CAAM;CAAO;CAAM;CAAQ;CAAU;CAAa;CAAU;CAAO;CAAO;CAAQ;CAAQ;CAClJ;CAAS;CAAa;CAAa;CAAW;CAAU;CAAS;CAAQ;CAAa;CAAU;CAAS;CAAW;CAAO;CAAY;CAAW;CAAU;CAAW;AACzK"}
|
|
@@ -19,7 +19,10 @@ export declare const NO_TOPIC_REASON = "Same topic needs a tags or category fiel
|
|
|
19
19
|
* The panel form; `to` is the field's collection. "How many" explains the chosen automatic mode. With `topic` false,
|
|
20
20
|
* Same topic is left out and Stories says why (a stored Same topic block keeps the option, so its value still shows).
|
|
21
21
|
*/
|
|
22
|
-
export declare const relatedSchema: (to: string[], mode: RelatedMode, topic?: boolean
|
|
22
|
+
export declare const relatedSchema: (to: string[], mode: RelatedMode, topic?: boolean, pick?: {
|
|
23
|
+
noun?: string;
|
|
24
|
+
excludeId?: string;
|
|
25
|
+
}) => TypeSchema;
|
|
23
26
|
/** What the preview asks the host for: the picked ids, or the automatic mode's query. */
|
|
24
27
|
export declare function relatedRequest(m: RelatedModel, collection?: string, excludeId?: string): KitRichTextRefRequest | null;
|
|
25
28
|
export declare function RelatedPreview({ m, onPick, ro, collection, excludeId }: {
|
|
@@ -29,7 +32,7 @@ export declare function RelatedPreview({ m, onPick, ro, collection, excludeId }:
|
|
|
29
32
|
collection?: string;
|
|
30
33
|
excludeId?: string;
|
|
31
34
|
}): import("react/jsx-runtime").JSX.Element;
|
|
32
|
-
export declare function RelatedBlock({ m, set, el, ctx, autoFocus, collection, excludeId, topic, }: {
|
|
35
|
+
export declare function RelatedBlock({ m, set, el, ctx, autoFocus, collection, excludeId, topic, noun, }: {
|
|
33
36
|
m: RelatedModel;
|
|
34
37
|
set: (fn: (m: RelatedModel) => RelatedModel, label?: string) => void;
|
|
35
38
|
el: IslandEl;
|
|
@@ -41,4 +44,6 @@ export declare function RelatedBlock({ m, set, el, ctx, autoFocus, collection, e
|
|
|
41
44
|
excludeId?: string;
|
|
42
45
|
/** false: the collection has no topic field, so Same topic is not offered */
|
|
43
46
|
topic?: boolean;
|
|
47
|
+
/** what one post is called (the collection's singular); absent → the picker's own noun */
|
|
48
|
+
noun?: string;
|
|
44
49
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -39,7 +39,7 @@ var clampCount = (v) => Math.min(6, Math.max(1, Math.round(Number(v) || 3)));
|
|
|
39
39
|
* The panel form; `to` is the field's collection. "How many" explains the chosen automatic mode. With `topic` false,
|
|
40
40
|
* Same topic is left out and Stories says why (a stored Same topic block keeps the option, so its value still shows).
|
|
41
41
|
*/
|
|
42
|
-
var relatedSchema = (to, mode, topic = true) => ({
|
|
42
|
+
var relatedSchema = (to, mode, topic = true, pick = {}) => ({
|
|
43
43
|
name: "relatedPosts",
|
|
44
44
|
title: RELATED_LABEL,
|
|
45
45
|
kind: "object",
|
|
@@ -106,7 +106,11 @@ var relatedSchema = (to, mode, topic = true) => ({
|
|
|
106
106
|
name: "post",
|
|
107
107
|
title: "Post",
|
|
108
108
|
type: "reference",
|
|
109
|
-
reference: {
|
|
109
|
+
reference: {
|
|
110
|
+
to,
|
|
111
|
+
...pick.noun ? { noun: pick.noun } : {},
|
|
112
|
+
...pick.excludeId ? { excludeId: pick.excludeId } : {}
|
|
113
|
+
}
|
|
110
114
|
}]
|
|
111
115
|
}
|
|
112
116
|
]
|
|
@@ -181,11 +185,16 @@ function RelatedPreview({ m, onPick, ro, collection, excludeId }) {
|
|
|
181
185
|
})]
|
|
182
186
|
});
|
|
183
187
|
}
|
|
184
|
-
function RelatedBlock({ m, set, el, ctx, autoFocus, collection, excludeId, topic = true }) {
|
|
185
|
-
const schema = React.useMemo(() => relatedSchema([collection], m.mode, topic
|
|
188
|
+
function RelatedBlock({ m, set, el, ctx, autoFocus, collection, excludeId, topic = true, noun }) {
|
|
189
|
+
const schema = React.useMemo(() => relatedSchema([collection], m.mode, topic, {
|
|
190
|
+
noun,
|
|
191
|
+
excludeId
|
|
192
|
+
}), [
|
|
186
193
|
collection,
|
|
187
194
|
m.mode,
|
|
188
|
-
topic
|
|
195
|
+
topic,
|
|
196
|
+
noun,
|
|
197
|
+
excludeId
|
|
189
198
|
]);
|
|
190
199
|
return /* @__PURE__ */ jsx(ArticlePanelBlock, {
|
|
191
200
|
label: RELATED_LABEL,
|