@duffcloudservices/cms 0.3.14 → 0.3.15

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.
@@ -39,6 +39,10 @@ interface EditorReadyPayload {
39
39
  hasBlogContent: boolean;
40
40
  /** Current innerHTML of the blog content element, if present */
41
41
  blogContentHtml: string | null;
42
+ /** Whether the page has a [data-event-content] or [data-rich-content="event"] element */
43
+ hasEventContent: boolean;
44
+ /** Current innerHTML of the event content element, if present */
45
+ eventContentHtml: string | null;
42
46
  /** Current blog post title text from [data-blog-title], if present */
43
47
  blogTitle: string | null;
44
48
  /** Current blog post summary/description text from [data-blog-summary], if present */
@@ -12,6 +12,8 @@ function postToParent(type, data) {
12
12
  }
13
13
  var TEXT_KEY_SELECTOR = "[data-text-key], [data-dcs-text]";
14
14
  var REVIEW_SELECTOR = "[data-dcs-reviews]";
15
+ var BLOG_CONTENT_SELECTOR = "[data-blog-content]";
16
+ var EVENT_CONTENT_SELECTOR = '[data-event-content], [data-rich-content="event"]';
15
17
  function getTextKey(el) {
16
18
  return el.dataset.dcsText ?? el.dataset.textKey ?? "";
17
19
  }
@@ -41,6 +43,7 @@ var initializedSections = /* @__PURE__ */ new WeakSet();
41
43
  var initializedReviewElements = /* @__PURE__ */ new WeakSet();
42
44
  var initializedManagedFormElements = /* @__PURE__ */ new WeakSet();
43
45
  var blogContentInitialized = false;
46
+ var eventContentInitialized = false;
44
47
  var blogMetadataInitialized = false;
45
48
  function scheduleRediscovery() {
46
49
  if (rediscoveryTimer) clearTimeout(rediscoveryTimer);
@@ -145,6 +148,7 @@ function checkForNavigation() {
145
148
  removeArrayEditIcon();
146
149
  removeReviewEditIcon();
147
150
  blogContentInitialized = false;
151
+ eventContentInitialized = false;
148
152
  blogMetadataInitialized = false;
149
153
  setTimeout(() => {
150
154
  rediscoverAndNotify();
@@ -164,9 +168,12 @@ function rediscoverAndNotify() {
164
168
  const reviewKeys = discoverReviewKeys();
165
169
  const managedFormIds = discoverManagedFormIds();
166
170
  const contentHeight = measureContentHeight(sections);
167
- const blogEl = document.querySelector("[data-blog-content]");
171
+ const blogEl = document.querySelector(BLOG_CONTENT_SELECTOR);
168
172
  const hasBlogContent = !!blogEl;
169
173
  const blogContentHtml = blogEl ? blogEl.innerHTML : null;
174
+ const eventEl = document.querySelector(EVENT_CONTENT_SELECTOR);
175
+ const hasEventContent = !!eventEl;
176
+ const eventContentHtml = eventEl ? eventEl.innerHTML : null;
170
177
  const blogTitleEl = document.querySelector("[data-blog-title]");
171
178
  const blogSummaryEl = document.querySelector("[data-blog-summary]");
172
179
  const blogTitle = blogTitleEl ? blogTitleEl.textContent?.trim() ?? null : null;
@@ -177,6 +184,8 @@ function rediscoverAndNotify() {
177
184
  contentHeight,
178
185
  hasBlogContent,
179
186
  blogContentHtml,
187
+ hasEventContent,
188
+ eventContentHtml,
180
189
  blogTitle,
181
190
  blogSummary,
182
191
  hasManagedForms: managedFormIds.length > 0,
@@ -301,7 +310,7 @@ function applyEditorToElements() {
301
310
  });
302
311
  });
303
312
  if (!blogContentInitialized) {
304
- const blogContentEl = document.querySelector("[data-blog-content]");
313
+ const blogContentEl = document.querySelector(BLOG_CONTENT_SELECTOR);
305
314
  if (blogContentEl) {
306
315
  blogContentInitialized = true;
307
316
  blogContentEl.classList.add("dcs-blog-content");
@@ -322,6 +331,28 @@ function applyEditorToElements() {
322
331
  postToParent("dcs:blog-content-ready", { content: blogContentEl.innerHTML });
323
332
  }
324
333
  }
334
+ if (!eventContentInitialized) {
335
+ const eventContentEl = document.querySelector(EVENT_CONTENT_SELECTOR);
336
+ if (eventContentEl) {
337
+ eventContentInitialized = true;
338
+ eventContentEl.classList.add("dcs-blog-content");
339
+ eventContentEl.addEventListener("click", (e) => {
340
+ const target = e.target;
341
+ if (target.closest(TEXT_KEY_SELECTOR)) return;
342
+ e.preventDefault();
343
+ e.stopPropagation();
344
+ postToParent("dcs:blog-content-click", { textKey: "event-content" });
345
+ });
346
+ eventContentEl.addEventListener("dblclick", (e) => {
347
+ const target = e.target;
348
+ if (target.closest(TEXT_KEY_SELECTOR)) return;
349
+ e.preventDefault();
350
+ e.stopPropagation();
351
+ postToParent("dcs:blog-content-click", { textKey: "event-content" });
352
+ });
353
+ postToParent("dcs:blog-content-ready", { content: eventContentEl.innerHTML });
354
+ }
355
+ }
325
356
  if (!blogMetadataInitialized) {
326
357
  const titleEl = document.querySelector("[data-blog-title]");
327
358
  const summaryEl = document.querySelector("[data-blog-summary]");
@@ -1024,9 +1055,9 @@ function handleMessage(event) {
1024
1055
  break;
1025
1056
  case "dcs:update-blog-content":
1026
1057
  if (data && typeof data === "object" && "content" in data) {
1027
- const blogEl = document.querySelector("[data-blog-content]");
1028
- if (blogEl) {
1029
- blogEl.innerHTML = data.content;
1058
+ const contentEl = document.querySelector(BLOG_CONTENT_SELECTOR) ?? document.querySelector(EVENT_CONTENT_SELECTOR);
1059
+ if (contentEl) {
1060
+ contentEl.innerHTML = data.content;
1030
1061
  scheduleRediscovery();
1031
1062
  }
1032
1063
  }
@@ -1123,7 +1154,8 @@ function initEditorBridge() {
1123
1154
  const reviewKeys = discoverReviewKeys();
1124
1155
  const managedFormIds = discoverManagedFormIds();
1125
1156
  const contentHeight = measureContentHeight(sections);
1126
- const blogEl = document.querySelector("[data-blog-content]");
1157
+ const blogEl = document.querySelector(BLOG_CONTENT_SELECTOR);
1158
+ const eventEl = document.querySelector(EVENT_CONTENT_SELECTOR);
1127
1159
  const blogTitleEl = document.querySelector("[data-blog-title]");
1128
1160
  const blogSummaryEl = document.querySelector("[data-blog-summary]");
1129
1161
  postToParent("dcs:ready", {
@@ -1132,6 +1164,8 @@ function initEditorBridge() {
1132
1164
  contentHeight,
1133
1165
  hasBlogContent: !!blogEl,
1134
1166
  blogContentHtml: blogEl ? blogEl.innerHTML : null,
1167
+ hasEventContent: !!eventEl,
1168
+ eventContentHtml: eventEl ? eventEl.innerHTML : null,
1135
1169
  blogTitle: blogTitleEl ? blogTitleEl.textContent?.trim() ?? null : null,
1136
1170
  blogSummary: blogSummaryEl ? blogSummaryEl.textContent?.trim() ?? null : null,
1137
1171
  hasManagedForms: managedFormIds.length > 0,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/editor/editorBridge.ts"],"names":[],"mappings":"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","file":"editorBridge.js","sourcesContent":["/**\n * Editor Bridge — injected into customer site iframes by the DCS visual editor.\n *\n * Responsibilities:\n * 1. Report available sections and text keys to the parent portal\n * 2. Enable inline contenteditable editing on data-text-key/data-dcs-text elements (double-click)\n * 3. Render section overlay highlights on hover/select from parent\n * 4. Communicate all interactions back to the parent via postMessage\n * 5. Monitor navigation within the iframe and notify the parent\n * 6. Show floating edit icon on hover for text-key elements\n * 7. Show floating image icon on hover for images within sections\n *\n * Supports both legacy `data-text-key` and modern `data-dcs-text` attributes\n * for backward compatibility during the migration period.\n *\n * NOTE: AI ✨ buttons are rendered by the portal-side SectionOverlayLayer,\n * NOT by this bridge. The bridge only reports section/text key data and\n * handles inline text editing.\n *\n * This script runs inside the iframe (customer site). The parent portal\n * communicates via postMessage using the `dcs:*` message protocol.\n */\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport interface SectionInfo {\n id: string\n label: string | null\n bounds: { x: number; y: number; width: number; height: number }\n textKeyCount: number\n}\n\nexport interface EditorReadyPayload {\n sections: SectionInfo[]\n textKeys: string[]\n contentHeight: number\n /** Whether the page has a [data-blog-content] element (blog post page) */\n hasBlogContent: boolean\n /** Current innerHTML of the blog content element, if present */\n blogContentHtml: string | null\n /** Current blog post title text from [data-blog-title], if present */\n blogTitle: string | null\n /** Current blog post summary/description text from [data-blog-summary], if present */\n blogSummary: string | null\n /** Whether the page has any managed form roots */\n hasManagedForms: boolean\n /** Form IDs for all detected managed-form roots */\n managedFormIds: string[]\n /** Whether the page has any [data-dcs-reviews] elements */\n hasReviews: boolean\n /** Keys of all [data-dcs-reviews] elements found on the page */\n reviewKeys: string[]\n}\n\n// ─── Message Protocol ────────────────────────────────────────────\n\ntype InboundMessageType =\n | 'dcs:init-editor'\n | 'dcs:highlight-section'\n | 'dcs:clear-highlight'\n | 'dcs:select-text-key'\n | 'dcs:update-text'\n | 'dcs:set-mode'\n | 'dcs:set-editing-enabled'\n | 'dcs:update-blog-content'\n | 'dcs:update-reviews'\n\ntype OutboundMessageType =\n | 'dcs:ready'\n | 'dcs:text-key-click'\n | 'dcs:text-key-changed'\n | 'dcs:section-click'\n | 'dcs:section-hover'\n | 'dcs:text-key-dblclick'\n | 'dcs:contextmenu'\n | 'dcs:click'\n | 'dcs:navigation'\n | 'dcs:array-key-click'\n | 'dcs:image-click'\n | 'dcs:blog-content-click'\n | 'dcs:blog-content-ready'\n | 'dcs:blog-metadata-click'\n | 'dcs:managed-form-click'\n | 'dcs:reviews-click'\n\nfunction isInIframe(): boolean {\n try { return globalThis.self !== globalThis.self.parent } catch { return true }\n}\n\nfunction postToParent(type: OutboundMessageType, data: unknown) {\n if (!isInIframe()) return\n // Use '*' because the portal origin varies between dev and production\n // and this script doesn't know the parent origin at injection time.\n // Security: messages are validated by the portal's useIframeBridge.\n // Origin validated via dcs: prefix check on all messages\n globalThis.self.parent.postMessage({ type, data }, '*') // NOSONAR\n}\n\n// ─── Attribute Compatibility ─────────────────────────────────────\n\n/**\n * CSS selector that matches elements with either the legacy `data-text-key`\n * attribute or the modern `data-dcs-text` attribute. This ensures the editor\n * bridge works with both pre-migration and post-migration sites.\n */\nconst TEXT_KEY_SELECTOR = '[data-text-key], [data-dcs-text]'\nconst REVIEW_SELECTOR = '[data-dcs-reviews]'\n\n/**\n * Get the text key value from an element, checking the modern `data-dcs-text`\n * attribute first, then falling back to the legacy `data-text-key`.\n */\nfunction getTextKey(el: HTMLElement): string {\n return el.dataset.dcsText ?? el.dataset.textKey ?? ''\n}\n\n/**\n * Build a CSS selector that matches a specific text key value\n * across both legacy and modern attribute names.\n */\nfunction textKeySelector(key: string): string {\n return `[data-text-key=\"${key}\"], [data-dcs-text=\"${key}\"]`\n}\n\n// ─── State ───────────────────────────────────────────────────────\n\nlet editorActive = false\nlet bridgeInitialized = false\nlet activeEditElement: HTMLElement | null = null\n/** The current pathname tracked for navigation detection */\nlet lastKnownPathname: string = ''\n/** The floating edit icon element shown on hover over text keys */\nlet editIconElement: HTMLElement | null = null\n/** The element the edit icon is currently attached to */\nlet editIconTarget: HTMLElement | null = null\n/** The floating edit icon for array/list text keys */\nlet arrayEditIconElement: HTMLElement | null = null\n/** The element the array edit icon is currently attached to */\nlet arrayEditIconTarget: HTMLElement | null = null\n/** The floating edit icons for image elements within sections (supports multiple for overlapping images) */\nlet imageEditIconElements: HTMLElement[] = []\n/** The image elements the image edit icons are currently positioned on */\nlet imageEditIconTargets: HTMLImageElement[] = []\n/** The floating edit icon for review sections */\nlet reviewEditIconElement: HTMLElement | null = null\n/** The review section element the review edit icon is currently attached to */\nlet reviewEditIconTarget: HTMLElement | null = null\n/** The floating manage-form button for managed form roots */\nlet managedFormEditIconElement: HTMLElement | null = null\n/** The managed form root the floating button is currently attached to */\nlet managedFormEditIconTarget: HTMLElement | null = null\n/** Whether editing features are enabled (disabled on unmanaged pages) */\nlet editingEnabled = true\n/** Original text values captured when inline editing starts, for change detection */\nconst originalTextValues = new Map<HTMLElement, string>()\n/** ResizeObserver for tracking section layout changes */\nlet sectionResizeObserver: ResizeObserver | null = null\n/** Debounce timer for re-discovery triggered by layout changes */\nlet rediscoveryTimer: ReturnType<typeof setTimeout> | null = null\n/** Track elements that have had editor event listeners attached (prevents duplicate listeners) */\nconst initializedTextElements = new WeakSet<HTMLElement>()\nconst initializedSections = new WeakSet<HTMLElement>()\nconst initializedReviewElements = new WeakSet<HTMLElement>()\nconst initializedManagedFormElements = new WeakSet<HTMLElement>()\nlet blogContentInitialized = false\nlet blogMetadataInitialized = false\n\n/** Debounced re-discovery — collapses rapid layout changes into one update */\nfunction scheduleRediscovery() {\n if (rediscoveryTimer) clearTimeout(rediscoveryTimer)\n rediscoveryTimer = setTimeout(() => {\n rediscoveryTimer = null\n rediscoverAndNotify()\n }, 300)\n}\n\n// ─── Section Discovery ───────────────────────────────────────────\n\nfunction discoverSections(): SectionInfo[] {\n const elements = document.querySelectorAll<HTMLElement>('[data-section]')\n return Array.from(elements).map((el) => {\n const rect = el.getBoundingClientRect()\n const textKeyCount = el.querySelectorAll(TEXT_KEY_SELECTOR).length\n return {\n id: el.dataset.section!,\n label: el.dataset.sectionLabel ?? null,\n bounds: {\n x: rect.left + scrollX,\n y: rect.top + scrollY,\n width: rect.width,\n height: rect.height,\n },\n textKeyCount,\n }\n })\n}\n\nfunction discoverTextKeys(): string[] {\n return Array.from(document.querySelectorAll<HTMLElement>(TEXT_KEY_SELECTOR)).map(\n (el) => getTextKey(el),\n )\n}\n\nfunction discoverReviewKeys(): string[] {\n return Array.from(document.querySelectorAll<HTMLElement>(REVIEW_SELECTOR))\n .map(el => el.dataset.dcsReviews ?? '')\n .filter(Boolean)\n}\n\nfunction discoverManagedFormIds(): string[] {\n return Array.from(document.querySelectorAll<HTMLElement>('[data-form-key]'))\n .map(el => el.dataset.formKey ?? '')\n .filter(Boolean)\n}\n\n// ─── Layout Change Observation ───────────────────────────────────\n\n/**\n * Watch for layout changes that affect section bounds and overall page height:\n * 1. ALL lazy-loaded images on the page — their load events shift subsequent sections\n * 2. ResizeObserver on the document body — detects overall page height changes\n * 3. ResizeObserver on section elements — detects any size change (font loading,\n * dynamic content expansion, CSS transitions, etc.)\n *\n * When changes are detected, we schedule a debounced re-discovery so the portal\n * overlay layer receives updated section bounds and content height that match\n * the live layout.\n */\nfunction observeSectionLayout() {\n // Clean up any previous observer (e.g., after SPA navigation)\n if (sectionResizeObserver) {\n sectionResizeObserver.disconnect()\n }\n\n // 1. Watch ALL lazy images on the page — not just inside sections.\n // Images outside sections (e.g., contact form, unmapped content) can still\n // push content down and change overall page height and section positions.\n const allImages = document.querySelectorAll<HTMLImageElement>('img')\n allImages.forEach(img => {\n if (!img.complete) {\n img.addEventListener('load', scheduleRediscovery, { once: true })\n img.addEventListener('error', scheduleRediscovery, { once: true })\n }\n })\n\n // 2. ResizeObserver on the document body + each section element.\n // Body observation catches overall page height changes from any source.\n if (typeof ResizeObserver !== 'undefined') {\n sectionResizeObserver = new ResizeObserver(scheduleRediscovery)\n // Observe body for overall height changes\n sectionResizeObserver.observe(document.body)\n // Observe each section for individual bound changes\n document.querySelectorAll<HTMLElement>('[data-section]').forEach(el => {\n sectionResizeObserver!.observe(el)\n })\n }\n}\n\n// ─── Section Overlay ─────────────────────────────────────────────\n\n/**\n * Section highlight is now handled entirely by the portal-side\n * SectionOverlayLayer rendered on top of the iframe. The bridge\n * only reports section bounds and hover events — no visual\n * overlays are rendered inside the iframe itself.\n */\nfunction showSectionHighlight(_sectionId: string) {\n // No-op: portal overlay layer handles section highlights\n}\n\nfunction clearSectionHighlight() {\n // No-op: portal overlay layer handles section highlights\n}\n\n// ─── Navigation Monitoring ───────────────────────────────────────\n\n/**\n * Monitor navigation within the iframe and notify the parent portal.\n * Instead of blocking all navigation, we allow SPA routing and inform\n * the portal when the iframe navigates to a different page. The portal\n * then updates its own URL and loads editing context for the new page.\n *\n * Strategy:\n * 1. Poll location.pathname on a short interval — this reliably detects\n * SPA navigation regardless of which router or framework is used,\n * since frameworks may cache History API references before our script loads.\n * 2. Block external/cross-origin link clicks (actual page reloads).\n * 3. Block form submissions.\n * 4. Monitor popstate for back/forward navigation.\n * 5. After navigation, re-discover sections and report them to the portal.\n */\nfunction monitorNavigation() {\n lastKnownPathname = globalThis.location.pathname\n\n // ── Poll for URL changes — reliable with any SPA router ──────\n // Frameworks like VitePress/Vue Router may cache history.pushState before\n // our bridge loads, so prototype overrides are unreliable. Polling\n // location.pathname directly is simple and always works.\n setInterval(() => {\n checkForNavigation()\n }, 250)\n\n // ── Catch back/forward navigation ────────────────────────────\n globalThis.addEventListener('popstate', () => {\n checkForNavigation()\n })\n\n // ── Block external link clicks (cross-origin / full page reloads) ──\n const handleLinkClick = (e: MouseEvent) => {\n const link = (e.target as HTMLElement).closest?.('a[href]') as HTMLAnchorElement | null\n if (!link) return\n\n const href = link.getAttribute('href')\n if (!href || href.startsWith('#') || href === 'javascript:void(0)') return\n\n // Check if this is an external link (different origin)\n try {\n const resolved = new URL(href, globalThis.location.href)\n if (resolved.origin !== globalThis.location.origin) {\n // Block external navigation — can't leave the iframe\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n }\n // Same-origin links: let the SPA router handle them.\n // The History API override above will detect the navigation.\n } catch {\n // Malformed URL — block it\n e.preventDefault()\n }\n }\n\n // Register on window first to beat VitePress's capture-phase handler\n globalThis.addEventListener('click', handleLinkClick, true)\n document.addEventListener('click', handleLinkClick, true)\n\n // Intercept form submissions\n document.addEventListener('submit', (e: Event) => {\n e.preventDefault()\n e.stopPropagation()\n }, true)\n\n // Block actual page reloads (location.href = ..., etc.)\n window.addEventListener('beforeunload', (e: BeforeUnloadEvent) => {\n e.preventDefault()\n })\n}\n\n/**\n * Check if the iframe pathname has changed and notify the portal.\n * Also re-initializes the editor bridge for the new page.\n */\nfunction checkForNavigation() {\n const currentPathname = globalThis.location.pathname\n if (currentPathname === lastKnownPathname) return\n\n lastKnownPathname = currentPathname\n\n // Notify parent about the navigation\n postToParent('dcs:navigation', { pathname: currentPathname })\n\n // Clean up current edit state\n if (activeEditElement) {\n finishEdit(activeEditElement)\n }\n removeEditIcon()\n removeArrayEditIcon()\n removeReviewEditIcon()\n\n // Reset blog initialization flags so new page elements get listeners\n blogContentInitialized = false\n blogMetadataInitialized = false\n\n // Wait for the SPA router to finish rendering the new page,\n // then re-discover sections and text keys\n setTimeout(() => {\n rediscoverAndNotify()\n }, 500)\n}\n\n// ─── Mode Management ─────────────────────────────────────────────\n\n/**\n * Set the current interaction mode. Both modes allow normal browsing —\n * inline editing works via double-click regardless of mode.\n * Switching to explore finishes any active inline edit.\n */\nfunction setMode(mode: 'edit' | 'explore') {\n if (mode === 'explore') {\n // When explicitly switching to explore, finish any active inline edit\n if (activeEditElement) {\n finishEdit(activeEditElement)\n }\n }\n}\n\n// ─── Inline Text Editing ─────────────────────────────────────────\n\n/**\n * Re-discover sections and text keys after an HMR update and\n * notify the parent portal so the overlay layer updates.\n */\nfunction rediscoverAndNotify() {\n activeEditElement = null\n\n const sections = discoverSections()\n const textKeys = discoverTextKeys()\n const reviewKeys = discoverReviewKeys()\n const managedFormIds = discoverManagedFormIds()\n const contentHeight = measureContentHeight(sections)\n\n // Skip re-report if height hasn't meaningfully changed (< 2px drift)\n // AND sections/textKeys are the same count — avoids portal re-render churn.\n if (Math.abs(contentHeight - lastReportedHeight) <= 2) {\n // Height is stable — still report in case sections/textKeys changed\n }\n lastReportedHeight = contentHeight\n\n // Detect blog content element\n const blogEl = document.querySelector<HTMLElement>('[data-blog-content]')\n const hasBlogContent = !!blogEl\n const blogContentHtml = blogEl ? blogEl.innerHTML : null\n\n // Detect blog metadata elements\n const blogTitleEl = document.querySelector<HTMLElement>('[data-blog-title]')\n const blogSummaryEl = document.querySelector<HTMLElement>('[data-blog-summary]')\n const blogTitle = blogTitleEl ? blogTitleEl.textContent?.trim() ?? null : null\n const blogSummary = blogSummaryEl ? blogSummaryEl.textContent?.trim() ?? null : null\n\n // Always report — sections/textKeys may have changed even if height is stable\n postToParent('dcs:ready', {\n sections,\n textKeys,\n contentHeight,\n hasBlogContent,\n blogContentHtml,\n blogTitle,\n blogSummary,\n hasManagedForms: managedFormIds.length > 0,\n managedFormIds,\n hasReviews: reviewKeys.length > 0,\n reviewKeys,\n } satisfies EditorReadyPayload)\n\n // Apply editor features to any new elements (e.g., after HMR or navigation)\n // without re-creating the ResizeObserver (which would cause an infinite loop)\n if (editorActive) {\n applyEditorToElements()\n\n // Observe any new section elements that appeared after navigation/HMR.\n // ResizeObserver.observe() is idempotent — safe to call on already-observed elements.\n if (sectionResizeObserver) {\n document.querySelectorAll<HTMLElement>('[data-section]').forEach(el => {\n sectionResizeObserver!.observe(el)\n })\n }\n }\n}\n\nfunction enableEditorMode() {\n if (editorActive) return\n editorActive = true\n\n // Inject editor styles\n injectEditorStyles()\n\n // Apply editor features (classes + listeners) to all current elements\n applyEditorToElements()\n}\n\n/**\n * Apply editor features (classes + event listeners) to elements on the page.\n * Uses WeakSets to track which elements have already been initialized,\n * preventing duplicate event listeners on repeated calls (e.g., after HMR\n * updates or layout-triggered re-discovery).\n */\nfunction applyEditorToElements() {\n // Set up all text key elements — double-click to start editing\n const textElements = document.querySelectorAll<HTMLElement>(TEXT_KEY_SELECTOR)\n textElements.forEach((htmlEl) => {\n // Always ensure the class is present (may have been removed by set-editing-enabled)\n htmlEl.classList.add('dcs-editable')\n\n // Only add event listeners once per element\n if (initializedTextElements.has(htmlEl)) return\n initializedTextElements.add(htmlEl)\n\n // Double-click to start inline editing (signals parent to switch to edit mode)\n htmlEl.addEventListener('dblclick', handleTextKeyDblClick)\n // Blur to finish editing\n htmlEl.addEventListener('blur', handleTextKeyBlur)\n // Keyboard: Enter to finish, Escape to cancel\n htmlEl.addEventListener('keydown', handleTextKeyKeydown)\n\n // Show/hide the floating edit icon on hover (T icon for regular text, List icon for array keys)\n htmlEl.addEventListener('mouseenter', () => {\n // Don't show icon while actively editing this or another element\n if (activeEditElement) return\n const key = getTextKey(htmlEl)\n if (isArrayTextKey(key)) {\n showArrayEditIcon(htmlEl)\n } else {\n showEditIcon(htmlEl)\n }\n })\n htmlEl.addEventListener('mouseleave', (e: MouseEvent) => {\n // Don't hide if moving to the edit icon or array edit icon\n const related = e.relatedTarget as HTMLElement | null\n if (related && (\n related.classList?.contains('dcs-edit-icon') || related.closest?.('.dcs-edit-icon') ||\n related.classList?.contains('dcs-array-edit-icon') || related.closest?.('.dcs-array-edit-icon')\n )) return\n removeEditIcon()\n removeArrayEditIcon()\n })\n })\n\n // Set up review sections — hover to show the floating reviews icon\n const reviewElements = document.querySelectorAll<HTMLElement>(REVIEW_SELECTOR)\n reviewElements.forEach((htmlEl) => {\n if (initializedReviewElements.has(htmlEl)) return\n initializedReviewElements.add(htmlEl)\n\n htmlEl.addEventListener('mouseenter', () => {\n // Don't show icon while actively editing text\n if (activeEditElement) return\n showReviewEditIcon(htmlEl)\n })\n htmlEl.addEventListener('mouseleave', (e: MouseEvent) => {\n // Don't hide if moving to the review edit icon\n const related = e.relatedTarget as HTMLElement | null\n if (related && (\n related.classList?.contains('dcs-review-edit-icon') || related.closest?.('.dcs-review-edit-icon')\n )) return\n removeReviewEditIcon()\n })\n })\n\n const managedFormElements = document.querySelectorAll<HTMLElement>('[data-form-key]')\n managedFormElements.forEach((formEl) => {\n formEl.classList.add('dcs-managed-form')\n\n if (!initializedManagedFormElements.has(formEl)) {\n initializedManagedFormElements.add(formEl)\n\n const openManagedForm = (e: Event) => {\n if (!editingEnabled) return\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n postToParent('dcs:managed-form-click', {\n formId: formEl.dataset.formKey ?? null,\n })\n }\n\n formEl.addEventListener('click', openManagedForm, true)\n formEl.addEventListener('dblclick', openManagedForm, true)\n formEl.addEventListener('mouseenter', () => {\n if (activeEditElement) return\n showManagedFormEditIcon(formEl)\n })\n formEl.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n if (related && (\n related.classList?.contains('dcs-managed-form-edit-icon') || related.closest?.('.dcs-managed-form-edit-icon')\n )) return\n removeManagedFormEditIcon()\n })\n }\n })\n\n // Set up section hover reporting (portal overlay handles the visual treatment)\n const sections = document.querySelectorAll<HTMLElement>('[data-section]')\n sections.forEach((el) => {\n if (initializedSections.has(el)) return\n initializedSections.add(el)\n\n const sectionId = el.dataset.section!\n\n el.addEventListener('mouseenter', () => {\n postToParent('dcs:section-hover', { sectionId })\n })\n el.addEventListener('mouseleave', () => {\n postToParent('dcs:section-hover', { sectionId: null })\n // Delayed cleanup of image icons — allows cursor to reach the icons\n setTimeout(() => {\n if (!isAnyImageIconHovered()) {\n removeAllImageEditIcons()\n }\n }, 100)\n })\n el.addEventListener('click', (e) => {\n // Only fire section click if not clicking on a text-key element\n const target = e.target as HTMLElement\n if (!target.closest(TEXT_KEY_SELECTOR)) {\n postToParent('dcs:section-click', { sectionId })\n }\n })\n\n // Image hover detection — detect when cursor is over an <img> inside a section.\n // Uses mousemove on the section because some images (e.g., inside sliders)\n // have pointer-events: none and cannot receive mouseenter directly.\n // For overlapping images (before/after sliders), show icons for ALL images\n // at the cursor position so each one is independently replaceable.\n el.addEventListener('mousemove', (e: MouseEvent) => {\n if (!editorActive || !editingEnabled || activeEditElement) {\n removeAllImageEditIcons()\n return\n }\n\n const allImages = findAllImagesAtPoint(el, e.clientX, e.clientY)\n if (allImages.length > 0) {\n showImageEditIcons(allImages)\n } else if (imageEditIconTargets.length > 0 && !isAnyImageIconHovered()) {\n removeAllImageEditIcons()\n }\n })\n })\n\n // Set up blog content area — click or double-click to open blog content editor\n if (!blogContentInitialized) {\n const blogContentEl = document.querySelector<HTMLElement>('[data-blog-content]')\n if (blogContentEl) {\n blogContentInitialized = true\n blogContentEl.classList.add('dcs-blog-content')\n blogContentEl.addEventListener('click', (e) => {\n // Don't trigger blog content click if clicking on a text-key element\n const target = e.target as HTMLElement\n if (target.closest(TEXT_KEY_SELECTOR)) return\n e.preventDefault()\n e.stopPropagation()\n postToParent('dcs:blog-content-click', { textKey: 'blog-content' })\n })\n // Double-click / double-tap also opens the blog content editor\n blogContentEl.addEventListener('dblclick', (e) => {\n const target = e.target as HTMLElement\n if (target.closest(TEXT_KEY_SELECTOR)) return\n e.preventDefault()\n e.stopPropagation()\n postToParent('dcs:blog-content-click', { textKey: 'blog-content' })\n })\n // Also send the current content to the parent for initial load\n postToParent('dcs:blog-content-ready', { content: blogContentEl.innerHTML })\n }\n }\n\n // Set up blog metadata elements — click to open blog metadata editor\n if (!blogMetadataInitialized) {\n const titleEl = document.querySelector<HTMLElement>('[data-blog-title]')\n const summaryEl = document.querySelector<HTMLElement>('[data-blog-summary]')\n if (titleEl || summaryEl) {\n blogMetadataInitialized = true\n const handler = (e: Event) => {\n e.preventDefault()\n e.stopPropagation()\n postToParent('dcs:blog-metadata-click', {\n title: titleEl?.textContent?.trim() ?? null,\n summary: summaryEl?.textContent?.trim() ?? null,\n })\n }\n titleEl?.addEventListener('click', handler)\n titleEl?.addEventListener('dblclick', handler)\n summaryEl?.addEventListener('click', handler)\n summaryEl?.addEventListener('dblclick', handler)\n // Add visual cue styles\n titleEl?.classList.add('dcs-blog-metadata')\n summaryEl?.classList.add('dcs-blog-metadata')\n }\n }\n\n}\n\n/**\n * Handle double-click on a text key element.\n * Notifies the parent portal to switch to edit mode and begins inline editing.\n */\nfunction handleTextKeyDblClick(e: Event) {\n if (!editingEnabled) return\n\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n const el = e.currentTarget as HTMLElement\n const key = getTextKey(el)\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n\n // Always notify the parent to switch to edit mode when starting inline editing.\n // In explore mode this triggers the mode switch; in edit mode it's a no-op on the parent side.\n postToParent('dcs:text-key-dblclick', { key, sectionId })\n\n // Start inline editing\n startInlineEdit(el, key, sectionId)\n}\n\n/**\n * Start inline contenteditable editing on a text key element.\n * Called by double-click or when the parent sends dcs:select-text-key.\n */\nfunction startInlineEdit(el: HTMLElement, key: string, sectionId: string | null) {\n // Finish any active edit first\n if (activeEditElement && activeEditElement !== el) {\n finishEdit(activeEditElement)\n }\n\n // Store original text for change detection on finish\n originalTextValues.set(el, el.innerText.trim())\n\n // Enable contenteditable\n el.setAttribute('contenteditable', 'true')\n el.classList.add('dcs-editing')\n el.focus()\n activeEditElement = el\n\n // Select all text for easy replacement\n const selection = getSelection()\n const range = document.createRange()\n range.selectNodeContents(el)\n selection?.removeAllRanges()\n selection?.addRange(range)\n\n postToParent('dcs:text-key-click', { key, sectionId })\n}\n\nfunction handleTextKeyBlur(e: Event) {\n const el = e.currentTarget as HTMLElement\n finishEdit(el)\n}\n\nfunction handleTextKeyKeydown(e: KeyboardEvent) {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n ;(e.currentTarget as HTMLElement).blur()\n }\n if (e.key === 'Escape') {\n e.preventDefault()\n ;(e.currentTarget as HTMLElement).blur()\n }\n}\n\nfunction finishEdit(el: HTMLElement) {\n if (!el.hasAttribute('contenteditable')) return\n\n el.removeAttribute('contenteditable')\n el.classList.remove('dcs-editing')\n\n const key = getTextKey(el)\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n const newValue = el.innerText.trim()\n const originalValue = originalTextValues.get(el) ?? ''\n\n if (activeEditElement === el) {\n activeEditElement = null\n }\n\n // Only report to the portal when text was actually modified\n if (newValue !== originalValue) {\n postToParent('dcs:text-key-changed', { key, value: newValue, sectionId })\n }\n\n originalTextValues.delete(el)\n}\n\nfunction focusTextKey(key: string) {\n const el = document.querySelector<HTMLElement>(textKeySelector(key))\n if (!el) return\n el.scrollIntoView({ behavior: 'smooth', block: 'center' })\n\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n\n // Start inline editing directly\n startInlineEdit(el, key, sectionId)\n}\n\nfunction updateTextInPlace(key: string, value: string) {\n const el = document.querySelector<HTMLElement>(textKeySelector(key))\n if (!el) return\n // Don't update if we're currently editing this element\n if (el === activeEditElement) return\n el.innerText = value\n}\n\n// ─── Edit Icon (Floating Text Cursor Button) ────────────────────\n\n/**\n * Create the floating edit icon element. This small button appears\n * in the top-left corner of a hovered [data-text-key] or [data-dcs-text] element,\n * providing a click target to start inline editing without triggering\n * navigation (important for text inside buttons/links).\n */\nfunction createEditIcon(): HTMLElement {\n const icon = document.createElement('button')\n icon.className = 'dcs-edit-icon'\n icon.setAttribute('type', 'button')\n icon.setAttribute('title', 'Edit text')\n icon.setAttribute('aria-label', 'Edit text inline')\n // SVG: Type icon (text cursor) — matches lucide \"Type\" icon\n icon.innerHTML = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"4 7 4 4 20 4 20 7\"/><line x1=\"9\" y1=\"20\" x2=\"15\" y2=\"20\"/><line x1=\"12\" y1=\"4\" x2=\"12\" y2=\"20\"/></svg>`\n\n icon.addEventListener('click', (e) => {\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n if (!editIconTarget) return\n const el = editIconTarget\n const key = getTextKey(el)\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n\n // Signal parent to switch to edit mode\n postToParent('dcs:text-key-dblclick', { key, sectionId })\n // Start inline editing\n startInlineEdit(el, key, sectionId)\n // Hide the icon while editing\n removeEditIcon()\n }, true)\n\n // Hide icon when mouse leaves it (and isn't going to the target element)\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n if (related && editIconTarget && (related === editIconTarget || editIconTarget.contains(related))) return\n removeEditIcon()\n })\n\n return icon\n}\n\n/**\n * Show the edit icon positioned at the top-left corner of the given element.\n */\nfunction showEditIcon(el: HTMLElement) {\n if (!editingEnabled) return\n\n if (!editIconElement) {\n editIconElement = createEditIcon()\n document.body.appendChild(editIconElement)\n }\n\n editIconTarget = el\n\n // Position relative to the element's bounding box\n const rect = el.getBoundingClientRect()\n editIconElement.style.top = `${rect.top + scrollY - 4}px`\n editIconElement.style.left = `${rect.left + scrollX - 4}px`\n editIconElement.style.display = 'flex'\n}\n\n/**\n * Hide and detach the edit icon.\n */\nfunction removeEditIcon() {\n if (editIconElement) {\n editIconElement.style.display = 'none'\n }\n editIconTarget = null\n}\n\n// ─── Array Key Detection ─────────────────────────────────────────\n\n/**\n * Check if a text key is part of an array pattern (e.g., \"conditions.0.title\").\n * Array text keys contain an indexed segment — either:\n * - Pure numeric: `baseKey.N.field` (e.g., `conditions.0.title`)\n * - Hyphenated index: `baseKey.prefix-N.field` (e.g., `steps.step-0.title`)\n */\nfunction isArrayTextKey(key: string): boolean {\n // Match pure numeric segment (.0.) or hyphenated index (.prefix-0.)\n return /\\.\\d+\\./.test(key) || /\\.\\w+-\\d+\\./.test(key)\n}\n\n/**\n * Extract the base array key from an array text key.\n * - `conditions.0.title` → `conditions`\n * - `steps.step-0.title` → `steps.step`\n */\nfunction getArrayBaseKey(key: string): string | null {\n // Try hyphenated pattern first (more specific)\n const hyphenMatch = key.match(/^(.+?)-\\d+\\./)\n if (hyphenMatch) return hyphenMatch[1]\n // Fall back to pure numeric pattern\n const numMatch = key.match(/^(.+?)\\.\\d+\\./)\n return numMatch ? numMatch[1] : null\n}\n\n// ─── Array Edit Icon (Floating List Button) ──────────────────────\n\n/**\n * Create the floating array edit icon element. This small button appears\n * in the top-left corner of a hovered [data-text-key] or [data-dcs-text] element whose key\n * matches an array pattern. Clicking it sends dcs:array-key-click to the\n * portal to open the array editor sheet.\n */\nfunction createArrayEditIcon(): HTMLElement {\n const icon = document.createElement('button')\n icon.className = 'dcs-array-edit-icon'\n icon.setAttribute('type', 'button')\n icon.setAttribute('title', 'Edit list items')\n icon.setAttribute('aria-label', 'Edit list items in panel')\n // SVG: List icon — matches lucide \"List\" icon\n icon.innerHTML = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"8\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"8\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"8\" y1=\"18\" x2=\"21\" y2=\"18\"/><line x1=\"3\" y1=\"6\" x2=\"3.01\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"3.01\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"3.01\" y2=\"18\"/></svg>`\n\n icon.addEventListener('click', (e) => {\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n if (!arrayEditIconTarget) return\n const el = arrayEditIconTarget\n const key = getTextKey(el)\n const arrayKey = getArrayBaseKey(key)\n if (!arrayKey) return\n\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n\n // Signal parent to open array editor sheet\n postToParent('dcs:array-key-click', { arrayKey, sectionId })\n // Hide the icon\n removeArrayEditIcon()\n }, true)\n\n // Hide icon when mouse leaves it (and isn't going to the target element)\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n if (related && arrayEditIconTarget && (related === arrayEditIconTarget || arrayEditIconTarget.contains(related))) return\n removeArrayEditIcon()\n })\n\n return icon\n}\n\n/**\n * Show the array edit icon positioned at the top-left corner of the given element.\n */\nfunction showArrayEditIcon(el: HTMLElement) {\n if (!editingEnabled) return\n\n if (!arrayEditIconElement) {\n arrayEditIconElement = createArrayEditIcon()\n document.body.appendChild(arrayEditIconElement)\n }\n\n arrayEditIconTarget = el\n\n // Position relative to the element's bounding box\n const rect = el.getBoundingClientRect()\n arrayEditIconElement.style.top = `${rect.top + scrollY - 4}px`\n arrayEditIconElement.style.left = `${rect.left + scrollX - 4}px`\n arrayEditIconElement.style.display = 'flex'\n}\n\n/**\n * Hide and detach the array edit icon.\n */\nfunction removeArrayEditIcon() {\n if (arrayEditIconElement) {\n arrayEditIconElement.style.display = 'none'\n }\n arrayEditIconTarget = null\n}\n\n// ─── Review Edit Icon (Floating Star Button) ──────────────────────\n\n/**\n * Create the floating review edit icon element. This small button appears\n * in the top-right corner of a hovered [data-dcs-reviews] element and\n * opens the reviews editor in the parent portal.\n */\nfunction createReviewEditIcon(): HTMLElement {\n const icon = document.createElement('button')\n icon.className = 'dcs-review-edit-icon'\n icon.setAttribute('type', 'button')\n icon.setAttribute('title', 'Edit Reviews')\n icon.setAttribute('aria-label', 'Edit reviews in panel')\n icon.textContent = '★'\n\n icon.addEventListener('click', (e) => {\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n if (!reviewEditIconTarget) return\n const el = reviewEditIconTarget\n const key = el.dataset.dcsReviews ?? ''\n if (!key) return\n\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n\n postToParent('dcs:reviews-click', { key, sectionId })\n removeReviewEditIcon()\n }, true)\n\n // Hide icon when mouse leaves it (and isn't going to the target element)\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n if (related && reviewEditIconTarget && (related === reviewEditIconTarget || reviewEditIconTarget.contains(related))) return\n removeReviewEditIcon()\n })\n\n return icon\n}\n\n/**\n * Show the review edit icon positioned at the top-right corner of the given element.\n */\nfunction showReviewEditIcon(el: HTMLElement) {\n if (!editingEnabled) return\n\n if (!reviewEditIconElement) {\n reviewEditIconElement = createReviewEditIcon()\n document.body.appendChild(reviewEditIconElement)\n }\n\n reviewEditIconTarget = el\n\n const rect = el.getBoundingClientRect()\n reviewEditIconElement.style.top = `${rect.top + scrollY - 4}px`\n reviewEditIconElement.style.left = `${rect.right + scrollX - 28}px`\n reviewEditIconElement.style.display = 'flex'\n}\n\n/**\n * Hide and detach the review edit icon.\n */\nfunction removeReviewEditIcon() {\n if (reviewEditIconElement) {\n reviewEditIconElement.style.display = 'none'\n }\n reviewEditIconTarget = null\n}\n\nfunction createManagedFormEditIcon(): HTMLElement {\n const icon = document.createElement('button')\n icon.className = 'dcs-managed-form-edit-icon'\n icon.setAttribute('type', 'button')\n icon.setAttribute('title', 'Manage form')\n icon.setAttribute('aria-label', 'Manage form fields')\n icon.innerHTML = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 6h18\"/><path d=\"M7 12h10\"/><path d=\"M10 18h4\"/>\n </svg>\n <span class=\"dcs-managed-form-edit-label\">Manage form</span>\n `\n\n icon.addEventListener('click', (e) => {\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n if (!managedFormEditIconTarget) return\n postToParent('dcs:managed-form-click', {\n formId: managedFormEditIconTarget.dataset.formKey ?? null,\n })\n removeManagedFormEditIcon()\n }, true)\n\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n if (related && managedFormEditIconTarget && (related === managedFormEditIconTarget || managedFormEditIconTarget.contains(related))) return\n removeManagedFormEditIcon()\n })\n\n return icon\n}\n\nfunction showManagedFormEditIcon(el: HTMLElement) {\n if (!editingEnabled) return\n\n if (!managedFormEditIconElement) {\n managedFormEditIconElement = createManagedFormEditIcon()\n document.body.appendChild(managedFormEditIconElement)\n }\n\n managedFormEditIconTarget = el\n\n const rect = el.getBoundingClientRect()\n managedFormEditIconElement.style.top = `${rect.top + scrollY + 8}px`\n managedFormEditIconElement.style.left = `${rect.left + scrollX + 8}px`\n managedFormEditIconElement.style.display = 'flex'\n}\n\nfunction removeManagedFormEditIcon() {\n if (managedFormEditIconElement) {\n managedFormEditIconElement.style.display = 'none'\n }\n managedFormEditIconTarget = null\n}\n\n// ─── Image Edit Icon (Floating Camera Button) ───────────────────\n\n/**\n * Create a floating image edit icon element for a specific image. This small\n * button appears in the top-right corner of a hovered image within a\n * [data-section] area, providing a click target to open image management —\n * essential for images inside interactive widgets (e.g., before/after sliders)\n * where right-click is intercepted by the widget.\n *\n * When multiple images overlap (e.g., before/after sliders), separate icons\n * are created for each image with labels to distinguish them.\n */\nfunction createImageEditIcon(label?: string): HTMLElement {\n const icon = document.createElement('button')\n icon.className = 'dcs-image-edit-icon'\n icon.setAttribute('type', 'button')\n icon.setAttribute('title', label ? `Replace ${label}` : 'Manage image')\n icon.setAttribute('aria-label', label ? `Replace ${label}` : 'Manage image')\n // SVG: Image icon — matches lucide \"Image\" icon\n const svgHtml = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/><path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\"/></svg>`\n icon.innerHTML = label ? `${svgHtml}<span class=\"dcs-image-edit-label\">${label}</span>` : svgHtml\n\n // Hide icon when mouse leaves it (and isn't going to the image)\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n // Keep icons visible if cursor moves back toward any tracked image\n if (related) {\n for (const target of imageEditIconTargets) {\n if (related === target || target.contains(related)) return\n }\n // Also keep visible if moving to another image icon\n if (related.classList?.contains('dcs-image-edit-icon') || related.closest?.('.dcs-image-edit-icon')) return\n }\n removeAllImageEditIcons()\n })\n\n return icon\n}\n\n/**\n * Show image edit icons for all provided images. When only one image is\n * present, a single icon appears at the top-right corner. When multiple\n * overlapping images are present (e.g., before/after sliders), separate\n * labeled icons appear stacked in the top-right corner.\n */\nfunction showImageEditIcons(images: HTMLImageElement[]) {\n if (!editingEnabled) return\n\n // Check if icons are already showing for these exact images\n if (imageEditIconTargets.length === images.length &&\n images.every((img, i) => imageEditIconTargets[i] === img)) return\n\n // Clear existing icons\n removeAllImageEditIcons()\n\n imageEditIconTargets = [...images]\n\n // Use the bounding box of the first image for positioning (they overlap anyway)\n const refRect = images[0].getBoundingClientRect()\n const hasMultiple = images.length > 1\n\n images.forEach((img, index) => {\n // Derive a label for multi-image scenarios from alt text\n let label: string | undefined\n if (hasMultiple) {\n const alt = img.alt ?? ''\n // Try to extract a meaningful label from alt text (e.g., \"Project — Before\" → \"Before\")\n const dashMatch = alt.match(/\\u2014\\s*(.+)$/) ?? alt.match(/-\\s*(.+)$/)\n label = dashMatch ? dashMatch[1].trim() : `Image ${index + 1}`\n }\n\n const iconEl = createImageEditIcon(label)\n\n // Attach click handler with the specific image reference\n iconEl.addEventListener('click', (e) => {\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n const sectionEl = img.closest<HTMLElement>('[data-section]')\n postToParent('dcs:image-click', {\n imageUrl: img.currentSrc ?? img.src,\n imageAlt: img.alt ?? null,\n imageWidth: img.naturalWidth,\n imageHeight: img.naturalHeight,\n sectionId: sectionEl?.dataset.section ?? null,\n sectionLabel: sectionEl?.dataset.sectionLabel ?? null,\n })\n removeAllImageEditIcons()\n }, true)\n\n document.body.appendChild(iconEl)\n\n // Stack icons vertically from the top-right corner\n iconEl.style.top = `${refRect.top + scrollY + 4 + (index * 28)}px`\n iconEl.style.left = hasMultiple\n ? `${refRect.right + scrollX - iconEl.offsetWidth - 4}px`\n : `${refRect.right + scrollX - 28}px`\n iconEl.style.display = 'flex'\n\n // Re-measure after display for labeled icons with dynamic width\n if (hasMultiple) {\n requestAnimationFrame(() => {\n iconEl.style.left = `${refRect.right + scrollX - iconEl.offsetWidth - 4}px`\n })\n }\n\n imageEditIconElements.push(iconEl)\n })\n}\n\n/**\n * Check if any image edit icon is currently being hovered.\n */\nfunction isAnyImageIconHovered(): boolean {\n return imageEditIconElements.some(el => el.matches(':hover'))\n}\n\n/**\n * Hide and remove all image edit icons.\n */\nfunction removeAllImageEditIcons() {\n for (const el of imageEditIconElements) {\n el.remove()\n }\n imageEditIconElements = []\n imageEditIconTargets = []\n}\n\n// ─── Shared Image Detection ──────────────────────────────────────\n\n/**\n * Find ALL images at the given viewport coordinates within a section.\n * Returns images in DOM order (first = visually behind, last = on top).\n * Used for overlapping image scenarios like before/after sliders where\n * multiple <img> elements occupy the same bounding box area.\n */\nfunction findAllImagesAtPoint(sectionEl: HTMLElement, clientX: number, clientY: number): HTMLImageElement[] {\n const results: HTMLImageElement[] = []\n const images = sectionEl.querySelectorAll<HTMLImageElement>('img')\n for (const img of images) {\n // Skip tiny images (icons, spacers, SVG fallbacks)\n if (img.naturalWidth > 0 && img.naturalWidth < 50 && img.naturalHeight < 50) continue\n const rect = img.getBoundingClientRect()\n if (rect.width < 30 || rect.height < 30) continue\n\n if (\n clientX >= rect.left && clientX <= rect.right &&\n clientY >= rect.top && clientY <= rect.bottom\n ) {\n results.push(img)\n }\n }\n return results\n}\n\nfunction injectEditorStyles() {\n if (document.getElementById('dcs-editor-styles')) return\n\n const style = document.createElement('style')\n style.id = 'dcs-editor-styles'\n style.textContent = `\n [data-text-key].dcs-editable,\n [data-dcs-text].dcs-editable {\n cursor: text !important;\n border-radius: 4px;\n position: relative;\n }\n\n [data-text-key].dcs-editable:hover,\n [data-dcs-text].dcs-editable:hover {\n outline: 2px dashed hsl(221.2 83.2% 53.3% / 0.4) !important;\n outline-offset: 4px;\n background-color: hsl(221.2 83.2% 53.3% / 0.03);\n }\n\n [data-text-key].dcs-editing,\n [data-dcs-text].dcs-editing {\n outline: 2px solid hsl(221.2 83.2% 53.3%) !important;\n outline-offset: 4px;\n background-color: hsl(221.2 83.2% 53.3% / 0.06);\n min-height: 1em;\n }\n\n [data-text-key].dcs-editing:focus,\n [data-dcs-text].dcs-editing:focus {\n outline: 2px solid hsl(221.2 83.2% 53.3%) !important;\n outline-offset: 4px;\n }\n\n .dcs-section-highlight {\n pointer-events: none;\n }\n\n /* Floating edit icon button */\n .dcs-edit-icon {\n position: absolute;\n z-index: 99999;\n display: none;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n padding: 0;\n margin: 0;\n border: 1.5px solid hsl(221.2 83.2% 53.3%);\n border-radius: 4px;\n background: hsl(221.2 83.2% 53.3% / 0.1);\n backdrop-filter: blur(4px);\n color: hsl(221.2 83.2% 53.3%);\n cursor: pointer;\n pointer-events: auto;\n transition: background 0.15s, transform 0.1s;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n }\n\n .dcs-edit-icon:hover {\n background: hsl(221.2 83.2% 53.3% / 0.25);\n transform: scale(1.1);\n }\n\n .dcs-edit-icon svg {\n display: block;\n }\n\n /* Floating array edit icon button */\n .dcs-array-edit-icon {\n position: absolute;\n z-index: 99999;\n display: none;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n padding: 0;\n margin: 0;\n border: 1.5px solid hsl(271 91% 65%);\n border-radius: 4px;\n background: hsl(271 91% 65% / 0.1);\n backdrop-filter: blur(4px);\n color: hsl(271 91% 65%);\n cursor: pointer;\n pointer-events: auto;\n transition: background 0.15s, transform 0.1s;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n }\n\n .dcs-array-edit-icon:hover {\n background: hsl(271 91% 65% / 0.25);\n transform: scale(1.1);\n }\n\n .dcs-array-edit-icon svg {\n display: block;\n }\n\n /* Floating review edit icon button */\n .dcs-review-edit-icon {\n position: absolute;\n z-index: 99999;\n display: none;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n padding: 0;\n margin: 0;\n border: 1.5px solid #f59e0b;\n border-radius: 4px;\n background: rgb(245 158 11 / 0.1);\n backdrop-filter: blur(4px);\n color: #f59e0b;\n cursor: pointer;\n pointer-events: auto;\n transition: background 0.15s, transform 0.1s;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n font-size: 14px;\n line-height: 1;\n }\n\n .dcs-review-edit-icon:hover {\n background: rgb(245 158 11 / 0.25);\n transform: scale(1.1);\n }\n\n .dcs-managed-form-edit-icon {\n position: absolute;\n z-index: 99999;\n display: none;\n align-items: center;\n justify-content: center;\n gap: 4px;\n min-width: 24px;\n height: 24px;\n padding: 0 8px;\n margin: 0;\n border: 1.5px solid hsl(271 91% 65%);\n border-radius: 4px;\n background: hsl(271 91% 65% / 0.12);\n backdrop-filter: blur(4px);\n color: hsl(271 91% 65%);\n cursor: pointer;\n pointer-events: auto;\n transition: background 0.15s, transform 0.1s;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n font-size: 11px;\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n }\n\n .dcs-managed-form-edit-icon:hover {\n background: hsl(271 91% 65% / 0.24);\n transform: scale(1.03);\n }\n\n .dcs-managed-form-edit-icon svg {\n display: block;\n flex-shrink: 0;\n }\n\n .dcs-managed-form-edit-label {\n white-space: nowrap;\n }\n\n /* Floating image edit icon button — green to distinguish from text (blue) and array (violet) */\n .dcs-image-edit-icon {\n position: absolute;\n z-index: 99999;\n display: none;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n padding: 0 4px;\n margin: 0;\n border: 1.5px solid hsl(142 71% 45%);\n border-radius: 4px;\n background: hsl(142 71% 45% / 0.1);\n backdrop-filter: blur(4px);\n color: hsl(142 71% 45%);\n cursor: pointer;\n pointer-events: auto;\n transition: background 0.15s, transform 0.1s;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n gap: 2px;\n }\n\n .dcs-image-edit-icon:hover {\n background: hsl(142 71% 45% / 0.25);\n transform: scale(1.1);\n }\n\n .dcs-image-edit-icon svg {\n display: block;\n flex-shrink: 0;\n }\n\n /* Label shown inside image edit icon for multi-image scenarios (e.g., before/after) */\n .dcs-image-edit-label {\n font-size: 9px;\n font-weight: 700;\n letter-spacing: 0.03em;\n white-space: nowrap;\n margin-left: 2px;\n }\n\n /* Blog content area — click to open WYSIWYG editor */\n [data-blog-content].dcs-blog-content {\n cursor: pointer;\n border-radius: 8px;\n position: relative;\n transition: outline-color 0.2s, background-color 0.2s;\n }\n\n [data-blog-content].dcs-blog-content:hover {\n outline: 2px dashed hsl(142 71% 45% / 0.5) !important;\n outline-offset: 8px;\n background-color: hsl(142 71% 45% / 0.03);\n }\n\n [data-blog-content].dcs-blog-content::after {\n content: 'Click to edit blog content';\n position: absolute;\n top: -28px;\n left: 8px;\n font-size: 11px;\n font-weight: 600;\n color: hsl(142 71% 45%);\n background: hsl(142 71% 45% / 0.1);\n border: 1px solid hsl(142 71% 45% / 0.3);\n padding: 2px 8px;\n border-radius: 4px;\n opacity: 0;\n transition: opacity 0.2s;\n pointer-events: none;\n white-space: nowrap;\n }\n\n [data-blog-content].dcs-blog-content:hover::after {\n opacity: 1;\n }\n\n /* Managed form area — click to open the portal Form Manager */\n [data-form-key].dcs-managed-form {\n cursor: pointer;\n border-radius: 12px;\n position: relative;\n transition: outline-color 0.2s, background-color 0.2s;\n }\n\n [data-form-key].dcs-managed-form:hover {\n outline: 2px dashed hsl(271 91% 65% / 0.65) !important;\n outline-offset: 4px;\n background-color: hsl(271 91% 65% / 0.04);\n }\n\n /* Blog metadata (title / summary) — click to edit */\n .dcs-blog-metadata {\n cursor: pointer;\n border-radius: 4px;\n transition: outline-color 0.2s, background-color 0.2s;\n }\n .dcs-blog-metadata:hover {\n outline: 2px dashed hsl(217 91% 60% / 0.5) !important;\n outline-offset: 4px;\n background-color: hsl(217 91% 60% / 0.04);\n }\n `\n document.head.appendChild(style)\n}\n\n// ─── Inbound Message Handler ─────────────────────────────────────\n\nfunction handleMessage(event: MessageEvent) {\n // Validate message structure (origin is not checked because\n // the parent portal origin varies between dev and production)\n const msg = event.data\n if (!msg || typeof msg !== 'object' || typeof msg.type !== 'string') return\n if (!msg.type.startsWith('dcs:')) return\n\n const { type, data } = msg as { type: InboundMessageType; data: unknown }\n\n switch (type) {\n case 'dcs:init-editor':\n enableEditorMode()\n break\n\n case 'dcs:highlight-section':\n if (data && typeof data === 'object' && 'sectionId' in data) {\n showSectionHighlight((data as { sectionId: string }).sectionId)\n }\n break\n\n case 'dcs:clear-highlight':\n clearSectionHighlight()\n break\n\n case 'dcs:select-text-key':\n if (data && typeof data === 'object' && 'key' in data) {\n focusTextKey((data as { key: string }).key)\n }\n break\n\n case 'dcs:update-text':\n if (data && typeof data === 'object' && 'key' in data && 'value' in data) {\n const d = data as { key: string; value: string }\n updateTextInPlace(d.key, d.value)\n }\n break\n\n case 'dcs:set-mode':\n if (data && typeof data === 'object' && 'mode' in data) {\n setMode((data as { mode: 'edit' | 'explore' }).mode)\n }\n break\n\n case 'dcs:set-editing-enabled':\n if (data && typeof data === 'object' && 'enabled' in data) {\n editingEnabled = (data as { enabled: boolean }).enabled\n if (editingEnabled) {\n // Re-add dcs-editable class to text key elements\n document.querySelectorAll<HTMLElement>(TEXT_KEY_SELECTOR).forEach(el => {\n el.classList.add('dcs-editable')\n })\n } else {\n // Clean up: finish any active edit, remove edit icon, remove hover styles\n if (activeEditElement) {\n finishEdit(activeEditElement)\n }\n removeEditIcon()\n removeArrayEditIcon()\n removeReviewEditIcon()\n removeManagedFormEditIcon()\n removeAllImageEditIcons()\n // Remove dcs-editable class to disable hover outlines\n document.querySelectorAll<HTMLElement>('.dcs-editable').forEach(el => {\n el.classList.remove('dcs-editable')\n })\n }\n }\n break\n\n case 'dcs:update-blog-content':\n if (data && typeof data === 'object' && 'content' in data) {\n const blogEl = document.querySelector<HTMLElement>('[data-blog-content]')\n if (blogEl) {\n blogEl.innerHTML = (data as { content: string }).content\n // Trigger re-discovery so the portal overlay updates for any height changes\n scheduleRediscovery()\n }\n }\n break\n\n case 'dcs:update-reviews':\n if (data && typeof data === 'object' && 'key' in data) {\n const d = data as { key: string; reviews?: unknown }\n globalThis.dispatchEvent(new CustomEvent('dcs:reviews-updated', {\n detail: {\n key: d.key,\n reviews: Array.isArray(d.reviews) ? d.reviews : null,\n },\n }))\n scheduleRediscovery()\n }\n break\n }\n}\n\n/**\n * Inject a stylesheet that neutralizes viewport-relative min-heights.\n * In the visual editor iframe, the portal controls height from body.scrollHeight.\n * CSS rules like `min-height: 100vh` create a feedback loop:\n * iframe height → vh grows → body.scrollHeight grows → iframe height grows → …\n * Neutralizing these rules lets the content determine its natural height.\n */\nfunction injectEditorHeightFix() {\n if (document.getElementById('dcs-editor-height-fix')) return\n const style = document.createElement('style')\n style.id = 'dcs-editor-height-fix'\n style.textContent = `\n /* DCS Editor Bridge: Neutralize viewport-relative min-heights */\n .min-h-screen, .min-h-dvh, .min-h-svh,\n .min-h-\\\\[100vh\\\\], .min-h-\\\\[100dvh\\\\], .min-h-\\\\[100svh\\\\] {\n min-height: 0 !important;\n }\n `\n document.head.appendChild(style)\n}\n\n/** Last reported content height — used to avoid re-sending when stable */\nlet lastReportedHeight = 0\n\n/**\n * Measure the true content height, avoiding inflation from viewport-relative\n * CSS rules. Uses section bounds as the primary signal (immune to vh feedback),\n * falling back to body.scrollHeight (which is reliable once the editor height\n * fix stylesheet has been injected).\n */\nfunction measureContentHeight(sections: SectionInfo[]): number {\n // Primary: compute from section bounds — these reflect actual element positions\n // after the height-fix stylesheet has neutralized viewport-relative min-heights.\n let maxBottom = 0\n for (const s of sections) {\n const bottom = s.bounds.y + s.bounds.height\n if (bottom > maxBottom) maxBottom = bottom\n }\n const sectionHeight = maxBottom > 0 ? Math.ceil(maxBottom) : 0\n\n // Fallback: body.scrollHeight (reliable now that min-height: 100vh is neutralized)\n const bodyHeight = document.body.scrollHeight\n\n // Use whichever is larger — sections might miss elements without data-section,\n // but bodyHeight should be accurate with the injected CSS fix.\n return Math.max(sectionHeight, bodyHeight)\n}\n\nexport function initEditorBridge() {\n if (!isInIframe()) return // Not in iframe\n\n // Inject height fix CSS before any measurement\n injectEditorHeightFix()\n\n // Prevent duplicate listener registration on HMR re-execution\n if (!bridgeInitialized) {\n bridgeInitialized = true\n\n // Monitor navigation within the iframe — notify the portal of page changes\n monitorNavigation()\n\n // Forward right-click events to the portal so the context menu\n // works when right-clicking inside the iframe (cross-origin boundary\n // prevents the parent from receiving native contextmenu events).\n document.addEventListener('contextmenu', (e: MouseEvent) => {\n e.preventDefault()\n const target = e.target as HTMLElement\n const sectionEl = target.closest?.('[data-section]') as HTMLElement | null\n const formEl = target.closest?.('[data-form-key]') as HTMLElement | null\n\n // Detect if the right-click target is an image or inside a <picture> element\n const imgEl = target.tagName === 'IMG'\n ? target as HTMLImageElement\n : (target.closest?.('picture')?.querySelector('img') as HTMLImageElement | null)\n\n // Find ALL images at the click point (handles overlapping images like before/after sliders)\n let allImages: HTMLImageElement[] = []\n if (sectionEl) {\n allImages = findAllImagesAtPoint(sectionEl, e.clientX, e.clientY)\n }\n\n // If direct target detection found an image not in the position-based list, add it\n if (imgEl && !allImages.includes(imgEl)) {\n allImages.push(imgEl)\n }\n\n // Use the topmost image for backward-compatible single-image fields\n const topImage = allImages.length > 0 ? allImages[allImages.length - 1] : null\n\n postToParent('dcs:contextmenu', {\n x: e.clientX,\n y: e.clientY,\n sectionId: sectionEl?.dataset.section ?? null,\n sectionLabel: sectionEl?.dataset.sectionLabel ?? null,\n formId: formEl?.dataset.formKey ?? null,\n // Single image context (backward compatible) — topmost image at click point\n imageUrl: topImage?.currentSrc ?? topImage?.src ?? null,\n imageAlt: topImage?.alt ?? null,\n imageWidth: topImage?.naturalWidth ?? null,\n imageHeight: topImage?.naturalHeight ?? null,\n // All images at click point (for overlapping image scenarios like before/after sliders)\n images: allImages.length > 1\n ? allImages.map(img => ({\n imageUrl: img.currentSrc ?? img.src,\n imageAlt: img.alt ?? null,\n imageWidth: img.naturalWidth,\n imageHeight: img.naturalHeight,\n }))\n : null,\n })\n }, true)\n\n // Forward click events to the portal so it can dismiss context menus\n // when the user clicks inside the iframe.\n // NOTE: Left-clicking an image does NOT open image management. Users must\n // right-click → \"Replace Image\" or use the floating image icon (top-right\n // corner on hover). This prevents accidental sheet openings during normal\n // page interaction and slider/gallery dragging.\n document.addEventListener('click', () => {\n postToParent('dcs:click', {})\n }, true)\n\n // Listen for portal commands. Origin is not validated because\n // the portal origin varies between environments and all inbound\n // messages are type-checked before processing.\n // Origin cannot be pre-validated because the portal URL varies by environment.\n // All inbound messages are type-checked via the dcs: prefix guard.\n globalThis.self.addEventListener('message', handleMessage) // NOSONAR\n\n // Watch for VitePress / Vite HMR page updates. When the framework\n // replaces DOM content, our event listeners and AI buttons are lost.\n // Re-discover sections after each update so the portal overlay stays in sync.\n if (import.meta.hot) {\n import.meta.hot.on('vite:afterUpdate', () => {\n // Small delay for the framework to finish DOM updates\n setTimeout(rediscoverAndNotify, 300)\n })\n }\n }\n\n // Report available sections, text keys, and content height\n const sections = discoverSections()\n const textKeys = discoverTextKeys()\n const reviewKeys = discoverReviewKeys()\n const managedFormIds = discoverManagedFormIds()\n const contentHeight = measureContentHeight(sections)\n lastReportedHeight = contentHeight\n\n // Detect blog content element for the initial ready report\n const blogEl = document.querySelector<HTMLElement>('[data-blog-content]')\n\n // Detect blog metadata elements for the initial ready report\n const blogTitleEl = document.querySelector<HTMLElement>('[data-blog-title]')\n const blogSummaryEl = document.querySelector<HTMLElement>('[data-blog-summary]')\n\n postToParent('dcs:ready', {\n sections,\n textKeys,\n contentHeight,\n hasBlogContent: !!blogEl,\n blogContentHtml: blogEl ? blogEl.innerHTML : null,\n blogTitle: blogTitleEl ? blogTitleEl.textContent?.trim() ?? null : null,\n blogSummary: blogSummaryEl ? blogSummaryEl.textContent?.trim() ?? null : null,\n hasManagedForms: managedFormIds.length > 0,\n managedFormIds,\n hasReviews: reviewKeys.length > 0,\n reviewKeys,\n } satisfies EditorReadyPayload)\n\n // Observe layout changes (lazy images, dynamic content) and re-report bounds\n observeSectionLayout()\n}\n\n// NOTE: Auto-initialization is NOT done here. The Vite plugin's virtual module\n// (dcsEditorPlugin) handles initialization by importing and calling initEditorBridge().\n// Having auto-init as a side effect of the import caused double-initialization\n// (two dcs:ready messages on startup).\n"]}
1
+ {"version":3,"sources":["../../src/editor/editorBridge.ts"],"names":[],"mappings":"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","file":"editorBridge.js","sourcesContent":["/**\n * Editor Bridge — injected into customer site iframes by the DCS visual editor.\n *\n * Responsibilities:\n * 1. Report available sections and text keys to the parent portal\n * 2. Enable inline contenteditable editing on data-text-key/data-dcs-text elements (double-click)\n * 3. Render section overlay highlights on hover/select from parent\n * 4. Communicate all interactions back to the parent via postMessage\n * 5. Monitor navigation within the iframe and notify the parent\n * 6. Show floating edit icon on hover for text-key elements\n * 7. Show floating image icon on hover for images within sections\n *\n * Supports both legacy `data-text-key` and modern `data-dcs-text` attributes\n * for backward compatibility during the migration period.\n *\n * NOTE: AI ✨ buttons are rendered by the portal-side SectionOverlayLayer,\n * NOT by this bridge. The bridge only reports section/text key data and\n * handles inline text editing.\n *\n * This script runs inside the iframe (customer site). The parent portal\n * communicates via postMessage using the `dcs:*` message protocol.\n */\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport interface SectionInfo {\n id: string\n label: string | null\n bounds: { x: number; y: number; width: number; height: number }\n textKeyCount: number\n}\n\nexport interface EditorReadyPayload {\n sections: SectionInfo[]\n textKeys: string[]\n contentHeight: number\n /** Whether the page has a [data-blog-content] element (blog post page) */\n hasBlogContent: boolean\n /** Current innerHTML of the blog content element, if present */\n blogContentHtml: string | null\n /** Whether the page has a [data-event-content] or [data-rich-content=\"event\"] element */\n hasEventContent: boolean\n /** Current innerHTML of the event content element, if present */\n eventContentHtml: string | null\n /** Current blog post title text from [data-blog-title], if present */\n blogTitle: string | null\n /** Current blog post summary/description text from [data-blog-summary], if present */\n blogSummary: string | null\n /** Whether the page has any managed form roots */\n hasManagedForms: boolean\n /** Form IDs for all detected managed-form roots */\n managedFormIds: string[]\n /** Whether the page has any [data-dcs-reviews] elements */\n hasReviews: boolean\n /** Keys of all [data-dcs-reviews] elements found on the page */\n reviewKeys: string[]\n}\n\n// ─── Message Protocol ────────────────────────────────────────────\n\ntype InboundMessageType =\n | 'dcs:init-editor'\n | 'dcs:highlight-section'\n | 'dcs:clear-highlight'\n | 'dcs:select-text-key'\n | 'dcs:update-text'\n | 'dcs:set-mode'\n | 'dcs:set-editing-enabled'\n | 'dcs:update-blog-content'\n | 'dcs:update-reviews'\n\ntype OutboundMessageType =\n | 'dcs:ready'\n | 'dcs:text-key-click'\n | 'dcs:text-key-changed'\n | 'dcs:section-click'\n | 'dcs:section-hover'\n | 'dcs:text-key-dblclick'\n | 'dcs:contextmenu'\n | 'dcs:click'\n | 'dcs:navigation'\n | 'dcs:array-key-click'\n | 'dcs:image-click'\n | 'dcs:blog-content-click'\n | 'dcs:blog-content-ready'\n | 'dcs:blog-metadata-click'\n | 'dcs:managed-form-click'\n | 'dcs:reviews-click'\n\nfunction isInIframe(): boolean {\n try { return globalThis.self !== globalThis.self.parent } catch { return true }\n}\n\nfunction postToParent(type: OutboundMessageType, data: unknown) {\n if (!isInIframe()) return\n // Use '*' because the portal origin varies between dev and production\n // and this script doesn't know the parent origin at injection time.\n // Security: messages are validated by the portal's useIframeBridge.\n // Origin validated via dcs: prefix check on all messages\n globalThis.self.parent.postMessage({ type, data }, '*') // NOSONAR\n}\n\n// ─── Attribute Compatibility ─────────────────────────────────────\n\n/**\n * CSS selector that matches elements with either the legacy `data-text-key`\n * attribute or the modern `data-dcs-text` attribute. This ensures the editor\n * bridge works with both pre-migration and post-migration sites.\n */\nconst TEXT_KEY_SELECTOR = '[data-text-key], [data-dcs-text]'\nconst REVIEW_SELECTOR = '[data-dcs-reviews]'\nconst BLOG_CONTENT_SELECTOR = '[data-blog-content]'\nconst EVENT_CONTENT_SELECTOR = '[data-event-content], [data-rich-content=\"event\"]'\n\n/**\n * Get the text key value from an element, checking the modern `data-dcs-text`\n * attribute first, then falling back to the legacy `data-text-key`.\n */\nfunction getTextKey(el: HTMLElement): string {\n return el.dataset.dcsText ?? el.dataset.textKey ?? ''\n}\n\n/**\n * Build a CSS selector that matches a specific text key value\n * across both legacy and modern attribute names.\n */\nfunction textKeySelector(key: string): string {\n return `[data-text-key=\"${key}\"], [data-dcs-text=\"${key}\"]`\n}\n\n// ─── State ───────────────────────────────────────────────────────\n\nlet editorActive = false\nlet bridgeInitialized = false\nlet activeEditElement: HTMLElement | null = null\n/** The current pathname tracked for navigation detection */\nlet lastKnownPathname: string = ''\n/** The floating edit icon element shown on hover over text keys */\nlet editIconElement: HTMLElement | null = null\n/** The element the edit icon is currently attached to */\nlet editIconTarget: HTMLElement | null = null\n/** The floating edit icon for array/list text keys */\nlet arrayEditIconElement: HTMLElement | null = null\n/** The element the array edit icon is currently attached to */\nlet arrayEditIconTarget: HTMLElement | null = null\n/** The floating edit icons for image elements within sections (supports multiple for overlapping images) */\nlet imageEditIconElements: HTMLElement[] = []\n/** The image elements the image edit icons are currently positioned on */\nlet imageEditIconTargets: HTMLImageElement[] = []\n/** The floating edit icon for review sections */\nlet reviewEditIconElement: HTMLElement | null = null\n/** The review section element the review edit icon is currently attached to */\nlet reviewEditIconTarget: HTMLElement | null = null\n/** The floating manage-form button for managed form roots */\nlet managedFormEditIconElement: HTMLElement | null = null\n/** The managed form root the floating button is currently attached to */\nlet managedFormEditIconTarget: HTMLElement | null = null\n/** Whether editing features are enabled (disabled on unmanaged pages) */\nlet editingEnabled = true\n/** Original text values captured when inline editing starts, for change detection */\nconst originalTextValues = new Map<HTMLElement, string>()\n/** ResizeObserver for tracking section layout changes */\nlet sectionResizeObserver: ResizeObserver | null = null\n/** Debounce timer for re-discovery triggered by layout changes */\nlet rediscoveryTimer: ReturnType<typeof setTimeout> | null = null\n/** Track elements that have had editor event listeners attached (prevents duplicate listeners) */\nconst initializedTextElements = new WeakSet<HTMLElement>()\nconst initializedSections = new WeakSet<HTMLElement>()\nconst initializedReviewElements = new WeakSet<HTMLElement>()\nconst initializedManagedFormElements = new WeakSet<HTMLElement>()\nlet blogContentInitialized = false\nlet eventContentInitialized = false\nlet blogMetadataInitialized = false\n\n/** Debounced re-discovery — collapses rapid layout changes into one update */\nfunction scheduleRediscovery() {\n if (rediscoveryTimer) clearTimeout(rediscoveryTimer)\n rediscoveryTimer = setTimeout(() => {\n rediscoveryTimer = null\n rediscoverAndNotify()\n }, 300)\n}\n\n// ─── Section Discovery ───────────────────────────────────────────\n\nfunction discoverSections(): SectionInfo[] {\n const elements = document.querySelectorAll<HTMLElement>('[data-section]')\n return Array.from(elements).map((el) => {\n const rect = el.getBoundingClientRect()\n const textKeyCount = el.querySelectorAll(TEXT_KEY_SELECTOR).length\n return {\n id: el.dataset.section!,\n label: el.dataset.sectionLabel ?? null,\n bounds: {\n x: rect.left + scrollX,\n y: rect.top + scrollY,\n width: rect.width,\n height: rect.height,\n },\n textKeyCount,\n }\n })\n}\n\nfunction discoverTextKeys(): string[] {\n return Array.from(document.querySelectorAll<HTMLElement>(TEXT_KEY_SELECTOR)).map(\n (el) => getTextKey(el),\n )\n}\n\nfunction discoverReviewKeys(): string[] {\n return Array.from(document.querySelectorAll<HTMLElement>(REVIEW_SELECTOR))\n .map(el => el.dataset.dcsReviews ?? '')\n .filter(Boolean)\n}\n\nfunction discoverManagedFormIds(): string[] {\n return Array.from(document.querySelectorAll<HTMLElement>('[data-form-key]'))\n .map(el => el.dataset.formKey ?? '')\n .filter(Boolean)\n}\n\n// ─── Layout Change Observation ───────────────────────────────────\n\n/**\n * Watch for layout changes that affect section bounds and overall page height:\n * 1. ALL lazy-loaded images on the page — their load events shift subsequent sections\n * 2. ResizeObserver on the document body — detects overall page height changes\n * 3. ResizeObserver on section elements — detects any size change (font loading,\n * dynamic content expansion, CSS transitions, etc.)\n *\n * When changes are detected, we schedule a debounced re-discovery so the portal\n * overlay layer receives updated section bounds and content height that match\n * the live layout.\n */\nfunction observeSectionLayout() {\n // Clean up any previous observer (e.g., after SPA navigation)\n if (sectionResizeObserver) {\n sectionResizeObserver.disconnect()\n }\n\n // 1. Watch ALL lazy images on the page — not just inside sections.\n // Images outside sections (e.g., contact form, unmapped content) can still\n // push content down and change overall page height and section positions.\n const allImages = document.querySelectorAll<HTMLImageElement>('img')\n allImages.forEach(img => {\n if (!img.complete) {\n img.addEventListener('load', scheduleRediscovery, { once: true })\n img.addEventListener('error', scheduleRediscovery, { once: true })\n }\n })\n\n // 2. ResizeObserver on the document body + each section element.\n // Body observation catches overall page height changes from any source.\n if (typeof ResizeObserver !== 'undefined') {\n sectionResizeObserver = new ResizeObserver(scheduleRediscovery)\n // Observe body for overall height changes\n sectionResizeObserver.observe(document.body)\n // Observe each section for individual bound changes\n document.querySelectorAll<HTMLElement>('[data-section]').forEach(el => {\n sectionResizeObserver!.observe(el)\n })\n }\n}\n\n// ─── Section Overlay ─────────────────────────────────────────────\n\n/**\n * Section highlight is now handled entirely by the portal-side\n * SectionOverlayLayer rendered on top of the iframe. The bridge\n * only reports section bounds and hover events — no visual\n * overlays are rendered inside the iframe itself.\n */\nfunction showSectionHighlight(_sectionId: string) {\n // No-op: portal overlay layer handles section highlights\n}\n\nfunction clearSectionHighlight() {\n // No-op: portal overlay layer handles section highlights\n}\n\n// ─── Navigation Monitoring ───────────────────────────────────────\n\n/**\n * Monitor navigation within the iframe and notify the parent portal.\n * Instead of blocking all navigation, we allow SPA routing and inform\n * the portal when the iframe navigates to a different page. The portal\n * then updates its own URL and loads editing context for the new page.\n *\n * Strategy:\n * 1. Poll location.pathname on a short interval — this reliably detects\n * SPA navigation regardless of which router or framework is used,\n * since frameworks may cache History API references before our script loads.\n * 2. Block external/cross-origin link clicks (actual page reloads).\n * 3. Block form submissions.\n * 4. Monitor popstate for back/forward navigation.\n * 5. After navigation, re-discover sections and report them to the portal.\n */\nfunction monitorNavigation() {\n lastKnownPathname = globalThis.location.pathname\n\n // ── Poll for URL changes — reliable with any SPA router ──────\n // Frameworks like VitePress/Vue Router may cache history.pushState before\n // our bridge loads, so prototype overrides are unreliable. Polling\n // location.pathname directly is simple and always works.\n setInterval(() => {\n checkForNavigation()\n }, 250)\n\n // ── Catch back/forward navigation ────────────────────────────\n globalThis.addEventListener('popstate', () => {\n checkForNavigation()\n })\n\n // ── Block external link clicks (cross-origin / full page reloads) ──\n const handleLinkClick = (e: MouseEvent) => {\n const link = (e.target as HTMLElement).closest?.('a[href]') as HTMLAnchorElement | null\n if (!link) return\n\n const href = link.getAttribute('href')\n if (!href || href.startsWith('#') || href === 'javascript:void(0)') return\n\n // Check if this is an external link (different origin)\n try {\n const resolved = new URL(href, globalThis.location.href)\n if (resolved.origin !== globalThis.location.origin) {\n // Block external navigation — can't leave the iframe\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n }\n // Same-origin links: let the SPA router handle them.\n // The History API override above will detect the navigation.\n } catch {\n // Malformed URL — block it\n e.preventDefault()\n }\n }\n\n // Register on window first to beat VitePress's capture-phase handler\n globalThis.addEventListener('click', handleLinkClick, true)\n document.addEventListener('click', handleLinkClick, true)\n\n // Intercept form submissions\n document.addEventListener('submit', (e: Event) => {\n e.preventDefault()\n e.stopPropagation()\n }, true)\n\n // Block actual page reloads (location.href = ..., etc.)\n window.addEventListener('beforeunload', (e: BeforeUnloadEvent) => {\n e.preventDefault()\n })\n}\n\n/**\n * Check if the iframe pathname has changed and notify the portal.\n * Also re-initializes the editor bridge for the new page.\n */\nfunction checkForNavigation() {\n const currentPathname = globalThis.location.pathname\n if (currentPathname === lastKnownPathname) return\n\n lastKnownPathname = currentPathname\n\n // Notify parent about the navigation\n postToParent('dcs:navigation', { pathname: currentPathname })\n\n // Clean up current edit state\n if (activeEditElement) {\n finishEdit(activeEditElement)\n }\n removeEditIcon()\n removeArrayEditIcon()\n removeReviewEditIcon()\n\n // Reset blog initialization flags so new page elements get listeners\n blogContentInitialized = false\n eventContentInitialized = false\n blogMetadataInitialized = false\n\n // Wait for the SPA router to finish rendering the new page,\n // then re-discover sections and text keys\n setTimeout(() => {\n rediscoverAndNotify()\n }, 500)\n}\n\n// ─── Mode Management ─────────────────────────────────────────────\n\n/**\n * Set the current interaction mode. Both modes allow normal browsing —\n * inline editing works via double-click regardless of mode.\n * Switching to explore finishes any active inline edit.\n */\nfunction setMode(mode: 'edit' | 'explore') {\n if (mode === 'explore') {\n // When explicitly switching to explore, finish any active inline edit\n if (activeEditElement) {\n finishEdit(activeEditElement)\n }\n }\n}\n\n// ─── Inline Text Editing ─────────────────────────────────────────\n\n/**\n * Re-discover sections and text keys after an HMR update and\n * notify the parent portal so the overlay layer updates.\n */\nfunction rediscoverAndNotify() {\n activeEditElement = null\n\n const sections = discoverSections()\n const textKeys = discoverTextKeys()\n const reviewKeys = discoverReviewKeys()\n const managedFormIds = discoverManagedFormIds()\n const contentHeight = measureContentHeight(sections)\n\n // Skip re-report if height hasn't meaningfully changed (< 2px drift)\n // AND sections/textKeys are the same count — avoids portal re-render churn.\n if (Math.abs(contentHeight - lastReportedHeight) <= 2) {\n // Height is stable — still report in case sections/textKeys changed\n }\n lastReportedHeight = contentHeight\n\n // Detect rich content elements\n const blogEl = document.querySelector<HTMLElement>(BLOG_CONTENT_SELECTOR)\n const hasBlogContent = !!blogEl\n const blogContentHtml = blogEl ? blogEl.innerHTML : null\n const eventEl = document.querySelector<HTMLElement>(EVENT_CONTENT_SELECTOR)\n const hasEventContent = !!eventEl\n const eventContentHtml = eventEl ? eventEl.innerHTML : null\n\n // Detect blog metadata elements\n const blogTitleEl = document.querySelector<HTMLElement>('[data-blog-title]')\n const blogSummaryEl = document.querySelector<HTMLElement>('[data-blog-summary]')\n const blogTitle = blogTitleEl ? blogTitleEl.textContent?.trim() ?? null : null\n const blogSummary = blogSummaryEl ? blogSummaryEl.textContent?.trim() ?? null : null\n\n // Always report — sections/textKeys may have changed even if height is stable\n postToParent('dcs:ready', {\n sections,\n textKeys,\n contentHeight,\n hasBlogContent,\n blogContentHtml,\n hasEventContent,\n eventContentHtml,\n blogTitle,\n blogSummary,\n hasManagedForms: managedFormIds.length > 0,\n managedFormIds,\n hasReviews: reviewKeys.length > 0,\n reviewKeys,\n } satisfies EditorReadyPayload)\n\n // Apply editor features to any new elements (e.g., after HMR or navigation)\n // without re-creating the ResizeObserver (which would cause an infinite loop)\n if (editorActive) {\n applyEditorToElements()\n\n // Observe any new section elements that appeared after navigation/HMR.\n // ResizeObserver.observe() is idempotent — safe to call on already-observed elements.\n if (sectionResizeObserver) {\n document.querySelectorAll<HTMLElement>('[data-section]').forEach(el => {\n sectionResizeObserver!.observe(el)\n })\n }\n }\n}\n\nfunction enableEditorMode() {\n if (editorActive) return\n editorActive = true\n\n // Inject editor styles\n injectEditorStyles()\n\n // Apply editor features (classes + listeners) to all current elements\n applyEditorToElements()\n}\n\n/**\n * Apply editor features (classes + event listeners) to elements on the page.\n * Uses WeakSets to track which elements have already been initialized,\n * preventing duplicate event listeners on repeated calls (e.g., after HMR\n * updates or layout-triggered re-discovery).\n */\nfunction applyEditorToElements() {\n // Set up all text key elements — double-click to start editing\n const textElements = document.querySelectorAll<HTMLElement>(TEXT_KEY_SELECTOR)\n textElements.forEach((htmlEl) => {\n // Always ensure the class is present (may have been removed by set-editing-enabled)\n htmlEl.classList.add('dcs-editable')\n\n // Only add event listeners once per element\n if (initializedTextElements.has(htmlEl)) return\n initializedTextElements.add(htmlEl)\n\n // Double-click to start inline editing (signals parent to switch to edit mode)\n htmlEl.addEventListener('dblclick', handleTextKeyDblClick)\n // Blur to finish editing\n htmlEl.addEventListener('blur', handleTextKeyBlur)\n // Keyboard: Enter to finish, Escape to cancel\n htmlEl.addEventListener('keydown', handleTextKeyKeydown)\n\n // Show/hide the floating edit icon on hover (T icon for regular text, List icon for array keys)\n htmlEl.addEventListener('mouseenter', () => {\n // Don't show icon while actively editing this or another element\n if (activeEditElement) return\n const key = getTextKey(htmlEl)\n if (isArrayTextKey(key)) {\n showArrayEditIcon(htmlEl)\n } else {\n showEditIcon(htmlEl)\n }\n })\n htmlEl.addEventListener('mouseleave', (e: MouseEvent) => {\n // Don't hide if moving to the edit icon or array edit icon\n const related = e.relatedTarget as HTMLElement | null\n if (related && (\n related.classList?.contains('dcs-edit-icon') || related.closest?.('.dcs-edit-icon') ||\n related.classList?.contains('dcs-array-edit-icon') || related.closest?.('.dcs-array-edit-icon')\n )) return\n removeEditIcon()\n removeArrayEditIcon()\n })\n })\n\n // Set up review sections — hover to show the floating reviews icon\n const reviewElements = document.querySelectorAll<HTMLElement>(REVIEW_SELECTOR)\n reviewElements.forEach((htmlEl) => {\n if (initializedReviewElements.has(htmlEl)) return\n initializedReviewElements.add(htmlEl)\n\n htmlEl.addEventListener('mouseenter', () => {\n // Don't show icon while actively editing text\n if (activeEditElement) return\n showReviewEditIcon(htmlEl)\n })\n htmlEl.addEventListener('mouseleave', (e: MouseEvent) => {\n // Don't hide if moving to the review edit icon\n const related = e.relatedTarget as HTMLElement | null\n if (related && (\n related.classList?.contains('dcs-review-edit-icon') || related.closest?.('.dcs-review-edit-icon')\n )) return\n removeReviewEditIcon()\n })\n })\n\n const managedFormElements = document.querySelectorAll<HTMLElement>('[data-form-key]')\n managedFormElements.forEach((formEl) => {\n formEl.classList.add('dcs-managed-form')\n\n if (!initializedManagedFormElements.has(formEl)) {\n initializedManagedFormElements.add(formEl)\n\n const openManagedForm = (e: Event) => {\n if (!editingEnabled) return\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n postToParent('dcs:managed-form-click', {\n formId: formEl.dataset.formKey ?? null,\n })\n }\n\n formEl.addEventListener('click', openManagedForm, true)\n formEl.addEventListener('dblclick', openManagedForm, true)\n formEl.addEventListener('mouseenter', () => {\n if (activeEditElement) return\n showManagedFormEditIcon(formEl)\n })\n formEl.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n if (related && (\n related.classList?.contains('dcs-managed-form-edit-icon') || related.closest?.('.dcs-managed-form-edit-icon')\n )) return\n removeManagedFormEditIcon()\n })\n }\n })\n\n // Set up section hover reporting (portal overlay handles the visual treatment)\n const sections = document.querySelectorAll<HTMLElement>('[data-section]')\n sections.forEach((el) => {\n if (initializedSections.has(el)) return\n initializedSections.add(el)\n\n const sectionId = el.dataset.section!\n\n el.addEventListener('mouseenter', () => {\n postToParent('dcs:section-hover', { sectionId })\n })\n el.addEventListener('mouseleave', () => {\n postToParent('dcs:section-hover', { sectionId: null })\n // Delayed cleanup of image icons — allows cursor to reach the icons\n setTimeout(() => {\n if (!isAnyImageIconHovered()) {\n removeAllImageEditIcons()\n }\n }, 100)\n })\n el.addEventListener('click', (e) => {\n // Only fire section click if not clicking on a text-key element\n const target = e.target as HTMLElement\n if (!target.closest(TEXT_KEY_SELECTOR)) {\n postToParent('dcs:section-click', { sectionId })\n }\n })\n\n // Image hover detection — detect when cursor is over an <img> inside a section.\n // Uses mousemove on the section because some images (e.g., inside sliders)\n // have pointer-events: none and cannot receive mouseenter directly.\n // For overlapping images (before/after sliders), show icons for ALL images\n // at the cursor position so each one is independently replaceable.\n el.addEventListener('mousemove', (e: MouseEvent) => {\n if (!editorActive || !editingEnabled || activeEditElement) {\n removeAllImageEditIcons()\n return\n }\n\n const allImages = findAllImagesAtPoint(el, e.clientX, e.clientY)\n if (allImages.length > 0) {\n showImageEditIcons(allImages)\n } else if (imageEditIconTargets.length > 0 && !isAnyImageIconHovered()) {\n removeAllImageEditIcons()\n }\n })\n })\n\n // Set up blog content area — click or double-click to open blog content editor\n if (!blogContentInitialized) {\n const blogContentEl = document.querySelector<HTMLElement>(BLOG_CONTENT_SELECTOR)\n if (blogContentEl) {\n blogContentInitialized = true\n blogContentEl.classList.add('dcs-blog-content')\n blogContentEl.addEventListener('click', (e) => {\n // Don't trigger blog content click if clicking on a text-key element\n const target = e.target as HTMLElement\n if (target.closest(TEXT_KEY_SELECTOR)) return\n e.preventDefault()\n e.stopPropagation()\n postToParent('dcs:blog-content-click', { textKey: 'blog-content' })\n })\n // Double-click / double-tap also opens the blog content editor\n blogContentEl.addEventListener('dblclick', (e) => {\n const target = e.target as HTMLElement\n if (target.closest(TEXT_KEY_SELECTOR)) return\n e.preventDefault()\n e.stopPropagation()\n postToParent('dcs:blog-content-click', { textKey: 'blog-content' })\n })\n // Also send the current content to the parent for initial load\n postToParent('dcs:blog-content-ready', { content: blogContentEl.innerHTML })\n }\n }\n\n // Set up event content area using the same rich-content editor protocol.\n if (!eventContentInitialized) {\n const eventContentEl = document.querySelector<HTMLElement>(EVENT_CONTENT_SELECTOR)\n if (eventContentEl) {\n eventContentInitialized = true\n eventContentEl.classList.add('dcs-blog-content')\n eventContentEl.addEventListener('click', (e) => {\n const target = e.target as HTMLElement\n if (target.closest(TEXT_KEY_SELECTOR)) return\n e.preventDefault()\n e.stopPropagation()\n postToParent('dcs:blog-content-click', { textKey: 'event-content' })\n })\n eventContentEl.addEventListener('dblclick', (e) => {\n const target = e.target as HTMLElement\n if (target.closest(TEXT_KEY_SELECTOR)) return\n e.preventDefault()\n e.stopPropagation()\n postToParent('dcs:blog-content-click', { textKey: 'event-content' })\n })\n postToParent('dcs:blog-content-ready', { content: eventContentEl.innerHTML })\n }\n }\n\n // Set up blog metadata elements — click to open blog metadata editor\n if (!blogMetadataInitialized) {\n const titleEl = document.querySelector<HTMLElement>('[data-blog-title]')\n const summaryEl = document.querySelector<HTMLElement>('[data-blog-summary]')\n if (titleEl || summaryEl) {\n blogMetadataInitialized = true\n const handler = (e: Event) => {\n e.preventDefault()\n e.stopPropagation()\n postToParent('dcs:blog-metadata-click', {\n title: titleEl?.textContent?.trim() ?? null,\n summary: summaryEl?.textContent?.trim() ?? null,\n })\n }\n titleEl?.addEventListener('click', handler)\n titleEl?.addEventListener('dblclick', handler)\n summaryEl?.addEventListener('click', handler)\n summaryEl?.addEventListener('dblclick', handler)\n // Add visual cue styles\n titleEl?.classList.add('dcs-blog-metadata')\n summaryEl?.classList.add('dcs-blog-metadata')\n }\n }\n\n}\n\n/**\n * Handle double-click on a text key element.\n * Notifies the parent portal to switch to edit mode and begins inline editing.\n */\nfunction handleTextKeyDblClick(e: Event) {\n if (!editingEnabled) return\n\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n const el = e.currentTarget as HTMLElement\n const key = getTextKey(el)\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n\n // Always notify the parent to switch to edit mode when starting inline editing.\n // In explore mode this triggers the mode switch; in edit mode it's a no-op on the parent side.\n postToParent('dcs:text-key-dblclick', { key, sectionId })\n\n // Start inline editing\n startInlineEdit(el, key, sectionId)\n}\n\n/**\n * Start inline contenteditable editing on a text key element.\n * Called by double-click or when the parent sends dcs:select-text-key.\n */\nfunction startInlineEdit(el: HTMLElement, key: string, sectionId: string | null) {\n // Finish any active edit first\n if (activeEditElement && activeEditElement !== el) {\n finishEdit(activeEditElement)\n }\n\n // Store original text for change detection on finish\n originalTextValues.set(el, el.innerText.trim())\n\n // Enable contenteditable\n el.setAttribute('contenteditable', 'true')\n el.classList.add('dcs-editing')\n el.focus()\n activeEditElement = el\n\n // Select all text for easy replacement\n const selection = getSelection()\n const range = document.createRange()\n range.selectNodeContents(el)\n selection?.removeAllRanges()\n selection?.addRange(range)\n\n postToParent('dcs:text-key-click', { key, sectionId })\n}\n\nfunction handleTextKeyBlur(e: Event) {\n const el = e.currentTarget as HTMLElement\n finishEdit(el)\n}\n\nfunction handleTextKeyKeydown(e: KeyboardEvent) {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n ;(e.currentTarget as HTMLElement).blur()\n }\n if (e.key === 'Escape') {\n e.preventDefault()\n ;(e.currentTarget as HTMLElement).blur()\n }\n}\n\nfunction finishEdit(el: HTMLElement) {\n if (!el.hasAttribute('contenteditable')) return\n\n el.removeAttribute('contenteditable')\n el.classList.remove('dcs-editing')\n\n const key = getTextKey(el)\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n const newValue = el.innerText.trim()\n const originalValue = originalTextValues.get(el) ?? ''\n\n if (activeEditElement === el) {\n activeEditElement = null\n }\n\n // Only report to the portal when text was actually modified\n if (newValue !== originalValue) {\n postToParent('dcs:text-key-changed', { key, value: newValue, sectionId })\n }\n\n originalTextValues.delete(el)\n}\n\nfunction focusTextKey(key: string) {\n const el = document.querySelector<HTMLElement>(textKeySelector(key))\n if (!el) return\n el.scrollIntoView({ behavior: 'smooth', block: 'center' })\n\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n\n // Start inline editing directly\n startInlineEdit(el, key, sectionId)\n}\n\nfunction updateTextInPlace(key: string, value: string) {\n const el = document.querySelector<HTMLElement>(textKeySelector(key))\n if (!el) return\n // Don't update if we're currently editing this element\n if (el === activeEditElement) return\n el.innerText = value\n}\n\n// ─── Edit Icon (Floating Text Cursor Button) ────────────────────\n\n/**\n * Create the floating edit icon element. This small button appears\n * in the top-left corner of a hovered [data-text-key] or [data-dcs-text] element,\n * providing a click target to start inline editing without triggering\n * navigation (important for text inside buttons/links).\n */\nfunction createEditIcon(): HTMLElement {\n const icon = document.createElement('button')\n icon.className = 'dcs-edit-icon'\n icon.setAttribute('type', 'button')\n icon.setAttribute('title', 'Edit text')\n icon.setAttribute('aria-label', 'Edit text inline')\n // SVG: Type icon (text cursor) — matches lucide \"Type\" icon\n icon.innerHTML = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"4 7 4 4 20 4 20 7\"/><line x1=\"9\" y1=\"20\" x2=\"15\" y2=\"20\"/><line x1=\"12\" y1=\"4\" x2=\"12\" y2=\"20\"/></svg>`\n\n icon.addEventListener('click', (e) => {\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n if (!editIconTarget) return\n const el = editIconTarget\n const key = getTextKey(el)\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n\n // Signal parent to switch to edit mode\n postToParent('dcs:text-key-dblclick', { key, sectionId })\n // Start inline editing\n startInlineEdit(el, key, sectionId)\n // Hide the icon while editing\n removeEditIcon()\n }, true)\n\n // Hide icon when mouse leaves it (and isn't going to the target element)\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n if (related && editIconTarget && (related === editIconTarget || editIconTarget.contains(related))) return\n removeEditIcon()\n })\n\n return icon\n}\n\n/**\n * Show the edit icon positioned at the top-left corner of the given element.\n */\nfunction showEditIcon(el: HTMLElement) {\n if (!editingEnabled) return\n\n if (!editIconElement) {\n editIconElement = createEditIcon()\n document.body.appendChild(editIconElement)\n }\n\n editIconTarget = el\n\n // Position relative to the element's bounding box\n const rect = el.getBoundingClientRect()\n editIconElement.style.top = `${rect.top + scrollY - 4}px`\n editIconElement.style.left = `${rect.left + scrollX - 4}px`\n editIconElement.style.display = 'flex'\n}\n\n/**\n * Hide and detach the edit icon.\n */\nfunction removeEditIcon() {\n if (editIconElement) {\n editIconElement.style.display = 'none'\n }\n editIconTarget = null\n}\n\n// ─── Array Key Detection ─────────────────────────────────────────\n\n/**\n * Check if a text key is part of an array pattern (e.g., \"conditions.0.title\").\n * Array text keys contain an indexed segment — either:\n * - Pure numeric: `baseKey.N.field` (e.g., `conditions.0.title`)\n * - Hyphenated index: `baseKey.prefix-N.field` (e.g., `steps.step-0.title`)\n */\nfunction isArrayTextKey(key: string): boolean {\n // Match pure numeric segment (.0.) or hyphenated index (.prefix-0.)\n return /\\.\\d+\\./.test(key) || /\\.\\w+-\\d+\\./.test(key)\n}\n\n/**\n * Extract the base array key from an array text key.\n * - `conditions.0.title` → `conditions`\n * - `steps.step-0.title` → `steps.step`\n */\nfunction getArrayBaseKey(key: string): string | null {\n // Try hyphenated pattern first (more specific)\n const hyphenMatch = key.match(/^(.+?)-\\d+\\./)\n if (hyphenMatch) return hyphenMatch[1]\n // Fall back to pure numeric pattern\n const numMatch = key.match(/^(.+?)\\.\\d+\\./)\n return numMatch ? numMatch[1] : null\n}\n\n// ─── Array Edit Icon (Floating List Button) ──────────────────────\n\n/**\n * Create the floating array edit icon element. This small button appears\n * in the top-left corner of a hovered [data-text-key] or [data-dcs-text] element whose key\n * matches an array pattern. Clicking it sends dcs:array-key-click to the\n * portal to open the array editor sheet.\n */\nfunction createArrayEditIcon(): HTMLElement {\n const icon = document.createElement('button')\n icon.className = 'dcs-array-edit-icon'\n icon.setAttribute('type', 'button')\n icon.setAttribute('title', 'Edit list items')\n icon.setAttribute('aria-label', 'Edit list items in panel')\n // SVG: List icon — matches lucide \"List\" icon\n icon.innerHTML = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"8\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"8\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"8\" y1=\"18\" x2=\"21\" y2=\"18\"/><line x1=\"3\" y1=\"6\" x2=\"3.01\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"3.01\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"3.01\" y2=\"18\"/></svg>`\n\n icon.addEventListener('click', (e) => {\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n if (!arrayEditIconTarget) return\n const el = arrayEditIconTarget\n const key = getTextKey(el)\n const arrayKey = getArrayBaseKey(key)\n if (!arrayKey) return\n\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n\n // Signal parent to open array editor sheet\n postToParent('dcs:array-key-click', { arrayKey, sectionId })\n // Hide the icon\n removeArrayEditIcon()\n }, true)\n\n // Hide icon when mouse leaves it (and isn't going to the target element)\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n if (related && arrayEditIconTarget && (related === arrayEditIconTarget || arrayEditIconTarget.contains(related))) return\n removeArrayEditIcon()\n })\n\n return icon\n}\n\n/**\n * Show the array edit icon positioned at the top-left corner of the given element.\n */\nfunction showArrayEditIcon(el: HTMLElement) {\n if (!editingEnabled) return\n\n if (!arrayEditIconElement) {\n arrayEditIconElement = createArrayEditIcon()\n document.body.appendChild(arrayEditIconElement)\n }\n\n arrayEditIconTarget = el\n\n // Position relative to the element's bounding box\n const rect = el.getBoundingClientRect()\n arrayEditIconElement.style.top = `${rect.top + scrollY - 4}px`\n arrayEditIconElement.style.left = `${rect.left + scrollX - 4}px`\n arrayEditIconElement.style.display = 'flex'\n}\n\n/**\n * Hide and detach the array edit icon.\n */\nfunction removeArrayEditIcon() {\n if (arrayEditIconElement) {\n arrayEditIconElement.style.display = 'none'\n }\n arrayEditIconTarget = null\n}\n\n// ─── Review Edit Icon (Floating Star Button) ──────────────────────\n\n/**\n * Create the floating review edit icon element. This small button appears\n * in the top-right corner of a hovered [data-dcs-reviews] element and\n * opens the reviews editor in the parent portal.\n */\nfunction createReviewEditIcon(): HTMLElement {\n const icon = document.createElement('button')\n icon.className = 'dcs-review-edit-icon'\n icon.setAttribute('type', 'button')\n icon.setAttribute('title', 'Edit Reviews')\n icon.setAttribute('aria-label', 'Edit reviews in panel')\n icon.textContent = '★'\n\n icon.addEventListener('click', (e) => {\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n if (!reviewEditIconTarget) return\n const el = reviewEditIconTarget\n const key = el.dataset.dcsReviews ?? ''\n if (!key) return\n\n const sectionParent = el.closest<HTMLElement>('[data-section]')\n const sectionId = sectionParent?.dataset.section ?? null\n\n postToParent('dcs:reviews-click', { key, sectionId })\n removeReviewEditIcon()\n }, true)\n\n // Hide icon when mouse leaves it (and isn't going to the target element)\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n if (related && reviewEditIconTarget && (related === reviewEditIconTarget || reviewEditIconTarget.contains(related))) return\n removeReviewEditIcon()\n })\n\n return icon\n}\n\n/**\n * Show the review edit icon positioned at the top-right corner of the given element.\n */\nfunction showReviewEditIcon(el: HTMLElement) {\n if (!editingEnabled) return\n\n if (!reviewEditIconElement) {\n reviewEditIconElement = createReviewEditIcon()\n document.body.appendChild(reviewEditIconElement)\n }\n\n reviewEditIconTarget = el\n\n const rect = el.getBoundingClientRect()\n reviewEditIconElement.style.top = `${rect.top + scrollY - 4}px`\n reviewEditIconElement.style.left = `${rect.right + scrollX - 28}px`\n reviewEditIconElement.style.display = 'flex'\n}\n\n/**\n * Hide and detach the review edit icon.\n */\nfunction removeReviewEditIcon() {\n if (reviewEditIconElement) {\n reviewEditIconElement.style.display = 'none'\n }\n reviewEditIconTarget = null\n}\n\nfunction createManagedFormEditIcon(): HTMLElement {\n const icon = document.createElement('button')\n icon.className = 'dcs-managed-form-edit-icon'\n icon.setAttribute('type', 'button')\n icon.setAttribute('title', 'Manage form')\n icon.setAttribute('aria-label', 'Manage form fields')\n icon.innerHTML = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 6h18\"/><path d=\"M7 12h10\"/><path d=\"M10 18h4\"/>\n </svg>\n <span class=\"dcs-managed-form-edit-label\">Manage form</span>\n `\n\n icon.addEventListener('click', (e) => {\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n if (!managedFormEditIconTarget) return\n postToParent('dcs:managed-form-click', {\n formId: managedFormEditIconTarget.dataset.formKey ?? null,\n })\n removeManagedFormEditIcon()\n }, true)\n\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n if (related && managedFormEditIconTarget && (related === managedFormEditIconTarget || managedFormEditIconTarget.contains(related))) return\n removeManagedFormEditIcon()\n })\n\n return icon\n}\n\nfunction showManagedFormEditIcon(el: HTMLElement) {\n if (!editingEnabled) return\n\n if (!managedFormEditIconElement) {\n managedFormEditIconElement = createManagedFormEditIcon()\n document.body.appendChild(managedFormEditIconElement)\n }\n\n managedFormEditIconTarget = el\n\n const rect = el.getBoundingClientRect()\n managedFormEditIconElement.style.top = `${rect.top + scrollY + 8}px`\n managedFormEditIconElement.style.left = `${rect.left + scrollX + 8}px`\n managedFormEditIconElement.style.display = 'flex'\n}\n\nfunction removeManagedFormEditIcon() {\n if (managedFormEditIconElement) {\n managedFormEditIconElement.style.display = 'none'\n }\n managedFormEditIconTarget = null\n}\n\n// ─── Image Edit Icon (Floating Camera Button) ───────────────────\n\n/**\n * Create a floating image edit icon element for a specific image. This small\n * button appears in the top-right corner of a hovered image within a\n * [data-section] area, providing a click target to open image management —\n * essential for images inside interactive widgets (e.g., before/after sliders)\n * where right-click is intercepted by the widget.\n *\n * When multiple images overlap (e.g., before/after sliders), separate icons\n * are created for each image with labels to distinguish them.\n */\nfunction createImageEditIcon(label?: string): HTMLElement {\n const icon = document.createElement('button')\n icon.className = 'dcs-image-edit-icon'\n icon.setAttribute('type', 'button')\n icon.setAttribute('title', label ? `Replace ${label}` : 'Manage image')\n icon.setAttribute('aria-label', label ? `Replace ${label}` : 'Manage image')\n // SVG: Image icon — matches lucide \"Image\" icon\n const svgHtml = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/><path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\"/></svg>`\n icon.innerHTML = label ? `${svgHtml}<span class=\"dcs-image-edit-label\">${label}</span>` : svgHtml\n\n // Hide icon when mouse leaves it (and isn't going to the image)\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\n const related = e.relatedTarget as HTMLElement | null\n // Keep icons visible if cursor moves back toward any tracked image\n if (related) {\n for (const target of imageEditIconTargets) {\n if (related === target || target.contains(related)) return\n }\n // Also keep visible if moving to another image icon\n if (related.classList?.contains('dcs-image-edit-icon') || related.closest?.('.dcs-image-edit-icon')) return\n }\n removeAllImageEditIcons()\n })\n\n return icon\n}\n\n/**\n * Show image edit icons for all provided images. When only one image is\n * present, a single icon appears at the top-right corner. When multiple\n * overlapping images are present (e.g., before/after sliders), separate\n * labeled icons appear stacked in the top-right corner.\n */\nfunction showImageEditIcons(images: HTMLImageElement[]) {\n if (!editingEnabled) return\n\n // Check if icons are already showing for these exact images\n if (imageEditIconTargets.length === images.length &&\n images.every((img, i) => imageEditIconTargets[i] === img)) return\n\n // Clear existing icons\n removeAllImageEditIcons()\n\n imageEditIconTargets = [...images]\n\n // Use the bounding box of the first image for positioning (they overlap anyway)\n const refRect = images[0].getBoundingClientRect()\n const hasMultiple = images.length > 1\n\n images.forEach((img, index) => {\n // Derive a label for multi-image scenarios from alt text\n let label: string | undefined\n if (hasMultiple) {\n const alt = img.alt ?? ''\n // Try to extract a meaningful label from alt text (e.g., \"Project — Before\" → \"Before\")\n const dashMatch = alt.match(/\\u2014\\s*(.+)$/) ?? alt.match(/-\\s*(.+)$/)\n label = dashMatch ? dashMatch[1].trim() : `Image ${index + 1}`\n }\n\n const iconEl = createImageEditIcon(label)\n\n // Attach click handler with the specific image reference\n iconEl.addEventListener('click', (e) => {\n e.preventDefault()\n e.stopPropagation()\n e.stopImmediatePropagation()\n\n const sectionEl = img.closest<HTMLElement>('[data-section]')\n postToParent('dcs:image-click', {\n imageUrl: img.currentSrc ?? img.src,\n imageAlt: img.alt ?? null,\n imageWidth: img.naturalWidth,\n imageHeight: img.naturalHeight,\n sectionId: sectionEl?.dataset.section ?? null,\n sectionLabel: sectionEl?.dataset.sectionLabel ?? null,\n })\n removeAllImageEditIcons()\n }, true)\n\n document.body.appendChild(iconEl)\n\n // Stack icons vertically from the top-right corner\n iconEl.style.top = `${refRect.top + scrollY + 4 + (index * 28)}px`\n iconEl.style.left = hasMultiple\n ? `${refRect.right + scrollX - iconEl.offsetWidth - 4}px`\n : `${refRect.right + scrollX - 28}px`\n iconEl.style.display = 'flex'\n\n // Re-measure after display for labeled icons with dynamic width\n if (hasMultiple) {\n requestAnimationFrame(() => {\n iconEl.style.left = `${refRect.right + scrollX - iconEl.offsetWidth - 4}px`\n })\n }\n\n imageEditIconElements.push(iconEl)\n })\n}\n\n/**\n * Check if any image edit icon is currently being hovered.\n */\nfunction isAnyImageIconHovered(): boolean {\n return imageEditIconElements.some(el => el.matches(':hover'))\n}\n\n/**\n * Hide and remove all image edit icons.\n */\nfunction removeAllImageEditIcons() {\n for (const el of imageEditIconElements) {\n el.remove()\n }\n imageEditIconElements = []\n imageEditIconTargets = []\n}\n\n// ─── Shared Image Detection ──────────────────────────────────────\n\n/**\n * Find ALL images at the given viewport coordinates within a section.\n * Returns images in DOM order (first = visually behind, last = on top).\n * Used for overlapping image scenarios like before/after sliders where\n * multiple <img> elements occupy the same bounding box area.\n */\nfunction findAllImagesAtPoint(sectionEl: HTMLElement, clientX: number, clientY: number): HTMLImageElement[] {\n const results: HTMLImageElement[] = []\n const images = sectionEl.querySelectorAll<HTMLImageElement>('img')\n for (const img of images) {\n // Skip tiny images (icons, spacers, SVG fallbacks)\n if (img.naturalWidth > 0 && img.naturalWidth < 50 && img.naturalHeight < 50) continue\n const rect = img.getBoundingClientRect()\n if (rect.width < 30 || rect.height < 30) continue\n\n if (\n clientX >= rect.left && clientX <= rect.right &&\n clientY >= rect.top && clientY <= rect.bottom\n ) {\n results.push(img)\n }\n }\n return results\n}\n\nfunction injectEditorStyles() {\n if (document.getElementById('dcs-editor-styles')) return\n\n const style = document.createElement('style')\n style.id = 'dcs-editor-styles'\n style.textContent = `\n [data-text-key].dcs-editable,\n [data-dcs-text].dcs-editable {\n cursor: text !important;\n border-radius: 4px;\n position: relative;\n }\n\n [data-text-key].dcs-editable:hover,\n [data-dcs-text].dcs-editable:hover {\n outline: 2px dashed hsl(221.2 83.2% 53.3% / 0.4) !important;\n outline-offset: 4px;\n background-color: hsl(221.2 83.2% 53.3% / 0.03);\n }\n\n [data-text-key].dcs-editing,\n [data-dcs-text].dcs-editing {\n outline: 2px solid hsl(221.2 83.2% 53.3%) !important;\n outline-offset: 4px;\n background-color: hsl(221.2 83.2% 53.3% / 0.06);\n min-height: 1em;\n }\n\n [data-text-key].dcs-editing:focus,\n [data-dcs-text].dcs-editing:focus {\n outline: 2px solid hsl(221.2 83.2% 53.3%) !important;\n outline-offset: 4px;\n }\n\n .dcs-section-highlight {\n pointer-events: none;\n }\n\n /* Floating edit icon button */\n .dcs-edit-icon {\n position: absolute;\n z-index: 99999;\n display: none;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n padding: 0;\n margin: 0;\n border: 1.5px solid hsl(221.2 83.2% 53.3%);\n border-radius: 4px;\n background: hsl(221.2 83.2% 53.3% / 0.1);\n backdrop-filter: blur(4px);\n color: hsl(221.2 83.2% 53.3%);\n cursor: pointer;\n pointer-events: auto;\n transition: background 0.15s, transform 0.1s;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n }\n\n .dcs-edit-icon:hover {\n background: hsl(221.2 83.2% 53.3% / 0.25);\n transform: scale(1.1);\n }\n\n .dcs-edit-icon svg {\n display: block;\n }\n\n /* Floating array edit icon button */\n .dcs-array-edit-icon {\n position: absolute;\n z-index: 99999;\n display: none;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n padding: 0;\n margin: 0;\n border: 1.5px solid hsl(271 91% 65%);\n border-radius: 4px;\n background: hsl(271 91% 65% / 0.1);\n backdrop-filter: blur(4px);\n color: hsl(271 91% 65%);\n cursor: pointer;\n pointer-events: auto;\n transition: background 0.15s, transform 0.1s;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n }\n\n .dcs-array-edit-icon:hover {\n background: hsl(271 91% 65% / 0.25);\n transform: scale(1.1);\n }\n\n .dcs-array-edit-icon svg {\n display: block;\n }\n\n /* Floating review edit icon button */\n .dcs-review-edit-icon {\n position: absolute;\n z-index: 99999;\n display: none;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n padding: 0;\n margin: 0;\n border: 1.5px solid #f59e0b;\n border-radius: 4px;\n background: rgb(245 158 11 / 0.1);\n backdrop-filter: blur(4px);\n color: #f59e0b;\n cursor: pointer;\n pointer-events: auto;\n transition: background 0.15s, transform 0.1s;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n font-size: 14px;\n line-height: 1;\n }\n\n .dcs-review-edit-icon:hover {\n background: rgb(245 158 11 / 0.25);\n transform: scale(1.1);\n }\n\n .dcs-managed-form-edit-icon {\n position: absolute;\n z-index: 99999;\n display: none;\n align-items: center;\n justify-content: center;\n gap: 4px;\n min-width: 24px;\n height: 24px;\n padding: 0 8px;\n margin: 0;\n border: 1.5px solid hsl(271 91% 65%);\n border-radius: 4px;\n background: hsl(271 91% 65% / 0.12);\n backdrop-filter: blur(4px);\n color: hsl(271 91% 65%);\n cursor: pointer;\n pointer-events: auto;\n transition: background 0.15s, transform 0.1s;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n font-size: 11px;\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n }\n\n .dcs-managed-form-edit-icon:hover {\n background: hsl(271 91% 65% / 0.24);\n transform: scale(1.03);\n }\n\n .dcs-managed-form-edit-icon svg {\n display: block;\n flex-shrink: 0;\n }\n\n .dcs-managed-form-edit-label {\n white-space: nowrap;\n }\n\n /* Floating image edit icon button — green to distinguish from text (blue) and array (violet) */\n .dcs-image-edit-icon {\n position: absolute;\n z-index: 99999;\n display: none;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n padding: 0 4px;\n margin: 0;\n border: 1.5px solid hsl(142 71% 45%);\n border-radius: 4px;\n background: hsl(142 71% 45% / 0.1);\n backdrop-filter: blur(4px);\n color: hsl(142 71% 45%);\n cursor: pointer;\n pointer-events: auto;\n transition: background 0.15s, transform 0.1s;\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\n gap: 2px;\n }\n\n .dcs-image-edit-icon:hover {\n background: hsl(142 71% 45% / 0.25);\n transform: scale(1.1);\n }\n\n .dcs-image-edit-icon svg {\n display: block;\n flex-shrink: 0;\n }\n\n /* Label shown inside image edit icon for multi-image scenarios (e.g., before/after) */\n .dcs-image-edit-label {\n font-size: 9px;\n font-weight: 700;\n letter-spacing: 0.03em;\n white-space: nowrap;\n margin-left: 2px;\n }\n\n /* Blog content area — click to open WYSIWYG editor */\n [data-blog-content].dcs-blog-content {\n cursor: pointer;\n border-radius: 8px;\n position: relative;\n transition: outline-color 0.2s, background-color 0.2s;\n }\n\n [data-blog-content].dcs-blog-content:hover {\n outline: 2px dashed hsl(142 71% 45% / 0.5) !important;\n outline-offset: 8px;\n background-color: hsl(142 71% 45% / 0.03);\n }\n\n [data-blog-content].dcs-blog-content::after {\n content: 'Click to edit blog content';\n position: absolute;\n top: -28px;\n left: 8px;\n font-size: 11px;\n font-weight: 600;\n color: hsl(142 71% 45%);\n background: hsl(142 71% 45% / 0.1);\n border: 1px solid hsl(142 71% 45% / 0.3);\n padding: 2px 8px;\n border-radius: 4px;\n opacity: 0;\n transition: opacity 0.2s;\n pointer-events: none;\n white-space: nowrap;\n }\n\n [data-blog-content].dcs-blog-content:hover::after {\n opacity: 1;\n }\n\n /* Managed form area — click to open the portal Form Manager */\n [data-form-key].dcs-managed-form {\n cursor: pointer;\n border-radius: 12px;\n position: relative;\n transition: outline-color 0.2s, background-color 0.2s;\n }\n\n [data-form-key].dcs-managed-form:hover {\n outline: 2px dashed hsl(271 91% 65% / 0.65) !important;\n outline-offset: 4px;\n background-color: hsl(271 91% 65% / 0.04);\n }\n\n /* Blog metadata (title / summary) — click to edit */\n .dcs-blog-metadata {\n cursor: pointer;\n border-radius: 4px;\n transition: outline-color 0.2s, background-color 0.2s;\n }\n .dcs-blog-metadata:hover {\n outline: 2px dashed hsl(217 91% 60% / 0.5) !important;\n outline-offset: 4px;\n background-color: hsl(217 91% 60% / 0.04);\n }\n `\n document.head.appendChild(style)\n}\n\n// ─── Inbound Message Handler ─────────────────────────────────────\n\nfunction handleMessage(event: MessageEvent) {\n // Validate message structure (origin is not checked because\n // the parent portal origin varies between dev and production)\n const msg = event.data\n if (!msg || typeof msg !== 'object' || typeof msg.type !== 'string') return\n if (!msg.type.startsWith('dcs:')) return\n\n const { type, data } = msg as { type: InboundMessageType; data: unknown }\n\n switch (type) {\n case 'dcs:init-editor':\n enableEditorMode()\n break\n\n case 'dcs:highlight-section':\n if (data && typeof data === 'object' && 'sectionId' in data) {\n showSectionHighlight((data as { sectionId: string }).sectionId)\n }\n break\n\n case 'dcs:clear-highlight':\n clearSectionHighlight()\n break\n\n case 'dcs:select-text-key':\n if (data && typeof data === 'object' && 'key' in data) {\n focusTextKey((data as { key: string }).key)\n }\n break\n\n case 'dcs:update-text':\n if (data && typeof data === 'object' && 'key' in data && 'value' in data) {\n const d = data as { key: string; value: string }\n updateTextInPlace(d.key, d.value)\n }\n break\n\n case 'dcs:set-mode':\n if (data && typeof data === 'object' && 'mode' in data) {\n setMode((data as { mode: 'edit' | 'explore' }).mode)\n }\n break\n\n case 'dcs:set-editing-enabled':\n if (data && typeof data === 'object' && 'enabled' in data) {\n editingEnabled = (data as { enabled: boolean }).enabled\n if (editingEnabled) {\n // Re-add dcs-editable class to text key elements\n document.querySelectorAll<HTMLElement>(TEXT_KEY_SELECTOR).forEach(el => {\n el.classList.add('dcs-editable')\n })\n } else {\n // Clean up: finish any active edit, remove edit icon, remove hover styles\n if (activeEditElement) {\n finishEdit(activeEditElement)\n }\n removeEditIcon()\n removeArrayEditIcon()\n removeReviewEditIcon()\n removeManagedFormEditIcon()\n removeAllImageEditIcons()\n // Remove dcs-editable class to disable hover outlines\n document.querySelectorAll<HTMLElement>('.dcs-editable').forEach(el => {\n el.classList.remove('dcs-editable')\n })\n }\n }\n break\n\n case 'dcs:update-blog-content':\n if (data && typeof data === 'object' && 'content' in data) {\n const contentEl = document.querySelector<HTMLElement>(BLOG_CONTENT_SELECTOR)\n ?? document.querySelector<HTMLElement>(EVENT_CONTENT_SELECTOR)\n if (contentEl) {\n contentEl.innerHTML = (data as { content: string }).content\n // Trigger re-discovery so the portal overlay updates for any height changes\n scheduleRediscovery()\n }\n }\n break\n\n case 'dcs:update-reviews':\n if (data && typeof data === 'object' && 'key' in data) {\n const d = data as { key: string; reviews?: unknown }\n globalThis.dispatchEvent(new CustomEvent('dcs:reviews-updated', {\n detail: {\n key: d.key,\n reviews: Array.isArray(d.reviews) ? d.reviews : null,\n },\n }))\n scheduleRediscovery()\n }\n break\n }\n}\n\n/**\n * Inject a stylesheet that neutralizes viewport-relative min-heights.\n * In the visual editor iframe, the portal controls height from body.scrollHeight.\n * CSS rules like `min-height: 100vh` create a feedback loop:\n * iframe height → vh grows → body.scrollHeight grows → iframe height grows → …\n * Neutralizing these rules lets the content determine its natural height.\n */\nfunction injectEditorHeightFix() {\n if (document.getElementById('dcs-editor-height-fix')) return\n const style = document.createElement('style')\n style.id = 'dcs-editor-height-fix'\n style.textContent = `\n /* DCS Editor Bridge: Neutralize viewport-relative min-heights */\n .min-h-screen, .min-h-dvh, .min-h-svh,\n .min-h-\\\\[100vh\\\\], .min-h-\\\\[100dvh\\\\], .min-h-\\\\[100svh\\\\] {\n min-height: 0 !important;\n }\n `\n document.head.appendChild(style)\n}\n\n/** Last reported content height — used to avoid re-sending when stable */\nlet lastReportedHeight = 0\n\n/**\n * Measure the true content height, avoiding inflation from viewport-relative\n * CSS rules. Uses section bounds as the primary signal (immune to vh feedback),\n * falling back to body.scrollHeight (which is reliable once the editor height\n * fix stylesheet has been injected).\n */\nfunction measureContentHeight(sections: SectionInfo[]): number {\n // Primary: compute from section bounds — these reflect actual element positions\n // after the height-fix stylesheet has neutralized viewport-relative min-heights.\n let maxBottom = 0\n for (const s of sections) {\n const bottom = s.bounds.y + s.bounds.height\n if (bottom > maxBottom) maxBottom = bottom\n }\n const sectionHeight = maxBottom > 0 ? Math.ceil(maxBottom) : 0\n\n // Fallback: body.scrollHeight (reliable now that min-height: 100vh is neutralized)\n const bodyHeight = document.body.scrollHeight\n\n // Use whichever is larger — sections might miss elements without data-section,\n // but bodyHeight should be accurate with the injected CSS fix.\n return Math.max(sectionHeight, bodyHeight)\n}\n\nexport function initEditorBridge() {\n if (!isInIframe()) return // Not in iframe\n\n // Inject height fix CSS before any measurement\n injectEditorHeightFix()\n\n // Prevent duplicate listener registration on HMR re-execution\n if (!bridgeInitialized) {\n bridgeInitialized = true\n\n // Monitor navigation within the iframe — notify the portal of page changes\n monitorNavigation()\n\n // Forward right-click events to the portal so the context menu\n // works when right-clicking inside the iframe (cross-origin boundary\n // prevents the parent from receiving native contextmenu events).\n document.addEventListener('contextmenu', (e: MouseEvent) => {\n e.preventDefault()\n const target = e.target as HTMLElement\n const sectionEl = target.closest?.('[data-section]') as HTMLElement | null\n const formEl = target.closest?.('[data-form-key]') as HTMLElement | null\n\n // Detect if the right-click target is an image or inside a <picture> element\n const imgEl = target.tagName === 'IMG'\n ? target as HTMLImageElement\n : (target.closest?.('picture')?.querySelector('img') as HTMLImageElement | null)\n\n // Find ALL images at the click point (handles overlapping images like before/after sliders)\n let allImages: HTMLImageElement[] = []\n if (sectionEl) {\n allImages = findAllImagesAtPoint(sectionEl, e.clientX, e.clientY)\n }\n\n // If direct target detection found an image not in the position-based list, add it\n if (imgEl && !allImages.includes(imgEl)) {\n allImages.push(imgEl)\n }\n\n // Use the topmost image for backward-compatible single-image fields\n const topImage = allImages.length > 0 ? allImages[allImages.length - 1] : null\n\n postToParent('dcs:contextmenu', {\n x: e.clientX,\n y: e.clientY,\n sectionId: sectionEl?.dataset.section ?? null,\n sectionLabel: sectionEl?.dataset.sectionLabel ?? null,\n formId: formEl?.dataset.formKey ?? null,\n // Single image context (backward compatible) — topmost image at click point\n imageUrl: topImage?.currentSrc ?? topImage?.src ?? null,\n imageAlt: topImage?.alt ?? null,\n imageWidth: topImage?.naturalWidth ?? null,\n imageHeight: topImage?.naturalHeight ?? null,\n // All images at click point (for overlapping image scenarios like before/after sliders)\n images: allImages.length > 1\n ? allImages.map(img => ({\n imageUrl: img.currentSrc ?? img.src,\n imageAlt: img.alt ?? null,\n imageWidth: img.naturalWidth,\n imageHeight: img.naturalHeight,\n }))\n : null,\n })\n }, true)\n\n // Forward click events to the portal so it can dismiss context menus\n // when the user clicks inside the iframe.\n // NOTE: Left-clicking an image does NOT open image management. Users must\n // right-click → \"Replace Image\" or use the floating image icon (top-right\n // corner on hover). This prevents accidental sheet openings during normal\n // page interaction and slider/gallery dragging.\n document.addEventListener('click', () => {\n postToParent('dcs:click', {})\n }, true)\n\n // Listen for portal commands. Origin is not validated because\n // the portal origin varies between environments and all inbound\n // messages are type-checked before processing.\n // Origin cannot be pre-validated because the portal URL varies by environment.\n // All inbound messages are type-checked via the dcs: prefix guard.\n globalThis.self.addEventListener('message', handleMessage) // NOSONAR\n\n // Watch for VitePress / Vite HMR page updates. When the framework\n // replaces DOM content, our event listeners and AI buttons are lost.\n // Re-discover sections after each update so the portal overlay stays in sync.\n if (import.meta.hot) {\n import.meta.hot.on('vite:afterUpdate', () => {\n // Small delay for the framework to finish DOM updates\n setTimeout(rediscoverAndNotify, 300)\n })\n }\n }\n\n // Report available sections, text keys, and content height\n const sections = discoverSections()\n const textKeys = discoverTextKeys()\n const reviewKeys = discoverReviewKeys()\n const managedFormIds = discoverManagedFormIds()\n const contentHeight = measureContentHeight(sections)\n lastReportedHeight = contentHeight\n\n // Detect rich content elements for the initial ready report\n const blogEl = document.querySelector<HTMLElement>(BLOG_CONTENT_SELECTOR)\n const eventEl = document.querySelector<HTMLElement>(EVENT_CONTENT_SELECTOR)\n\n // Detect blog metadata elements for the initial ready report\n const blogTitleEl = document.querySelector<HTMLElement>('[data-blog-title]')\n const blogSummaryEl = document.querySelector<HTMLElement>('[data-blog-summary]')\n\n postToParent('dcs:ready', {\n sections,\n textKeys,\n contentHeight,\n hasBlogContent: !!blogEl,\n blogContentHtml: blogEl ? blogEl.innerHTML : null,\n hasEventContent: !!eventEl,\n eventContentHtml: eventEl ? eventEl.innerHTML : null,\n blogTitle: blogTitleEl ? blogTitleEl.textContent?.trim() ?? null : null,\n blogSummary: blogSummaryEl ? blogSummaryEl.textContent?.trim() ?? null : null,\n hasManagedForms: managedFormIds.length > 0,\n managedFormIds,\n hasReviews: reviewKeys.length > 0,\n reviewKeys,\n } satisfies EditorReadyPayload)\n\n // Observe layout changes (lazy images, dynamic content) and re-report bounds\n observeSectionLayout()\n}\n\n// NOTE: Auto-initialization is NOT done here. The Vite plugin's virtual module\n// (dcsEditorPlugin) handles initialization by importing and calling initEditorBridge().\n// Having auto-init as a side effect of the import caused double-initialization\n// (two dcs:ready messages on startup).\n"]}
package/dist/index.d.ts CHANGED
@@ -702,6 +702,7 @@ interface ReviewItem {
702
702
  date?: string;
703
703
  replyText?: string;
704
704
  locationName?: string;
705
+ sourceLocationName?: string;
705
706
  sourceUrl?: string;
706
707
  }
707
708
  interface UseReviewContentConfig {
package/dist/index.js CHANGED
@@ -572,6 +572,12 @@ function useResponsiveImage(options) {
572
572
  }
573
573
  };
574
574
  }
575
+ function withoutAuthorPhotos(items) {
576
+ return items.map((item) => ({
577
+ ...item,
578
+ authorPhotoUrl: void 0
579
+ }));
580
+ }
575
581
  var previewReviewOverrides = shallowRef({});
576
582
  var activePreviewReviewConsumers = 0;
577
583
  function normalizeReviewItem(item) {
@@ -586,11 +592,12 @@ function normalizeReviewItem(item) {
586
592
  platform: String(item.platform ?? "google"),
587
593
  rating,
588
594
  authorName: String(item.authorName ?? "Anonymous"),
589
- authorPhotoUrl: item.authorPhotoUrl ? String(item.authorPhotoUrl) : void 0,
595
+ authorPhotoUrl: void 0,
590
596
  text: item.text ? String(item.text) : void 0,
591
597
  date: item.date ? String(item.date) : void 0,
592
598
  replyText: item.replyText ? String(item.replyText) : void 0,
593
599
  locationName: item.locationName ? String(item.locationName) : void 0,
600
+ sourceLocationName: item.sourceLocationName ? String(item.sourceLocationName) : void 0,
594
601
  sourceUrl: item.sourceUrl ? String(item.sourceUrl) : void 0
595
602
  };
596
603
  }
@@ -633,7 +640,7 @@ function useReviewContent(config) {
633
640
  return previewReviewOverrides.value[sectionKey] ?? [];
634
641
  }
635
642
  if (typeof __DCS_CONTENT__ === "undefined" || __DCS_CONTENT__ == null) {
636
- return defaults;
643
+ return withoutAuthorPhotos(defaults);
637
644
  }
638
645
  let reviewData = null;
639
646
  if (pageSlug && __DCS_CONTENT__.pages?.[pageSlug]) {
@@ -643,7 +650,7 @@ function useReviewContent(config) {
643
650
  reviewData = __DCS_CONTENT__.global[`reviews.${sectionKey}.items`] ?? __DCS_CONTENT__.global[`reviews.${sectionKey}`];
644
651
  }
645
652
  if (!reviewData || !Array.isArray(reviewData)) {
646
- return defaults;
653
+ return withoutAuthorPhotos(defaults);
647
654
  }
648
655
  return normalizeReviewList(reviewData);
649
656
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/composables/useTextContent.ts","../src/composables/useSEO.ts","../src/composables/useReleaseNotes.ts","../src/composables/useSiteVersion.ts","../src/composables/useMediaCarousel.ts","../src/composables/useResponsiveImage.ts","../src/composables/useReviewContent.ts"],"names":["computed","getEnvVar","ref","onMounted","CACHE_TTL"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * useTextContent Composable\n *\n * Provides text content management with build-time injection support and optional\n * runtime API overrides for DCS-managed customer sites.\n *\n * Content resolution order:\n * 1. Runtime API overrides (premium tier only, if mode is 'runtime')\n * 2. Build-time content from .dcs/content.yaml (injected via dcsContentPlugin)\n * 3. Hardcoded defaults passed to the composable\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useTextContent } from '@duffcloudservices/cms'\n *\n * const { t, isLoading, error } = useTextContent({\n * pageSlug: 'home',\n * defaults: {\n * 'hero.title': 'Welcome to Our Site',\n * 'hero.subtitle': 'Build something amazing',\n * 'cta.primary': 'Get Started'\n * }\n * })\n * </script>\n *\n * <template>\n * <h1>{{ t('hero.title') }}</h1>\n * <p>{{ t('hero.subtitle') }}</p>\n * <button>{{ t('cta.primary') }}</button>\n * </template>\n * ```\n */\n\nimport { ref, computed, readonly, onMounted, type Ref } from 'vue'\nimport type { DcsContentFile, TextContentConfig, TextContentReturn } from '../types/content'\n\n// Declare the global injected by dcsContentPlugin\ndeclare const __DCS_CONTENT__: DcsContentFile | undefined\n\n// Simple in-memory cache for runtime fetches\nconst fetchCache = new Map<string, { data: Record<string, string>; expiresAt: number }>()\n\n/**\n * Safely get build-time content configuration.\n * Returns undefined if not available (no content.yaml or plugin not configured).\n */\nfunction getBuildTimeContent(): DcsContentFile | undefined {\n try {\n if (typeof __DCS_CONTENT__ !== 'undefined' && __DCS_CONTENT__ !== null) {\n return __DCS_CONTENT__\n }\n } catch {\n // __DCS_CONTENT__ not defined - that's fine, use defaults\n }\n return undefined\n}\n\n/**\n * Get build-time content for a specific page, merging global and page-specific content.\n */\nfunction getBuildTimePageContent(pageSlug: string): Record<string, string> {\n const content = getBuildTimeContent()\n if (!content) return {}\n\n const global = content.global ?? {}\n const page = content.pages?.[pageSlug] ?? {}\n\n return { ...global, ...page }\n}\n\n/**\n * Get environment variable value, handling both Vite and process.env patterns.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n // Vite pattern\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n\n try {\n // Node.js pattern\n if (typeof process !== 'undefined' && process.env) {\n const value = process.env[key]\n if (value !== undefined) return value\n }\n } catch {\n // process not available\n }\n\n return defaultValue\n}\n\n/**\n * useTextContent composable for DCS-managed text content.\n *\n * @param config - Configuration object\n * @returns Text content helpers and state\n */\nexport function useTextContent(config: TextContentConfig): TextContentReturn {\n const {\n pageSlug,\n defaults,\n fetchOnMount = true,\n cacheKey,\n cacheTtl = 60000,\n } = config\n\n // Get configuration from environment\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', '')\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\n const textOverrideMode = getEnvVar('VITE_TEXT_OVERRIDE_MODE', 'commit')\n const mode: 'commit' | 'runtime' = textOverrideMode === 'runtime' ? 'runtime' : 'commit'\n\n // Get build-time content immediately (synchronous)\n const buildTimeContent = getBuildTimePageContent(pageSlug)\n const hasBuildTimeContent = Object.keys(buildTimeContent).length > 0\n\n // State - initialize with build-time content\n const overrides = ref<Record<string, string>>({ ...buildTimeContent })\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n\n // Computed merged texts (defaults + overrides)\n const texts = computed(() => ({ ...defaults, ...overrides.value }))\n\n /**\n * Get text by key with optional fallback.\n * Resolution order: overrides → defaults → fallback → key\n */\n function t(key: string, fallback?: string): string {\n return overrides.value[key] ?? defaults[key] ?? fallback ?? key\n }\n\n /**\n * Check if a key has an override (from build-time or runtime).\n */\n function hasOverride(key: string): boolean {\n return key in overrides.value\n }\n\n /**\n * Get an array of objects from indexed content keys.\n * Useful for lists where keys follow the pattern: arrayKey.index.property\n * Example: features.1.title, features.1.description, features.2.title, etc.\n *\n * @param arrayKey - The base key prefix (e.g., 'features', 'items')\n * @returns Array of objects with properties extracted from matching keys, sorted by index\n *\n * @example\n * ```ts\n * // Given content keys:\n * // positions.1.title = \"Software Engineer\"\n * // positions.1.description = \"Build cool stuff\"\n * // positions.2.title = \"Designer\"\n * // positions.2.description = \"Design cool stuff\"\n *\n * const positions = getArray('positions')\n * // Returns:\n * // [\n * // { _index: 1, title: \"Software Engineer\", description: \"Build cool stuff\" },\n * // { _index: 2, title: \"Designer\", description: \"Design cool stuff\" }\n * // ]\n * ```\n */\n function getArray(arrayKey: string): Array<Record<string, unknown> & { _index: number }> {\n const items: Record<number, Record<string, unknown> & { _index: number }> = {}\n const source = { ...defaults, ...overrides.value }\n\n Object.keys(source).forEach((key) => {\n if (key.startsWith(`${arrayKey}.`)) {\n const parts = key.split('.')\n // Format: arrayKey.index.property (or arrayKey.index.nested.property)\n // Example: features.1.title -> index=1, prop=title\n if (parts.length >= 3) {\n const index = Number.parseInt(parts[1], 10)\n const prop = parts.slice(2).join('.')\n\n if (!Number.isNaN(index)) {\n if (!items[index]) items[index] = { _index: index }\n items[index][prop] = source[key]\n }\n }\n }\n })\n\n return Object.values(items).sort((a, b) => a._index - b._index)\n }\n\n /**\n * Fetch runtime overrides from the API.\n * Only runs if mode is 'runtime' and API is configured.\n */\n async function fetchOverrides(): Promise<void> {\n // Skip API fetch in commit mode - use build-time content only\n if (mode !== 'runtime') {\n return\n }\n\n // Skip if API not configured\n if (!apiBaseUrl || !siteSlug) {\n console.warn(\n '[@duffcloudservices/cms] Runtime mode enabled but VITE_API_BASE_URL or VITE_SITE_SLUG not set'\n )\n return\n }\n\n // Check cache first\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\n const cached = fetchCache.get(effectiveCacheKey)\n if (cached && cached.expiresAt > Date.now()) {\n overrides.value = { ...buildTimeContent, ...cached.data }\n return\n }\n\n isLoading.value = true\n error.value = null\n\n try {\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/pages/${pageSlug}/text`\n const response = await fetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n if (response.status === 404) {\n // No overrides for this page - that's fine\n return\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const data = await response.json()\n const apiOverrides = data.overrides ?? data.texts ?? {}\n\n // Cache the result\n fetchCache.set(effectiveCacheKey, {\n data: apiOverrides,\n expiresAt: Date.now() + cacheTtl,\n })\n\n // Merge: build-time content is baseline, API overrides on top\n overrides.value = { ...buildTimeContent, ...apiOverrides }\n } catch (e) {\n error.value = e instanceof Error ? e.message : 'Failed to load text overrides'\n console.error('[@duffcloudservices/cms] Failed to fetch text overrides:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n /**\n * Manually refresh overrides.\n * In commit mode, resets to build-time content.\n * In runtime mode, fetches fresh data from API.\n */\n async function refresh(): Promise<void> {\n if (mode !== 'runtime') {\n // In commit mode, just reset to build-time content\n overrides.value = { ...buildTimeContent }\n return\n }\n\n // Clear cache for this key\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\n fetchCache.delete(effectiveCacheKey)\n\n await fetchOverrides()\n }\n\n // Fetch on mount if enabled and in runtime mode (browser only)\n if (fetchOnMount && mode === 'runtime' && globalThis.window !== undefined) {\n onMounted(() => {\n fetchOverrides()\n })\n }\n\n return {\n t,\n getArray,\n texts,\n overrides,\n isLoading: readonly(isLoading) as Ref<boolean>,\n error: readonly(error) as Ref<string | null>,\n refresh,\n hasOverride,\n hasBuildTimeContent,\n mode,\n }\n}\n","/**\n * useSEO Composable\n *\n * Provides SEO configuration with build-time injection support from .dcs/seo.yaml.\n * Generates meta tags, Open Graph, Twitter Cards, and JSON-LD structured data.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useSEO } from '@duffcloudservices/cms'\n *\n * const { applyHead, getSchema, config } = useSEO('home')\n *\n * // Apply all meta tags\n * applyHead()\n *\n * // Or customize before applying\n * applyHead({\n * title: 'Custom Override Title',\n * schemas: [...getSchema(), customSchema]\n * })\n * </script>\n * ```\n */\n\nimport { computed, type ComputedRef } from 'vue'\nimport { useHead } from '@unhead/vue'\nimport type {\n SeoConfiguration,\n GlobalSeoConfig,\n SeoOpenGraphConfig,\n SeoTwitterConfig,\n SeoSchemaConfig,\n ResolvedPageSeo,\n UseSeoReturn,\n HeadOverrides,\n} from '../types/seo'\n\n// Declare the global injected by dcsSeoPlugin\ndeclare const __DCS_SEO__: SeoConfiguration | undefined\n\n/**\n * Safely get build-time SEO configuration.\n * Returns undefined if not available (no seo.yaml or plugin not configured).\n */\nfunction getBuildTimeSeo(): SeoConfiguration | undefined {\n try {\n if (typeof __DCS_SEO__ !== 'undefined' && __DCS_SEO__ !== null) {\n return __DCS_SEO__\n }\n } catch {\n // __DCS_SEO__ not defined - that's fine, use defaults\n }\n return undefined\n}\n\n// =============================================================================\n// Meta Tag Generation Utilities\n// =============================================================================\n\ninterface HeadInput {\n title?: string\n titleTemplate?: string | ((title: string) => string)\n meta?: Array<{ name?: string; property?: string; content: string }>\n link?: Array<{ rel: string; href: string; hreflang?: string }>\n script?: Array<{ type: string; children: string }>\n}\n\n/**\n * Generate Open Graph meta tags from config\n */\nfunction generateOpenGraphMeta(\n og: SeoOpenGraphConfig,\n global: GlobalSeoConfig,\n pageTitle: string,\n pageDescription: string,\n canonical: string\n): Array<{ property: string; content: string }> {\n const tags: Array<{ property: string; content: string }> = []\n\n tags.push({ property: 'og:title', content: og.title || pageTitle })\n tags.push({ property: 'og:description', content: og.description || pageDescription })\n tags.push({ property: 'og:url', content: og.url || canonical })\n tags.push({ property: 'og:type', content: og.type || 'website' })\n\n const image = og.image || global.images?.ogDefault\n if (image) {\n tags.push({ property: 'og:image', content: image })\n if (og.imageAlt || pageTitle) {\n tags.push({ property: 'og:image:alt', content: og.imageAlt || pageTitle })\n }\n if (og.imageWidth) {\n tags.push({ property: 'og:image:width', content: String(og.imageWidth) })\n }\n if (og.imageHeight) {\n tags.push({ property: 'og:image:height', content: String(og.imageHeight) })\n }\n }\n\n if (global.siteName) {\n tags.push({ property: 'og:site_name', content: global.siteName })\n }\n\n if (global.locale) {\n tags.push({ property: 'og:locale', content: global.locale })\n }\n\n // Article-specific tags\n if (og.type === 'article') {\n if (og.publishedTime) {\n tags.push({ property: 'article:published_time', content: og.publishedTime })\n }\n if (og.modifiedTime) {\n tags.push({ property: 'article:modified_time', content: og.modifiedTime })\n }\n if (og.author) {\n tags.push({ property: 'article:author', content: og.author })\n }\n if (og.section) {\n tags.push({ property: 'article:section', content: og.section })\n }\n if (og.tags) {\n og.tags.forEach((tag) => {\n tags.push({ property: 'article:tag', content: tag })\n })\n }\n }\n\n return tags\n}\n\n/**\n * Generate Twitter Card meta tags from config\n */\nfunction generateTwitterMeta(\n twitter: SeoTwitterConfig,\n global: GlobalSeoConfig,\n pageTitle: string,\n pageDescription: string\n): Array<{ name: string; content: string }> {\n const tags: Array<{ name: string; content: string }> = []\n\n tags.push({ name: 'twitter:card', content: twitter.card || 'summary_large_image' })\n tags.push({ name: 'twitter:title', content: twitter.title || pageTitle })\n tags.push({ name: 'twitter:description', content: twitter.description || pageDescription })\n\n const image = twitter.image || global.images?.twitterDefault\n if (image) {\n tags.push({ name: 'twitter:image', content: image })\n if (twitter.imageAlt || pageTitle) {\n tags.push({ name: 'twitter:image:alt', content: twitter.imageAlt || pageTitle })\n }\n }\n\n const site = twitter.site || global.social?.twitter\n if (site) {\n tags.push({ name: 'twitter:site', content: site.startsWith('@') ? site : `@${site}` })\n }\n\n if (twitter.creator) {\n tags.push({\n name: 'twitter:creator',\n content: twitter.creator.startsWith('@') ? twitter.creator : `@${twitter.creator}`,\n })\n }\n\n return tags\n}\n\n/**\n * Generate JSON-LD script content from schemas\n */\nfunction generateJsonLd(schemas: SeoSchemaConfig[], global: GlobalSeoConfig): object[] {\n return schemas.map((schema) => {\n const base: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': schema.type,\n }\n\n // Merge properties\n if (schema.properties) {\n Object.assign(base, schema.properties)\n }\n\n // Auto-populate common properties from global config\n if (schema.type === 'WebSite' && global.siteUrl && !base.url) {\n base.url = global.siteUrl\n }\n if (schema.type === 'WebSite' && global.siteName && !base.name) {\n base.name = global.siteName\n }\n\n return base\n })\n}\n\n/**\n * Resolve page SEO by merging global defaults with page-specific config\n */\nfunction resolvePageSeo(\n pageSlug: string,\n pagePath: string | undefined,\n seoConfig: SeoConfiguration | undefined\n): ResolvedPageSeo {\n const global = seoConfig?.global ?? {}\n const page = seoConfig?.pages?.[pageSlug] ?? {}\n\n // Build canonical URL\n let canonical = page.canonical || ''\n if (!canonical && global.siteUrl) {\n const path = pagePath ?? (pageSlug === 'home' ? '/' : `/${pageSlug}`)\n canonical = `${global.siteUrl.replace(/\\/$/, '')}${path}`\n }\n\n // Build title\n let title = page.title || global.defaultTitle || pageSlug\n if (!page.noTitleTemplate && global.titleTemplate) {\n title = global.titleTemplate.replace('%s', title)\n }\n\n // Merge Open Graph\n const openGraph: ResolvedPageSeo['openGraph'] = {\n type: page.openGraph?.type || 'website',\n title: page.openGraph?.title || page.title || global.defaultTitle || '',\n description: page.openGraph?.description || page.description || global.defaultDescription || '',\n ...page.openGraph,\n }\n\n // Merge Twitter\n const twitter: ResolvedPageSeo['twitter'] = {\n card: page.twitter?.card || 'summary_large_image',\n ...page.twitter,\n }\n\n // Combine schemas (global + page)\n const schemas = [...(global.schemas ?? []), ...(page.schemas ?? [])]\n\n return {\n title,\n description: page.description || global.defaultDescription || '',\n canonical,\n robots: page.robots || global.robots || 'index, follow',\n openGraph,\n twitter,\n schemas,\n alternates: page.alternates ?? [],\n }\n}\n\n/**\n * useSEO composable for DCS-managed SEO configuration.\n *\n * @param pageSlug - Page slug matching entry in seo.yaml\n * @param pagePath - Optional page path for canonical URL generation\n * @returns SEO helpers and state\n */\nexport function useSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\n const seoConfig = getBuildTimeSeo()\n const hasBuildTimeSeo = seoConfig !== undefined\n\n // Computed resolved config\n const config: ComputedRef<ResolvedPageSeo> = computed(() =>\n resolvePageSeo(pageSlug, pagePath, seoConfig)\n )\n\n /**\n * Get JSON-LD schema objects for the page\n */\n function getSchema(): object[] {\n return generateJsonLd(config.value.schemas, seoConfig?.global ?? {})\n }\n\n /**\n * Get canonical URL for the page\n */\n function getCanonical(): string {\n return config.value.canonical\n }\n\n /**\n * Apply all meta tags via useHead\n */\n function applyHead(overrides?: HeadOverrides): void {\n const resolved = config.value\n const global = seoConfig?.global ?? {}\n\n const title = overrides?.title ?? resolved.title\n const description = overrides?.description ?? resolved.description\n\n // Build meta tags\n const meta: HeadInput['meta'] = []\n\n // Basic meta\n meta.push({ name: 'description', content: description })\n if (resolved.robots) {\n meta.push({ name: 'robots', content: resolved.robots })\n }\n\n // Verification codes\n if (global.verification?.google) {\n meta.push({ name: 'google-site-verification', content: global.verification.google })\n }\n if (global.verification?.bing) {\n meta.push({ name: 'msvalidate.01', content: global.verification.bing })\n }\n\n // Open Graph\n const ogMeta = generateOpenGraphMeta(\n resolved.openGraph,\n global,\n title,\n description,\n resolved.canonical\n )\n meta.push(...ogMeta.map((t) => ({ property: t.property, content: t.content })))\n\n // Twitter\n const twitterMeta = generateTwitterMeta(resolved.twitter, global, title, description)\n meta.push(...twitterMeta.map((t) => ({ name: t.name, content: t.content })))\n\n // Additional overrides\n if (overrides?.meta) {\n meta.push(...overrides.meta)\n }\n\n // Build links\n const link: HeadInput['link'] = []\n\n // Canonical\n if (resolved.canonical) {\n link.push({ rel: 'canonical', href: resolved.canonical })\n }\n\n // Alternate languages\n resolved.alternates.forEach((alt) => {\n link.push({ rel: 'alternate', href: alt.href, hreflang: alt.hreflang })\n })\n\n // Build scripts (JSON-LD)\n const schemas = overrides?.schemas ?? getSchema()\n const script: HeadInput['script'] = schemas.map((schema) => ({\n type: 'application/ld+json',\n children: JSON.stringify(schema),\n }))\n\n // Apply via useHead\n useHead({\n title,\n meta,\n link,\n script,\n })\n }\n\n return {\n config,\n applyHead,\n getSchema,\n getCanonical,\n hasBuildTimeSeo,\n }\n}\n\n/**\n * Create a typed useSEO function with site-specific defaults.\n * Useful for creating a site-wide wrapper.\n *\n * @example\n * ```ts\n * // composables/useSiteSeo.ts\n * import { createSiteSEO } from '@duffcloudservices/cms'\n *\n * export const useSiteSeo = createSiteSEO({\n * siteName: 'My Site',\n * siteUrl: 'https://example.com'\n * })\n * ```\n */\nexport function createSiteSEO(\n _siteDefaults: Partial<GlobalSeoConfig>\n): (pageSlug: string, pagePath?: string) => UseSeoReturn {\n return function siteUseSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\n // Note: siteDefaults would be used if we needed to override at runtime\n // but build-time injection handles this via dcsSeoPlugin\n return useSEO(pageSlug, pagePath)\n }\n}\n","/**\n * useReleaseNotes Composable\n *\n * Fetches and displays versioned release notes from the DCS Portal API.\n * Supports fetching specific versions or the latest release.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useReleaseNotes } from '@duffcloudservices/cms'\n * import { useRoute } from 'vue-router'\n *\n * const route = useRoute()\n * const version = route.params.version as string || 'latest'\n *\n * const { releaseNote, isLoading, error } = useReleaseNotes(version)\n * </script>\n *\n * <template>\n * <div v-if=\"isLoading\">Loading...</div>\n * <div v-else-if=\"error\">{{ error }}</div>\n * <article v-else-if=\"releaseNote\">\n * <h1>{{ releaseNote.title }}</h1>\n * <p>{{ releaseNote.summary }}</p>\n * <div v-html=\"renderedMarkdown\" />\n * </article>\n * </template>\n * ```\n */\n\nimport { ref, onMounted } from 'vue'\nimport type { ReleaseNote, ReleaseNotesReturn } from '../types/release-notes'\n\n/**\n * Get environment variable value.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n return defaultValue\n}\n\n// Simple cache for release notes\nconst releaseNotesCache = new Map<string, { data: ReleaseNote; expiresAt: number }>()\nconst CACHE_TTL = 5 * 60 * 1000 // 5 minutes\n\n/**\n * useReleaseNotes composable for fetching release notes from the DCS API.\n *\n * @param version - Semantic version (e.g., \"1.2.0\") or \"latest\"\n * @param options - Optional configuration\n * @returns Release notes data and state\n */\nexport function useReleaseNotes(\n version: string,\n options: { fetchOnMount?: boolean } = {}\n): ReleaseNotesReturn {\n const { fetchOnMount = true } = options\n\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\n\n const releaseNote = ref<ReleaseNote | null>(null)\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n\n async function fetchReleaseNotes(): Promise<void> {\n if (!siteSlug) {\n error.value = 'VITE_SITE_SLUG environment variable not set'\n return\n }\n\n // Check cache\n const cacheKey = `${siteSlug}:${version}`\n const cached = releaseNotesCache.get(cacheKey)\n if (cached && cached.expiresAt > Date.now()) {\n releaseNote.value = cached.data\n return\n }\n\n isLoading.value = true\n error.value = null\n\n try {\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/release-notes/${version}`\n const response = await fetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n if (response.status === 404) {\n error.value = `Release notes for version ${version} not found`\n return\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const data = await response.json()\n\n // Normalize the response\n const note: ReleaseNote = {\n version: data.version,\n title: data.title,\n summary: data.summary || '',\n notesMarkdown: data.notesMarkdown || data.notes || '',\n changeCount: data.changeCount || 0,\n releaseDate: data.releaseDate || data.releasedAt || '',\n }\n\n // Cache the result\n releaseNotesCache.set(cacheKey, {\n data: note,\n expiresAt: Date.now() + CACHE_TTL,\n })\n\n releaseNote.value = note\n } catch (e) {\n error.value = e instanceof Error ? e.message : 'Failed to load release notes'\n console.error('[@duffcloudservices/cms] Failed to fetch release notes:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n async function refresh(): Promise<void> {\n // Clear cache\n const cacheKey = `${siteSlug}:${version}`\n releaseNotesCache.delete(cacheKey)\n await fetchReleaseNotes()\n }\n\n // Fetch on mount if enabled\n if (fetchOnMount && typeof window !== 'undefined') {\n onMounted(() => {\n fetchReleaseNotes()\n })\n }\n\n return {\n releaseNote,\n isLoading,\n error,\n refresh,\n }\n}\n","/**\n * useSiteVersion Composable\n *\n * Gets the current site version for footer badges and version displays.\n * Fetches the latest release version from the DCS Portal API.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useSiteVersion } from '@duffcloudservices/cms'\n *\n * const { version, releaseNotesUrl } = useSiteVersion()\n * </script>\n *\n * <template>\n * <footer>\n * <a v-if=\"version\" :href=\"releaseNotesUrl\" class=\"version-badge\">\n * v{{ version }}\n * </a>\n * </footer>\n * </template>\n * ```\n */\n\nimport { ref, computed, onMounted } from 'vue'\nimport type { SiteVersionReturn } from '../types/release-notes'\n\n/**\n * Get environment variable value.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n return defaultValue\n}\n\n// Cache for site version\nlet versionCache: { version: string; expiresAt: number } | null = null\nconst CACHE_TTL = 10 * 60 * 1000 // 10 minutes\n\n/**\n * useSiteVersion composable for displaying the current site version.\n *\n * @param options - Optional configuration\n * @returns Site version data and computed URL\n */\nexport function useSiteVersion(options: { fetchOnMount?: boolean } = {}): SiteVersionReturn {\n const { fetchOnMount = true } = options\n\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\n\n const version = ref<string | null>(null)\n const isLoading = ref(false)\n\n const releaseNotesUrl = computed(() => {\n if (!version.value) return '/releaseNotes/latest'\n return `/releaseNotes/${version.value}`\n })\n\n async function fetchVersion(): Promise<void> {\n if (!siteSlug) {\n return\n }\n\n // Check cache\n if (versionCache && versionCache.expiresAt > Date.now()) {\n version.value = versionCache.version\n return\n }\n\n isLoading.value = true\n\n try {\n // Fetch the latest release notes to get the version\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/release-notes/latest`\n const response = await fetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n // No release notes yet - that's fine\n return\n }\n\n const data = await response.json()\n const latestVersion = data.version\n\n // Cache the result\n versionCache = {\n version: latestVersion,\n expiresAt: Date.now() + CACHE_TTL,\n }\n\n version.value = latestVersion\n } catch (e) {\n console.error('[@duffcloudservices/cms] Failed to fetch site version:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n // Fetch on mount if enabled\n if (fetchOnMount && typeof window !== 'undefined') {\n onMounted(() => {\n fetchVersion()\n })\n }\n\n return {\n version,\n isLoading,\n releaseNotesUrl,\n }\n}\n","/**\n * useMediaCarousel Composable\n *\n * Extracts media carousel items from text content keys following the pattern:\n * `{prefix}.{N}.url`, `{prefix}.{N}.type`, `{prefix}.{N}.alt`\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useTextContent, useMediaCarousel } from '@duffcloudservices/cms'\n *\n * const { t } = useTextContent({\n * pageSlug: 'bio-mackenzie',\n * defaults: {\n * 'hero.media-carousel.0.url': '/images/staff/mackenzie.webp',\n * 'hero.media-carousel.0.type': 'image',\n * 'hero.media-carousel.0.alt': 'Mackenzie Kowalick',\n * 'hero.media-carousel.1.url': '/videos/intro.mp4',\n * 'hero.media-carousel.1.type': 'video',\n * 'hero.media-carousel.1.alt': 'Introduction video',\n * }\n * })\n *\n * const { items } = useMediaCarousel({\n * prefix: 'hero.media-carousel',\n * t,\n * defaults: [\n * { url: '/images/staff/mackenzie.webp', type: 'image', alt: 'Mackenzie Kowalick' }\n * ]\n * })\n * </script>\n *\n * <template>\n * <MediaCarousel :items=\"items\" />\n * </template>\n * ```\n */\n\nimport { computed, type ComputedRef } from 'vue'\nimport { isCdnAssetUrl } from '@duffcloudservices/cms-core'\n\n/**\n * Media carousel item representing an image, video, or embed\n */\nexport interface MediaCarouselItem {\n /** URL to the image, video file, or embed URL */\n url: string\n /** Type of media: 'image', 'video' (direct file), 'youtube', or 'instagram' */\n type: 'image' | 'video' | 'youtube' | 'instagram'\n /** Accessibility alt text */\n alt?: string\n /**\n * Whether this image has responsive CDN variants available.\n * Automatically set to `true` when the URL matches the DCS CDN asset pattern.\n * Components rendering the carousel should use `<ResponsiveImage>` when this is `true`.\n */\n responsive?: boolean\n}\n\n/**\n * Configuration for useMediaCarousel composable\n */\nexport interface UseMediaCarouselConfig {\n /** Key prefix for carousel items (e.g., 'hero.media-carousel') */\n prefix: string\n /** The t() function from useTextContent */\n t: (key: string, fallback?: string) => string\n /** Default items to use if no content keys are found */\n defaults?: MediaCarouselItem[]\n /** Maximum number of items to look for (default: 10) */\n maxItems?: number\n}\n\n/**\n * Return type for useMediaCarousel composable\n */\nexport interface UseMediaCarouselReturn {\n /** Computed array of media carousel items */\n items: ComputedRef<MediaCarouselItem[]>\n /** Whether any items were found from content keys */\n hasItems: ComputedRef<boolean>\n /** Number of items in the carousel */\n count: ComputedRef<number>\n}\n\n/**\n * Extract media carousel items from text content keys.\n *\n * Looks for keys in the format:\n * - `{prefix}.{N}.url` - Required URL for the media\n * - `{prefix}.{N}.type` - Type: 'image' or 'video' (defaults to 'image')\n * - `{prefix}.{N}.alt` - Alt text for accessibility\n *\n * Items are sorted by index (0, 1, etc.) and only included if they have a valid URL.\n *\n * @param config - Configuration object\n * @returns Media carousel helpers and state\n */\nexport function useMediaCarousel(config: UseMediaCarouselConfig): UseMediaCarouselReturn {\n const {\n prefix,\n t,\n defaults = [],\n maxItems = 10,\n } = config\n\n const items = computed<MediaCarouselItem[]>(() => {\n const result: MediaCarouselItem[] = []\n\n // Look for items from 0 to maxItems\n for (let i = 0; i < maxItems; i++) {\n const urlKey = `${prefix}.${i}.url`\n const typeKey = `${prefix}.${i}.type`\n const altKey = `${prefix}.${i}.alt`\n\n // Use a sentinel value to detect if the key exists\n const url = t(urlKey, '')\n \n // Skip if no URL (key doesn't exist or is empty)\n if (!url || url === urlKey) {\n continue\n }\n\n const typeValue = t(typeKey, 'image')\n // Parse type value - support image, video, youtube, instagram\n let type: 'image' | 'video' | 'youtube' | 'instagram' = 'image'\n if (typeValue === 'video') type = 'video'\n else if (typeValue === 'youtube') type = 'youtube'\n else if (typeValue === 'instagram') type = 'instagram'\n const alt = t(altKey, '')\n\n result.push({\n url,\n type,\n alt: alt && alt !== altKey ? alt : undefined,\n // Flag CDN-hosted images as responsive so carousel components\n // can render them with <ResponsiveImage> automatically\n responsive: type === 'image' && isCdnAssetUrl(url),\n })\n }\n\n // If no items found from content keys, use defaults\n if (result.length === 0) {\n return defaults\n }\n\n return result\n })\n\n const hasItems = computed(() => items.value.length > 0)\n const count = computed(() => items.value.length)\n\n return {\n items,\n hasItems,\n count,\n }\n}\n","/**\n * Vue 3 composable that resolves responsive image variants for DCS CDN-hosted assets.\n *\n * Wraps the framework-agnostic `resolveResponsiveImage` from `@duffcloudservices/cms-core`\n * with reactive Vue refs so it can be used directly in `<script setup>` blocks.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useResponsiveImage } from '@duffcloudservices/cms'\n *\n * const hero = useResponsiveImage({\n * src: 'https://files.duffcloudservices.com/kept/assets/hero/abc-123.jpg',\n * alt: 'Hero image',\n * context: 'hero',\n * })\n * </script>\n *\n * <template>\n * <picture v-if=\"hero.hasVariants\">\n * <source\n * v-for=\"source in hero.sources\"\n * :key=\"source.type\"\n * :srcset=\"source.srcset\"\n * :type=\"source.type\"\n * :sizes=\"source.sizes\"\n * />\n * <img v-bind=\"hero.imgProps\" class=\"w-full h-full object-cover\" />\n * </picture>\n * <img v-else v-bind=\"hero.imgProps\" class=\"w-full h-full object-cover\" />\n * </template>\n * ```\n */\n\nimport { computed, type MaybeRefOrGetter, toValue } from 'vue'\nimport {\n resolveResponsiveImage,\n type ImageContext,\n type ResponsiveImageResult,\n} from '@duffcloudservices/cms-core'\n\nexport interface UseResponsiveImageOptions {\n /** Source URL — can be a reactive ref, getter, or plain string. */\n src: MaybeRefOrGetter<string>\n /** Alt text — can be a reactive ref, getter, or plain string. */\n alt: MaybeRefOrGetter<string>\n /** Sizing context — determines which variants to include. */\n context?: MaybeRefOrGetter<ImageContext | undefined>\n /** Optional `sizes` attribute override. */\n sizes?: MaybeRefOrGetter<string | undefined>\n /** Skip variant resolution and use the original URL only. */\n original?: MaybeRefOrGetter<boolean | undefined>\n}\n\n/**\n * Reactively resolves responsive image metadata for a DCS CDN URL.\n *\n * The returned object is a computed ref that recomputes whenever any\n * of the input refs change. Spread `imgProps` onto an `<img>` or\n * combine with `sources` inside a `<picture>` element.\n */\nexport function useResponsiveImage(options: UseResponsiveImageOptions): ResponsiveImageResult {\n const result = computed(() =>\n resolveResponsiveImage({\n src: toValue(options.src),\n alt: toValue(options.alt),\n context: toValue(options.context),\n sizes: toValue(options.sizes),\n original: toValue(options.original) ?? undefined,\n }),\n )\n\n // Return a reactive proxy that delegates to the computed\n return {\n get imgProps() {\n return result.value.imgProps\n },\n get sources() {\n return result.value.sources\n },\n get hasVariants() {\n return result.value.hasVariants\n },\n }\n}\n","/**\n * Composable for reading curated review selections from DCS content.\n * Reviews are stored in content.yaml by the visual editor's ReviewPickerSheet.\n */\nimport { computed, onMounted, onUnmounted, shallowRef, type ComputedRef } from 'vue'\n\nexport interface ReviewItem {\n id: string\n platform: 'google' | 'meta' | string\n rating: number\n authorName: string\n authorPhotoUrl?: string\n text?: string\n date?: string\n replyText?: string\n locationName?: string\n sourceUrl?: string\n}\n\nexport interface UseReviewContentConfig {\n /** The section key matching the data-dcs-reviews attribute value */\n sectionKey: string\n /** Page slug for page-specific content lookup (defaults to current page) */\n pageSlug?: string\n /** Fallback reviews when no content is available */\n defaults?: ReviewItem[]\n}\n\nexport interface UseReviewContentReturn {\n /** The curated review items from content */\n reviews: ComputedRef<ReviewItem[]>\n /** Whether any reviews are available */\n hasReviews: ComputedRef<boolean>\n /** Number of reviews */\n count: ComputedRef<number>\n}\n\nconst previewReviewOverrides = shallowRef<Record<string, ReviewItem[]>>({})\nlet activePreviewReviewConsumers = 0\n\n// Declare the global content variable injected by dcsContentPlugin\ndeclare const __DCS_CONTENT__: {\n global?: Record<string, unknown>\n pages?: Record<string, Record<string, unknown>>\n} | undefined\n\nfunction normalizeReviewItem(item: Record<string, unknown>): ReviewItem | null {\n const id = String(item.id ?? '').trim()\n if (!id) {\n return null\n }\n\n const rawRating = Number(item.rating ?? 5)\n const rating = Number.isFinite(rawRating)\n ? Math.min(5, Math.max(1, Math.round(rawRating)))\n : 5\n\n return {\n id,\n platform: String(item.platform ?? 'google'),\n rating,\n authorName: String(item.authorName ?? 'Anonymous'),\n authorPhotoUrl: item.authorPhotoUrl ? String(item.authorPhotoUrl) : undefined,\n text: item.text ? String(item.text) : undefined,\n date: item.date ? String(item.date) : undefined,\n replyText: item.replyText ? String(item.replyText) : undefined,\n locationName: item.locationName ? String(item.locationName) : undefined,\n sourceUrl: item.sourceUrl ? String(item.sourceUrl) : undefined,\n }\n}\n\nfunction normalizeReviewList(value: unknown): ReviewItem[] {\n if (!Array.isArray(value)) {\n return []\n }\n\n return value\n .filter((item): item is Record<string, unknown> => item != null && typeof item === 'object')\n .map(normalizeReviewItem)\n .filter((item): item is ReviewItem => item != null)\n}\n\nfunction handlePreviewReviewUpdate(event: Event) {\n const detail = event instanceof CustomEvent && event.detail != null && typeof event.detail === 'object'\n ? event.detail as { key?: unknown; reviews?: unknown }\n : null\n const key = typeof detail?.key === 'string' ? detail.key.trim() : ''\n if (!key) {\n return\n }\n\n const next = { ...previewReviewOverrides.value }\n if (Array.isArray(detail?.reviews)) {\n next[key] = normalizeReviewList(detail.reviews)\n } else {\n delete next[key]\n }\n previewReviewOverrides.value = next\n}\n\nexport function useReviewContent(config: UseReviewContentConfig): UseReviewContentReturn {\n const { sectionKey, pageSlug, defaults = [] } = config\n\n onMounted(() => {\n activePreviewReviewConsumers += 1\n if (activePreviewReviewConsumers === 1) {\n window.addEventListener('dcs:reviews-updated', handlePreviewReviewUpdate)\n }\n })\n\n onUnmounted(() => {\n activePreviewReviewConsumers = Math.max(0, activePreviewReviewConsumers - 1)\n if (activePreviewReviewConsumers === 0) {\n window.removeEventListener('dcs:reviews-updated', handlePreviewReviewUpdate)\n }\n })\n\n const reviews = computed<ReviewItem[]>(() => {\n if (Object.prototype.hasOwnProperty.call(previewReviewOverrides.value, sectionKey)) {\n return previewReviewOverrides.value[sectionKey] ?? []\n }\n\n if (typeof __DCS_CONTENT__ === 'undefined' || __DCS_CONTENT__ == null) {\n return defaults\n }\n\n let reviewData: unknown = null\n\n if (pageSlug && __DCS_CONTENT__.pages?.[pageSlug]) {\n reviewData = __DCS_CONTENT__.pages[pageSlug][`reviews.${sectionKey}.items`]\n ?? __DCS_CONTENT__.pages[pageSlug][`reviews.${sectionKey}`]\n }\n\n if (!reviewData && __DCS_CONTENT__.global) {\n reviewData = __DCS_CONTENT__.global[`reviews.${sectionKey}.items`]\n ?? __DCS_CONTENT__.global[`reviews.${sectionKey}`]\n }\n\n if (!reviewData || !Array.isArray(reviewData)) {\n return defaults\n }\n\n return normalizeReviewList(reviewData)\n })\n\n const hasReviews = computed(() => reviews.value.length > 0)\n const count = computed(() => reviews.value.length)\n\n return { reviews, hasReviews, count }\n}\n"]}
1
+ {"version":3,"sources":["../src/composables/useTextContent.ts","../src/composables/useSEO.ts","../src/composables/useReleaseNotes.ts","../src/composables/useSiteVersion.ts","../src/composables/useMediaCarousel.ts","../src/composables/useResponsiveImage.ts","../src/composables/useReviewContent.ts"],"names":["computed","getEnvVar","ref","onMounted","CACHE_TTL"],"mappings":";;;;;;AAyCA,IAAM,UAAA,uBAAiB,GAAA,EAAiE;AAMxF,SAAS,mBAAA,GAAkD;AACzD,EAAA,IAAI;AACF,IAAA,IAAI,OAAO,eAAA,KAAoB,WAAA,IAAe,eAAA,KAAoB,IAAA,EAAM;AACtE,MAAA,OAAO,eAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,MAAA;AACT;AAKA,SAAS,wBAAwB,QAAA,EAA0C;AACzE,EAAA,MAAM,UAAU,mBAAA,EAAoB;AACpC,EAAA,IAAI,CAAC,OAAA,EAAS,OAAO,EAAC;AAEtB,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,IAAU,EAAC;AAClC,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAA,GAAQ,QAAQ,KAAK,EAAC;AAE3C,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,GAAG,IAAA,EAAK;AAC9B;AAKA,SAAS,SAAA,CAAU,GAAA,EAAa,YAAA,GAAe,EAAA,EAAY;AACzD,EAAA,IAAI;AAEF,IAAA,IAAI,OAAO,MAAA,CAAA,IAAA,KAAgB,WAAA,IAAe,MAAA,CAAA,IAAA,CAAY,GAAA,EAAK;AACzD,MAAA,MAAM,KAAA,GAAS,MAAA,CAAA,IAAA,CAAY,GAAA,CAA2C,GAAG,CAAA;AACzE,MAAA,IAAI,KAAA,KAAU,QAAW,OAAO,KAAA;AAAA,IAClC;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,IAAI;AAEF,IAAA,IAAI,OAAO,OAAA,KAAY,WAAA,IAAe,OAAA,CAAQ,GAAA,EAAK;AACjD,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA;AAC7B,MAAA,IAAI,KAAA,KAAU,QAAW,OAAO,KAAA;AAAA,IAClC;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,OAAO,YAAA;AACT;AAQO,SAAS,eAAe,MAAA,EAA8C;AAC3E,EAAA,MAAM;AAAA,IACJ,QAAA;AAAA,IACA,QAAA;AAAA,IACA,YAAA,GAAe,IAAA;AAAA,IACf,QAAA;AAAA,IACA,QAAA,GAAW;AAAA,GACb,GAAI,MAAA;AAGJ,EAAA,MAAM,UAAA,GAAa,SAAA,CAAU,mBAAA,EAAqB,EAAE,CAAA;AACpD,EAAA,MAAM,QAAA,GAAW,SAAA,CAAU,gBAAA,EAAkB,EAAE,CAAA;AAC/C,EAAA,MAAM,gBAAA,GAAmB,SAAA,CAAU,yBAAA,EAA2B,QAAQ,CAAA;AACtE,EAAA,MAAM,IAAA,GAA6B,gBAAA,KAAqB,SAAA,GAAY,SAAA,GAAY,QAAA;AAGhF,EAAA,MAAM,gBAAA,GAAmB,wBAAwB,QAAQ,CAAA;AACzD,EAAA,MAAM,mBAAA,GAAsB,MAAA,CAAO,IAAA,CAAK,gBAAgB,EAAE,MAAA,GAAS,CAAA;AAGnE,EAAA,MAAM,SAAA,GAAY,GAAA,CAA4B,EAAE,GAAG,kBAAkB,CAAA;AACrE,EAAA,MAAM,SAAA,GAAY,IAAI,KAAK,CAAA;AAC3B,EAAA,MAAM,KAAA,GAAQ,IAAmB,IAAI,CAAA;AAGrC,EAAA,MAAM,KAAA,GAAQ,SAAS,OAAO,EAAE,GAAG,QAAA,EAAU,GAAG,SAAA,CAAU,KAAA,EAAM,CAAE,CAAA;AAMlE,EAAA,SAAS,CAAA,CAAE,KAAa,QAAA,EAA2B;AACjD,IAAA,OAAO,UAAU,KAAA,CAAM,GAAG,KAAK,QAAA,CAAS,GAAG,KAAK,QAAA,IAAY,GAAA;AAAA,EAC9D;AAKA,EAAA,SAAS,YAAY,GAAA,EAAsB;AACzC,IAAA,OAAO,OAAO,SAAA,CAAU,KAAA;AAAA,EAC1B;AA0BA,EAAA,SAAS,SAAS,QAAA,EAAuE;AACvF,IAAA,MAAM,QAAsE,EAAC;AAC7E,IAAA,MAAM,SAAS,EAAE,GAAG,QAAA,EAAU,GAAG,UAAU,KAAA,EAAM;AAEjD,IAAA,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,OAAA,CAAQ,CAAC,GAAA,KAAQ;AACnC,MAAA,IAAI,GAAA,CAAI,UAAA,CAAW,CAAA,EAAG,QAAQ,GAAG,CAAA,EAAG;AAClC,QAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA;AAG3B,QAAA,IAAI,KAAA,CAAM,UAAU,CAAA,EAAG;AACrB,UAAA,MAAM,QAAQ,MAAA,CAAO,QAAA,CAAS,KAAA,CAAM,CAAC,GAAG,EAAE,CAAA;AAC1C,UAAA,MAAM,OAAO,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA,CAAE,KAAK,GAAG,CAAA;AAEpC,UAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,KAAK,CAAA,EAAG;AACxB,YAAA,IAAI,CAAC,MAAM,KAAK,CAAA,QAAS,KAAK,CAAA,GAAI,EAAE,MAAA,EAAQ,KAAA,EAAM;AAClD,YAAA,KAAA,CAAM,KAAK,CAAA,CAAE,IAAI,CAAA,GAAI,OAAO,GAAG,CAAA;AAAA,UACjC;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAED,IAAA,OAAO,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,MAAA,GAAS,CAAA,CAAE,MAAM,CAAA;AAAA,EAChE;AAMA,EAAA,eAAe,cAAA,GAAgC;AAE7C,IAAA,IAAI,SAAS,SAAA,EAAW;AACtB,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,CAAC,UAAA,IAAc,CAAC,QAAA,EAAU;AAC5B,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN;AAAA,OACF;AACA,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,iBAAA,GAAoB,QAAA,IAAY,CAAA,EAAG,QAAQ,IAAI,QAAQ,CAAA,CAAA;AAC7D,IAAA,MAAM,MAAA,GAAS,UAAA,CAAW,GAAA,CAAI,iBAAiB,CAAA;AAC/C,IAAA,IAAI,MAAA,IAAU,MAAA,CAAO,SAAA,GAAY,IAAA,CAAK,KAAI,EAAG;AAC3C,MAAA,SAAA,CAAU,QAAQ,EAAE,GAAG,gBAAA,EAAkB,GAAG,OAAO,IAAA,EAAK;AACxD,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,KAAA,GAAQ,IAAA;AAClB,IAAA,KAAA,CAAM,KAAA,GAAQ,IAAA;AAEd,IAAA,IAAI;AACF,MAAA,MAAM,MAAM,CAAA,EAAG,UAAU,CAAA,cAAA,EAAiB,QAAQ,UAAU,QAAQ,CAAA,KAAA,CAAA;AACpE,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,QAChC,OAAA,EAAS;AAAA,UACP,MAAA,EAAQ;AAAA;AACV,OACD,CAAA;AAED,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAE3B,UAAA;AAAA,QACF;AACA,QAAA,MAAM,IAAI,MAAM,CAAA,KAAA,EAAQ,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,QAAA,CAAS,UAAU,CAAA,CAAE,CAAA;AAAA,MACnE;AAEA,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,MAAA,MAAM,YAAA,GAAe,IAAA,CAAK,SAAA,IAAa,IAAA,CAAK,SAAS,EAAC;AAGtD,MAAA,UAAA,CAAW,IAAI,iBAAA,EAAmB;AAAA,QAChC,IAAA,EAAM,YAAA;AAAA,QACN,SAAA,EAAW,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,OACzB,CAAA;AAGD,MAAA,SAAA,CAAU,KAAA,GAAQ,EAAE,GAAG,gBAAA,EAAkB,GAAG,YAAA,EAAa;AAAA,IAC3D,SAAS,CAAA,EAAG;AACV,MAAA,KAAA,CAAM,KAAA,GAAQ,CAAA,YAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,+BAAA;AAC/C,MAAA,OAAA,CAAQ,KAAA,CAAM,4DAA4D,CAAC,CAAA;AAAA,IAC7E,CAAA,SAAE;AACA,MAAA,SAAA,CAAU,KAAA,GAAQ,KAAA;AAAA,IACpB;AAAA,EACF;AAOA,EAAA,eAAe,OAAA,GAAyB;AACtC,IAAA,IAAI,SAAS,SAAA,EAAW;AAEtB,MAAA,SAAA,CAAU,KAAA,GAAQ,EAAE,GAAG,gBAAA,EAAiB;AACxC,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,iBAAA,GAAoB,QAAA,IAAY,CAAA,EAAG,QAAQ,IAAI,QAAQ,CAAA,CAAA;AAC7D,IAAA,UAAA,CAAW,OAAO,iBAAiB,CAAA;AAEnC,IAAA,MAAM,cAAA,EAAe;AAAA,EACvB;AAGA,EAAA,IAAI,YAAA,IAAgB,IAAA,KAAS,SAAA,IAAa,UAAA,CAAW,WAAW,MAAA,EAAW;AACzE,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,cAAA,EAAe;AAAA,IACjB,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAO;AAAA,IACL,CAAA;AAAA,IACA,QAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA,SAAA,EAAW,SAAS,SAAS,CAAA;AAAA,IAC7B,KAAA,EAAO,SAAS,KAAK,CAAA;AAAA,IACrB,OAAA;AAAA,IACA,WAAA;AAAA,IACA,mBAAA;AAAA,IACA;AAAA,GACF;AACF;AC3PA,SAAS,eAAA,GAAgD;AACvD,EAAA,IAAI;AACF,IAAA,IAAI,OAAO,WAAA,KAAgB,WAAA,IAAe,WAAA,KAAgB,IAAA,EAAM;AAC9D,MAAA,OAAO,WAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,MAAA;AACT;AAiBA,SAAS,qBAAA,CACP,EAAA,EACA,MAAA,EACA,SAAA,EACA,iBACA,SAAA,EAC8C;AAC9C,EAAA,MAAM,OAAqD,EAAC;AAE5D,EAAA,IAAA,CAAK,IAAA,CAAK,EAAE,QAAA,EAAU,UAAA,EAAY,SAAS,EAAA,CAAG,KAAA,IAAS,WAAW,CAAA;AAClE,EAAA,IAAA,CAAK,IAAA,CAAK,EAAE,QAAA,EAAU,gBAAA,EAAkB,SAAS,EAAA,CAAG,WAAA,IAAe,iBAAiB,CAAA;AACpF,EAAA,IAAA,CAAK,IAAA,CAAK,EAAE,QAAA,EAAU,QAAA,EAAU,SAAS,EAAA,CAAG,GAAA,IAAO,WAAW,CAAA;AAC9D,EAAA,IAAA,CAAK,IAAA,CAAK,EAAE,QAAA,EAAU,SAAA,EAAW,SAAS,EAAA,CAAG,IAAA,IAAQ,WAAW,CAAA;AAEhE,EAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,KAAA,IAAS,MAAA,CAAO,MAAA,EAAQ,SAAA;AACzC,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,IAAA,CAAK,KAAK,EAAE,QAAA,EAAU,UAAA,EAAY,OAAA,EAAS,OAAO,CAAA;AAClD,IAAA,IAAI,EAAA,CAAG,YAAY,SAAA,EAAW;AAC5B,MAAA,IAAA,CAAK,IAAA,CAAK,EAAE,QAAA,EAAU,cAAA,EAAgB,SAAS,EAAA,CAAG,QAAA,IAAY,WAAW,CAAA;AAAA,IAC3E;AACA,IAAA,IAAI,GAAG,UAAA,EAAY;AACjB,MAAA,IAAA,CAAK,IAAA,CAAK,EAAE,QAAA,EAAU,gBAAA,EAAkB,SAAS,MAAA,CAAO,EAAA,CAAG,UAAU,CAAA,EAAG,CAAA;AAAA,IAC1E;AACA,IAAA,IAAI,GAAG,WAAA,EAAa;AAClB,MAAA,IAAA,CAAK,IAAA,CAAK,EAAE,QAAA,EAAU,iBAAA,EAAmB,SAAS,MAAA,CAAO,EAAA,CAAG,WAAW,CAAA,EAAG,CAAA;AAAA,IAC5E;AAAA,EACF;AAEA,EAAA,IAAI,OAAO,QAAA,EAAU;AACnB,IAAA,IAAA,CAAK,KAAK,EAAE,QAAA,EAAU,gBAAgB,OAAA,EAAS,MAAA,CAAO,UAAU,CAAA;AAAA,EAClE;AAEA,EAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,IAAA,IAAA,CAAK,KAAK,EAAE,QAAA,EAAU,aAAa,OAAA,EAAS,MAAA,CAAO,QAAQ,CAAA;AAAA,EAC7D;AAGA,EAAA,IAAI,EAAA,CAAG,SAAS,SAAA,EAAW;AACzB,IAAA,IAAI,GAAG,aAAA,EAAe;AACpB,MAAA,IAAA,CAAK,KAAK,EAAE,QAAA,EAAU,0BAA0B,OAAA,EAAS,EAAA,CAAG,eAAe,CAAA;AAAA,IAC7E;AACA,IAAA,IAAI,GAAG,YAAA,EAAc;AACnB,MAAA,IAAA,CAAK,KAAK,EAAE,QAAA,EAAU,yBAAyB,OAAA,EAAS,EAAA,CAAG,cAAc,CAAA;AAAA,IAC3E;AACA,IAAA,IAAI,GAAG,MAAA,EAAQ;AACb,MAAA,IAAA,CAAK,KAAK,EAAE,QAAA,EAAU,kBAAkB,OAAA,EAAS,EAAA,CAAG,QAAQ,CAAA;AAAA,IAC9D;AACA,IAAA,IAAI,GAAG,OAAA,EAAS;AACd,MAAA,IAAA,CAAK,KAAK,EAAE,QAAA,EAAU,mBAAmB,OAAA,EAAS,EAAA,CAAG,SAAS,CAAA;AAAA,IAChE;AACA,IAAA,IAAI,GAAG,IAAA,EAAM;AACX,MAAA,EAAA,CAAG,IAAA,CAAK,OAAA,CAAQ,CAAC,GAAA,KAAQ;AACvB,QAAA,IAAA,CAAK,KAAK,EAAE,QAAA,EAAU,aAAA,EAAe,OAAA,EAAS,KAAK,CAAA;AAAA,MACrD,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AAEA,EAAA,OAAO,IAAA;AACT;AAKA,SAAS,mBAAA,CACP,OAAA,EACA,MAAA,EACA,SAAA,EACA,eAAA,EAC0C;AAC1C,EAAA,MAAM,OAAiD,EAAC;AAExD,EAAA,IAAA,CAAK,IAAA,CAAK,EAAE,IAAA,EAAM,cAAA,EAAgB,SAAS,OAAA,CAAQ,IAAA,IAAQ,uBAAuB,CAAA;AAClF,EAAA,IAAA,CAAK,IAAA,CAAK,EAAE,IAAA,EAAM,eAAA,EAAiB,SAAS,OAAA,CAAQ,KAAA,IAAS,WAAW,CAAA;AACxE,EAAA,IAAA,CAAK,IAAA,CAAK,EAAE,IAAA,EAAM,qBAAA,EAAuB,SAAS,OAAA,CAAQ,WAAA,IAAe,iBAAiB,CAAA;AAE1F,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,IAAS,MAAA,CAAO,MAAA,EAAQ,cAAA;AAC9C,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,IAAA,CAAK,KAAK,EAAE,IAAA,EAAM,eAAA,EAAiB,OAAA,EAAS,OAAO,CAAA;AACnD,IAAA,IAAI,OAAA,CAAQ,YAAY,SAAA,EAAW;AACjC,MAAA,IAAA,CAAK,IAAA,CAAK,EAAE,IAAA,EAAM,mBAAA,EAAqB,SAAS,OAAA,CAAQ,QAAA,IAAY,WAAW,CAAA;AAAA,IACjF;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,IAAQ,MAAA,CAAO,MAAA,EAAQ,OAAA;AAC5C,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,IAAA,CAAK,IAAA,CAAK,EAAE,IAAA,EAAM,cAAA,EAAgB,OAAA,EAAS,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAAI,IAAA,GAAO,CAAA,CAAA,EAAI,IAAI,IAAI,CAAA;AAAA,EACvF;AAEA,EAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,IAAA,IAAA,CAAK,IAAA,CAAK;AAAA,MACR,IAAA,EAAM,iBAAA;AAAA,MACN,OAAA,EAAS,OAAA,CAAQ,OAAA,CAAQ,UAAA,CAAW,GAAG,IAAI,OAAA,CAAQ,OAAA,GAAU,CAAA,CAAA,EAAI,OAAA,CAAQ,OAAO,CAAA;AAAA,KACjF,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,IAAA;AACT;AAKA,SAAS,cAAA,CAAe,SAA4B,MAAA,EAAmC;AACrF,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW;AAC7B,IAAA,MAAM,IAAA,GAAgC;AAAA,MACpC,UAAA,EAAY,oBAAA;AAAA,MACZ,SAAS,MAAA,CAAO;AAAA,KAClB;AAGA,IAAA,IAAI,OAAO,UAAA,EAAY;AACrB,MAAA,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,MAAA,CAAO,UAAU,CAAA;AAAA,IACvC;AAGA,IAAA,IAAI,OAAO,IAAA,KAAS,SAAA,IAAa,OAAO,OAAA,IAAW,CAAC,KAAK,GAAA,EAAK;AAC5D,MAAA,IAAA,CAAK,MAAM,MAAA,CAAO,OAAA;AAAA,IACpB;AACA,IAAA,IAAI,OAAO,IAAA,KAAS,SAAA,IAAa,OAAO,QAAA,IAAY,CAAC,KAAK,IAAA,EAAM;AAC9D,MAAA,IAAA,CAAK,OAAO,MAAA,CAAO,QAAA;AAAA,IACrB;AAEA,IAAA,OAAO,IAAA;AAAA,EACT,CAAC,CAAA;AACH;AAKA,SAAS,cAAA,CACP,QAAA,EACA,QAAA,EACA,SAAA,EACiB;AACjB,EAAA,MAAM,MAAA,GAAS,SAAA,EAAW,MAAA,IAAU,EAAC;AACrC,EAAA,MAAM,IAAA,GAAO,SAAA,EAAW,KAAA,GAAQ,QAAQ,KAAK,EAAC;AAG9C,EAAA,IAAI,SAAA,GAAY,KAAK,SAAA,IAAa,EAAA;AAClC,EAAA,IAAI,CAAC,SAAA,IAAa,MAAA,CAAO,OAAA,EAAS;AAChC,IAAA,MAAM,OAAO,QAAA,KAAa,QAAA,KAAa,MAAA,GAAS,GAAA,GAAM,IAAI,QAAQ,CAAA,CAAA,CAAA;AAClE,IAAA,SAAA,GAAY,CAAA,EAAG,OAAO,OAAA,CAAQ,OAAA,CAAQ,OAAO,EAAE,CAAC,GAAG,IAAI,CAAA,CAAA;AAAA,EACzD;AAGA,EAAA,IAAI,KAAA,GAAQ,IAAA,CAAK,KAAA,IAAS,MAAA,CAAO,YAAA,IAAgB,QAAA;AACjD,EAAA,IAAI,CAAC,IAAA,CAAK,eAAA,IAAmB,MAAA,CAAO,aAAA,EAAe;AACjD,IAAA,KAAA,GAAQ,MAAA,CAAO,aAAA,CAAc,OAAA,CAAQ,IAAA,EAAM,KAAK,CAAA;AAAA,EAClD;AAGA,EAAA,MAAM,SAAA,GAA0C;AAAA,IAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,EAAW,IAAA,IAAQ,SAAA;AAAA,IAC9B,OAAO,IAAA,CAAK,SAAA,EAAW,SAAS,IAAA,CAAK,KAAA,IAAS,OAAO,YAAA,IAAgB,EAAA;AAAA,IACrE,aAAa,IAAA,CAAK,SAAA,EAAW,eAAe,IAAA,CAAK,WAAA,IAAe,OAAO,kBAAA,IAAsB,EAAA;AAAA,IAC7F,GAAG,IAAA,CAAK;AAAA,GACV;AAGA,EAAA,MAAM,OAAA,GAAsC;AAAA,IAC1C,IAAA,EAAM,IAAA,CAAK,OAAA,EAAS,IAAA,IAAQ,qBAAA;AAAA,IAC5B,GAAG,IAAA,CAAK;AAAA,GACV;AAGA,EAAA,MAAM,OAAA,GAAU,CAAC,GAAI,MAAA,CAAO,OAAA,IAAW,EAAC,EAAI,GAAI,IAAA,CAAK,OAAA,IAAW,EAAG,CAAA;AAEnE,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,WAAA,EAAa,IAAA,CAAK,WAAA,IAAe,MAAA,CAAO,kBAAA,IAAsB,EAAA;AAAA,IAC9D,SAAA;AAAA,IACA,MAAA,EAAQ,IAAA,CAAK,MAAA,IAAU,MAAA,CAAO,MAAA,IAAU,eAAA;AAAA,IACxC,SAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA,EAAY,IAAA,CAAK,UAAA,IAAc;AAAC,GAClC;AACF;AASO,SAAS,MAAA,CAAO,UAAkB,QAAA,EAAiC;AACxE,EAAA,MAAM,YAAY,eAAA,EAAgB;AAClC,EAAA,MAAM,kBAAkB,SAAA,KAAc,MAAA;AAGtC,EAAA,MAAM,MAAA,GAAuCA,QAAAA;AAAA,IAAS,MACpD,cAAA,CAAe,QAAA,EAAU,QAAA,EAAU,SAAS;AAAA,GAC9C;AAKA,EAAA,SAAS,SAAA,GAAsB;AAC7B,IAAA,OAAO,eAAe,MAAA,CAAO,KAAA,CAAM,SAAS,SAAA,EAAW,MAAA,IAAU,EAAE,CAAA;AAAA,EACrE;AAKA,EAAA,SAAS,YAAA,GAAuB;AAC9B,IAAA,OAAO,OAAO,KAAA,CAAM,SAAA;AAAA,EACtB;AAKA,EAAA,SAAS,UAAU,SAAA,EAAiC;AAClD,IAAA,MAAM,WAAW,MAAA,CAAO,KAAA;AACxB,IAAA,MAAM,MAAA,GAAS,SAAA,EAAW,MAAA,IAAU,EAAC;AAErC,IAAA,MAAM,KAAA,GAAQ,SAAA,EAAW,KAAA,IAAS,QAAA,CAAS,KAAA;AAC3C,IAAA,MAAM,WAAA,GAAc,SAAA,EAAW,WAAA,IAAe,QAAA,CAAS,WAAA;AAGvD,IAAA,MAAM,OAA0B,EAAC;AAGjC,IAAA,IAAA,CAAK,KAAK,EAAE,IAAA,EAAM,aAAA,EAAe,OAAA,EAAS,aAAa,CAAA;AACvD,IAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,MAAA,IAAA,CAAK,KAAK,EAAE,IAAA,EAAM,UAAU,OAAA,EAAS,QAAA,CAAS,QAAQ,CAAA;AAAA,IACxD;AAGA,IAAA,IAAI,MAAA,CAAO,cAAc,MAAA,EAAQ;AAC/B,MAAA,IAAA,CAAK,IAAA,CAAK,EAAE,IAAA,EAAM,0BAAA,EAA4B,SAAS,MAAA,CAAO,YAAA,CAAa,QAAQ,CAAA;AAAA,IACrF;AACA,IAAA,IAAI,MAAA,CAAO,cAAc,IAAA,EAAM;AAC7B,MAAA,IAAA,CAAK,IAAA,CAAK,EAAE,IAAA,EAAM,eAAA,EAAiB,SAAS,MAAA,CAAO,YAAA,CAAa,MAAM,CAAA;AAAA,IACxE;AAGA,IAAA,MAAM,MAAA,GAAS,qBAAA;AAAA,MACb,QAAA,CAAS,SAAA;AAAA,MACT,MAAA;AAAA,MACA,KAAA;AAAA,MACA,WAAA;AAAA,MACA,QAAA,CAAS;AAAA,KACX;AACA,IAAA,IAAA,CAAK,IAAA,CAAK,GAAG,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO,EAAE,QAAA,EAAU,CAAA,CAAE,QAAA,EAAU,OAAA,EAAS,CAAA,CAAE,OAAA,GAAU,CAAC,CAAA;AAG9E,IAAA,MAAM,cAAc,mBAAA,CAAoB,QAAA,CAAS,OAAA,EAAS,MAAA,EAAQ,OAAO,WAAW,CAAA;AACpF,IAAA,IAAA,CAAK,IAAA,CAAK,GAAG,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,MAAO,EAAE,IAAA,EAAM,CAAA,CAAE,IAAA,EAAM,OAAA,EAAS,CAAA,CAAE,OAAA,GAAU,CAAC,CAAA;AAG3E,IAAA,IAAI,WAAW,IAAA,EAAM;AACnB,MAAA,IAAA,CAAK,IAAA,CAAK,GAAG,SAAA,CAAU,IAAI,CAAA;AAAA,IAC7B;AAGA,IAAA,MAAM,OAA0B,EAAC;AAGjC,IAAA,IAAI,SAAS,SAAA,EAAW;AACtB,MAAA,IAAA,CAAK,KAAK,EAAE,GAAA,EAAK,aAAa,IAAA,EAAM,QAAA,CAAS,WAAW,CAAA;AAAA,IAC1D;AAGA,IAAA,QAAA,CAAS,UAAA,CAAW,OAAA,CAAQ,CAAC,GAAA,KAAQ;AACnC,MAAA,IAAA,CAAK,IAAA,CAAK,EAAE,GAAA,EAAK,WAAA,EAAa,IAAA,EAAM,IAAI,IAAA,EAAM,QAAA,EAAU,GAAA,CAAI,QAAA,EAAU,CAAA;AAAA,IACxE,CAAC,CAAA;AAGD,IAAA,MAAM,OAAA,GAAU,SAAA,EAAW,OAAA,IAAW,SAAA,EAAU;AAChD,IAAA,MAAM,MAAA,GAA8B,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,MAAY;AAAA,MAC3D,IAAA,EAAM,qBAAA;AAAA,MACN,QAAA,EAAU,IAAA,CAAK,SAAA,CAAU,MAAM;AAAA,KACjC,CAAE,CAAA;AAGF,IAAA,OAAA,CAAQ;AAAA,MACN,KAAA;AAAA,MACA,IAAA;AAAA,MACA,IAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH;AAEA,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,SAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,GACF;AACF;AAiBO,SAAS,cACd,aAAA,EACuD;AACvD,EAAA,OAAO,SAAS,UAAA,CAAW,QAAA,EAAkB,QAAA,EAAiC;AAG5E,IAAA,OAAO,MAAA,CAAO,UAAU,QAAQ,CAAA;AAAA,EAClC,CAAA;AACF;AC9VA,SAASC,UAAAA,CAAU,GAAA,EAAa,YAAA,GAAe,EAAA,EAAY;AACzD,EAAA,IAAI;AACF,IAAA,IAAI,OAAO,MAAA,CAAA,IAAA,KAAgB,WAAA,IAAe,MAAA,CAAA,IAAA,CAAY,GAAA,EAAK;AACzD,MAAA,MAAM,KAAA,GAAS,MAAA,CAAA,IAAA,CAAY,GAAA,CAA2C,GAAG,CAAA;AACzE,MAAA,IAAI,KAAA,KAAU,QAAW,OAAO,KAAA;AAAA,IAClC;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,YAAA;AACT;AAGA,IAAM,iBAAA,uBAAwB,GAAA,EAAsD;AACpF,IAAM,SAAA,GAAY,IAAI,EAAA,GAAK,GAAA;AASpB,SAAS,eAAA,CACd,OAAA,EACA,OAAA,GAAsC,EAAC,EACnB;AACpB,EAAA,MAAM,EAAE,YAAA,GAAe,IAAA,EAAK,GAAI,OAAA;AAEhC,EAAA,MAAM,UAAA,GAAaA,UAAAA,CAAU,mBAAA,EAAqB,sCAAsC,CAAA;AACxF,EAAA,MAAM,QAAA,GAAWA,UAAAA,CAAU,gBAAA,EAAkB,EAAE,CAAA;AAE/C,EAAA,MAAM,WAAA,GAAcC,IAAwB,IAAI,CAAA;AAChD,EAAA,MAAM,SAAA,GAAYA,IAAI,KAAK,CAAA;AAC3B,EAAA,MAAM,KAAA,GAAQA,IAAmB,IAAI,CAAA;AAErC,EAAA,eAAe,iBAAA,GAAmC;AAChD,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,KAAA,CAAM,KAAA,GAAQ,6CAAA;AACd,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,QAAA,GAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA;AACvC,IAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,GAAA,CAAI,QAAQ,CAAA;AAC7C,IAAA,IAAI,MAAA,IAAU,MAAA,CAAO,SAAA,GAAY,IAAA,CAAK,KAAI,EAAG;AAC3C,MAAA,WAAA,CAAY,QAAQ,MAAA,CAAO,IAAA;AAC3B,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,KAAA,GAAQ,IAAA;AAClB,IAAA,KAAA,CAAM,KAAA,GAAQ,IAAA;AAEd,IAAA,IAAI;AACF,MAAA,MAAM,MAAM,CAAA,EAAG,UAAU,CAAA,cAAA,EAAiB,QAAQ,kBAAkB,OAAO,CAAA,CAAA;AAC3E,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,QAChC,OAAA,EAAS;AAAA,UACP,MAAA,EAAQ;AAAA;AACV,OACD,CAAA;AAED,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,UAAA,KAAA,CAAM,KAAA,GAAQ,6BAA6B,OAAO,CAAA,UAAA,CAAA;AAClD,UAAA;AAAA,QACF;AACA,QAAA,MAAM,IAAI,MAAM,CAAA,KAAA,EAAQ,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,QAAA,CAAS,UAAU,CAAA,CAAE,CAAA;AAAA,MACnE;AAEA,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAGjC,MAAA,MAAM,IAAA,GAAoB;AAAA,QACxB,SAAS,IAAA,CAAK,OAAA;AAAA,QACd,OAAO,IAAA,CAAK,KAAA;AAAA,QACZ,OAAA,EAAS,KAAK,OAAA,IAAW,EAAA;AAAA,QACzB,aAAA,EAAe,IAAA,CAAK,aAAA,IAAiB,IAAA,CAAK,KAAA,IAAS,EAAA;AAAA,QACnD,WAAA,EAAa,KAAK,WAAA,IAAe,CAAA;AAAA,QACjC,WAAA,EAAa,IAAA,CAAK,WAAA,IAAe,IAAA,CAAK,UAAA,IAAc;AAAA,OACtD;AAGA,MAAA,iBAAA,CAAkB,IAAI,QAAA,EAAU;AAAA,QAC9B,IAAA,EAAM,IAAA;AAAA,QACN,SAAA,EAAW,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,OACzB,CAAA;AAED,MAAA,WAAA,CAAY,KAAA,GAAQ,IAAA;AAAA,IACtB,SAAS,CAAA,EAAG;AACV,MAAA,KAAA,CAAM,KAAA,GAAQ,CAAA,YAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,8BAAA;AAC/C,MAAA,OAAA,CAAQ,KAAA,CAAM,2DAA2D,CAAC,CAAA;AAAA,IAC5E,CAAA,SAAE;AACA,MAAA,SAAA,CAAU,KAAA,GAAQ,KAAA;AAAA,IACpB;AAAA,EACF;AAEA,EAAA,eAAe,OAAA,GAAyB;AAEtC,IAAA,MAAM,QAAA,GAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA;AACvC,IAAA,iBAAA,CAAkB,OAAO,QAAQ,CAAA;AACjC,IAAA,MAAM,iBAAA,EAAkB;AAAA,EAC1B;AAGA,EAAA,IAAI,YAAA,IAAgB,OAAO,MAAA,KAAW,WAAA,EAAa;AACjD,IAAAC,UAAU,MAAM;AACd,MAAA,iBAAA,EAAkB;AAAA,IACpB,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAO;AAAA,IACL,WAAA;AAAA,IACA,SAAA;AAAA,IACA,KAAA;AAAA,IACA;AAAA,GACF;AACF;AC1HA,SAASF,UAAAA,CAAU,GAAA,EAAa,YAAA,GAAe,EAAA,EAAY;AACzD,EAAA,IAAI;AACF,IAAA,IAAI,OAAO,MAAA,CAAA,IAAA,KAAgB,WAAA,IAAe,MAAA,CAAA,IAAA,CAAY,GAAA,EAAK;AACzD,MAAA,MAAM,KAAA,GAAS,MAAA,CAAA,IAAA,CAAY,GAAA,CAA2C,GAAG,CAAA;AACzE,MAAA,IAAI,KAAA,KAAU,QAAW,OAAO,KAAA;AAAA,IAClC;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,YAAA;AACT;AAGA,IAAI,YAAA,GAA8D,IAAA;AAClE,IAAMG,UAAAA,GAAY,KAAK,EAAA,GAAK,GAAA;AAQrB,SAAS,cAAA,CAAe,OAAA,GAAsC,EAAC,EAAsB;AAC1F,EAAA,MAAM,EAAE,YAAA,GAAe,IAAA,EAAK,GAAI,OAAA;AAEhC,EAAA,MAAM,UAAA,GAAaH,UAAAA,CAAU,mBAAA,EAAqB,sCAAsC,CAAA;AACxF,EAAA,MAAM,QAAA,GAAWA,UAAAA,CAAU,gBAAA,EAAkB,EAAE,CAAA;AAE/C,EAAA,MAAM,OAAA,GAAUC,IAAmB,IAAI,CAAA;AACvC,EAAA,MAAM,SAAA,GAAYA,IAAI,KAAK,CAAA;AAE3B,EAAA,MAAM,eAAA,GAAkBF,SAAS,MAAM;AACrC,IAAA,IAAI,CAAC,OAAA,CAAQ,KAAA,EAAO,OAAO,sBAAA;AAC3B,IAAA,OAAO,CAAA,cAAA,EAAiB,QAAQ,KAAK,CAAA,CAAA;AAAA,EACvC,CAAC,CAAA;AAED,EAAA,eAAe,YAAA,GAA8B;AAC3C,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,YAAA,IAAgB,YAAA,CAAa,SAAA,GAAY,IAAA,CAAK,KAAI,EAAG;AACvD,MAAA,OAAA,CAAQ,QAAQ,YAAA,CAAa,OAAA;AAC7B,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,KAAA,GAAQ,IAAA;AAElB,IAAA,IAAI;AAEF,MAAA,MAAM,GAAA,GAAM,CAAA,EAAG,UAAU,CAAA,cAAA,EAAiB,QAAQ,CAAA,qBAAA,CAAA;AAClD,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,QAChC,OAAA,EAAS;AAAA,UACP,MAAA,EAAQ;AAAA;AACV,OACD,CAAA;AAED,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAEhB,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,MAAA,MAAM,gBAAgB,IAAA,CAAK,OAAA;AAG3B,MAAA,YAAA,GAAe;AAAA,QACb,OAAA,EAAS,aAAA;AAAA,QACT,SAAA,EAAW,IAAA,CAAK,GAAA,EAAI,GAAII;AAAA,OAC1B;AAEA,MAAA,OAAA,CAAQ,KAAA,GAAQ,aAAA;AAAA,IAClB,SAAS,CAAA,EAAG;AACV,MAAA,OAAA,CAAQ,KAAA,CAAM,0DAA0D,CAAC,CAAA;AAAA,IAC3E,CAAA,SAAE;AACA,MAAA,SAAA,CAAU,KAAA,GAAQ,KAAA;AAAA,IACpB;AAAA,EACF;AAGA,EAAA,IAAI,YAAA,IAAgB,OAAO,MAAA,KAAW,WAAA,EAAa;AACjD,IAAAD,UAAU,MAAM;AACd,MAAA,YAAA,EAAa;AAAA,IACf,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;ACxBO,SAAS,iBAAiB,MAAA,EAAwD;AACvF,EAAA,MAAM;AAAA,IACJ,MAAA;AAAA,IACA,CAAA;AAAA,IACA,WAAW,EAAC;AAAA,IACZ,QAAA,GAAW;AAAA,GACb,GAAI,MAAA;AAEJ,EAAA,MAAM,KAAA,GAAQH,SAA8B,MAAM;AAChD,IAAA,MAAM,SAA8B,EAAC;AAGrC,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,QAAA,EAAU,CAAA,EAAA,EAAK;AACjC,MAAA,MAAM,MAAA,GAAS,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,CAAC,CAAA,IAAA,CAAA;AAC7B,MAAA,MAAM,OAAA,GAAU,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,CAAC,CAAA,KAAA,CAAA;AAC9B,MAAA,MAAM,MAAA,GAAS,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,CAAC,CAAA,IAAA,CAAA;AAG7B,MAAA,MAAM,GAAA,GAAM,CAAA,CAAE,MAAA,EAAQ,EAAE,CAAA;AAGxB,MAAA,IAAI,CAAC,GAAA,IAAO,GAAA,KAAQ,MAAA,EAAQ;AAC1B,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,SAAA,GAAY,CAAA,CAAE,OAAA,EAAS,OAAO,CAAA;AAEpC,MAAA,IAAI,IAAA,GAAoD,OAAA;AACxD,MAAA,IAAI,SAAA,KAAc,SAAS,IAAA,GAAO,OAAA;AAAA,WAAA,IACzB,SAAA,KAAc,WAAW,IAAA,GAAO,SAAA;AAAA,WAAA,IAChC,SAAA,KAAc,aAAa,IAAA,GAAO,WAAA;AAC3C,MAAA,MAAM,GAAA,GAAM,CAAA,CAAE,MAAA,EAAQ,EAAE,CAAA;AAExB,MAAA,MAAA,CAAO,IAAA,CAAK;AAAA,QACV,GAAA;AAAA,QACA,IAAA;AAAA,QACA,GAAA,EAAK,GAAA,IAAO,GAAA,KAAQ,MAAA,GAAS,GAAA,GAAM,MAAA;AAAA;AAAA;AAAA,QAGnC,UAAA,EAAY,IAAA,KAAS,OAAA,IAAW,aAAA,CAAc,GAAG;AAAA,OAClD,CAAA;AAAA,IACH;AAGA,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,MAAA,OAAO,QAAA;AAAA,IACT;AAEA,IAAA,OAAO,MAAA;AAAA,EACT,CAAC,CAAA;AAED,EAAA,MAAM,WAAWA,QAAAA,CAAS,MAAM,KAAA,CAAM,KAAA,CAAM,SAAS,CAAC,CAAA;AACtD,EAAA,MAAM,KAAA,GAAQA,QAAAA,CAAS,MAAM,KAAA,CAAM,MAAM,MAAM,CAAA;AAE/C,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACF;AACF;AChGO,SAAS,mBAAmB,OAAA,EAA2D;AAC5F,EAAA,MAAM,MAAA,GAASA,QAAAA;AAAA,IAAS,MACtB,sBAAA,CAAuB;AAAA,MACrB,GAAA,EAAK,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AAAA,MACxB,GAAA,EAAK,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AAAA,MACxB,OAAA,EAAS,OAAA,CAAQ,OAAA,CAAQ,OAAO,CAAA;AAAA,MAChC,KAAA,EAAO,OAAA,CAAQ,OAAA,CAAQ,KAAK,CAAA;AAAA,MAC5B,QAAA,EAAU,OAAA,CAAQ,OAAA,CAAQ,QAAQ,CAAA,IAAK;AAAA,KACxC;AAAA,GACH;AAGA,EAAA,OAAO;AAAA,IACL,IAAI,QAAA,GAAW;AACb,MAAA,OAAO,OAAO,KAAA,CAAM,QAAA;AAAA,IACtB,CAAA;AAAA,IACA,IAAI,OAAA,GAAU;AACZ,MAAA,OAAO,OAAO,KAAA,CAAM,OAAA;AAAA,IACtB,CAAA;AAAA,IACA,IAAI,WAAA,GAAc;AAChB,MAAA,OAAO,OAAO,KAAA,CAAM,WAAA;AAAA,IACtB;AAAA,GACF;AACF;AChEA,SAAS,oBAAoB,KAAA,EAAmC;AAC9D,EAAA,OAAO,KAAA,CAAM,IAAI,CAAA,IAAA,MAAS;AAAA,IACxB,GAAG,IAAA;AAAA,IACH,cAAA,EAAgB;AAAA,GAClB,CAAE,CAAA;AACJ;AAoBA,IAAM,sBAAA,GAAyB,UAAA,CAAyC,EAAE,CAAA;AAC1E,IAAI,4BAAA,GAA+B,CAAA;AAQnC,SAAS,oBAAoB,IAAA,EAAkD;AAC7E,EAAA,MAAM,KAAK,MAAA,CAAO,IAAA,CAAK,EAAA,IAAM,EAAE,EAAE,IAAA,EAAK;AACtC,EAAA,IAAI,CAAC,EAAA,EAAI;AACP,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,IAAA,CAAK,MAAA,IAAU,CAAC,CAAA;AACzC,EAAA,MAAM,SAAS,MAAA,CAAO,QAAA,CAAS,SAAS,CAAA,GACpC,KAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,GAAG,IAAA,CAAK,KAAA,CAAM,SAAS,CAAC,CAAC,CAAA,GAC9C,CAAA;AAEJ,EAAA,OAAO;AAAA,IACL,EAAA;AAAA,IACA,QAAA,EAAU,MAAA,CAAO,IAAA,CAAK,QAAA,IAAY,QAAQ,CAAA;AAAA,IAC1C,MAAA;AAAA,IACA,UAAA,EAAY,MAAA,CAAO,IAAA,CAAK,UAAA,IAAc,WAAW,CAAA;AAAA,IACjD,cAAA,EAAgB,MAAA;AAAA,IAChB,MAAM,IAAA,CAAK,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA;AAAA,IACtC,MAAM,IAAA,CAAK,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA;AAAA,IACtC,WAAW,IAAA,CAAK,SAAA,GAAY,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA,GAAI,MAAA;AAAA,IACrD,cAAc,IAAA,CAAK,YAAA,GAAe,MAAA,CAAO,IAAA,CAAK,YAAY,CAAA,GAAI,MAAA;AAAA,IAC9D,oBAAoB,IAAA,CAAK,kBAAA,GAAqB,MAAA,CAAO,IAAA,CAAK,kBAAkB,CAAA,GAAI,MAAA;AAAA,IAChF,WAAW,IAAA,CAAK,SAAA,GAAY,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA,GAAI;AAAA,GACvD;AACF;AAEA,SAAS,oBAAoB,KAAA,EAA8B;AACzD,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACzB,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,OAAO,MACJ,MAAA,CAAO,CAAC,IAAA,KAA0C,IAAA,IAAQ,QAAQ,OAAO,IAAA,KAAS,QAAQ,CAAA,CAC1F,IAAI,mBAAmB,CAAA,CACvB,OAAO,CAAC,IAAA,KAA6B,QAAQ,IAAI,CAAA;AACtD;AAEA,SAAS,0BAA0B,KAAA,EAAc;AAC/C,EAAA,MAAM,MAAA,GAAS,KAAA,YAAiB,WAAA,IAAe,KAAA,CAAM,MAAA,IAAU,IAAA,IAAQ,OAAO,KAAA,CAAM,MAAA,KAAW,QAAA,GAC3F,KAAA,CAAM,MAAA,GACN,IAAA;AACJ,EAAA,MAAM,GAAA,GAAM,OAAO,MAAA,EAAQ,GAAA,KAAQ,WAAW,MAAA,CAAO,GAAA,CAAI,MAAK,GAAI,EAAA;AAClE,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,EAAE,GAAG,sBAAA,CAAuB,KAAA,EAAM;AAC/C,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAA,EAAQ,OAAO,CAAA,EAAG;AAClC,IAAA,IAAA,CAAK,GAAG,CAAA,GAAI,mBAAA,CAAoB,MAAA,CAAO,OAAO,CAAA;AAAA,EAChD,CAAA,MAAO;AACL,IAAA,OAAO,KAAK,GAAG,CAAA;AAAA,EACjB;AACA,EAAA,sBAAA,CAAuB,KAAA,GAAQ,IAAA;AACjC;AAEO,SAAS,iBAAiB,MAAA,EAAwD;AACvF,EAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAU,QAAA,GAAW,IAAG,GAAI,MAAA;AAEhD,EAAAG,UAAU,MAAM;AACd,IAAA,4BAAA,IAAgC,CAAA;AAChC,IAAA,IAAI,iCAAiC,CAAA,EAAG;AACtC,MAAA,MAAA,CAAO,gBAAA,CAAiB,uBAAuB,yBAAyB,CAAA;AAAA,IAC1E;AAAA,EACF,CAAC,CAAA;AAED,EAAA,WAAA,CAAY,MAAM;AAChB,IAAA,4BAAA,GAA+B,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,4BAAA,GAA+B,CAAC,CAAA;AAC3E,IAAA,IAAI,iCAAiC,CAAA,EAAG;AACtC,MAAA,MAAA,CAAO,mBAAA,CAAoB,uBAAuB,yBAAyB,CAAA;AAAA,IAC7E;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAM,OAAA,GAAUH,SAAuB,MAAM;AAC3C,IAAA,IAAI,OAAO,SAAA,CAAU,cAAA,CAAe,KAAK,sBAAA,CAAuB,KAAA,EAAO,UAAU,CAAA,EAAG;AAClF,MAAA,OAAO,sBAAA,CAAuB,KAAA,CAAM,UAAU,CAAA,IAAK,EAAC;AAAA,IACtD;AAEA,IAAA,IAAI,OAAO,eAAA,KAAoB,WAAA,IAAe,eAAA,IAAmB,IAAA,EAAM;AACrE,MAAA,OAAO,oBAAoB,QAAQ,CAAA;AAAA,IACrC;AAEA,IAAA,IAAI,UAAA,GAAsB,IAAA;AAE1B,IAAA,IAAI,QAAA,IAAY,eAAA,CAAgB,KAAA,GAAQ,QAAQ,CAAA,EAAG;AACjD,MAAA,UAAA,GAAa,eAAA,CAAgB,KAAA,CAAM,QAAQ,CAAA,CAAE,WAAW,UAAU,CAAA,MAAA,CAAQ,CAAA,IACrE,eAAA,CAAgB,KAAA,CAAM,QAAQ,CAAA,CAAE,CAAA,QAAA,EAAW,UAAU,CAAA,CAAE,CAAA;AAAA,IAC9D;AAEA,IAAA,IAAI,CAAC,UAAA,IAAc,eAAA,CAAgB,MAAA,EAAQ;AACzC,MAAA,UAAA,GAAa,eAAA,CAAgB,MAAA,CAAO,CAAA,QAAA,EAAW,UAAU,CAAA,MAAA,CAAQ,KAC5D,eAAA,CAAgB,MAAA,CAAO,CAAA,QAAA,EAAW,UAAU,CAAA,CAAE,CAAA;AAAA,IACrD;AAEA,IAAA,IAAI,CAAC,UAAA,IAAc,CAAC,KAAA,CAAM,OAAA,CAAQ,UAAU,CAAA,EAAG;AAC7C,MAAA,OAAO,oBAAoB,QAAQ,CAAA;AAAA,IACrC;AAEA,IAAA,OAAO,oBAAoB,UAAU,CAAA;AAAA,EACvC,CAAC,CAAA;AAED,EAAA,MAAM,aAAaA,QAAAA,CAAS,MAAM,OAAA,CAAQ,KAAA,CAAM,SAAS,CAAC,CAAA;AAC1D,EAAA,MAAM,KAAA,GAAQA,QAAAA,CAAS,MAAM,OAAA,CAAQ,MAAM,MAAM,CAAA;AAEjD,EAAA,OAAO,EAAE,OAAA,EAAS,UAAA,EAAY,KAAA,EAAM;AACtC","file":"index.js","sourcesContent":["/**\n * useTextContent Composable\n *\n * Provides text content management with build-time injection support and optional\n * runtime API overrides for DCS-managed customer sites.\n *\n * Content resolution order:\n * 1. Runtime API overrides (premium tier only, if mode is 'runtime')\n * 2. Build-time content from .dcs/content.yaml (injected via dcsContentPlugin)\n * 3. Hardcoded defaults passed to the composable\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useTextContent } from '@duffcloudservices/cms'\n *\n * const { t, isLoading, error } = useTextContent({\n * pageSlug: 'home',\n * defaults: {\n * 'hero.title': 'Welcome to Our Site',\n * 'hero.subtitle': 'Build something amazing',\n * 'cta.primary': 'Get Started'\n * }\n * })\n * </script>\n *\n * <template>\n * <h1>{{ t('hero.title') }}</h1>\n * <p>{{ t('hero.subtitle') }}</p>\n * <button>{{ t('cta.primary') }}</button>\n * </template>\n * ```\n */\n\nimport { ref, computed, readonly, onMounted, type Ref } from 'vue'\nimport type { DcsContentFile, TextContentConfig, TextContentReturn } from '../types/content'\n\n// Declare the global injected by dcsContentPlugin\ndeclare const __DCS_CONTENT__: DcsContentFile | undefined\n\n// Simple in-memory cache for runtime fetches\nconst fetchCache = new Map<string, { data: Record<string, string>; expiresAt: number }>()\n\n/**\n * Safely get build-time content configuration.\n * Returns undefined if not available (no content.yaml or plugin not configured).\n */\nfunction getBuildTimeContent(): DcsContentFile | undefined {\n try {\n if (typeof __DCS_CONTENT__ !== 'undefined' && __DCS_CONTENT__ !== null) {\n return __DCS_CONTENT__\n }\n } catch {\n // __DCS_CONTENT__ not defined - that's fine, use defaults\n }\n return undefined\n}\n\n/**\n * Get build-time content for a specific page, merging global and page-specific content.\n */\nfunction getBuildTimePageContent(pageSlug: string): Record<string, string> {\n const content = getBuildTimeContent()\n if (!content) return {}\n\n const global = content.global ?? {}\n const page = content.pages?.[pageSlug] ?? {}\n\n return { ...global, ...page }\n}\n\n/**\n * Get environment variable value, handling both Vite and process.env patterns.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n // Vite pattern\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n\n try {\n // Node.js pattern\n if (typeof process !== 'undefined' && process.env) {\n const value = process.env[key]\n if (value !== undefined) return value\n }\n } catch {\n // process not available\n }\n\n return defaultValue\n}\n\n/**\n * useTextContent composable for DCS-managed text content.\n *\n * @param config - Configuration object\n * @returns Text content helpers and state\n */\nexport function useTextContent(config: TextContentConfig): TextContentReturn {\n const {\n pageSlug,\n defaults,\n fetchOnMount = true,\n cacheKey,\n cacheTtl = 60000,\n } = config\n\n // Get configuration from environment\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', '')\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\n const textOverrideMode = getEnvVar('VITE_TEXT_OVERRIDE_MODE', 'commit')\n const mode: 'commit' | 'runtime' = textOverrideMode === 'runtime' ? 'runtime' : 'commit'\n\n // Get build-time content immediately (synchronous)\n const buildTimeContent = getBuildTimePageContent(pageSlug)\n const hasBuildTimeContent = Object.keys(buildTimeContent).length > 0\n\n // State - initialize with build-time content\n const overrides = ref<Record<string, string>>({ ...buildTimeContent })\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n\n // Computed merged texts (defaults + overrides)\n const texts = computed(() => ({ ...defaults, ...overrides.value }))\n\n /**\n * Get text by key with optional fallback.\n * Resolution order: overrides → defaults → fallback → key\n */\n function t(key: string, fallback?: string): string {\n return overrides.value[key] ?? defaults[key] ?? fallback ?? key\n }\n\n /**\n * Check if a key has an override (from build-time or runtime).\n */\n function hasOverride(key: string): boolean {\n return key in overrides.value\n }\n\n /**\n * Get an array of objects from indexed content keys.\n * Useful for lists where keys follow the pattern: arrayKey.index.property\n * Example: features.1.title, features.1.description, features.2.title, etc.\n *\n * @param arrayKey - The base key prefix (e.g., 'features', 'items')\n * @returns Array of objects with properties extracted from matching keys, sorted by index\n *\n * @example\n * ```ts\n * // Given content keys:\n * // positions.1.title = \"Software Engineer\"\n * // positions.1.description = \"Build cool stuff\"\n * // positions.2.title = \"Designer\"\n * // positions.2.description = \"Design cool stuff\"\n *\n * const positions = getArray('positions')\n * // Returns:\n * // [\n * // { _index: 1, title: \"Software Engineer\", description: \"Build cool stuff\" },\n * // { _index: 2, title: \"Designer\", description: \"Design cool stuff\" }\n * // ]\n * ```\n */\n function getArray(arrayKey: string): Array<Record<string, unknown> & { _index: number }> {\n const items: Record<number, Record<string, unknown> & { _index: number }> = {}\n const source = { ...defaults, ...overrides.value }\n\n Object.keys(source).forEach((key) => {\n if (key.startsWith(`${arrayKey}.`)) {\n const parts = key.split('.')\n // Format: arrayKey.index.property (or arrayKey.index.nested.property)\n // Example: features.1.title -> index=1, prop=title\n if (parts.length >= 3) {\n const index = Number.parseInt(parts[1], 10)\n const prop = parts.slice(2).join('.')\n\n if (!Number.isNaN(index)) {\n if (!items[index]) items[index] = { _index: index }\n items[index][prop] = source[key]\n }\n }\n }\n })\n\n return Object.values(items).sort((a, b) => a._index - b._index)\n }\n\n /**\n * Fetch runtime overrides from the API.\n * Only runs if mode is 'runtime' and API is configured.\n */\n async function fetchOverrides(): Promise<void> {\n // Skip API fetch in commit mode - use build-time content only\n if (mode !== 'runtime') {\n return\n }\n\n // Skip if API not configured\n if (!apiBaseUrl || !siteSlug) {\n console.warn(\n '[@duffcloudservices/cms] Runtime mode enabled but VITE_API_BASE_URL or VITE_SITE_SLUG not set'\n )\n return\n }\n\n // Check cache first\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\n const cached = fetchCache.get(effectiveCacheKey)\n if (cached && cached.expiresAt > Date.now()) {\n overrides.value = { ...buildTimeContent, ...cached.data }\n return\n }\n\n isLoading.value = true\n error.value = null\n\n try {\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/pages/${pageSlug}/text`\n const response = await fetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n if (response.status === 404) {\n // No overrides for this page - that's fine\n return\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const data = await response.json()\n const apiOverrides = data.overrides ?? data.texts ?? {}\n\n // Cache the result\n fetchCache.set(effectiveCacheKey, {\n data: apiOverrides,\n expiresAt: Date.now() + cacheTtl,\n })\n\n // Merge: build-time content is baseline, API overrides on top\n overrides.value = { ...buildTimeContent, ...apiOverrides }\n } catch (e) {\n error.value = e instanceof Error ? e.message : 'Failed to load text overrides'\n console.error('[@duffcloudservices/cms] Failed to fetch text overrides:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n /**\n * Manually refresh overrides.\n * In commit mode, resets to build-time content.\n * In runtime mode, fetches fresh data from API.\n */\n async function refresh(): Promise<void> {\n if (mode !== 'runtime') {\n // In commit mode, just reset to build-time content\n overrides.value = { ...buildTimeContent }\n return\n }\n\n // Clear cache for this key\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\n fetchCache.delete(effectiveCacheKey)\n\n await fetchOverrides()\n }\n\n // Fetch on mount if enabled and in runtime mode (browser only)\n if (fetchOnMount && mode === 'runtime' && globalThis.window !== undefined) {\n onMounted(() => {\n fetchOverrides()\n })\n }\n\n return {\n t,\n getArray,\n texts,\n overrides,\n isLoading: readonly(isLoading) as Ref<boolean>,\n error: readonly(error) as Ref<string | null>,\n refresh,\n hasOverride,\n hasBuildTimeContent,\n mode,\n }\n}\n","/**\n * useSEO Composable\n *\n * Provides SEO configuration with build-time injection support from .dcs/seo.yaml.\n * Generates meta tags, Open Graph, Twitter Cards, and JSON-LD structured data.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useSEO } from '@duffcloudservices/cms'\n *\n * const { applyHead, getSchema, config } = useSEO('home')\n *\n * // Apply all meta tags\n * applyHead()\n *\n * // Or customize before applying\n * applyHead({\n * title: 'Custom Override Title',\n * schemas: [...getSchema(), customSchema]\n * })\n * </script>\n * ```\n */\n\nimport { computed, type ComputedRef } from 'vue'\nimport { useHead } from '@unhead/vue'\nimport type {\n SeoConfiguration,\n GlobalSeoConfig,\n SeoOpenGraphConfig,\n SeoTwitterConfig,\n SeoSchemaConfig,\n ResolvedPageSeo,\n UseSeoReturn,\n HeadOverrides,\n} from '../types/seo'\n\n// Declare the global injected by dcsSeoPlugin\ndeclare const __DCS_SEO__: SeoConfiguration | undefined\n\n/**\n * Safely get build-time SEO configuration.\n * Returns undefined if not available (no seo.yaml or plugin not configured).\n */\nfunction getBuildTimeSeo(): SeoConfiguration | undefined {\n try {\n if (typeof __DCS_SEO__ !== 'undefined' && __DCS_SEO__ !== null) {\n return __DCS_SEO__\n }\n } catch {\n // __DCS_SEO__ not defined - that's fine, use defaults\n }\n return undefined\n}\n\n// =============================================================================\n// Meta Tag Generation Utilities\n// =============================================================================\n\ninterface HeadInput {\n title?: string\n titleTemplate?: string | ((title: string) => string)\n meta?: Array<{ name?: string; property?: string; content: string }>\n link?: Array<{ rel: string; href: string; hreflang?: string }>\n script?: Array<{ type: string; children: string }>\n}\n\n/**\n * Generate Open Graph meta tags from config\n */\nfunction generateOpenGraphMeta(\n og: SeoOpenGraphConfig,\n global: GlobalSeoConfig,\n pageTitle: string,\n pageDescription: string,\n canonical: string\n): Array<{ property: string; content: string }> {\n const tags: Array<{ property: string; content: string }> = []\n\n tags.push({ property: 'og:title', content: og.title || pageTitle })\n tags.push({ property: 'og:description', content: og.description || pageDescription })\n tags.push({ property: 'og:url', content: og.url || canonical })\n tags.push({ property: 'og:type', content: og.type || 'website' })\n\n const image = og.image || global.images?.ogDefault\n if (image) {\n tags.push({ property: 'og:image', content: image })\n if (og.imageAlt || pageTitle) {\n tags.push({ property: 'og:image:alt', content: og.imageAlt || pageTitle })\n }\n if (og.imageWidth) {\n tags.push({ property: 'og:image:width', content: String(og.imageWidth) })\n }\n if (og.imageHeight) {\n tags.push({ property: 'og:image:height', content: String(og.imageHeight) })\n }\n }\n\n if (global.siteName) {\n tags.push({ property: 'og:site_name', content: global.siteName })\n }\n\n if (global.locale) {\n tags.push({ property: 'og:locale', content: global.locale })\n }\n\n // Article-specific tags\n if (og.type === 'article') {\n if (og.publishedTime) {\n tags.push({ property: 'article:published_time', content: og.publishedTime })\n }\n if (og.modifiedTime) {\n tags.push({ property: 'article:modified_time', content: og.modifiedTime })\n }\n if (og.author) {\n tags.push({ property: 'article:author', content: og.author })\n }\n if (og.section) {\n tags.push({ property: 'article:section', content: og.section })\n }\n if (og.tags) {\n og.tags.forEach((tag) => {\n tags.push({ property: 'article:tag', content: tag })\n })\n }\n }\n\n return tags\n}\n\n/**\n * Generate Twitter Card meta tags from config\n */\nfunction generateTwitterMeta(\n twitter: SeoTwitterConfig,\n global: GlobalSeoConfig,\n pageTitle: string,\n pageDescription: string\n): Array<{ name: string; content: string }> {\n const tags: Array<{ name: string; content: string }> = []\n\n tags.push({ name: 'twitter:card', content: twitter.card || 'summary_large_image' })\n tags.push({ name: 'twitter:title', content: twitter.title || pageTitle })\n tags.push({ name: 'twitter:description', content: twitter.description || pageDescription })\n\n const image = twitter.image || global.images?.twitterDefault\n if (image) {\n tags.push({ name: 'twitter:image', content: image })\n if (twitter.imageAlt || pageTitle) {\n tags.push({ name: 'twitter:image:alt', content: twitter.imageAlt || pageTitle })\n }\n }\n\n const site = twitter.site || global.social?.twitter\n if (site) {\n tags.push({ name: 'twitter:site', content: site.startsWith('@') ? site : `@${site}` })\n }\n\n if (twitter.creator) {\n tags.push({\n name: 'twitter:creator',\n content: twitter.creator.startsWith('@') ? twitter.creator : `@${twitter.creator}`,\n })\n }\n\n return tags\n}\n\n/**\n * Generate JSON-LD script content from schemas\n */\nfunction generateJsonLd(schemas: SeoSchemaConfig[], global: GlobalSeoConfig): object[] {\n return schemas.map((schema) => {\n const base: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': schema.type,\n }\n\n // Merge properties\n if (schema.properties) {\n Object.assign(base, schema.properties)\n }\n\n // Auto-populate common properties from global config\n if (schema.type === 'WebSite' && global.siteUrl && !base.url) {\n base.url = global.siteUrl\n }\n if (schema.type === 'WebSite' && global.siteName && !base.name) {\n base.name = global.siteName\n }\n\n return base\n })\n}\n\n/**\n * Resolve page SEO by merging global defaults with page-specific config\n */\nfunction resolvePageSeo(\n pageSlug: string,\n pagePath: string | undefined,\n seoConfig: SeoConfiguration | undefined\n): ResolvedPageSeo {\n const global = seoConfig?.global ?? {}\n const page = seoConfig?.pages?.[pageSlug] ?? {}\n\n // Build canonical URL\n let canonical = page.canonical || ''\n if (!canonical && global.siteUrl) {\n const path = pagePath ?? (pageSlug === 'home' ? '/' : `/${pageSlug}`)\n canonical = `${global.siteUrl.replace(/\\/$/, '')}${path}`\n }\n\n // Build title\n let title = page.title || global.defaultTitle || pageSlug\n if (!page.noTitleTemplate && global.titleTemplate) {\n title = global.titleTemplate.replace('%s', title)\n }\n\n // Merge Open Graph\n const openGraph: ResolvedPageSeo['openGraph'] = {\n type: page.openGraph?.type || 'website',\n title: page.openGraph?.title || page.title || global.defaultTitle || '',\n description: page.openGraph?.description || page.description || global.defaultDescription || '',\n ...page.openGraph,\n }\n\n // Merge Twitter\n const twitter: ResolvedPageSeo['twitter'] = {\n card: page.twitter?.card || 'summary_large_image',\n ...page.twitter,\n }\n\n // Combine schemas (global + page)\n const schemas = [...(global.schemas ?? []), ...(page.schemas ?? [])]\n\n return {\n title,\n description: page.description || global.defaultDescription || '',\n canonical,\n robots: page.robots || global.robots || 'index, follow',\n openGraph,\n twitter,\n schemas,\n alternates: page.alternates ?? [],\n }\n}\n\n/**\n * useSEO composable for DCS-managed SEO configuration.\n *\n * @param pageSlug - Page slug matching entry in seo.yaml\n * @param pagePath - Optional page path for canonical URL generation\n * @returns SEO helpers and state\n */\nexport function useSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\n const seoConfig = getBuildTimeSeo()\n const hasBuildTimeSeo = seoConfig !== undefined\n\n // Computed resolved config\n const config: ComputedRef<ResolvedPageSeo> = computed(() =>\n resolvePageSeo(pageSlug, pagePath, seoConfig)\n )\n\n /**\n * Get JSON-LD schema objects for the page\n */\n function getSchema(): object[] {\n return generateJsonLd(config.value.schemas, seoConfig?.global ?? {})\n }\n\n /**\n * Get canonical URL for the page\n */\n function getCanonical(): string {\n return config.value.canonical\n }\n\n /**\n * Apply all meta tags via useHead\n */\n function applyHead(overrides?: HeadOverrides): void {\n const resolved = config.value\n const global = seoConfig?.global ?? {}\n\n const title = overrides?.title ?? resolved.title\n const description = overrides?.description ?? resolved.description\n\n // Build meta tags\n const meta: HeadInput['meta'] = []\n\n // Basic meta\n meta.push({ name: 'description', content: description })\n if (resolved.robots) {\n meta.push({ name: 'robots', content: resolved.robots })\n }\n\n // Verification codes\n if (global.verification?.google) {\n meta.push({ name: 'google-site-verification', content: global.verification.google })\n }\n if (global.verification?.bing) {\n meta.push({ name: 'msvalidate.01', content: global.verification.bing })\n }\n\n // Open Graph\n const ogMeta = generateOpenGraphMeta(\n resolved.openGraph,\n global,\n title,\n description,\n resolved.canonical\n )\n meta.push(...ogMeta.map((t) => ({ property: t.property, content: t.content })))\n\n // Twitter\n const twitterMeta = generateTwitterMeta(resolved.twitter, global, title, description)\n meta.push(...twitterMeta.map((t) => ({ name: t.name, content: t.content })))\n\n // Additional overrides\n if (overrides?.meta) {\n meta.push(...overrides.meta)\n }\n\n // Build links\n const link: HeadInput['link'] = []\n\n // Canonical\n if (resolved.canonical) {\n link.push({ rel: 'canonical', href: resolved.canonical })\n }\n\n // Alternate languages\n resolved.alternates.forEach((alt) => {\n link.push({ rel: 'alternate', href: alt.href, hreflang: alt.hreflang })\n })\n\n // Build scripts (JSON-LD)\n const schemas = overrides?.schemas ?? getSchema()\n const script: HeadInput['script'] = schemas.map((schema) => ({\n type: 'application/ld+json',\n children: JSON.stringify(schema),\n }))\n\n // Apply via useHead\n useHead({\n title,\n meta,\n link,\n script,\n })\n }\n\n return {\n config,\n applyHead,\n getSchema,\n getCanonical,\n hasBuildTimeSeo,\n }\n}\n\n/**\n * Create a typed useSEO function with site-specific defaults.\n * Useful for creating a site-wide wrapper.\n *\n * @example\n * ```ts\n * // composables/useSiteSeo.ts\n * import { createSiteSEO } from '@duffcloudservices/cms'\n *\n * export const useSiteSeo = createSiteSEO({\n * siteName: 'My Site',\n * siteUrl: 'https://example.com'\n * })\n * ```\n */\nexport function createSiteSEO(\n _siteDefaults: Partial<GlobalSeoConfig>\n): (pageSlug: string, pagePath?: string) => UseSeoReturn {\n return function siteUseSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\n // Note: siteDefaults would be used if we needed to override at runtime\n // but build-time injection handles this via dcsSeoPlugin\n return useSEO(pageSlug, pagePath)\n }\n}\n","/**\n * useReleaseNotes Composable\n *\n * Fetches and displays versioned release notes from the DCS Portal API.\n * Supports fetching specific versions or the latest release.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useReleaseNotes } from '@duffcloudservices/cms'\n * import { useRoute } from 'vue-router'\n *\n * const route = useRoute()\n * const version = route.params.version as string || 'latest'\n *\n * const { releaseNote, isLoading, error } = useReleaseNotes(version)\n * </script>\n *\n * <template>\n * <div v-if=\"isLoading\">Loading...</div>\n * <div v-else-if=\"error\">{{ error }}</div>\n * <article v-else-if=\"releaseNote\">\n * <h1>{{ releaseNote.title }}</h1>\n * <p>{{ releaseNote.summary }}</p>\n * <div v-html=\"renderedMarkdown\" />\n * </article>\n * </template>\n * ```\n */\n\nimport { ref, onMounted } from 'vue'\nimport type { ReleaseNote, ReleaseNotesReturn } from '../types/release-notes'\n\n/**\n * Get environment variable value.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n return defaultValue\n}\n\n// Simple cache for release notes\nconst releaseNotesCache = new Map<string, { data: ReleaseNote; expiresAt: number }>()\nconst CACHE_TTL = 5 * 60 * 1000 // 5 minutes\n\n/**\n * useReleaseNotes composable for fetching release notes from the DCS API.\n *\n * @param version - Semantic version (e.g., \"1.2.0\") or \"latest\"\n * @param options - Optional configuration\n * @returns Release notes data and state\n */\nexport function useReleaseNotes(\n version: string,\n options: { fetchOnMount?: boolean } = {}\n): ReleaseNotesReturn {\n const { fetchOnMount = true } = options\n\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\n\n const releaseNote = ref<ReleaseNote | null>(null)\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n\n async function fetchReleaseNotes(): Promise<void> {\n if (!siteSlug) {\n error.value = 'VITE_SITE_SLUG environment variable not set'\n return\n }\n\n // Check cache\n const cacheKey = `${siteSlug}:${version}`\n const cached = releaseNotesCache.get(cacheKey)\n if (cached && cached.expiresAt > Date.now()) {\n releaseNote.value = cached.data\n return\n }\n\n isLoading.value = true\n error.value = null\n\n try {\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/release-notes/${version}`\n const response = await fetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n if (response.status === 404) {\n error.value = `Release notes for version ${version} not found`\n return\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const data = await response.json()\n\n // Normalize the response\n const note: ReleaseNote = {\n version: data.version,\n title: data.title,\n summary: data.summary || '',\n notesMarkdown: data.notesMarkdown || data.notes || '',\n changeCount: data.changeCount || 0,\n releaseDate: data.releaseDate || data.releasedAt || '',\n }\n\n // Cache the result\n releaseNotesCache.set(cacheKey, {\n data: note,\n expiresAt: Date.now() + CACHE_TTL,\n })\n\n releaseNote.value = note\n } catch (e) {\n error.value = e instanceof Error ? e.message : 'Failed to load release notes'\n console.error('[@duffcloudservices/cms] Failed to fetch release notes:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n async function refresh(): Promise<void> {\n // Clear cache\n const cacheKey = `${siteSlug}:${version}`\n releaseNotesCache.delete(cacheKey)\n await fetchReleaseNotes()\n }\n\n // Fetch on mount if enabled\n if (fetchOnMount && typeof window !== 'undefined') {\n onMounted(() => {\n fetchReleaseNotes()\n })\n }\n\n return {\n releaseNote,\n isLoading,\n error,\n refresh,\n }\n}\n","/**\n * useSiteVersion Composable\n *\n * Gets the current site version for footer badges and version displays.\n * Fetches the latest release version from the DCS Portal API.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useSiteVersion } from '@duffcloudservices/cms'\n *\n * const { version, releaseNotesUrl } = useSiteVersion()\n * </script>\n *\n * <template>\n * <footer>\n * <a v-if=\"version\" :href=\"releaseNotesUrl\" class=\"version-badge\">\n * v{{ version }}\n * </a>\n * </footer>\n * </template>\n * ```\n */\n\nimport { ref, computed, onMounted } from 'vue'\nimport type { SiteVersionReturn } from '../types/release-notes'\n\n/**\n * Get environment variable value.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n return defaultValue\n}\n\n// Cache for site version\nlet versionCache: { version: string; expiresAt: number } | null = null\nconst CACHE_TTL = 10 * 60 * 1000 // 10 minutes\n\n/**\n * useSiteVersion composable for displaying the current site version.\n *\n * @param options - Optional configuration\n * @returns Site version data and computed URL\n */\nexport function useSiteVersion(options: { fetchOnMount?: boolean } = {}): SiteVersionReturn {\n const { fetchOnMount = true } = options\n\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\n\n const version = ref<string | null>(null)\n const isLoading = ref(false)\n\n const releaseNotesUrl = computed(() => {\n if (!version.value) return '/releaseNotes/latest'\n return `/releaseNotes/${version.value}`\n })\n\n async function fetchVersion(): Promise<void> {\n if (!siteSlug) {\n return\n }\n\n // Check cache\n if (versionCache && versionCache.expiresAt > Date.now()) {\n version.value = versionCache.version\n return\n }\n\n isLoading.value = true\n\n try {\n // Fetch the latest release notes to get the version\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/release-notes/latest`\n const response = await fetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n // No release notes yet - that's fine\n return\n }\n\n const data = await response.json()\n const latestVersion = data.version\n\n // Cache the result\n versionCache = {\n version: latestVersion,\n expiresAt: Date.now() + CACHE_TTL,\n }\n\n version.value = latestVersion\n } catch (e) {\n console.error('[@duffcloudservices/cms] Failed to fetch site version:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n // Fetch on mount if enabled\n if (fetchOnMount && typeof window !== 'undefined') {\n onMounted(() => {\n fetchVersion()\n })\n }\n\n return {\n version,\n isLoading,\n releaseNotesUrl,\n }\n}\n","/**\n * useMediaCarousel Composable\n *\n * Extracts media carousel items from text content keys following the pattern:\n * `{prefix}.{N}.url`, `{prefix}.{N}.type`, `{prefix}.{N}.alt`\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useTextContent, useMediaCarousel } from '@duffcloudservices/cms'\n *\n * const { t } = useTextContent({\n * pageSlug: 'bio-mackenzie',\n * defaults: {\n * 'hero.media-carousel.0.url': '/images/staff/mackenzie.webp',\n * 'hero.media-carousel.0.type': 'image',\n * 'hero.media-carousel.0.alt': 'Mackenzie Kowalick',\n * 'hero.media-carousel.1.url': '/videos/intro.mp4',\n * 'hero.media-carousel.1.type': 'video',\n * 'hero.media-carousel.1.alt': 'Introduction video',\n * }\n * })\n *\n * const { items } = useMediaCarousel({\n * prefix: 'hero.media-carousel',\n * t,\n * defaults: [\n * { url: '/images/staff/mackenzie.webp', type: 'image', alt: 'Mackenzie Kowalick' }\n * ]\n * })\n * </script>\n *\n * <template>\n * <MediaCarousel :items=\"items\" />\n * </template>\n * ```\n */\n\nimport { computed, type ComputedRef } from 'vue'\nimport { isCdnAssetUrl } from '@duffcloudservices/cms-core'\n\n/**\n * Media carousel item representing an image, video, or embed\n */\nexport interface MediaCarouselItem {\n /** URL to the image, video file, or embed URL */\n url: string\n /** Type of media: 'image', 'video' (direct file), 'youtube', or 'instagram' */\n type: 'image' | 'video' | 'youtube' | 'instagram'\n /** Accessibility alt text */\n alt?: string\n /**\n * Whether this image has responsive CDN variants available.\n * Automatically set to `true` when the URL matches the DCS CDN asset pattern.\n * Components rendering the carousel should use `<ResponsiveImage>` when this is `true`.\n */\n responsive?: boolean\n}\n\n/**\n * Configuration for useMediaCarousel composable\n */\nexport interface UseMediaCarouselConfig {\n /** Key prefix for carousel items (e.g., 'hero.media-carousel') */\n prefix: string\n /** The t() function from useTextContent */\n t: (key: string, fallback?: string) => string\n /** Default items to use if no content keys are found */\n defaults?: MediaCarouselItem[]\n /** Maximum number of items to look for (default: 10) */\n maxItems?: number\n}\n\n/**\n * Return type for useMediaCarousel composable\n */\nexport interface UseMediaCarouselReturn {\n /** Computed array of media carousel items */\n items: ComputedRef<MediaCarouselItem[]>\n /** Whether any items were found from content keys */\n hasItems: ComputedRef<boolean>\n /** Number of items in the carousel */\n count: ComputedRef<number>\n}\n\n/**\n * Extract media carousel items from text content keys.\n *\n * Looks for keys in the format:\n * - `{prefix}.{N}.url` - Required URL for the media\n * - `{prefix}.{N}.type` - Type: 'image' or 'video' (defaults to 'image')\n * - `{prefix}.{N}.alt` - Alt text for accessibility\n *\n * Items are sorted by index (0, 1, etc.) and only included if they have a valid URL.\n *\n * @param config - Configuration object\n * @returns Media carousel helpers and state\n */\nexport function useMediaCarousel(config: UseMediaCarouselConfig): UseMediaCarouselReturn {\n const {\n prefix,\n t,\n defaults = [],\n maxItems = 10,\n } = config\n\n const items = computed<MediaCarouselItem[]>(() => {\n const result: MediaCarouselItem[] = []\n\n // Look for items from 0 to maxItems\n for (let i = 0; i < maxItems; i++) {\n const urlKey = `${prefix}.${i}.url`\n const typeKey = `${prefix}.${i}.type`\n const altKey = `${prefix}.${i}.alt`\n\n // Use a sentinel value to detect if the key exists\n const url = t(urlKey, '')\n \n // Skip if no URL (key doesn't exist or is empty)\n if (!url || url === urlKey) {\n continue\n }\n\n const typeValue = t(typeKey, 'image')\n // Parse type value - support image, video, youtube, instagram\n let type: 'image' | 'video' | 'youtube' | 'instagram' = 'image'\n if (typeValue === 'video') type = 'video'\n else if (typeValue === 'youtube') type = 'youtube'\n else if (typeValue === 'instagram') type = 'instagram'\n const alt = t(altKey, '')\n\n result.push({\n url,\n type,\n alt: alt && alt !== altKey ? alt : undefined,\n // Flag CDN-hosted images as responsive so carousel components\n // can render them with <ResponsiveImage> automatically\n responsive: type === 'image' && isCdnAssetUrl(url),\n })\n }\n\n // If no items found from content keys, use defaults\n if (result.length === 0) {\n return defaults\n }\n\n return result\n })\n\n const hasItems = computed(() => items.value.length > 0)\n const count = computed(() => items.value.length)\n\n return {\n items,\n hasItems,\n count,\n }\n}\n","/**\n * Vue 3 composable that resolves responsive image variants for DCS CDN-hosted assets.\n *\n * Wraps the framework-agnostic `resolveResponsiveImage` from `@duffcloudservices/cms-core`\n * with reactive Vue refs so it can be used directly in `<script setup>` blocks.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useResponsiveImage } from '@duffcloudservices/cms'\n *\n * const hero = useResponsiveImage({\n * src: 'https://files.duffcloudservices.com/kept/assets/hero/abc-123.jpg',\n * alt: 'Hero image',\n * context: 'hero',\n * })\n * </script>\n *\n * <template>\n * <picture v-if=\"hero.hasVariants\">\n * <source\n * v-for=\"source in hero.sources\"\n * :key=\"source.type\"\n * :srcset=\"source.srcset\"\n * :type=\"source.type\"\n * :sizes=\"source.sizes\"\n * />\n * <img v-bind=\"hero.imgProps\" class=\"w-full h-full object-cover\" />\n * </picture>\n * <img v-else v-bind=\"hero.imgProps\" class=\"w-full h-full object-cover\" />\n * </template>\n * ```\n */\n\nimport { computed, type MaybeRefOrGetter, toValue } from 'vue'\nimport {\n resolveResponsiveImage,\n type ImageContext,\n type ResponsiveImageResult,\n} from '@duffcloudservices/cms-core'\n\nexport interface UseResponsiveImageOptions {\n /** Source URL — can be a reactive ref, getter, or plain string. */\n src: MaybeRefOrGetter<string>\n /** Alt text — can be a reactive ref, getter, or plain string. */\n alt: MaybeRefOrGetter<string>\n /** Sizing context — determines which variants to include. */\n context?: MaybeRefOrGetter<ImageContext | undefined>\n /** Optional `sizes` attribute override. */\n sizes?: MaybeRefOrGetter<string | undefined>\n /** Skip variant resolution and use the original URL only. */\n original?: MaybeRefOrGetter<boolean | undefined>\n}\n\n/**\n * Reactively resolves responsive image metadata for a DCS CDN URL.\n *\n * The returned object is a computed ref that recomputes whenever any\n * of the input refs change. Spread `imgProps` onto an `<img>` or\n * combine with `sources` inside a `<picture>` element.\n */\nexport function useResponsiveImage(options: UseResponsiveImageOptions): ResponsiveImageResult {\n const result = computed(() =>\n resolveResponsiveImage({\n src: toValue(options.src),\n alt: toValue(options.alt),\n context: toValue(options.context),\n sizes: toValue(options.sizes),\n original: toValue(options.original) ?? undefined,\n }),\n )\n\n // Return a reactive proxy that delegates to the computed\n return {\n get imgProps() {\n return result.value.imgProps\n },\n get sources() {\n return result.value.sources\n },\n get hasVariants() {\n return result.value.hasVariants\n },\n }\n}\n","/**\n * Composable for reading curated review selections from DCS content.\n * Reviews are stored in content.yaml by the visual editor's ReviewPickerSheet.\n */\nimport { computed, onMounted, onUnmounted, shallowRef, type ComputedRef } from 'vue'\n\nexport interface ReviewItem {\n id: string\n platform: 'google' | 'meta' | string\n rating: number\n authorName: string\n authorPhotoUrl?: string\n text?: string\n date?: string\n replyText?: string\n locationName?: string\n sourceLocationName?: string\n sourceUrl?: string\n}\n\nfunction withoutAuthorPhotos(items: ReviewItem[]): ReviewItem[] {\n return items.map(item => ({\n ...item,\n authorPhotoUrl: undefined,\n }))\n}\n\nexport interface UseReviewContentConfig {\n /** The section key matching the data-dcs-reviews attribute value */\n sectionKey: string\n /** Page slug for page-specific content lookup (defaults to current page) */\n pageSlug?: string\n /** Fallback reviews when no content is available */\n defaults?: ReviewItem[]\n}\n\nexport interface UseReviewContentReturn {\n /** The curated review items from content */\n reviews: ComputedRef<ReviewItem[]>\n /** Whether any reviews are available */\n hasReviews: ComputedRef<boolean>\n /** Number of reviews */\n count: ComputedRef<number>\n}\n\nconst previewReviewOverrides = shallowRef<Record<string, ReviewItem[]>>({})\nlet activePreviewReviewConsumers = 0\n\n// Declare the global content variable injected by dcsContentPlugin\ndeclare const __DCS_CONTENT__: {\n global?: Record<string, unknown>\n pages?: Record<string, Record<string, unknown>>\n} | undefined\n\nfunction normalizeReviewItem(item: Record<string, unknown>): ReviewItem | null {\n const id = String(item.id ?? '').trim()\n if (!id) {\n return null\n }\n\n const rawRating = Number(item.rating ?? 5)\n const rating = Number.isFinite(rawRating)\n ? Math.min(5, Math.max(1, Math.round(rawRating)))\n : 5\n\n return {\n id,\n platform: String(item.platform ?? 'google'),\n rating,\n authorName: String(item.authorName ?? 'Anonymous'),\n authorPhotoUrl: undefined,\n text: item.text ? String(item.text) : undefined,\n date: item.date ? String(item.date) : undefined,\n replyText: item.replyText ? String(item.replyText) : undefined,\n locationName: item.locationName ? String(item.locationName) : undefined,\n sourceLocationName: item.sourceLocationName ? String(item.sourceLocationName) : undefined,\n sourceUrl: item.sourceUrl ? String(item.sourceUrl) : undefined,\n }\n}\n\nfunction normalizeReviewList(value: unknown): ReviewItem[] {\n if (!Array.isArray(value)) {\n return []\n }\n\n return value\n .filter((item): item is Record<string, unknown> => item != null && typeof item === 'object')\n .map(normalizeReviewItem)\n .filter((item): item is ReviewItem => item != null)\n}\n\nfunction handlePreviewReviewUpdate(event: Event) {\n const detail = event instanceof CustomEvent && event.detail != null && typeof event.detail === 'object'\n ? event.detail as { key?: unknown; reviews?: unknown }\n : null\n const key = typeof detail?.key === 'string' ? detail.key.trim() : ''\n if (!key) {\n return\n }\n\n const next = { ...previewReviewOverrides.value }\n if (Array.isArray(detail?.reviews)) {\n next[key] = normalizeReviewList(detail.reviews)\n } else {\n delete next[key]\n }\n previewReviewOverrides.value = next\n}\n\nexport function useReviewContent(config: UseReviewContentConfig): UseReviewContentReturn {\n const { sectionKey, pageSlug, defaults = [] } = config\n\n onMounted(() => {\n activePreviewReviewConsumers += 1\n if (activePreviewReviewConsumers === 1) {\n window.addEventListener('dcs:reviews-updated', handlePreviewReviewUpdate)\n }\n })\n\n onUnmounted(() => {\n activePreviewReviewConsumers = Math.max(0, activePreviewReviewConsumers - 1)\n if (activePreviewReviewConsumers === 0) {\n window.removeEventListener('dcs:reviews-updated', handlePreviewReviewUpdate)\n }\n })\n\n const reviews = computed<ReviewItem[]>(() => {\n if (Object.prototype.hasOwnProperty.call(previewReviewOverrides.value, sectionKey)) {\n return previewReviewOverrides.value[sectionKey] ?? []\n }\n\n if (typeof __DCS_CONTENT__ === 'undefined' || __DCS_CONTENT__ == null) {\n return withoutAuthorPhotos(defaults)\n }\n\n let reviewData: unknown = null\n\n if (pageSlug && __DCS_CONTENT__.pages?.[pageSlug]) {\n reviewData = __DCS_CONTENT__.pages[pageSlug][`reviews.${sectionKey}.items`]\n ?? __DCS_CONTENT__.pages[pageSlug][`reviews.${sectionKey}`]\n }\n\n if (!reviewData && __DCS_CONTENT__.global) {\n reviewData = __DCS_CONTENT__.global[`reviews.${sectionKey}.items`]\n ?? __DCS_CONTENT__.global[`reviews.${sectionKey}`]\n }\n\n if (!reviewData || !Array.isArray(reviewData)) {\n return withoutAuthorPhotos(defaults)\n }\n\n return normalizeReviewList(reviewData)\n })\n\n const hasReviews = computed(() => reviews.value.length > 0)\n const count = computed(() => reviews.value.length)\n\n return { reviews, hasReviews, count }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duffcloudservices/cms",
3
- "version": "0.3.14",
3
+ "version": "0.3.15",
4
4
  "description": "Vue 3 composables and Vite plugins for DCS CMS integration",
5
5
  "type": "module",
6
6
  "exports": {
@@ -14,9 +14,17 @@ export interface ReviewItem {
14
14
  date?: string
15
15
  replyText?: string
16
16
  locationName?: string
17
+ sourceLocationName?: string
17
18
  sourceUrl?: string
18
19
  }
19
20
 
21
+ function withoutAuthorPhotos(items: ReviewItem[]): ReviewItem[] {
22
+ return items.map(item => ({
23
+ ...item,
24
+ authorPhotoUrl: undefined,
25
+ }))
26
+ }
27
+
20
28
  export interface UseReviewContentConfig {
21
29
  /** The section key matching the data-dcs-reviews attribute value */
22
30
  sectionKey: string
@@ -60,11 +68,12 @@ function normalizeReviewItem(item: Record<string, unknown>): ReviewItem | null {
60
68
  platform: String(item.platform ?? 'google'),
61
69
  rating,
62
70
  authorName: String(item.authorName ?? 'Anonymous'),
63
- authorPhotoUrl: item.authorPhotoUrl ? String(item.authorPhotoUrl) : undefined,
71
+ authorPhotoUrl: undefined,
64
72
  text: item.text ? String(item.text) : undefined,
65
73
  date: item.date ? String(item.date) : undefined,
66
74
  replyText: item.replyText ? String(item.replyText) : undefined,
67
75
  locationName: item.locationName ? String(item.locationName) : undefined,
76
+ sourceLocationName: item.sourceLocationName ? String(item.sourceLocationName) : undefined,
68
77
  sourceUrl: item.sourceUrl ? String(item.sourceUrl) : undefined,
69
78
  }
70
79
  }
@@ -121,7 +130,7 @@ export function useReviewContent(config: UseReviewContentConfig): UseReviewConte
121
130
  }
122
131
 
123
132
  if (typeof __DCS_CONTENT__ === 'undefined' || __DCS_CONTENT__ == null) {
124
- return defaults
133
+ return withoutAuthorPhotos(defaults)
125
134
  }
126
135
 
127
136
  let reviewData: unknown = null
@@ -137,7 +146,7 @@ export function useReviewContent(config: UseReviewContentConfig): UseReviewConte
137
146
  }
138
147
 
139
148
  if (!reviewData || !Array.isArray(reviewData)) {
140
- return defaults
149
+ return withoutAuthorPhotos(defaults)
141
150
  }
142
151
 
143
152
  return normalizeReviewList(reviewData)