@cancia/toolbar 0.0.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.
- package/LICENSE +21 -0
- package/dist/cancia.d.ts +50 -0
- package/dist/cancia.iife.js +605 -0
- package/dist/cancia.js +2629 -0
- package/dist/cancia.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/state.ts","../src/api.ts","../src/highlight.ts","../src/events.ts","../src/popup.ts","../src/list-panel.ts","../src/entry-modal.ts","../src/toolbar.ts","../src/index.ts"],"sourcesContent":["// =============================================================================\n// Cancia Toolbar — Shared State\n// =============================================================================\n\nimport type { CanciaConfig, CMSData, PendingChange } from \"./types\";\nimport type { ListSchemaDescription } from \"./api\";\n\nexport const state = {\n config: null as CanciaConfig | null,\n cmsData: {} as CMSData,\n pending: new Map<string, PendingChange>(),\n activeLang: \"\",\n editMode: false,\n /** Auth token from sessionStorage — set by toolbar/index.ts after session validation */\n sessionToken: \"\" as string,\n /** Called by toolbar logout button — wired up by index.ts to avoid circular deps */\n onLogout: null as (() => void) | null,\n /** List schemas, keyed by name. Loaded once on toolbar init. */\n schemas: {} as Record<string, ListSchemaDescription>,\n /** Currently-selected locale for list-panel + modal. Defaults to activeLang on open. */\n activeListLocale: \"\" as string,\n};\n\nexport function pendingKey(key: string, lang: string) {\n return `${key}.${lang}`;\n}\n\nexport function getValue(key: string, lang: string): string {\n const full = `${key}.${lang}`;\n // Pending changes win\n const p = state.pending.get(full);\n if (p) return p.value;\n // Then CMS overrides\n if (state.cmsData[full] !== undefined) return state.cmsData[full];\n return \"\";\n}\n\nexport function setPending(key: string, lang: string, value: string) {\n const full = pendingKey(key, lang);\n state.pending.set(full, { key, lang, value });\n}\n\nexport function clearPending() {\n state.pending.clear();\n}\n\n/**\n * Overlay saved (draft) CMS values onto the prerendered page.\n *\n * On a `static` build the page HTML is baked at build time, so an editor's\n * saved-but-unpublished changes (which live in `state.cmsData`) never show up\n * on reload. This walks every `[data-cms]` element and, when there is a saved\n * value for the active language, applies it client-side — so editors see their\n * drafts while normal visitors (no session, no toolbar) keep the baked build.\n *\n * Only runs from `init`, which is gated behind a valid session or `public`\n * mode, so it never affects normal visitors.\n */\nexport function applyOverlay() {\n document.querySelectorAll<HTMLElement>(\"[data-cms]\").forEach((el) => {\n // Skip list-region containers — they mark list areas, not text nodes.\n if (el.dataset.cmsList !== undefined) return;\n\n const key = el.dataset.cms;\n if (!key) return;\n\n const savedValue = state.cmsData[`${key}.${state.activeLang}`];\n // No saved override → leave the baked value in place.\n if (savedValue === undefined) return;\n\n if (el.tagName === \"IMG\") {\n (el as HTMLImageElement).src = savedValue;\n return;\n }\n\n // Safety: setting textContent on an element with child ELEMENT nodes would\n // destroy them. Only overlay elements whose children are all text/comments.\n if (el.childElementCount > 0) {\n console.warn(\n `Cancia: skipping overlay for \"${key}\" — element has child elements ` +\n `(textContent would destroy them).`\n );\n return;\n }\n\n el.textContent = savedValue;\n });\n}\n\n/** Revert all pending changes — restores DOM elements to their last saved CMS value */\nexport function revertPending() {\n for (const [fullKey, { key, lang }] of state.pending) {\n const savedValue = state.cmsData[fullKey] ?? \"\";\n // Update any matching data-cms elements in the DOM\n document.querySelectorAll<HTMLElement>(`[data-cms=\"${key}\"]`).forEach((el) => {\n if (el.tagName === \"IMG\") {\n (el as HTMLImageElement).src = savedValue;\n } else {\n el.textContent = savedValue;\n }\n });\n }\n state.pending.clear();\n}\n","// =============================================================================\n// Cancia Toolbar — API Client\n// =============================================================================\n\nimport { state } from \"./state\";\nimport type { CMSData } from \"./types\";\n\nfunction headers(): HeadersInit {\n const h: HeadersInit = { \"Content-Type\": \"application/json\" };\n if (state.sessionToken) h[\"Authorization\"] = `Bearer ${state.sessionToken}`;\n return h;\n}\n\nexport async function fetchContent(): Promise<CMSData> {\n const { apiUrl, site } = state.config!;\n const res = await fetch(`${apiUrl}/api/cancia/content?site=${encodeURIComponent(site)}`, {\n headers: headers(),\n });\n if (!res.ok) throw new Error(`Cancia: failed to fetch content (${res.status})`);\n return res.json();\n}\n\nexport async function saveEntry(key: string, lang: string, value: string): Promise<void> {\n const { apiUrl, site } = state.config!;\n const res = await fetch(`${apiUrl}/api/cancia/save`, {\n method: \"POST\",\n headers: headers(),\n body: JSON.stringify({ key, lang, value, site }),\n });\n if (!res.ok) throw new Error(`Cancia: failed to save (${res.status})`);\n}\n\nexport function isAuthError(err: unknown): boolean {\n return err instanceof Error && err.message.includes(\"(401)\");\n}\n\nexport function uploadImage(\n file: File,\n onProgress?: (percent: number) => void\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const { apiUrl, site } = state.config!;\n const form = new FormData();\n form.append(\"file\", file);\n form.append(\"site\", site);\n\n const xhr = new XMLHttpRequest();\n xhr.open(\"POST\", `${apiUrl}/api/cancia/upload`);\n if (state.sessionToken) xhr.setRequestHeader(\"Authorization\", `Bearer ${state.sessionToken}`);\n\n xhr.upload.addEventListener(\"progress\", (e) => {\n if (e.lengthComputable) onProgress?.(Math.round((e.loaded / e.total) * 100));\n });\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n resolve(JSON.parse(xhr.responseText).url as string);\n } catch {\n reject(new Error(\"Cancia: invalid upload response\"));\n }\n } else {\n reject(new Error(`Cancia: failed to upload image (${xhr.status})`));\n }\n });\n xhr.addEventListener(\"error\", () => reject(new Error(\"Cancia: upload network error\")));\n xhr.send(form);\n });\n}\n\nexport async function triggerPublish(): Promise<void> {\n const { apiUrl, site } = state.config!;\n const res = await fetch(`${apiUrl}/api/cancia/publish`, {\n method: \"POST\",\n headers: headers(),\n body: JSON.stringify({ site }),\n });\n if (!res.ok) throw new Error(`Cancia: publish failed (${res.status})`);\n}\n\n// ---------------------------------------------------------------------------\n// Lists + schemas (v2)\n// ---------------------------------------------------------------------------\n\nexport interface ListSchemaField {\n name: string;\n label: string;\n description?: string;\n widget: \"text\" | \"textarea\" | \"url\" | \"email\" | \"datetime\" | \"number\" | \"checkbox\" | \"select\" | \"image\";\n required: boolean;\n placeholder?: string;\n options?: string[];\n minLength?: number;\n maxLength?: number;\n min?: number;\n max?: number;\n}\n\nexport interface ListSchemaDescription {\n name: string;\n label: string;\n labelSingular: string;\n titleField: string;\n bodyField?: string;\n slugField?: string;\n fields: ListSchemaField[];\n}\n\nexport interface ListEntry {\n id: string;\n data: Record<string, unknown>;\n locale: string;\n createdAt: string;\n updatedAt: string;\n _rev: string;\n}\n\nexport interface TranslationStatus {\n id: string;\n locales: string[];\n}\n\nexport async function fetchSchemas(): Promise<Record<string, ListSchemaDescription>> {\n const { apiUrl } = state.config!;\n const res = await fetch(`${apiUrl}/api/cancia/schemas`, { headers: headers() });\n if (!res.ok) throw new Error(`Cancia: failed to fetch schemas (${res.status})`);\n const body = (await res.json()) as { schemas: Record<string, ListSchemaDescription> };\n return body.schemas;\n}\n\nfunction localeParam(locale?: string): string {\n return locale ? `&locale=${encodeURIComponent(locale)}` : \"\";\n}\n\nexport async function fetchList(listName: string, locale?: string): Promise<ListEntry[]> {\n const { apiUrl, site } = state.config!;\n const res = await fetch(\n `${apiUrl}/api/cancia/lists/${encodeURIComponent(listName)}?site=${encodeURIComponent(site)}${localeParam(locale)}`,\n { headers: headers() },\n );\n if (!res.ok) throw new Error(`Cancia: failed to fetch list \"${listName}\" (${res.status})`);\n const body = (await res.json()) as { entries: ListEntry[] };\n return body.entries;\n}\n\nexport async function fetchTranslations(listName: string): Promise<TranslationStatus[]> {\n const { apiUrl, site } = state.config!;\n const res = await fetch(\n `${apiUrl}/api/cancia/lists/${encodeURIComponent(listName)}/_translations?site=${encodeURIComponent(site)}`,\n { headers: headers() },\n );\n if (!res.ok) throw new Error(`Cancia: failed to fetch translations (${res.status})`);\n const body = (await res.json()) as { translations: TranslationStatus[] };\n return body.translations;\n}\n\nexport async function createListEntry(\n listName: string,\n data: Record<string, unknown>,\n locale: string,\n id?: string,\n): Promise<ListEntry> {\n const { apiUrl, site } = state.config!;\n const res = await fetch(\n `${apiUrl}/api/cancia/lists/${encodeURIComponent(listName)}?site=${encodeURIComponent(site)}${localeParam(locale)}`,\n { method: \"POST\", headers: headers(), body: JSON.stringify({ data, id }) },\n );\n if (!res.ok) {\n const err = await res.json().catch(() => ({})) as { error?: string };\n throw new Error(err.error ?? `Cancia: failed to create entry (${res.status})`);\n }\n const body = (await res.json()) as { entry: ListEntry };\n return body.entry;\n}\n\nexport async function updateListEntry(\n listName: string,\n id: string,\n data: Record<string, unknown>,\n rev: string,\n locale: string,\n): Promise<ListEntry> {\n const { apiUrl, site } = state.config!;\n const res = await fetch(\n `${apiUrl}/api/cancia/lists/${encodeURIComponent(listName)}/${encodeURIComponent(id)}?site=${encodeURIComponent(site)}${localeParam(locale)}`,\n { method: \"PATCH\", headers: headers(), body: JSON.stringify({ data, _rev: rev }) },\n );\n if (!res.ok) {\n const err = await res.json().catch(() => ({})) as { error?: string; code?: string };\n const message = err.error ?? `Cancia: failed to update entry (${res.status})`;\n throw Object.assign(new Error(message), { code: err.code });\n }\n const body = (await res.json()) as { entry: ListEntry };\n return body.entry;\n}\n\nexport async function deleteListEntry(listName: string, id: string, locale: string): Promise<void> {\n const { apiUrl, site } = state.config!;\n const res = await fetch(\n `${apiUrl}/api/cancia/lists/${encodeURIComponent(listName)}/${encodeURIComponent(id)}?site=${encodeURIComponent(site)}${localeParam(locale)}`,\n { method: \"DELETE\", headers: headers() },\n );\n if (!res.ok) throw new Error(`Cancia: failed to delete entry (${res.status})`);\n}\n\nexport async function reorderList(listName: string, ids: string[]): Promise<void> {\n const { apiUrl, site } = state.config!;\n const res = await fetch(\n `${apiUrl}/api/cancia/lists/${encodeURIComponent(listName)}/reorder?site=${encodeURIComponent(site)}`,\n { method: \"POST\", headers: headers(), body: JSON.stringify({ ids }) },\n );\n if (!res.ok) throw new Error(`Cancia: failed to reorder list (${res.status})`);\n}\n\n/** Save all pending changes in one batch. Removes successfully saved entries from pending. */\nexport async function flushPending(): Promise<void> {\n const entries = Array.from(state.pending.values());\n const results = await Promise.allSettled(\n entries.map(({ key, lang, value }) => saveEntry(key, lang, value))\n );\n results.forEach((result, i) => {\n if (result.status === \"fulfilled\") {\n const { key, lang } = entries[i];\n state.pending.delete(`${key}.${lang}`);\n }\n });\n const failed = results.filter((r) => r.status === \"rejected\").length;\n if (failed > 0) throw new Error(`Cancia: ${failed} save(s) failed`);\n}\n","// =============================================================================\n// Cancia Toolbar — Hover Highlight\n// =============================================================================\n// Scans [data-cms] elements. While edit mode is active, hovering shows a\n// coloured outline overlay + tooltip with field key. Clicking fires onSelect.\n// =============================================================================\n\nimport { state } from \"./state\";\n\nexport type CanciaSelection =\n | { kind: \"field\"; el: HTMLElement; key: string; fieldType: \"text\" | \"image\" }\n | { kind: \"list\"; el: HTMLElement; listName: string };\n\ntype SelectCallback = (selection: CanciaSelection) => void;\n\nconst CMS_SELECTOR = \"[data-cms], [data-cms-list]\";\n\nlet onSelect: SelectCallback | null = null;\nlet currentHighlighted: HTMLElement | null = null;\nlet cleanupFns: (() => void)[] = [];\nlet scrollRAF: number | null = null;\n\n// Overlay + tooltip elements (reused, not re-created per hover)\nlet overlayEl: HTMLElement | null = null;\nlet tooltipEl: HTMLElement | null = null;\nlet styleInjected = false;\n\nfunction accent() {\n return state.config?.accentColor ?? \"#6366f1\";\n}\n\nfunction fieldType(el: HTMLElement): \"text\" | \"image\" {\n if (el.dataset.cmsType === \"image\") return \"image\";\n return el.tagName === \"IMG\" ? \"image\" : \"text\";\n}\n\n/**\n * The two CMS modes. data-cms-list takes precedence when both are set on\n * the same element (rare but possible if a list is nested inside a KV\n * region — the inner list wins on the closest() lookup anyway).\n */\nfunction elementMode(el: HTMLElement): \"list\" | \"field\" {\n return el.dataset.cmsList ? \"list\" : \"field\";\n}\n\n// ---------------------------------------------------------------------------\n// Inject keyframes once\n// ---------------------------------------------------------------------------\n\nfunction injectStyles() {\n if (styleInjected) return;\n styleInjected = true;\n const s = document.createElement(\"style\");\n s.textContent = `\n @keyframes cancia-highlight-in {\n from { opacity: 0; transform: scale(0.98); }\n to { opacity: 1; transform: scale(1); }\n }\n @keyframes cancia-tooltip-in {\n from { opacity: 0; transform: scale(0.95) translateY(3px); }\n to { opacity: 1; transform: scale(1) translateY(0); }\n }\n `;\n document.head.appendChild(s);\n}\n\n// ---------------------------------------------------------------------------\n// Overlay: fixed border box that tracks the hovered element\n// ---------------------------------------------------------------------------\n\nfunction getOrCreateOverlay(): HTMLElement {\n if (!overlayEl) {\n overlayEl = document.createElement(\"div\");\n overlayEl.dataset.canciaOverlay = \"1\";\n overlayEl.style.cssText = `\n position: fixed;\n pointer-events: none !important;\n box-sizing: border-box;\n border-radius: 5px;\n z-index: 2147483644;\n will-change: top, left, width, height, opacity;\n transition: top 0.08s cubic-bezier(0.16,1,0.3,1), left 0.08s cubic-bezier(0.16,1,0.3,1),\n width 0.08s cubic-bezier(0.16,1,0.3,1), height 0.08s cubic-bezier(0.16,1,0.3,1);\n `;\n document.body.appendChild(overlayEl);\n }\n return overlayEl;\n}\n\nfunction getOrCreateTooltip(): HTMLElement {\n if (!tooltipEl) {\n tooltipEl = document.createElement(\"div\");\n tooltipEl.dataset.canciaTooltip = \"1\";\n tooltipEl.style.cssText = `\n position: fixed;\n pointer-events: none !important;\n z-index: 2147483645;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n font-size: 11px;\n font-weight: 500;\n letter-spacing: 0.02em;\n color: #fff;\n background: rgba(10,10,12,0.92);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n border: 1px solid rgba(255,255,255,0.1);\n padding: 4px 8px;\n border-radius: 6px;\n white-space: nowrap;\n max-width: 260px;\n overflow: hidden;\n text-overflow: ellipsis;\n box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n `;\n document.body.appendChild(tooltipEl);\n }\n return tooltipEl;\n}\n\nlet lastOverlayEl: HTMLElement | null = null; // track which el we last styled the badge for\n\n/** Shift a hex color's hue/saturation slightly so lists read as related-but-distinct from fields. */\nfunction listAccent(hex: string): string {\n // Cheap visual differentiation: deepen by 18% via a fixed shade rather than\n // recompute HSL. Falls back to a strong emerald if the accent isn't a hex.\n if (!/^#[0-9a-f]{6}$/i.test(hex)) return \"#059669\";\n const r = parseInt(hex.slice(1, 3), 16);\n const g = parseInt(hex.slice(3, 5), 16);\n const b = parseInt(hex.slice(5, 7), 16);\n // Swap RGB channels deterministically to land on a related-but-distinct hue.\n // Boring trick that keeps tone consistent with the user's accent choice.\n const shifted = [g, b, r].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\");\n return `#${shifted}`;\n}\n\nconst FIELD_ICON = `<svg width=\"10\" height=\"10\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\">\n <path d=\"M2 4h10M2 7h7M2 10h5\"/>\n</svg>`;\n\nconst IMAGE_ICON = `<svg width=\"10\" height=\"10\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"1\" y=\"1\" width=\"12\" height=\"12\" rx=\"2\"/>\n <circle cx=\"4.5\" cy=\"4.5\" r=\"1.2\"/>\n <path d=\"M1 9.5l3.5-3 2.5 2.5 2-1.5 3 3.5\"/>\n</svg>`;\n\nconst LIST_ICON = `<svg width=\"10\" height=\"10\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"1\" y=\"2\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n <rect x=\"1\" y=\"7.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n <path d=\"M6 3.5h7M6 9h7\"/>\n</svg>`;\n\nfunction positionOverlay(el: HTMLElement, animate = false) {\n const rect = el.getBoundingClientRect();\n const mode = elementMode(el);\n const a = mode === \"list\" ? listAccent(accent()) : accent();\n const overlay = getOrCreateOverlay();\n const tooltip = getOrCreateTooltip();\n\n const padding = 3;\n overlay.style.top = `${rect.top - padding}px`;\n overlay.style.left = `${rect.left - padding}px`;\n overlay.style.width = `${rect.width + padding * 2}px`;\n overlay.style.height = `${rect.height + padding * 2}px`;\n\n // Rebuild badge only when switching to a new element\n if (lastOverlayEl !== el) {\n lastOverlayEl = el;\n overlay.style.border = `2px ${mode === \"list\" ? \"dashed\" : \"solid\"} ${a}`;\n overlay.style.background = `${a}12`;\n\n let badgeIcon: string;\n let badgeLabel: string;\n let tooltipText: string;\n if (mode === \"list\") {\n badgeIcon = LIST_ICON;\n badgeLabel = \"list\";\n tooltipText = `list: ${el.dataset.cmsList}`;\n } else {\n const type = fieldType(el);\n badgeIcon = type === \"image\" ? IMAGE_ICON : FIELD_ICON;\n badgeLabel = type;\n tooltipText = el.dataset.cms ?? \"\";\n }\n\n overlay.innerHTML = `\n <div style=\"\n position: absolute; top: -1px; left: -1px;\n background: ${a}; color: #fff;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n font-size: 10px; font-weight: 600; letter-spacing: 0.04em;\n padding: 2px 6px; border-radius: 3px 0 4px 0;\n display: flex; align-items: center; gap: 4px;\n line-height: 1;\n \">\n ${badgeIcon}\n ${badgeLabel}\n </div>\n `;\n tooltip.textContent = tooltipText;\n }\n\n overlay.style.display = \"block\";\n if (animate) overlay.style.animation = \"cancia-highlight-in 0.12s ease-out forwards\";\n\n // Position tooltip above the element (flip below if not enough space)\n tooltip.style.display = \"block\";\n if (animate) tooltip.style.animation = \"cancia-tooltip-in 0.1s ease-out forwards\";\n\n const tooltipMargin = 8;\n const tooltipH = 26;\n if (rect.top - tooltipH - tooltipMargin > 0) {\n tooltip.style.top = `${rect.top - tooltipH - tooltipMargin + padding}px`;\n tooltip.style.left = `${rect.left - padding}px`;\n } else {\n tooltip.style.top = `${rect.bottom + tooltipMargin - padding}px`;\n tooltip.style.left = `${rect.left - padding}px`;\n }\n}\n\nfunction hideOverlay() {\n lastOverlayEl = null;\n if (overlayEl) {\n overlayEl.style.display = \"none\";\n overlayEl.style.animation = \"none\";\n }\n if (tooltipEl) {\n tooltipEl.style.display = \"none\";\n tooltipEl.style.animation = \"none\";\n }\n}\n\nfunction handleScroll() {\n // Throttle with rAF to avoid layout thrash on every scroll tick\n if (scrollRAF !== null) return;\n scrollRAF = requestAnimationFrame(() => {\n scrollRAF = null;\n if (currentHighlighted) {\n positionOverlay(currentHighlighted);\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Event handlers\n// ---------------------------------------------------------------------------\n\nfunction handleMouseOver(e: MouseEvent) {\n const target = (e.target as HTMLElement).closest(CMS_SELECTOR) as HTMLElement | null;\n if (!target) return;\n currentHighlighted = target;\n target.style.cursor = \"pointer\";\n positionOverlay(target, true);\n}\n\nfunction handleMouseOut(e: MouseEvent) {\n const target = (e.target as HTMLElement).closest(CMS_SELECTOR) as HTMLElement | null;\n if (!target) return;\n // Only hide if we're actually leaving this element (not moving to a child)\n const related = e.relatedTarget as HTMLElement | null;\n if (related && target.contains(related)) return;\n target.style.cursor = \"\";\n if (currentHighlighted === target) {\n currentHighlighted = null;\n hideOverlay();\n }\n}\n\nfunction handleClick(e: MouseEvent) {\n const target = (e.target as HTMLElement).closest(CMS_SELECTOR) as HTMLElement | null;\n if (!target) return;\n e.preventDefault();\n e.stopPropagation();\n hideOverlay();\n if (elementMode(target) === \"list\") {\n const listName = target.dataset.cmsList!;\n onSelect?.({ kind: \"list\", el: target, listName });\n } else {\n const key = target.dataset.cms!;\n onSelect?.({ kind: \"field\", el: target, key, fieldType: fieldType(target) });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport function attachHighlight(selectCallback: SelectCallback) {\n injectStyles();\n onSelect = selectCallback;\n\n document.addEventListener(\"mouseover\", handleMouseOver, true);\n document.addEventListener(\"mouseout\", handleMouseOut, true);\n document.addEventListener(\"click\", handleClick, true);\n window.addEventListener(\"scroll\", handleScroll, { passive: true, capture: true });\n\n cleanupFns = [\n () => document.removeEventListener(\"mouseover\", handleMouseOver, true),\n () => document.removeEventListener(\"mouseout\", handleMouseOut, true),\n () => document.removeEventListener(\"click\", handleClick, true),\n () => window.removeEventListener(\"scroll\", handleScroll, true),\n ];\n}\n\nexport function detachHighlight() {\n if (currentHighlighted) {\n currentHighlighted.style.cursor = \"\";\n currentHighlighted = null;\n }\n if (scrollRAF !== null) {\n cancelAnimationFrame(scrollRAF);\n scrollRAF = null;\n }\n hideOverlay();\n // Remove overlay + tooltip from DOM entirely when edit mode is off\n overlayEl?.remove();\n overlayEl = null;\n tooltipEl?.remove();\n tooltipEl = null;\n cleanupFns.forEach((fn) => fn());\n cleanupFns = [];\n onSelect = null;\n}\n","// =============================================================================\n// Cancia Toolbar — Internal Events\n// =============================================================================\n// Simple pub/sub so toolbar.ts can react to changes from popup.ts without\n// circular imports.\n// =============================================================================\n\ntype Listener = () => void;\nconst listeners = new Set<Listener>();\n\nexport function onPendingChange(cb?: Listener) {\n if (cb) {\n listeners.add(cb);\n return () => listeners.delete(cb);\n }\n // Called with no args to emit\n listeners.forEach((fn) => fn());\n}\n","// =============================================================================\n// Cancia Toolbar — Edit Popup\n// =============================================================================\n// Anchors near the clicked element. Handles text editing and image upload.\n// =============================================================================\n\nimport { state, getValue, setPending, applyOverlay } from \"./state\";\nimport { uploadImage } from \"./api\";\nimport { onPendingChange } from \"./events\";\n\nlet popupEl: HTMLElement | null = null;\nlet outsideListener: ((e: MouseEvent) => void) | null = null;\nlet keyListener: ((e: KeyboardEvent) => void) | null = null;\nlet dragover = false;\n\n// WeakMap replaces the unsafe _canciaInput monkey-patch on textarea elements\nconst inputHandlers = new WeakMap<HTMLTextAreaElement, () => void>();\n\nfunction accent() {\n return state.config?.accentColor ?? \"#6366f1\";\n}\n\n// ---------------------------------------------------------------------------\n// Position helpers\n// ---------------------------------------------------------------------------\n\nfunction getPopupPosition(anchor: HTMLElement): { top: number; left: number; origin: string } {\n const rect = anchor.getBoundingClientRect();\n const scrollY = window.scrollY;\n const scrollX = window.scrollX;\n const popupW = 320;\n const popupH = 260;\n const margin = 10;\n\n let left = rect.left + scrollX;\n let top = rect.bottom + scrollY + margin;\n let origin = \"top left\";\n\n // Flip up if not enough space below\n if (rect.bottom + popupH + margin > window.innerHeight) {\n top = rect.top + scrollY - popupH - margin;\n origin = \"bottom left\";\n }\n\n // Keep within viewport horizontally\n if (left + popupW > window.innerWidth + scrollX) {\n left = window.innerWidth + scrollX - popupW - margin;\n origin = origin.replace(\"left\", \"right\");\n }\n if (left < scrollX + margin) left = scrollX + margin;\n\n return { top, left, origin };\n}\n\n// ---------------------------------------------------------------------------\n// Build popup DOM\n// ---------------------------------------------------------------------------\n\nfunction buildHeader(key: string, onClose: () => void): HTMLElement {\n const header = document.createElement(\"div\");\n header.style.cssText = `\n display: flex; align-items: center; justify-content: space-between;\n margin-bottom: 14px;\n `;\n\n const titleWrap = document.createElement(\"div\");\n titleWrap.style.cssText = `display: flex; flex-direction: column; gap: 1px; min-width: 0;`;\n\n const titleEl = document.createElement(\"span\");\n const keyParts = key.split(\".\");\n titleEl.textContent = keyParts[keyParts.length - 1].replace(/[-_]/g, \" \").replace(/\\b\\w/g, c => c.toUpperCase());\n titleEl.style.cssText = `\n font-size: 13px; font-weight: 600;\n color: rgba(255,255,255,0.9);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n `;\n\n const keyEl = document.createElement(\"span\");\n keyEl.textContent = key;\n keyEl.style.cssText = `\n font-size: 10px; font-family: \"SF Mono\", \"Fira Code\", ui-monospace, monospace;\n color: rgba(255,255,255,0.22); letter-spacing: 0.03em;\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n `;\n\n titleWrap.appendChild(titleEl);\n titleWrap.appendChild(keyEl);\n header.appendChild(titleWrap);\n header.appendChild(makeCloseButton(onClose));\n return header;\n}\n\nfunction buildTextPopup(\n key: string,\n anchorEl: HTMLElement,\n onClose: () => void\n): HTMLElement {\n const langs = state.config?.languages ?? [\"en\"];\n let activeLang = state.activeLang || langs[0];\n\n const wrap = document.createElement(\"div\");\n wrap.dataset.canciaPopup = \"1\";\n applyPopupStyles(wrap);\n wrap.appendChild(buildHeader(key, onClose));\n\n // Language tabs — minimal underline style, only if multiple langs\n if (langs.length > 1) {\n const tabs = document.createElement(\"div\");\n tabs.style.cssText = `display: flex; gap: 0; margin-bottom: 12px; border-bottom: 1px solid rgba(255,255,255,0.06);`;\n\n const renderTabs = () => {\n tabs.innerHTML = \"\";\n langs.forEach((lang) => {\n const tab = document.createElement(\"button\");\n tab.textContent = lang.toUpperCase();\n const isActive = lang === activeLang;\n tab.style.cssText = `\n padding: 5px 10px 6px; border: none; border-bottom: 2px solid;\n margin-bottom: -1px;\n font-size: 11px; font-weight: 600; cursor: pointer; letter-spacing: 0.06em;\n background: transparent;\n border-bottom-color: ${isActive ? accent() : \"transparent\"};\n color: ${isActive ? \"#fff\" : \"rgba(255,255,255,0.3)\"};\n transition: color 0.15s, border-color 0.15s;\n `;\n tab.addEventListener(\"mouseenter\", () => { if (!isActive) tab.style.color = \"rgba(255,255,255,0.6)\"; });\n tab.addEventListener(\"mouseleave\", () => { if (!isActive) tab.style.color = \"rgba(255,255,255,0.3)\"; });\n tab.addEventListener(\"click\", () => {\n const current = wrap.querySelector(\"textarea\") as HTMLTextAreaElement | null;\n if (current) {\n const existing = getValue(key, activeLang);\n const fallback = anchorEl.textContent?.trim() || \"\";\n if (current.value !== (existing || fallback)) {\n setPending(key, activeLang, current.value);\n }\n }\n activeLang = lang;\n state.activeLang = lang;\n // Active language changed globally — re-overlay the page's drafts so\n // other [data-cms] elements match the newly-selected language.\n applyOverlay();\n renderTabs();\n renderTextarea();\n });\n tabs.appendChild(tab);\n });\n };\n renderTabs();\n wrap.appendChild(tabs);\n }\n\n // Textarea\n let textarea: HTMLTextAreaElement;\n\n const attachInputHandler = () => {\n const prev = inputHandlers.get(textarea);\n if (prev) textarea.removeEventListener(\"input\", prev);\n const handler = () => {\n setPending(key, activeLang, textarea.value);\n onPendingChange();\n anchorEl.textContent = textarea.value;\n };\n inputHandlers.set(textarea, handler);\n textarea.addEventListener(\"input\", handler);\n };\n\n const renderTextarea = (isInit = false) => {\n if (!isInit && textarea) {\n // Swap value in-place — no DOM removal so no focus event, no blink\n textarea.value = getValue(key, activeLang) || anchorEl.textContent?.trim() || \"\";\n // Re-apply focused border since element stays focused\n textarea.style.borderColor = `${accent()}66`;\n textarea.style.background = \"rgba(255,255,255,0.05)\";\n attachInputHandler();\n return;\n }\n\n const footerEl = wrap.querySelector(\"[data-cancia-footer]\");\n\n textarea = document.createElement(\"textarea\");\n textarea.value = getValue(key, activeLang) || anchorEl.textContent?.trim() || \"\";\n textarea.rows = 4;\n textarea.placeholder = \"Enter text…\";\n textarea.style.cssText = `\n width: 100%; box-sizing: border-box;\n background: rgba(255,255,255,0.03); color: rgba(255,255,255,0.9);\n border: 1px solid rgba(255,255,255,0.07); border-radius: 10px;\n padding: 10px 12px;\n font-size: 13px; font-family: inherit; resize: none; outline: none;\n transition: border-color 0.18s, background 0.18s;\n line-height: 1.55;\n caret-color: ${accent()};\n `;\n textarea.addEventListener(\"focus\", () => {\n textarea.style.borderColor = `${accent()}66`;\n textarea.style.background = \"rgba(255,255,255,0.05)\";\n });\n textarea.addEventListener(\"blur\", () => {\n textarea.style.borderColor = \"rgba(255,255,255,0.07)\";\n textarea.style.background = \"rgba(255,255,255,0.03)\";\n });\n attachInputHandler();\n\n if (footerEl) {\n wrap.insertBefore(textarea, footerEl);\n } else {\n wrap.appendChild(textarea);\n }\n setTimeout(() => textarea.focus(), 80);\n };\n\n renderTextarea(true);\n\n // Footer\n const footer = document.createElement(\"div\");\n footer.dataset.canciaFooter = \"1\";\n footer.style.cssText = `display: flex; justify-content: flex-end; margin-top: 10px;`;\n\n const saveBtn = makePrimaryButton(\"Save\", accent());\n saveBtn.dataset.canciaSave = \"1\";\n saveBtn.title = \"Save (⌘S)\";\n saveBtn.addEventListener(\"click\", () => {\n const existing = getValue(key, activeLang);\n const fallback = anchorEl.textContent?.trim() || \"\";\n if (textarea.value !== (existing || fallback)) {\n setPending(key, activeLang, textarea.value);\n }\n onPendingChange();\n onClose();\n });\n\n footer.appendChild(saveBtn);\n wrap.appendChild(footer);\n\n return wrap;\n}\n\nfunction buildImagePopup(\n key: string,\n anchorEl: HTMLElement,\n onClose: () => void\n): HTMLElement {\n const wrap = document.createElement(\"div\");\n wrap.dataset.canciaPopup = \"1\";\n applyPopupStyles(wrap);\n wrap.appendChild(buildHeader(key, onClose));\n\n // Current image preview\n const currentSrc = anchorEl.tagName === \"IMG\"\n ? (anchorEl as HTMLImageElement).src\n : anchorEl.querySelector(\"img\")?.src ?? \"\";\n\n if (currentSrc && !currentSrc.startsWith(\"data:\")) {\n const previewWrap = document.createElement(\"div\");\n previewWrap.style.cssText = `\n border-radius: 10px; overflow: hidden; margin-bottom: 10px;\n border: 1px solid rgba(255,255,255,0.06);\n position: relative; height: 100px;\n `;\n const previewImg = document.createElement(\"img\");\n previewImg.src = currentSrc;\n previewImg.style.cssText = `width: 100%; height: 100%; object-fit: cover; display: block;`;\n const previewLabel = document.createElement(\"div\");\n previewLabel.textContent = \"Current\";\n previewLabel.style.cssText = `\n position: absolute; bottom: 0; left: 0; right: 0;\n font-size: 10px; color: rgba(255,255,255,0.45); letter-spacing: 0.04em;\n padding: 16px 8px 6px;\n background: linear-gradient(transparent, rgba(0,0,0,0.55));\n `;\n previewWrap.appendChild(previewImg);\n previewWrap.appendChild(previewLabel);\n wrap.appendChild(previewWrap);\n }\n\n // Drop zone — clean, minimal\n const dropZone = document.createElement(\"label\");\n dropZone.style.cssText = `\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n gap: 8px;\n border: 1.5px dashed rgba(255,255,255,0.1); border-radius: 10px;\n padding: 24px 20px;\n cursor: pointer;\n transition: border-color 0.18s, background 0.18s;\n background: rgba(255,255,255,0.015);\n `;\n\n // Simple arrow-up icon, no container box\n const uploadIcon = document.createElement(\"div\");\n uploadIcon.style.cssText = `color: rgba(255,255,255,0.35); transition: color 0.18s;`;\n uploadIcon.innerHTML = `<svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 15V3m0 0L8 7m4-4l4 4M2 17l.621 2.485A2 2 0 004.561 21h14.878a2 2 0 001.94-1.515L22 17\"/>\n </svg>`;\n\n const dropText = document.createElement(\"div\");\n dropText.style.cssText = `text-align: center;`;\n dropText.innerHTML = `\n <div style=\"font-size:12px;font-weight:500;color:rgba(255,255,255,0.5);\">Drop an image</div>\n <div style=\"font-size:11px;color:rgba(255,255,255,0.25);margin-top:2px;\">or click to browse</div>\n `;\n\n dropZone.appendChild(uploadIcon);\n dropZone.appendChild(dropText);\n\n const fileInput = document.createElement(\"input\");\n fileInput.type = \"file\";\n fileInput.accept = \"image/*\";\n fileInput.style.display = \"none\";\n dropZone.appendChild(fileInput);\n\n // Status area\n const statusWrap = document.createElement(\"div\");\n statusWrap.style.cssText = `margin-top: 8px; min-height: 18px;`;\n\n const statusMsg = document.createElement(\"p\");\n statusMsg.style.cssText = `font-size: 11px; color: rgba(255,255,255,0.35); margin: 0; text-align: center; transition: color 0.2s;`;\n\n const progressBar = document.createElement(\"div\");\n progressBar.style.cssText = `\n height: 2px; border-radius: 2px; background: rgba(255,255,255,0.05);\n overflow: hidden; margin-top: 6px; display: none;\n `;\n const progressFill = document.createElement(\"div\");\n progressFill.style.cssText = `\n height: 100%; border-radius: 2px; background: ${accent()};\n width: 0%; transition: width 0.4s cubic-bezier(0.16, 1, 0.3, 1);\n `;\n progressBar.appendChild(progressFill);\n statusWrap.appendChild(statusMsg);\n statusWrap.appendChild(progressBar);\n\n const handleFile = async (file: File) => {\n if (!file.type.startsWith(\"image/\")) {\n statusMsg.textContent = \"Only image files are supported\";\n statusMsg.style.color = \"#f87171\";\n return;\n }\n const maxMb = 10;\n if (file.size > maxMb * 1024 * 1024) {\n statusMsg.textContent = `File too large (max ${maxMb}MB)`;\n statusMsg.style.color = \"#f87171\";\n return;\n }\n dropZone.style.borderColor = `${accent()}55`;\n dropZone.style.background = `${accent()}0a`;\n uploadIcon.style.color = accent();\n statusMsg.textContent = \"Uploading…\";\n statusMsg.style.color = \"rgba(255,255,255,0.45)\";\n progressBar.style.display = \"block\";\n progressFill.style.width = \"0%\";\n\n try {\n const url = await uploadImage(file, (percent) => {\n progressFill.style.width = `${percent}%`;\n });\n progressFill.style.width = \"100%\";\n setPending(key, state.activeLang, url);\n onPendingChange();\n\n if (anchorEl.tagName === \"IMG\") {\n const img = anchorEl as HTMLImageElement;\n img.srcset = \"\";\n img.src = url;\n } else {\n const img = document.createElement(\"img\");\n img.src = url;\n img.alt = \"\";\n img.style.cssText = \"width:100%;height:100%;object-fit:cover;display:block;\";\n img.dataset.cms = key;\n anchorEl.replaceWith(img);\n }\n\n setTimeout(() => {\n statusMsg.textContent = \"Done\";\n statusMsg.style.color = \"#4ade80\";\n setTimeout(onClose, 600);\n }, 200);\n } catch {\n progressBar.style.display = \"none\";\n statusMsg.textContent = \"Upload failed — try again\";\n statusMsg.style.color = \"#f87171\";\n dropZone.style.borderColor = \"rgba(255,255,255,0.1)\";\n dropZone.style.background = \"rgba(255,255,255,0.015)\";\n uploadIcon.style.color = \"rgba(255,255,255,0.35)\";\n }\n };\n\n fileInput.addEventListener(\"change\", () => {\n if (fileInput.files?.[0]) handleFile(fileInput.files[0]);\n });\n\n dropZone.addEventListener(\"dragover\", (e) => {\n e.preventDefault();\n if (!dragover) {\n dragover = true;\n dropZone.style.borderColor = `${accent()}88`;\n dropZone.style.background = `${accent()}0d`;\n uploadIcon.style.color = accent();\n }\n });\n dropZone.addEventListener(\"dragleave\", () => {\n dragover = false;\n dropZone.style.borderColor = \"rgba(255,255,255,0.1)\";\n dropZone.style.background = \"rgba(255,255,255,0.015)\";\n uploadIcon.style.color = \"rgba(255,255,255,0.35)\";\n });\n dropZone.addEventListener(\"drop\", (e) => {\n e.preventDefault();\n dragover = false;\n dropZone.style.borderColor = \"rgba(255,255,255,0.1)\";\n dropZone.style.background = \"rgba(255,255,255,0.015)\";\n const file = e.dataTransfer?.files[0];\n if (file) handleFile(file);\n });\n\n dropZone.addEventListener(\"mouseenter\", () => {\n if (!dragover) {\n dropZone.style.borderColor = \"rgba(255,255,255,0.18)\";\n dropZone.style.background = \"rgba(255,255,255,0.03)\";\n }\n });\n dropZone.addEventListener(\"mouseleave\", () => {\n if (!dragover) {\n dropZone.style.borderColor = \"rgba(255,255,255,0.1)\";\n dropZone.style.background = \"rgba(255,255,255,0.015)\";\n }\n });\n\n wrap.appendChild(dropZone);\n wrap.appendChild(statusWrap);\n\n return wrap;\n}\n\n// ---------------------------------------------------------------------------\n// Shared UI helpers\n// ---------------------------------------------------------------------------\n\nexport function makeCloseButton(onClose: () => void): HTMLButtonElement {\n const btn = document.createElement(\"button\");\n btn.style.cssText = `\n display: flex; align-items: center; justify-content: center;\n width: 24px; height: 24px; border-radius: 6px; flex-shrink: 0;\n background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.06);\n cursor: pointer; color: rgba(255,255,255,0.35); padding: 0;\n transition: background 0.15s, color 0.15s;\n `;\n btn.innerHTML = `<svg width=\"9\" height=\"9\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M1 1l8 8M9 1L1 9\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>`;\n btn.addEventListener(\"mouseenter\", () => {\n btn.style.background = \"rgba(255,255,255,0.08)\";\n btn.style.color = \"rgba(255,255,255,0.75)\";\n });\n btn.addEventListener(\"mouseleave\", () => {\n btn.style.background = \"rgba(255,255,255,0.04)\";\n btn.style.color = \"rgba(255,255,255,0.35)\";\n });\n btn.addEventListener(\"click\", onClose);\n return btn;\n}\n\nexport function makePrimaryButton(label: string, color: string): HTMLButtonElement {\n const btn = document.createElement(\"button\");\n btn.textContent = label;\n btn.style.cssText = `\n padding: 7px 16px; border-radius: 8px; border: none; cursor: pointer;\n background: #fff; color: #0c0c0e;\n font-size: 12px; font-weight: 600; letter-spacing: 0.01em;\n transition: opacity 0.15s, transform 0.1s cubic-bezier(0.2, 0, 0, 1);\n `;\n btn.addEventListener(\"mouseenter\", () => (btn.style.opacity = \"0.88\"));\n btn.addEventListener(\"mouseleave\", () => (btn.style.opacity = \"1\"));\n return btn;\n}\n\n// ---------------------------------------------------------------------------\n// Styles\n// ---------------------------------------------------------------------------\n\nfunction applyPopupStyles(el: HTMLElement) {\n el.style.cssText = `\n position: absolute;\n z-index: 2147483646;\n width: 320px;\n background: rgba(14, 14, 16, 0.97);\n backdrop-filter: blur(24px) saturate(180%);\n -webkit-backdrop-filter: blur(24px) saturate(180%);\n border: 1px solid rgba(255,255,255,0.07);\n border-radius: 14px;\n padding: 14px;\n box-shadow: 0 0 0 1px rgba(0,0,0,0.4), 0 8px 24px rgba(0,0,0,0.5), 0 24px 64px rgba(0,0,0,0.4);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n animation: cancia-popup-in 0.25s cubic-bezier(0.16, 1, 0.3, 1) both;\n `;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport function openPopup(\n key: string,\n fieldType: \"text\" | \"image\",\n anchorEl: HTMLElement,\n onClose: () => void\n) {\n closePopup();\n\n const popup =\n fieldType === \"image\"\n ? buildImagePopup(key, anchorEl, () => { onClose(); closePopup(); })\n : buildTextPopup(key, anchorEl, () => { onClose(); closePopup(); });\n\n document.body.appendChild(popup);\n popupEl = popup;\n\n const { top, left, origin } = getPopupPosition(anchorEl);\n popup.style.top = `${top}px`;\n popup.style.left = `${left}px`;\n popup.style.transformOrigin = origin;\n\n // Click outside to close\n outsideListener = (e: MouseEvent) => {\n if (!popup.contains(e.target as Node)) {\n closePopup();\n onClose();\n }\n };\n setTimeout(() => {\n if (outsideListener) document.addEventListener(\"click\", outsideListener, true);\n }, 100);\n\n // Keyboard shortcuts\n keyListener = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n closePopup();\n onClose();\n }\n if ((e.metaKey || e.ctrlKey) && e.key === \"s\") {\n e.preventDefault();\n popup.querySelector<HTMLButtonElement>(\"[data-cancia-save]\")?.click();\n }\n };\n document.addEventListener(\"keydown\", keyListener, true);\n}\n\nexport function closePopup() {\n if (outsideListener) {\n document.removeEventListener(\"click\", outsideListener, true);\n outsideListener = null;\n }\n if (keyListener) {\n document.removeEventListener(\"keydown\", keyListener, true);\n keyListener = null;\n }\n if (popupEl) {\n const el = popupEl;\n popupEl = null;\n el.style.animation = \"none\";\n el.style.transition = \"opacity 0.18s cubic-bezier(0.4, 0, 1, 1), transform 0.18s cubic-bezier(0.4, 0, 1, 1)\";\n el.style.opacity = \"0\";\n el.style.transform = \"scale(0.96) translateY(3px)\";\n setTimeout(() => el.remove(), 200);\n }\n}\n","// =============================================================================\n// Cancia Toolbar — List Panel\n// =============================================================================\n// Right-side slide-in panel that opens when the user clicks a [data-cms-list]\n// element. Per-locale: tabs at the top switch which locale's entries are\n// rendered. Entries that exist in other locales but not the active one are\n// shown as a \"not translated\" stub so the user can fill them in.\n// =============================================================================\n\nimport {\n fetchList,\n fetchTranslations,\n type ListEntry,\n type ListSchemaDescription,\n type TranslationStatus,\n} from \"./api\";\nimport { state } from \"./state\";\n\nconst PANEL_Z = 2147483646;\nconst BACKDROP_Z = 2147483646;\n\nlet panelEl: HTMLElement | null = null;\nlet backdropEl: HTMLElement | null = null;\nlet styleInjected = false;\n// Captured per-open so refreshListPanel() can re-fetch and re-render in place.\nlet currentSchema: ListSchemaDescription | null = null;\nlet currentBody: HTMLElement | null = null;\nlet currentTabsRow: HTMLElement | null = null;\nlet currentOnEditEntry: ((entry: ListEntry, locale: string) => void) | null = null;\nlet currentOnAddEntry: ((locale: string) => void) | null = null;\nlet currentOnTranslateEntry:\n | ((id: string, sourceEntry: ListEntry | null, targetLocale: string) => void)\n | null = null;\n\nfunction injectStyles() {\n if (styleInjected) return;\n styleInjected = true;\n const s = document.createElement(\"style\");\n s.textContent = `\n @keyframes cancia-panel-in {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n @keyframes cancia-panel-out {\n from { transform: translateX(0); }\n to { transform: translateX(100%); }\n }\n @keyframes cancia-backdrop-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n `;\n document.head.appendChild(s);\n}\n\nfunction accent(): string {\n return state.config?.accentColor ?? \"#6366f1\";\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction formatExcerpt(value: unknown, maxLength = 120): string {\n if (typeof value !== \"string\") return \"\";\n const trimmed = value.trim();\n if (trimmed.length <= maxLength) return trimmed;\n return trimmed.slice(0, maxLength).trimEnd() + \"…\";\n}\n\nfunction locales(): string[] {\n return state.config?.languages ?? [];\n}\n\nfunction buildShell(schema: ListSchemaDescription): {\n panel: HTMLElement;\n body: HTMLElement;\n tabsRow: HTMLElement;\n} {\n const panel = document.createElement(\"div\");\n panel.dataset.canciaListPanel = \"1\";\n panel.style.cssText = `\n position: fixed;\n top: 0; right: 0; bottom: 0;\n width: min(420px, 100vw);\n background: #fff;\n color: #1a1a1d;\n z-index: ${PANEL_Z};\n box-shadow: -8px 0 32px rgba(0,0,0,0.18);\n display: flex;\n flex-direction: column;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n animation: cancia-panel-in 0.22s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n `;\n\n panel.innerHTML = `\n <header style=\"\n display: flex; align-items: center; justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid #eaeaea;\n \">\n <div>\n <div style=\"font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: #777;\">List</div>\n <div style=\"font-size: 17px; font-weight: 600; margin-top: 2px;\">${escapeHtml(schema.label)}</div>\n </div>\n <button data-cancia-close style=\"\n appearance: none; border: 0; background: transparent;\n cursor: pointer; padding: 6px; border-radius: 6px;\n color: #555; transition: background 0.12s, color 0.12s;\n \" aria-label=\"Close panel\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <path d=\"M4 4l10 10M14 4L4 14\"/>\n </svg>\n </button>\n </header>\n <div data-cancia-tabs style=\"\n display: flex; gap: 4px;\n padding: 8px 16px 0;\n border-bottom: 1px solid #f3f3f3;\n overflow-x: auto;\n \"></div>\n <div style=\"padding: 12px 20px; border-bottom: 1px solid #f3f3f3;\">\n <button data-cancia-add style=\"\n appearance: none; border: 1px dashed ${accent()}; background: ${accent()}10;\n color: ${accent()}; font-weight: 600; font-size: 13px;\n padding: 10px 14px; border-radius: 8px; width: 100%; cursor: pointer;\n display: flex; align-items: center; justify-content: center; gap: 6px;\n transition: background 0.12s;\n \">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <path d=\"M7 2v10M2 7h10\"/>\n </svg>\n Add ${escapeHtml(schema.labelSingular.toLowerCase())}\n </button>\n </div>\n <div data-cancia-entries style=\"\n flex: 1; overflow-y: auto;\n padding: 8px 12px 16px;\n \">\n <div data-cancia-loading style=\"text-align: center; padding: 32px 12px; color: #888; font-size: 13px;\">Loading…</div>\n </div>\n `;\n\n const body = panel.querySelector(\"[data-cancia-entries]\") as HTMLElement;\n const tabsRow = panel.querySelector(\"[data-cancia-tabs]\") as HTMLElement;\n return { panel, body, tabsRow };\n}\n\nfunction renderTabs(tabsRow: HTMLElement, activeLocale: string, onSwitch: (loc: string) => void) {\n tabsRow.innerHTML = \"\";\n const langs = locales();\n if (langs.length <= 1) {\n tabsRow.style.display = \"none\";\n return;\n }\n tabsRow.style.display = \"flex\";\n\n for (const loc of langs) {\n const tab = document.createElement(\"button\");\n const isActive = loc === activeLocale;\n tab.style.cssText = `\n appearance: none; border: 0; background: transparent;\n font-family: inherit; font-size: 12px; font-weight: 600;\n letter-spacing: 0.04em; text-transform: uppercase;\n padding: 8px 10px 9px;\n cursor: ${isActive ? \"default\" : \"pointer\"};\n color: ${isActive ? accent() : \"#777\"};\n border-bottom: 2px solid ${isActive ? accent() : \"transparent\"};\n margin-bottom: -1px;\n transition: color 0.12s, border-color 0.12s;\n `;\n tab.textContent = loc;\n if (!isActive) {\n tab.addEventListener(\"mouseenter\", () => { tab.style.color = \"#333\"; });\n tab.addEventListener(\"mouseleave\", () => { tab.style.color = \"#777\"; });\n tab.addEventListener(\"click\", () => onSwitch(loc));\n }\n tabsRow.appendChild(tab);\n }\n}\n\ninterface RenderRow {\n id: string;\n locale: string;\n entry: ListEntry | null;\n translatedFrom: string | null;\n}\n\nfunction renderEntries(\n body: HTMLElement,\n schema: ListSchemaDescription,\n activeLocale: string,\n entriesInLocale: ListEntry[],\n translations: TranslationStatus[],\n allEntries: Map<string, ListEntry[]>,\n) {\n const entryById = new Map<string, ListEntry>();\n for (const e of entriesInLocale) entryById.set(e.id, e);\n\n const orderedIds: string[] = entriesInLocale.map((e) => e.id);\n const seen = new Set(orderedIds);\n for (const t of translations) {\n if (!seen.has(t.id)) {\n orderedIds.push(t.id);\n seen.add(t.id);\n }\n }\n\n const rows: RenderRow[] = orderedIds.map((id) => {\n const entry = entryById.get(id) ?? null;\n let translatedFrom: string | null = null;\n if (!entry) {\n const t = translations.find((x) => x.id === id);\n if (t && t.locales.length > 0) translatedFrom = t.locales[0];\n }\n return { id, locale: activeLocale, entry, translatedFrom };\n });\n\n if (rows.length === 0) {\n body.innerHTML = `\n <div style=\"text-align: center; padding: 40px 12px; color: #888; font-size: 13px;\">\n No entries yet. Click \"Add ${escapeHtml(schema.labelSingular.toLowerCase())}\" above to create one.\n </div>\n `;\n return;\n }\n\n body.innerHTML = \"\";\n rows.forEach((row) => {\n const item = document.createElement(\"button\");\n const isStub = row.entry === null;\n item.style.cssText = `\n appearance: none; border: 1px solid transparent; background: ${isStub ? \"#fff8ee\" : \"#fafafa\"};\n text-align: left; width: 100%;\n padding: 12px 14px; border-radius: 8px; cursor: pointer;\n margin-bottom: 6px;\n transition: background 0.12s, border-color 0.12s, transform 0.12s;\n display: block;\n `;\n\n if (row.entry) {\n const titleValue = row.entry.data[schema.titleField];\n const title =\n typeof titleValue === \"string\" && titleValue.trim().length > 0\n ? titleValue\n : `(untitled ${schema.labelSingular.toLowerCase()})`;\n const excerptValue = schema.bodyField ? row.entry.data[schema.bodyField] : undefined;\n const excerpt = formatExcerpt(excerptValue);\n item.innerHTML = `\n <div style=\"font-weight: 600; font-size: 14px; color: #1a1a1d;\">${escapeHtml(title)}</div>\n ${excerpt ? `<div style=\"font-size: 12px; color: #666; margin-top: 4px; line-height: 1.4;\">${escapeHtml(excerpt)}</div>` : \"\"}\n `;\n item.addEventListener(\"mouseenter\", () => {\n item.style.background = \"#f3f3f3\";\n item.style.borderColor = \"#e3e3e3\";\n });\n item.addEventListener(\"mouseleave\", () => {\n item.style.background = \"#fafafa\";\n item.style.borderColor = \"transparent\";\n });\n item.addEventListener(\"click\", () => currentOnEditEntry?.(row.entry!, activeLocale));\n } else {\n const sourceLocale = row.translatedFrom!;\n const sourceEntry =\n allEntries.get(sourceLocale)?.find((e) => e.id === row.id) ?? null;\n const sourceTitleValue = sourceEntry?.data[schema.titleField];\n const sourceTitle =\n typeof sourceTitleValue === \"string\" && sourceTitleValue.trim().length > 0\n ? sourceTitleValue\n : row.id;\n item.innerHTML = `\n <div style=\"display: flex; align-items: center; gap: 8px;\">\n <span style=\"\n font-size: 9px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;\n background: #f5b400; color: #fff;\n padding: 2px 6px; border-radius: 4px;\n \">Not translated</span>\n <span style=\"font-size: 11px; color: #888;\">from ${escapeHtml(sourceLocale)}</span>\n </div>\n <div style=\"font-weight: 600; font-size: 14px; color: #6b5616; margin-top: 6px;\">${escapeHtml(sourceTitle)}</div>\n <div style=\"font-size: 11px; color: #b08800; margin-top: 4px;\">Click to translate into ${escapeHtml(activeLocale)}</div>\n `;\n item.addEventListener(\"mouseenter\", () => {\n item.style.background = \"#fff2d5\";\n item.style.borderColor = \"#f5d27a\";\n });\n item.addEventListener(\"mouseleave\", () => {\n item.style.background = \"#fff8ee\";\n item.style.borderColor = \"transparent\";\n });\n item.addEventListener(\"click\", () =>\n currentOnTranslateEntry?.(row.id, sourceEntry, activeLocale),\n );\n }\n body.appendChild(item);\n });\n}\n\nexport interface OpenListPanelOptions {\n schema: ListSchemaDescription;\n /** Locale to start on. Defaults to state.activeLang. */\n initialLocale?: string;\n onAddEntry: (locale: string) => void;\n onEditEntry: (entry: ListEntry, locale: string) => void;\n /** Called when user clicks a \"not translated\" row. */\n onTranslateEntry: (id: string, sourceEntry: ListEntry | null, targetLocale: string) => void;\n}\n\nexport async function openListPanel(opts: OpenListPanelOptions): Promise<void> {\n closeListPanel();\n injectStyles();\n\n const backdrop = document.createElement(\"div\");\n backdrop.dataset.canciaPanelBackdrop = \"1\";\n backdrop.style.cssText = `\n position: fixed; inset: 0;\n background: rgba(10,10,12,0.32);\n z-index: ${BACKDROP_Z};\n animation: cancia-backdrop-in 0.18s ease-out forwards;\n `;\n backdrop.addEventListener(\"click\", () => closeListPanel());\n document.body.appendChild(backdrop);\n backdropEl = backdrop;\n\n const { panel, body, tabsRow } = buildShell(opts.schema);\n document.body.appendChild(panel);\n panelEl = panel;\n currentSchema = opts.schema;\n currentBody = body;\n currentTabsRow = tabsRow;\n currentOnEditEntry = opts.onEditEntry;\n currentOnAddEntry = opts.onAddEntry;\n currentOnTranslateEntry = opts.onTranslateEntry;\n\n const langs = locales();\n const initial = opts.initialLocale ?? state.activeLang ?? langs[0] ?? \"\";\n state.activeListLocale = langs.includes(initial) ? initial : (langs[0] ?? initial);\n\n panel.querySelector<HTMLButtonElement>(\"[data-cancia-close]\")?.addEventListener(\n \"click\",\n () => closeListPanel(),\n );\n panel.querySelector<HTMLButtonElement>(\"[data-cancia-add]\")?.addEventListener(\n \"click\",\n () => currentOnAddEntry?.(state.activeListLocale),\n );\n\n await refreshListPanel();\n}\n\n/**\n * Re-fetch the current list and re-render the entries. No-op if the panel\n * isn't open. Called by the entry modal after a successful save/delete, and\n * by tab switches.\n */\nexport async function refreshListPanel(): Promise<void> {\n if (!panelEl || !currentSchema || !currentBody || !currentTabsRow) return;\n const schema = currentSchema;\n const body = currentBody;\n const tabsRow = currentTabsRow;\n\n renderTabs(tabsRow, state.activeListLocale, async (newLocale) => {\n state.activeListLocale = newLocale;\n await refreshListPanel();\n });\n\n try {\n const langs = locales();\n const [translations, ...perLocale] = await Promise.all([\n fetchTranslations(schema.name),\n ...langs.map((l) => fetchList(schema.name, l)),\n ]);\n if (!panelEl) return;\n const allEntries = new Map<string, ListEntry[]>();\n langs.forEach((l, i) => allEntries.set(l, perLocale[i] ?? []));\n const activeEntries = allEntries.get(state.activeListLocale) ?? [];\n renderEntries(body, schema, state.activeListLocale, activeEntries, translations, allEntries);\n } catch (err) {\n if (!panelEl) return;\n body.innerHTML = `\n <div style=\"text-align: center; padding: 32px 12px; color: #c0392b; font-size: 13px;\">\n Failed to load entries: ${escapeHtml(err instanceof Error ? err.message : String(err))}\n </div>\n `;\n }\n}\n\nexport function closeListPanel(): void {\n if (panelEl) {\n panelEl.style.animation = \"cancia-panel-out 0.18s cubic-bezier(0.7, 0, 0.84, 0) forwards\";\n const el = panelEl;\n setTimeout(() => el.remove(), 180);\n panelEl = null;\n }\n if (backdropEl) {\n const el = backdropEl;\n el.style.opacity = \"0\";\n el.style.transition = \"opacity 0.18s ease-out\";\n setTimeout(() => el.remove(), 180);\n backdropEl = null;\n }\n currentSchema = null;\n currentBody = null;\n currentTabsRow = null;\n currentOnEditEntry = null;\n currentOnAddEntry = null;\n currentOnTranslateEntry = null;\n}\n\nexport function isListPanelOpen(): boolean {\n return panelEl !== null;\n}\n","// =============================================================================\n// Cancia Toolbar — Entry Modal\n// =============================================================================\n// Same visual family as popup.ts (dark frosted shell, same close + primary\n// button helpers) but laid out as a centered, viewport-bounded modal so it\n// can host a longer form than the anchored popup.\n//\n// Layout:\n// ┌─ header (fixed) ─────────────────────────┐\n// │ eyebrow + title close │\n// ├─ body (scrolls) ─────────────────────────┤\n// │ form fields… │\n// ├─ footer (fixed) ─────────────────────────┤\n// │ [Delete] [Cancel] [Save] │\n// └───────────────────────────────────────────┘\n//\n// Only the body scrolls. Header + footer are always visible, so Save and\n// Delete are always reachable regardless of how many fields the schema has.\n// =============================================================================\n\nimport {\n createListEntry,\n updateListEntry,\n deleteListEntry,\n uploadImage,\n type ListEntry,\n type ListSchemaDescription,\n type ListSchemaField,\n} from \"./api\";\nimport { state } from \"./state\";\nimport { makeCloseButton, makePrimaryButton } from \"./popup\";\n\nconst MODAL_Z = 2147483647;\nconst BACKDROP_Z = 2147483646;\n\nlet modalEl: HTMLElement | null = null;\nlet backdropEl: HTMLElement | null = null;\nlet escListener: ((e: KeyboardEvent) => void) | null = null;\nlet styleInjected = false;\n\n// ---------------------------------------------------------------------------\n// Styles — shared keyframes + focus styles for inputs inside the modal\n// ---------------------------------------------------------------------------\n\nfunction injectStyles() {\n if (styleInjected) return;\n styleInjected = true;\n const s = document.createElement(\"style\");\n s.textContent = `\n @keyframes cancia-modal-in {\n from { opacity: 0; transform: translate(-50%, calc(-50% + 8px)) scale(0.985); }\n to { opacity: 1; transform: translate(-50%, -50%) scale(1); }\n }\n @keyframes cancia-modal-out {\n from { opacity: 1; transform: translate(-50%, -50%) scale(1); }\n to { opacity: 0; transform: translate(-50%, calc(-50% + 8px)) scale(0.985); }\n }\n .cancia-form-input:focus,\n .cancia-form-textarea:focus,\n .cancia-form-select:focus {\n border-color: var(--cancia-accent-border, rgba(99,102,241,0.6));\n background: rgba(255,255,255,0.05);\n outline: none;\n }\n .cancia-form-input::placeholder,\n .cancia-form-textarea::placeholder {\n color: rgba(255,255,255,0.25);\n }\n .cancia-form-select option {\n background: #15151a;\n color: rgba(255,255,255,0.9);\n }\n .cancia-field-error {\n color: #ff8786;\n font-size: 11px;\n margin-top: 5px;\n line-height: 1.35;\n }\n `;\n document.head.appendChild(s);\n}\n\nfunction accent(): string {\n return state.config?.accentColor ?? \"#6366f1\";\n}\n\nfunction accentBorder(): string {\n const a = accent();\n if (/^#[0-9a-f]{6}$/i.test(a)) return `${a}99`;\n return \"rgba(99,102,241,0.6)\";\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\n// ---------------------------------------------------------------------------\n// Datetime helpers — ISO ⇄ datetime-local\n// ---------------------------------------------------------------------------\n\nfunction isoToLocalInput(iso: string): string {\n if (!iso) return \"\";\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return \"\";\n const pad = (n: number) => n.toString().padStart(2, \"0\");\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;\n}\n\nfunction localInputToIso(local: string): string {\n if (!local) return \"\";\n const d = new Date(local);\n if (Number.isNaN(d.getTime())) return \"\";\n return d.toISOString();\n}\n\n// ---------------------------------------------------------------------------\n// Field rendering — all inputs share the same dark style\n// ---------------------------------------------------------------------------\n\nconst INPUT_BASE = `\n width: 100%; box-sizing: border-box;\n background: rgba(255,255,255,0.03);\n color: rgba(255,255,255,0.9);\n border: 1px solid rgba(255,255,255,0.07);\n border-radius: 8px;\n padding: 8px 11px;\n font-size: 13px;\n font-family: inherit;\n line-height: 1.5;\n outline: none;\n transition: border-color 0.18s, background 0.18s;\n`;\n\ninterface FieldState {\n field: ListSchemaField;\n getValue: () => unknown;\n setError: (msg: string | null) => void;\n}\n\nfunction renderField(\n field: ListSchemaField,\n initial: unknown,\n): { wrapper: HTMLElement; fieldState: FieldState } {\n const wrapper = document.createElement(\"div\");\n wrapper.style.cssText = `margin-bottom: 14px;`;\n\n const labelRow = document.createElement(\"label\");\n labelRow.style.cssText = `\n display: flex; align-items: baseline; justify-content: space-between;\n gap: 8px;\n font-size: 11px; font-weight: 600;\n color: rgba(255,255,255,0.75);\n letter-spacing: 0.04em;\n margin-bottom: 5px;\n `;\n\n const labelText = document.createElement(\"span\");\n labelText.textContent = field.label;\n if (field.required) {\n const star = document.createElement(\"span\");\n star.textContent = \" *\";\n star.style.color = \"rgba(255,135,134,0.8)\";\n labelText.appendChild(star);\n }\n labelRow.appendChild(labelText);\n wrapper.appendChild(labelRow);\n\n if (field.description) {\n const help = document.createElement(\"div\");\n help.style.cssText = `font-size: 11px; color: rgba(255,255,255,0.35); margin-bottom: 6px; line-height: 1.4;`;\n help.textContent = field.description;\n wrapper.appendChild(help);\n }\n\n const errorEl = document.createElement(\"div\");\n errorEl.className = \"cancia-field-error\";\n errorEl.style.display = \"none\";\n\n const setError = (msg: string | null) => {\n if (msg) {\n errorEl.textContent = msg;\n errorEl.style.display = \"block\";\n } else {\n errorEl.textContent = \"\";\n errorEl.style.display = \"none\";\n }\n };\n\n let getValue: () => unknown;\n\n switch (field.widget) {\n case \"textarea\": {\n const ta = document.createElement(\"textarea\");\n ta.className = \"cancia-form-textarea\";\n ta.style.cssText = `${INPUT_BASE} resize: vertical; min-height: 110px; max-height: 320px; caret-color: ${accent()};`;\n ta.rows = 5;\n if (field.placeholder) ta.placeholder = field.placeholder;\n if (typeof initial === \"string\") ta.value = initial;\n wrapper.appendChild(ta);\n getValue = () => ta.value;\n break;\n }\n\n case \"checkbox\": {\n const row = document.createElement(\"label\");\n row.style.cssText = `display: flex; align-items: center; gap: 9px; cursor: pointer; user-select: none; padding: 6px 0;`;\n const cb = document.createElement(\"input\");\n cb.type = \"checkbox\";\n cb.style.cssText = `width: 16px; height: 16px; accent-color: ${accent()};`;\n if (initial === true) cb.checked = true;\n const txt = document.createElement(\"span\");\n txt.style.cssText = `font-size: 13px; color: rgba(255,255,255,0.7);`;\n txt.textContent = field.placeholder ?? `Enable ${field.label.toLowerCase()}`;\n row.appendChild(cb);\n row.appendChild(txt);\n wrapper.appendChild(row);\n getValue = () => cb.checked;\n break;\n }\n\n case \"select\": {\n const sel = document.createElement(\"select\");\n sel.className = \"cancia-form-select\";\n sel.style.cssText = `${INPUT_BASE} appearance: none; padding-right: 30px; background-image: url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'><path d='M2.5 3.75l2.5 2.5 2.5-2.5' stroke='rgba(255,255,255,0.4)' stroke-width='1.3' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>\"); background-repeat: no-repeat; background-position: right 10px center; cursor: pointer;`;\n if (!field.required) {\n const empty = document.createElement(\"option\");\n empty.value = \"\";\n empty.textContent = \"—\";\n sel.appendChild(empty);\n }\n for (const opt of field.options ?? []) {\n const o = document.createElement(\"option\");\n o.value = opt;\n o.textContent = opt;\n if (initial === opt) o.selected = true;\n sel.appendChild(o);\n }\n wrapper.appendChild(sel);\n getValue = () => (sel.value === \"\" ? undefined : sel.value);\n break;\n }\n\n case \"image\": {\n const initialUrl = typeof initial === \"string\" ? initial : \"\";\n let currentUrl = initialUrl;\n\n const container = document.createElement(\"div\");\n container.style.cssText = `\n display: flex; gap: 10px; align-items: stretch;\n background: rgba(255,255,255,0.02);\n border: 1px dashed rgba(255,255,255,0.1);\n border-radius: 10px;\n padding: 10px;\n `;\n\n const preview = document.createElement(\"div\");\n preview.style.cssText = `\n width: 72px; height: 72px; flex-shrink: 0;\n background: rgba(255,255,255,0.04) no-repeat center / cover;\n border: 1px solid rgba(255,255,255,0.06);\n border-radius: 6px;\n display: flex; align-items: center; justify-content: center;\n color: rgba(255,255,255,0.25);\n `;\n const updatePreview = (url: string) => {\n if (url) {\n preview.style.backgroundImage = `url(\"${url.replace(/\"/g, '\\\\\"')}\")`;\n preview.innerHTML = \"\";\n } else {\n preview.style.backgroundImage = \"\";\n preview.innerHTML = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><path d=\"M21 15l-5-5L5 21\"/></svg>`;\n }\n };\n updatePreview(initialUrl);\n\n const right = document.createElement(\"div\");\n right.style.cssText = `flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px;`;\n\n const fileInput = document.createElement(\"input\");\n fileInput.type = \"file\";\n fileInput.accept = \"image/*\";\n fileInput.style.display = \"none\";\n\n const btnRow = document.createElement(\"div\");\n btnRow.style.cssText = `display: flex; gap: 6px;`;\n\n const uploadBtn = document.createElement(\"button\");\n uploadBtn.type = \"button\";\n uploadBtn.style.cssText = `\n appearance: none; cursor: pointer;\n background: rgba(255,255,255,0.06); color: rgba(255,255,255,0.85);\n border: 1px solid rgba(255,255,255,0.07);\n font-size: 11px; font-weight: 500; letter-spacing: 0.02em;\n padding: 5px 10px; border-radius: 6px;\n transition: background 0.15s;\n `;\n uploadBtn.textContent = \"Upload…\";\n uploadBtn.addEventListener(\"mouseenter\", () => { uploadBtn.style.background = \"rgba(255,255,255,0.1)\"; });\n uploadBtn.addEventListener(\"mouseleave\", () => { uploadBtn.style.background = \"rgba(255,255,255,0.06)\"; });\n uploadBtn.addEventListener(\"click\", () => fileInput.click());\n\n const clearBtn = document.createElement(\"button\");\n clearBtn.type = \"button\";\n clearBtn.style.cssText = `\n appearance: none; cursor: pointer;\n background: transparent; color: rgba(255,255,255,0.4);\n border: 1px solid transparent;\n font-size: 11px;\n padding: 5px 8px; border-radius: 6px;\n `;\n clearBtn.textContent = \"Clear\";\n clearBtn.addEventListener(\"click\", () => {\n currentUrl = \"\";\n urlField.value = \"\";\n updatePreview(\"\");\n });\n\n btnRow.appendChild(uploadBtn);\n btnRow.appendChild(clearBtn);\n\n const urlField = document.createElement(\"input\");\n urlField.type = \"url\";\n urlField.className = \"cancia-form-input\";\n urlField.style.cssText = `${INPUT_BASE} font-size: 11px; padding: 6px 9px;`;\n urlField.placeholder = \"https://… or upload\";\n urlField.value = initialUrl;\n urlField.addEventListener(\"input\", () => {\n currentUrl = urlField.value.trim();\n updatePreview(currentUrl);\n });\n\n const progressEl = document.createElement(\"div\");\n progressEl.style.cssText = `font-size: 10px; color: rgba(255,255,255,0.5); height: 12px;`;\n\n right.appendChild(btnRow);\n right.appendChild(urlField);\n right.appendChild(progressEl);\n\n container.appendChild(preview);\n container.appendChild(right);\n container.appendChild(fileInput);\n wrapper.appendChild(container);\n\n fileInput.addEventListener(\"change\", async () => {\n const file = fileInput.files?.[0];\n if (!file) return;\n uploadBtn.disabled = true;\n progressEl.style.color = \"rgba(255,255,255,0.5)\";\n try {\n const url = await uploadImage(file, (pct) => {\n progressEl.textContent = `Uploading… ${pct}%`;\n });\n currentUrl = url;\n urlField.value = url;\n updatePreview(url);\n progressEl.textContent = \"Uploaded\";\n setTimeout(() => { progressEl.textContent = \"\"; }, 1500);\n } catch (err) {\n progressEl.textContent = `Upload failed: ${err instanceof Error ? err.message : String(err)}`;\n progressEl.style.color = \"#ff8786\";\n } finally {\n uploadBtn.disabled = false;\n fileInput.value = \"\";\n }\n });\n\n getValue = () => (currentUrl === \"\" ? undefined : currentUrl);\n break;\n }\n\n case \"datetime\": {\n const input = document.createElement(\"input\");\n input.type = \"datetime-local\";\n input.className = \"cancia-form-input\";\n // datetime-local on dark backgrounds needs a colour-scheme nudge so the\n // calendar/clock icon renders white instead of black.\n input.style.cssText = `${INPUT_BASE} color-scheme: dark; caret-color: ${accent()};`;\n if (typeof initial === \"string\") input.value = isoToLocalInput(initial);\n wrapper.appendChild(input);\n getValue = () => {\n const v = input.value.trim();\n if (!v) return undefined;\n return localInputToIso(v);\n };\n break;\n }\n\n case \"number\": {\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.className = \"cancia-form-input\";\n input.style.cssText = `${INPUT_BASE} caret-color: ${accent()};`;\n if (field.min !== undefined) input.min = String(field.min);\n if (field.max !== undefined) input.max = String(field.max);\n if (typeof initial === \"number\") input.value = String(initial);\n else if (typeof initial === \"string\" && initial !== \"\") input.value = initial;\n wrapper.appendChild(input);\n getValue = () => {\n const v = input.value.trim();\n if (v === \"\") return undefined;\n const n = Number(v);\n return Number.isNaN(n) ? undefined : n;\n };\n break;\n }\n\n default: {\n // text | url | email\n const input = document.createElement(\"input\");\n input.type = field.widget === \"url\" ? \"url\" : field.widget === \"email\" ? \"email\" : \"text\";\n input.className = \"cancia-form-input\";\n input.style.cssText = `${INPUT_BASE} caret-color: ${accent()};`;\n if (field.placeholder) input.placeholder = field.placeholder;\n if (field.minLength !== undefined) input.minLength = field.minLength;\n if (field.maxLength !== undefined) input.maxLength = field.maxLength;\n if (typeof initial === \"string\") input.value = initial;\n wrapper.appendChild(input);\n getValue = () => input.value;\n break;\n }\n }\n\n wrapper.appendChild(errorEl);\n return { wrapper, fieldState: { field, getValue, setError } };\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\ninterface ValidationResult {\n data: Record<string, unknown>;\n ok: boolean;\n}\n\nfunction preValidate(fieldStates: FieldState[]): ValidationResult {\n const data: Record<string, unknown> = {};\n let ok = true;\n\n for (const f of fieldStates) {\n const value = f.getValue();\n f.setError(null);\n\n if (f.field.required && (value === undefined || value === \"\" || value === null)) {\n f.setError(`${f.field.label} is required`);\n ok = false;\n continue;\n }\n\n if (typeof value === \"string\") {\n if (f.field.minLength !== undefined && value.length < f.field.minLength) {\n f.setError(`Must be at least ${f.field.minLength} character${f.field.minLength === 1 ? \"\" : \"s\"}`);\n ok = false;\n continue;\n }\n if (f.field.maxLength !== undefined && value.length > f.field.maxLength) {\n f.setError(`Must be at most ${f.field.maxLength} characters`);\n ok = false;\n continue;\n }\n }\n\n if (value !== undefined && value !== \"\") {\n data[f.field.name] = value;\n }\n }\n\n return { data, ok };\n}\n\n// ---------------------------------------------------------------------------\n// Modal lifecycle\n// ---------------------------------------------------------------------------\n\nexport interface EntryModalOptions {\n schema: ListSchemaDescription;\n /** Locale being edited / created in. */\n locale: string;\n /** null = create; ListEntry = edit. */\n entry: ListEntry | null;\n /**\n * Translation mode: fill in the form pre-populated from `entry` (which is in\n * a different locale than `locale`) and save into `locale` with a fixed id.\n */\n translateFromEntry?: ListEntry | null;\n /** Fixed id when translating (so order matches across locales). */\n fixedId?: string;\n /** Called after successful save or delete. */\n onSaved: () => void;\n}\n\nexport function openEntryModal(opts: EntryModalOptions): void {\n closeEntryModal();\n injectStyles();\n\n const isEdit = opts.entry !== null;\n const isTranslate = !isEdit && (opts.translateFromEntry ?? null) !== null;\n\n // CSS var for input focus border so the focus rule above can pick it up.\n document.documentElement.style.setProperty(\"--cancia-accent-border\", accentBorder());\n\n // ---- Backdrop ----\n const backdrop = document.createElement(\"div\");\n backdrop.dataset.canciaModalBackdrop = \"1\";\n backdrop.style.cssText = `\n position: fixed; inset: 0;\n background: rgba(8,8,10,0.55);\n backdrop-filter: blur(3px);\n -webkit-backdrop-filter: blur(3px);\n z-index: ${BACKDROP_Z};\n opacity: 0;\n transition: opacity 0.2s ease-out;\n `;\n document.body.appendChild(backdrop);\n requestAnimationFrame(() => { backdrop.style.opacity = \"1\"; });\n backdropEl = backdrop;\n\n // ---- Modal shell — same family as popup.ts (dark, frosted, rounded) ----\n // Sized to fit within the viewport: max-height is clamped, and the body\n // is the only scrolling region so header + footer stay reachable.\n const modal = document.createElement(\"div\");\n modal.dataset.canciaModal = \"1\";\n modal.style.cssText = `\n position: fixed;\n top: 50%; left: 50%;\n transform: translate(-50%, -50%);\n width: min(480px, calc(100vw - 32px));\n max-height: min(680px, calc(100vh - 48px));\n display: flex; flex-direction: column;\n background: rgba(14, 14, 16, 0.97);\n backdrop-filter: blur(24px) saturate(180%);\n -webkit-backdrop-filter: blur(24px) saturate(180%);\n border: 1px solid rgba(255,255,255,0.07);\n border-radius: 14px;\n box-shadow: 0 0 0 1px rgba(0,0,0,0.4), 0 8px 24px rgba(0,0,0,0.5), 0 24px 64px rgba(0,0,0,0.4);\n z-index: ${MODAL_Z};\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n color: rgba(255,255,255,0.9);\n animation: cancia-modal-in 0.22s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n overflow: hidden;\n `;\n document.body.appendChild(modal);\n modalEl = modal;\n\n // ---- Header (fixed) ----\n const header = document.createElement(\"div\");\n header.style.cssText = `\n display: flex; align-items: center; justify-content: space-between;\n padding: 14px 16px 12px;\n border-bottom: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n `;\n\n const titleWrap = document.createElement(\"div\");\n titleWrap.style.cssText = `display: flex; flex-direction: column; gap: 2px; min-width: 0;`;\n\n const eyebrow = document.createElement(\"span\");\n eyebrow.style.cssText = `\n font-size: 10px; font-weight: 600;\n color: rgba(255,255,255,0.32);\n letter-spacing: 0.08em; text-transform: uppercase;\n `;\n const action = isEdit ? \"Edit\" : isTranslate ? \"Translate\" : \"New\";\n eyebrow.textContent = `${action} ${opts.schema.labelSingular.toLowerCase()} · ${opts.locale}`;\n\n const titleEl = document.createElement(\"span\");\n titleEl.style.cssText = `\n font-size: 14px; font-weight: 600;\n color: rgba(255,255,255,0.92);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n `;\n titleEl.textContent = opts.schema.label;\n\n titleWrap.appendChild(eyebrow);\n titleWrap.appendChild(titleEl);\n\n header.appendChild(titleWrap);\n header.appendChild(makeCloseButton(() => closeEntryModal()));\n modal.appendChild(header);\n\n // ---- Body (scrolls) ----\n const body = document.createElement(\"div\");\n body.style.cssText = `\n padding: 14px 16px 4px;\n overflow-y: auto;\n flex: 1 1 auto;\n min-height: 0;\n `;\n modal.appendChild(body);\n\n // Form-level error banner (REV_CONFLICT, network errors)\n const formError = document.createElement(\"div\");\n formError.style.cssText = `\n display: none;\n background: rgba(255,135,134,0.08);\n color: #ff8786;\n border: 1px solid rgba(255,135,134,0.18);\n border-radius: 8px;\n padding: 9px 11px;\n font-size: 12px;\n line-height: 1.4;\n margin-bottom: 12px;\n `;\n body.appendChild(formError);\n\n function showFormError(msg: string): void {\n formError.textContent = msg;\n formError.style.display = \"block\";\n }\n function clearFormError(): void {\n formError.textContent = \"\";\n formError.style.display = \"none\";\n }\n\n if (isTranslate) {\n const banner = document.createElement(\"div\");\n banner.style.cssText = `\n background: rgba(245,180,0,0.1);\n color: #f5b400;\n border: 1px solid rgba(245,180,0,0.25);\n border-radius: 8px;\n padding: 9px 11px;\n font-size: 12px;\n line-height: 1.4;\n margin-bottom: 12px;\n `;\n const src = opts.translateFromEntry!;\n banner.textContent = `Translating from ${src.locale} into ${opts.locale}. Fields are pre-filled from the source.`;\n body.appendChild(banner);\n }\n\n const fieldStates: FieldState[] = [];\n const sourceForInitial = opts.entry ?? opts.translateFromEntry ?? null;\n for (const f of opts.schema.fields) {\n const initial = sourceForInitial?.data[f.name];\n const { wrapper, fieldState } = renderField(f, initial);\n body.appendChild(wrapper);\n fieldStates.push(fieldState);\n }\n\n // ---- Footer (fixed) ----\n const footer = document.createElement(\"div\");\n footer.style.cssText = `\n display: flex; align-items: center; justify-content: space-between;\n gap: 10px;\n padding: 12px 16px;\n border-top: 1px solid rgba(255,255,255,0.06);\n background: rgba(0,0,0,0.18);\n flex-shrink: 0;\n `;\n\n const leftActions = document.createElement(\"div\");\n const rightActions = document.createElement(\"div\");\n rightActions.style.cssText = `display: flex; gap: 8px;`;\n\n if (isEdit) {\n const deleteBtn = document.createElement(\"button\");\n deleteBtn.type = \"button\";\n deleteBtn.style.cssText = `\n appearance: none; cursor: pointer;\n background: transparent; border: 1px solid transparent;\n color: #ff8786;\n font-size: 12px; font-weight: 500;\n padding: 6px 10px; border-radius: 7px;\n transition: background 0.15s;\n `;\n deleteBtn.textContent = \"Delete\";\n deleteBtn.addEventListener(\"mouseenter\", () => { deleteBtn.style.background = \"rgba(255,135,134,0.08)\"; });\n deleteBtn.addEventListener(\"mouseleave\", () => { deleteBtn.style.background = \"transparent\"; });\n deleteBtn.addEventListener(\"click\", async () => {\n if (!confirm(`Delete the ${opts.locale} version of this ${opts.schema.labelSingular.toLowerCase()}? This can't be undone.`)) return;\n deleteBtn.disabled = true;\n try {\n await deleteListEntry(opts.schema.name, opts.entry!.id, opts.locale);\n opts.onSaved();\n closeEntryModal();\n } catch (err) {\n showFormError(err instanceof Error ? err.message : String(err));\n deleteBtn.disabled = false;\n }\n });\n leftActions.appendChild(deleteBtn);\n }\n\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.type = \"button\";\n cancelBtn.style.cssText = `\n appearance: none; cursor: pointer;\n background: rgba(255,255,255,0.04);\n border: 1px solid rgba(255,255,255,0.07);\n color: rgba(255,255,255,0.75);\n font-size: 12px; font-weight: 500;\n padding: 7px 14px; border-radius: 8px;\n transition: background 0.15s, color 0.15s;\n `;\n cancelBtn.textContent = \"Cancel\";\n cancelBtn.addEventListener(\"mouseenter\", () => {\n cancelBtn.style.background = \"rgba(255,255,255,0.08)\";\n cancelBtn.style.color = \"rgba(255,255,255,0.9)\";\n });\n cancelBtn.addEventListener(\"mouseleave\", () => {\n cancelBtn.style.background = \"rgba(255,255,255,0.04)\";\n cancelBtn.style.color = \"rgba(255,255,255,0.75)\";\n });\n cancelBtn.addEventListener(\"click\", () => closeEntryModal());\n\n const saveBtn = makePrimaryButton(isEdit ? \"Save\" : \"Create\", accent());\n\n saveBtn.addEventListener(\"click\", async () => {\n clearFormError();\n const { data, ok } = preValidate(fieldStates);\n if (!ok) return;\n\n saveBtn.disabled = true;\n saveBtn.style.opacity = \"0.6\";\n const original = saveBtn.textContent;\n saveBtn.textContent = isEdit ? \"Saving…\" : \"Creating…\";\n\n try {\n if (isEdit) {\n await updateListEntry(opts.schema.name, opts.entry!.id, data, opts.entry!._rev, opts.locale);\n } else {\n let id: string | undefined;\n if (opts.fixedId) {\n id = opts.fixedId;\n } else {\n const slugFieldName = opts.schema.slugField;\n id = slugFieldName && typeof data[slugFieldName] === \"string\"\n ? (data[slugFieldName] as string)\n : undefined;\n }\n await createListEntry(opts.schema.name, data, opts.locale, id);\n }\n opts.onSaved();\n closeEntryModal();\n } catch (err) {\n const error = err as Error & { code?: string };\n if (error.message.includes(\"Validation failed\")) {\n showFormError(\"Server-side validation failed. Check the fields above.\");\n } else if (error.code === \"REV_CONFLICT\") {\n showFormError(\"This entry was changed by someone else. Close and reopen to see the latest version.\");\n } else {\n showFormError(error.message);\n }\n saveBtn.disabled = false;\n saveBtn.style.opacity = \"1\";\n saveBtn.textContent = original ?? (isEdit ? \"Save\" : \"Create\");\n }\n });\n\n rightActions.appendChild(cancelBtn);\n rightActions.appendChild(saveBtn);\n footer.appendChild(leftActions);\n footer.appendChild(rightActions);\n modal.appendChild(footer);\n\n // ---- Close handlers ----\n backdrop.addEventListener(\"click\", () => closeEntryModal());\n\n escListener = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.stopPropagation();\n closeEntryModal();\n }\n };\n document.addEventListener(\"keydown\", escListener, true);\n\n // Focus the first input — small delay so the focus ring doesn't fight\n // the open animation.\n setTimeout(() => {\n const firstInput = body.querySelector<HTMLElement>(\"input, textarea, select\");\n firstInput?.focus();\n }, 90);\n}\n\nexport function closeEntryModal(): void {\n if (modalEl) {\n modalEl.style.animation = \"cancia-modal-out 0.16s cubic-bezier(0.7, 0, 0.84, 0) forwards\";\n const el = modalEl;\n setTimeout(() => el.remove(), 160);\n modalEl = null;\n }\n if (backdropEl) {\n const el = backdropEl;\n el.style.opacity = \"0\";\n setTimeout(() => el.remove(), 180);\n backdropEl = null;\n }\n if (escListener) {\n document.removeEventListener(\"keydown\", escListener, true);\n escListener = null;\n }\n}\n\nexport function isEntryModalOpen(): boolean {\n return modalEl !== null;\n}\n","// =============================================================================\n// Cancia Toolbar — Floating Bar\n// =============================================================================\n// Starts as a 44px circle, morphs into a pill on load/click.\n// =============================================================================\n\nimport { state, clearPending, revertPending } from \"./state\";\nimport { attachHighlight, detachHighlight } from \"./highlight\";\nimport { openPopup, closePopup } from \"./popup\";\nimport { openListPanel, closeListPanel, isListPanelOpen, refreshListPanel } from \"./list-panel\";\nimport { openEntryModal, closeEntryModal, isEntryModalOpen } from \"./entry-modal\";\nimport { flushPending, triggerPublish, isAuthError } from \"./api\";\nimport { onPendingChange } from \"./events\";\n\nlet toolbarEl: HTMLElement | null = null;\nlet pendingPanelEl: HTMLElement | null = null;\nlet isExpanded = false;\nlet expandedEscListener: ((e: KeyboardEvent) => void) | null = null;\n\nfunction accent() {\n return state.config?.accentColor ?? \"#6366f1\";\n}\n\n// ---------------------------------------------------------------------------\n// Inject keyframe animations\n// ---------------------------------------------------------------------------\n\nlet styleInjected = false;\nfunction injectStyles() {\n if (styleInjected) return;\n styleInjected = true;\n const s = document.createElement(\"style\");\n s.textContent = `\n @keyframes cancia-enter {\n from { opacity: 0; transform: scale(0.5) rotate(90deg); }\n to { opacity: 1; transform: scale(1) rotate(0deg); }\n }\n @keyframes cancia-exit {\n from { opacity: 1; transform: scale(1); }\n to { opacity: 0; transform: scale(0.8); }\n }\n @keyframes cancia-controls-in {\n from { opacity: 0; filter: blur(8px); transform: scale(0.6); }\n to { opacity: 1; filter: blur(0px); transform: scale(1); }\n }\n @keyframes cancia-controls-out {\n from { opacity: 1; filter: blur(0px); transform: scale(1); }\n to { opacity: 0; filter: blur(6px); transform: scale(0.5); }\n }\n @keyframes cancia-fade-in {\n from { opacity: 0; transform: scale(0.94) translateY(5px); }\n to { opacity: 1; transform: scale(1) translateY(0); }\n }\n @keyframes cancia-popup-in {\n from { opacity: 0; transform: scale(0.93); }\n to { opacity: 1; transform: scale(1); }\n }\n @keyframes cancia-badge-pop {\n 0% { transform: scale(0); }\n 60% { transform: scale(1.25); }\n 100% { transform: scale(1); }\n }\n @keyframes cancia-icon-slide-in {\n from { transform: translateY(-150%); }\n to { transform: translateY(0); }\n }\n @keyframes cancia-tooltip-in {\n from { opacity: 0; transform: translateX(-50%) translateY(4px); }\n to { opacity: 1; transform: translateX(-50%) translateY(0); }\n }\n [data-cancia-toolbar] * { box-sizing: border-box; }\n [data-cancia-toolbar] button:active:not(:disabled) { transform: scale(0.92) !important; }\n [data-cancia-popup] * { box-sizing: border-box; }\n [data-cancia-popup] button:active:not(:disabled) { transform: scale(0.94) !important; }\n /* Protect stroke-based icons from host page \"svg { fill: currentColor }\" rules */\n [data-cancia-toolbar] svg[fill=\"none\"] { fill: none !important; }\n [data-cancia-toolbar] svg[fill=\"none\"] :not([fill]) { fill: none !important; }\n [data-cancia-popup] svg[fill=\"none\"] { fill: none !important; }\n [data-cancia-popup] svg[fill=\"none\"] :not([fill]) { fill: none !important; }\n /* Reset cosmetic host CSS leaking into toolbar buttons */\n [data-cancia-toolbar] :where(button) {\n background: unset; border: unset; border-radius: unset; padding: unset;\n margin: unset; color: unset; font-family: unset; font-weight: unset;\n font-size: unset; line-height: unset; letter-spacing: unset;\n box-shadow: unset; outline: unset; text-transform: unset;\n }\n `;\n document.head.appendChild(s);\n}\n\n// ---------------------------------------------------------------------------\n// Shared tooltip element (reused across buttons)\n// ---------------------------------------------------------------------------\n\nlet btnTooltipEl: HTMLElement | null = null;\nlet tooltipHideTimer: ReturnType<typeof setTimeout> | null = null;\nlet tooltipShowTimer: ReturnType<typeof setTimeout> | null = null;\nlet tooltipVisible = false;\n\nfunction getOrCreateBtnTooltip(): HTMLElement {\n if (!btnTooltipEl) {\n btnTooltipEl = document.createElement(\"div\");\n btnTooltipEl.style.cssText = `\n position: fixed;\n pointer-events: none;\n z-index: 2147483646;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n font-size: 11px; font-weight: 500; letter-spacing: 0.02em;\n color: #fff;\n background: rgba(10,10,12,0.92);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n border: 1px solid rgba(255,255,255,0.1);\n padding: 4px 8px; border-radius: 6px;\n white-space: nowrap;\n box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n display: none;\n `;\n document.body.appendChild(btnTooltipEl);\n }\n return btnTooltipEl;\n}\n\nfunction showBtnTooltip(btn: HTMLElement, label: string) {\n if (tooltipHideTimer) { clearTimeout(tooltipHideTimer); tooltipHideTimer = null; }\n if (tooltipShowTimer) { clearTimeout(tooltipShowTimer); tooltipShowTimer = null; }\n\n const doShow = () => {\n tooltipVisible = true;\n const tooltip = getOrCreateBtnTooltip();\n tooltip.textContent = label;\n tooltip.style.display = \"block\";\n tooltip.style.animation = \"cancia-tooltip-in 0.12s cubic-bezier(0.16,1,0.3,1) both\";\n\n const rect = btn.getBoundingClientRect();\n const tooltipH = 26;\n const gap = 8;\n tooltip.style.top = `${rect.top - tooltipH - gap}px`;\n tooltip.style.left = `${rect.left + rect.width / 2}px`;\n };\n\n // Skip delay if tooltip is already visible (moving between buttons)\n if (tooltipVisible) {\n doShow();\n } else {\n tooltipShowTimer = setTimeout(doShow, 400);\n }\n}\n\nfunction hideBtnTooltip() {\n if (tooltipShowTimer) { clearTimeout(tooltipShowTimer); tooltipShowTimer = null; }\n if (tooltipHideTimer) clearTimeout(tooltipHideTimer);\n tooltipHideTimer = setTimeout(() => {\n tooltipVisible = false;\n if (btnTooltipEl) btnTooltipEl.style.display = \"none\";\n }, 80);\n}\n\n// ---------------------------------------------------------------------------\n// Build the toolbar DOM\n// ---------------------------------------------------------------------------\n\nfunction buildToolbar(): HTMLElement {\n injectStyles();\n\n // Single morphing container — collapses to 44px circle, expands to pill\n const bar = document.createElement(\"div\");\n bar.dataset.canciaToolbar = \"1\";\n bar.style.cssText = `\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: 2147483647;\n width: 44px;\n height: 44px;\n border-radius: 22px;\n background: rgba(12, 12, 14, 0.92);\n backdrop-filter: blur(16px) saturate(180%);\n -webkit-backdrop-filter: blur(16px) saturate(180%);\n border: 1px solid rgba(255,255,255,0.08);\n box-shadow: 0 2px 8px rgba(0,0,0,0.3), 0 8px 32px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.05);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n font-size: 13px;\n color: #f0f0f0;\n user-select: none;\n cursor: pointer;\n overflow: hidden;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: width 0.45s cubic-bezier(0.19, 1, 0.22, 1), border-radius 0.45s cubic-bezier(0.19, 1, 0.22, 1);\n animation: cancia-enter 0.5s cubic-bezier(0.34, 1.2, 0.64, 1) both;\n `;\n\n // Icon shown when collapsed\n const collapseIcon = document.createElement(\"div\");\n collapseIcon.style.cssText = `\n position: absolute;\n display: flex; align-items: center; justify-content: center;\n color: rgba(255,255,255,0.7);\n transition: opacity 0.15s, transform 0.15s cubic-bezier(0.2,0,0,1);\n pointer-events: none;\n `;\n // Pencil-line icon (Lucide style)\n collapseIcon.innerHTML = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 20h9\"/>\n <path d=\"M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z\"/>\n </svg>`;\n\n\n // Controls row (shown when expanded)\n const controls = document.createElement(\"div\");\n controls.style.cssText = `\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 5px;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transform-origin: right center;\n `;\n\n // Edit toggle — pencil icon, tooltip \"Edit\" / \"Editing\"\n let editActive = false;\n const editBtn = makeIconButton(\n `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/>\n <path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/>\n </svg>`,\n \"Edit\",\n () => toggleEditMode()\n );\n\n controls.appendChild(editBtn);\n\n // Publish — disabled if no deploy hook configured\n const hasHook = state.config?.hasDeployHook ?? true;\n const publishBtn = makeIconButton(\n `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M22 2L11 13\"/>\n <path d=\"M22 2L15 22l-4-9-9-4 20-7z\"/>\n </svg>`,\n hasHook ? \"Publish\" : \"Publish (no deploy hook configured)\",\n () => handlePublish(publishBtn)\n );\n if (!hasHook) {\n publishBtn.disabled = true;\n publishBtn.style.opacity = \"0.3\";\n publishBtn.style.cursor = \"not-allowed\";\n }\n controls.appendChild(publishBtn);\n\n // Log out — door/exit icon\n const logoutBtn = makeIconButton(\n `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4\"/>\n <path d=\"M16 17l5-5-5-5\"/>\n <path d=\"M21 12H9\"/>\n </svg>`,\n \"Log out\",\n () => state.onLogout?.()\n );\n controls.appendChild(logoutBtn);\n controls.appendChild(makeDivider());\n\n // Collapse — X icon\n const collapseBtn = makeIconButton(\n `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\">\n <path d=\"M6 6l12 12M18 6L6 18\"/>\n </svg>`,\n \"Close\",\n () => collapse(bar, collapseIcon, controls)\n );\n controls.appendChild(collapseBtn);\n\n bar.appendChild(collapseIcon);\n bar.appendChild(controls);\n\n // Click on the collapsed bar to expand\n bar.addEventListener(\"click\", () => {\n if (!isExpanded) expand(bar, collapseIcon, controls);\n });\n\n // Hover effect when collapsed\n bar.addEventListener(\"mouseenter\", () => {\n if (!isExpanded) bar.style.background = \"rgba(24, 24, 28, 0.96)\";\n });\n bar.addEventListener(\"mouseleave\", () => {\n bar.style.background = \"rgba(12, 12, 14, 0.92)\";\n });\n\n // Pending changes listener — show/hide floating panel above toolbar\n onPendingChange(() => {\n const count = state.pending.size;\n if (count > 0) {\n showPendingPanel(count);\n } else {\n hidePendingPanel();\n }\n });\n\n let editModeEscListener: ((e: KeyboardEvent) => void) | null = null;\n\n // Edit toggle implementation (closes over controls)\n function toggleEditMode() {\n editActive = !editActive;\n state.editMode = editActive;\n\n const svgEl = editBtn.querySelector(\"svg\");\n if (editActive) {\n editBtn.style.background = `${accent()}22`;\n editBtn.style.color = accent();\n if (svgEl) svgEl.style.stroke = accent();\n editBtn.dataset.canciaTooltip = \"Stop editing (Esc)\";\n attachHighlight((selection) => {\n if (selection.kind === \"field\") {\n openPopup(selection.key, selection.fieldType, selection.el, () => {});\n return;\n }\n // selection.kind === \"list\"\n const schema = state.schemas[selection.listName];\n if (!schema) {\n console.warn(`[cancia] No schema found for list \"${selection.listName}\". Define it in src/cms/schemas.ts.`);\n return;\n }\n openListPanel({\n schema,\n onAddEntry: (locale) => {\n openEntryModal({\n schema,\n locale,\n entry: null,\n onSaved: () => { refreshListPanel(); },\n });\n },\n onEditEntry: (entry, locale) => {\n openEntryModal({\n schema,\n locale,\n entry,\n onSaved: () => { refreshListPanel(); },\n });\n },\n onTranslateEntry: (id, sourceEntry, targetLocale) => {\n openEntryModal({\n schema,\n locale: targetLocale,\n entry: null,\n translateFromEntry: sourceEntry,\n fixedId: id,\n onSaved: () => { refreshListPanel(); },\n });\n },\n });\n });\n // Esc cascade: modal → panel → exit edit mode\n editModeEscListener = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return;\n if (isEntryModalOpen()) {\n // The modal owns its own Esc handler (capture: true) so this branch\n // only runs once the modal is gone. Guard anyway for safety.\n return;\n }\n if (isListPanelOpen()) {\n closeListPanel();\n return;\n }\n if (!document.querySelector(\"[data-cancia-popup]\")) {\n toggleEditMode();\n }\n };\n document.addEventListener(\"keydown\", editModeEscListener, true);\n } else {\n editBtn.style.background = \"transparent\";\n editBtn.style.color = \"rgba(255,255,255,0.85)\";\n if (svgEl) svgEl.style.stroke = \"\";\n editBtn.dataset.canciaTooltip = \"Edit\";\n detachHighlight();\n closePopup();\n closeEntryModal();\n closeListPanel();\n if (editModeEscListener) {\n document.removeEventListener(\"keydown\", editModeEscListener, true);\n editModeEscListener = null;\n }\n }\n }\n\n return bar;\n}\n\n// ---------------------------------------------------------------------------\n// Expand / collapse\n// ---------------------------------------------------------------------------\n\nfunction expand(bar: HTMLElement, icon: HTMLElement, controls: HTMLElement) {\n if (isExpanded) return;\n isExpanded = true;\n\n // Esc collapses toolbar when expanded but not in edit mode\n expandedEscListener = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && !document.querySelector(\"[data-cancia-popup]\") && !state.editMode) {\n collapse(bar, icon, controls);\n }\n };\n document.addEventListener(\"keydown\", expandedEscListener, true);\n\n // Measure natural content width, then animate to it\n controls.style.visibility = \"hidden\";\n controls.style.opacity = \"0\";\n controls.style.pointerEvents = \"none\";\n bar.style.width = \"max-content\";\n bar.style.borderRadius = \"100px\";\n\n requestAnimationFrame(() => {\n const naturalW = bar.scrollWidth;\n bar.style.width = \"44px\"; // reset for transition start\n controls.style.visibility = \"\";\n\n requestAnimationFrame(() => {\n bar.style.width = `${naturalW}px`;\n bar.style.borderRadius = \"100px\";\n bar.style.cursor = \"default\";\n icon.style.opacity = \"0\";\n icon.style.transform = \"scale(0.5) rotate(-90deg)\";\n\n setTimeout(() => {\n controls.style.pointerEvents = \"auto\";\n controls.style.animation = \"cancia-controls-in 0.4s cubic-bezier(0.19, 1, 0.22, 1) both\";\n controls.style.opacity = \"1\";\n }, 80);\n });\n });\n}\n\nfunction collapse(bar: HTMLElement, icon: HTMLElement, controls: HTMLElement) {\n if (!isExpanded) return;\n isExpanded = false;\n hideBtnTooltip();\n\n if (expandedEscListener) {\n document.removeEventListener(\"keydown\", expandedEscListener, true);\n expandedEscListener = null;\n }\n\n controls.style.pointerEvents = \"none\";\n controls.style.animation = \"cancia-controls-out 0.15s cubic-bezier(0.4, 0, 1, 1) both\";\n\n setTimeout(() => {\n controls.style.opacity = \"0\";\n bar.style.width = \"44px\";\n bar.style.borderRadius = \"22px\";\n bar.style.cursor = \"pointer\";\n icon.style.opacity = \"1\";\n icon.style.transform = \"scale(1) rotate(0deg)\";\n }, 100);\n}\n\n// ---------------------------------------------------------------------------\n// Save handler\n// ---------------------------------------------------------------------------\n\nasync function handleSave(btn?: HTMLButtonElement) {\n if (btn) { btn.disabled = true; btn.style.opacity = \"0.5\"; }\n\n try {\n await flushPending();\n flashPanelMessage(\"Saved\", \"success\");\n } catch (err) {\n console.error(err);\n if (isAuthError(err)) { unmountToolbar(); return; }\n if (btn) { btn.disabled = false; btn.style.opacity = \"1\"; }\n flashPanelMessage(\"Save failed\", \"error\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Publish handler\n// ---------------------------------------------------------------------------\n\nasync function handlePublish(btn: HTMLButtonElement) {\n btn.disabled = true;\n btn.style.opacity = \"0.5\";\n\n try {\n if (state.pending.size > 0) await flushPending();\n await triggerPublish();\n showToast(\"Published!\", \"success\");\n } catch (err) {\n console.error(err);\n showToast(\"Publish failed\", \"error\");\n } finally {\n btn.disabled = false;\n btn.style.opacity = \"1\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Toast\n// ---------------------------------------------------------------------------\n\nfunction showToast(message: string, type: \"success\" | \"error\") {\n const toast = document.createElement(\"div\");\n const color = type === \"success\" ? \"#4ade80\" : \"#f87171\";\n toast.style.cssText = `\n position: fixed; bottom: 80px; right: 24px; z-index: 2147483647;\n display: flex; align-items: center; gap: 8px;\n background: rgba(12, 12, 14, 0.95);\n backdrop-filter: blur(16px);\n border: 1px solid rgba(255,255,255,0.08);\n border-radius: 10px; padding: 10px 14px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n font-size: 13px; font-weight: 500; color: #f0f0f0;\n box-shadow: 0 4px 24px rgba(0,0,0,0.4);\n pointer-events: none;\n animation: cancia-fade-in 0.3s cubic-bezier(0.16, 1, 0.3, 1) both;\n `;\n const dot = document.createElement(\"span\");\n dot.style.cssText = `width: 7px; height: 7px; border-radius: 50%; background: ${color}; flex-shrink: 0;`;\n const label = document.createElement(\"span\");\n label.textContent = message;\n toast.appendChild(dot);\n toast.appendChild(label);\n document.body.appendChild(toast);\n\n setTimeout(() => {\n toast.style.transition = \"opacity 0.25s cubic-bezier(0.4,0,1,1), transform 0.25s cubic-bezier(0.4,0,1,1)\";\n toast.style.opacity = \"0\";\n toast.style.transform = \"translateY(4px)\";\n setTimeout(() => toast.remove(), 300);\n }, 2000);\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction makeIconButton(\n svg: string,\n title: string,\n onClick: () => void,\n): HTMLButtonElement {\n const btn = document.createElement(\"button\");\n btn.style.cssText = `\n display: flex; align-items: center; justify-content: center;\n width: 34px; height: 34px; border-radius: 50%;\n border: none; background: transparent;\n cursor: pointer; color: rgba(255,255,255,0.85); flex-shrink: 0; padding: 0;\n transition: color 0.15s, background 0.15s, transform 0.1s cubic-bezier(0.2,0,0,1);\n `;\n btn.innerHTML = svg;\n // Force consistent stroke-width on all child SVGs to prevent host CSS override\n const svgEl = btn.querySelector(\"svg\");\n if (svgEl) {\n svgEl.style.cssText = \"display:block;flex-shrink:0;overflow:visible;margin:auto;\";\n svgEl.setAttribute(\"stroke-width\", \"1.5\");\n }\n btn.dataset.canciaTooltip = title;\n btn.addEventListener(\"mouseenter\", () => {\n if (!btn.disabled) {\n btn.style.background = \"rgba(255,255,255,0.1)\";\n showBtnTooltip(btn, btn.dataset.canciaTooltip ?? title);\n }\n });\n btn.addEventListener(\"mouseleave\", () => {\n // Only reset bg if not in active state (active state managed externally)\n const activeColor = state.config?.accentColor ?? \"#6366f1\";\n if (!btn.style.background.includes(activeColor.slice(1, 7))) {\n btn.style.background = \"transparent\";\n }\n hideBtnTooltip();\n });\n btn.addEventListener(\"click\", (e) => { e.stopPropagation(); hideBtnTooltip(); onClick(); });\n return btn;\n}\n\nfunction makeDivider(): HTMLElement {\n const d = document.createElement(\"span\");\n d.style.cssText = `width: 1px; height: 14px; background: rgba(255,255,255,0.08); flex-shrink: 0; margin: 0 1px;`;\n return d;\n}\n\n// ---------------------------------------------------------------------------\n// Pending panel — floats above the toolbar\n// ---------------------------------------------------------------------------\n\nfunction showPendingPanel(count: number) {\n if (!pendingPanelEl) {\n pendingPanelEl = document.createElement(\"div\");\n pendingPanelEl.style.cssText = `\n position: fixed;\n bottom: 80px;\n right: 24px;\n z-index: 2147483646;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n background: rgba(12, 12, 14, 0.92);\n backdrop-filter: blur(16px) saturate(180%);\n -webkit-backdrop-filter: blur(16px) saturate(180%);\n border: 1px solid rgba(255,255,255,0.08);\n border-radius: 12px;\n padding: 0;\n box-shadow: 0 2px 8px rgba(0,0,0,0.3), 0 8px 32px rgba(0,0,0,0.4);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n animation: cancia-fade-in 0.25s cubic-bezier(0.16,1,0.3,1) both;\n width: max-content;\n overflow: hidden;\n `;\n\n const label = document.createElement(\"span\");\n label.dataset.canciaPendingLabel = \"1\";\n label.style.cssText = `font-size: 12px; font-weight: 500; color: rgba(255,255,255,0.5); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;`;\n\n const saveBtn = document.createElement(\"button\");\n saveBtn.dataset.canciaSaveBtn = \"1\";\n saveBtn.title = \"Save changes\";\n saveBtn.style.cssText = `\n padding: 5px 10px; border-radius: 7px; border: none; cursor: pointer;\n background: #fff; color: #0c0c0e;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n font-size: 12px; font-weight: 600; letter-spacing: 0.01em;\n transition: opacity 0.15s, transform 0.1s cubic-bezier(0.2,0,0,1);\n flex-shrink: 0;\n `;\n saveBtn.textContent = \"Save\";\n saveBtn.addEventListener(\"mouseenter\", () => { saveBtn.style.opacity = \"0.85\"; });\n saveBtn.addEventListener(\"mouseleave\", () => { saveBtn.style.opacity = \"1\"; });\n saveBtn.addEventListener(\"click\", (e) => { e.stopPropagation(); handleSave(saveBtn); });\n\n const undoBtn = document.createElement(\"button\");\n undoBtn.title = \"Discard changes\";\n undoBtn.style.cssText = `\n padding: 5px 10px; border-radius: 7px; border: 1px solid rgba(255,255,255,0.1); cursor: pointer;\n background: transparent; color: rgba(255,255,255,0.5);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n font-size: 12px; font-weight: 500; letter-spacing: 0.01em;\n transition: color 0.15s, border-color 0.15s;\n flex-shrink: 0;\n `;\n undoBtn.textContent = \"Discard\";\n undoBtn.addEventListener(\"mouseenter\", () => { undoBtn.style.color = \"rgba(255,255,255,0.8)\"; undoBtn.style.borderColor = \"rgba(255,255,255,0.2)\"; });\n undoBtn.addEventListener(\"mouseleave\", () => { undoBtn.style.color = \"rgba(255,255,255,0.5)\"; undoBtn.style.borderColor = \"rgba(255,255,255,0.1)\"; });\n undoBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n revertPending();\n closePopup();\n hidePendingPanel();\n });\n\n // Inner slot: stacks current content + flash message via grid\n const slot = document.createElement(\"div\");\n slot.dataset.canciaPanelSlot = \"1\";\n slot.style.cssText = `display:grid;place-items:center;overflow:hidden;`;\n\n // Default row: label + undo + save button\n const row = document.createElement(\"div\");\n row.dataset.canciaPanelRow = \"1\";\n row.style.cssText = `\n grid-area: 1/1; display: flex; align-items: center; gap: 6px;\n padding: 7px 7px 7px 12px;\n transition: transform 200ms cubic-bezier(0.785,0.135,0.15,0.86);\n `;\n row.appendChild(label);\n row.appendChild(undoBtn);\n row.appendChild(saveBtn);\n\n // Flash row: success/error message (hidden above initially)\n const flash = document.createElement(\"div\");\n flash.dataset.canciaPanelFlash = \"1\";\n flash.style.cssText = `\n grid-area: 1/1; display: flex; align-items: center; gap: 7px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n font-size: 12px; font-weight: 500; color: rgba(255,255,255,0.8); white-space: nowrap;\n padding: 7px 12px;\n transform: translateY(-150%);\n transition: transform 200ms cubic-bezier(0.785,0.135,0.15,0.86);\n `;\n\n slot.appendChild(row);\n slot.appendChild(flash);\n pendingPanelEl.appendChild(slot);\n document.body.appendChild(pendingPanelEl);\n }\n\n const label = pendingPanelEl.querySelector<HTMLElement>(\"[data-cancia-pending-label]\");\n if (label) label.textContent = `${count} unsaved change${count === 1 ? \"\" : \"s\"}`;\n}\n\nfunction flashPanelMessage(message: string, type: \"success\" | \"error\") {\n if (!pendingPanelEl) return;\n const color = type === \"success\" ? \"#4ade80\" : \"#f87171\";\n\n const row = pendingPanelEl.querySelector<HTMLElement>(\"[data-cancia-panel-row]\");\n const flash = pendingPanelEl.querySelector<HTMLElement>(\"[data-cancia-panel-flash]\");\n if (!row || !flash) return;\n\n // Build flash content\n flash.innerHTML = `<span style=\"width:7px;height:7px;border-radius:50%;background:${color};flex-shrink:0;display:block;\"></span>${message}`;\n\n // Slide current content down, flash content in from above\n row.style.transform = \"translateY(150%)\";\n flash.style.transform = \"translateY(0)\";\n\n setTimeout(() => hidePendingPanel(), 1600);\n}\n\nfunction hidePendingPanel() {\n if (!pendingPanelEl) return;\n const panel = pendingPanelEl;\n pendingPanelEl = null;\n panel.style.transition = \"opacity 0.2s cubic-bezier(0.4,0,1,1), transform 0.2s cubic-bezier(0.4,0,1,1)\";\n panel.style.opacity = \"0\";\n panel.style.transform = \"translateY(4px)\";\n setTimeout(() => panel.remove(), 220);\n}\n\n// ---------------------------------------------------------------------------\n// Mount / unmount\n// ---------------------------------------------------------------------------\n\nexport function mountToolbar() {\n if (toolbarEl) return;\n isExpanded = false;\n toolbarEl = buildToolbar();\n document.body.appendChild(toolbarEl);\n}\n\nexport function unmountToolbar() {\n detachHighlight();\n closePopup();\n isExpanded = false;\n // Clean up shared tooltip\n btnTooltipEl?.remove();\n btnTooltipEl = null;\n // Clean up pending panel\n pendingPanelEl?.remove();\n pendingPanelEl = null;\n if (toolbarEl) {\n toolbarEl.style.animation = \"cancia-exit 0.25s cubic-bezier(0.4, 0, 1, 1) both\";\n setTimeout(() => {\n toolbarEl?.remove();\n toolbarEl = null;\n }, 260);\n }\n}\n","// =============================================================================\n// Cancia Toolbar — Entry Point\n// =============================================================================\n\nimport type { CanciaConfig } from \"./types\";\nimport { state, applyOverlay } from \"./state\";\nimport { fetchContent, fetchSchemas } from \"./api\";\nimport { mountToolbar, unmountToolbar } from \"./toolbar\";\nimport { onPendingChange } from \"./events\";\n\nexport type { CanciaConfig } from \"./types\";\nexport type { CMSData, CMSEntry, PendingChange } from \"./types\";\n\nconst SESSION_KEY = \"cancia_session\";\n\n// ---------------------------------------------------------------------------\n// Session helpers — token lives in sessionStorage only\n// ---------------------------------------------------------------------------\n\nfunction getSessionToken(): string | null {\n try {\n return sessionStorage.getItem(SESSION_KEY);\n } catch {\n return null;\n }\n}\n\nfunction setSessionToken(token: string): void {\n try {\n sessionStorage.setItem(SESSION_KEY, token);\n } catch {}\n}\n\nexport function clearSession(): void {\n try {\n sessionStorage.removeItem(SESSION_KEY);\n } catch {}\n}\n\n// ---------------------------------------------------------------------------\n// Magic URL: ?cancia=<token>\n// Validates against the API, stores in sessionStorage, strips from URL.\n// ---------------------------------------------------------------------------\n\nasync function handleMagicUrl(): Promise<string | null> {\n const params = new URLSearchParams(window.location.search);\n const token = params.get(\"cancia\");\n if (!token) return null;\n\n // Strip ?cancia=... from the URL immediately (before any validation)\n params.delete(\"cancia\");\n const newSearch = params.toString();\n const cleanUrl = window.location.pathname + (newSearch ? `?${newSearch}` : \"\") + window.location.hash;\n window.history.replaceState(null, \"\", cleanUrl);\n\n // Validate with the server\n try {\n const res = await fetch(\"/api/cancia/auth\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token }),\n });\n if (res.ok) {\n setSessionToken(token);\n return token;\n }\n } catch {}\n\n console.warn(\"Cancia: magic link token is invalid or expired.\");\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// init()\n// ---------------------------------------------------------------------------\n\nexport async function init(config: CanciaConfig): Promise<void> {\n state.config = config;\n state.activeLang = config.languages[0];\n\n // Preload CMS overrides from window if available (injected at build time)\n if (window.__CANCIA_DATA__) {\n state.cmsData = window.__CANCIA_DATA__;\n }\n\n try {\n const fresh = await fetchContent();\n state.cmsData = fresh;\n } catch (err: unknown) {\n if (!config.public && err instanceof Error && err.message.includes(\"401\")) {\n clearSession();\n console.warn(\"Cancia: session expired or invalid, toolbar not mounted.\");\n return;\n }\n console.warn(\"Cancia: could not fetch CMS content, using preloaded data.\");\n }\n\n // Overlay saved drafts onto the (prerendered) page so editors see their\n // unpublished changes. Uses whatever cmsData ended up loaded above — fresh\n // fetch, or the window.__CANCIA_DATA__ preload if the fetch failed.\n applyOverlay();\n\n // Schemas are best-effort: a v1 install with no schemas.ts returns {}.\n // Lists with no schema are simply unclickable from the toolbar.\n try {\n state.schemas = await fetchSchemas();\n } catch {\n state.schemas = {};\n }\n\n mountToolbar();\n}\n\n// ---------------------------------------------------------------------------\n// Auto-init from window.__CANCIA__\n// Checks magic URL first, then falls back to existing sessionStorage session.\n// Does nothing if no valid session exists — toolbar stays hidden from visitors.\n// ---------------------------------------------------------------------------\n\nasync function tryAutoInit() {\n if (!window.__CANCIA__) return;\n\n // Public mode — skip auth, always show toolbar (demo sites only)\n if (window.__CANCIA__.public) {\n init(window.__CANCIA__);\n return;\n }\n\n // Check magic URL first, then existing session\n const token = (await handleMagicUrl()) ?? getSessionToken();\n if (!token) return; // No session — normal visitor, nothing to do\n\n // Attach the session token to state so API calls can use it\n state.sessionToken = token;\n\n // Wire up logout handler — called by the toolbar button\n state.onLogout = () => {\n clearSession();\n unmountToolbar();\n };\n\n init(window.__CANCIA__);\n}\n\nif (typeof document !== \"undefined\") {\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", tryAutoInit);\n } else {\n tryAutoInit();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Re-exports for programmatic use\n// ---------------------------------------------------------------------------\n\nexport { unmountToolbar as destroy } from \"./toolbar\";\nexport { getValue } from \"./state\";\nexport { onPendingChange };\n"],"mappings":"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","names":["accent","renderTabs","fieldType","styleInjected","injectStyles","accent","BACKDROP_Z","backdropEl","styleInjected","injectStyles","accent","getValue","accent","injectStyles","BACKDROP_Z","backdropEl","accent","styleInjected","injectStyles","label"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cancia/toolbar",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Vanilla JS inline editor toolbar for Cancia CMS",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/ninuzdellalb/cancia",
|
|
9
|
+
"directory": "packages/toolbar"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/cancia.js",
|
|
13
|
+
"module": "./dist/cancia.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/cancia.js"
|
|
19
|
+
},
|
|
20
|
+
"./snippet": "./dist/cancia.iife.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"tsup": "^8.0.0",
|
|
27
|
+
"typescript": "^5.4.0"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"dev": "tsup --watch",
|
|
31
|
+
"build": "tsup",
|
|
32
|
+
"typecheck": "tsc --noEmit"
|
|
33
|
+
}
|
|
34
|
+
}
|