@xiwebdesign/xicms-core 6.4.3 → 6.4.4

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.
@@ -480,7 +480,11 @@ function VisualEditorInner({ onClose }) {
480
480
  try {
481
481
  await saveDraft(siteId, draft.id, draftPayload(draft));
482
482
  await publishPage(siteId, draft.id);
483
- toast({ title: "Gepubliceerd", description: "Herlaad de pagina om het live resultaat te zien.", tone: "success" });
483
+ toast({
484
+ title: "Gepubliceerd",
485
+ description: "Live voor bezoekers over ongeveer 15 minuten. Gebruik \u201CBekijk live\u201D hierboven om het direct te controleren.",
486
+ tone: "success"
487
+ });
484
488
  } catch (e) {
485
489
  toast({ title: "Publiceren mislukt", description: e.message, tone: "error" });
486
490
  } finally {
@@ -525,6 +529,17 @@ function VisualEditorInner({ onClose }) {
525
529
  /* @__PURE__ */ jsx("span", { className: "xicms-ve__status", "aria-live": "polite", children: status === "saving" ? "Opslaan\u2026" : status === "saved" ? "\u2713 Opgeslagen" : status === "error" ? "\u26A0 Niet opgeslagen" : "" }),
526
530
  status === "error" && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: () => void flushSave().catch(() => setStatus("error")), children: "Opnieuw opslaan" }),
527
531
  /* @__PURE__ */ jsxs("div", { className: "xicms-ve__bar-actions", children: [
532
+ /* @__PURE__ */ jsx(
533
+ "a",
534
+ {
535
+ className: "xicms-btn xicms-btn--ghost",
536
+ href: `${path}${path.includes("?") ? "&" : "?"}preview=1`,
537
+ target: "_blank",
538
+ rel: "noopener noreferrer",
539
+ title: "Toont de gepubliceerde pagina rechtstreeks (zonder cache) \u2014 zo zie je meteen wat je zojuist gepubliceerd hebt.",
540
+ children: "\u{1F441} Bekijk live"
541
+ }
542
+ ),
528
543
  /* @__PURE__ */ jsx(Button, { size: "sm", disabled: publishing || !draft, onClick: onPublish, children: publishing ? "Bezig\u2026" : "Publiceren" }),
529
544
  /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: handleClose, children: "Sluiten" })
530
545
  ] })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/visual/VisualEditor.tsx"],"sourcesContent":["\"use client\";\n\n// De admin-stijlen (Tailwind, scoped onder .xicms-admin) horen bij dit lazy-geladen chunk, zodat\n// publieke bezoekers ze nooit downloaden — alleen wanneer een editor de pagina-editor opent.\nimport \"@xiwebdesign/xicms-core/admin.css\";\nimport { useCallback, useEffect, useMemo, useRef, useState, type ComponentType } from \"react\";\nimport { useSiteConfig } from \"../../config\";\nimport { useRegistry } from \"../../blocks/context\";\nimport { introspectToFields } from \"../lib/zodFields\";\nimport { BlockErrorBoundary } from \"../../blocks/BlockErrorBoundary\";\nimport { InlineImageEditProvider } from \"../../media/ImageContext\";\nimport { MediaPickerDialog } from \"../media/MediaPicker\";\nimport { EmptyState } from \"@astryxdesign/core/EmptyState\";\nimport { buildBlockAddOptions } from \"../../blocks/registry\";\nimport DOMPurify from \"dompurify\";\nimport { createPage, getDraftByPath, saveDraft, publishPage, subscribeDraft, subscribeVersions, restoreVersion, type DraftPage } from \"../lib/pagesClient\";\nimport { isDemoActive } from \"../../demo/demoStore\";\nimport { builtinBlockEditors, GenericEditor, type BlockEditorProps } from \"../blockEditors\";\nimport { StyleEditor } from \"../blockEditors/StyleEditor\";\nimport { siteLocales, parseLocaleSlugs, localizeBlocks } from \"../../i18n/overlay\";\nimport { TranslationPanel } from \"./TranslationPanel\";\nimport { reorderBlocks } from \"./reorder\";\nimport { PatternInserter } from \"../patterns/PatternInserter\";\nimport type { BlockStyle } from \"../../schema/blocks\";\nimport { Button, Field, Input, Textarea } from \"../ui/primitives\";\nimport { Select } from \"../ui/select\";\nimport { ToastProvider, useToast } from \"../ui/toast\";\nimport { ConfirmHost, useConfirm } from \"../ui/ConfirmDialog\";\n\ntype Block = Record<string, unknown> & { _key: string; _type: string };\ntype Status = \"loading\" | \"none\" | \"idle\" | \"saving\" | \"saved\" | \"error\";\n\n/**\n * Inline bewerken is sinds 5.0 volledig generiek: élk blok markeert zijn tekstvelden met\n * `data-xicms-edit=\"veldpad\"` (de builtin-Renders doen dat), plus de rich-text-haak\n * `.xicms-richtext`. De oude hardgecodeerde per-type selector-map (`INLINE_FIELDS`) is vervallen.\n * In development waarschuwt `warnInlineCoverage` wanneer een tekstveld uit het schema géén\n * DOM-haak heeft — zo blijven Renders eerlijk t.o.v. hun schema.\n */\nconst GLOBAL_INLINE = [\n { selector: \".xicms-richtext\", field: \"html\", html: true as const },\n { selector: \"[data-xicms-edit]\", field: \"\", html: false as const, fromData: true as const },\n];\n\n/** Dev-vangnet (5.0): meld tekstvelden zonder inline-haak — één keer per bloktype. */\nconst inlineCoverageWarned = new Set<string>();\nfunction warnInlineCoverage(type: string, def: { schema: Parameters<typeof introspectToFields>[0]; fields?: Parameters<typeof introspectToFields>[1] } | undefined, host: HTMLElement) {\n if (process.env.NODE_ENV === \"production\" || !def || inlineCoverageWarned.has(type)) return;\n inlineCoverageWarned.add(type);\n const missing = introspectToFields(def.schema, def.fields)\n .filter((f) => f.type === \"text\" || f.type === \"textarea\")\n .map((f) => f.key)\n .filter((key) => !host.querySelector(`[data-xicms-edit=\"${key}\"], [data-xicms-edit$=\".${key}\"]`));\n if (missing.length > 0) {\n console.warn(\n `[xicms] blok \"${type}\": tekstveld(en) zonder inline-haak: ${missing.join(\", \")} — voeg data-xicms-edit=\"<veld>\" toe aan de Render zodat ze direct op de pagina bewerkbaar zijn.`,\n );\n }\n}\n\n/* Lees/schrijf een waarde op een gepunt pad (bv. \"items.0.title\") — zodat ook lijst-items inline\n bewerkt kunnen worden. setPath kloont onderweg (immutable) zodat React de wijziging detecteert. */\nfunction getPath(obj: unknown, path: string): unknown {\n return path.split(\".\").reduce<unknown>((o, k) => (o == null ? undefined : (o as Record<string, unknown>)[k]), obj);\n}\nfunction setPath<T>(obj: T, path: string, value: unknown): T {\n const keys = path.split(\".\");\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const clone = (v: unknown): any => (Array.isArray(v) ? [...v] : v && typeof v === \"object\" ? { ...(v as object) } : v);\n const root = clone(obj);\n let cur = root;\n for (let i = 0; i < keys.length - 1; i++) {\n cur[keys[i]] = clone(cur[keys[i]] ?? {});\n cur = cur[keys[i]];\n }\n cur[keys[keys.length - 1]] = value;\n return root as T;\n}\n\nfunction newKey() {\n return (globalThis.crypto?.randomUUID?.() ?? `b${Date.now()}${Math.random()}`).slice(0, 12);\n}\n\n/** JSON.stringify met gesorteerde sleutels — voor betrouwbare inhoudsvergelijking (conflictdetectie):\n * Firestore kan velden in een andere volgorde teruggeven dan wij ze lokaal opbouwden. */\nfunction stableStringify(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : 1));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n\n/**\n * Visuele/inline pagina-editor: opent als overlay over de live pagina, laadt de werkversie van de\n * huidige route, en laat de editor blokken op een live canvas selecteren, herordenen, dupliceren,\n * verwijderen en toevoegen — met autosave en publiceren. De zware code laadt pas bij openen\n * (lazy, via EditorProvider), dus bezoekers downloaden hem nooit.\n */\nexport default function VisualEditor({ onClose }: { onClose: () => void }) {\n return (\n <div className=\"xicms-admin xicms-ve\">\n <ToastProvider>\n <ConfirmHost>\n <VisualEditorInner onClose={onClose} />\n </ConfirmHost>\n </ToastProvider>\n </div>\n );\n}\n\nfunction VisualEditorInner({ onClose }: { onClose: () => void }) {\n const config = useSiteConfig();\n const { siteId, name } = config;\n const registry = useRegistry();\n const { toast } = useToast();\n const confirm = useConfirm();\n\n // Meertalig (5.0): een taalprefix in de URL (/de/over-ons) hoort niet bij het canonieke\n // pagina-pad — de editor opent dan de pagina zélf, met de betreffende taal actief.\n const localeCfg = useMemo(() => siteLocales(config), [config]);\n const { path, urlLocale } = useMemo(() => {\n const raw = typeof window !== \"undefined\" ? window.location.pathname || \"/\" : \"/\";\n const { locale, slugs } = parseLocaleSlugs(raw.split(\"/\").filter(Boolean), localeCfg);\n const p = \"/\" + slugs.join(\"/\");\n return { path: p === \"/\" ? \"/\" : p.replace(/\\/$/, \"\"), urlLocale: locale };\n }, [localeCfg]);\n const [locale, setLocale] = useState(urlLocale);\n const isTranslating = locale !== localeCfg.defaultLocale;\n const [draft, setDraft] = useState<DraftPage | null>(null);\n const [status, setStatus] = useState<Status>(\"loading\");\n const [selectedKey, setSelectedKey] = useState<string | null>(null);\n const [publishing, setPublishing] = useState(false);\n const [imgPicker, setImgPicker] = useState<{ key: string; field: string } | null>(null);\n const firstSave = useRef(true);\n const canvasRef = useRef<HTMLDivElement>(null);\n const [conflict, setConflict] = useState(false);\n\n // Drag-and-drop herordenen: dragIndex = het gesleepte blok, dropIndex = invoegpunt (0..length).\n // draggingRef pauzeert de inline-bind MutationObserver tijdens het slepen (re-bind-ruis).\n const [dragIndex, setDragIndex] = useState<number | null>(null);\n const [dropIndex, setDropIndex] = useState<number | null>(null);\n const draggingRef = useRef(false);\n const endDrag = useCallback(() => {\n draggingRef.current = false;\n setDragIndex(null);\n setDropIndex(null);\n }, []);\n\n // Altijd de laatste draft + \"zijn er niet-opgeslagen wijzigingen\" bij de hand houden, zodat we\n // bij sluiten/wegnavigeren nog kunnen flushen (de debounce zou de laatste 700ms anders weggooien).\n const draftRef = useRef<DraftPage | null>(null);\n const dirtyRef = useRef(false);\n // Inhoud (stable-stringified) van de laatst succesvol opgeslagen payload — voor conflictdetectie.\n const lastSavedRef = useRef<string | null>(null);\n useEffect(() => {\n draftRef.current = draft;\n }, [draft]);\n\n // ── Undo/redo ──────────────────────────────────────────────────────────────\n // History over draft-snapshots. Tekstedits (per toetsaanslag) worden ge-coalesced: binnen één\n // typ-burst (<800ms) staat het pre-burst-snapshot al op de stack, dus niet opnieuw pushen —\n // Ctrl+Z springt dan per burst terug i.p.v. per letter. `histVersion` forceert re-render van\n // de knop-states (de stacks leven in een ref).\n const historyRef = useRef<{ past: DraftPage[]; future: DraftPage[]; lastPush: number }>({ past: [], future: [], lastPush: 0 });\n const [, setHistVersion] = useState(0);\n\n const pushHistory = useCallback((prev: DraftPage, coalesce: boolean) => {\n const h = historyRef.current;\n const now = Date.now();\n if (!(coalesce && now - h.lastPush < 800 && h.past.length > 0)) {\n h.past.push(prev);\n if (h.past.length > 100) h.past.shift();\n }\n h.lastPush = now;\n h.future = [];\n setHistVersion((v) => v + 1);\n }, []);\n\n /** Enige route waarlangs de draft muteert: bewaart eerst een undo-snapshot. */\n const mutateDraft = useCallback(\n (updater: (d: DraftPage) => DraftPage, opts?: { coalesce?: boolean }) => {\n const d = draftRef.current;\n if (!d) return;\n const next = updater(d);\n if (next === d) return;\n pushHistory(d, opts?.coalesce ?? false);\n draftRef.current = next;\n // Dirty HIER markeren (synchroon met de mutatie), niet pas in het debounced autosave-effect:\n // een inline edit (blur) direct gevolgd door \"Sluiten\" in dezelfde gesture zou anders\n // flushSave() zien met dirtyRef=false → laatste wijziging verloren.\n dirtyRef.current = true;\n setDraft(next);\n },\n [pushHistory],\n );\n\n const undo = useCallback(() => {\n const h = historyRef.current;\n const d = draftRef.current;\n const prev = h.past.pop();\n if (!prev || !d) return;\n h.future.push(d);\n h.lastPush = 0;\n draftRef.current = prev;\n dirtyRef.current = true; // undo is óók een wijziging die opgeslagen moet worden\n setDraft(prev);\n setHistVersion((v) => v + 1);\n }, []);\n\n const redo = useCallback(() => {\n const h = historyRef.current;\n const d = draftRef.current;\n const next = h.future.pop();\n if (!next || !d) return;\n h.past.push(d);\n h.lastPush = 0;\n draftRef.current = next;\n dirtyRef.current = true; // redo is óók een wijziging die opgeslagen moet worden\n setDraft(next);\n setHistVersion((v) => v + 1);\n }, []);\n\n const draftPayload = (d: DraftPage) => ({\n blocks: d.blocks,\n title: d.title,\n slug: d.slug,\n path: d.path,\n seo: d.seo,\n // Alleen meesturen wanneer aanwezig: `undefined` weigert de Firestore-client.\n ...(d.translations ? { translations: d.translations } : {}),\n });\n\n // Schrijf eventuele pending wijziging direct weg (gebruikt bij Sluiten, tab-wissel en retry).\n const flushSave = useCallback(async () => {\n const d = draftRef.current;\n if (!d || !dirtyRef.current) return;\n const payload = draftPayload(d);\n await saveDraft(siteId, d.id, payload);\n lastSavedRef.current = stableStringify(payload);\n // Alleen \"schoon\" markeren als er intussen niets nieuws is getypt (anders zou een trage\n // write nieuwere wijzigingen als opgeslagen bestempelen).\n if (draftRef.current === d) {\n dirtyRef.current = false;\n setStatus(\"saved\");\n }\n }, [siteId]);\n\n const handleClose = useCallback(() => {\n void flushSave().finally(() => onClose());\n }, [flushSave, onClose]);\n\n useEffect(() => {\n let active = true;\n getDraftByPath(siteId, path)\n .then((d) => {\n if (!active) return;\n if (d) {\n setDraft(d);\n lastSavedRef.current = stableStringify(draftPayload(d));\n setStatus(\"idle\");\n } else {\n setStatus(\"none\");\n }\n })\n .catch(() => active && setStatus(\"none\"));\n return () => {\n active = false;\n };\n }, [siteId, path]);\n\n /** Bootstrap vanuit de \"Geen werkversie\"-toestand: maak de pagina hier direct aan (geen omweg\n * via /beheer → Pagina's — dat was een doodloop voor niet-technische klanten). */\n const handleCreatePage = useCallback(async () => {\n setStatus(\"loading\");\n try {\n const segment = path.split(\"/\").filter(Boolean).pop() ?? \"\";\n const title = segment ? segment.replace(/[-_]+/g, \" \").replace(/^\\w/, (c) => c.toUpperCase()) : \"Home\";\n await createPage(siteId, { title, slug: segment || \"home\", path });\n const d = await getDraftByPath(siteId, path);\n if (d) {\n setDraft(d);\n lastSavedRef.current = stableStringify(draftPayload(d));\n setStatus(\"idle\");\n toast({ title: \"Pagina aangemaakt\", description: \"Voeg je eerste blok toe en publiceer wanneer je klaar bent.\", tone: \"success\" });\n } else {\n setStatus(\"none\");\n }\n } catch (e) {\n setStatus(\"none\");\n toast({ title: \"Pagina aanmaken mislukt\", description: (e as Error)?.message, tone: \"error\" });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [siteId, path, toast]);\n\n // ── Conflictdetectie ───────────────────────────────────────────────────────\n // Luister naar de draft in Firestore. Een server-snapshot die afwijkt van zowel onze laatst\n // opgeslagen payload als onze huidige lokale staat, betekent: iemand anders (tab/collega)\n // heeft deze pagina gewijzigd. Waarschuw i.p.v. elkaars werk stil te overschrijven.\n const draftId = draft?.id;\n useEffect(() => {\n if (!draftId) return;\n return subscribeDraft(siteId, draftId, (remote, meta) => {\n if (!meta.fromServer || !remote) return;\n const remotePayload = stableStringify(draftPayload(remote));\n const local = draftRef.current;\n const localPayload = local ? stableStringify(draftPayload(local)) : null;\n if (remotePayload !== lastSavedRef.current && remotePayload !== localPayload) setConflict(true);\n });\n }, [siteId, draftId]);\n\n const blocks = (draft?.blocks ?? []) as Block[];\n // Canvas in een niet-default taal: toon de vertaal-overlay over de canonieke blokken.\n const overlay = draft?.translations?.[locale];\n const displayBlocks = useMemo(\n () => (isTranslating ? (localizeBlocks(blocks, overlay) as Block[]) : blocks),\n [blocks, isTranslating, overlay],\n );\n\n // Schrijf een inline (in-canvas) tekstwijziging terug naar het juiste blokveld (canoniek).\n const commitInline = useCallback(\n (key: string, field: string, value: string) => {\n mutateDraft(\n (d) => {\n const bl = d.blocks as Block[];\n const idx = bl.findIndex((x) => x._key === key);\n if (idx < 0 || getPath(bl[idx], field) === value) return d;\n const next = [...bl];\n next[idx] = setPath(next[idx], field, value);\n return { ...d, blocks: next };\n },\n { coalesce: true },\n );\n },\n [mutateDraft],\n );\n\n // Schrijf een vertaling naar de taal-overlay; leeg = sleutel weg (terugvallen op origineel).\n const setTranslation = useCallback(\n (tkey: string, value: string) => {\n mutateDraft(\n (d) => {\n const overlay = { ...(d.translations?.[locale] ?? {}) };\n if (value.trim() === \"\" || value === undefined) delete overlay[tkey];\n else overlay[tkey] = value;\n return { ...d, translations: { ...(d.translations ?? {}), [locale]: overlay } };\n },\n { coalesce: true },\n );\n },\n [mutateDraft, locale],\n );\n\n // ── Inline opmaak-toolbar (WYSIWYG op het canvas) ─────────────────────────\n // Zwevende mini-toolbar boven een gefocust rich-text-veld: vet/schuin/onderstrepen/link +\n // groter-kleiner via kop-niveaus (H2/H3/normaal — semantisch i.p.v. losse font-sizes).\n // document.execCommand is deprecated-maar-universeel en precies goed voor contentEditable;\n // de output gaat bij commit door DOMPurify én server-side door de allowlist-sanitizer.\n const [fmtBar, setFmtBar] = useState<{ top: number; left: number } | null>(null);\n const fmtElRef = useRef<HTMLElement | null>(null);\n const positionFmtBar = useCallback((el: HTMLElement) => {\n const r = el.getBoundingClientRect();\n setFmtBar({\n top: Math.max(56, r.top - 44),\n left: Math.min(Math.max(8, r.left), Math.max(8, window.innerWidth - 300)),\n });\n }, []);\n useEffect(() => {\n if (!fmtBar) return;\n const onScroll = () => {\n const el = fmtElRef.current;\n if (el) positionFmtBar(el);\n };\n window.addEventListener(\"scroll\", onScroll, { capture: true, passive: true });\n return () => window.removeEventListener(\"scroll\", onScroll, { capture: true });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [fmtBar !== null, positionFmtBar]);\n\n // Inline canvas-edits: in de default-taal naar het blokveld, in een andere taal naar de overlay.\n const commitText = useCallback(\n (key: string, field: string, value: string) => {\n if (isTranslating) setTranslation(`${key}.${field}`, value);\n else commitInline(key, field, value);\n },\n [isTranslating, setTranslation, commitInline],\n );\n\n // Maak de tekstvelden van het GESELECTEERDE blok ter plekke bewerkbaar (contentEditable) en synct\n // op blur terug — zo bewerk je tekst inline op het canvas i.p.v. via de zijbalk. We binden via een\n // MutationObserver én her-binden zodra React de DOM-node vervangt (re-render), met een dataset-flag\n // zodat we nooit dubbele listeners zetten.\n useEffect(() => {\n if (!selectedKey) return;\n const host = canvasRef.current?.querySelector<HTMLElement>(`[data-ve-content=\"${selectedKey}\"]`);\n if (!host) return;\n const type = blocks.find((b) => b._key === selectedKey)?._type ?? \"\";\n const specs = [...GLOBAL_INLINE];\n warnInlineCoverage(String(type), registry.get(String(type)), host);\n\n const bound = new Set<HTMLElement>();\n const bind = (el: HTMLElement, field: string, html: boolean) => {\n if (el.dataset.veInline === \"1\") return;\n el.dataset.veInline = \"1\";\n el.setAttribute(\"contenteditable\", \"true\");\n el.setAttribute(\"spellcheck\", \"true\");\n el.classList.add(\"xicms-ve-inline\");\n // Inline HTML wordt vóór commit gesanitized (DOMPurify) — zelfde belofte als het TipTap-pad;\n // de server sanitized nogmaals bij publiceren. Geplakte Word-/Docs-rommel blijft zo buiten.\n el.addEventListener(\"blur\", () => commitText(selectedKey, field, html ? DOMPurify.sanitize(el.innerHTML) : (el.textContent ?? \"\")));\n if (html) {\n // Opmaak-toolbar tonen zolang dit rich-text-veld focus heeft.\n el.addEventListener(\"focus\", () => {\n fmtElRef.current = el;\n positionFmtBar(el);\n });\n el.addEventListener(\"blur\", () => {\n fmtElRef.current = null;\n setFmtBar(null);\n });\n }\n el.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\") el.blur();\n else if (!html && e.key === \"Enter\") { e.preventDefault(); el.blur(); }\n });\n bound.add(el);\n };\n const apply = () => {\n for (const m of specs) {\n host.querySelectorAll<HTMLElement>(m.selector).forEach((el) =>\n bind(el, \"fromData\" in m && m.fromData ? (el.dataset.xicmsEdit as string) : m.field, !!m.html),\n );\n }\n };\n\n apply();\n // Her-bind na elke re-render die de tekstnodes vervangt (niet tijdens slepen: dan muteert de\n // DOM continu door de drop-indicatoren en zou her-binden alleen maar ruis geven).\n const obs = new MutationObserver(() => {\n if (!draggingRef.current) apply();\n });\n obs.observe(host, { childList: true, subtree: true });\n return () => {\n obs.disconnect();\n bound.forEach((el) => {\n el.removeAttribute(\"contenteditable\");\n el.classList.remove(\"xicms-ve-inline\");\n delete el.dataset.veInline;\n });\n };\n }, [selectedKey, blocks, commitText, positionFmtBar]);\n\n // Autosave naar de draft (publiceren blijft een aparte, bewuste stap).\n // Debounce 700ms ná de laatste wijziging, met een MAX-WAIT van 3s: de debounce herstart bij elke\n // toetsaanslag, dus zonder max-wait zou iemand die blijft doortypen nooit tussentijds opslaan.\n const dirtySinceRef = useRef<number | null>(null);\n useEffect(() => {\n if (!draft) return;\n if (firstSave.current) {\n firstSave.current = false;\n return;\n }\n // Bij een openstaand conflict PAUZEREN we autosave: de banner wegklikken en doorwerken\n // overschreef anders stil de wijzigingen van de collega/andere tab (destructief-by-default).\n // De klant kiest eerst expliciet \"Nieuwste laden\" of \"Mijn versie behouden\".\n if (conflict) return;\n dirtyRef.current = true;\n dirtySinceRef.current ??= Date.now();\n setStatus(\"saving\");\n const waited = Date.now() - dirtySinceRef.current;\n const delay = Math.max(0, Math.min(700, 3000 - waited));\n const t = setTimeout(() => {\n dirtySinceRef.current = null;\n const payload = draftPayload(draft);\n void saveDraft(siteId, draft.id, payload)\n .then(() => {\n lastSavedRef.current = stableStringify(payload);\n // Alleen \"schoon\"/\"saved\" als dit nog de nieuwste versie is (zie flushSave).\n if (draftRef.current === draft) {\n dirtyRef.current = false;\n setStatus(\"saved\");\n }\n })\n .catch(() => setStatus(\"error\"));\n }, delay);\n return () => clearTimeout(t);\n }, [draft, siteId, conflict]);\n\n // Waarschuw bij het verlaten van de pagina met niet-opgeslagen wijzigingen; flush bij tab-wissel/\n // verbergen (visibilitychange) en bij unmount. Escape sluit de editor (na flush).\n useEffect(() => {\n const beforeUnload = (e: BeforeUnloadEvent) => {\n if (dirtyRef.current) {\n e.preventDefault();\n e.returnValue = \"\";\n }\n };\n const onVisibility = () => {\n // Tab-wissel/minimaliseren: beste moment om pending werk veilig te stellen.\n if (document.visibilityState === \"hidden\") void flushSave().catch(() => {});\n };\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n // Escape sluit alléén de editor als er niets \"boven\" de editor actief is: een open\n // dialog (mediakiezer, bevestiging — Radix rendert [role=dialog] buiten deze shell) of\n // een inline bewerking/invoerveld vangt zijn eigen Escape. Anders verdween bij het\n // sluiten van de mediakiezer meteen de hele editor.\n const t = e.target as HTMLElement | null;\n const inField = !!t && (t.isContentEditable || t.tagName === \"INPUT\" || t.tagName === \"TEXTAREA\" || t.tagName === \"SELECT\");\n const dialogOpen = document.querySelector('[role=\"dialog\"][data-state=\"open\"], [role=\"alertdialog\"][data-state=\"open\"]');\n if (!inField && !dialogOpen) handleClose();\n return;\n }\n // Undo/redo op canvas-niveau. In invoervelden/contentEditable laten we de native\n // tekst-undo van de browser zijn werk doen (anders vecht Ctrl+Z met het veld zelf).\n const t = e.target as HTMLElement | null;\n const inField = !!t && (t.isContentEditable || t.tagName === \"INPUT\" || t.tagName === \"TEXTAREA\" || t.tagName === \"SELECT\");\n if ((e.metaKey || e.ctrlKey) && !inField && (e.key.toLowerCase() === \"z\" || e.key.toLowerCase() === \"y\")) {\n e.preventDefault();\n if (e.key.toLowerCase() === \"y\" || e.shiftKey) redo();\n else undo();\n }\n };\n window.addEventListener(\"beforeunload\", beforeUnload);\n document.addEventListener(\"visibilitychange\", onVisibility);\n window.addEventListener(\"keydown\", onKeyDown);\n return () => {\n window.removeEventListener(\"beforeunload\", beforeUnload);\n document.removeEventListener(\"visibilitychange\", onVisibility);\n window.removeEventListener(\"keydown\", onKeyDown);\n const d = draftRef.current;\n if (d && dirtyRef.current) {\n // Laatste redmiddel bij unmount; log een mislukking i.p.v. stil verliezen.\n void saveDraft(siteId, d.id, draftPayload(d)).catch((err) =>\n console.warn(\"[xicms] autosave bij sluiten mislukt:\", err),\n );\n }\n };\n }, [siteId, handleClose, flushSave, undo, redo]);\n\n function setBlocks(next: Block[], opts?: { coalesce?: boolean }) {\n mutateDraft((d) => ({ ...d, blocks: next }), opts);\n }\n function updateByKey(key: string, next: Record<string, unknown>) {\n // Zijpaneel-edits komen per toetsaanslag binnen → coalesce voor zinnige undo-stappen.\n setBlocks(blocks.map((b) => (b._key === key ? ({ ...(next as Block), _key: b._key, _type: b._type }) : b)), { coalesce: true });\n }\n function move(i: number, dir: -1 | 1) {\n const j = i + dir;\n if (j < 0 || j >= blocks.length) return;\n const copy = [...blocks];\n [copy[i], copy[j]] = [copy[j], copy[i]];\n setBlocks(copy);\n }\n function duplicate(i: number) {\n const copy = [...blocks];\n copy.splice(i + 1, 0, { ...blocks[i], _key: newKey() });\n setBlocks(copy);\n }\n /** Symbool loskoppelen (5.0): vervang de referentie door een kloon van de symboolblokken —\n * daarna beweegt dit exemplaar niet meer mee met het gedeelde symbool. */\n async function detachSymbol(i: number) {\n const blk = blocks[i] as { _type?: string; symbolId?: string; _key: string };\n if (blk._type !== \"symbolRef\" || !blk.symbolId) return;\n const ok = await confirm({\n title: \"Symbool loskoppelen?\",\n description: \"Dit blok wordt een losse kopie en beweegt niet meer mee met het gedeelde symbool.\",\n confirmLabel: \"Loskoppelen\",\n });\n if (!ok) return;\n const { getSymbolOnce } = await import(\"../lib/symbolsClient\");\n const sym = await getSymbolOnce(siteId, blk.symbolId);\n if (!sym) {\n toast({ title: \"Symbool niet gevonden\", tone: \"error\" });\n return;\n }\n const clones = sym.blocks.map((b) => ({ ...(b as Record<string, unknown>), _key: newKey() })) as Block[];\n const copy = [...blocks];\n copy.splice(i, 1, ...clones);\n setBlocks(copy);\n if (selectedKey === blk._key) setSelectedKey(null);\n toast({ title: \"Losgekoppeld\", description: \"Dit is nu een losse kopie.\", tone: \"success\" });\n }\n async function remove(i: number) {\n const ok = await confirm({ title: \"Blok verwijderen?\", confirmLabel: \"Verwijderen\", tone: \"danger\" });\n if (!ok) return;\n const removed = blocks[i];\n setBlocks(blocks.filter((_, idx) => idx !== i));\n if (selectedKey === removed._key) setSelectedKey(null);\n }\n function addBlock(type: string) {\n const def = registry.get(type);\n const base = def?.defaults?.() ?? {};\n const block = { _key: newKey(), _type: type, ...base } as Block;\n insertAfterSelected([block]);\n setSelectedKey(block._key);\n }\n function insertAfterSelected(newBlocks: Array<Record<string, unknown>>) {\n const sel = blocks.findIndex((b) => b._key === selectedKey);\n const copy = [...blocks];\n copy.splice(sel >= 0 ? sel + 1 : blocks.length, 0, ...(newBlocks as Block[]));\n setBlocks(copy);\n }\n\n async function onPublish() {\n if (!draft) return;\n // Openstaand conflict eerst oplossen: publiceren zou anders een stale merge live zetten.\n if (conflict) {\n toast({ title: \"Los eerst het conflict op\", description: \"Kies 'Nieuwste versie laden' of 'Mijn versie behouden' in de gele balk.\", tone: \"error\" });\n return;\n }\n const ok = await confirm({ title: \"Pagina publiceren?\", description: `De live site wordt bijgewerkt (${draft.path}).`, confirmLabel: \"Publiceren\" });\n if (!ok) return;\n setPublishing(true);\n try {\n await saveDraft(siteId, draft.id, draftPayload(draft));\n await publishPage(siteId, draft.id);\n toast({ title: \"Gepubliceerd\", description: \"Herlaad de pagina om het live resultaat te zien.\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Publiceren mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setPublishing(false);\n }\n }\n\n const addOptions = useMemo(() => buildBlockAddOptions(registry.values()), [registry]);\n\n const selectedIndex = blocks.findIndex((b) => b._key === selectedKey);\n const selected = selectedIndex >= 0 ? blocks[selectedIndex] : null;\n const canUndo = historyRef.current.past.length > 0;\n const canRedo = historyRef.current.future.length > 0;\n\n /** Laad de nieuwste (remote) versie in de editor — bij een gedetecteerd conflict. */\n const reloadRemote = useCallback(async () => {\n const remote = await getDraftByPath(siteId, path).catch(() => null);\n if (remote) {\n mutateDraft(() => remote);\n lastSavedRef.current = stableStringify(draftPayload(remote));\n dirtyRef.current = false;\n setStatus(\"idle\");\n }\n setConflict(false);\n }, [siteId, path, mutateDraft]);\n\n return (\n <div className=\"xicms-ve__shell\" role=\"dialog\" aria-modal=\"true\" aria-label={`Pagina bewerken: ${path}`}>\n <header className=\"xicms-ve__bar\">\n <strong className=\"xicms-ve__title\">Bewerken · {name}</strong>\n <span className=\"xicms-ve__path\">{path}</span>\n {localeCfg.locales.length > 1 && (\n <span className=\"xicms-ve__locale\" title=\"Taal — in een andere taal dan de standaard bewerk je vertalingen\">\n 🌐\n <Select\n value={locale}\n onValueChange={setLocale}\n options={localeCfg.locales.map((l) => ({ value: l, label: l === localeCfg.defaultLocale ? `${l} (standaard)` : l }))}\n />\n </span>\n )}\n <button type=\"button\" className=\"xicms-ve__histbtn\" disabled={!canUndo} onClick={undo} title=\"Ongedaan maken (Ctrl+Z)\" aria-label=\"Ongedaan maken (Ctrl+Z)\">↶</button>\n <button type=\"button\" className=\"xicms-ve__histbtn\" disabled={!canRedo} onClick={redo} title=\"Opnieuw (Ctrl+Shift+Z)\" aria-label=\"Opnieuw (Ctrl+Shift+Z)\">↷</button>\n <span className=\"xicms-ve__status\" aria-live=\"polite\">\n {status === \"saving\" ? \"Opslaan…\" : status === \"saved\" ? \"✓ Opgeslagen\" : status === \"error\" ? \"⚠ Niet opgeslagen\" : \"\"}\n </span>\n {status === \"error\" && (\n <Button size=\"sm\" variant=\"secondary\" onClick={() => void flushSave().catch(() => setStatus(\"error\"))}>\n Opnieuw opslaan\n </Button>\n )}\n <div className=\"xicms-ve__bar-actions\">\n <Button size=\"sm\" disabled={publishing || !draft} onClick={onPublish}>\n {publishing ? \"Bezig…\" : \"Publiceren\"}\n </Button>\n <Button size=\"sm\" variant=\"secondary\" onClick={handleClose}>Sluiten</Button>\n </div>\n </header>\n\n {conflict && (\n <div className=\"xicms-ve__conflict\" role=\"alert\">\n <span>⚠ Deze pagina is intussen ergens anders gewijzigd (andere tab of collega). Doorwerken kan die wijzigingen overschrijven.</span>\n <Button size=\"sm\" onClick={() => void reloadRemote()}>Nieuwste versie laden</Button>\n <Button size=\"sm\" variant=\"secondary\" onClick={() => setConflict(false)}>Mijn versie behouden</Button>\n </div>\n )}\n\n <div className=\"xicms-ve__body\">\n <main className=\"xicms-ve__canvas\">\n {status === \"loading\" && <p className=\"xicms-ve__hint\">Laden…</p>}\n {status === \"none\" && (\n <div className=\"xicms-ve__empty\">\n <EmptyState\n icon=\"📄\"\n title=\"Geen werkversie gevonden\"\n description={`Voor deze pagina (${path}) bestaat nog geen werkversie. Maak hem hier direct aan, of ga naar /beheer → Pagina's.`}\n />\n <p style={{ textAlign: \"center\", marginTop: \"1rem\" }}>\n <button type=\"button\" className=\"xicms-btn\" onClick={() => void handleCreatePage()}>\n + Deze pagina aanmaken\n </button>\n </p>\n </div>\n )}\n {draft && (\n <div className=\"container\" ref={canvasRef}>\n {blocks.length === 0 && (\n <EmptyState icon=\"🧱\" title=\"Nog geen blokken\" description=\"Voeg hieronder je eerste blok toe.\" />\n )}\n {displayBlocks.map((b, i) => {\n const def = registry.get(b._type);\n const Render = def?.Render;\n // Coerce via het schema zodat defaults zijn toegepast (zoals de live pagina via\n // validateBlocks doet). Ruwe drafts missen anders defaults (bv. hero.buttons) en\n // zouden de Render laten throwen. Lukt het parsen niet, val terug op het ruwe blok\n // (de error-boundary hieronder vangt een eventuele fout dan netjes op).\n const safe = (() => {\n const parsed = def?.schema?.safeParse(b);\n return parsed?.success ? (parsed.data as Block) : b;\n })();\n const dropClass = dropIndex === i ? \" is-drop-before\" : dropIndex === i + 1 ? \" is-drop-after\" : \"\";\n return (\n <div\n key={b._key}\n className={`xicms-ve-block${b._key === selectedKey ? \" is-selected\" : \"\"}${dragIndex === i ? \" is-dragging\" : \"\"}${dropClass}`}\n onDragOver={(e) => {\n if (dragIndex === null) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n const rect = e.currentTarget.getBoundingClientRect();\n setDropIndex(e.clientY < rect.top + rect.height / 2 ? i : i + 1);\n }}\n onDrop={(e) => {\n if (dragIndex === null || dropIndex === null) return;\n e.preventDefault();\n setBlocks(reorderBlocks(blocks, dragIndex, dropIndex));\n endDrag();\n }}\n >\n <div className=\"xicms-ve-block__toolbar\">\n {/* Sleep-greep: apart element (niet het hele blok) zodat slepen nooit botst met\n het inline contentEditable-bewerken; ↑/↓ blijven het toetsenbord-alternatief. */}\n <button\n type=\"button\"\n className=\"xicms-ve-block__drag\"\n draggable\n onDragStart={(e) => {\n draggingRef.current = true;\n setDragIndex(i);\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setData(\"text/plain\", String(i));\n const blockEl = e.currentTarget.closest(\".xicms-ve-block\");\n if (blockEl) e.dataTransfer.setDragImage(blockEl, 24, 24);\n }}\n onDragEnd={endDrag}\n aria-label={`Versleep ${def?.label ?? b._type}`}\n title=\"Slepen om te verplaatsen\"\n >\n ⋮⋮\n </button>\n <span className=\"xicms-ve-block__label\">{def?.label ?? b._type}</span>\n <button type=\"button\" onClick={() => setSelectedKey(b._key)} aria-label={`Bewerk ${def?.label ?? b._type}`} title=\"Bewerken\">✏️</button>\n <button type=\"button\" disabled={i === 0} onClick={() => move(i, -1)} aria-label=\"Omhoog\">↑</button>\n <button type=\"button\" disabled={i === blocks.length - 1} onClick={() => move(i, 1)} aria-label=\"Omlaag\">↓</button>\n {b._type === \"symbolRef\" && (\n <button type=\"button\" onClick={() => void detachSymbol(i)} aria-label=\"Symbool loskoppelen\" title=\"Loskoppelen (losse kopie)\">🔗✂</button>\n )}\n <button type=\"button\" onClick={() => duplicate(i)} aria-label=\"Dupliceren\">⎘</button>\n <button type=\"button\" onClick={() => void remove(i)} aria-label=\"Verwijderen\">🗑</button>\n </div>\n {/* Altijd renderen (geen conditie) → geen remount van de inhoud bij selecteren,\n anders verliest het inline-bewerken zijn contentEditable. Bij selectie zet de\n CSS deze overlay op pointer-events:none zodat je de tekst kunt bewerken. */}\n <button type=\"button\" className=\"xicms-ve-block__select\" aria-label={`Selecteer ${def?.label ?? b._type}`} onClick={() => setSelectedKey(b._key)} />\n <div className=\"xicms-ve-block__content\" data-ve-content={b._key}>\n {/* Net als BlockRenderer: elk blok in een per-blok error-boundary zodat één\n falend (maatwerk-)blok de hele editor niet blank maakt. mode=\"preview\". */}\n {Render ? (\n <BlockErrorBoundary mode=\"preview\" type={b._type} blockKey={b._key}>\n {/* Alleen het geselecteerde blok krijgt de inline beeld-vervangen-overlay. */}\n <InlineImageEditProvider onReplace={b._key === selectedKey ? (field) => setImgPicker({ key: b._key, field }) : null}>\n <Render block={safe} siteId={siteId} mode=\"preview\" />\n </InlineImageEditProvider>\n </BlockErrorBoundary>\n ) : (\n <p className=\"xicms-ve__hint\">Onbekend blok: {b._type}</p>\n )}\n </div>\n </div>\n );\n })}\n <div className=\"xicms-ve__inserter\">\n <span>Toevoegen{selected ? \" (na selectie)\" : \"\"}:</span>\n <div className=\"xicms-ve__inserter-select\">\n <Select value=\"\" placeholder=\"Kies een blok…\" onValueChange={(v) => v && addBlock(v)} options={addOptions} />\n </div>\n <PatternInserter onInsert={insertAfterSelected} />\n </div>\n </div>\n )}\n </main>\n\n <aside className=\"xicms-ve__panel\">\n {selected && isTranslating ? (\n <div className=\"grid gap-3\">\n <h3 className=\"text-sm font-semibold\">{registry.get(selected._type)?.label ?? selected._type} — vertaling ({locale})</h3>\n <TranslationPanel\n key={`${selected._key}:${locale}`}\n block={selected}\n siteId={siteId}\n registry={registry}\n locale={locale}\n overlay={overlay ?? {}}\n onTranslate={setTranslation}\n />\n </div>\n ) : selected ? (\n <div className=\"grid gap-3\">\n <div className=\"xicms-ve__panel-head\">\n <button\n type=\"button\"\n className=\"xicms-btn xicms-btn--ghost\"\n onClick={() => void flushSave().catch(() => setStatus(\"error\"))}\n >\n ✓ Opslaan\n </button>\n <button type=\"button\" className=\"xicms-btn xicms-btn--ghost\" onClick={() => setSelectedKey(null)}>\n ✕ Sluiten\n </button>\n </div>\n <SelectedEditor key={selected._key} block={selected} siteId={siteId} registry={registry} onChange={(next) => updateByKey(selected._key, next)} />\n </div>\n ) : draft && isTranslating ? (\n <div className=\"grid gap-4\">\n <p className=\"xicms-ve__hint\">\n Je bewerkt de vertaling <strong>{locale}</strong>. Klik op een blok om zijn teksten te\n vertalen; lege velden vallen terug op de standaardtaal. Blokken toevoegen/verplaatsen\n doe je in de standaardtaal.\n </p>\n <PageTranslationSettings draft={draft} locale={locale} onTranslate={setTranslation} />\n </div>\n ) : draft ? (\n <div className=\"grid gap-4\">\n <p className=\"xicms-ve__hint\">Klik op een blok om het te bewerken.</p>\n <PageSettings draft={draft} onChange={(part) => mutateDraft((d) => ({ ...d, ...part }), { coalesce: true })} />\n {!isDemoActive() && (\n <VersionsPanel\n siteId={siteId}\n pageId={draft.id}\n onRestore={(restored) => {\n mutateDraft((d) => ({ ...d, ...restored }));\n toast({ title: \"Versie hersteld naar werkversie\", description: \"Publiceer om hem live te zetten.\", tone: \"success\" });\n }}\n />\n )}\n </div>\n ) : (\n <p className=\"xicms-ve__hint\">Klik op een blok om het te bewerken.</p>\n )}\n </aside>\n </div>\n\n {/* Inline afbeelding vervangen: opent de mediabibliotheek; alleen de URL van het veld wordt gezet. */}\n <MediaPickerDialog\n siteId={siteId}\n open={!!imgPicker}\n onOpenChange={(v) => { if (!v) setImgPicker(null); }}\n onSelect={(m) => {\n if (imgPicker) {\n commitInline(imgPicker.key, imgPicker.field, m.url);\n // Alt-tekst van de gekozen media meenemen (a11y/SEO): conventie url→alt / imageUrl→alt\n // op hetzelfde niveau. Bestaat het veld niet in het schema, dan stript Zod het bij\n // publiceren — nooit schadelijk, meestal precies goed (Image/Gallery/Cards/Slider).\n const altField = imgPicker.field.replace(/(^|\\.)((image)?[uU]rl)$/, \"$1alt\");\n if (altField !== imgPicker.field) commitInline(imgPicker.key, altField, m.alt ?? \"\");\n }\n setImgPicker(null);\n }}\n />\n\n {/* Zwevende opmaak-toolbar boven het gefocuste inline rich-text-veld. onMouseDown-preventDefault\n houdt focus + selectie in het veld, zodat execCommand op de selectie werkt. */}\n {fmtBar && (\n <div className=\"xicms-ve-fmtbar\" style={{ top: fmtBar.top, left: fmtBar.left }} role=\"toolbar\" aria-label=\"Tekstopmaak\">\n <FmtBtn label={<b>B</b>} title=\"Vet (Ctrl/Cmd+B)\" onRun={() => document.execCommand(\"bold\")} />\n <FmtBtn label={<i>I</i>} title=\"Cursief (Ctrl/Cmd+I)\" onRun={() => document.execCommand(\"italic\")} />\n <FmtBtn label={<u>U</u>} title=\"Onderstrepen (Ctrl/Cmd+U)\" onRun={() => document.execCommand(\"underline\")} />\n <FmtBtn\n label=\"🔗\"\n title=\"Link (selecteer eerst tekst; leeg = link verwijderen)\"\n onRun={() => {\n const url = window.prompt(\"Link-URL (leeg = link verwijderen)\", \"https://\");\n if (url === null) return;\n if (url === \"\" || url === \"https://\") document.execCommand(\"unlink\");\n else document.execCommand(\"createLink\", false, url);\n }}\n />\n <span className=\"xicms-ve-fmtbar__sep\" aria-hidden />\n <FmtBtn label=\"H2\" title=\"Grote kop\" onRun={() => document.execCommand(\"formatBlock\", false, \"<h2>\")} />\n <FmtBtn label=\"H3\" title=\"Kleine kop\" onRun={() => document.execCommand(\"formatBlock\", false, \"<h3>\")} />\n <FmtBtn label=\"¶\" title=\"Normale tekst\" onRun={() => document.execCommand(\"formatBlock\", false, \"<p>\")} />\n <span className=\"xicms-ve-fmtbar__sep\" aria-hidden />\n <FmtBtn label=\"•\" title=\"Opsomming\" onRun={() => document.execCommand(\"insertUnorderedList\")} />\n </div>\n )}\n </div>\n );\n}\n\n/** Toolbar-knop die de selectie/focus in het contentEditable-veld intact laat. */\nfunction FmtBtn({ label, title, onRun }: { label: React.ReactNode; title: string; onRun: () => void }) {\n return (\n <button\n type=\"button\"\n title={title}\n aria-label={title}\n onMouseDown={(e) => e.preventDefault()}\n onClick={onRun}\n >\n {label}\n </button>\n );\n}\n\n/**\n * Pagina-instellingen (titel + SEO) direct in de visuele editor — de klant hoeft niet meer naar\n * /beheer voor een titel- of meta-omschrijving-wijziging. Slug/pad blijven bewust in /beheer\n * (die raken URL-structuur en zijn niet iets om per ongeluk inline aan te passen).\n */\nfunction PageSettings({\n draft,\n onChange,\n}: {\n draft: DraftPage;\n onChange: (part: Partial<DraftPage>) => void;\n}) {\n const seo = draft.seo ?? {};\n return (\n <details className=\"xicms-ve__section\" open>\n <summary className=\"text-sm font-semibold\">Pagina-instellingen</summary>\n <div className=\"mt-2 grid gap-3\">\n <Field label=\"Paginatitel\">\n <Input value={draft.title ?? \"\"} onChange={(e) => onChange({ title: e.target.value })} />\n </Field>\n <Field label=\"SEO-titel\" hint=\"Leeg = paginatitel\">\n <Input value={seo.title ?? \"\"} onChange={(e) => onChange({ seo: { ...seo, title: e.target.value } })} />\n </Field>\n <Field label=\"SEO-omschrijving\" hint=\"Wordt getoond in zoekresultaten\">\n <Textarea rows={3} value={seo.description ?? \"\"} onChange={(e) => onChange({ seo: { ...seo, description: e.target.value } })} />\n </Field>\n </div>\n </details>\n );\n}\n\n/** Pagina-instellingen in vertaalmodus (5.0): titel + SEO als overlay-sleutels. */\nfunction PageTranslationSettings({\n draft,\n locale,\n onTranslate,\n}: {\n draft: DraftPage;\n locale: string;\n onTranslate: (key: string, value: string) => void;\n}) {\n const ov = draft.translations?.[locale] ?? {};\n return (\n <details className=\"xicms-ve__section\" open>\n <summary className=\"text-sm font-semibold\">Pagina-instellingen ({locale})</summary>\n <div className=\"mt-2 grid gap-3\">\n <Field label=\"Paginatitel\" hint={`Origineel: ${draft.title ?? \"\"}`}>\n <Input value={ov[\"title\"] ?? \"\"} placeholder={draft.title ?? \"\"} onChange={(e) => onTranslate(\"title\", e.target.value)} />\n </Field>\n <Field label=\"SEO-titel\">\n <Input value={ov[\"seo.title\"] ?? \"\"} placeholder={draft.seo?.title ?? \"\"} onChange={(e) => onTranslate(\"seo.title\", e.target.value)} />\n </Field>\n <Field label=\"SEO-omschrijving\">\n <Textarea rows={3} value={ov[\"seo.description\"] ?? \"\"} placeholder={draft.seo?.description ?? \"\"} onChange={(e) => onTranslate(\"seo.description\", e.target.value)} />\n </Field>\n </div>\n </details>\n );\n}\n\n/** Versiegeschiedenis in de visuele editor: herstellen zet een snapshot terug naar de WERKVERSIE. */\nfunction VersionsPanel({\n siteId,\n pageId,\n onRestore,\n}: {\n siteId: string;\n pageId: string;\n onRestore: (restored: Partial<DraftPage>) => void;\n}) {\n const [versions, setVersions] = useState<{ id: string; createdAt?: unknown; kind?: string; label?: string }[]>([]);\n const { toast } = useToast();\n const confirm = useConfirm();\n useEffect(() => subscribeVersions(siteId, pageId, setVersions), [siteId, pageId]);\n if (versions.length === 0) return null;\n\n const restore = async (versionId: string) => {\n const ok = await confirm({\n title: \"Versie herstellen?\",\n description: \"De snapshot vervangt je huidige werkversie (ongedaan te maken met Ctrl+Z). De live site verandert pas bij Publiceren.\",\n confirmLabel: \"Herstellen\",\n });\n if (!ok) return;\n try {\n const restored = await restoreVersion(siteId, pageId, versionId);\n onRestore(restored);\n } catch (e) {\n toast({ title: \"Herstellen mislukt\", description: (e as Error).message, tone: \"error\" });\n }\n };\n\n return (\n <details className=\"xicms-ve__section\">\n <summary className=\"text-sm font-semibold\">Versiegeschiedenis</summary>\n <ul className=\"mt-2 grid gap-1 text-sm\">\n {versions.slice(0, 10).map((v) => (\n <li key={v.id} className=\"flex items-center justify-between gap-2\">\n <span>\n {v.label ?? (v.kind === \"publish\" ? \"Gepubliceerd\" : v.kind ?? \"Snapshot\")}\n {formatVersionDate(v.createdAt) && <span className=\"text-slate-400\"> · {formatVersionDate(v.createdAt)}</span>}\n </span>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void restore(v.id)}>\n Herstellen\n </Button>\n </li>\n ))}\n </ul>\n </details>\n );\n}\n\nfunction formatVersionDate(createdAt: unknown): string {\n const date =\n createdAt && typeof (createdAt as { toDate?: () => Date }).toDate === \"function\"\n ? (createdAt as { toDate: () => Date }).toDate()\n : typeof createdAt === \"string\"\n ? new Date(createdAt)\n : null;\n return date && !Number.isNaN(date.getTime()) ? date.toLocaleString(\"nl-NL\", { dateStyle: \"short\", timeStyle: \"short\" }) : \"\";\n}\n\nfunction SelectedEditor({\n block,\n siteId,\n registry,\n onChange,\n}: {\n block: Block;\n siteId: string;\n registry: ReturnType<typeof useRegistry>;\n onChange: (next: Record<string, unknown>) => void;\n}) {\n const def = registry.get(block._type);\n const Editor: ComponentType<BlockEditorProps> =\n (def?.Editor as ComponentType<BlockEditorProps> | undefined) ?? builtinBlockEditors[block._type] ?? GenericEditor;\n return (\n <div className=\"grid gap-3\">\n <h3 className=\"text-sm font-semibold\">{def?.label ?? block._type}</h3>\n {/* Maatwerk-Editors kunnen falen → vang per-blok op zodat het paneel niet leeg wordt. */}\n <BlockErrorBoundary mode=\"preview\" type={block._type} blockKey={block._key}>\n <Editor block={block} siteId={siteId} onChange={onChange} />\n </BlockErrorBoundary>\n <StyleEditor\n value={block._style as BlockStyle | undefined}\n onChange={(style) => onChange({ ...block, _style: style })}\n />\n </div>\n );\n}\n"],"mappings":"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","names":["locale","overlay","t","inField"]}
