@xiwebdesign/xicms-core 7.16.0 → 7.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 { registerBuiltinSchemas } from \"../../blocks/builtinSchemas\";\nimport { useModules } from \"../../modules/context\";\n// Schema's zijn lazy (7.13): dit is een editor-chunk, dus hier horen ze thuis.\nregisterBuiltinSchemas();\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 { useThemeVars } from \"../../XiCMSProvider\";\nimport { buildBlockAddOptions } from \"../../blocks/registry\";\nimport { safeParse } from \"zod/v4/core\";\nimport DOMPurify from \"dompurify\";\nimport { createPage, getDraftByPath, getPublishedPage, saveDraft, publishPage, subscribeDraft, subscribeVersions, restoreVersion, type DraftPage } from \"../lib/pagesClient\";\nimport { isDemoActive } from \"../../demo/demoStore\";\nimport { setProductField } from \"../shop/productClient\";\nimport { ProductPaneel } from \"../shop/ProductPaneel\";\nimport { builtinBlockEditors, GenericEditor, type BlockEditorProps } from \"../blockEditors\";\nimport { StyleEditor } from \"../blockEditors/StyleEditor\";\nimport { siteLocales, parseLocaleSlugs, localizeBlocks } from \"../../i18n/overlay\";\nimport { useEditor } from \"../../editor/EditorProvider\";\nimport { AdminModes } from \"../../components/EditBar\";\nimport { useBalkOffset } from \"../../components/barOffset\";\nimport { aantalWijzigingen, bepaalWijzigingen, GEEN_WIJZIGINGEN, type Wijzigingen } from \"./wijzigingen\";\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 // `field: \"html\"` is de AANNAME, geen wet: een element mag met `data-xicms-edit` zeggen welk veld\n // het werkelijk toont, en dat wint (zie de bind hieronder). Zonder die voorrang schreef élk\n // `.xicms-richtext`-element naar een veld dat letterlijk \"html\" heet — waar de FAQ zijn antwoord\n // in `items[].aHtml` bewaart en het accordeon in `items[].bodyHtml`. Inline bewerken van een\n // FAQ-antwoord landde daardoor op een niet-bestaand veld: de wijziging verdween stil en het blok\n // hield een zwerfveld over.\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/**\n * Dev-vangnet (5.0): meld tekstvelden zonder inline-haak — één keer per bloktype. Vooral voor\n * MAATWERKblokken in een klantrepo; de ingebouwde blokken worden statisch en volledig gedekt door\n * `npm run check:inline`.\n *\n * Een veld telt alleen als het zijn waarde ook echt als tekst op het scherm zet. Zonder die eis\n * riep dit elk blok na over lege velden (een niet-ingevulde `eyebrow` rendert niet, dus valt er\n * niets aan te klikken) en over configuratie die toevallig een tekstveld is (`overlayColor`).\n * Zulke loze meldingen leren je de waarschuwing wegkijken, en dan mist hij zijn doel.\n */\nconst inlineCoverageWarned = new Set<string>();\nfunction warnInlineCoverage(\n type: string,\n def: { schema?: Parameters<typeof introspectToFields>[0]; fields?: Parameters<typeof introspectToFields>[1] } | undefined,\n host: HTMLElement,\n values: Record<string, unknown>,\n) {\n if (process.env.NODE_ENV === \"production\" || !def || inlineCoverageWarned.has(type)) return;\n inlineCoverageWarned.add(type);\n const zichtbaar = host.textContent ?? \"\";\n const missing = introspectToFields(def.schema, def.fields)\n .filter((f) => f.type === \"text\" || f.type === \"textarea\")\n .filter((f) => {\n const waarde = getPath(values, f.key);\n return typeof waarde === \"string\" && waarde.trim().length > 1 && zichtbaar.includes(waarde.trim());\n })\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 // Het zijpaneel stond altijd open en nam 360px van het canvas. Sinds élk tekstveld inline\n // bewerkbaar is, is dat paneel niet meer de hoofdweg maar het uitzonderingsgeval: de instellingen\n // die je niet op de pagina ziet (een link achter een knop, een kleur, een limiet). Het opent\n // daarom op verzoek — via het tandwiel bij het blok — en het canvas krijgt de volle breedte.\n const [panelOpen, setPanelOpen] = useState(false);\n const [status, setStatus] = useState<Status>(\"loading\");\n const [selectedKey, setSelectedKey] = useState<string | null>(null);\n /** Staat het zijpaneel op een PRODUCT in plaats van op een blok? Dan wint het product. */\n const [selectedProduct, setSelectedProduct] = 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 * De blokken zoals ze NU LIVE staan — de maatstaf voor \"wat heb ik veranderd\".\n *\n * `undefined` = nog niet gelezen (dan zeggen we niets, want een lege pagina rood kleuren omdat\n * de lezing nog loopt is erger dan even niets tonen). `null` = gelezen, maar deze pagina is nog\n * nooit gepubliceerd.\n */\n const [liveBlocks, setLiveBlocks] = useState<Block[] | null | undefined>(undefined);\n useBalkOffset(true);\n\n // ── GEEN modale discipline meer ───────────────────────────────────────────\n // Tot 7.10 stond hier het complete WAI-ARIA-dialoogpatroon: `useScrollLock(true)` en een\n // focus-trap om de shell. Dat was juist toen de editor een schermvullende overlay met eigen\n // canvas was — en is precies verkeerd nu de editor gereedschap óver de live pagina is:\n //\n // - De scroll-lock pinde de body vast (`position: fixed`). De site zelf kon niet meer\n // scrollen, dus bestonden er voor alles onder de vouw ook geen bewerkknoppen. \"De\n // bewerkknop verschijnt niet bij elk blok\" wás geen meetfout van de knoppenlaag — de\n // pagina kon er simpelweg niet komen.\n // - De focus-trap bewaakte de shell, maar de tekst die je bewerkt staat op de PAGINA,\n // buiten die shell. Elke focus op een net-contenteditable gemaakt veld werd door de trap\n // teruggetrokken naar de werkbalk: potlood aan, caret meteen weer weg. Dat inline bewerken\n // \"lang niet altijd\" werkte, was deze trap die won of verloor van een race.\n //\n // De pagina is het werkgebied; alleen echte dialogen (mediakiezer, bevestiging) zijn modaal,\n // en die regelen hun eigen trap en lock via Astryx.\n // Het canvas is een VOORBEELD van de live pagina en moet dus dezelfde merk-tokens dragen.\n // De editor rendert buiten `.xicms-root`, dus die erven we hier expliciet via context.\n const { vars: themeVars, darkAuto } = useThemeVars();\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // Focus de eerste bediening bij openen, en geef hem bij sluiten terug aan het element dat de\n // editor opende (\"Pagina bewerken\"). Zonder dat teruggeven valt de focus terug naar <body> en\n // begint een toetsenbordgebruiker weer bovenaan de pagina.\n useEffect(() => {\n const opener = document.activeElement as HTMLElement | null;\n containerRef.current\n ?.querySelector<HTMLElement>(\"button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])\")\n ?.focus();\n return () => opener?.focus?.();\n }, []);\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 /**\n * Undo die een refresh overleeft (7.14). Een F5 — of een link in je eigen site, dat is dezelfde\n * volledige navigatie — gooide de complete undo-stapel weg, precies op momenten dat je hem\n * nodig kunt hebben. De laatste 25 stappen gaan daarom mee in sessionStorage (per tabblad,\n * verdwijnt met het tabblad — zoals de bewerkmodus zelf). Alleen `past`: een redo-toekomst\n * over een refresh heen is verwarrender dan behulpzaam. Alles best-effort in try/catch: een\n * volle of geblokkeerde opslag mag nooit de editor raken.\n */\n const undoOpslagSleutel = useCallback((id: string) => `xicms-undo:${siteId}:${id}`, [siteId]);\n const bewaarHistory = useCallback(() => {\n try {\n const id = draftRef.current?.id;\n if (!id) return;\n sessionStorage.setItem(undoOpslagSleutel(id), JSON.stringify(historyRef.current.past.slice(-25)));\n } catch { /* opslag vol/geblokkeerd: dan geen persistente undo */ }\n }, [undoOpslagSleutel]);\n const herstelHistory = useCallback((id: string) => {\n try {\n const raw = sessionStorage.getItem(undoOpslagSleutel(id));\n if (!raw || historyRef.current.past.length > 0) return;\n const past = JSON.parse(raw) as DraftPage[];\n if (Array.isArray(past) && past.length) {\n historyRef.current.past = past;\n setHistVersion((v) => v + 1);\n }\n } catch { /* corrupte opslag: verse stapel */ }\n }, [undoOpslagSleutel]);\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 // Een veld dat pas op blur committeert (bv. het JSON-redmiddel in GenericEditor) moet die\n // commit eerst nog uitvoeren. Op desktop vuurt blur al vóór de click van deze knop, maar op\n // mobiel Safari is die volgorde niet gegarandeerd (het toetsenbord-sluiten kan blur vertragen\n // tot ná de klik) — expliciet blur() forceert de commit synchroon, ongeacht platform.\n (document.activeElement as HTMLElement | null)?.blur();\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 /**\n * Eénmalige starthint. De knoppen zijn er allemaal, maar wie de bewerkmodus voor het eerst\n * opent moet ze nu zelf ontdekken — en \"waar begin ik?\" is precies het moment waarop een\n * klant afhaakt of gaat bellen. Eén zin, één keer, weg te klikken; daarna nooit meer\n * (localStorage). Bewust geen tour/overlay: de pagina zelf is de uitleg.\n */\n const [toonStartHint, setToonStartHint] = useState(false);\n useEffect(() => {\n try {\n if (!localStorage.getItem(\"xicms-hint-bewerken-v1\")) setToonStartHint(true);\n } catch { /* geblokkeerde opslag: dan geen hint — nooit een kapotte editor om een hint */ }\n }, []);\n const sluitStartHint = useCallback(() => {\n setToonStartHint(false);\n try { localStorage.setItem(\"xicms-hint-bewerken-v1\", \"1\"); } catch { /* idem */ }\n }, []);\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 herstelHistory(d.id);\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 /**\n * Lees erbij wat er live staat, zodat de markering en de publiceerknop ergens op slaan.\n *\n * Eén keer per pagina, niet als abonnement: de gepubliceerde versie verandert alleen doordat\n * JIJ publiceert, en dat moment wordt hieronder zelf afgehandeld. Een tweede live-listener zou\n * bij elke autosave-echo opnieuw willen vergelijken zonder dat er iets veranderd is.\n */\n useEffect(() => {\n const id = draft?.id;\n if (!id) {\n setLiveBlocks(undefined);\n return;\n }\n let active = true;\n getPublishedPage(siteId, id)\n .then((pagina) => {\n if (active) setLiveBlocks((pagina?.blocks as Block[] | undefined) ?? null);\n })\n // Mislukte lezing: dan liever géén markering dan een verkeerde. `undefined` houdt de\n // publiceerknop bereikbaar maar rustig, en niets kleurt rood.\n .catch(() => active && setLiveBlocks(undefined));\n return () => {\n active = false;\n };\n // Alleen op de identiteit van de pagina: de INHOUD van de werkversie verandert continu en mag\n // deze lezing niet opnieuw aanjagen.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [siteId, draft?.id]);\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 // Via een ref, zodat het bind-effect hieronder NIET op elke draft-wijziging opnieuw hoeft te\n // draaien. Deed het dat wel, dan brak een autosave van de vorige bewerking de bewerking af die\n // je net was begonnen: de cleanup haalde contentEditable weg en je toetsaanslagen verdwenen.\n const blocksRef = useRef(blocks);\n blocksRef.current = blocks;\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 /**\n * Wat staat er nog niet live? Hierop leunen de rode markering én de knop rechtsonder.\n *\n * Bewust op `blocks` en niet op `displayBlocks`: in vertaalmodus is `displayBlocks` de\n * samengevoegde weergave (standaardtaal + overlay), en die zou élk vertaald veld als wijziging\n * aanwijzen. Publiceren gaat over de bron, dus vergelijken we de bron.\n */\n /** Voor de waarschuwing bij wegnavigeren: die luistert buiten React om en heeft een ref nodig. */\n const nietLiveRef = useRef(false);\n const wijzigingen: Wijzigingen = useMemo(\n () => (liveBlocks === undefined ? GEEN_WIJZIGINGEN : bepaalWijzigingen(blocks, liveBlocks)),\n [blocks, liveBlocks],\n );\n nietLiveRef.current = wijzigingen.iets;\n\n /**\n * Duw de werkversie naar de PAGINA. Dit is de kern van het nieuwe bewerken: de site rendert\n * zichzelf — eigen layout, eigen huisstijl, eigen componenten — maar met deze blokken in plaats\n * van de gepubliceerde. Daardoor is wat je ziet precies wat je bewerkt, zonder dat de editor de\n * pagina hoeft na te bouwen.\n *\n * `displayBlocks` en niet `blocks`: bewerk je een vertaling, dan hoort de pagina die vertaling te\n * tonen en niet het origineel.\n */\n const { publishDraft, zetFotoVervanger } = useEditor();\n\n // Module-schema's lazy laden (7.14): zelfde reden als registerBuiltinSchemas hierboven — de\n // schema's van actieve modules (shop) zijn editor-kennis en komen pas binnen als er bewerkt\n // wordt. Fouten stil: een module zonder schema's valt terug op de lichte vormcheck.\n const actieveModules = useModules();\n useEffect(() => {\n for (const m of actieveModules.defs) {\n if (actieveModules.activeIds.has(m.id)) void m.loadSchemas?.().catch(() => {});\n }\n }, [actieveModules]);\n\n /**\n * De 📷-knopjes op de pagina aansluiten op de mediakiezer van deze editor.\n *\n * Dit herstelt een stille regressie: `InlineImageEditProvider` werd sinds het verdwijnen van het\n * canvas nergens meer gerenderd, dus geen enkel beeld toonde nog \"Foto wijzigen\" — de import\n * stond er nog, de dialoog stond er nog, alleen de draad ertussen was weg. Een klant kon\n * daardoor geen foto vervangen zonder naar /beheer te gaan.\n */\n useEffect(() => {\n zetFotoVervanger((field, blockKey) => {\n if (blockKey) setImgPicker({ key: blockKey, field });\n });\n return () => zetFotoVervanger(null);\n }, [zetFotoVervanger]);\n const laatstGepubliceerd = useRef<string>(\"\");\n useEffect(() => {\n if (!draft) return;\n // ALLEEN bij een echte inhoudelijke wijziging. Dit effect duwt state naar een context waar de\n // hele pagina op meeleest — en dus ook deze editor. Duw je bij elke render, dan veroorzaakt de\n // render een update en de update een render: \"Maximum update depth exceeded\". Een vergelijking\n // op reference is niet genoeg, want `displayBlocks` kan een nieuwe array zijn met dezelfde\n // inhoud; vandaar een stabiele vergelijking op de inhoud zelf.\n const vinger = stableStringify(displayBlocks);\n if (vinger === laatstGepubliceerd.current) return;\n laatstGepubliceerd.current = vinger;\n publishDraft(path, displayBlocks);\n }, [draft, displayBlocks, path, publishDraft]);\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 // ── Inline bewerken: eerst het potlood, dan pas de tekst ──────────────────\n // Tot 7.3 werd élk tekstveld van het geselecteerde blok meteen contentEditable. Dat leest als\n // \"alles staat open\": één misklik in een kop en je typt al in de pagina, met autosave eronder.\n // Nu markeert de editor de bewerkbare velden alleen (een subtiele stippellijn) en zet een\n // potloodje ernaast de bewerking BEWUST aan — pas daarna verschijnen caret en opmaak-toolbar.\n // Dat maakt bovendien zichtbaar wát inline kan; dat wist je voorheen pas als je erop klikte.\n /**\n * Tandwielen naast elk blok OP DE PAGINA.\n *\n * Dit zat vroeger in de werkbalkjes van het canvas. Nu er geen canvas meer is, moeten ze over de\n * echte blokken heen — dezelfde aanpak als de potloodjes: vast gepositioneerd in de editorlaag,\n * en opnieuw meten zodra er gescrold of geschaald wordt. De pagina zelf wordt niet aangeraakt,\n * want dat is de site van de klant.\n */\n // Via een ref: de registry is een context-waarde en mag de meting niet opnieuw aanjagen.\n // Via een ref: de registry is een context-waarde en mag de meting niet opnieuw aanjagen.\n const registryRef = useRef(registry);\n registryRef.current = registry;\n\n /**\n * Hoe ver van bovenaf is het veilig om iets neer te zetten?\n *\n * De knoppen van het bovenste blok landden onder de balk van de editor én onder de plakkende\n * menubalk van de site zelf. Ze wáren er wel — ze lagen alleen onder iets anders, en dus kon je\n * het hoofdblok als enige blok niet bewerken of instellen. Dat was geen randgeval: het eerste\n * blok van de homepagina is meestal juist het blok dat je wilt aanpassen.\n *\n * We meten daarom wat er werkelijk bovenaan vastgeplakt zit — de eigen balk plus elk plakkend of\n * vastgezet element van de site dat de bovenrand raakt — in plaats van een vast getal te kiezen\n * dat op de ene site klopt en op de andere niet.\n */\n function veiligeBovenkant(): number {\n let onderkant = 0;\n for (const el of Array.from(document.querySelectorAll<HTMLElement>(\"body *\"))) {\n const pos = getComputedStyle(el).position;\n if (pos !== \"fixed\" && pos !== \"sticky\") continue;\n const r = el.getBoundingClientRect();\n // Alleen wat écht bovenaan hangt telt mee; een plakkende knop onderin de pagina niet.\n if (r.height === 0 || r.top > 4) continue;\n onderkant = Math.max(onderkant, r.bottom);\n }\n return onderkant;\n }\n\n /**\n * Potlood én tandwiel bij ELK blok.\n *\n * Tot 7.8 stond er per blok alleen een tandwiel, en verschenen de potloden pas nádat je een blok\n * had geselecteerd — via datzelfde tandwiel. Inline bewerken, de hoofdweg van dit hele systeem,\n * zat dus achter de knop voor het uitzonderingsgeval. Nu staan beide er meteen: het potlood zet\n * de tekst van dat blok open, het tandwiel opent alles wat je niet kunt typen.\n */\n const [blokknoppen, setBlokknoppen] = useState<{ key: string; type: string; top: number; right: number; label: string }[]>([]);\n useEffect(() => {\n if (!draft) {\n // `setBlokknoppen([])` zou hier een NIEUWE lege array zetten bij elke render — en dus elke\n // render een update, en elke update een render: \"Maximum update depth exceeded\". De\n // functionele vorm geeft de bestaande array terug als er toch al niets stond.\n setBlokknoppen((v) => (v.length === 0 ? v : []));\n return;\n }\n /**\n * De veilige bovenkant is duur om te bepalen (hij loopt de hele DOM af op zoek naar plakkende\n * elementen) en verandert alleen bij een resize of een structurele wijziging — niet bij\n * scrollen. `meet()` draait wél bij elke scroll-tick; die twee horen dus niet in één adem.\n * Gemeten winst: van O(alle elementen) naar O(blokken) per scroll-frame.\n */\n let veilig = veiligeBovenkant();\n const herijk = () => {\n veilig = veiligeBovenkant();\n meet();\n };\n const meet = () => {\n const gevonden: { key: string; type: string; top: number; right: number; label: string }[] = [];\n for (const el of Array.from(document.querySelectorAll<HTMLElement>(\"[data-xicms-block]\"))) {\n const key = el.dataset.xicmsBlock;\n if (!key) continue;\n /**\n * Blokken zonder eigen blokstijl zitten in een `display:contents`-wrapper, en die meet\n * 0×0 — de wrapper doet niet mee aan layout, zijn KINDEREN wel. Dat 0×0 werd hier als\n * \"onzichtbaar, overslaan\" gelezen, waardoor het merendeel van de blokken op een\n * doorsnee pagina géén potlood en tandwiel kreeg: precies de blokken zonder aangepaste\n * stijl, oftewel bijna allemaal. Meet in dat geval de omtrek van de kinderen.\n */\n let r = el.getBoundingClientRect();\n if (r.width === 0 && r.height === 0 && el.children.length > 0) {\n let top = Infinity, right = -Infinity, bottom = -Infinity;\n for (const kind of Array.from(el.children)) {\n const kr = kind.getBoundingClientRect();\n if (kr.width === 0 && kr.height === 0) continue;\n top = Math.min(top, kr.top);\n right = Math.max(right, kr.right);\n bottom = Math.max(bottom, kr.bottom);\n }\n if (Number.isFinite(top)) r = { top, right, bottom, width: right, height: bottom - top } as DOMRect;\n }\n // Dan nog 0×0 (echt niets zichtbaars, bv. een script-achtig blok): geen aanwijzing maar ruis.\n if (r.width === 0 && r.height === 0) continue;\n // Volledig uit beeld gescrolde blokken hoeven geen knoppen: die zouden vastgeklemd worden\n // aan de boven- of onderrand en daar blijven zweven bij een blok dat je niet ziet.\n if (r.bottom < veilig || r.top > window.innerHeight) continue;\n const type = el.dataset.xicmsBlockType ?? \"\";\n gevonden.push({\n key,\n type,\n top: Math.max(r.top + 8, veilig + 8),\n // Vanaf de RECHTERkant verankerd: het palet klapt uit en moet die ruimte naar LINKS\n // pakken — een full-width blok eindigt op de viewportrand, rechts is daar per definitie\n // geen plek. Met een left-anker schoof het uitgeklapte deel het scherm af en leek de\n // hele uitbreiding gewoon niet te bestaan.\n right: Math.max(window.innerWidth - r.right + 8, 8),\n label: registryRef.current.get(type)?.label ?? type,\n });\n }\n setBlokknoppen((vorige) => {\n // Ongewijzigde posities niet terugschrijven: elke setState is een render, en een render\n // meet opnieuw. Zonder deze vergelijking draait dat rond.\n if (vorige.length === gevonden.length && vorige.every((v, i) =>\n v.key === gevonden[i].key && v.top === gevonden[i].top && v.right === gevonden[i].right)) {\n return vorige;\n }\n return gevonden;\n });\n };\n // rAF-bundeling: scroll- en resize-events komen in salvo's; meten hoeft hoogstens één keer\n // per frame. Zonder dit liep de meting tijdens het scrollen vaker dan het scherm ververst.\n let frame = 0;\n const meetGepland = () => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(meet);\n };\n meet();\n window.addEventListener(\"scroll\", meetGepland, { capture: true, passive: true });\n window.addEventListener(\"resize\", herijk);\n return () => {\n cancelAnimationFrame(frame);\n window.removeEventListener(\"scroll\", meetGepland, { capture: true });\n window.removeEventListener(\"resize\", herijk);\n };\n }, [draft, displayBlocks, registryRef]);\n\n /**\n * Producten krijgen hun eigen potlood en tandwiel, maar alleen waar je muis is.\n *\n * Een cataloguspagina toont er twaalf tegelijk. Twaalf vaste knoppenparen naast twaalf kaarten is\n * geen gereedschap meer maar een raster van knoppen over de site heen — dan kijk je niet meer naar\n * je winkel. Op aanwijzen verschijnt er precies één paar, bij het product waar je naar kijkt.\n */\n const [productKnop, setProductKnop] = useState<{ id: string; top: number; right: number } | null>(null);\n useEffect(() => {\n if (!draft) return;\n const onOver = (e: MouseEvent) => {\n const doel = (e.target as HTMLElement | null)?.closest?.(\"[data-xicms-product]\") as HTMLElement | null;\n if (!doel) {\n // Niet meteen weghalen zodra je de kaart verlaat: je verlaat hem juist om NAAR de knoppen te\n // gaan. De knoppen zelf zitten niet in de kaart, dus zonder deze uitzondering verdwijnen ze\n // op weg ernaartoe en is er niets aan te klikken.\n const naarKnop = (e.target as HTMLElement | null)?.closest?.(\".xicms-ve-blokknoppen\");\n if (!naarKnop) setProductKnop(null);\n return;\n }\n const id = doel.dataset.xicmsProduct;\n if (!id) return;\n const r = doel.getBoundingClientRect();\n const top = Math.max(r.top + 8, 44);\n const right = Math.max(window.innerWidth - r.right + 8, 8);\n setProductKnop((v) => (v && v.id === id && v.top === top && v.right === right ? v : { id, top, right }));\n };\n document.addEventListener(\"mouseover\", onOver, true);\n return () => document.removeEventListener(\"mouseover\", onOver, true);\n }, [draft]);\n\n /**\n * Inline bewerken van een PRODUCT — een ander pad dan een blokveld, met opzet.\n *\n * Een blokveld gaat naar de werkversie van deze pagina en wordt zichtbaar bij publiceren. Een\n * product staat in de productencollectie en wordt door élke pagina gelezen die het toont; daar\n * bestaat geen werkversie voor. Zie `productClient.ts` voor waarom dat zo blijft.\n */\n const productBewerkRef = useRef<HTMLElement | null>(null);\n const startProductInline = useCallback(\n (productId: string) => {\n const host = document.querySelector<HTMLElement>(`[data-xicms-product=\"${CSS.escape(productId)}\"]`);\n const el = host?.querySelector<HTMLElement>(\"[data-xicms-product-edit]\");\n if (!el) {\n toast({\n title: \"Hier valt niets te typen\",\n description: \"Gebruik het tandwiel voor prijs, foto's, varianten en voorraad.\",\n });\n return;\n }\n const veld = el.dataset.xicmsProductEdit;\n if (!veld) return;\n const origineel = el.textContent ?? \"\";\n el.setAttribute(\"contenteditable\", \"true\");\n el.setAttribute(\"spellcheck\", \"true\");\n el.classList.add(\"is-editing\");\n productBewerkRef.current = el;\n\n const stop = () => {\n el.removeAttribute(\"contenteditable\");\n el.classList.remove(\"is-editing\");\n el.removeEventListener(\"blur\", opBlur);\n el.removeEventListener(\"keydown\", opToets);\n if (productBewerkRef.current === el) productBewerkRef.current = null;\n };\n const opBlur = () => {\n const waarde = el.textContent ?? \"\";\n stop();\n if (waarde === origineel) return;\n void setProductField(siteId, productId, veld, waarde)\n .then(() =>\n toast({\n title: \"Product bijgewerkt\",\n description: \"Producten hebben geen werkversie: dit staat meteen live, op elke pagina.\",\n tone: \"success\",\n }),\n )\n .catch((err: Error) => {\n // Terugdraaien op het scherm, anders blijft er tekst staan die nergens bewaard is.\n el.textContent = origineel;\n toast({ title: \"Opslaan mislukt\", description: err.message, tone: \"error\" });\n });\n };\n const opToets = (ev: KeyboardEvent) => {\n if (ev.key === \"Escape\") {\n ev.stopPropagation();\n el.textContent = origineel;\n el.blur();\n } else if (ev.key === \"Enter\") {\n ev.preventDefault();\n el.blur();\n }\n };\n el.addEventListener(\"blur\", opBlur);\n el.addEventListener(\"keydown\", opToets);\n el.focus();\n const selection = window.getSelection();\n const range = document.createRange();\n range.selectNodeContents(el);\n range.collapse(false);\n selection?.removeAllRanges();\n selection?.addRange(range);\n },\n [siteId, toast],\n );\n\n /**\n * Rood en onderstreept: dit staat nog niet op de live site.\n *\n * De vraag die dit beantwoordt is \"wat heb ik nou eigenlijk veranderd?\" — en die was tot 7.8\n * onbeantwoordbaar. Je zag je eigen tekst staan en kon niet zien of een bezoeker hem al had.\n * Na publiceren wordt de markering vanzelf schoon, want dan is de werkversie gelijk aan wat er\n * live staat en levert `bepaalWijzigingen` niets meer op. De kleur is dus geen opmaak maar een\n * toestand: rood = van jou, gewone kleur = van iedereen.\n *\n * De klassen gaan op de LIVE DOM, net als de potloden — de render van de klant blijft schoon en\n * de markering verdwijnt met het gereedschap mee.\n */\n useEffect(() => {\n if (!draft) return;\n const gemarkeerd: HTMLElement[] = [];\n const markeer = (el: HTMLElement | null, klasse: string) => {\n if (!el) return;\n el.classList.add(klasse);\n gemarkeerd.push(el);\n };\n\n for (const key of wijzigingen.blokken) {\n markeer(document.querySelector<HTMLElement>(`[data-xicms-block=\"${CSS.escape(key)}\"]`), \"xicms-ve-nieuw\");\n }\n for (const [key, velden] of wijzigingen.velden) {\n const host = document.querySelector<HTMLElement>(`[data-xicms-block=\"${CSS.escape(key)}\"]`);\n if (!host) continue;\n for (const veld of velden) {\n // Het veldpad is dezelfde puntnotatie die het blok in zijn DOM-haak zet, dus dit is een\n // rechtstreekse treffer. Een veld dat wel wijzigde maar niets op het scherm zet (een kleur,\n // een limiet) heeft geen haak en levert hier gewoon niets op — terecht: er valt niets te\n // markeren aan iets wat je niet ziet. Dát er iets veranderde blijkt uit de knop rechtsonder.\n host\n .querySelectorAll<HTMLElement>(`[data-xicms-edit=\"${CSS.escape(veld)}\"]`)\n .forEach((el) => markeer(el, \"xicms-ve-gewijzigd\"));\n }\n }\n\n return () => {\n for (const el of gemarkeerd) {\n el.classList.remove(\"xicms-ve-nieuw\", \"xicms-ve-gewijzigd\");\n }\n };\n // `displayBlocks` staat erbij omdat de DOM-nodes na een re-render vervangen kunnen zijn: dan\n // moet er opnieuw gemarkeerd worden, ook al is de verzameling wijzigingen dezelfde.\n }, [draft, wijzigingen, displayBlocks]);\n\n const [spots, setSpots] = useState<{ top: number; left: number; title: string }[]>([]);\n const targetsRef = useRef<{ el: HTMLElement; field: string; html: boolean }[]>([]);\n const editingRef = useRef<HTMLElement | null>(null);\n /** Gezet door het potlood bij een blok: na het binden meteen in het eerste veld staan. */\n const wilInlineRef = useRef(false);\n /**\n * Welke spot (veld-potlood) hoort bij het veld dat NU bewerkt wordt? Dat potlood wordt een groen\n * vinkje (\"het is goed, klaar met dit blok\") — de afronding op de plek waar je hand al is, in\n * plaats van alleen in het blokknoppen-cluster rechtsboven het blok, buiten je blikveld.\n */\n const [editingIndex, setEditingIndex] = useState<number | null>(null);\n\n const stopInline = useCallback((el: HTMLElement) => {\n el.removeAttribute(\"contenteditable\");\n el.classList.remove(\"is-editing\");\n if (editingRef.current === el) {\n editingRef.current = null;\n setEditingIndex(null);\n }\n }, []);\n\n /** Eerlijk melden waarom het potlood niets opent — bij een blok zonder inline-velden. */\n const meldGeenTekst = useCallback(() => {\n toast({\n title: \"Dit blok heeft geen eigen tekst\",\n description: \"Wijs een product aan voor zijn naam, of gebruik het tandwiel voor de instellingen van dit blok.\",\n });\n }, [toast]);\n\n const startInline = useCallback((index: number) => {\n const target = targetsRef.current[index];\n if (!target) return;\n const { el } = target;\n if (editingRef.current && editingRef.current !== el) editingRef.current.blur();\n el.setAttribute(\"contenteditable\", \"true\");\n el.setAttribute(\"spellcheck\", \"true\");\n el.classList.add(\"is-editing\");\n editingRef.current = el;\n setEditingIndex(index);\n el.focus();\n // Caret aan het eind, geen selectie over de hele tekst: bewerken is meestal bijschaven, en een\n // volledige selectie betekent dat de eerste toetsaanslag alles wist.\n const selection = window.getSelection();\n const range = document.createRange();\n range.selectNodeContents(el);\n range.collapse(false);\n selection?.removeAllRanges();\n selection?.addRange(range);\n }, []);\n\n useEffect(() => {\n if (!selectedKey) {\n targetsRef.current = [];\n setSpots([]);\n return;\n }\n // Het blok staat op de LIVE pagina, niet in een canvas van de editor. `BlockRenderer` zet\n // `data-xicms-block` op elk blok — op de gepubliceerde pagina net zo goed als hier — dus dat is\n // de haak. Vandaar ook `document` en niet een ref: de pagina is niet van de editor.\n const host = document.querySelector<HTMLElement>(`[data-xicms-block=\"${selectedKey}\"]`);\n if (!host) return;\n const geselecteerd = blocksRef.current.find((b) => b._key === selectedKey);\n const type = geselecteerd?._type ?? \"\";\n const def = registry.get(String(type));\n warnInlineCoverage(String(type), def, host, geselecteerd ?? {});\n\n // Leesbare veldnamen voor de tooltip van het potlood (\"Bewerk: Ondertitel\"), uit dezelfde\n // schema-beschrijvingen die het zijpaneel gebruikt — zodat beide paden hetzelfde heten.\n const labels = new Map<string, string>();\n if (def) {\n try {\n for (const f of introspectToFields(def.schema, def.fields)) labels.set(f.key, f.label);\n } catch {\n /* een blok zonder introspecteerbaar schema krijgt gewoon de veldnaam */\n }\n }\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.classList.add(\"xicms-ve-inline\");\n el.addEventListener(\"blur\", () => {\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 commitText(selectedKey, field, html ? DOMPurify.sanitize(el.innerHTML) : (el.textContent ?? \"\"));\n stopInline(el);\n });\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\") {\n // NIET laten doorborrelen. De editor luistert zelf ook op Escape om te sluiten, en die\n // controle (\"sta ik in een invoerveld?\") kijkt naar het element dat focus HEEFT. Na\n // `blur()` is dat de body, dus concludeerde hij dat je niet aan het typen was en sloot\n // hij de hele bewerkmodus. Eén Escape hoorde het veld te sluiten, niet de werkplek.\n e.stopPropagation();\n el.blur();\n } else if (!html && e.key === \"Enter\") { e.preventDefault(); el.blur(); }\n });\n bound.add(el);\n };\n\n /** De potloodjes staan in de editor-laag, niet in het blok: de render van de klant blijft schoon. */\n const measure = () => {\n const next = targetsRef.current.map(({ el, field }) => {\n const r = el.getBoundingClientRect();\n // Naast het veld, op ooghoogte van de tekst: rechts ervan, verticaal gecentreerd. Niet\n // links (dan staat het potlood van de tweede knop bovenop de eerste) en niet ín de hoek\n // (dan ligt het over de tekst). Aanwijzen laat het veld oplichten, dus wélk veld het is\n // blijft ook bij twee elementen naast elkaar duidelijk.\n return {\n top: Math.min(window.innerHeight - 22, Math.max(4, r.top + r.height / 2 - 9)),\n left: Math.min(window.innerWidth - 22, Math.max(4, r.right + 6)),\n title: `Bewerk: ${labels.get(field) ?? field}`,\n };\n });\n setSpots((prev) =>\n prev.length === next.length && prev.every((p, i) => p.top === next[i].top && p.left === next[i].left && p.title === next[i].title)\n ? prev\n : next,\n );\n };\n\n const apply = () => {\n const found: { el: HTMLElement; field: string; html: boolean }[] = [];\n for (const m of GLOBAL_INLINE) {\n host.querySelectorAll<HTMLElement>(m.selector).forEach((el) => {\n // Een expliciete `data-xicms-edit` wint altijd; anders het veld van de matcher.\n const field = el.dataset.xicmsEdit ?? m.field;\n bind(el, field, !!m.html);\n found.push({ el, field, html: !!m.html });\n });\n }\n targetsRef.current = found;\n measure();\n };\n\n apply();\n if (wilInlineRef.current) {\n wilInlineRef.current = false;\n if (targetsRef.current.length > 0) {\n startInline(0);\n } else {\n /**\n * Nog geen velden gebonden ≠ geen velden. Het blok kan op ditzelfde moment re-renderen\n * (de selectie kleurt hem bij, een afbeelding hydrateert) en dan is de DOM waar apply()\n * net in zocht alweer vervangen. Twee frames later nog eens kijken vangt precies dat —\n * en pas als het er dán nog niet is, is \"geen tekst\" een eerlijke conclusie in plaats\n * van een race verkocht als feit.\n */\n let pogingen = 0;\n const opnieuw = () => {\n apply();\n if (targetsRef.current.length > 0) startInline(0);\n else if (++pogingen < 3) requestAnimationFrame(opnieuw);\n else meldGeenTekst();\n };\n requestAnimationFrame(opnieuw);\n }\n }\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 // Het canvas scrollt onder de potloodjes door; ze staan fixed, dus hermeten bij elke beweging.\n let frame = 0;\n const onMove = () => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(measure);\n };\n window.addEventListener(\"scroll\", onMove, { capture: true, passive: true });\n window.addEventListener(\"resize\", onMove, { passive: true });\n return () => {\n obs.disconnect();\n cancelAnimationFrame(frame);\n window.removeEventListener(\"scroll\", onMove, { capture: true });\n window.removeEventListener(\"resize\", onMove);\n bound.forEach((el) => {\n stopInline(el);\n el.classList.remove(\"xicms-ve-inline\");\n delete el.dataset.veInline;\n });\n targetsRef.current = [];\n setSpots([]);\n };\n // Alleen `selectedKey`: dit effect bindt de velden van hét geselecteerde blok. `blocks` en\n // `registry` lezen we uit een ref/context — draaide dit effect op elke draft-wijziging, dan\n // sloopte een autosave de lopende bewerking. Nieuwe DOM na een re-render wordt opgevangen door\n // de MutationObserver hieronder, niet door een her-run van het effect.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedKey, commitText, positionFmtBar, stopInline, startInline, toast]);\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 // Twee redenen om te waarschuwen, en ze zijn niet hetzelfde:\n // · `dirtyRef` — de laatste toetsaanslagen staan nog niet in de werkversie (autosave loopt).\n // · `nietLiveRef` — er is wél opgeslagen, maar niets van dit alles staat op de live site.\n // Tot 7.8 waarschuwde alleen de eerste. Wie rustig had zitten typen kreeg dus géén melding,\n // want alles was netjes bewaard — terwijl er nog steeds niets gepubliceerd was. Precies de\n // situatie waarin je denkt dat je klaar bent.\n if (dirtyRef.current || nietLiveRef.current) {\n e.preventDefault();\n e.returnValue = \"\";\n }\n bewaarHistory(); // synchroon en goedkoop: de undo-stapel overleeft zo ook een refresh\n };\n const onVisibility = () => {\n // Tab-wissel/minimaliseren: beste moment om pending werk veilig te stellen.\n if (document.visibilityState === \"hidden\") {\n void flushSave().catch(() => {});\n bewaarHistory();\n }\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 // Zelfde herkenning als useNestedDialog: Astryx' dialoog is een natieve <dialog open>.\n const dialogOpen = document.querySelector('dialog[open], [role=\"dialog\"][data-state=\"open\"], [role=\"alertdialog\"][data-state=\"open\"]');\n if (!inField && !dialogOpen) {\n // Lagen sluiten van binnen naar buiten: staat de zijlade open, dan sluit Escape éérst\n // de lade. De hele werkplek dichtgooien terwijl je alleen het paneel kwijt wilde, is\n // precies de verrassing die we bij invoervelden al hadden weggenomen.\n if (document.querySelector(\".xicms-ve__panel\")) setPanelOpen(false);\n else handleClose();\n }\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, bewaarHistory]);\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 /**\n * ✓ Klaar met dit blok: laatste bewerking committen, opslaan, selectie opruimen.\n *\n * Autosave deed het bewaren al; wat ontbrak was het MOMENT. Wie klaar was met een blok had\n * geen enkele knop om dat te zeggen en bleef achter met een geselecteerd blok vol potloodjes —\n * en de vraag of het nou opgeslagen was. Deze knop beantwoordt die vraag: veld dicht, werkversie\n * geschreven, gereedschap weg.\n */\n const klaarMetBlok = useCallback(() => {\n (document.activeElement as HTMLElement | null)?.blur();\n setSelectedKey(null);\n setPanelOpen(false);\n void flushSave()\n .then(() => toast({ title: \"Opgeslagen als werkversie\", description: \"Publiceer (rechtsonder) om het live te zetten.\", tone: \"success\" }))\n .catch(() => setStatus(\"error\"));\n }, [flushSave, toast]);\n\n /**\n * ↺ Wijzigingen van dít blok verwerpen: terug naar wat er live staat.\n *\n * Undo bestaat, maar undo loopt over de hele pagina en in bewerkvolgorde — wie drie blokken\n * heeft aangeraakt en er ééntje wil terugdraaien, kan daar niet mee uit de voeten. Dit is de\n * gerichte variant: de gepubliceerde versie van precies dit blok komt terug (in de demo: de\n * seed). Een blok dat nog niet live bestaat heeft niets om naar terug te keren — verwerpen is\n * dan verwijderen, langs de bestaande bevestiging.\n */\n async function herstelBlok(i: number) {\n const key = blocks[i]?._key;\n if (!key) return;\n const liveBlok = (liveBlocks ?? []).find((b) => String(b._key) === key);\n if (!liveBlok) {\n await remove(i);\n return;\n }\n const ok = await confirm({\n title: \"Wijzigingen van dit blok verwerpen?\",\n description: \"Het blok wordt teruggezet naar de versie die nu live staat. Met Ongedaan maken (Ctrl+Z) kun je hier nog op terugkomen.\",\n confirmLabel: \"Verwerpen\",\n tone: \"danger\",\n });\n if (!ok) return;\n setBlocks(blocks.map((b, idx) => (idx === i ? (structuredClone(liveBlok) as Block) : b)));\n toast({ title: \"Blok teruggezet\", description: \"Dit blok is weer gelijk aan de live versie.\", tone: \"success\" });\n }\n\n /**\n * Alles verwerpen: de hele pagina terug naar wat er live staat — het \"Annuleren\" naast\n * Publiceren. Er wás wel een uitgang per blok (↺) en undo, maar geen tegenhanger van de\n * publiceerknop zelf: wie een bewerksessie wilde weggooien moest blok voor blok terug.\n * Via setBlocks, dus de history vangt het op: Ctrl+Z maakt ook dít ongedaan.\n */\n async function verwerpAlles() {\n const ok = await confirm({\n title: \"Alle wijzigingen op deze pagina verwerpen?\",\n description:\n \"De hele pagina wordt teruggezet naar de versie die nu live staat. Met Ongedaan maken (Ctrl+Z) kun je hier nog op terugkomen.\",\n confirmLabel: \"Alles verwerpen\",\n tone: \"danger\",\n });\n if (!ok) return;\n (document.activeElement as HTMLElement | null)?.blur();\n setSelectedKey(null);\n setPanelOpen(false);\n setBlocks(structuredClone(liveBlocks ?? []) as Block[]);\n toast({ title: \"Wijzigingen verworpen\", description: \"De pagina is weer gelijk aan de live versie.\", tone: \"success\" });\n }\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 // Zie flushSave: een veld dat pas op blur committeert (JSON-redmiddel) moet die commit eerst\n // nog doen — anders publiceert dit de stand van vóór de laatste bewerking.\n (document.activeElement as HTMLElement | null)?.blur();\n const d = draftRef.current;\n if (!d) 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 // In een demo gaat er niets naar een live site; dat beloven zou liegen tegen precies de\n // bezoeker die we aan het overtuigen zijn. Zelfde knop, eerlijke woorden.\n const demo = isDemoActive();\n const ok = await confirm({\n title: \"Pagina publiceren?\",\n description: demo\n ? \"In deze demo blijft alles in je eigen browser — er gaat niets echt online.\"\n : `De live site wordt bijgewerkt (${d.path}).`,\n confirmLabel: \"Publiceren\",\n });\n if (!ok) return;\n setPublishing(true);\n try {\n await saveDraft(siteId, d.id, draftPayload(d));\n await publishPage(siteId, d.id);\n /**\n * De maatstaf schuift mee: wat zojuist gepubliceerd is, IS nu de live versie.\n *\n * Zonder deze regel bleef `liveBlocks` op de vorige publicatie staan — en dus bleef élke\n * markering rood en de knop blauw (\"14 wijzigingen\") terwijl alles allang live stond. De\n * hele belofte van de knop (\"grijs = er is niets te publiceren\") brak precies op het moment\n * dat hij zijn werk had gedaan. Lokaal bijschuiven is hier ook juister dan opnieuw lezen:\n * wat de server zojuist heeft weggeschreven is per definitie wat we net stuurden.\n */\n setLiveBlocks(structuredClone(d.blocks) as Block[]);\n toast({\n title: \"Gepubliceerd\",\n description: demo\n ? \"In deze demo blijft de wijziging in je browser staan.\"\n : \"Binnen enkele seconden zichtbaar voor iedereen.\",\n tone: \"success\",\n // Sluit de mentale lus \"staat het er nu echt?\": één klik en je kijkt als bezoeker.\n action: { label: \"Bekijk als bezoeker →\", onClick: handleClose },\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 ref={containerRef} className=\"xicms-ve__shell\" data-xicms-tooling=\"\" role=\"group\" aria-label={`Pagina bewerken: ${path}`}>\n {toonStartHint && (\n <div className=\"xicms-ve-starthint\" role=\"status\">\n <span>\n ✏️ Klik het <strong>potlood</strong> naast een tekst om die op de pagina zelf te bewerken;\n foto&apos;s wissel je met <strong>📷 Foto wijzigen</strong>. Klaar? <strong>Publiceren</strong> staat rechtsonder.\n </span>\n <button type=\"button\" onClick={sluitStartHint}>Begrepen</button>\n </div>\n )}\n {/* Dezelfde balk als op de live site (`EditBar`), met hetzelfde `AdminModes` erin — alleen\n staat \"Bewerken\" nu ingedrukt en hangt het gereedschap ernaast. Zo verspringt er niets\n op het moment dat je overschakelt: de knoppen blijven waar je ze net nog zag.\n Publiceren staat hier BEWUST niet meer; dat is de zwevende knop rechtsonder, bij je werk,\n en niet weggestopt tussen de navigatie. */}\n <header className=\"xicms-ve__bar\" role=\"toolbar\" aria-label=\"Beheer\">\n <span className=\"xicms-editbar__brand\">{isDemoActive() ? \"XiCMS · demo\" : \"XiCMS\"}</span>\n <AdminModes\n actief=\"bewerken\"\n voorSwitch={(m) => {\n if (m === \"live\" && nietLiveRef.current) {\n const n = aantalWijzigingen(wijzigingen);\n toast({\n title: \"Nog niet gepubliceerd\",\n // Enkelvoud/meervoud netjes: \"1 wijziging blijft\", \"14 wijzigingen blijven\".\n description: `${n} ${n.startsWith(\"1 \") ? \"blijft\" : \"blijven\"} bewaard als werkversie. Je ziet ze terug zodra je weer naar Bewerken gaat.`,\n });\n }\n }}\n />\n <span className=\"xicms-ve__sep\" aria-hidden=\"true\" />\n <span className=\"xicms-ve__path\" title={`Je bewerkt ${name} — ${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={(l) => { setLocale(l); if (l !== localeCfg.defaultLocale) setPanelOpen(true); }}\n options={localeCfg.locales.map((l) => ({ value: l, label: l === localeCfg.defaultLocale ? `${l} (standaard)` : l }))}\n />\n </span>\n )}\n <button\n type=\"button\"\n className={`xicms-ve__histbtn${panelOpen && !selectedKey ? \" is-on\" : \"\"}`}\n onClick={() => { if (panelOpen && !selectedKey) { setPanelOpen(false); } else { setSelectedKey(null); setPanelOpen(true); } }}\n title=\"Pagina-instellingen (titel, SEO, versies)\"\n aria-label=\"Pagina-instellingen\"\n aria-pressed={panelOpen && !selectedKey}\n >\n ⚙\n </button>\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 {status === \"none\" && (\n <span className=\"xicms-ve__balkmelding\">\n Nog geen werkversie\n <Button size=\"sm\" onClick={() => void handleCreatePage()}>+ Pagina aanmaken</Button>\n </span>\n )}\n {/* \"Opgeslagen\" gaat over de WERKVERSIE, niet over de live site. Dat onderscheid stond\n nergens en werd daardoor gelezen als \"het staat online\". Vandaar de toevoeging. */}\n <span className=\"xicms-ve__status\" aria-live=\"polite\">\n {status === \"loading\" ? \"Werkversie laden…\" : null}\n {status === \"saving\" ? \"Opslaan…\" : status === \"saved\" ? \"✓ Bewaard als werkversie\" : 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 </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${panelOpen ? \" is-panel-open\" : \"\"}`}>\n {/* GEEN CANVAS MEER. De pagina eronder is de echte pagina en toont de werkversie (zie\n EditorProvider). Wat de editor nog moet zeggen — laden, of nog geen werkversie — staat\n in de balk hierboven, niet als los zwevend kaartje: dat dekte de menubalk van de site af,\n en gereedschap hoort de site niet in de weg te zitten. */}\n\n {panelOpen && (\n <aside className=\"xicms-ve__panel\">\n {/* Blokken toevoegen zat in de werkbalkjes van het canvas. Dat canvas is er niet meer, dus\n staat het hier — zichtbaar wanneer je het paneel toch al open hebt om aan de opbouw\n van de pagina te werken. Zonder deze verhuizing zou het toevoegen van een blok\n stilzwijgend zijn verdwenen. */}\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 {/* Een aangeklikt PRODUCT wint van een aangeklikt blok: je wees het product aan, dus dat\n wil je zien. Blok en product uitsluiten elkaar hier bewust — twee formulieren onder\n elkaar in één smal paneel is niet te lezen. */}\n {selectedProduct ? (\n <div className=\"grid gap-3\">\n <div className=\"xicms-ve__panel-head\">\n <span className=\"text-sm text-slate-500\">Webwinkel</span>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => { setSelectedProduct(null); setPanelOpen(false); }}>\n ✕ Sluiten\n </Button>\n </div>\n <ProductPaneel siteId={siteId} productId={selectedProduct} />\n </div>\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 {/* Kleur volgt de autosave-status (zelfde bron als de statustekst in de topbalk):\n blauw = nog niet-opgeslagen wijziging (of een mislukte save die opnieuw moet),\n grijs = alles staat al veilig. Directe visuele bevestiging i.p.v. alleen tekst. */}\n <Button\n size=\"sm\"\n variant={status === \"saving\" || status === \"error\" ? \"primary\" : \"ghost\"}\n onClick={() => void flushSave().catch(() => setStatus(\"error\"))}\n >\n {status === \"saving\" ? \"Opslaan…\" : status === \"error\" ? \"⚠ Opnieuw opslaan\" : \"✓ Opgeslagen\"}\n </Button>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setPanelOpen(false)}>\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 )}\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 {/* Potlood én tandwiel per blok, plus — op aanwijzen — de rest van het gereedschap.\n De acties verplaatsen/dupliceren/verwijderen BESTONDEN al die tijd: ze zaten in de\n werkbalkjes van het oude canvas, en toen dat canvas verdween is de UI eromheen nooit\n teruggebouwd. De functies bleven staan, niets riep ze aan, en TypeScript klaagt niet over\n een ongebruikte functiedeclaratie — dus de enige weg om een blok te verplaatsen liep\n maandenlang via het formulierscherm in /beheer. Precies waar de klant weg moest blijven.\n\n Twee knoppen in rust, de rest klapt uit bij aanwijzen: zes knoppen die permanent naast\n élk blok staan zijn geen gereedschap meer maar een hek om de site heen. */}\n {blokknoppen.map((b) => {\n const i = blocks.findIndex((blok) => blok._key === b.key);\n const isSymbool = b.type === \"symbolRef\";\n return (\n <div key={b.key} className={`xicms-ve-blokknoppen${b.key === selectedKey ? \" is-open\" : \"\"}`} style={{ top: b.top, right: b.right }}>\n <button\n type=\"button\"\n className={`xicms-ve-blokknop${b.key === selectedKey ? \" is-on\" : \"\"}`}\n title={`Tekst van ${b.label} bewerken`}\n aria-label={`Tekst van ${b.label} bewerken`}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n /**\n * Al geselecteerd? Dan draait het bind-effect niet opnieuw (zelfde key) en zou deze\n * klik NIETS doen — precies wat er gebeurde zodra je één veld had bewerkt en\n * daarna nóg eens op het potlood drukte. Het eerste veld staat dan al gebonden in\n * targetsRef, dus we openen het rechtstreeks.\n */\n if (b.key === selectedKey) {\n if (targetsRef.current.length > 0) startInline(0);\n else meldGeenTekst();\n return;\n }\n // Selecteren zet de velden van dít blok open (zie het bind-effect hierboven); de vlag\n // zorgt dat de caret meteen in het eerste veld staat.\n wilInlineRef.current = true;\n setSelectedProduct(null);\n setSelectedKey(b.key);\n }}\n >\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M4 20h4L19.5 8.5a2.1 2.1 0 0 0-3-3L5 17v3z\" />\n <path d=\"M14.5 6.5l3 3\" />\n </svg>\n </button>\n <button\n type=\"button\"\n className={`xicms-ve-blokknop${b.key === selectedKey && panelOpen ? \" is-on\" : \"\"}`}\n title={`Instellingen van ${b.label}`}\n aria-label={`Instellingen van ${b.label}`}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => { setSelectedProduct(null); setSelectedKey(b.key); setPanelOpen(true); }}\n >\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\" />\n </svg>\n </button>\n {i >= 0 && (\n <span className=\"xicms-ve-blokknoppen__meer\"><span className=\"xicms-ve-blokknoppen__meer-in\">\n <button type=\"button\" className=\"xicms-ve-blokknop\" title=\"Blok omhoog\" aria-label={`${b.label} omhoog`} disabled={i === 0}\n onMouseDown={(e) => e.preventDefault()} onClick={() => move(i, -1)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M12 19V5M5 12l7-7 7 7\" /></svg>\n </button>\n <button type=\"button\" className=\"xicms-ve-blokknop\" title=\"Blok omlaag\" aria-label={`${b.label} omlaag`} disabled={i === blocks.length - 1}\n onMouseDown={(e) => e.preventDefault()} onClick={() => move(i, 1)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M12 5v14M19 12l-7 7-7-7\" /></svg>\n </button>\n <button type=\"button\" className=\"xicms-ve-blokknop\" title=\"Blok dupliceren\" aria-label={`${b.label} dupliceren`}\n onMouseDown={(e) => e.preventDefault()} onClick={() => duplicate(i)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\" /><path d=\"M5 15V5a2 2 0 0 1 2-2h10\" /></svg>\n </button>\n {isSymbool && (\n <button type=\"button\" className=\"xicms-ve-blokknop\" title=\"Symbool loskoppelen (wordt een losse kopie)\" aria-label={`${b.label} loskoppelen`}\n onMouseDown={(e) => e.preventDefault()} onClick={() => void detachSymbol(i)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M9 12h6M8 7a4 4 0 0 0 0 8M16 7a4 4 0 0 1 0 8\" /><path d=\"M3 3l18 18\" /></svg>\n </button>\n )}\n <button type=\"button\" className=\"xicms-ve-blokknop xicms-ve-blokknop--gevaar\" title=\"Blok verwijderen\" aria-label={`${b.label} verwijderen`}\n onMouseDown={(e) => e.preventDefault()} onClick={() => void remove(i)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2M6 6l1 14a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-14\" /></svg>\n </button>\n {/* ↺ alleen wanneer dit blok afwijkt van wat er live staat — een terugzet-knop op een\n ongewijzigd blok is een knop die niets kan doen, en die hoort er dan niet te zijn. */}\n {(wijzigingen.velden.has(b.key) || wijzigingen.blokken.has(b.key)) && (\n <button type=\"button\" className=\"xicms-ve-blokknop\" title=\"Wijzigingen van dit blok verwerpen (terug naar live)\" aria-label={`Wijzigingen van ${b.label} verwerpen`}\n onMouseDown={(e) => e.preventDefault()} onClick={() => void herstelBlok(i)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M3 7v6h6\" /><path d=\"M3 13a9 9 0 1 0 3-7.7L3 7\" /></svg>\n </button>\n )}\n {b.key === selectedKey && (\n <button type=\"button\" className=\"xicms-ve-blokknop xicms-ve-blokknop--klaar\" title=\"Klaar met dit blok (opslaan als werkversie)\" aria-label={`Klaar met ${b.label}`}\n onMouseDown={(e) => e.preventDefault()} onClick={klaarMetBlok}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M20 6L9 17l-5-5\" /></svg>\n </button>\n )}\n </span></span>\n )}\n </div>\n );\n })}\n\n {/* Potloodjes bij elk inline bewerkbaar veld van het geselecteerde blok. Ze staan fixed in de\n editor-laag (niet in de render van de klant) en zetten het bewerken bewust aan.\n onMouseDown-preventDefault: anders neemt de knop de focus en sluit het veld meteen weer. */}\n {spots.map((s, i) => {\n // Het potlood van het veld dat je nú bewerkt is een groen vinkje: klikken rondt het blok\n // af (opslaan als werkversie), precies wat \"het is goed\" betekent.\n const actief = editingIndex === i;\n return (\n <button\n key={i}\n type=\"button\"\n className={`xicms-ve-pencil${actief ? \" is-klaar\" : \"\"}`}\n style={{ top: s.top, left: s.left }}\n title={actief ? \"Het is goed — klaar met dit blok (opslaan als werkversie)\" : s.title}\n aria-label={actief ? \"Klaar met dit blok\" : s.title}\n onMouseDown={(e) => e.preventDefault()}\n onMouseEnter={() => targetsRef.current[i]?.el.classList.add(\"is-hover\")}\n onMouseLeave={() => targetsRef.current[i]?.el.classList.remove(\"is-hover\")}\n onClick={() => (actief ? klaarMetBlok() : startInline(i))}\n >\n {actief ? (\n <svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M20 6L9 17l-5-5\" />\n </svg>\n ) : (\n /* Fijn getekend potlood i.p.v. een emoji: een emoji is per platform anders, kleurt niet\n mee en oogt op deze schaal als een vlek. Eén pad, currentColor, 1.4 lijndikte. */\n <svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M4 20h4L19.5 8.5a2.1 2.1 0 0 0-3-3L5 17v3z\" />\n <path d=\"M14.5 6.5l3 3\" />\n </svg>\n )}\n </button>\n );\n })}\n\n {/* Hetzelfde knoppenpaar, maar dan voor een PRODUCT — alleen zichtbaar bij het product dat je\n aanwijst (zie hierboven waarom). Zelfde vorm en zelfde plek als bij een blok, want het is\n dezelfde handeling: potlood voor de tekst, tandwiel voor de rest. */}\n {productKnop && (\n <div\n className=\"xicms-ve-blokknoppen is-product\"\n style={{ top: productKnop.top, right: productKnop.right }}\n >\n <button\n type=\"button\"\n className=\"xicms-ve-blokknop\"\n title=\"Productnaam bewerken (staat meteen live)\"\n aria-label=\"Productnaam bewerken\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => startProductInline(productKnop.id)}\n >\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M4 20h4L19.5 8.5a2.1 2.1 0 0 0-3-3L5 17v3z\" />\n <path d=\"M14.5 6.5l3 3\" />\n </svg>\n </button>\n <button\n type=\"button\"\n className={`xicms-ve-blokknop${selectedProduct === productKnop.id ? \" is-on\" : \"\"}`}\n title=\"Product bewerken: prijs, foto's, varianten, voorraad\"\n aria-label=\"Product bewerken\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n setSelectedKey(null);\n setSelectedProduct(productKnop.id);\n setPanelOpen(true);\n }}\n >\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\" />\n </svg>\n </button>\n </div>\n )}\n\n {/*\n Publiceren staat rechtsonder, bij je werk — niet bovenin tussen de navigatie.\n Daar was het een knop die altijd even hard riep, ook als er niets te publiceren viel, en\n dus niets meer betekende. Nu zegt de knop zelf of er iets klaarstaat: grijs en uitgeschakeld\n zolang de pagina gelijk is aan wat er live staat, en pas gekleurd zodra dat niet meer zo is.\n Dat is dezelfde bron als de rode markering op de pagina, dus de knop en de pagina kunnen het\n nooit oneens zijn.\n */}\n {/* Annuleren naast Publiceren: pas zichtbaar als er iets te verwerpen vált — een knop die\n niets kan doen hoort er niet te staan (zelfde regel als de ↺ per blok). */}\n {draft && wijzigingen.iets && !publishing && (\n <button\n type=\"button\"\n className=\"xicms-ve-verwerp\"\n onClick={() => void verwerpAlles()}\n title=\"Alle wijzigingen op deze pagina verwerpen (terug naar live)\"\n >\n ↺ Verwerpen\n </button>\n )}\n {draft && (\n <button\n type=\"button\"\n className={`xicms-ve-publiceer${wijzigingen.iets ? \" is-klaar\" : \"\"}`}\n disabled={publishing || !wijzigingen.iets}\n onClick={() => void onPublish()}\n title={\n wijzigingen.iets\n ? \"Zet deze wijzigingen op de live site\"\n : \"Er is niets veranderd sinds de laatste publicatie\"\n }\n >\n <span className=\"xicms-ve-publiceer__label\">{publishing ? \"Bezig…\" : \"Publiceren\"}</span>\n <span className=\"xicms-ve-publiceer__sub\">\n {wijzigingen.iets ? aantalWijzigingen(wijzigingen) : \"niets gewijzigd\"}\n </span>\n </button>\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 { registerBuiltinSchemas } from \"../../blocks/builtinSchemas\";\nimport { useModules } from \"../../modules/context\";\n// Schema's zijn lazy (7.13): dit is een editor-chunk, dus hier horen ze thuis.\nregisterBuiltinSchemas();\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 { useThemeVars } from \"../../XiCMSProvider\";\nimport { buildBlockAddOptions } from \"../../blocks/registry\";\nimport { safeParse } from \"zod/v4/core\";\nimport DOMPurify from \"dompurify\";\nimport { createPage, getDraftByPath, getPublishedPage, saveDraft, publishPage, subscribeDraft, subscribeVersions, restoreVersion, type DraftPage } from \"../lib/pagesClient\";\nimport { isDemoActive } from \"../../demo/demoStore\";\nimport { setProductField } from \"../shop/productClient\";\nimport { ProductPaneel } from \"../shop/ProductPaneel\";\nimport { builtinBlockEditors, GenericEditor, type BlockEditorProps } from \"../blockEditors\";\nimport { StyleEditor } from \"../blockEditors/StyleEditor\";\nimport { siteLocales, parseLocaleSlugs, localizeBlocks } from \"../../i18n/overlay\";\nimport { useEditor } from \"../../editor/EditorProvider\";\nimport { AdminModes } from \"../../components/EditBar\";\nimport { aantalWijzigingen, bepaalWijzigingen, GEEN_WIJZIGINGEN, type Wijzigingen } from \"./wijzigingen\";\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 // `field: \"html\"` is de AANNAME, geen wet: een element mag met `data-xicms-edit` zeggen welk veld\n // het werkelijk toont, en dat wint (zie de bind hieronder). Zonder die voorrang schreef élk\n // `.xicms-richtext`-element naar een veld dat letterlijk \"html\" heet — waar de FAQ zijn antwoord\n // in `items[].aHtml` bewaart en het accordeon in `items[].bodyHtml`. Inline bewerken van een\n // FAQ-antwoord landde daardoor op een niet-bestaand veld: de wijziging verdween stil en het blok\n // hield een zwerfveld over.\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/**\n * Dev-vangnet (5.0): meld tekstvelden zonder inline-haak — één keer per bloktype. Vooral voor\n * MAATWERKblokken in een klantrepo; de ingebouwde blokken worden statisch en volledig gedekt door\n * `npm run check:inline`.\n *\n * Een veld telt alleen als het zijn waarde ook echt als tekst op het scherm zet. Zonder die eis\n * riep dit elk blok na over lege velden (een niet-ingevulde `eyebrow` rendert niet, dus valt er\n * niets aan te klikken) en over configuratie die toevallig een tekstveld is (`overlayColor`).\n * Zulke loze meldingen leren je de waarschuwing wegkijken, en dan mist hij zijn doel.\n */\nconst inlineCoverageWarned = new Set<string>();\nfunction warnInlineCoverage(\n type: string,\n def: { schema?: Parameters<typeof introspectToFields>[0]; fields?: Parameters<typeof introspectToFields>[1] } | undefined,\n host: HTMLElement,\n values: Record<string, unknown>,\n) {\n if (process.env.NODE_ENV === \"production\" || !def || inlineCoverageWarned.has(type)) return;\n inlineCoverageWarned.add(type);\n const zichtbaar = host.textContent ?? \"\";\n const missing = introspectToFields(def.schema, def.fields)\n .filter((f) => f.type === \"text\" || f.type === \"textarea\")\n .filter((f) => {\n const waarde = getPath(values, f.key);\n return typeof waarde === \"string\" && waarde.trim().length > 1 && zichtbaar.includes(waarde.trim());\n })\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 // Het zijpaneel stond altijd open en nam 360px van het canvas. Sinds élk tekstveld inline\n // bewerkbaar is, is dat paneel niet meer de hoofdweg maar het uitzonderingsgeval: de instellingen\n // die je niet op de pagina ziet (een link achter een knop, een kleur, een limiet). Het opent\n // daarom op verzoek — via het tandwiel bij het blok — en het canvas krijgt de volle breedte.\n const [panelOpen, setPanelOpen] = useState(false);\n const [status, setStatus] = useState<Status>(\"loading\");\n const [selectedKey, setSelectedKey] = useState<string | null>(null);\n /** Staat het zijpaneel op een PRODUCT in plaats van op een blok? Dan wint het product. */\n const [selectedProduct, setSelectedProduct] = 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 * De blokken zoals ze NU LIVE staan — de maatstaf voor \"wat heb ik veranderd\".\n *\n * `undefined` = nog niet gelezen (dan zeggen we niets, want een lege pagina rood kleuren omdat\n * de lezing nog loopt is erger dan even niets tonen). `null` = gelezen, maar deze pagina is nog\n * nooit gepubliceerd.\n */\n const [liveBlocks, setLiveBlocks] = useState<Block[] | null | undefined>(undefined);\n // Geen useBalkOffset meer: het pilletje zweeft óver de site (7.16) en duwt niets omlaag.\n\n // ── GEEN modale discipline meer ───────────────────────────────────────────\n // Tot 7.10 stond hier het complete WAI-ARIA-dialoogpatroon: `useScrollLock(true)` en een\n // focus-trap om de shell. Dat was juist toen de editor een schermvullende overlay met eigen\n // canvas was — en is precies verkeerd nu de editor gereedschap óver de live pagina is:\n //\n // - De scroll-lock pinde de body vast (`position: fixed`). De site zelf kon niet meer\n // scrollen, dus bestonden er voor alles onder de vouw ook geen bewerkknoppen. \"De\n // bewerkknop verschijnt niet bij elk blok\" wás geen meetfout van de knoppenlaag — de\n // pagina kon er simpelweg niet komen.\n // - De focus-trap bewaakte de shell, maar de tekst die je bewerkt staat op de PAGINA,\n // buiten die shell. Elke focus op een net-contenteditable gemaakt veld werd door de trap\n // teruggetrokken naar de werkbalk: potlood aan, caret meteen weer weg. Dat inline bewerken\n // \"lang niet altijd\" werkte, was deze trap die won of verloor van een race.\n //\n // De pagina is het werkgebied; alleen echte dialogen (mediakiezer, bevestiging) zijn modaal,\n // en die regelen hun eigen trap en lock via Astryx.\n // Het canvas is een VOORBEELD van de live pagina en moet dus dezelfde merk-tokens dragen.\n // De editor rendert buiten `.xicms-root`, dus die erven we hier expliciet via context.\n const { vars: themeVars, darkAuto } = useThemeVars();\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // Focus de eerste bediening bij openen, en geef hem bij sluiten terug aan het element dat de\n // editor opende (\"Pagina bewerken\"). Zonder dat teruggeven valt de focus terug naar <body> en\n // begint een toetsenbordgebruiker weer bovenaan de pagina.\n useEffect(() => {\n const opener = document.activeElement as HTMLElement | null;\n containerRef.current\n ?.querySelector<HTMLElement>(\"button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])\")\n ?.focus();\n return () => opener?.focus?.();\n }, []);\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 /**\n * Undo die een refresh overleeft (7.14). Een F5 — of een link in je eigen site, dat is dezelfde\n * volledige navigatie — gooide de complete undo-stapel weg, precies op momenten dat je hem\n * nodig kunt hebben. De laatste 25 stappen gaan daarom mee in sessionStorage (per tabblad,\n * verdwijnt met het tabblad — zoals de bewerkmodus zelf). Alleen `past`: een redo-toekomst\n * over een refresh heen is verwarrender dan behulpzaam. Alles best-effort in try/catch: een\n * volle of geblokkeerde opslag mag nooit de editor raken.\n */\n const undoOpslagSleutel = useCallback((id: string) => `xicms-undo:${siteId}:${id}`, [siteId]);\n const bewaarHistory = useCallback(() => {\n try {\n const id = draftRef.current?.id;\n if (!id) return;\n sessionStorage.setItem(undoOpslagSleutel(id), JSON.stringify(historyRef.current.past.slice(-25)));\n } catch { /* opslag vol/geblokkeerd: dan geen persistente undo */ }\n }, [undoOpslagSleutel]);\n const herstelHistory = useCallback((id: string) => {\n try {\n const raw = sessionStorage.getItem(undoOpslagSleutel(id));\n if (!raw || historyRef.current.past.length > 0) return;\n const past = JSON.parse(raw) as DraftPage[];\n if (Array.isArray(past) && past.length) {\n historyRef.current.past = past;\n setHistVersion((v) => v + 1);\n }\n } catch { /* corrupte opslag: verse stapel */ }\n }, [undoOpslagSleutel]);\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 // Een veld dat pas op blur committeert (bv. het JSON-redmiddel in GenericEditor) moet die\n // commit eerst nog uitvoeren. Op desktop vuurt blur al vóór de click van deze knop, maar op\n // mobiel Safari is die volgorde niet gegarandeerd (het toetsenbord-sluiten kan blur vertragen\n // tot ná de klik) — expliciet blur() forceert de commit synchroon, ongeacht platform.\n (document.activeElement as HTMLElement | null)?.blur();\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 /**\n * Eénmalige starthint. De knoppen zijn er allemaal, maar wie de bewerkmodus voor het eerst\n * opent moet ze nu zelf ontdekken — en \"waar begin ik?\" is precies het moment waarop een\n * klant afhaakt of gaat bellen. Eén zin, één keer, weg te klikken; daarna nooit meer\n * (localStorage). Bewust geen tour/overlay: de pagina zelf is de uitleg.\n */\n const [toonStartHint, setToonStartHint] = useState(false);\n useEffect(() => {\n try {\n if (!localStorage.getItem(\"xicms-hint-bewerken-v1\")) setToonStartHint(true);\n } catch { /* geblokkeerde opslag: dan geen hint — nooit een kapotte editor om een hint */ }\n }, []);\n const sluitStartHint = useCallback(() => {\n setToonStartHint(false);\n try { localStorage.setItem(\"xicms-hint-bewerken-v1\", \"1\"); } catch { /* idem */ }\n }, []);\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 herstelHistory(d.id);\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 /**\n * Lees erbij wat er live staat, zodat de markering en de publiceerknop ergens op slaan.\n *\n * Eén keer per pagina, niet als abonnement: de gepubliceerde versie verandert alleen doordat\n * JIJ publiceert, en dat moment wordt hieronder zelf afgehandeld. Een tweede live-listener zou\n * bij elke autosave-echo opnieuw willen vergelijken zonder dat er iets veranderd is.\n */\n useEffect(() => {\n const id = draft?.id;\n if (!id) {\n setLiveBlocks(undefined);\n return;\n }\n let active = true;\n getPublishedPage(siteId, id)\n .then((pagina) => {\n if (active) setLiveBlocks((pagina?.blocks as Block[] | undefined) ?? null);\n })\n // Mislukte lezing: dan liever géén markering dan een verkeerde. `undefined` houdt de\n // publiceerknop bereikbaar maar rustig, en niets kleurt rood.\n .catch(() => active && setLiveBlocks(undefined));\n return () => {\n active = false;\n };\n // Alleen op de identiteit van de pagina: de INHOUD van de werkversie verandert continu en mag\n // deze lezing niet opnieuw aanjagen.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [siteId, draft?.id]);\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 // Via een ref, zodat het bind-effect hieronder NIET op elke draft-wijziging opnieuw hoeft te\n // draaien. Deed het dat wel, dan brak een autosave van de vorige bewerking de bewerking af die\n // je net was begonnen: de cleanup haalde contentEditable weg en je toetsaanslagen verdwenen.\n const blocksRef = useRef(blocks);\n blocksRef.current = blocks;\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 /**\n * Wat staat er nog niet live? Hierop leunen de rode markering én de knop rechtsonder.\n *\n * Bewust op `blocks` en niet op `displayBlocks`: in vertaalmodus is `displayBlocks` de\n * samengevoegde weergave (standaardtaal + overlay), en die zou élk vertaald veld als wijziging\n * aanwijzen. Publiceren gaat over de bron, dus vergelijken we de bron.\n */\n /** Voor de waarschuwing bij wegnavigeren: die luistert buiten React om en heeft een ref nodig. */\n const nietLiveRef = useRef(false);\n const wijzigingen: Wijzigingen = useMemo(\n () => (liveBlocks === undefined ? GEEN_WIJZIGINGEN : bepaalWijzigingen(blocks, liveBlocks)),\n [blocks, liveBlocks],\n );\n nietLiveRef.current = wijzigingen.iets;\n\n /**\n * Duw de werkversie naar de PAGINA. Dit is de kern van het nieuwe bewerken: de site rendert\n * zichzelf — eigen layout, eigen huisstijl, eigen componenten — maar met deze blokken in plaats\n * van de gepubliceerde. Daardoor is wat je ziet precies wat je bewerkt, zonder dat de editor de\n * pagina hoeft na te bouwen.\n *\n * `displayBlocks` en niet `blocks`: bewerk je een vertaling, dan hoort de pagina die vertaling te\n * tonen en niet het origineel.\n */\n const { publishDraft, zetFotoVervanger } = useEditor();\n\n // Module-schema's lazy laden (7.14): zelfde reden als registerBuiltinSchemas hierboven — de\n // schema's van actieve modules (shop) zijn editor-kennis en komen pas binnen als er bewerkt\n // wordt. Fouten stil: een module zonder schema's valt terug op de lichte vormcheck.\n const actieveModules = useModules();\n useEffect(() => {\n for (const m of actieveModules.defs) {\n if (actieveModules.activeIds.has(m.id)) void m.loadSchemas?.().catch(() => {});\n }\n }, [actieveModules]);\n\n /**\n * De 📷-knopjes op de pagina aansluiten op de mediakiezer van deze editor.\n *\n * Dit herstelt een stille regressie: `InlineImageEditProvider` werd sinds het verdwijnen van het\n * canvas nergens meer gerenderd, dus geen enkel beeld toonde nog \"Foto wijzigen\" — de import\n * stond er nog, de dialoog stond er nog, alleen de draad ertussen was weg. Een klant kon\n * daardoor geen foto vervangen zonder naar /beheer te gaan.\n */\n useEffect(() => {\n zetFotoVervanger((field, blockKey) => {\n if (blockKey) setImgPicker({ key: blockKey, field });\n });\n return () => zetFotoVervanger(null);\n }, [zetFotoVervanger]);\n const laatstGepubliceerd = useRef<string>(\"\");\n useEffect(() => {\n if (!draft) return;\n // ALLEEN bij een echte inhoudelijke wijziging. Dit effect duwt state naar een context waar de\n // hele pagina op meeleest — en dus ook deze editor. Duw je bij elke render, dan veroorzaakt de\n // render een update en de update een render: \"Maximum update depth exceeded\". Een vergelijking\n // op reference is niet genoeg, want `displayBlocks` kan een nieuwe array zijn met dezelfde\n // inhoud; vandaar een stabiele vergelijking op de inhoud zelf.\n const vinger = stableStringify(displayBlocks);\n if (vinger === laatstGepubliceerd.current) return;\n laatstGepubliceerd.current = vinger;\n publishDraft(path, displayBlocks);\n }, [draft, displayBlocks, path, publishDraft]);\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 // ── Inline bewerken: eerst het potlood, dan pas de tekst ──────────────────\n // Tot 7.3 werd élk tekstveld van het geselecteerde blok meteen contentEditable. Dat leest als\n // \"alles staat open\": één misklik in een kop en je typt al in de pagina, met autosave eronder.\n // Nu markeert de editor de bewerkbare velden alleen (een subtiele stippellijn) en zet een\n // potloodje ernaast de bewerking BEWUST aan — pas daarna verschijnen caret en opmaak-toolbar.\n // Dat maakt bovendien zichtbaar wát inline kan; dat wist je voorheen pas als je erop klikte.\n /**\n * Tandwielen naast elk blok OP DE PAGINA.\n *\n * Dit zat vroeger in de werkbalkjes van het canvas. Nu er geen canvas meer is, moeten ze over de\n * echte blokken heen — dezelfde aanpak als de potloodjes: vast gepositioneerd in de editorlaag,\n * en opnieuw meten zodra er gescrold of geschaald wordt. De pagina zelf wordt niet aangeraakt,\n * want dat is de site van de klant.\n */\n // Via een ref: de registry is een context-waarde en mag de meting niet opnieuw aanjagen.\n // Via een ref: de registry is een context-waarde en mag de meting niet opnieuw aanjagen.\n const registryRef = useRef(registry);\n registryRef.current = registry;\n\n /**\n * Hoe ver van bovenaf is het veilig om iets neer te zetten?\n *\n * De knoppen van het bovenste blok landden onder de balk van de editor én onder de plakkende\n * menubalk van de site zelf. Ze wáren er wel — ze lagen alleen onder iets anders, en dus kon je\n * het hoofdblok als enige blok niet bewerken of instellen. Dat was geen randgeval: het eerste\n * blok van de homepagina is meestal juist het blok dat je wilt aanpassen.\n *\n * We meten daarom wat er werkelijk bovenaan vastgeplakt zit — de eigen balk plus elk plakkend of\n * vastgezet element van de site dat de bovenrand raakt — in plaats van een vast getal te kiezen\n * dat op de ene site klopt en op de andere niet.\n */\n function veiligeBovenkant(): number {\n let onderkant = 0;\n for (const el of Array.from(document.querySelectorAll<HTMLElement>(\"body *\"))) {\n const pos = getComputedStyle(el).position;\n if (pos !== \"fixed\" && pos !== \"sticky\") continue;\n const r = el.getBoundingClientRect();\n // Alleen wat écht bovenaan hangt telt mee; een plakkende knop onderin de pagina niet.\n if (r.height === 0 || r.top > 4) continue;\n onderkant = Math.max(onderkant, r.bottom);\n }\n return onderkant;\n }\n\n /**\n * Potlood én tandwiel bij ELK blok.\n *\n * Tot 7.8 stond er per blok alleen een tandwiel, en verschenen de potloden pas nádat je een blok\n * had geselecteerd — via datzelfde tandwiel. Inline bewerken, de hoofdweg van dit hele systeem,\n * zat dus achter de knop voor het uitzonderingsgeval. Nu staan beide er meteen: het potlood zet\n * de tekst van dat blok open, het tandwiel opent alles wat je niet kunt typen.\n */\n const [blokknoppen, setBlokknoppen] = useState<{ key: string; type: string; top: number; right: number; label: string }[]>([]);\n useEffect(() => {\n if (!draft) {\n // `setBlokknoppen([])` zou hier een NIEUWE lege array zetten bij elke render — en dus elke\n // render een update, en elke update een render: \"Maximum update depth exceeded\". De\n // functionele vorm geeft de bestaande array terug als er toch al niets stond.\n setBlokknoppen((v) => (v.length === 0 ? v : []));\n return;\n }\n /**\n * De veilige bovenkant is duur om te bepalen (hij loopt de hele DOM af op zoek naar plakkende\n * elementen) en verandert alleen bij een resize of een structurele wijziging — niet bij\n * scrollen. `meet()` draait wél bij elke scroll-tick; die twee horen dus niet in één adem.\n * Gemeten winst: van O(alle elementen) naar O(blokken) per scroll-frame.\n */\n let veilig = veiligeBovenkant();\n const herijk = () => {\n veilig = veiligeBovenkant();\n meet();\n };\n const meet = () => {\n const gevonden: { key: string; type: string; top: number; right: number; label: string }[] = [];\n for (const el of Array.from(document.querySelectorAll<HTMLElement>(\"[data-xicms-block]\"))) {\n const key = el.dataset.xicmsBlock;\n if (!key) continue;\n /**\n * Blokken zonder eigen blokstijl zitten in een `display:contents`-wrapper, en die meet\n * 0×0 — de wrapper doet niet mee aan layout, zijn KINDEREN wel. Dat 0×0 werd hier als\n * \"onzichtbaar, overslaan\" gelezen, waardoor het merendeel van de blokken op een\n * doorsnee pagina géén potlood en tandwiel kreeg: precies de blokken zonder aangepaste\n * stijl, oftewel bijna allemaal. Meet in dat geval de omtrek van de kinderen.\n */\n let r = el.getBoundingClientRect();\n if (r.width === 0 && r.height === 0 && el.children.length > 0) {\n let top = Infinity, right = -Infinity, bottom = -Infinity;\n for (const kind of Array.from(el.children)) {\n const kr = kind.getBoundingClientRect();\n if (kr.width === 0 && kr.height === 0) continue;\n top = Math.min(top, kr.top);\n right = Math.max(right, kr.right);\n bottom = Math.max(bottom, kr.bottom);\n }\n if (Number.isFinite(top)) r = { top, right, bottom, width: right, height: bottom - top } as DOMRect;\n }\n // Dan nog 0×0 (echt niets zichtbaars, bv. een script-achtig blok): geen aanwijzing maar ruis.\n if (r.width === 0 && r.height === 0) continue;\n // Volledig uit beeld gescrolde blokken hoeven geen knoppen: die zouden vastgeklemd worden\n // aan de boven- of onderrand en daar blijven zweven bij een blok dat je niet ziet.\n if (r.bottom < veilig || r.top > window.innerHeight) continue;\n const type = el.dataset.xicmsBlockType ?? \"\";\n gevonden.push({\n key,\n type,\n top: Math.max(r.top + 8, veilig + 8),\n // Vanaf de RECHTERkant verankerd: het palet klapt uit en moet die ruimte naar LINKS\n // pakken — een full-width blok eindigt op de viewportrand, rechts is daar per definitie\n // geen plek. Met een left-anker schoof het uitgeklapte deel het scherm af en leek de\n // hele uitbreiding gewoon niet te bestaan.\n right: Math.max(window.innerWidth - r.right + 8, 8),\n label: registryRef.current.get(type)?.label ?? type,\n });\n }\n setBlokknoppen((vorige) => {\n // Ongewijzigde posities niet terugschrijven: elke setState is een render, en een render\n // meet opnieuw. Zonder deze vergelijking draait dat rond.\n if (vorige.length === gevonden.length && vorige.every((v, i) =>\n v.key === gevonden[i].key && v.top === gevonden[i].top && v.right === gevonden[i].right)) {\n return vorige;\n }\n return gevonden;\n });\n };\n // rAF-bundeling: scroll- en resize-events komen in salvo's; meten hoeft hoogstens één keer\n // per frame. Zonder dit liep de meting tijdens het scrollen vaker dan het scherm ververst.\n let frame = 0;\n const meetGepland = () => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(meet);\n };\n meet();\n window.addEventListener(\"scroll\", meetGepland, { capture: true, passive: true });\n window.addEventListener(\"resize\", herijk);\n return () => {\n cancelAnimationFrame(frame);\n window.removeEventListener(\"scroll\", meetGepland, { capture: true });\n window.removeEventListener(\"resize\", herijk);\n };\n }, [draft, displayBlocks, registryRef]);\n\n /**\n * Producten krijgen hun eigen potlood en tandwiel, maar alleen waar je muis is.\n *\n * Een cataloguspagina toont er twaalf tegelijk. Twaalf vaste knoppenparen naast twaalf kaarten is\n * geen gereedschap meer maar een raster van knoppen over de site heen — dan kijk je niet meer naar\n * je winkel. Op aanwijzen verschijnt er precies één paar, bij het product waar je naar kijkt.\n */\n const [productKnop, setProductKnop] = useState<{ id: string; top: number; right: number } | null>(null);\n useEffect(() => {\n if (!draft) return;\n const onOver = (e: MouseEvent) => {\n const doel = (e.target as HTMLElement | null)?.closest?.(\"[data-xicms-product]\") as HTMLElement | null;\n if (!doel) {\n // Niet meteen weghalen zodra je de kaart verlaat: je verlaat hem juist om NAAR de knoppen te\n // gaan. De knoppen zelf zitten niet in de kaart, dus zonder deze uitzondering verdwijnen ze\n // op weg ernaartoe en is er niets aan te klikken.\n const naarKnop = (e.target as HTMLElement | null)?.closest?.(\".xicms-ve-blokknoppen\");\n if (!naarKnop) setProductKnop(null);\n return;\n }\n const id = doel.dataset.xicmsProduct;\n if (!id) return;\n const r = doel.getBoundingClientRect();\n const top = Math.max(r.top + 8, 44);\n const right = Math.max(window.innerWidth - r.right + 8, 8);\n setProductKnop((v) => (v && v.id === id && v.top === top && v.right === right ? v : { id, top, right }));\n };\n document.addEventListener(\"mouseover\", onOver, true);\n return () => document.removeEventListener(\"mouseover\", onOver, true);\n }, [draft]);\n\n /**\n * Inline bewerken van een PRODUCT — een ander pad dan een blokveld, met opzet.\n *\n * Een blokveld gaat naar de werkversie van deze pagina en wordt zichtbaar bij publiceren. Een\n * product staat in de productencollectie en wordt door élke pagina gelezen die het toont; daar\n * bestaat geen werkversie voor. Zie `productClient.ts` voor waarom dat zo blijft.\n */\n const productBewerkRef = useRef<HTMLElement | null>(null);\n const startProductInline = useCallback(\n (productId: string) => {\n const host = document.querySelector<HTMLElement>(`[data-xicms-product=\"${CSS.escape(productId)}\"]`);\n const el = host?.querySelector<HTMLElement>(\"[data-xicms-product-edit]\");\n if (!el) {\n toast({\n title: \"Hier valt niets te typen\",\n description: \"Gebruik het tandwiel voor prijs, foto's, varianten en voorraad.\",\n });\n return;\n }\n const veld = el.dataset.xicmsProductEdit;\n if (!veld) return;\n const origineel = el.textContent ?? \"\";\n el.setAttribute(\"contenteditable\", \"true\");\n el.setAttribute(\"spellcheck\", \"true\");\n el.classList.add(\"is-editing\");\n productBewerkRef.current = el;\n\n const stop = () => {\n el.removeAttribute(\"contenteditable\");\n el.classList.remove(\"is-editing\");\n el.removeEventListener(\"blur\", opBlur);\n el.removeEventListener(\"keydown\", opToets);\n if (productBewerkRef.current === el) productBewerkRef.current = null;\n };\n const opBlur = () => {\n const waarde = el.textContent ?? \"\";\n stop();\n if (waarde === origineel) return;\n void setProductField(siteId, productId, veld, waarde)\n .then(() =>\n toast({\n title: \"Product bijgewerkt\",\n description: \"Producten hebben geen werkversie: dit staat meteen live, op elke pagina.\",\n tone: \"success\",\n }),\n )\n .catch((err: Error) => {\n // Terugdraaien op het scherm, anders blijft er tekst staan die nergens bewaard is.\n el.textContent = origineel;\n toast({ title: \"Opslaan mislukt\", description: err.message, tone: \"error\" });\n });\n };\n const opToets = (ev: KeyboardEvent) => {\n if (ev.key === \"Escape\") {\n ev.stopPropagation();\n el.textContent = origineel;\n el.blur();\n } else if (ev.key === \"Enter\") {\n ev.preventDefault();\n el.blur();\n }\n };\n el.addEventListener(\"blur\", opBlur);\n el.addEventListener(\"keydown\", opToets);\n el.focus();\n const selection = window.getSelection();\n const range = document.createRange();\n range.selectNodeContents(el);\n range.collapse(false);\n selection?.removeAllRanges();\n selection?.addRange(range);\n },\n [siteId, toast],\n );\n\n /**\n * Rood en onderstreept: dit staat nog niet op de live site.\n *\n * De vraag die dit beantwoordt is \"wat heb ik nou eigenlijk veranderd?\" — en die was tot 7.8\n * onbeantwoordbaar. Je zag je eigen tekst staan en kon niet zien of een bezoeker hem al had.\n * Na publiceren wordt de markering vanzelf schoon, want dan is de werkversie gelijk aan wat er\n * live staat en levert `bepaalWijzigingen` niets meer op. De kleur is dus geen opmaak maar een\n * toestand: rood = van jou, gewone kleur = van iedereen.\n *\n * De klassen gaan op de LIVE DOM, net als de potloden — de render van de klant blijft schoon en\n * de markering verdwijnt met het gereedschap mee.\n */\n useEffect(() => {\n if (!draft) return;\n const gemarkeerd: HTMLElement[] = [];\n const markeer = (el: HTMLElement | null, klasse: string) => {\n if (!el) return;\n el.classList.add(klasse);\n gemarkeerd.push(el);\n };\n\n for (const key of wijzigingen.blokken) {\n markeer(document.querySelector<HTMLElement>(`[data-xicms-block=\"${CSS.escape(key)}\"]`), \"xicms-ve-nieuw\");\n }\n for (const [key, velden] of wijzigingen.velden) {\n const host = document.querySelector<HTMLElement>(`[data-xicms-block=\"${CSS.escape(key)}\"]`);\n if (!host) continue;\n for (const veld of velden) {\n // Het veldpad is dezelfde puntnotatie die het blok in zijn DOM-haak zet, dus dit is een\n // rechtstreekse treffer. Een veld dat wel wijzigde maar niets op het scherm zet (een kleur,\n // een limiet) heeft geen haak en levert hier gewoon niets op — terecht: er valt niets te\n // markeren aan iets wat je niet ziet. Dát er iets veranderde blijkt uit de knop rechtsonder.\n host\n .querySelectorAll<HTMLElement>(`[data-xicms-edit=\"${CSS.escape(veld)}\"]`)\n .forEach((el) => markeer(el, \"xicms-ve-gewijzigd\"));\n }\n }\n\n return () => {\n for (const el of gemarkeerd) {\n el.classList.remove(\"xicms-ve-nieuw\", \"xicms-ve-gewijzigd\");\n }\n };\n // `displayBlocks` staat erbij omdat de DOM-nodes na een re-render vervangen kunnen zijn: dan\n // moet er opnieuw gemarkeerd worden, ook al is de verzameling wijzigingen dezelfde.\n }, [draft, wijzigingen, displayBlocks]);\n\n const [spots, setSpots] = useState<{ top: number; left: number; title: string }[]>([]);\n const targetsRef = useRef<{ el: HTMLElement; field: string; html: boolean }[]>([]);\n const editingRef = useRef<HTMLElement | null>(null);\n /** Gezet door het potlood bij een blok: na het binden meteen in het eerste veld staan. */\n const wilInlineRef = useRef(false);\n /**\n * Welke spot (veld-potlood) hoort bij het veld dat NU bewerkt wordt? Dat potlood wordt een groen\n * vinkje (\"het is goed, klaar met dit blok\") — de afronding op de plek waar je hand al is, in\n * plaats van alleen in het blokknoppen-cluster rechtsboven het blok, buiten je blikveld.\n */\n const [editingIndex, setEditingIndex] = useState<number | null>(null);\n\n const stopInline = useCallback((el: HTMLElement) => {\n el.removeAttribute(\"contenteditable\");\n el.classList.remove(\"is-editing\");\n if (editingRef.current === el) {\n editingRef.current = null;\n setEditingIndex(null);\n }\n }, []);\n\n /** Eerlijk melden waarom het potlood niets opent — bij een blok zonder inline-velden. */\n const meldGeenTekst = useCallback(() => {\n toast({\n title: \"Dit blok heeft geen eigen tekst\",\n description: \"Wijs een product aan voor zijn naam, of gebruik het tandwiel voor de instellingen van dit blok.\",\n });\n }, [toast]);\n\n const startInline = useCallback((index: number) => {\n const target = targetsRef.current[index];\n if (!target) return;\n const { el } = target;\n if (editingRef.current && editingRef.current !== el) editingRef.current.blur();\n el.setAttribute(\"contenteditable\", \"true\");\n el.setAttribute(\"spellcheck\", \"true\");\n el.classList.add(\"is-editing\");\n editingRef.current = el;\n setEditingIndex(index);\n el.focus();\n // Caret aan het eind, geen selectie over de hele tekst: bewerken is meestal bijschaven, en een\n // volledige selectie betekent dat de eerste toetsaanslag alles wist.\n const selection = window.getSelection();\n const range = document.createRange();\n range.selectNodeContents(el);\n range.collapse(false);\n selection?.removeAllRanges();\n selection?.addRange(range);\n }, []);\n\n useEffect(() => {\n if (!selectedKey) {\n targetsRef.current = [];\n setSpots([]);\n return;\n }\n // Het blok staat op de LIVE pagina, niet in een canvas van de editor. `BlockRenderer` zet\n // `data-xicms-block` op elk blok — op de gepubliceerde pagina net zo goed als hier — dus dat is\n // de haak. Vandaar ook `document` en niet een ref: de pagina is niet van de editor.\n const host = document.querySelector<HTMLElement>(`[data-xicms-block=\"${selectedKey}\"]`);\n if (!host) return;\n const geselecteerd = blocksRef.current.find((b) => b._key === selectedKey);\n const type = geselecteerd?._type ?? \"\";\n const def = registry.get(String(type));\n warnInlineCoverage(String(type), def, host, geselecteerd ?? {});\n\n // Leesbare veldnamen voor de tooltip van het potlood (\"Bewerk: Ondertitel\"), uit dezelfde\n // schema-beschrijvingen die het zijpaneel gebruikt — zodat beide paden hetzelfde heten.\n const labels = new Map<string, string>();\n if (def) {\n try {\n for (const f of introspectToFields(def.schema, def.fields)) labels.set(f.key, f.label);\n } catch {\n /* een blok zonder introspecteerbaar schema krijgt gewoon de veldnaam */\n }\n }\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.classList.add(\"xicms-ve-inline\");\n el.addEventListener(\"blur\", () => {\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 commitText(selectedKey, field, html ? DOMPurify.sanitize(el.innerHTML) : (el.textContent ?? \"\"));\n stopInline(el);\n });\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\") {\n // NIET laten doorborrelen. De editor luistert zelf ook op Escape om te sluiten, en die\n // controle (\"sta ik in een invoerveld?\") kijkt naar het element dat focus HEEFT. Na\n // `blur()` is dat de body, dus concludeerde hij dat je niet aan het typen was en sloot\n // hij de hele bewerkmodus. Eén Escape hoorde het veld te sluiten, niet de werkplek.\n e.stopPropagation();\n el.blur();\n } else if (!html && e.key === \"Enter\") { e.preventDefault(); el.blur(); }\n });\n bound.add(el);\n };\n\n /** De potloodjes staan in de editor-laag, niet in het blok: de render van de klant blijft schoon. */\n const measure = () => {\n const next = targetsRef.current.map(({ el, field }) => {\n const r = el.getBoundingClientRect();\n // Naast het veld, op ooghoogte van de tekst: rechts ervan, verticaal gecentreerd. Niet\n // links (dan staat het potlood van de tweede knop bovenop de eerste) en niet ín de hoek\n // (dan ligt het over de tekst). Aanwijzen laat het veld oplichten, dus wélk veld het is\n // blijft ook bij twee elementen naast elkaar duidelijk.\n return {\n top: Math.min(window.innerHeight - 22, Math.max(4, r.top + r.height / 2 - 9)),\n left: Math.min(window.innerWidth - 22, Math.max(4, r.right + 6)),\n title: `Bewerk: ${labels.get(field) ?? field}`,\n };\n });\n setSpots((prev) =>\n prev.length === next.length && prev.every((p, i) => p.top === next[i].top && p.left === next[i].left && p.title === next[i].title)\n ? prev\n : next,\n );\n };\n\n const apply = () => {\n const found: { el: HTMLElement; field: string; html: boolean }[] = [];\n for (const m of GLOBAL_INLINE) {\n host.querySelectorAll<HTMLElement>(m.selector).forEach((el) => {\n // Een expliciete `data-xicms-edit` wint altijd; anders het veld van de matcher.\n const field = el.dataset.xicmsEdit ?? m.field;\n bind(el, field, !!m.html);\n found.push({ el, field, html: !!m.html });\n });\n }\n targetsRef.current = found;\n measure();\n };\n\n apply();\n if (wilInlineRef.current) {\n wilInlineRef.current = false;\n if (targetsRef.current.length > 0) {\n startInline(0);\n } else {\n /**\n * Nog geen velden gebonden ≠ geen velden. Het blok kan op ditzelfde moment re-renderen\n * (de selectie kleurt hem bij, een afbeelding hydrateert) en dan is de DOM waar apply()\n * net in zocht alweer vervangen. Twee frames later nog eens kijken vangt precies dat —\n * en pas als het er dán nog niet is, is \"geen tekst\" een eerlijke conclusie in plaats\n * van een race verkocht als feit.\n */\n let pogingen = 0;\n const opnieuw = () => {\n apply();\n if (targetsRef.current.length > 0) startInline(0);\n else if (++pogingen < 3) requestAnimationFrame(opnieuw);\n else meldGeenTekst();\n };\n requestAnimationFrame(opnieuw);\n }\n }\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 // Het canvas scrollt onder de potloodjes door; ze staan fixed, dus hermeten bij elke beweging.\n let frame = 0;\n const onMove = () => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(measure);\n };\n window.addEventListener(\"scroll\", onMove, { capture: true, passive: true });\n window.addEventListener(\"resize\", onMove, { passive: true });\n return () => {\n obs.disconnect();\n cancelAnimationFrame(frame);\n window.removeEventListener(\"scroll\", onMove, { capture: true });\n window.removeEventListener(\"resize\", onMove);\n bound.forEach((el) => {\n stopInline(el);\n el.classList.remove(\"xicms-ve-inline\");\n delete el.dataset.veInline;\n });\n targetsRef.current = [];\n setSpots([]);\n };\n // Alleen `selectedKey`: dit effect bindt de velden van hét geselecteerde blok. `blocks` en\n // `registry` lezen we uit een ref/context — draaide dit effect op elke draft-wijziging, dan\n // sloopte een autosave de lopende bewerking. Nieuwe DOM na een re-render wordt opgevangen door\n // de MutationObserver hieronder, niet door een her-run van het effect.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedKey, commitText, positionFmtBar, stopInline, startInline, toast]);\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 // Twee redenen om te waarschuwen, en ze zijn niet hetzelfde:\n // · `dirtyRef` — de laatste toetsaanslagen staan nog niet in de werkversie (autosave loopt).\n // · `nietLiveRef` — er is wél opgeslagen, maar niets van dit alles staat op de live site.\n // Tot 7.8 waarschuwde alleen de eerste. Wie rustig had zitten typen kreeg dus géén melding,\n // want alles was netjes bewaard — terwijl er nog steeds niets gepubliceerd was. Precies de\n // situatie waarin je denkt dat je klaar bent.\n if (dirtyRef.current || nietLiveRef.current) {\n e.preventDefault();\n e.returnValue = \"\";\n }\n bewaarHistory(); // synchroon en goedkoop: de undo-stapel overleeft zo ook een refresh\n };\n const onVisibility = () => {\n // Tab-wissel/minimaliseren: beste moment om pending werk veilig te stellen.\n if (document.visibilityState === \"hidden\") {\n void flushSave().catch(() => {});\n bewaarHistory();\n }\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 // Zelfde herkenning als useNestedDialog: Astryx' dialoog is een natieve <dialog open>.\n const dialogOpen = document.querySelector('dialog[open], [role=\"dialog\"][data-state=\"open\"], [role=\"alertdialog\"][data-state=\"open\"]');\n if (!inField && !dialogOpen) {\n // Lagen sluiten van binnen naar buiten: staat de zijlade open, dan sluit Escape éérst\n // de lade. De hele werkplek dichtgooien terwijl je alleen het paneel kwijt wilde, is\n // precies de verrassing die we bij invoervelden al hadden weggenomen.\n if (document.querySelector(\".xicms-ve__panel\")) setPanelOpen(false);\n else handleClose();\n }\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, bewaarHistory]);\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 /**\n * ✓ Klaar met dit blok: laatste bewerking committen, opslaan, selectie opruimen.\n *\n * Autosave deed het bewaren al; wat ontbrak was het MOMENT. Wie klaar was met een blok had\n * geen enkele knop om dat te zeggen en bleef achter met een geselecteerd blok vol potloodjes —\n * en de vraag of het nou opgeslagen was. Deze knop beantwoordt die vraag: veld dicht, werkversie\n * geschreven, gereedschap weg.\n */\n const klaarMetBlok = useCallback(() => {\n (document.activeElement as HTMLElement | null)?.blur();\n setSelectedKey(null);\n setPanelOpen(false);\n void flushSave()\n .then(() => toast({ title: \"Opgeslagen als werkversie\", description: \"Publiceer (rechtsonder) om het live te zetten.\", tone: \"success\" }))\n .catch(() => setStatus(\"error\"));\n }, [flushSave, toast]);\n\n /**\n * ↺ Wijzigingen van dít blok verwerpen: terug naar wat er live staat.\n *\n * Undo bestaat, maar undo loopt over de hele pagina en in bewerkvolgorde — wie drie blokken\n * heeft aangeraakt en er ééntje wil terugdraaien, kan daar niet mee uit de voeten. Dit is de\n * gerichte variant: de gepubliceerde versie van precies dit blok komt terug (in de demo: de\n * seed). Een blok dat nog niet live bestaat heeft niets om naar terug te keren — verwerpen is\n * dan verwijderen, langs de bestaande bevestiging.\n */\n async function herstelBlok(i: number) {\n const key = blocks[i]?._key;\n if (!key) return;\n const liveBlok = (liveBlocks ?? []).find((b) => String(b._key) === key);\n if (!liveBlok) {\n await remove(i);\n return;\n }\n const ok = await confirm({\n title: \"Wijzigingen van dit blok verwerpen?\",\n description: \"Het blok wordt teruggezet naar de versie die nu live staat. Met Ongedaan maken (Ctrl+Z) kun je hier nog op terugkomen.\",\n confirmLabel: \"Verwerpen\",\n tone: \"danger\",\n });\n if (!ok) return;\n setBlocks(blocks.map((b, idx) => (idx === i ? (structuredClone(liveBlok) as Block) : b)));\n toast({ title: \"Blok teruggezet\", description: \"Dit blok is weer gelijk aan de live versie.\", tone: \"success\" });\n }\n\n /**\n * Alles verwerpen: de hele pagina terug naar wat er live staat — het \"Annuleren\" naast\n * Publiceren. Er wás wel een uitgang per blok (↺) en undo, maar geen tegenhanger van de\n * publiceerknop zelf: wie een bewerksessie wilde weggooien moest blok voor blok terug.\n * Via setBlocks, dus de history vangt het op: Ctrl+Z maakt ook dít ongedaan.\n */\n async function verwerpAlles() {\n const ok = await confirm({\n title: \"Alle wijzigingen op deze pagina verwerpen?\",\n description:\n \"De hele pagina wordt teruggezet naar de versie die nu live staat. Met Ongedaan maken (Ctrl+Z) kun je hier nog op terugkomen.\",\n confirmLabel: \"Alles verwerpen\",\n tone: \"danger\",\n });\n if (!ok) return;\n (document.activeElement as HTMLElement | null)?.blur();\n setSelectedKey(null);\n setPanelOpen(false);\n setBlocks(structuredClone(liveBlocks ?? []) as Block[]);\n toast({ title: \"Wijzigingen verworpen\", description: \"De pagina is weer gelijk aan de live versie.\", tone: \"success\" });\n }\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 // Zie flushSave: een veld dat pas op blur committeert (JSON-redmiddel) moet die commit eerst\n // nog doen — anders publiceert dit de stand van vóór de laatste bewerking.\n (document.activeElement as HTMLElement | null)?.blur();\n const d = draftRef.current;\n if (!d) 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 // In een demo gaat er niets naar een live site; dat beloven zou liegen tegen precies de\n // bezoeker die we aan het overtuigen zijn. Zelfde knop, eerlijke woorden.\n const demo = isDemoActive();\n const ok = await confirm({\n title: \"Pagina publiceren?\",\n description: demo\n ? \"In deze demo blijft alles in je eigen browser — er gaat niets echt online.\"\n : `De live site wordt bijgewerkt (${d.path}).`,\n confirmLabel: \"Publiceren\",\n });\n if (!ok) return;\n setPublishing(true);\n try {\n await saveDraft(siteId, d.id, draftPayload(d));\n await publishPage(siteId, d.id);\n /**\n * De maatstaf schuift mee: wat zojuist gepubliceerd is, IS nu de live versie.\n *\n * Zonder deze regel bleef `liveBlocks` op de vorige publicatie staan — en dus bleef élke\n * markering rood en de knop blauw (\"14 wijzigingen\") terwijl alles allang live stond. De\n * hele belofte van de knop (\"grijs = er is niets te publiceren\") brak precies op het moment\n * dat hij zijn werk had gedaan. Lokaal bijschuiven is hier ook juister dan opnieuw lezen:\n * wat de server zojuist heeft weggeschreven is per definitie wat we net stuurden.\n */\n setLiveBlocks(structuredClone(d.blocks) as Block[]);\n toast({\n title: \"Gepubliceerd\",\n description: demo\n ? \"In deze demo blijft de wijziging in je browser staan.\"\n : \"Binnen enkele seconden zichtbaar voor iedereen.\",\n tone: \"success\",\n // Sluit de mentale lus \"staat het er nu echt?\": één klik en je kijkt als bezoeker.\n action: { label: \"Bekijk als bezoeker →\", onClick: handleClose },\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 ref={containerRef} className=\"xicms-ve__shell\" data-xicms-tooling=\"\" role=\"group\" aria-label={`Pagina bewerken: ${path}`}>\n {toonStartHint && (\n <div className=\"xicms-ve-starthint\" role=\"status\">\n <span>\n ✏️ Klik het <strong>potlood</strong> naast een tekst om die op de pagina zelf te bewerken;\n foto&apos;s wissel je met <strong>📷 Foto wijzigen</strong>. Klaar? <strong>Publiceren</strong> staat rechtsonder.\n </span>\n <button type=\"button\" onClick={sluitStartHint}>Begrepen</button>\n </div>\n )}\n {/* Dezelfde balk als op de live site (`EditBar`), met hetzelfde `AdminModes` erin — alleen\n staat \"Bewerken\" nu ingedrukt en hangt het gereedschap ernaast. Zo verspringt er niets\n op het moment dat je overschakelt: de knoppen blijven waar je ze net nog zag.\n Publiceren staat hier BEWUST niet meer; dat is de zwevende knop rechtsonder, bij je werk,\n en niet weggestopt tussen de navigatie. */}\n <header className=\"xicms-ve__bar\" role=\"toolbar\" aria-label=\"Beheer\">\n <span className=\"xicms-editbar__brand\">{isDemoActive() ? \"XiCMS · demo\" : \"XiCMS\"}</span>\n <AdminModes\n actief=\"bewerken\"\n voorSwitch={(m) => {\n if (m === \"live\" && nietLiveRef.current) {\n const n = aantalWijzigingen(wijzigingen);\n toast({\n title: \"Nog niet gepubliceerd\",\n // Enkelvoud/meervoud netjes: \"1 wijziging blijft\", \"14 wijzigingen blijven\".\n description: `${n} ${n.startsWith(\"1 \") ? \"blijft\" : \"blijven\"} bewaard als werkversie. Je ziet ze terug zodra je weer naar Bewerken gaat.`,\n });\n }\n }}\n />\n <span className=\"xicms-ve__sep\" aria-hidden=\"true\" />\n <span className=\"xicms-ve__path\" title={`Je bewerkt ${name} — ${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={(l) => { setLocale(l); if (l !== localeCfg.defaultLocale) setPanelOpen(true); }}\n options={localeCfg.locales.map((l) => ({ value: l, label: l === localeCfg.defaultLocale ? `${l} (standaard)` : l }))}\n />\n </span>\n )}\n <button\n type=\"button\"\n className={`xicms-ve__histbtn${panelOpen && !selectedKey ? \" is-on\" : \"\"}`}\n onClick={() => { if (panelOpen && !selectedKey) { setPanelOpen(false); } else { setSelectedKey(null); setPanelOpen(true); } }}\n title=\"Pagina-instellingen (titel, SEO, versies)\"\n aria-label=\"Pagina-instellingen\"\n aria-pressed={panelOpen && !selectedKey}\n >\n ⚙\n </button>\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 {status === \"none\" && (\n <span className=\"xicms-ve__balkmelding\">\n Nog geen werkversie\n <Button size=\"sm\" onClick={() => void handleCreatePage()}>+ Pagina aanmaken</Button>\n </span>\n )}\n {/* \"Opgeslagen\" gaat over de WERKVERSIE, niet over de live site. Dat onderscheid stond\n nergens en werd daardoor gelezen als \"het staat online\". Vandaar de toevoeging. */}\n <span className=\"xicms-ve__status\" aria-live=\"polite\">\n {status === \"loading\" ? \"Werkversie laden…\" : null}\n {status === \"saving\" ? \"Opslaan…\" : status === \"saved\" ? \"✓ Bewaard als werkversie\" : 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 </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${panelOpen ? \" is-panel-open\" : \"\"}`}>\n {/* GEEN CANVAS MEER. De pagina eronder is de echte pagina en toont de werkversie (zie\n EditorProvider). Wat de editor nog moet zeggen — laden, of nog geen werkversie — staat\n in de balk hierboven, niet als los zwevend kaartje: dat dekte de menubalk van de site af,\n en gereedschap hoort de site niet in de weg te zitten. */}\n\n {panelOpen && (\n <aside className=\"xicms-ve__panel\">\n {/* Blokken toevoegen zat in de werkbalkjes van het canvas. Dat canvas is er niet meer, dus\n staat het hier — zichtbaar wanneer je het paneel toch al open hebt om aan de opbouw\n van de pagina te werken. Zonder deze verhuizing zou het toevoegen van een blok\n stilzwijgend zijn verdwenen. */}\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 {/* Een aangeklikt PRODUCT wint van een aangeklikt blok: je wees het product aan, dus dat\n wil je zien. Blok en product uitsluiten elkaar hier bewust — twee formulieren onder\n elkaar in één smal paneel is niet te lezen. */}\n {selectedProduct ? (\n <div className=\"grid gap-3\">\n <div className=\"xicms-ve__panel-head\">\n <span className=\"text-sm text-slate-500\">Webwinkel</span>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => { setSelectedProduct(null); setPanelOpen(false); }}>\n ✕ Sluiten\n </Button>\n </div>\n <ProductPaneel siteId={siteId} productId={selectedProduct} />\n </div>\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 {/* Kleur volgt de autosave-status (zelfde bron als de statustekst in de topbalk):\n blauw = nog niet-opgeslagen wijziging (of een mislukte save die opnieuw moet),\n grijs = alles staat al veilig. Directe visuele bevestiging i.p.v. alleen tekst. */}\n <Button\n size=\"sm\"\n variant={status === \"saving\" || status === \"error\" ? \"primary\" : \"ghost\"}\n onClick={() => void flushSave().catch(() => setStatus(\"error\"))}\n >\n {status === \"saving\" ? \"Opslaan…\" : status === \"error\" ? \"⚠ Opnieuw opslaan\" : \"✓ Opgeslagen\"}\n </Button>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setPanelOpen(false)}>\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 )}\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 {/* Potlood én tandwiel per blok, plus — op aanwijzen — de rest van het gereedschap.\n De acties verplaatsen/dupliceren/verwijderen BESTONDEN al die tijd: ze zaten in de\n werkbalkjes van het oude canvas, en toen dat canvas verdween is de UI eromheen nooit\n teruggebouwd. De functies bleven staan, niets riep ze aan, en TypeScript klaagt niet over\n een ongebruikte functiedeclaratie — dus de enige weg om een blok te verplaatsen liep\n maandenlang via het formulierscherm in /beheer. Precies waar de klant weg moest blijven.\n\n Twee knoppen in rust, de rest klapt uit bij aanwijzen: zes knoppen die permanent naast\n élk blok staan zijn geen gereedschap meer maar een hek om de site heen. */}\n {blokknoppen.map((b) => {\n const i = blocks.findIndex((blok) => blok._key === b.key);\n const isSymbool = b.type === \"symbolRef\";\n return (\n <div key={b.key} className={`xicms-ve-blokknoppen${b.key === selectedKey ? \" is-open\" : \"\"}`} style={{ top: b.top, right: b.right }}>\n <button\n type=\"button\"\n className={`xicms-ve-blokknop${b.key === selectedKey ? \" is-on\" : \"\"}`}\n title={`Tekst van ${b.label} bewerken`}\n aria-label={`Tekst van ${b.label} bewerken`}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n /**\n * Al geselecteerd? Dan draait het bind-effect niet opnieuw (zelfde key) en zou deze\n * klik NIETS doen — precies wat er gebeurde zodra je één veld had bewerkt en\n * daarna nóg eens op het potlood drukte. Het eerste veld staat dan al gebonden in\n * targetsRef, dus we openen het rechtstreeks.\n */\n if (b.key === selectedKey) {\n if (targetsRef.current.length > 0) startInline(0);\n else meldGeenTekst();\n return;\n }\n // Selecteren zet de velden van dít blok open (zie het bind-effect hierboven); de vlag\n // zorgt dat de caret meteen in het eerste veld staat.\n wilInlineRef.current = true;\n setSelectedProduct(null);\n setSelectedKey(b.key);\n }}\n >\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M4 20h4L19.5 8.5a2.1 2.1 0 0 0-3-3L5 17v3z\" />\n <path d=\"M14.5 6.5l3 3\" />\n </svg>\n </button>\n <button\n type=\"button\"\n className={`xicms-ve-blokknop${b.key === selectedKey && panelOpen ? \" is-on\" : \"\"}`}\n title={`Instellingen van ${b.label}`}\n aria-label={`Instellingen van ${b.label}`}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => { setSelectedProduct(null); setSelectedKey(b.key); setPanelOpen(true); }}\n >\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\" />\n </svg>\n </button>\n {i >= 0 && (\n <span className=\"xicms-ve-blokknoppen__meer\"><span className=\"xicms-ve-blokknoppen__meer-in\">\n <button type=\"button\" className=\"xicms-ve-blokknop\" title=\"Blok omhoog\" aria-label={`${b.label} omhoog`} disabled={i === 0}\n onMouseDown={(e) => e.preventDefault()} onClick={() => move(i, -1)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M12 19V5M5 12l7-7 7 7\" /></svg>\n </button>\n <button type=\"button\" className=\"xicms-ve-blokknop\" title=\"Blok omlaag\" aria-label={`${b.label} omlaag`} disabled={i === blocks.length - 1}\n onMouseDown={(e) => e.preventDefault()} onClick={() => move(i, 1)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M12 5v14M19 12l-7 7-7-7\" /></svg>\n </button>\n <button type=\"button\" className=\"xicms-ve-blokknop\" title=\"Blok dupliceren\" aria-label={`${b.label} dupliceren`}\n onMouseDown={(e) => e.preventDefault()} onClick={() => duplicate(i)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\" /><path d=\"M5 15V5a2 2 0 0 1 2-2h10\" /></svg>\n </button>\n {isSymbool && (\n <button type=\"button\" className=\"xicms-ve-blokknop\" title=\"Symbool loskoppelen (wordt een losse kopie)\" aria-label={`${b.label} loskoppelen`}\n onMouseDown={(e) => e.preventDefault()} onClick={() => void detachSymbol(i)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M9 12h6M8 7a4 4 0 0 0 0 8M16 7a4 4 0 0 1 0 8\" /><path d=\"M3 3l18 18\" /></svg>\n </button>\n )}\n <button type=\"button\" className=\"xicms-ve-blokknop xicms-ve-blokknop--gevaar\" title=\"Blok verwijderen\" aria-label={`${b.label} verwijderen`}\n onMouseDown={(e) => e.preventDefault()} onClick={() => void remove(i)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2M6 6l1 14a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-14\" /></svg>\n </button>\n {/* ↺ alleen wanneer dit blok afwijkt van wat er live staat — een terugzet-knop op een\n ongewijzigd blok is een knop die niets kan doen, en die hoort er dan niet te zijn. */}\n {(wijzigingen.velden.has(b.key) || wijzigingen.blokken.has(b.key)) && (\n <button type=\"button\" className=\"xicms-ve-blokknop\" title=\"Wijzigingen van dit blok verwerpen (terug naar live)\" aria-label={`Wijzigingen van ${b.label} verwerpen`}\n onMouseDown={(e) => e.preventDefault()} onClick={() => void herstelBlok(i)}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M3 7v6h6\" /><path d=\"M3 13a9 9 0 1 0 3-7.7L3 7\" /></svg>\n </button>\n )}\n {b.key === selectedKey && (\n <button type=\"button\" className=\"xicms-ve-blokknop xicms-ve-blokknop--klaar\" title=\"Klaar met dit blok (opslaan als werkversie)\" aria-label={`Klaar met ${b.label}`}\n onMouseDown={(e) => e.preventDefault()} onClick={klaarMetBlok}>\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M20 6L9 17l-5-5\" /></svg>\n </button>\n )}\n </span></span>\n )}\n </div>\n );\n })}\n\n {/* Potloodjes bij elk inline bewerkbaar veld van het geselecteerde blok. Ze staan fixed in de\n editor-laag (niet in de render van de klant) en zetten het bewerken bewust aan.\n onMouseDown-preventDefault: anders neemt de knop de focus en sluit het veld meteen weer. */}\n {spots.map((s, i) => {\n // Het potlood van het veld dat je nú bewerkt is een groen vinkje: klikken rondt het blok\n // af (opslaan als werkversie), precies wat \"het is goed\" betekent.\n const actief = editingIndex === i;\n return (\n <button\n key={i}\n type=\"button\"\n className={`xicms-ve-pencil${actief ? \" is-klaar\" : \"\"}`}\n style={{ top: s.top, left: s.left }}\n title={actief ? \"Het is goed — klaar met dit blok (opslaan als werkversie)\" : s.title}\n aria-label={actief ? \"Klaar met dit blok\" : s.title}\n onMouseDown={(e) => e.preventDefault()}\n onMouseEnter={() => targetsRef.current[i]?.el.classList.add(\"is-hover\")}\n onMouseLeave={() => targetsRef.current[i]?.el.classList.remove(\"is-hover\")}\n onClick={() => (actief ? klaarMetBlok() : startInline(i))}\n >\n {actief ? (\n <svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M20 6L9 17l-5-5\" />\n </svg>\n ) : (\n /* Fijn getekend potlood i.p.v. een emoji: een emoji is per platform anders, kleurt niet\n mee en oogt op deze schaal als een vlek. Eén pad, currentColor, 1.4 lijndikte. */\n <svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M4 20h4L19.5 8.5a2.1 2.1 0 0 0-3-3L5 17v3z\" />\n <path d=\"M14.5 6.5l3 3\" />\n </svg>\n )}\n </button>\n );\n })}\n\n {/* Hetzelfde knoppenpaar, maar dan voor een PRODUCT — alleen zichtbaar bij het product dat je\n aanwijst (zie hierboven waarom). Zelfde vorm en zelfde plek als bij een blok, want het is\n dezelfde handeling: potlood voor de tekst, tandwiel voor de rest. */}\n {productKnop && (\n <div\n className=\"xicms-ve-blokknoppen is-product\"\n style={{ top: productKnop.top, right: productKnop.right }}\n >\n <button\n type=\"button\"\n className=\"xicms-ve-blokknop\"\n title=\"Productnaam bewerken (staat meteen live)\"\n aria-label=\"Productnaam bewerken\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => startProductInline(productKnop.id)}\n >\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M4 20h4L19.5 8.5a2.1 2.1 0 0 0-3-3L5 17v3z\" />\n <path d=\"M14.5 6.5l3 3\" />\n </svg>\n </button>\n <button\n type=\"button\"\n className={`xicms-ve-blokknop${selectedProduct === productKnop.id ? \" is-on\" : \"\"}`}\n title=\"Product bewerken: prijs, foto's, varianten, voorraad\"\n aria-label=\"Product bewerken\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n setSelectedKey(null);\n setSelectedProduct(productKnop.id);\n setPanelOpen(true);\n }}\n >\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\" />\n </svg>\n </button>\n </div>\n )}\n\n {/*\n Publiceren staat rechtsonder, bij je werk — niet bovenin tussen de navigatie.\n Daar was het een knop die altijd even hard riep, ook als er niets te publiceren viel, en\n dus niets meer betekende. Nu zegt de knop zelf of er iets klaarstaat: grijs en uitgeschakeld\n zolang de pagina gelijk is aan wat er live staat, en pas gekleurd zodra dat niet meer zo is.\n Dat is dezelfde bron als de rode markering op de pagina, dus de knop en de pagina kunnen het\n nooit oneens zijn.\n */}\n {/* Annuleren naast Publiceren: pas zichtbaar als er iets te verwerpen vált — een knop die\n niets kan doen hoort er niet te staan (zelfde regel als de ↺ per blok). */}\n {draft && wijzigingen.iets && !publishing && (\n <button\n type=\"button\"\n className=\"xicms-ve-verwerp\"\n onClick={() => void verwerpAlles()}\n title=\"Alle wijzigingen op deze pagina verwerpen (terug naar live)\"\n >\n ↺ Verwerpen\n </button>\n )}\n {draft && (\n <button\n type=\"button\"\n className={`xicms-ve-publiceer${wijzigingen.iets ? \" is-klaar\" : \"\"}`}\n disabled={publishing || !wijzigingen.iets}\n onClick={() => void onPublish()}\n title={\n wijzigingen.iets\n ? \"Zet deze wijzigingen op de live site\"\n : \"Er is niets veranderd sinds de laatste publicatie\"\n }\n >\n <span className=\"xicms-ve-publiceer__label\">{publishing ? \"Bezig…\" : \"Publiceren\"}</span>\n <span className=\"xicms-ve-publiceer__sub\">\n {wijzigingen.iets ? aantalWijzigingen(wijzigingen) : \"niets gewijzigd\"}\n </span>\n </button>\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"]}