1
+ {"version":3,"sources":["../../../src/admin/visual/VisualEditor.tsx"],"sourcesContent":["\"use client\";\n\n// De admin-stijlen (Tailwind, scoped onder .xicms-admin) horen bij dit lazy-geladen chunk, zodat\n// publieke bezoekers ze nooit downloaden — alleen wanneer een editor de pagina-editor opent.\nimport \"@xiwebdesign/xicms-core/admin.css\";\nimport { useCallback, useEffect, useMemo, useRef, useState, type ComponentType } from \"react\";\nimport { useSiteConfig } from \"../../config\";\nimport { useRegistry } from \"../../blocks/context\";\nimport { introspectToFields } from \"../lib/zodFields\";\nimport { BlockErrorBoundary } from \"../../blocks/BlockErrorBoundary\";\nimport { InlineImageEditProvider } from \"../../media/ImageContext\";\nimport { MediaPickerDialog } from \"../media/MediaPicker\";\nimport { EmptyState } from \"@astryxdesign/core/EmptyState\";\nimport { buildBlockAddOptions } from \"../../blocks/registry\";\nimport DOMPurify from \"dompurify\";\nimport { createPage, getDraftByPath, saveDraft, publishPage, subscribeDraft, subscribeVersions, restoreVersion, type DraftPage } from \"../lib/pagesClient\";\nimport { isDemoActive } from \"../../demo/demoStore\";\nimport { builtinBlockEditors, GenericEditor, type BlockEditorProps } from \"../blockEditors\";\nimport { StyleEditor } from \"../blockEditors/StyleEditor\";\nimport { siteLocales, parseLocaleSlugs, localizeBlocks } from \"../../i18n/overlay\";\nimport { TranslationPanel } from \"./TranslationPanel\";\nimport { reorderBlocks } from \"./reorder\";\nimport { PatternInserter } from \"../patterns/PatternInserter\";\nimport type { BlockStyle } from \"../../schema/blocks\";\nimport { Button, Field, Input, Textarea } from \"../ui/primitives\";\nimport { Select } from \"../ui/select\";\nimport { ToastProvider, useToast } from \"../ui/toast\";\nimport { ConfirmHost, useConfirm } from \"../ui/ConfirmDialog\";\n\ntype Block = Record<string, unknown> & { _key: string; _type: string };\ntype Status = \"loading\" | \"none\" | \"idle\" | \"saving\" | \"saved\" | \"error\";\n\n/**\n * Inline bewerken is sinds 5.0 volledig generiek: élk blok markeert zijn tekstvelden met\n * `data-xicms-edit=\"veldpad\"` (de builtin-Renders doen dat), plus de rich-text-haak\n * `.xicms-richtext`. De oude hardgecodeerde per-type selector-map (`INLINE_FIELDS`) is vervallen.\n * In development waarschuwt `warnInlineCoverage` wanneer een tekstveld uit het schema géén\n * DOM-haak heeft — zo blijven Renders eerlijk t.o.v. hun schema.\n */\nconst GLOBAL_INLINE = [\n { selector: \".xicms-richtext\", field: \"html\", html: true as const },\n { selector: \"[data-xicms-edit]\", field: \"\", html: false as const, fromData: true as const },\n];\n\n/** Dev-vangnet (5.0): meld tekstvelden zonder inline-haak — één keer per bloktype. */\nconst inlineCoverageWarned = new Set<string>();\nfunction warnInlineCoverage(type: string, def: { schema: Parameters<typeof introspectToFields>[0]; fields?: Parameters<typeof introspectToFields>[1] } | undefined, host: HTMLElement) {\n if (process.env.NODE_ENV === \"production\" || !def || inlineCoverageWarned.has(type)) return;\n inlineCoverageWarned.add(type);\n const missing = introspectToFields(def.schema, def.fields)\n .filter((f) => f.type === \"text\" || f.type === \"textarea\")\n .map((f) => f.key)\n .filter((key) => !host.querySelector(`[data-xicms-edit=\"${key}\"], [data-xicms-edit$=\".${key}\"]`));\n if (missing.length > 0) {\n console.warn(\n `[xicms] blok \"${type}\": tekstveld(en) zonder inline-haak: ${missing.join(\", \")} — voeg data-xicms-edit=\"<veld>\" toe aan de Render zodat ze direct op de pagina bewerkbaar zijn.`,\n );\n }\n}\n\n/* Lees/schrijf een waarde op een gepunt pad (bv. \"items.0.title\") — zodat ook lijst-items inline\n bewerkt kunnen worden. setPath kloont onderweg (immutable) zodat React de wijziging detecteert. */\nfunction getPath(obj: unknown, path: string): unknown {\n return path.split(\".\").reduce<unknown>((o, k) => (o == null ? undefined : (o as Record<string, unknown>)[k]), obj);\n}\nfunction setPath<T>(obj: T, path: string, value: unknown): T {\n const keys = path.split(\".\");\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const clone = (v: unknown): any => (Array.isArray(v) ? [...v] : v && typeof v === \"object\" ? { ...(v as object) } : v);\n const root = clone(obj);\n let cur = root;\n for (let i = 0; i < keys.length - 1; i++) {\n cur[keys[i]] = clone(cur[keys[i]] ?? {});\n cur = cur[keys[i]];\n }\n cur[keys[keys.length - 1]] = value;\n return root as T;\n}\n\nfunction newKey() {\n return (globalThis.crypto?.randomUUID?.() ?? `b${Date.now()}${Math.random()}`).slice(0, 12);\n}\n\n/** JSON.stringify met gesorteerde sleutels — voor betrouwbare inhoudsvergelijking (conflictdetectie):\n * Firestore kan velden in een andere volgorde teruggeven dan wij ze lokaal opbouwden. */\nfunction stableStringify(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : 1));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n\n/**\n * Visuele/inline pagina-editor: opent als overlay over de live pagina, laadt de werkversie van de\n * huidige route, en laat de editor blokken op een live canvas selecteren, herordenen, dupliceren,\n * verwijderen en toevoegen — met autosave en publiceren. De zware code laadt pas bij openen\n * (lazy, via EditorProvider), dus bezoekers downloaden hem nooit.\n */\nexport default function VisualEditor({ onClose }: { onClose: () => void }) {\n return (\n <div className=\"xicms-admin xicms-ve\">\n <ToastProvider>\n <ConfirmHost>\n <VisualEditorInner onClose={onClose} />\n </ConfirmHost>\n </ToastProvider>\n </div>\n );\n}\n\nfunction VisualEditorInner({ onClose }: { onClose: () => void }) {\n const config = useSiteConfig();\n const { siteId, name } = config;\n const registry = useRegistry();\n const { toast } = useToast();\n const confirm = useConfirm();\n\n // Meertalig (5.0): een taalprefix in de URL (/de/over-ons) hoort niet bij het canonieke\n // pagina-pad — de editor opent dan de pagina zélf, met de betreffende taal actief.\n const localeCfg = useMemo(() => siteLocales(config), [config]);\n const { path, urlLocale } = useMemo(() => {\n const raw = typeof window !== \"undefined\" ? window.location.pathname || \"/\" : \"/\";\n const { locale, slugs } = parseLocaleSlugs(raw.split(\"/\").filter(Boolean), localeCfg);\n const p = \"/\" + slugs.join(\"/\");\n return { path: p === \"/\" ? \"/\" : p.replace(/\\/$/, \"\"), urlLocale: locale };\n }, [localeCfg]);\n const [locale, setLocale] = useState(urlLocale);\n const isTranslating = locale !== localeCfg.defaultLocale;\n const [draft, setDraft] = useState<DraftPage | null>(null);\n const [status, setStatus] = useState<Status>(\"loading\");\n const [selectedKey, setSelectedKey] = useState<string | null>(null);\n const [publishing, setPublishing] = useState(false);\n const [imgPicker, setImgPicker] = useState<{ key: string; field: string } | null>(null);\n const firstSave = useRef(true);\n const canvasRef = useRef<HTMLDivElement>(null);\n const [conflict, setConflict] = useState(false);\n\n // Drag-and-drop herordenen: dragIndex = het gesleepte blok, dropIndex = invoegpunt (0..length).\n // draggingRef pauzeert de inline-bind MutationObserver tijdens het slepen (re-bind-ruis).\n const [dragIndex, setDragIndex] = useState<number | null>(null);\n const [dropIndex, setDropIndex] = useState<number | null>(null);\n const draggingRef = useRef(false);\n const endDrag = useCallback(() => {\n draggingRef.current = false;\n setDragIndex(null);\n setDropIndex(null);\n }, []);\n\n // Altijd de laatste draft + \"zijn er niet-opgeslagen wijzigingen\" bij de hand houden, zodat we\n // bij sluiten/wegnavigeren nog kunnen flushen (de debounce zou de laatste 700ms anders weggooien).\n const draftRef = useRef<DraftPage | null>(null);\n const dirtyRef = useRef(false);\n // Inhoud (stable-stringified) van de laatst succesvol opgeslagen payload — voor conflictdetectie.\n const lastSavedRef = useRef<string | null>(null);\n useEffect(() => {\n draftRef.current = draft;\n }, [draft]);\n\n // ── Undo/redo ──────────────────────────────────────────────────────────────\n // History over draft-snapshots. Tekstedits (per toetsaanslag) worden ge-coalesced: binnen één\n // typ-burst (<800ms) staat het pre-burst-snapshot al op de stack, dus niet opnieuw pushen —\n // Ctrl+Z springt dan per burst terug i.p.v. per letter. `histVersion` forceert re-render van\n // de knop-states (de stacks leven in een ref).\n const historyRef = useRef<{ past: DraftPage[]; future: DraftPage[]; lastPush: number }>({ past: [], future: [], lastPush: 0 });\n const [, setHistVersion] = useState(0);\n\n const pushHistory = useCallback((prev: DraftPage, coalesce: boolean) => {\n const h = historyRef.current;\n const now = Date.now();\n if (!(coalesce && now - h.lastPush < 800 && h.past.length > 0)) {\n h.past.push(prev);\n if (h.past.length > 100) h.past.shift();\n }\n h.lastPush = now;\n h.future = [];\n setHistVersion((v) => v + 1);\n }, []);\n\n /** Enige route waarlangs de draft muteert: bewaart eerst een undo-snapshot. */\n const mutateDraft = useCallback(\n (updater: (d: DraftPage) => DraftPage, opts?: { coalesce?: boolean }) => {\n const d = draftRef.current;\n if (!d) return;\n const next = updater(d);\n if (next === d) return;\n pushHistory(d, opts?.coalesce ?? false);\n draftRef.current = next;\n // Dirty HIER markeren (synchroon met de mutatie), niet pas in het debounced autosave-effect:\n // een inline edit (blur) direct gevolgd door \"Sluiten\" in dezelfde gesture zou anders\n // flushSave() zien met dirtyRef=false → laatste wijziging verloren.\n dirtyRef.current = true;\n setDraft(next);\n },\n [pushHistory],\n );\n\n const undo = useCallback(() => {\n const h = historyRef.current;\n const d = draftRef.current;\n const prev = h.past.pop();\n if (!prev || !d) return;\n h.future.push(d);\n h.lastPush = 0;\n draftRef.current = prev;\n dirtyRef.current = true; // undo is óók een wijziging die opgeslagen moet worden\n setDraft(prev);\n setHistVersion((v) => v + 1);\n }, []);\n\n const redo = useCallback(() => {\n const h = historyRef.current;\n const d = draftRef.current;\n const next = h.future.pop();\n if (!next || !d) return;\n h.past.push(d);\n h.lastPush = 0;\n draftRef.current = next;\n dirtyRef.current = true; // redo is óók een wijziging die opgeslagen moet worden\n setDraft(next);\n setHistVersion((v) => v + 1);\n }, []);\n\n const draftPayload = (d: DraftPage) => ({\n blocks: d.blocks,\n title: d.title,\n slug: d.slug,\n path: d.path,\n seo: d.seo,\n // Alleen meesturen wanneer aanwezig: `undefined` weigert de Firestore-client.\n ...(d.translations ? { translations: d.translations } : {}),\n });\n\n // Schrijf eventuele pending wijziging direct weg (gebruikt bij Sluiten, tab-wissel en retry).\n const flushSave = useCallback(async () => {\n const d = draftRef.current;\n if (!d || !dirtyRef.current) return;\n const payload = draftPayload(d);\n await saveDraft(siteId, d.id, payload);\n lastSavedRef.current = stableStringify(payload);\n // Alleen \"schoon\" markeren als er intussen niets nieuws is getypt (anders zou een trage\n // write nieuwere wijzigingen als opgeslagen bestempelen).\n if (draftRef.current === d) {\n dirtyRef.current = false;\n setStatus(\"saved\");\n }\n }, [siteId]);\n\n const handleClose = useCallback(() => {\n void flushSave().finally(() => onClose());\n }, [flushSave, onClose]);\n\n useEffect(() => {\n let active = true;\n getDraftByPath(siteId, path)\n .then((d) => {\n if (!active) return;\n if (d) {\n setDraft(d);\n lastSavedRef.current = stableStringify(draftPayload(d));\n setStatus(\"idle\");\n } else {\n setStatus(\"none\");\n }\n })\n .catch(() => active && setStatus(\"none\"));\n return () => {\n active = false;\n };\n }, [siteId, path]);\n\n /** Bootstrap vanuit de \"Geen werkversie\"-toestand: maak de pagina hier direct aan (geen omweg\n * via /beheer → Pagina's — dat was een doodloop voor niet-technische klanten). */\n const handleCreatePage = useCallback(async () => {\n setStatus(\"loading\");\n try {\n const segment = path.split(\"/\").filter(Boolean).pop() ?? \"\";\n const title = segment ? segment.replace(/[-_]+/g, \" \").replace(/^\\w/, (c) => c.toUpperCase()) : \"Home\";\n await createPage(siteId, { title, slug: segment || \"home\", path });\n const d = await getDraftByPath(siteId, path);\n if (d) {\n setDraft(d);\n lastSavedRef.current = stableStringify(draftPayload(d));\n setStatus(\"idle\");\n toast({ title: \"Pagina aangemaakt\", description: \"Voeg je eerste blok toe en publiceer wanneer je klaar bent.\", tone: \"success\" });\n } else {\n setStatus(\"none\");\n }\n } catch (e) {\n setStatus(\"none\");\n toast({ title: \"Pagina aanmaken mislukt\", description: (e as Error)?.message, tone: \"error\" });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [siteId, path, toast]);\n\n // ── Conflictdetectie ───────────────────────────────────────────────────────\n // Luister naar de draft in Firestore. Een server-snapshot die afwijkt van zowel onze laatst\n // opgeslagen payload als onze huidige lokale staat, betekent: iemand anders (tab/collega)\n // heeft deze pagina gewijzigd. Waarschuw i.p.v. elkaars werk stil te overschrijven.\n const draftId = draft?.id;\n useEffect(() => {\n if (!draftId) return;\n return subscribeDraft(siteId, draftId, (remote, meta) => {\n if (!meta.fromServer || !remote) return;\n const remotePayload = stableStringify(draftPayload(remote));\n const local = draftRef.current;\n const localPayload = local ? stableStringify(draftPayload(local)) : null;\n if (remotePayload !== lastSavedRef.current && remotePayload !== localPayload) setConflict(true);\n });\n }, [siteId, draftId]);\n\n const blocks = (draft?.blocks ?? []) as Block[];\n // Canvas in een niet-default taal: toon de vertaal-overlay over de canonieke blokken.\n const overlay = draft?.translations?.[locale];\n const displayBlocks = useMemo(\n () => (isTranslating ? (localizeBlocks(blocks, overlay) as Block[]) : blocks),\n [blocks, isTranslating, overlay],\n );\n\n // Schrijf een inline (in-canvas) tekstwijziging terug naar het juiste blokveld (canoniek).\n const commitInline = useCallback(\n (key: string, field: string, value: string) => {\n mutateDraft(\n (d) => {\n const bl = d.blocks as Block[];\n const idx = bl.findIndex((x) => x._key === key);\n if (idx < 0 || getPath(bl[idx], field) === value) return d;\n const next = [...bl];\n next[idx] = setPath(next[idx], field, value);\n return { ...d, blocks: next };\n },\n { coalesce: true },\n );\n },\n [mutateDraft],\n );\n\n // Schrijf een vertaling naar de taal-overlay; leeg = sleutel weg (terugvallen op origineel).\n const setTranslation = useCallback(\n (tkey: string, value: string) => {\n mutateDraft(\n (d) => {\n const overlay = { ...(d.translations?.[locale] ?? {}) };\n if (value.trim() === \"\" || value === undefined) delete overlay[tkey];\n else overlay[tkey] = value;\n return { ...d, translations: { ...(d.translations ?? {}), [locale]: overlay } };\n },\n { coalesce: true },\n );\n },\n [mutateDraft, locale],\n );\n\n // ── Inline opmaak-toolbar (WYSIWYG op het canvas) ─────────────────────────\n // Zwevende mini-toolbar boven een gefocust rich-text-veld: vet/schuin/onderstrepen/link +\n // groter-kleiner via kop-niveaus (H2/H3/normaal — semantisch i.p.v. losse font-sizes).\n // document.execCommand is deprecated-maar-universeel en precies goed voor contentEditable;\n // de output gaat bij commit door DOMPurify én server-side door de allowlist-sanitizer.\n const [fmtBar, setFmtBar] = useState<{ top: number; left: number } | null>(null);\n const fmtElRef = useRef<HTMLElement | null>(null);\n const positionFmtBar = useCallback((el: HTMLElement) => {\n const r = el.getBoundingClientRect();\n setFmtBar({\n top: Math.max(56, r.top - 44),\n left: Math.min(Math.max(8, r.left), Math.max(8, window.innerWidth - 300)),\n });\n }, []);\n useEffect(() => {\n if (!fmtBar) return;\n const onScroll = () => {\n const el = fmtElRef.current;\n if (el) positionFmtBar(el);\n };\n window.addEventListener(\"scroll\", onScroll, { capture: true, passive: true });\n return () => window.removeEventListener(\"scroll\", onScroll, { capture: true });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [fmtBar !== null, positionFmtBar]);\n\n // Inline canvas-edits: in de default-taal naar het blokveld, in een andere taal naar de overlay.\n const commitText = useCallback(\n (key: string, field: string, value: string) => {\n if (isTranslating) setTranslation(`${key}.${field}`, value);\n else commitInline(key, field, value);\n },\n [isTranslating, setTranslation, commitInline],\n );\n\n // Maak de tekstvelden van het GESELECTEERDE blok ter plekke bewerkbaar (contentEditable) en synct\n // op blur terug — zo bewerk je tekst inline op het canvas i.p.v. via de zijbalk. We binden via een\n // MutationObserver én her-binden zodra React de DOM-node vervangt (re-render), met een dataset-flag\n // zodat we nooit dubbele listeners zetten.\n useEffect(() => {\n if (!selectedKey) return;\n const host = canvasRef.current?.querySelector<HTMLElement>(`[data-ve-content=\"${selectedKey}\"]`);\n if (!host) return;\n const type = blocks.find((b) => b._key === selectedKey)?._type ?? \"\";\n const specs = [...GLOBAL_INLINE];\n warnInlineCoverage(String(type), registry.get(String(type)), host);\n\n const bound = new Set<HTMLElement>();\n const bind = (el: HTMLElement, field: string, html: boolean) => {\n if (el.dataset.veInline === \"1\") return;\n el.dataset.veInline = \"1\";\n el.setAttribute(\"contenteditable\", \"true\");\n el.setAttribute(\"spellcheck\", \"true\");\n el.classList.add(\"xicms-ve-inline\");\n // Inline HTML wordt vóór commit gesanitized (DOMPurify) — zelfde belofte als het TipTap-pad;\n // de server sanitized nogmaals bij publiceren. Geplakte Word-/Docs-rommel blijft zo buiten.\n el.addEventListener(\"blur\", () => commitText(selectedKey, field, html ? DOMPurify.sanitize(el.innerHTML) : (el.textContent ?? \"\")));\n if (html) {\n // Opmaak-toolbar tonen zolang dit rich-text-veld focus heeft.\n el.addEventListener(\"focus\", () => {\n fmtElRef.current = el;\n positionFmtBar(el);\n });\n el.addEventListener(\"blur\", () => {\n fmtElRef.current = null;\n setFmtBar(null);\n });\n }\n el.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\") el.blur();\n else if (!html && e.key === \"Enter\") { e.preventDefault(); el.blur(); }\n });\n bound.add(el);\n };\n const apply = () => {\n for (const m of specs) {\n host.querySelectorAll<HTMLElement>(m.selector).forEach((el) =>\n bind(el, \"fromData\" in m && m.fromData ? (el.dataset.xicmsEdit as string) : m.field, !!m.html),\n );\n }\n };\n\n apply();\n // Her-bind na elke re-render die de tekstnodes vervangt (niet tijdens slepen: dan muteert de\n // DOM continu door de drop-indicatoren en zou her-binden alleen maar ruis geven).\n const obs = new MutationObserver(() => {\n if (!draggingRef.current) apply();\n });\n obs.observe(host, { childList: true, subtree: true });\n return () => {\n obs.disconnect();\n bound.forEach((el) => {\n el.removeAttribute(\"contenteditable\");\n el.classList.remove(\"xicms-ve-inline\");\n delete el.dataset.veInline;\n });\n };\n }, [selectedKey, blocks, commitText, positionFmtBar]);\n\n // Autosave naar de draft (publiceren blijft een aparte, bewuste stap).\n // Debounce 700ms ná de laatste wijziging, met een MAX-WAIT van 3s: de debounce herstart bij elke\n // toetsaanslag, dus zonder max-wait zou iemand die blijft doortypen nooit tussentijds opslaan.\n const dirtySinceRef = useRef<number | null>(null);\n useEffect(() => {\n if (!draft) return;\n if (firstSave.current) {\n firstSave.current = false;\n return;\n }\n // Bij een openstaand conflict PAUZEREN we autosave: de banner wegklikken en doorwerken\n // overschreef anders stil de wijzigingen van de collega/andere tab (destructief-by-default).\n // De klant kiest eerst expliciet \"Nieuwste laden\" of \"Mijn versie behouden\".\n if (conflict) return;\n dirtyRef.current = true;\n dirtySinceRef.current ??= Date.now();\n setStatus(\"saving\");\n const waited = Date.now() - dirtySinceRef.current;\n const delay = Math.max(0, Math.min(700, 3000 - waited));\n const t = setTimeout(() => {\n dirtySinceRef.current = null;\n const payload = draftPayload(draft);\n void saveDraft(siteId, draft.id, payload)\n .then(() => {\n lastSavedRef.current = stableStringify(payload);\n // Alleen \"schoon\"/\"saved\" als dit nog de nieuwste versie is (zie flushSave).\n if (draftRef.current === draft) {\n dirtyRef.current = false;\n setStatus(\"saved\");\n }\n })\n .catch(() => setStatus(\"error\"));\n }, delay);\n return () => clearTimeout(t);\n }, [draft, siteId, conflict]);\n\n // Waarschuw bij het verlaten van de pagina met niet-opgeslagen wijzigingen; flush bij tab-wissel/\n // verbergen (visibilitychange) en bij unmount. Escape sluit de editor (na flush).\n useEffect(() => {\n const beforeUnload = (e: BeforeUnloadEvent) => {\n if (dirtyRef.current) {\n e.preventDefault();\n e.returnValue = \"\";\n }\n };\n const onVisibility = () => {\n // Tab-wissel/minimaliseren: beste moment om pending werk veilig te stellen.\n if (document.visibilityState === \"hidden\") void flushSave().catch(() => {});\n };\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n // Escape sluit alléén de editor als er niets \"boven\" de editor actief is: een open\n // dialog (mediakiezer, bevestiging — Radix rendert [role=dialog] buiten deze shell) of\n // een inline bewerking/invoerveld vangt zijn eigen Escape. Anders verdween bij het\n // sluiten van de mediakiezer meteen de hele editor.\n const t = e.target as HTMLElement | null;\n const inField = !!t && (t.isContentEditable || t.tagName === \"INPUT\" || t.tagName === \"TEXTAREA\" || t.tagName === \"SELECT\");\n const dialogOpen = document.querySelector('[role=\"dialog\"][data-state=\"open\"], [role=\"alertdialog\"][data-state=\"open\"]');\n if (!inField && !dialogOpen) handleClose();\n return;\n }\n // Undo/redo op canvas-niveau. In invoervelden/contentEditable laten we de native\n // tekst-undo van de browser zijn werk doen (anders vecht Ctrl+Z met het veld zelf).\n const t = e.target as HTMLElement | null;\n const inField = !!t && (t.isContentEditable || t.tagName === \"INPUT\" || t.tagName === \"TEXTAREA\" || t.tagName === \"SELECT\");\n if ((e.metaKey || e.ctrlKey) && !inField && (e.key.toLowerCase() === \"z\" || e.key.toLowerCase() === \"y\")) {\n e.preventDefault();\n if (e.key.toLowerCase() === \"y\" || e.shiftKey) redo();\n else undo();\n }\n };\n window.addEventListener(\"beforeunload\", beforeUnload);\n document.addEventListener(\"visibilitychange\", onVisibility);\n window.addEventListener(\"keydown\", onKeyDown);\n return () => {\n window.removeEventListener(\"beforeunload\", beforeUnload);\n document.removeEventListener(\"visibilitychange\", onVisibility);\n window.removeEventListener(\"keydown\", onKeyDown);\n const d = draftRef.current;\n if (d && dirtyRef.current) {\n // Laatste redmiddel bij unmount; log een mislukking i.p.v. stil verliezen.\n void saveDraft(siteId, d.id, draftPayload(d)).catch((err) =>\n console.warn(\"[xicms] autosave bij sluiten mislukt:\", err),\n );\n }\n };\n }, [siteId, handleClose, flushSave, undo, redo]);\n\n function setBlocks(next: Block[], opts?: { coalesce?: boolean }) {\n mutateDraft((d) => ({ ...d, blocks: next }), opts);\n }\n function updateByKey(key: string, next: Record<string, unknown>) {\n // Zijpaneel-edits komen per toetsaanslag binnen → coalesce voor zinnige undo-stappen.\n setBlocks(blocks.map((b) => (b._key === key ? ({ ...(next as Block), _key: b._key, _type: b._type }) : b)), { coalesce: true });\n }\n function move(i: number, dir: -1 | 1) {\n const j = i + dir;\n if (j < 0 || j >= blocks.length) return;\n const copy = [...blocks];\n [copy[i], copy[j]] = [copy[j], copy[i]];\n setBlocks(copy);\n }\n function duplicate(i: number) {\n const copy = [...blocks];\n copy.splice(i + 1, 0, { ...blocks[i], _key: newKey() });\n setBlocks(copy);\n }\n /** Symbool loskoppelen (5.0): vervang de referentie door een kloon van de symboolblokken —\n * daarna beweegt dit exemplaar niet meer mee met het gedeelde symbool. */\n async function detachSymbol(i: number) {\n const blk = blocks[i] as { _type?: string; symbolId?: string; _key: string };\n if (blk._type !== \"symbolRef\" || !blk.symbolId) return;\n const ok = await confirm({\n title: \"Symbool loskoppelen?\",\n description: \"Dit blok wordt een losse kopie en beweegt niet meer mee met het gedeelde symbool.\",\n confirmLabel: \"Loskoppelen\",\n });\n if (!ok) return;\n const { getSymbolOnce } = await import(\"../lib/symbolsClient\");\n const sym = await getSymbolOnce(siteId, blk.symbolId);\n if (!sym) {\n toast({ title: \"Symbool niet gevonden\", tone: \"error\" });\n return;\n }\n const clones = sym.blocks.map((b) => ({ ...(b as Record<string, unknown>), _key: newKey() })) as Block[];\n const copy = [...blocks];\n copy.splice(i, 1, ...clones);\n setBlocks(copy);\n if (selectedKey === blk._key) setSelectedKey(null);\n toast({ title: \"Losgekoppeld\", description: \"Dit is nu een losse kopie.\", tone: \"success\" });\n }\n async function remove(i: number) {\n const ok = await confirm({ title: \"Blok verwijderen?\", confirmLabel: \"Verwijderen\", tone: \"danger\" });\n if (!ok) return;\n const removed = blocks[i];\n setBlocks(blocks.filter((_, idx) => idx !== i));\n if (selectedKey === removed._key) setSelectedKey(null);\n }\n function addBlock(type: string) {\n const def = registry.get(type);\n const base = def?.defaults?.() ?? {};\n const block = { _key: newKey(), _type: type, ...base } as Block;\n insertAfterSelected([block]);\n setSelectedKey(block._key);\n }\n function insertAfterSelected(newBlocks: Array<Record<string, unknown>>) {\n const sel = blocks.findIndex((b) => b._key === selectedKey);\n const copy = [...blocks];\n copy.splice(sel >= 0 ? sel + 1 : blocks.length, 0, ...(newBlocks as Block[]));\n setBlocks(copy);\n }\n\n async function onPublish() {\n if (!draft) return;\n // Openstaand conflict eerst oplossen: publiceren zou anders een stale merge live zetten.\n if (conflict) {\n toast({ title: \"Los eerst het conflict op\", description: \"Kies 'Nieuwste versie laden' of 'Mijn versie behouden' in de gele balk.\", tone: \"error\" });\n return;\n }\n const ok = await confirm({ title: \"Pagina publiceren?\", description: `De live site wordt bijgewerkt (${draft.path}).`, confirmLabel: \"Publiceren\" });\n if (!ok) return;\n setPublishing(true);\n try {\n await saveDraft(siteId, draft.id, draftPayload(draft));\n await publishPage(siteId, draft.id);\n toast({\n title: \"Gepubliceerd\",\n description: \"Live voor bezoekers over ongeveer 15 minuten. Gebruik “Bekijk live” hierboven om het direct te controleren.\",\n tone: \"success\",\n });\n } catch (e) {\n toast({ title: \"Publiceren mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setPublishing(false);\n }\n }\n\n const addOptions = useMemo(() => buildBlockAddOptions(registry.values()), [registry]);\n\n const selectedIndex = blocks.findIndex((b) => b._key === selectedKey);\n const selected = selectedIndex >= 0 ? blocks[selectedIndex] : null;\n const canUndo = historyRef.current.past.length > 0;\n const canRedo = historyRef.current.future.length > 0;\n\n /** Laad de nieuwste (remote) versie in de editor — bij een gedetecteerd conflict. */\n const reloadRemote = useCallback(async () => {\n const remote = await getDraftByPath(siteId, path).catch(() => null);\n if (remote) {\n mutateDraft(() => remote);\n lastSavedRef.current = stableStringify(draftPayload(remote));\n dirtyRef.current = false;\n setStatus(\"idle\");\n }\n setConflict(false);\n }, [siteId, path, mutateDraft]);\n\n return (\n <div className=\"xicms-ve__shell\" role=\"dialog\" aria-modal=\"true\" aria-label={`Pagina bewerken: ${path}`}>\n <header className=\"xicms-ve__bar\">\n <strong className=\"xicms-ve__title\">Bewerken · {name}</strong>\n <span className=\"xicms-ve__path\">{path}</span>\n {localeCfg.locales.length > 1 && (\n <span className=\"xicms-ve__locale\" title=\"Taal — in een andere taal dan de standaard bewerk je vertalingen\">\n 🌐\n <Select\n value={locale}\n onValueChange={setLocale}\n options={localeCfg.locales.map((l) => ({ value: l, label: l === localeCfg.defaultLocale ? `${l} (standaard)` : l }))}\n />\n </span>\n )}\n <button type=\"button\" className=\"xicms-ve__histbtn\" disabled={!canUndo} onClick={undo} title=\"Ongedaan maken (Ctrl+Z)\" aria-label=\"Ongedaan maken (Ctrl+Z)\">↶</button>\n <button type=\"button\" className=\"xicms-ve__histbtn\" disabled={!canRedo} onClick={redo} title=\"Opnieuw (Ctrl+Shift+Z)\" aria-label=\"Opnieuw (Ctrl+Shift+Z)\">↷</button>\n <span className=\"xicms-ve__status\" aria-live=\"polite\">\n {status === \"saving\" ? \"Opslaan…\" : status === \"saved\" ? \"✓ Opgeslagen\" : status === \"error\" ? \"⚠ Niet opgeslagen\" : \"\"}\n </span>\n {status === \"error\" && (\n <Button size=\"sm\" variant=\"secondary\" onClick={() => void flushSave().catch(() => setStatus(\"error\"))}>\n Opnieuw opslaan\n </Button>\n )}\n <div className=\"xicms-ve__bar-actions\">\n {/* Publiceren zet de pagina live in Firestore, maar de site zelf leest voor bezoekers uit\n een gecommitte bundel die pas na ~5-15 min meekomt (debounced deploy). `?preview=1`\n laat de pagina de gepubliceerde stand rechtstreeks en ongecachet tonen, zodat je als\n beheerder je eigen publicatie meteen kunt controleren zonder te hoeven wachten. */}\n <a\n className=\"xicms-btn xicms-btn--ghost\"\n href={`${path}${path.includes(\"?\") ? \"&\" : \"?\"}preview=1`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n title=\"Toont de gepubliceerde pagina rechtstreeks (zonder cache) — zo zie je meteen wat je zojuist gepubliceerd hebt.\"\n >\n 👁 Bekijk live\n </a>\n <Button size=\"sm\" disabled={publishing || !draft} onClick={onPublish}>\n {publishing ? \"Bezig…\" : \"Publiceren\"}\n </Button>\n <Button size=\"sm\" variant=\"secondary\" onClick={handleClose}>Sluiten</Button>\n </div>\n </header>\n\n {conflict && (\n <div className=\"xicms-ve__conflict\" role=\"alert\">\n <span>⚠ Deze pagina is intussen ergens anders gewijzigd (andere tab of collega). Doorwerken kan die wijzigingen overschrijven.</span>\n <Button size=\"sm\" onClick={() => void reloadRemote()}>Nieuwste versie laden</Button>\n <Button size=\"sm\" variant=\"secondary\" onClick={() => setConflict(false)}>Mijn versie behouden</Button>\n </div>\n )}\n\n <div className=\"xicms-ve__body\">\n <main className=\"xicms-ve__canvas\">\n {status === \"loading\" && <p className=\"xicms-ve__hint\">Laden…</p>}\n {status === \"none\" && (\n <div className=\"xicms-ve__empty\">\n <EmptyState\n icon=\"📄\"\n title=\"Geen werkversie gevonden\"\n description={`Voor deze pagina (${path}) bestaat nog geen werkversie. Maak hem hier direct aan, of ga naar /beheer → Pagina's.`}\n />\n <p style={{ textAlign: \"center\", marginTop: \"1rem\" }}>\n <button type=\"button\" className=\"xicms-btn\" onClick={() => void handleCreatePage()}>\n + Deze pagina aanmaken\n </button>\n </p>\n </div>\n )}\n {draft && (\n <div className=\"container\" ref={canvasRef}>\n {blocks.length === 0 && (\n <EmptyState icon=\"🧱\" title=\"Nog geen blokken\" description=\"Voeg hieronder je eerste blok toe.\" />\n )}\n {displayBlocks.map((b, i) => {\n const def = registry.get(b._type);\n const Render = def?.Render;\n // Coerce via het schema zodat defaults zijn toegepast (zoals de live pagina via\n // validateBlocks doet). Ruwe drafts missen anders defaults (bv. hero.buttons) en\n // zouden de Render laten throwen. Lukt het parsen niet, val terug op het ruwe blok\n // (de error-boundary hieronder vangt een eventuele fout dan netjes op).\n const safe = (() => {\n const parsed = def?.schema?.safeParse(b);\n return parsed?.success ? (parsed.data as Block) : b;\n })();\n const dropClass = dropIndex === i ? \" is-drop-before\" : dropIndex === i + 1 ? \" is-drop-after\" : \"\";\n return (\n <div\n key={b._key}\n className={`xicms-ve-block${b._key === selectedKey ? \" is-selected\" : \"\"}${dragIndex === i ? \" is-dragging\" : \"\"}${dropClass}`}\n onDragOver={(e) => {\n if (dragIndex === null) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n const rect = e.currentTarget.getBoundingClientRect();\n setDropIndex(e.clientY < rect.top + rect.height / 2 ? i : i + 1);\n }}\n onDrop={(e) => {\n if (dragIndex === null || dropIndex === null) return;\n e.preventDefault();\n setBlocks(reorderBlocks(blocks, dragIndex, dropIndex));\n endDrag();\n }}\n >\n <div className=\"xicms-ve-block__toolbar\">\n {/* Sleep-greep: apart element (niet het hele blok) zodat slepen nooit botst met\n het inline contentEditable-bewerken; ↑/↓ blijven het toetsenbord-alternatief. */}\n <button\n type=\"button\"\n className=\"xicms-ve-block__drag\"\n draggable\n onDragStart={(e) => {\n draggingRef.current = true;\n setDragIndex(i);\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setData(\"text/plain\", String(i));\n const blockEl = e.currentTarget.closest(\".xicms-ve-block\");\n if (blockEl) e.dataTransfer.setDragImage(blockEl, 24, 24);\n }}\n onDragEnd={endDrag}\n aria-label={`Versleep ${def?.label ?? b._type}`}\n title=\"Slepen om te verplaatsen\"\n >\n ⋮⋮\n </button>\n <span className=\"xicms-ve-block__label\">{def?.label ?? b._type}</span>\n <button type=\"button\" onClick={() => setSelectedKey(b._key)} aria-label={`Bewerk ${def?.label ?? b._type}`} title=\"Bewerken\">✏️</button>\n <button type=\"button\" disabled={i === 0} onClick={() => move(i, -1)} aria-label=\"Omhoog\">↑</button>\n <button type=\"button\" disabled={i === blocks.length - 1} onClick={() => move(i, 1)} aria-label=\"Omlaag\">↓</button>\n {b._type === \"symbolRef\" && (\n <button type=\"button\" onClick={() => void detachSymbol(i)} aria-label=\"Symbool loskoppelen\" title=\"Loskoppelen (losse kopie)\">🔗✂</button>\n )}\n <button type=\"button\" onClick={() => duplicate(i)} aria-label=\"Dupliceren\">⎘</button>\n <button type=\"button\" onClick={() => void remove(i)} aria-label=\"Verwijderen\">🗑</button>\n </div>\n {/* Altijd renderen (geen conditie) → geen remount van de inhoud bij selecteren,\n anders verliest het inline-bewerken zijn contentEditable. Bij selectie zet de\n CSS deze overlay op pointer-events:none zodat je de tekst kunt bewerken. */}\n <button type=\"button\" className=\"xicms-ve-block__select\" aria-label={`Selecteer ${def?.label ?? b._type}`} onClick={() => setSelectedKey(b._key)} />\n <div className=\"xicms-ve-block__content\" data-ve-content={b._key}>\n {/* Net als BlockRenderer: elk blok in een per-blok error-boundary zodat één\n falend (maatwerk-)blok de hele editor niet blank maakt. mode=\"preview\". */}\n {Render ? (\n <BlockErrorBoundary mode=\"preview\" type={b._type} blockKey={b._key}>\n {/* Alleen het geselecteerde blok krijgt de inline beeld-vervangen-overlay. */}\n <InlineImageEditProvider onReplace={b._key === selectedKey ? (field) => setImgPicker({ key: b._key, field }) : null}>\n <Render block={safe} siteId={siteId} mode=\"preview\" />\n </InlineImageEditProvider>\n </BlockErrorBoundary>\n ) : (\n <p className=\"xicms-ve__hint\">Onbekend blok: {b._type}</p>\n )}\n </div>\n </div>\n );\n })}\n <div className=\"xicms-ve__inserter\">\n <span>Toevoegen{selected ? \" (na selectie)\" : \"\"}:</span>\n <div className=\"xicms-ve__inserter-select\">\n <Select value=\"\" placeholder=\"Kies een blok…\" onValueChange={(v) => v && addBlock(v)} options={addOptions} />\n </div>\n <PatternInserter onInsert={insertAfterSelected} />\n </div>\n </div>\n )}\n </main>\n\n <aside className=\"xicms-ve__panel\">\n {selected && isTranslating ? (\n <div className=\"grid gap-3\">\n <h3 className=\"text-sm font-semibold\">{registry.get(selected._type)?.label ?? selected._type} — vertaling ({locale})</h3>\n <TranslationPanel\n key={`${selected._key}:${locale}`}\n block={selected}\n siteId={siteId}\n registry={registry}\n locale={locale}\n overlay={overlay ?? {}}\n onTranslate={setTranslation}\n />\n </div>\n ) : selected ? (\n <div className=\"grid gap-3\">\n <div className=\"xicms-ve__panel-head\">\n <button\n type=\"button\"\n className=\"xicms-btn xicms-btn--ghost\"\n onClick={() => void flushSave().catch(() => setStatus(\"error\"))}\n >\n ✓ Opslaan\n </button>\n <button type=\"button\" className=\"xicms-btn xicms-btn--ghost\" onClick={() => setSelectedKey(null)}>\n ✕ Sluiten\n </button>\n </div>\n <SelectedEditor key={selected._key} block={selected} siteId={siteId} registry={registry} onChange={(next) => updateByKey(selected._key, next)} />\n </div>\n ) : draft && isTranslating ? (\n <div className=\"grid gap-4\">\n <p className=\"xicms-ve__hint\">\n Je bewerkt de vertaling <strong>{locale}</strong>. Klik op een blok om zijn teksten te\n vertalen; lege velden vallen terug op de standaardtaal. Blokken toevoegen/verplaatsen\n doe je in de standaardtaal.\n </p>\n <PageTranslationSettings draft={draft} locale={locale} onTranslate={setTranslation} />\n </div>\n ) : draft ? (\n <div className=\"grid gap-4\">\n <p className=\"xicms-ve__hint\">Klik op een blok om het te bewerken.</p>\n <PageSettings draft={draft} onChange={(part) => mutateDraft((d) => ({ ...d, ...part }), { coalesce: true })} />\n {!isDemoActive() && (\n <VersionsPanel\n siteId={siteId}\n pageId={draft.id}\n onRestore={(restored) => {\n mutateDraft((d) => ({ ...d, ...restored }));\n toast({ title: \"Versie hersteld naar werkversie\", description: \"Publiceer om hem live te zetten.\", tone: \"success\" });\n }}\n />\n )}\n </div>\n ) : (\n <p className=\"xicms-ve__hint\">Klik op een blok om het te bewerken.</p>\n )}\n </aside>\n </div>\n\n {/* Inline afbeelding vervangen: opent de mediabibliotheek; alleen de URL van het veld wordt gezet. */}\n <MediaPickerDialog\n siteId={siteId}\n open={!!imgPicker}\n onOpenChange={(v) => { if (!v) setImgPicker(null); }}\n onSelect={(m) => {\n if (imgPicker) {\n commitInline(imgPicker.key, imgPicker.field, m.url);\n // Alt-tekst van de gekozen media meenemen (a11y/SEO): conventie url→alt / imageUrl→alt\n // op hetzelfde niveau. Bestaat het veld niet in het schema, dan stript Zod het bij\n // publiceren — nooit schadelijk, meestal precies goed (Image/Gallery/Cards/Slider).\n const altField = imgPicker.field.replace(/(^|\\.)((image)?[uU]rl)$/, \"$1alt\");\n if (altField !== imgPicker.field) commitInline(imgPicker.key, altField, m.alt ?? \"\");\n }\n setImgPicker(null);\n }}\n />\n\n {/* Zwevende opmaak-toolbar boven het gefocuste inline rich-text-veld. onMouseDown-preventDefault\n houdt focus + selectie in het veld, zodat execCommand op de selectie werkt. */}\n {fmtBar && (\n <div className=\"xicms-ve-fmtbar\" style={{ top: fmtBar.top, left: fmtBar.left }} role=\"toolbar\" aria-label=\"Tekstopmaak\">\n <FmtBtn label={<b>B</b>} title=\"Vet (Ctrl/Cmd+B)\" onRun={() => document.execCommand(\"bold\")} />\n <FmtBtn label={<i>I</i>} title=\"Cursief (Ctrl/Cmd+I)\" onRun={() => document.execCommand(\"italic\")} />\n <FmtBtn label={<u>U</u>} title=\"Onderstrepen (Ctrl/Cmd+U)\" onRun={() => document.execCommand(\"underline\")} />\n <FmtBtn\n label=\"🔗\"\n title=\"Link (selecteer eerst tekst; leeg = link verwijderen)\"\n onRun={() => {\n const url = window.prompt(\"Link-URL (leeg = link verwijderen)\", \"https://\");\n if (url === null) return;\n if (url === \"\" || url === \"https://\") document.execCommand(\"unlink\");\n else document.execCommand(\"createLink\", false, url);\n }}\n />\n <span className=\"xicms-ve-fmtbar__sep\" aria-hidden />\n <FmtBtn label=\"H2\" title=\"Grote kop\" onRun={() => document.execCommand(\"formatBlock\", false, \"<h2>\")} />\n <FmtBtn label=\"H3\" title=\"Kleine kop\" onRun={() => document.execCommand(\"formatBlock\", false, \"<h3>\")} />\n <FmtBtn label=\"¶\" title=\"Normale tekst\" onRun={() => document.execCommand(\"formatBlock\", false, \"<p>\")} />\n <span className=\"xicms-ve-fmtbar__sep\" aria-hidden />\n <FmtBtn label=\"•\" title=\"Opsomming\" onRun={() => document.execCommand(\"insertUnorderedList\")} />\n </div>\n )}\n </div>\n );\n}\n\n/** Toolbar-knop die de selectie/focus in het contentEditable-veld intact laat. */\nfunction FmtBtn({ label, title, onRun }: { label: React.ReactNode; title: string; onRun: () => void }) {\n return (\n <button\n type=\"button\"\n title={title}\n aria-label={title}\n onMouseDown={(e) => e.preventDefault()}\n onClick={onRun}\n >\n {label}\n </button>\n );\n}\n\n/**\n * Pagina-instellingen (titel + SEO) direct in de visuele editor — de klant hoeft niet meer naar\n * /beheer voor een titel- of meta-omschrijving-wijziging. Slug/pad blijven bewust in /beheer\n * (die raken URL-structuur en zijn niet iets om per ongeluk inline aan te passen).\n */\nfunction PageSettings({\n draft,\n onChange,\n}: {\n draft: DraftPage;\n onChange: (part: Partial<DraftPage>) => void;\n}) {\n const seo = draft.seo ?? {};\n return (\n <details className=\"xicms-ve__section\" open>\n <summary className=\"text-sm font-semibold\">Pagina-instellingen</summary>\n <div className=\"mt-2 grid gap-3\">\n <Field label=\"Paginatitel\">\n <Input value={draft.title ?? \"\"} onChange={(e) => onChange({ title: e.target.value })} />\n </Field>\n <Field label=\"SEO-titel\" hint=\"Leeg = paginatitel\">\n <Input value={seo.title ?? \"\"} onChange={(e) => onChange({ seo: { ...seo, title: e.target.value } })} />\n </Field>\n <Field label=\"SEO-omschrijving\" hint=\"Wordt getoond in zoekresultaten\">\n <Textarea rows={3} value={seo.description ?? \"\"} onChange={(e) => onChange({ seo: { ...seo, description: e.target.value } })} />\n </Field>\n </div>\n </details>\n );\n}\n\n/** Pagina-instellingen in vertaalmodus (5.0): titel + SEO als overlay-sleutels. */\nfunction PageTranslationSettings({\n draft,\n locale,\n onTranslate,\n}: {\n draft: DraftPage;\n locale: string;\n onTranslate: (key: string, value: string) => void;\n}) {\n const ov = draft.translations?.[locale] ?? {};\n return (\n <details className=\"xicms-ve__section\" open>\n <summary className=\"text-sm font-semibold\">Pagina-instellingen ({locale})</summary>\n <div className=\"mt-2 grid gap-3\">\n <Field label=\"Paginatitel\" hint={`Origineel: ${draft.title ?? \"\"}`}>\n <Input value={ov[\"title\"] ?? \"\"} placeholder={draft.title ?? \"\"} onChange={(e) => onTranslate(\"title\", e.target.value)} />\n </Field>\n <Field label=\"SEO-titel\">\n <Input value={ov[\"seo.title\"] ?? \"\"} placeholder={draft.seo?.title ?? \"\"} onChange={(e) => onTranslate(\"seo.title\", e.target.value)} />\n </Field>\n <Field label=\"SEO-omschrijving\">\n <Textarea rows={3} value={ov[\"seo.description\"] ?? \"\"} placeholder={draft.seo?.description ?? \"\"} onChange={(e) => onTranslate(\"seo.description\", e.target.value)} />\n </Field>\n </div>\n </details>\n );\n}\n\n/** Versiegeschiedenis in de visuele editor: herstellen zet een snapshot terug naar de WERKVERSIE. */\nfunction VersionsPanel({\n siteId,\n pageId,\n onRestore,\n}: {\n siteId: string;\n pageId: string;\n onRestore: (restored: Partial<DraftPage>) => void;\n}) {\n const [versions, setVersions] = useState<{ id: string; createdAt?: unknown; kind?: string; label?: string }[]>([]);\n const { toast } = useToast();\n const confirm = useConfirm();\n useEffect(() => subscribeVersions(siteId, pageId, setVersions), [siteId, pageId]);\n if (versions.length === 0) return null;\n\n const restore = async (versionId: string) => {\n const ok = await confirm({\n title: \"Versie herstellen?\",\n description: \"De snapshot vervangt je huidige werkversie (ongedaan te maken met Ctrl+Z). De live site verandert pas bij Publiceren.\",\n confirmLabel: \"Herstellen\",\n });\n if (!ok) return;\n try {\n const restored = await restoreVersion(siteId, pageId, versionId);\n onRestore(restored);\n } catch (e) {\n toast({ title: \"Herstellen mislukt\", description: (e as Error).message, tone: \"error\" });\n }\n };\n\n return (\n <details className=\"xicms-ve__section\">\n <summary className=\"text-sm font-semibold\">Versiegeschiedenis</summary>\n <ul className=\"mt-2 grid gap-1 text-sm\">\n {versions.slice(0, 10).map((v) => (\n <li key={v.id} className=\"flex items-center justify-between gap-2\">\n <span>\n {v.label ?? (v.kind === \"publish\" ? \"Gepubliceerd\" : v.kind ?? \"Snapshot\")}\n {formatVersionDate(v.createdAt) && <span className=\"text-slate-400\"> · {formatVersionDate(v.createdAt)}</span>}\n </span>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void restore(v.id)}>\n Herstellen\n </Button>\n </li>\n ))}\n </ul>\n </details>\n );\n}\n\nfunction formatVersionDate(createdAt: unknown): string {\n const date =\n createdAt && typeof (createdAt as { toDate?: () => Date }).toDate === \"function\"\n ? (createdAt as { toDate: () => Date }).toDate()\n : typeof createdAt === \"string\"\n ? new Date(createdAt)\n : null;\n return date && !Number.isNaN(date.getTime()) ? date.toLocaleString(\"nl-NL\", { dateStyle: \"short\", timeStyle: \"short\" }) : \"\";\n}\n\nfunction SelectedEditor({\n block,\n siteId,\n registry,\n onChange,\n}: {\n block: Block;\n siteId: string;\n registry: ReturnType<typeof useRegistry>;\n onChange: (next: Record<string, unknown>) => void;\n}) {\n const def = registry.get(block._type);\n const Editor: ComponentType<BlockEditorProps> =\n (def?.Editor as ComponentType<BlockEditorProps> | undefined) ?? builtinBlockEditors[block._type] ?? GenericEditor;\n return (\n <div className=\"grid gap-3\">\n <h3 className=\"text-sm font-semibold\">{def?.label ?? block._type}</h3>\n {/* Maatwerk-Editors kunnen falen → vang per-blok op zodat het paneel niet leeg wordt. */}\n <BlockErrorBoundary mode=\"preview\" type={block._type} blockKey={block._key}>\n <Editor block={block} siteId={siteId} onChange={onChange} />\n </BlockErrorBoundary>\n <StyleEditor\n value={block._style as BlockStyle | undefined}\n onChange={(style) => onChange({ ...block, _style: style })}\n />\n </div>\n );\n}\n"],"mappings":"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","names":["locale","overlay","t","inField"]}
@@ -29,8 +29,15 @@ export declare const getPublishedPageByPath: typeof getPublishedPageByPathUncach
29
29
  export declare const getSiteSymbols: typeof getSiteSymbolsUncached;
30
30
  export declare const getSiteCustomBlocks: typeof getSiteCustomBlocksUncached;
31
31
  export declare const getPublicSiteSettings: typeof getPublicSiteSettingsUncached;
32
- /** Eén gepubliceerde pagina op pad. Bundle-first, anders keyless via REST/Admin. */
33
- declare function getPublishedPageByPathUncached(siteId: string, path: string): Promise<StoredPage | null>;
32
+ /**
33
+ * Eén gepubliceerde pagina op pad. Bundle-first, anders keyless via REST/Admin.
34
+ * `opts.skipBundle` (voorbeeld-modus, `?preview=1` in de visuele editor): negeert de gecommitte
35
+ * bundel en leest rechtstreeks — de bundel komt pas met vertraging mee via de gedebouncede deploy-
36
+ * worker, dus een beheerder die net gepubliceerd heeft ziet anders tot ~15 min lang de oude tekst.
37
+ */
38
+ declare function getPublishedPageByPathUncached(siteId: string, path: string, opts?: {
39
+ skipBundle?: boolean;
40
+ }): Promise<StoredPage | null>;
34
41
  /** Draft-versie (voor preview achter auth). */
35
42
  export declare function getDraftPageById(siteId: string, pageId: string): Promise<StoredPage | null>;
36
43
  /** Draft op pad (preview). */
@@ -55,7 +62,9 @@ export declare function expandSymbols<T extends {
55
62
  * `getSiteSymbols`, die beide al bundle-first zijn — de i18n-overlay en expansie draaien identiek
56
63
  * op bundle- én Firestore-data.
57
64
  */
58
- export declare function getPublishedPageByPathExpanded(siteId: string, path: string, locale?: string): Promise<StoredPage | null>;
65
+ export declare function getPublishedPageByPathExpanded(siteId: string, path: string, locale?: string, opts?: {
66
+ skipBundle?: boolean;
67
+ }): Promise<StoredPage | null>;
59
68
  /**
60
69
  * Publieke site-instellingen (`sites/{siteId}/settings/public`) server-side — voor de
61
70
  * `settingsData`-prop van XiCMSProvider. Zo zit het klant-thema/nav/footer al in de server-HTML
@@ -93,8 +93,8 @@ const getPublishedPageByPath = cache(getPublishedPageByPathUncached);
93
93
  const getSiteSymbols = cache(getSiteSymbolsUncached);
94
94
  const getSiteCustomBlocks = cache(getSiteCustomBlocksUncached);
95
95
  const getPublicSiteSettings = cache(getPublicSiteSettingsUncached);
96
- async function getPublishedPageByPathUncached(siteId, path) {
97
- const bundle = getBundle();
96
+ async function getPublishedPageByPathUncached(siteId, path, opts) {
97
+ const bundle = opts?.skipBundle ? null : getBundle();
98
98
  if (bundle && bundle.siteId === siteId) {
99
99
  const hit = getBundlePage(bundle, path);
100
100
  return hit ? parse(hit.id, hit) : null;
@@ -176,8 +176,8 @@ function expandSymbols(page, symbols) {
176
176
  });
177
177
  return { ...page, blocks };
178
178
  }
179
- async function getPublishedPageByPathExpanded(siteId, path, locale) {
180
- let page = await getPublishedPageByPath(siteId, path);
179
+ async function getPublishedPageByPathExpanded(siteId, path, locale, opts) {
180
+ let page = await getPublishedPageByPath(siteId, path, opts);
181
181
  if (!page) return null;
182
182
  if (locale) page = localizePage(page, locale, "");
183
183
  if (!(page.blocks ?? []).some((b) => b._type === "symbolRef")) return page;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/pages.ts"],"sourcesContent":["import \"server-only\";\n\nimport { cache } from \"react\";\n\nimport { getAdminDb } from \"../firebase/admin\";\nimport { PageSchema, type Page } from \"../schema/pages\";\nimport { SymbolSchema, type StoredSymbol } from \"../schema/symbols\";\nimport { CustomBlockSchema, type CustomBlock } from \"../schema/customBlocks\";\nimport { PublicSiteSettingsSchema, type PublicSiteSettings } from \"../schema/settings\";\nimport { localizePage, localizeBlocks } from \"../i18n/overlay\";\nimport { getBundle, getBundlePage } from \"./bundle\";\n\n/**\n * BUNDLE-FIRST, Firestore-fallback (5.7).\n *\n * Elke publieke reader hieronder kijkt eerst of er een gecommit content-bundle is (server/bundle.ts,\n * `content/published.bundle.json`). Zo ja → serveer daaruit met exact dezelfde return-vormen als het\n * Firestore-pad (incl. schema-validatie, i18n-overlay en symbol-expansie). Zo nee (dev/lokaal of een\n * repo zonder gecommit bundle) → val ongewijzigd terug op de Admin-SDK / keyless-REST-implementatie.\n * De bundle-tak is puur additief: signatures en gedrag van de fallback blijven identiek.\n */\n\nexport type StoredPage = Page & { id: string };\n\nfunction toPath(slugs: string[] | undefined): string {\n const clean = (slugs ?? []).filter(Boolean);\n return \"/\" + clean.join(\"/\");\n}\n\n/** Pad uit Next catch-all params (`[[...slug]]`). \"/\" voor de root. */\nexport function pathFromSlugs(slugs: string[] | undefined): string {\n const p = toPath(slugs);\n return p === \"/\" ? \"/\" : p.replace(/\\/$/, \"\");\n}\n\nfunction parse(id: string, data: unknown): StoredPage | null {\n const res = PageSchema.safeParse(data);\n if (!res.success) {\n // Niet stil 404'en: een gepubliceerde pagina die het schema niet haalt is een diagnose-signaal\n // (schema-evolutie zonder default, of corrupte write) — zelfde patroon als symbols/customBlocks.\n console.warn(`[xicms] pagina \"${id}\" faalt schema-validatie en wordt overgeslagen:`, res.error.issues[0]);\n return null;\n }\n return { id, ...res.data };\n}\n\n/**\n * Admin-SDK-reads mogen een PUBLIEKE pagina nooit laten 500'en — zelfde belofte als het REST-pad\n * (dat al overal try/catch → lege fallback heeft). Een Firestore-hikje wordt gelogd en levert de\n * fallback op; de route rendert dan 404/fallback i.p.v. een servererror.\n */\nasync function safeAdminRead<T>(fallback: T, what: string, fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (e) {\n console.warn(`[xicms] Admin-read mislukt (${what}) — fallback gebruikt:`, (e as Error)?.message ?? e);\n return fallback;\n }\n}\n\n/* ---------------- keyless public reads via Firestore REST (alleen apiKey) ----------------\n * Gepubliceerde pagina's zijn publiek leesbaar (rules). De Firestore REST-API honoreert die\n * rules met enkel de web-apiKey (geen Admin-credentials, geen client-SDK), dus dit werkt\n * server-side op keyless deployments (org-policy zonder service-account-keys).\n */\ntype FsValue = Record<string, unknown>;\nfunction decode(v: FsValue): unknown {\n if (v == null) return null;\n if (\"stringValue\" in v) return v.stringValue;\n if (\"integerValue\" in v) return Number(v.integerValue);\n if (\"doubleValue\" in v) return v.doubleValue;\n if (\"booleanValue\" in v) return v.booleanValue;\n if (\"nullValue\" in v) return null;\n if (\"timestampValue\" in v) return v.timestampValue;\n if (\"referenceValue\" in v) return v.referenceValue;\n if (\"geoPointValue\" in v) return v.geoPointValue;\n if (\"arrayValue\" in v) return (((v.arrayValue as FsValue)?.values as FsValue[]) ?? []).map(decode);\n if (\"mapValue\" in v) return decodeFields(((v.mapValue as FsValue)?.fields as Record<string, FsValue>) ?? {});\n return null;\n}\nfunction decodeFields(fields: Record<string, FsValue>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(fields).map(([k, x]) => [k, decode(x)]));\n}\n\ninterface Filter { fieldFilter: { field: { fieldPath: string }; op: \"EQUAL\"; value: { stringValue: string } } }\nfunction eq(fieldPath: string, value: string): Filter {\n return { fieldFilter: { field: { fieldPath }, op: \"EQUAL\", value: { stringValue: value } } };\n}\n\nasync function restQuery(siteId: string, filters: Filter[], limit?: number): Promise<StoredPage[]> {\n const apiKey = process.env.NEXT_PUBLIC_FIREBASE_API_KEY;\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID;\n if (!apiKey || !projectId) return [];\n const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/sites/${siteId}:runQuery?key=${apiKey}`;\n const where = filters.length === 1 ? filters[0] : { compositeFilter: { op: \"AND\", filters } };\n const structuredQuery: Record<string, unknown> = { from: [{ collectionId: \"pages\" }], where };\n if (limit) structuredQuery.limit = limit;\n // Netwerk-/parsefouten mogen een publieke pagina nooit laten crashen (500) — dan liever een\n // lege lijst (Next toont dan een 404 of de fallback, en de volgende revalidate probeert opnieuw).\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ structuredQuery }),\n // Pagina's zijn ISR-gecached; next-cache mag dit hergebruiken.\n next: { revalidate: 300 },\n });\n if (!res.ok) return [];\n const rows = (await res.json()) as { document?: { name: string; fields: Record<string, FsValue> } }[];\n return rows\n .filter((r) => r.document)\n .map((r) => parse(r.document!.name.split(\"/\").pop()!, decodeFields(r.document!.fields)))\n .filter((p): p is StoredPage => p !== null);\n } catch (err) {\n console.error(\"[xicms] Firestore REST-query mislukt:\", err);\n return [];\n }\n}\n\n/** Alle gepubliceerde pagina's (sitemap / generateStaticParams). Bundle-first, anders keyless/Admin. */\nexport async function listPublishedPages(siteId: string): Promise<StoredPage[]> {\n const bundle = getBundle();\n if (bundle && bundle.siteId === siteId) {\n return bundle.pages.map((p) => parse(p.id, p)).filter((p): p is StoredPage => p !== null);\n }\n const adb = getAdminDb();\n if (adb) {\n return safeAdminRead([], `listPublishedPages ${siteId}`, async () => {\n const snap = await adb.collection(`sites/${siteId}/pages`).where(\"status\", \"==\", \"published\").get();\n return snap.docs.map((d) => parse(d.id, d.data())).filter((p): p is StoredPage => p !== null);\n });\n }\n return restQuery(siteId, [eq(\"status\", \"published\")]);\n}\n\n/**\n * Per-request-dedup via React cache(): metadata (generateMetadata) én de render lezen dezelfde\n * pagina — zonder cache() waren dat twee identieke Firestore-reads per request. NB: de\n * `next:{revalidate}`-hint op het REST-pad dekt dit niet (POST-fetches cachen niet); route-level\n * ISR (revalidate=300 in de consumer) blijft de echte kosten-demper tussen requests.\n */\nexport const getPublishedPageByPath = cache(getPublishedPageByPathUncached);\nexport const getSiteSymbols = cache(getSiteSymbolsUncached);\nexport const getSiteCustomBlocks = cache(getSiteCustomBlocksUncached);\nexport const getPublicSiteSettings = cache(getPublicSiteSettingsUncached);\n\n/** Eén gepubliceerde pagina op pad. Bundle-first, anders keyless via REST/Admin. */\nasync function getPublishedPageByPathUncached(siteId: string, path: string): Promise<StoredPage | null> {\n const bundle = getBundle();\n if (bundle && bundle.siteId === siteId) {\n const hit = getBundlePage(bundle, path);\n return hit ? parse(hit.id, hit) : null;\n }\n const adb = getAdminDb();\n if (adb) {\n return safeAdminRead(null, `getPublishedPageByPath ${siteId}${path}`, async () => {\n const snap = await adb\n .collection(`sites/${siteId}/pages`)\n .where(\"status\", \"==\", \"published\")\n .where(\"path\", \"==\", path)\n .limit(1)\n .get();\n const d = snap.docs[0];\n return d ? parse(d.id, d.data()) : null;\n });\n }\n const rows = await restQuery(siteId, [eq(\"status\", \"published\"), eq(\"path\", path)], 1);\n return rows[0] ?? null;\n}\n\n/** Draft-versie (voor preview achter auth). */\nexport async function getDraftPageById(siteId: string, pageId: string): Promise<StoredPage | null> {\n const db = getAdminDb();\n if (!db) return null;\n return safeAdminRead(null, `getDraftPageById ${siteId}/${pageId}`, async () => {\n const d = await db.doc(`sites/${siteId}/drafts/${pageId}`).get();\n return d.exists ? parse(d.id, d.data()) : null;\n });\n}\n\n/** Draft op pad (preview). */\nexport async function getDraftPageByPath(siteId: string, path: string): Promise<StoredPage | null> {\n const db = getAdminDb();\n if (!db) return null;\n return safeAdminRead(null, `getDraftPageByPath ${siteId}${path}`, async () => {\n const snap = await db.collection(`sites/${siteId}/drafts`).where(\"path\", \"==\", path).limit(1).get();\n const d = snap.docs[0];\n return d ? parse(d.id, d.data()) : null;\n });\n}\n\n/* ---------------- symbols & custom blocks (5.0) ---------------- */\n\n/** Generieke publieke collectie-read onder sites/{siteId}/ — Admin als beschikbaar, anders REST. */\nasync function readCollection(siteId: string, collectionId: string): Promise<{ id: string; data: Record<string, unknown> }[]> {\n const adb = getAdminDb();\n if (adb) {\n return safeAdminRead([], `readCollection ${siteId}/${collectionId}`, async () => {\n const snap = await adb.collection(`sites/${siteId}/${collectionId}`).get();\n return snap.docs.map((d) => ({ id: d.id, data: d.data() as Record<string, unknown> }));\n });\n }\n const apiKey = process.env.NEXT_PUBLIC_FIREBASE_API_KEY;\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID;\n if (!apiKey || !projectId) return [];\n const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/sites/${siteId}:runQuery?key=${apiKey}`;\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ structuredQuery: { from: [{ collectionId }] } }),\n next: { revalidate: 300 },\n });\n if (!res.ok) return [];\n const rows = (await res.json()) as { document?: { name: string; fields: Record<string, FsValue> } }[];\n return rows\n .filter((r) => r.document)\n .map((r) => ({ id: r.document!.name.split(\"/\").pop()!, data: decodeFields(r.document!.fields ?? {}) }));\n } catch (err) {\n console.error(`[xicms] Firestore REST-read van ${collectionId} mislukt:`, err);\n return [];\n }\n}\n\n/** Alle symbolen van een site (gevalideerd; ongeldige overgeslagen met waarschuwing). Bundle-first. */\nasync function getSiteSymbolsUncached(siteId: string): Promise<StoredSymbol[]> {\n const bundle = getBundle();\n const rows =\n bundle && bundle.siteId === siteId\n ? bundle.symbols.map((s) => ({ id: s.id, data: s as Record<string, unknown> }))\n : await readCollection(siteId, \"symbols\");\n const out: StoredSymbol[] = [];\n for (const r of rows) {\n const res = SymbolSchema.safeParse(r.data);\n if (res.success) out.push({ id: r.id, ...res.data });\n else console.warn(`[xicms] symbool \"${r.id}\" ongeldig — overgeslagen.`);\n }\n return out;\n}\n\n/**\n * Vervangt symbolRef-blokken door de blokken van hun symbool (één niveau — symbolen kunnen zelf\n * geen symbolRef bevatten). `_key`s van de ingevoegde blokken worden geprefixt met de ref-key\n * zodat ze uniek blijven wanneer hetzelfde symbool vaker op een pagina staat. Verweesde refs\n * verdwijnen stil (de pagina mag nooit breken op een verwijderd symbool).\n */\nexport function expandSymbols<T extends { blocks?: unknown[] }>(page: T, symbols: StoredSymbol[]): T {\n const byId = new Map(symbols.map((s) => [s.id, s]));\n const blocks = (page.blocks ?? []).flatMap((b) => {\n const blk = b as { _type?: string; _key?: string; symbolId?: string };\n if (blk?._type !== \"symbolRef\") return [b];\n const sym = blk.symbolId ? byId.get(blk.symbolId) : undefined;\n if (!sym) return [];\n return sym.blocks.map((sb, i) => ({ ...(sb as Record<string, unknown>), _key: `${blk._key ?? \"sym\"}-${(sb as { _key?: string })._key ?? i}` }));\n });\n return { ...page, blocks };\n}\n\n/**\n * Gepubliceerde pagina op pad mét symbolRef-expansie (SEO: symboolinhoud zit in de SSR-HTML).\n * Geef `locale` mee (een NIET-default taal) om de vertaal-overlay toe te passen — op de pagina\n * én op de ingevoegde symbolen (elk symbool draagt zijn eigen `translations`).\n *\n * Bundle-first werkt hier automatisch: deze functie componeert `getPublishedPageByPath` en\n * `getSiteSymbols`, die beide al bundle-first zijn — de i18n-overlay en expansie draaien identiek\n * op bundle- én Firestore-data.\n */\nexport async function getPublishedPageByPathExpanded(\n siteId: string,\n path: string,\n locale?: string,\n): Promise<StoredPage | null> {\n let page = await getPublishedPageByPath(siteId, path);\n if (!page) return null;\n // De caller geeft alléén een niet-default taal door; \"\" als default matcht dus nooit.\n if (locale) page = localizePage(page, locale, \"\");\n if (!(page.blocks ?? []).some((b) => (b as { _type?: string })._type === \"symbolRef\")) return page;\n let symbols = await getSiteSymbols(siteId);\n if (locale) {\n symbols = symbols.map((s) => ({\n ...s,\n blocks: localizeBlocks(s.blocks as Array<{ _key?: string }>, s.translations?.[locale]) as typeof s.blocks,\n }));\n }\n return expandSymbols(page, symbols);\n}\n\n/**\n * Publieke site-instellingen (`sites/{siteId}/settings/public`) server-side — voor de\n * `settingsData`-prop van XiCMSProvider. Zo zit het klant-thema/nav/footer al in de server-HTML\n * (geen theme-flits bij eerste paint) én vervalt de per-bezoeker `getDoc` in de browser: de\n * layout-route is ISR-gecached, dus dit kost ~1 read per revalidate i.p.v. per bezoeker.\n * Fouten leveren `{}` op — de site valt dan terug op de statische SiteConfig.\n */\nasync function getPublicSiteSettingsUncached(siteId: string): Promise<PublicSiteSettings> {\n const bundle = getBundle();\n if (bundle && bundle.siteId === siteId) {\n const parsed = PublicSiteSettingsSchema.safeParse(bundle.settings ?? {});\n return parsed.success ? parsed.data : {};\n }\n const adb = getAdminDb();\n let data: unknown = {};\n if (adb) {\n data = await safeAdminRead<unknown>({}, `getPublicSiteSettings ${siteId}`, async () => {\n const d = await adb.doc(`sites/${siteId}/settings/public`).get();\n return d.exists ? d.data() : {};\n });\n } else {\n const apiKey = process.env.NEXT_PUBLIC_FIREBASE_API_KEY;\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID;\n if (apiKey && projectId) {\n const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/sites/${siteId}/settings/public?key=${apiKey}`;\n try {\n // GET-fetches worden — anders dan de runQuery-POSTs — wél door de Next-datacache gedekt.\n const res = await fetch(url, { next: { revalidate: 300 } });\n if (res.ok) {\n const doc = (await res.json()) as { fields?: Record<string, FsValue> };\n data = decodeFields(doc.fields ?? {});\n }\n } catch (err) {\n console.error(\"[xicms] Firestore REST-read van settings/public mislukt:\", err);\n }\n }\n }\n const parsed = PublicSiteSettingsSchema.safeParse(data ?? {});\n return parsed.success ? parsed.data : {};\n}\n\n/** Alle custom blocks van een site (gevalideerd) — voor SSR-registratie via XiCMSProvider. Bundle-first. */\nasync function getSiteCustomBlocksUncached(siteId: string): Promise<CustomBlock[]> {\n const bundle = getBundle();\n const rows =\n bundle && bundle.siteId === siteId\n ? bundle.customBlocks.map((c) => ({ id: c.id, data: c as Record<string, unknown> }))\n : await readCollection(siteId, \"customBlocks\");\n const out: CustomBlock[] = [];\n for (const r of rows) {\n const res = CustomBlockSchema.safeParse({ ...r.data, type: r.data.type ?? r.id });\n if (res.success) out.push(res.data);\n else console.warn(`[xicms] custom block \"${r.id}\" ongeldig — overgeslagen.`);\n }\n return out;\n}\n"],"mappings":"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","names":["parsed"]}
1
+ {"version":3,"sources":["../../src/server/pages.ts"],"sourcesContent":["import \"server-only\";\n\nimport { cache } from \"react\";\n\nimport { getAdminDb } from \"../firebase/admin\";\nimport { PageSchema, type Page } from \"../schema/pages\";\nimport { SymbolSchema, type StoredSymbol } from \"../schema/symbols\";\nimport { CustomBlockSchema, type CustomBlock } from \"../schema/customBlocks\";\nimport { PublicSiteSettingsSchema, type PublicSiteSettings } from \"../schema/settings\";\nimport { localizePage, localizeBlocks } from \"../i18n/overlay\";\nimport { getBundle, getBundlePage } from \"./bundle\";\n\n/**\n * BUNDLE-FIRST, Firestore-fallback (5.7).\n *\n * Elke publieke reader hieronder kijkt eerst of er een gecommit content-bundle is (server/bundle.ts,\n * `content/published.bundle.json`). Zo ja → serveer daaruit met exact dezelfde return-vormen als het\n * Firestore-pad (incl. schema-validatie, i18n-overlay en symbol-expansie). Zo nee (dev/lokaal of een\n * repo zonder gecommit bundle) → val ongewijzigd terug op de Admin-SDK / keyless-REST-implementatie.\n * De bundle-tak is puur additief: signatures en gedrag van de fallback blijven identiek.\n */\n\nexport type StoredPage = Page & { id: string };\n\nfunction toPath(slugs: string[] | undefined): string {\n const clean = (slugs ?? []).filter(Boolean);\n return \"/\" + clean.join(\"/\");\n}\n\n/** Pad uit Next catch-all params (`[[...slug]]`). \"/\" voor de root. */\nexport function pathFromSlugs(slugs: string[] | undefined): string {\n const p = toPath(slugs);\n return p === \"/\" ? \"/\" : p.replace(/\\/$/, \"\");\n}\n\nfunction parse(id: string, data: unknown): StoredPage | null {\n const res = PageSchema.safeParse(data);\n if (!res.success) {\n // Niet stil 404'en: een gepubliceerde pagina die het schema niet haalt is een diagnose-signaal\n // (schema-evolutie zonder default, of corrupte write) — zelfde patroon als symbols/customBlocks.\n console.warn(`[xicms] pagina \"${id}\" faalt schema-validatie en wordt overgeslagen:`, res.error.issues[0]);\n return null;\n }\n return { id, ...res.data };\n}\n\n/**\n * Admin-SDK-reads mogen een PUBLIEKE pagina nooit laten 500'en — zelfde belofte als het REST-pad\n * (dat al overal try/catch → lege fallback heeft). Een Firestore-hikje wordt gelogd en levert de\n * fallback op; de route rendert dan 404/fallback i.p.v. een servererror.\n */\nasync function safeAdminRead<T>(fallback: T, what: string, fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (e) {\n console.warn(`[xicms] Admin-read mislukt (${what}) — fallback gebruikt:`, (e as Error)?.message ?? e);\n return fallback;\n }\n}\n\n/* ---------------- keyless public reads via Firestore REST (alleen apiKey) ----------------\n * Gepubliceerde pagina's zijn publiek leesbaar (rules). De Firestore REST-API honoreert die\n * rules met enkel de web-apiKey (geen Admin-credentials, geen client-SDK), dus dit werkt\n * server-side op keyless deployments (org-policy zonder service-account-keys).\n */\ntype FsValue = Record<string, unknown>;\nfunction decode(v: FsValue): unknown {\n if (v == null) return null;\n if (\"stringValue\" in v) return v.stringValue;\n if (\"integerValue\" in v) return Number(v.integerValue);\n if (\"doubleValue\" in v) return v.doubleValue;\n if (\"booleanValue\" in v) return v.booleanValue;\n if (\"nullValue\" in v) return null;\n if (\"timestampValue\" in v) return v.timestampValue;\n if (\"referenceValue\" in v) return v.referenceValue;\n if (\"geoPointValue\" in v) return v.geoPointValue;\n if (\"arrayValue\" in v) return (((v.arrayValue as FsValue)?.values as FsValue[]) ?? []).map(decode);\n if (\"mapValue\" in v) return decodeFields(((v.mapValue as FsValue)?.fields as Record<string, FsValue>) ?? {});\n return null;\n}\nfunction decodeFields(fields: Record<string, FsValue>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(fields).map(([k, x]) => [k, decode(x)]));\n}\n\ninterface Filter { fieldFilter: { field: { fieldPath: string }; op: \"EQUAL\"; value: { stringValue: string } } }\nfunction eq(fieldPath: string, value: string): Filter {\n return { fieldFilter: { field: { fieldPath }, op: \"EQUAL\", value: { stringValue: value } } };\n}\n\nasync function restQuery(siteId: string, filters: Filter[], limit?: number): Promise<StoredPage[]> {\n const apiKey = process.env.NEXT_PUBLIC_FIREBASE_API_KEY;\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID;\n if (!apiKey || !projectId) return [];\n const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/sites/${siteId}:runQuery?key=${apiKey}`;\n const where = filters.length === 1 ? filters[0] : { compositeFilter: { op: \"AND\", filters } };\n const structuredQuery: Record<string, unknown> = { from: [{ collectionId: \"pages\" }], where };\n if (limit) structuredQuery.limit = limit;\n // Netwerk-/parsefouten mogen een publieke pagina nooit laten crashen (500) — dan liever een\n // lege lijst (Next toont dan een 404 of de fallback, en de volgende revalidate probeert opnieuw).\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ structuredQuery }),\n // Pagina's zijn ISR-gecached; next-cache mag dit hergebruiken.\n next: { revalidate: 300 },\n });\n if (!res.ok) return [];\n const rows = (await res.json()) as { document?: { name: string; fields: Record<string, FsValue> } }[];\n return rows\n .filter((r) => r.document)\n .map((r) => parse(r.document!.name.split(\"/\").pop()!, decodeFields(r.document!.fields)))\n .filter((p): p is StoredPage => p !== null);\n } catch (err) {\n console.error(\"[xicms] Firestore REST-query mislukt:\", err);\n return [];\n }\n}\n\n/** Alle gepubliceerde pagina's (sitemap / generateStaticParams). Bundle-first, anders keyless/Admin. */\nexport async function listPublishedPages(siteId: string): Promise<StoredPage[]> {\n const bundle = getBundle();\n if (bundle && bundle.siteId === siteId) {\n return bundle.pages.map((p) => parse(p.id, p)).filter((p): p is StoredPage => p !== null);\n }\n const adb = getAdminDb();\n if (adb) {\n return safeAdminRead([], `listPublishedPages ${siteId}`, async () => {\n const snap = await adb.collection(`sites/${siteId}/pages`).where(\"status\", \"==\", \"published\").get();\n return snap.docs.map((d) => parse(d.id, d.data())).filter((p): p is StoredPage => p !== null);\n });\n }\n return restQuery(siteId, [eq(\"status\", \"published\")]);\n}\n\n/**\n * Per-request-dedup via React cache(): metadata (generateMetadata) én de render lezen dezelfde\n * pagina — zonder cache() waren dat twee identieke Firestore-reads per request. NB: de\n * `next:{revalidate}`-hint op het REST-pad dekt dit niet (POST-fetches cachen niet); route-level\n * ISR (revalidate=300 in de consumer) blijft de echte kosten-demper tussen requests.\n */\nexport const getPublishedPageByPath = cache(getPublishedPageByPathUncached);\nexport const getSiteSymbols = cache(getSiteSymbolsUncached);\nexport const getSiteCustomBlocks = cache(getSiteCustomBlocksUncached);\nexport const getPublicSiteSettings = cache(getPublicSiteSettingsUncached);\n\n/**\n * Eén gepubliceerde pagina op pad. Bundle-first, anders keyless via REST/Admin.\n * `opts.skipBundle` (voorbeeld-modus, `?preview=1` in de visuele editor): negeert de gecommitte\n * bundel en leest rechtstreeks — de bundel komt pas met vertraging mee via de gedebouncede deploy-\n * worker, dus een beheerder die net gepubliceerd heeft ziet anders tot ~15 min lang de oude tekst.\n */\nasync function getPublishedPageByPathUncached(\n siteId: string,\n path: string,\n opts?: { skipBundle?: boolean },\n): Promise<StoredPage | null> {\n const bundle = opts?.skipBundle ? null : getBundle();\n if (bundle && bundle.siteId === siteId) {\n const hit = getBundlePage(bundle, path);\n return hit ? parse(hit.id, hit) : null;\n }\n const adb = getAdminDb();\n if (adb) {\n return safeAdminRead(null, `getPublishedPageByPath ${siteId}${path}`, async () => {\n const snap = await adb\n .collection(`sites/${siteId}/pages`)\n .where(\"status\", \"==\", \"published\")\n .where(\"path\", \"==\", path)\n .limit(1)\n .get();\n const d = snap.docs[0];\n return d ? parse(d.id, d.data()) : null;\n });\n }\n const rows = await restQuery(siteId, [eq(\"status\", \"published\"), eq(\"path\", path)], 1);\n return rows[0] ?? null;\n}\n\n/** Draft-versie (voor preview achter auth). */\nexport async function getDraftPageById(siteId: string, pageId: string): Promise<StoredPage | null> {\n const db = getAdminDb();\n if (!db) return null;\n return safeAdminRead(null, `getDraftPageById ${siteId}/${pageId}`, async () => {\n const d = await db.doc(`sites/${siteId}/drafts/${pageId}`).get();\n return d.exists ? parse(d.id, d.data()) : null;\n });\n}\n\n/** Draft op pad (preview). */\nexport async function getDraftPageByPath(siteId: string, path: string): Promise<StoredPage | null> {\n const db = getAdminDb();\n if (!db) return null;\n return safeAdminRead(null, `getDraftPageByPath ${siteId}${path}`, async () => {\n const snap = await db.collection(`sites/${siteId}/drafts`).where(\"path\", \"==\", path).limit(1).get();\n const d = snap.docs[0];\n return d ? parse(d.id, d.data()) : null;\n });\n}\n\n/* ---------------- symbols & custom blocks (5.0) ---------------- */\n\n/** Generieke publieke collectie-read onder sites/{siteId}/ — Admin als beschikbaar, anders REST. */\nasync function readCollection(siteId: string, collectionId: string): Promise<{ id: string; data: Record<string, unknown> }[]> {\n const adb = getAdminDb();\n if (adb) {\n return safeAdminRead([], `readCollection ${siteId}/${collectionId}`, async () => {\n const snap = await adb.collection(`sites/${siteId}/${collectionId}`).get();\n return snap.docs.map((d) => ({ id: d.id, data: d.data() as Record<string, unknown> }));\n });\n }\n const apiKey = process.env.NEXT_PUBLIC_FIREBASE_API_KEY;\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID;\n if (!apiKey || !projectId) return [];\n const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/sites/${siteId}:runQuery?key=${apiKey}`;\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ structuredQuery: { from: [{ collectionId }] } }),\n next: { revalidate: 300 },\n });\n if (!res.ok) return [];\n const rows = (await res.json()) as { document?: { name: string; fields: Record<string, FsValue> } }[];\n return rows\n .filter((r) => r.document)\n .map((r) => ({ id: r.document!.name.split(\"/\").pop()!, data: decodeFields(r.document!.fields ?? {}) }));\n } catch (err) {\n console.error(`[xicms] Firestore REST-read van ${collectionId} mislukt:`, err);\n return [];\n }\n}\n\n/** Alle symbolen van een site (gevalideerd; ongeldige overgeslagen met waarschuwing). Bundle-first. */\nasync function getSiteSymbolsUncached(siteId: string): Promise<StoredSymbol[]> {\n const bundle = getBundle();\n const rows =\n bundle && bundle.siteId === siteId\n ? bundle.symbols.map((s) => ({ id: s.id, data: s as Record<string, unknown> }))\n : await readCollection(siteId, \"symbols\");\n const out: StoredSymbol[] = [];\n for (const r of rows) {\n const res = SymbolSchema.safeParse(r.data);\n if (res.success) out.push({ id: r.id, ...res.data });\n else console.warn(`[xicms] symbool \"${r.id}\" ongeldig — overgeslagen.`);\n }\n return out;\n}\n\n/**\n * Vervangt symbolRef-blokken door de blokken van hun symbool (één niveau — symbolen kunnen zelf\n * geen symbolRef bevatten). `_key`s van de ingevoegde blokken worden geprefixt met de ref-key\n * zodat ze uniek blijven wanneer hetzelfde symbool vaker op een pagina staat. Verweesde refs\n * verdwijnen stil (de pagina mag nooit breken op een verwijderd symbool).\n */\nexport function expandSymbols<T extends { blocks?: unknown[] }>(page: T, symbols: StoredSymbol[]): T {\n const byId = new Map(symbols.map((s) => [s.id, s]));\n const blocks = (page.blocks ?? []).flatMap((b) => {\n const blk = b as { _type?: string; _key?: string; symbolId?: string };\n if (blk?._type !== \"symbolRef\") return [b];\n const sym = blk.symbolId ? byId.get(blk.symbolId) : undefined;\n if (!sym) return [];\n return sym.blocks.map((sb, i) => ({ ...(sb as Record<string, unknown>), _key: `${blk._key ?? \"sym\"}-${(sb as { _key?: string })._key ?? i}` }));\n });\n return { ...page, blocks };\n}\n\n/**\n * Gepubliceerde pagina op pad mét symbolRef-expansie (SEO: symboolinhoud zit in de SSR-HTML).\n * Geef `locale` mee (een NIET-default taal) om de vertaal-overlay toe te passen — op de pagina\n * én op de ingevoegde symbolen (elk symbool draagt zijn eigen `translations`).\n *\n * Bundle-first werkt hier automatisch: deze functie componeert `getPublishedPageByPath` en\n * `getSiteSymbols`, die beide al bundle-first zijn — de i18n-overlay en expansie draaien identiek\n * op bundle- én Firestore-data.\n */\nexport async function getPublishedPageByPathExpanded(\n siteId: string,\n path: string,\n locale?: string,\n opts?: { skipBundle?: boolean },\n): Promise<StoredPage | null> {\n let page = await getPublishedPageByPath(siteId, path, opts);\n if (!page) return null;\n // De caller geeft alléén een niet-default taal door; \"\" als default matcht dus nooit.\n if (locale) page = localizePage(page, locale, \"\");\n if (!(page.blocks ?? []).some((b) => (b as { _type?: string })._type === \"symbolRef\")) return page;\n let symbols = await getSiteSymbols(siteId);\n if (locale) {\n symbols = symbols.map((s) => ({\n ...s,\n blocks: localizeBlocks(s.blocks as Array<{ _key?: string }>, s.translations?.[locale]) as typeof s.blocks,\n }));\n }\n return expandSymbols(page, symbols);\n}\n\n/**\n * Publieke site-instellingen (`sites/{siteId}/settings/public`) server-side — voor de\n * `settingsData`-prop van XiCMSProvider. Zo zit het klant-thema/nav/footer al in de server-HTML\n * (geen theme-flits bij eerste paint) én vervalt de per-bezoeker `getDoc` in de browser: de\n * layout-route is ISR-gecached, dus dit kost ~1 read per revalidate i.p.v. per bezoeker.\n * Fouten leveren `{}` op — de site valt dan terug op de statische SiteConfig.\n */\nasync function getPublicSiteSettingsUncached(siteId: string): Promise<PublicSiteSettings> {\n const bundle = getBundle();\n if (bundle && bundle.siteId === siteId) {\n const parsed = PublicSiteSettingsSchema.safeParse(bundle.settings ?? {});\n return parsed.success ? parsed.data : {};\n }\n const adb = getAdminDb();\n let data: unknown = {};\n if (adb) {\n data = await safeAdminRead<unknown>({}, `getPublicSiteSettings ${siteId}`, async () => {\n const d = await adb.doc(`sites/${siteId}/settings/public`).get();\n return d.exists ? d.data() : {};\n });\n } else {\n const apiKey = process.env.NEXT_PUBLIC_FIREBASE_API_KEY;\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID;\n if (apiKey && projectId) {\n const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/sites/${siteId}/settings/public?key=${apiKey}`;\n try {\n // GET-fetches worden — anders dan de runQuery-POSTs — wél door de Next-datacache gedekt.\n const res = await fetch(url, { next: { revalidate: 300 } });\n if (res.ok) {\n const doc = (await res.json()) as { fields?: Record<string, FsValue> };\n data = decodeFields(doc.fields ?? {});\n }\n } catch (err) {\n console.error(\"[xicms] Firestore REST-read van settings/public mislukt:\", err);\n }\n }\n }\n const parsed = PublicSiteSettingsSchema.safeParse(data ?? {});\n return parsed.success ? parsed.data : {};\n}\n\n/** Alle custom blocks van een site (gevalideerd) — voor SSR-registratie via XiCMSProvider. Bundle-first. */\nasync function getSiteCustomBlocksUncached(siteId: string): Promise<CustomBlock[]> {\n const bundle = getBundle();\n const rows =\n bundle && bundle.siteId === siteId\n ? bundle.customBlocks.map((c) => ({ id: c.id, data: c as Record<string, unknown> }))\n : await readCollection(siteId, \"customBlocks\");\n const out: CustomBlock[] = [];\n for (const r of rows) {\n const res = CustomBlockSchema.safeParse({ ...r.data, type: r.data.type ?? r.id });\n if (res.success) out.push(res.data);\n else console.warn(`[xicms] custom block \"${r.id}\" ongeldig — overgeslagen.`);\n }\n return out;\n}\n"],"mappings":"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","names":["parsed"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiwebdesign/xicms-core",
3
- "version": "6.4.3",
3
+ "version": "6.4.4",
4
4
  "description": "XiCMS engine: gestructureerde content, pagina's, draft/publish, admin (TipTap+Radix), SEO en beveiliging voor klantsites.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {