@duffcloudservices/cms 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor/editorBridge.d.ts +37 -0
- package/dist/editor/editorBridge.js +556 -0
- package/dist/editor/editorBridge.js.map +1 -0
- package/dist/index.d.ts +93 -6
- package/dist/index.js +43 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins/index.d.ts +46 -1
- package/dist/plugins/index.js +38 -1
- package/dist/plugins/index.js.map +1 -1
- package/package.json +72 -69
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/editor/editorBridge.ts"],"names":[],"mappings":"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","file":"editorBridge.js","sourcesContent":["/**\r\n * Editor Bridge — injected into customer site iframes by the DCS visual editor.\r\n *\r\n * Responsibilities:\r\n * 1. Report available sections and text keys to the parent portal\r\n * 2. Enable inline contenteditable editing on data-text-key elements (double-click)\r\n * 3. Render section overlay highlights on hover/select from parent\r\n * 4. Communicate all interactions back to the parent via postMessage\r\n * 5. Monitor navigation within the iframe and notify the parent\r\n * 6. Show floating edit icon on hover for text-key elements\r\n *\r\n * NOTE: AI ✨ buttons are rendered by the portal-side SectionOverlayLayer,\r\n * NOT by this bridge. The bridge only reports section/text key data and\r\n * handles inline text editing.\r\n *\r\n * This script runs inside the iframe (customer site). The parent portal\r\n * communicates via postMessage using the `dcs:*` message protocol.\r\n */\r\n\r\n// ─── Types ───────────────────────────────────────────────────────\r\n\r\nexport interface SectionInfo {\r\n id: string\r\n label: string | null\r\n bounds: { x: number; y: number; width: number; height: number }\r\n textKeyCount: number\r\n}\r\n\r\nexport interface EditorReadyPayload {\r\n sections: SectionInfo[]\r\n textKeys: string[]\r\n contentHeight: number\r\n}\r\n\r\n// ─── Message Protocol ────────────────────────────────────────────\r\n\r\ntype InboundMessageType =\r\n | 'dcs:init-editor'\r\n | 'dcs:highlight-section'\r\n | 'dcs:clear-highlight'\r\n | 'dcs:select-text-key'\r\n | 'dcs:update-text'\r\n | 'dcs:set-mode'\r\n | 'dcs:set-editing-enabled'\r\n\r\ntype OutboundMessageType =\r\n | 'dcs:ready'\r\n | 'dcs:text-key-click'\r\n | 'dcs:text-key-changed'\r\n | 'dcs:section-click'\r\n | 'dcs:section-hover'\r\n | 'dcs:text-key-dblclick'\r\n | 'dcs:contextmenu'\r\n | 'dcs:click'\r\n | 'dcs:navigation'\r\n | 'dcs:array-key-click'\r\n\r\nfunction isInIframe(): boolean {\r\n try { return globalThis.self !== globalThis.self.parent } catch { return true }\r\n}\r\n\r\nfunction postToParent(type: OutboundMessageType, data: unknown) {\r\n if (!isInIframe()) return\r\n // Use '*' because the portal origin varies between dev and production\r\n // and this script doesn't know the parent origin at injection time.\r\n // Security: messages are validated by the portal's useIframeBridge.\r\n // Origin validated via dcs: prefix check on all messages\r\n globalThis.self.parent.postMessage({ type, data }, '*') // NOSONAR\r\n}\r\n\r\n// ─── State ───────────────────────────────────────────────────────\r\n\r\nlet editorActive = false\r\nlet bridgeInitialized = false\r\nlet currentHighlightOverlay: HTMLElement | null = null\r\nlet activeEditElement: HTMLElement | null = null\r\n/** The current pathname tracked for navigation detection */\r\nlet lastKnownPathname: string = ''\r\n/** The floating edit icon element shown on hover over text keys */\r\nlet editIconElement: HTMLElement | null = null\r\n/** The element the edit icon is currently attached to */\r\nlet editIconTarget: HTMLElement | null = null\r\n/** The floating edit icon for array/list text keys */\r\nlet arrayEditIconElement: HTMLElement | null = null\r\n/** The element the array edit icon is currently attached to */\r\nlet arrayEditIconTarget: HTMLElement | null = null\r\n/** Whether editing features are enabled (disabled on unmanaged pages) */\r\nlet editingEnabled = true\r\n/** Original text values captured when inline editing starts, for change detection */\r\nconst originalTextValues = new Map<HTMLElement, string>()\r\n\r\n// ─── Section Discovery ───────────────────────────────────────────\r\n\r\nfunction discoverSections(): SectionInfo[] {\r\n const elements = document.querySelectorAll<HTMLElement>('[data-section]')\r\n return Array.from(elements).map((el) => {\r\n const rect = el.getBoundingClientRect()\r\n const textKeyCount = el.querySelectorAll('[data-text-key]').length\r\n return {\r\n id: el.dataset.section!,\r\n label: el.dataset.sectionLabel ?? null,\r\n bounds: {\r\n x: rect.left + scrollX,\r\n y: rect.top + scrollY,\r\n width: rect.width,\r\n height: rect.height,\r\n },\r\n textKeyCount,\r\n }\r\n })\r\n}\r\n\r\nfunction discoverTextKeys(): string[] {\r\n return Array.from(document.querySelectorAll<HTMLElement>('[data-text-key]')).map(\r\n (el) => el.dataset.textKey!,\r\n )\r\n}\r\n\r\n// ─── Section Overlay ─────────────────────────────────────────────\r\n\r\nfunction showSectionHighlight(sectionId: string) {\r\n clearSectionHighlight()\r\n const el = document.querySelector(`[data-section=\"${sectionId}\"]`)\r\n if (!el) return\r\n\r\n const rect = el.getBoundingClientRect()\r\n const overlay = document.createElement('div')\r\n overlay.className = 'dcs-section-highlight'\r\n overlay.style.cssText = `\r\n position: absolute;\r\n left: ${rect.left + scrollX}px;\r\n top: ${rect.top + scrollY}px;\r\n width: ${rect.width}px;\r\n height: ${rect.height}px;\r\n border: 2px dashed hsl(221.2 83.2% 53.3%);\r\n background: hsla(221.2, 83.2%, 53.3%, 0.05);\r\n pointer-events: none;\r\n z-index: 99998;\r\n border-radius: 8px;\r\n transition: all 0.15s ease;\r\n `\r\n document.body.appendChild(overlay)\r\n currentHighlightOverlay = overlay\r\n}\r\n\r\nfunction clearSectionHighlight() {\r\n if (currentHighlightOverlay) {\r\n currentHighlightOverlay.remove()\r\n currentHighlightOverlay = null\r\n }\r\n}\r\n\r\n// ─── Navigation Monitoring ───────────────────────────────────────\r\n\r\n/**\r\n * Monitor navigation within the iframe and notify the parent portal.\r\n * Instead of blocking all navigation, we allow SPA routing and inform\r\n * the portal when the iframe navigates to a different page. The portal\r\n * then updates its own URL and loads editing context for the new page.\r\n *\r\n * Strategy:\r\n * 1. Poll location.pathname on a short interval — this reliably detects\r\n * SPA navigation regardless of which router or framework is used,\r\n * since frameworks may cache History API references before our script loads.\r\n * 2. Block external/cross-origin link clicks (actual page reloads).\r\n * 3. Block form submissions.\r\n * 4. Monitor popstate for back/forward navigation.\r\n * 5. After navigation, re-discover sections and report them to the portal.\r\n */\r\nfunction monitorNavigation() {\r\n lastKnownPathname = globalThis.location.pathname\r\n\r\n // ── Poll for URL changes — reliable with any SPA router ──────\r\n // Frameworks like VitePress/Vue Router may cache history.pushState before\r\n // our bridge loads, so prototype overrides are unreliable. Polling\r\n // location.pathname directly is simple and always works.\r\n setInterval(() => {\r\n checkForNavigation()\r\n }, 250)\r\n\r\n // ── Catch back/forward navigation ────────────────────────────\r\n globalThis.addEventListener('popstate', () => {\r\n checkForNavigation()\r\n })\r\n\r\n // ── Block external link clicks (cross-origin / full page reloads) ──\r\n const handleLinkClick = (e: MouseEvent) => {\r\n const link = (e.target as HTMLElement).closest?.('a[href]') as HTMLAnchorElement | null\r\n if (!link) return\r\n\r\n const href = link.getAttribute('href')\r\n if (!href || href.startsWith('#') || href === 'javascript:void(0)') return\r\n\r\n // Check if this is an external link (different origin)\r\n try {\r\n const resolved = new URL(href, globalThis.location.href)\r\n if (resolved.origin !== globalThis.location.origin) {\r\n // Block external navigation — can't leave the iframe\r\n e.preventDefault()\r\n e.stopPropagation()\r\n e.stopImmediatePropagation()\r\n }\r\n // Same-origin links: let the SPA router handle them.\r\n // The History API override above will detect the navigation.\r\n } catch {\r\n // Malformed URL — block it\r\n e.preventDefault()\r\n }\r\n }\r\n\r\n // Register on window first to beat VitePress's capture-phase handler\r\n globalThis.addEventListener('click', handleLinkClick, true)\r\n document.addEventListener('click', handleLinkClick, true)\r\n\r\n // Intercept form submissions\r\n document.addEventListener('submit', (e: Event) => {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n }, true)\r\n\r\n // Block actual page reloads (location.href = ..., etc.)\r\n window.addEventListener('beforeunload', (e: BeforeUnloadEvent) => {\r\n e.preventDefault()\r\n })\r\n}\r\n\r\n/**\r\n * Check if the iframe pathname has changed and notify the portal.\r\n * Also re-initializes the editor bridge for the new page.\r\n */\r\nfunction checkForNavigation() {\r\n const currentPathname = globalThis.location.pathname\r\n if (currentPathname === lastKnownPathname) return\r\n\r\n lastKnownPathname = currentPathname\r\n\r\n // Notify parent about the navigation\r\n postToParent('dcs:navigation', { pathname: currentPathname })\r\n\r\n // Clean up current edit state\r\n if (activeEditElement) {\r\n finishEdit(activeEditElement)\r\n }\r\n removeEditIcon()\r\n removeArrayEditIcon()\r\n\r\n // Wait for the SPA router to finish rendering the new page,\r\n // then re-discover sections and text keys\r\n setTimeout(() => {\r\n rediscoverAndNotify()\r\n }, 500)\r\n}\r\n\r\n// ─── Mode Management ─────────────────────────────────────────────\r\n\r\n/**\r\n * Set the current interaction mode. Both modes allow normal browsing —\r\n * inline editing works via double-click regardless of mode.\r\n * Switching to explore finishes any active inline edit.\r\n */\r\nfunction setMode(mode: 'edit' | 'explore') {\r\n if (mode === 'explore') {\r\n // When explicitly switching to explore, finish any active inline edit\r\n if (activeEditElement) {\r\n finishEdit(activeEditElement)\r\n }\r\n }\r\n}\r\n\r\n// ─── Inline Text Editing ─────────────────────────────────────────\r\n\r\n/**\r\n * Re-discover sections and text keys after an HMR update and\r\n * notify the parent portal so the overlay layer updates.\r\n */\r\nfunction rediscoverAndNotify() {\r\n currentHighlightOverlay = null\r\n activeEditElement = null\r\n\r\n const sections = discoverSections()\r\n const textKeys = discoverTextKeys()\r\n const contentHeight = document.body.scrollHeight\r\n postToParent('dcs:ready', { sections, textKeys, contentHeight } satisfies EditorReadyPayload)\r\n\r\n // Re-enable editor mode on the new elements\r\n if (editorActive) {\r\n editorActive = false // allow re-initialization\r\n enableEditorMode()\r\n }\r\n}\r\n\r\nfunction enableEditorMode() {\r\n if (editorActive) return\r\n editorActive = true\r\n\r\n // Inject editor styles\r\n injectEditorStyles()\r\n\r\n // Set up all text key elements — double-click to start editing\r\n const textElements = document.querySelectorAll<HTMLElement>('[data-text-key]')\r\n textElements.forEach((htmlEl) => {\r\n htmlEl.classList.add('dcs-editable')\r\n\r\n // Double-click to start inline editing (signals parent to switch to edit mode)\r\n htmlEl.addEventListener('dblclick', handleTextKeyDblClick)\r\n // Blur to finish editing\r\n htmlEl.addEventListener('blur', handleTextKeyBlur)\r\n // Keyboard: Enter to finish, Escape to cancel\r\n htmlEl.addEventListener('keydown', handleTextKeyKeydown)\r\n\r\n // Show/hide the floating edit icon on hover (T icon for regular text, List icon for array keys)\r\n htmlEl.addEventListener('mouseenter', () => {\r\n // Don't show icon while actively editing this or another element\r\n if (activeEditElement) return\r\n const key = htmlEl.dataset.textKey!\r\n if (isArrayTextKey(key)) {\r\n showArrayEditIcon(htmlEl)\r\n } else {\r\n showEditIcon(htmlEl)\r\n }\r\n })\r\n htmlEl.addEventListener('mouseleave', (e: MouseEvent) => {\r\n // Don't hide if moving to the edit icon or array edit icon\r\n const related = e.relatedTarget as HTMLElement | null\r\n if (related && (\r\n related.classList?.contains('dcs-edit-icon') || related.closest?.('.dcs-edit-icon') ||\r\n related.classList?.contains('dcs-array-edit-icon') || related.closest?.('.dcs-array-edit-icon')\r\n )) return\r\n removeEditIcon()\r\n removeArrayEditIcon()\r\n })\r\n })\r\n\r\n // Set up section hover reporting (portal overlay handles the visual treatment)\r\n const sections = document.querySelectorAll<HTMLElement>('[data-section]')\r\n sections.forEach((el) => {\r\n const sectionId = el.dataset.section!\r\n\r\n el.addEventListener('mouseenter', () => {\r\n postToParent('dcs:section-hover', { sectionId })\r\n })\r\n el.addEventListener('mouseleave', () => {\r\n postToParent('dcs:section-hover', { sectionId: null })\r\n })\r\n el.addEventListener('click', (e) => {\r\n // Only fire section click if not clicking on a text-key element\r\n const target = e.target as HTMLElement\r\n if (!target.closest('[data-text-key]')) {\r\n postToParent('dcs:section-click', { sectionId })\r\n }\r\n })\r\n })\r\n}\r\n\r\n/**\r\n * Handle double-click on a text key element.\r\n * Notifies the parent portal to switch to edit mode and begins inline editing.\r\n */\r\nfunction handleTextKeyDblClick(e: Event) {\r\n if (!editingEnabled) return\r\n\r\n e.preventDefault()\r\n e.stopPropagation()\r\n e.stopImmediatePropagation()\r\n\r\n const el = e.currentTarget as HTMLElement\r\n const key = el.dataset.textKey!\r\n const sectionParent = el.closest<HTMLElement>('[data-section]')\r\n const sectionId = sectionParent?.dataset.section ?? null\r\n\r\n // Always notify the parent to switch to edit mode when starting inline editing.\r\n // In explore mode this triggers the mode switch; in edit mode it's a no-op on the parent side.\r\n postToParent('dcs:text-key-dblclick', { key, sectionId })\r\n\r\n // Start inline editing\r\n startInlineEdit(el, key, sectionId)\r\n}\r\n\r\n/**\r\n * Start inline contenteditable editing on a text key element.\r\n * Called by double-click or when the parent sends dcs:select-text-key.\r\n */\r\nfunction startInlineEdit(el: HTMLElement, key: string, sectionId: string | null) {\r\n // Finish any active edit first\r\n if (activeEditElement && activeEditElement !== el) {\r\n finishEdit(activeEditElement)\r\n }\r\n\r\n // Store original text for change detection on finish\r\n originalTextValues.set(el, el.innerText.trim())\r\n\r\n // Enable contenteditable\r\n el.setAttribute('contenteditable', 'true')\r\n el.classList.add('dcs-editing')\r\n el.focus()\r\n activeEditElement = el\r\n\r\n // Select all text for easy replacement\r\n const selection = getSelection()\r\n const range = document.createRange()\r\n range.selectNodeContents(el)\r\n selection?.removeAllRanges()\r\n selection?.addRange(range)\r\n\r\n postToParent('dcs:text-key-click', { key, sectionId })\r\n}\r\n\r\nfunction handleTextKeyBlur(e: Event) {\r\n const el = e.currentTarget as HTMLElement\r\n finishEdit(el)\r\n}\r\n\r\nfunction handleTextKeyKeydown(e: KeyboardEvent) {\r\n if (e.key === 'Enter' && !e.shiftKey) {\r\n e.preventDefault()\r\n ;(e.currentTarget as HTMLElement).blur()\r\n }\r\n if (e.key === 'Escape') {\r\n e.preventDefault()\r\n ;(e.currentTarget as HTMLElement).blur()\r\n }\r\n}\r\n\r\nfunction finishEdit(el: HTMLElement) {\r\n if (!el.hasAttribute('contenteditable')) return\r\n\r\n el.removeAttribute('contenteditable')\r\n el.classList.remove('dcs-editing')\r\n\r\n const key = el.dataset.textKey!\r\n const sectionParent = el.closest<HTMLElement>('[data-section]')\r\n const sectionId = sectionParent?.dataset.section ?? null\r\n const newValue = el.innerText.trim()\r\n const originalValue = originalTextValues.get(el) ?? ''\r\n\r\n if (activeEditElement === el) {\r\n activeEditElement = null\r\n }\r\n\r\n // Only report to the portal when text was actually modified\r\n if (newValue !== originalValue) {\r\n postToParent('dcs:text-key-changed', { key, value: newValue, sectionId })\r\n }\r\n\r\n originalTextValues.delete(el)\r\n}\r\n\r\nfunction focusTextKey(key: string) {\r\n const el = document.querySelector<HTMLElement>(`[data-text-key=\"${key}\"]`)\r\n if (!el) return\r\n el.scrollIntoView({ behavior: 'smooth', block: 'center' })\r\n\r\n const sectionParent = el.closest<HTMLElement>('[data-section]')\r\n const sectionId = sectionParent?.dataset.section ?? null\r\n\r\n // Start inline editing directly\r\n startInlineEdit(el, key, sectionId)\r\n}\r\n\r\nfunction updateTextInPlace(key: string, value: string) {\r\n const el = document.querySelector<HTMLElement>(`[data-text-key=\"${key}\"]`)\r\n if (!el) return\r\n // Don't update if we're currently editing this element\r\n if (el === activeEditElement) return\r\n el.innerText = value\r\n}\r\n\r\n// ─── Edit Icon (Floating Text Cursor Button) ────────────────────\r\n\r\n/**\r\n * Create the floating edit icon element. This small button appears\r\n * in the top-left corner of a hovered [data-text-key] element,\r\n * providing a click target to start inline editing without triggering\r\n * navigation (important for text inside buttons/links).\r\n */\r\nfunction createEditIcon(): HTMLElement {\r\n const icon = document.createElement('button')\r\n icon.className = 'dcs-edit-icon'\r\n icon.setAttribute('type', 'button')\r\n icon.setAttribute('title', 'Edit text')\r\n icon.setAttribute('aria-label', 'Edit text inline')\r\n // SVG: Type icon (text cursor) — matches lucide \"Type\" icon\r\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>`\r\n\r\n icon.addEventListener('click', (e) => {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n e.stopImmediatePropagation()\r\n\r\n if (!editIconTarget) return\r\n const el = editIconTarget\r\n const key = el.dataset.textKey!\r\n const sectionParent = el.closest<HTMLElement>('[data-section]')\r\n const sectionId = sectionParent?.dataset.section ?? null\r\n\r\n // Signal parent to switch to edit mode\r\n postToParent('dcs:text-key-dblclick', { key, sectionId })\r\n // Start inline editing\r\n startInlineEdit(el, key, sectionId)\r\n // Hide the icon while editing\r\n removeEditIcon()\r\n }, true)\r\n\r\n // Hide icon when mouse leaves it (and isn't going to the target element)\r\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\r\n const related = e.relatedTarget as HTMLElement | null\r\n if (related && editIconTarget && (related === editIconTarget || editIconTarget.contains(related))) return\r\n removeEditIcon()\r\n })\r\n\r\n return icon\r\n}\r\n\r\n/**\r\n * Show the edit icon positioned at the top-left corner of the given element.\r\n */\r\nfunction showEditIcon(el: HTMLElement) {\r\n if (!editingEnabled) return\r\n\r\n if (!editIconElement) {\r\n editIconElement = createEditIcon()\r\n document.body.appendChild(editIconElement)\r\n }\r\n\r\n editIconTarget = el\r\n\r\n // Position relative to the element's bounding box\r\n const rect = el.getBoundingClientRect()\r\n editIconElement.style.top = `${rect.top + scrollY - 4}px`\r\n editIconElement.style.left = `${rect.left + scrollX - 4}px`\r\n editIconElement.style.display = 'flex'\r\n}\r\n\r\n/**\r\n * Hide and detach the edit icon.\r\n */\r\nfunction removeEditIcon() {\r\n if (editIconElement) {\r\n editIconElement.style.display = 'none'\r\n }\r\n editIconTarget = null\r\n}\r\n\r\n// ─── Array Key Detection ─────────────────────────────────────────\r\n\r\n/**\r\n * Check if a text key is part of an array pattern (e.g., \"conditions.0.title\").\r\n * Array text keys contain an indexed segment — either:\r\n * - Pure numeric: `baseKey.N.field` (e.g., `conditions.0.title`)\r\n * - Hyphenated index: `baseKey.prefix-N.field` (e.g., `steps.step-0.title`)\r\n */\r\nfunction isArrayTextKey(key: string): boolean {\r\n // Match pure numeric segment (.0.) or hyphenated index (.prefix-0.)\r\n return /\\.\\d+\\./.test(key) || /\\.\\w+-\\d+\\./.test(key)\r\n}\r\n\r\n/**\r\n * Extract the base array key from an array text key.\r\n * - `conditions.0.title` → `conditions`\r\n * - `steps.step-0.title` → `steps.step`\r\n */\r\nfunction getArrayBaseKey(key: string): string | null {\r\n // Try hyphenated pattern first (more specific)\r\n const hyphenMatch = key.match(/^(.+?)-\\d+\\./)\r\n if (hyphenMatch) return hyphenMatch[1]\r\n // Fall back to pure numeric pattern\r\n const numMatch = key.match(/^(.+?)\\.\\d+\\./)\r\n return numMatch ? numMatch[1] : null\r\n}\r\n\r\n// ─── Array Edit Icon (Floating List Button) ──────────────────────\r\n\r\n/**\r\n * Create the floating array edit icon element. This small button appears\r\n * in the top-left corner of a hovered [data-text-key] element whose key\r\n * matches an array pattern. Clicking it sends dcs:array-key-click to the\r\n * portal to open the array editor sheet.\r\n */\r\nfunction createArrayEditIcon(): HTMLElement {\r\n const icon = document.createElement('button')\r\n icon.className = 'dcs-array-edit-icon'\r\n icon.setAttribute('type', 'button')\r\n icon.setAttribute('title', 'Edit list items')\r\n icon.setAttribute('aria-label', 'Edit list items in panel')\r\n // SVG: List icon — matches lucide \"List\" icon\r\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>`\r\n\r\n icon.addEventListener('click', (e) => {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n e.stopImmediatePropagation()\r\n\r\n if (!arrayEditIconTarget) return\r\n const el = arrayEditIconTarget\r\n const key = el.dataset.textKey!\r\n const arrayKey = getArrayBaseKey(key)\r\n if (!arrayKey) return\r\n\r\n const sectionParent = el.closest<HTMLElement>('[data-section]')\r\n const sectionId = sectionParent?.dataset.section ?? null\r\n\r\n // Signal parent to open array editor sheet\r\n postToParent('dcs:array-key-click', { arrayKey, sectionId })\r\n // Hide the icon\r\n removeArrayEditIcon()\r\n }, true)\r\n\r\n // Hide icon when mouse leaves it (and isn't going to the target element)\r\n icon.addEventListener('mouseleave', (e: MouseEvent) => {\r\n const related = e.relatedTarget as HTMLElement | null\r\n if (related && arrayEditIconTarget && (related === arrayEditIconTarget || arrayEditIconTarget.contains(related))) return\r\n removeArrayEditIcon()\r\n })\r\n\r\n return icon\r\n}\r\n\r\n/**\r\n * Show the array edit icon positioned at the top-left corner of the given element.\r\n */\r\nfunction showArrayEditIcon(el: HTMLElement) {\r\n if (!editingEnabled) return\r\n\r\n if (!arrayEditIconElement) {\r\n arrayEditIconElement = createArrayEditIcon()\r\n document.body.appendChild(arrayEditIconElement)\r\n }\r\n\r\n arrayEditIconTarget = el\r\n\r\n // Position relative to the element's bounding box\r\n const rect = el.getBoundingClientRect()\r\n arrayEditIconElement.style.top = `${rect.top + scrollY - 4}px`\r\n arrayEditIconElement.style.left = `${rect.left + scrollX - 4}px`\r\n arrayEditIconElement.style.display = 'flex'\r\n}\r\n\r\n/**\r\n * Hide and detach the array edit icon.\r\n */\r\nfunction removeArrayEditIcon() {\r\n if (arrayEditIconElement) {\r\n arrayEditIconElement.style.display = 'none'\r\n }\r\n arrayEditIconTarget = null\r\n}\r\n\r\nfunction injectEditorStyles() {\r\n if (document.getElementById('dcs-editor-styles')) return\r\n\r\n const style = document.createElement('style')\r\n style.id = 'dcs-editor-styles'\r\n style.textContent = `\r\n [data-text-key].dcs-editable {\r\n cursor: text !important;\r\n border-radius: 4px;\r\n position: relative;\r\n }\r\n\r\n [data-text-key].dcs-editable:hover {\r\n outline: 2px dashed hsl(221.2 83.2% 53.3% / 0.4) !important;\r\n outline-offset: 4px;\r\n background-color: hsl(221.2 83.2% 53.3% / 0.03);\r\n }\r\n\r\n [data-text-key].dcs-editing {\r\n outline: 2px solid hsl(221.2 83.2% 53.3%) !important;\r\n outline-offset: 4px;\r\n background-color: hsl(221.2 83.2% 53.3% / 0.06);\r\n min-height: 1em;\r\n }\r\n\r\n [data-text-key].dcs-editing:focus {\r\n outline: 2px solid hsl(221.2 83.2% 53.3%) !important;\r\n outline-offset: 4px;\r\n }\r\n\r\n .dcs-section-highlight {\r\n pointer-events: none;\r\n }\r\n\r\n /* Floating edit icon button */\r\n .dcs-edit-icon {\r\n position: absolute;\r\n z-index: 99999;\r\n display: none;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px;\r\n height: 24px;\r\n padding: 0;\r\n margin: 0;\r\n border: 1.5px solid hsl(221.2 83.2% 53.3%);\r\n border-radius: 4px;\r\n background: hsl(221.2 83.2% 53.3% / 0.1);\r\n backdrop-filter: blur(4px);\r\n color: hsl(221.2 83.2% 53.3%);\r\n cursor: pointer;\r\n pointer-events: auto;\r\n transition: background 0.15s, transform 0.1s;\r\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\r\n }\r\n\r\n .dcs-edit-icon:hover {\r\n background: hsl(221.2 83.2% 53.3% / 0.25);\r\n transform: scale(1.1);\r\n }\r\n\r\n .dcs-edit-icon svg {\r\n display: block;\r\n }\r\n\r\n /* Floating array edit icon button */\r\n .dcs-array-edit-icon {\r\n position: absolute;\r\n z-index: 99999;\r\n display: none;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px;\r\n height: 24px;\r\n padding: 0;\r\n margin: 0;\r\n border: 1.5px solid hsl(271 91% 65%);\r\n border-radius: 4px;\r\n background: hsl(271 91% 65% / 0.1);\r\n backdrop-filter: blur(4px);\r\n color: hsl(271 91% 65%);\r\n cursor: pointer;\r\n pointer-events: auto;\r\n transition: background 0.15s, transform 0.1s;\r\n box-shadow: 0 1px 3px rgba(0,0,0,0.12);\r\n }\r\n\r\n .dcs-array-edit-icon:hover {\r\n background: hsl(271 91% 65% / 0.25);\r\n transform: scale(1.1);\r\n }\r\n\r\n .dcs-array-edit-icon svg {\r\n display: block;\r\n }\r\n `\r\n document.head.appendChild(style)\r\n}\r\n\r\n// ─── Inbound Message Handler ─────────────────────────────────────\r\n\r\nfunction handleMessage(event: MessageEvent) {\r\n // Validate message structure (origin is not checked because\r\n // the parent portal origin varies between dev and production)\r\n const msg = event.data\r\n if (!msg || typeof msg !== 'object' || typeof msg.type !== 'string') return\r\n if (!msg.type.startsWith('dcs:')) return\r\n\r\n const { type, data } = msg as { type: InboundMessageType; data: unknown }\r\n\r\n switch (type) {\r\n case 'dcs:init-editor':\r\n enableEditorMode()\r\n break\r\n\r\n case 'dcs:highlight-section':\r\n if (data && typeof data === 'object' && 'sectionId' in data) {\r\n showSectionHighlight((data as { sectionId: string }).sectionId)\r\n }\r\n break\r\n\r\n case 'dcs:clear-highlight':\r\n clearSectionHighlight()\r\n break\r\n\r\n case 'dcs:select-text-key':\r\n if (data && typeof data === 'object' && 'key' in data) {\r\n focusTextKey((data as { key: string }).key)\r\n }\r\n break\r\n\r\n case 'dcs:update-text':\r\n if (data && typeof data === 'object' && 'key' in data && 'value' in data) {\r\n const d = data as { key: string; value: string }\r\n updateTextInPlace(d.key, d.value)\r\n }\r\n break\r\n\r\n case 'dcs:set-mode':\r\n if (data && typeof data === 'object' && 'mode' in data) {\r\n setMode((data as { mode: 'edit' | 'explore' }).mode)\r\n }\r\n break\r\n\r\n case 'dcs:set-editing-enabled':\r\n if (data && typeof data === 'object' && 'enabled' in data) {\r\n editingEnabled = (data as { enabled: boolean }).enabled\r\n if (editingEnabled) {\r\n // Re-add dcs-editable class to text key elements\r\n document.querySelectorAll<HTMLElement>('[data-text-key]').forEach(el => {\r\n el.classList.add('dcs-editable')\r\n })\r\n } else {\r\n // Clean up: finish any active edit, remove edit icon, remove hover styles\r\n if (activeEditElement) {\r\n finishEdit(activeEditElement)\r\n }\r\n removeEditIcon()\r\n removeArrayEditIcon()\r\n // Remove dcs-editable class to disable hover outlines\r\n document.querySelectorAll<HTMLElement>('.dcs-editable').forEach(el => {\r\n el.classList.remove('dcs-editable')\r\n })\r\n }\r\n }\r\n break\r\n }\r\n}\r\n\r\n// ─── Bootstrap ───────────────────────────────────────────────────\r\n\r\nexport function initEditorBridge() {\r\n if (!isInIframe()) return // Not in iframe\r\n\r\n // Prevent duplicate listener registration on HMR re-execution\r\n if (!bridgeInitialized) {\r\n bridgeInitialized = true\r\n\r\n // Monitor navigation within the iframe — notify the portal of page changes\r\n monitorNavigation()\r\n\r\n // Forward right-click events to the portal so the context menu\r\n // works when right-clicking inside the iframe (cross-origin boundary\r\n // prevents the parent from receiving native contextmenu events).\r\n document.addEventListener('contextmenu', (e: MouseEvent) => {\r\n e.preventDefault()\r\n const sectionEl = (e.target as HTMLElement).closest?.('[data-section]') as HTMLElement | null\r\n postToParent('dcs:contextmenu', {\r\n x: e.clientX,\r\n y: e.clientY,\r\n sectionId: sectionEl?.dataset.section ?? null,\r\n sectionLabel: sectionEl?.dataset.sectionLabel ?? null,\r\n })\r\n }, true)\r\n\r\n // Forward click events to the portal so it can dismiss context menus\r\n // when the user clicks inside the iframe.\r\n document.addEventListener('click', () => {\r\n postToParent('dcs:click', {})\r\n }, true)\r\n\r\n // Listen for portal commands. Origin is not validated because\r\n // the portal origin varies between environments and all inbound\r\n // messages are type-checked before processing.\r\n // Origin cannot be pre-validated because the portal URL varies by environment.\r\n // All inbound messages are type-checked via the dcs: prefix guard.\r\n globalThis.self.addEventListener('message', handleMessage) // NOSONAR\r\n\r\n // Watch for VitePress / Vite HMR page updates. When the framework\r\n // replaces DOM content, our event listeners and AI buttons are lost.\r\n // Re-discover sections after each update so the portal overlay stays in sync.\r\n if (import.meta.hot) {\r\n import.meta.hot.on('vite:afterUpdate', () => {\r\n // Small delay for the framework to finish DOM updates\r\n setTimeout(rediscoverAndNotify, 300)\r\n })\r\n }\r\n }\r\n\r\n // Report available sections, text keys, and content height\r\n const sections = discoverSections()\r\n const textKeys = discoverTextKeys()\r\n const contentHeight = document.body.scrollHeight\r\n\r\n postToParent('dcs:ready', {\r\n sections,\r\n textKeys,\r\n contentHeight,\r\n } satisfies EditorReadyPayload)\r\n}\r\n\r\n// Auto-initialize when the script is loaded (e.g., injected by Vite plugin)\r\n// Delay slightly to ensure the page is fully rendered\r\nif (typeof globalThis.self !== 'undefined' && isInIframe()) {\r\n if (document.readyState === 'loading') {\r\n document.addEventListener('DOMContentLoaded', () => {\r\n // Small delay for Vue/framework hydration\r\n setTimeout(initEditorBridge, 200)\r\n })\r\n } else {\r\n setTimeout(initEditorBridge, 200)\r\n }\r\n}\r\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as vue from 'vue';
|
|
2
|
+
import { ComputedRef } from 'vue';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Types for .dcs/content.yaml structure
|
|
@@ -81,7 +82,7 @@ interface TextContentReturn {
|
|
|
81
82
|
* pageSlug: 'home',
|
|
82
83
|
* defaults: {
|
|
83
84
|
* 'hero.title': 'Welcome to Our Site',
|
|
84
|
-
* 'hero.subtitle': '
|
|
85
|
+
* 'hero.subtitle': 'Build something amazing',
|
|
85
86
|
* 'cta.primary': 'Get Started'
|
|
86
87
|
* }
|
|
87
88
|
* })
|
|
@@ -297,10 +298,8 @@ interface SeoVerificationConfig {
|
|
|
297
298
|
google?: string;
|
|
298
299
|
/** Bing Webmaster Tools verification code */
|
|
299
300
|
bing?: string;
|
|
300
|
-
/**
|
|
301
|
-
|
|
302
|
-
/** Pinterest verification code */
|
|
303
|
-
pinterest?: string;
|
|
301
|
+
/** DuckDuckGo verification (reserved for future use) */
|
|
302
|
+
duckduckgo?: string;
|
|
304
303
|
}
|
|
305
304
|
/**
|
|
306
305
|
* Resolved page SEO configuration (after merging global + page)
|
|
@@ -535,4 +534,92 @@ declare function useSiteVersion(options?: {
|
|
|
535
534
|
fetchOnMount?: boolean;
|
|
536
535
|
}): SiteVersionReturn;
|
|
537
536
|
|
|
538
|
-
|
|
537
|
+
/**
|
|
538
|
+
* useMediaCarousel Composable
|
|
539
|
+
*
|
|
540
|
+
* Extracts media carousel items from text content keys following the pattern:
|
|
541
|
+
* `{prefix}.{N}.url`, `{prefix}.{N}.type`, `{prefix}.{N}.alt`
|
|
542
|
+
*
|
|
543
|
+
* @example
|
|
544
|
+
* ```vue
|
|
545
|
+
* <script setup lang="ts">
|
|
546
|
+
* import { useTextContent, useMediaCarousel } from '@duffcloudservices/cms'
|
|
547
|
+
*
|
|
548
|
+
* const { t } = useTextContent({
|
|
549
|
+
* pageSlug: 'bio-mackenzie',
|
|
550
|
+
* defaults: {
|
|
551
|
+
* 'hero.media-carousel.0.url': '/images/staff/mackenzie.webp',
|
|
552
|
+
* 'hero.media-carousel.0.type': 'image',
|
|
553
|
+
* 'hero.media-carousel.0.alt': 'Mackenzie Kowalick',
|
|
554
|
+
* 'hero.media-carousel.1.url': '/videos/intro.mp4',
|
|
555
|
+
* 'hero.media-carousel.1.type': 'video',
|
|
556
|
+
* 'hero.media-carousel.1.alt': 'Introduction video',
|
|
557
|
+
* }
|
|
558
|
+
* })
|
|
559
|
+
*
|
|
560
|
+
* const { items } = useMediaCarousel({
|
|
561
|
+
* prefix: 'hero.media-carousel',
|
|
562
|
+
* t,
|
|
563
|
+
* defaults: [
|
|
564
|
+
* { url: '/images/staff/mackenzie.webp', type: 'image', alt: 'Mackenzie Kowalick' }
|
|
565
|
+
* ]
|
|
566
|
+
* })
|
|
567
|
+
* </script>
|
|
568
|
+
*
|
|
569
|
+
* <template>
|
|
570
|
+
* <MediaCarousel :items="items" />
|
|
571
|
+
* </template>
|
|
572
|
+
* ```
|
|
573
|
+
*/
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Media carousel item representing an image, video, or embed
|
|
577
|
+
*/
|
|
578
|
+
interface MediaCarouselItem {
|
|
579
|
+
/** URL to the image, video file, or embed URL */
|
|
580
|
+
url: string;
|
|
581
|
+
/** Type of media: 'image', 'video' (direct file), 'youtube', or 'instagram' */
|
|
582
|
+
type: 'image' | 'video' | 'youtube' | 'instagram';
|
|
583
|
+
/** Accessibility alt text */
|
|
584
|
+
alt?: string;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Configuration for useMediaCarousel composable
|
|
588
|
+
*/
|
|
589
|
+
interface UseMediaCarouselConfig {
|
|
590
|
+
/** Key prefix for carousel items (e.g., 'hero.media-carousel') */
|
|
591
|
+
prefix: string;
|
|
592
|
+
/** The t() function from useTextContent */
|
|
593
|
+
t: (key: string, fallback?: string) => string;
|
|
594
|
+
/** Default items to use if no content keys are found */
|
|
595
|
+
defaults?: MediaCarouselItem[];
|
|
596
|
+
/** Maximum number of items to look for (default: 10) */
|
|
597
|
+
maxItems?: number;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Return type for useMediaCarousel composable
|
|
601
|
+
*/
|
|
602
|
+
interface UseMediaCarouselReturn {
|
|
603
|
+
/** Computed array of media carousel items */
|
|
604
|
+
items: ComputedRef<MediaCarouselItem[]>;
|
|
605
|
+
/** Whether any items were found from content keys */
|
|
606
|
+
hasItems: ComputedRef<boolean>;
|
|
607
|
+
/** Number of items in the carousel */
|
|
608
|
+
count: ComputedRef<number>;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Extract media carousel items from text content keys.
|
|
612
|
+
*
|
|
613
|
+
* Looks for keys in the format:
|
|
614
|
+
* - `{prefix}.{N}.url` - Required URL for the media
|
|
615
|
+
* - `{prefix}.{N}.type` - Type: 'image' or 'video' (defaults to 'image')
|
|
616
|
+
* - `{prefix}.{N}.alt` - Alt text for accessibility
|
|
617
|
+
*
|
|
618
|
+
* Items are sorted by index (0, 1, etc.) and only included if they have a valid URL.
|
|
619
|
+
*
|
|
620
|
+
* @param config - Configuration object
|
|
621
|
+
* @returns Media carousel helpers and state
|
|
622
|
+
*/
|
|
623
|
+
declare function useMediaCarousel(config: UseMediaCarouselConfig): UseMediaCarouselReturn;
|
|
624
|
+
|
|
625
|
+
export { type DcsContentFile, type GlobalSeoConfig, type HeadOverrides, type MediaCarouselItem, type PageSeoConfig, type ReleaseNote, type ReleaseNotesReturn, type ResolvedPageSeo, type SeoAlternateConfig, type SeoAuthorConfig, type SeoConfiguration, type SeoImagesConfig, type SeoOpenGraphConfig, type SeoSchemaConfig, type SeoSocialConfig, type SeoTwitterConfig, type SeoVerificationConfig, type SiteVersionReturn, type TextContentConfig, type TextContentReturn, type UseMediaCarouselConfig, type UseMediaCarouselReturn, type UseSeoConfig, type UseSeoReturn, createSiteSEO, useMediaCarousel, useReleaseNotes, useSEO, useSiteVersion, useTextContent };
|
package/dist/index.js
CHANGED
|
@@ -503,7 +503,49 @@ function useSiteVersion(options = {}) {
|
|
|
503
503
|
releaseNotesUrl
|
|
504
504
|
};
|
|
505
505
|
}
|
|
506
|
+
function useMediaCarousel(config) {
|
|
507
|
+
const {
|
|
508
|
+
prefix,
|
|
509
|
+
t,
|
|
510
|
+
defaults = [],
|
|
511
|
+
maxItems = 10
|
|
512
|
+
} = config;
|
|
513
|
+
const items = computed(() => {
|
|
514
|
+
const result = [];
|
|
515
|
+
for (let i = 0; i < maxItems; i++) {
|
|
516
|
+
const urlKey = `${prefix}.${i}.url`;
|
|
517
|
+
const typeKey = `${prefix}.${i}.type`;
|
|
518
|
+
const altKey = `${prefix}.${i}.alt`;
|
|
519
|
+
const url = t(urlKey, "");
|
|
520
|
+
if (!url || url === urlKey) {
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
const typeValue = t(typeKey, "image");
|
|
524
|
+
let type = "image";
|
|
525
|
+
if (typeValue === "video") type = "video";
|
|
526
|
+
else if (typeValue === "youtube") type = "youtube";
|
|
527
|
+
else if (typeValue === "instagram") type = "instagram";
|
|
528
|
+
const alt = t(altKey, "");
|
|
529
|
+
result.push({
|
|
530
|
+
url,
|
|
531
|
+
type,
|
|
532
|
+
alt: alt && alt !== altKey ? alt : void 0
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
if (result.length === 0) {
|
|
536
|
+
return defaults;
|
|
537
|
+
}
|
|
538
|
+
return result;
|
|
539
|
+
});
|
|
540
|
+
const hasItems = computed(() => items.value.length > 0);
|
|
541
|
+
const count = computed(() => items.value.length);
|
|
542
|
+
return {
|
|
543
|
+
items,
|
|
544
|
+
hasItems,
|
|
545
|
+
count
|
|
546
|
+
};
|
|
547
|
+
}
|
|
506
548
|
|
|
507
|
-
export { createSiteSEO, useReleaseNotes, useSEO, useSiteVersion, useTextContent };
|
|
549
|
+
export { createSiteSEO, useMediaCarousel, useReleaseNotes, useSEO, useSiteVersion, useTextContent };
|
|
508
550
|
//# sourceMappingURL=index.js.map
|
|
509
551
|
//# sourceMappingURL=index.js.map
|
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"],"names":["computed","getEnvVar","ref","onMounted","CACHE_TTL"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * useTextContent Composable\r\n *\r\n * Provides text content management with build-time injection support and optional\r\n * runtime API overrides for DCS-managed customer sites.\r\n *\r\n * Content resolution order:\r\n * 1. Runtime API overrides (premium tier only, if mode is 'runtime')\r\n * 2. Build-time content from .dcs/content.yaml (injected via dcsContentPlugin)\r\n * 3. Hardcoded defaults passed to the composable\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup lang=\"ts\">\r\n * import { useTextContent } from '@duffcloudservices/cms'\r\n *\r\n * const { t, isLoading, error } = useTextContent({\r\n * pageSlug: 'home',\r\n * defaults: {\r\n * 'hero.title': 'Welcome to Our Site',\r\n * 'hero.subtitle': 'The best place for widgets',\r\n * 'cta.primary': 'Get Started'\r\n * }\r\n * })\r\n * </script>\r\n *\r\n * <template>\r\n * <h1>{{ t('hero.title') }}</h1>\r\n * <p>{{ t('hero.subtitle') }}</p>\r\n * <button>{{ t('cta.primary') }}</button>\r\n * </template>\r\n * ```\r\n */\r\n\r\nimport { ref, computed, readonly, onMounted, type Ref } from 'vue'\r\nimport type { DcsContentFile, TextContentConfig, TextContentReturn } from '../types/content'\r\n\r\n// Declare the global injected by dcsContentPlugin\r\ndeclare const __DCS_CONTENT__: DcsContentFile | undefined\r\n\r\n// Simple in-memory cache for runtime fetches\r\nconst fetchCache = new Map<string, { data: Record<string, string>; expiresAt: number }>()\r\n\r\n/**\r\n * Safely get build-time content configuration.\r\n * Returns undefined if not available (no content.yaml or plugin not configured).\r\n */\r\nfunction getBuildTimeContent(): DcsContentFile | undefined {\r\n try {\r\n if (typeof __DCS_CONTENT__ !== 'undefined' && __DCS_CONTENT__ !== null) {\r\n return __DCS_CONTENT__\r\n }\r\n } catch {\r\n // __DCS_CONTENT__ not defined - that's fine, use defaults\r\n }\r\n return undefined\r\n}\r\n\r\n/**\r\n * Get build-time content for a specific page, merging global and page-specific content.\r\n */\r\nfunction getBuildTimePageContent(pageSlug: string): Record<string, string> {\r\n const content = getBuildTimeContent()\r\n if (!content) return {}\r\n\r\n const global = content.global ?? {}\r\n const page = content.pages?.[pageSlug] ?? {}\r\n\r\n return { ...global, ...page }\r\n}\r\n\r\n/**\r\n * Get environment variable value, handling both Vite and process.env patterns.\r\n */\r\nfunction getEnvVar(key: string, defaultValue = ''): string {\r\n try {\r\n // Vite pattern\r\n if (typeof import.meta !== 'undefined' && import.meta.env) {\r\n const value = (import.meta.env as Record<string, string | undefined>)[key]\r\n if (value !== undefined) return value\r\n }\r\n } catch {\r\n // import.meta not available\r\n }\r\n\r\n try {\r\n // Node.js pattern\r\n if (typeof process !== 'undefined' && process.env) {\r\n const value = process.env[key]\r\n if (value !== undefined) return value\r\n }\r\n } catch {\r\n // process not available\r\n }\r\n\r\n return defaultValue\r\n}\r\n\r\n/**\r\n * useTextContent composable for DCS-managed text content.\r\n *\r\n * @param config - Configuration object\r\n * @returns Text content helpers and state\r\n */\r\nexport function useTextContent(config: TextContentConfig): TextContentReturn {\r\n const {\r\n pageSlug,\r\n defaults,\r\n fetchOnMount = true,\r\n cacheKey,\r\n cacheTtl = 60000,\r\n } = config\r\n\r\n // Get configuration from environment\r\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', '')\r\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\r\n const textOverrideMode = getEnvVar('VITE_TEXT_OVERRIDE_MODE', 'commit')\r\n const mode: 'commit' | 'runtime' = textOverrideMode === 'runtime' ? 'runtime' : 'commit'\r\n\r\n // Get build-time content immediately (synchronous)\r\n const buildTimeContent = getBuildTimePageContent(pageSlug)\r\n const hasBuildTimeContent = Object.keys(buildTimeContent).length > 0\r\n\r\n // State - initialize with build-time content\r\n const overrides = ref<Record<string, string>>({ ...buildTimeContent })\r\n const isLoading = ref(false)\r\n const error = ref<string | null>(null)\r\n\r\n // Computed merged texts (defaults + overrides)\r\n const texts = computed(() => ({ ...defaults, ...overrides.value }))\r\n\r\n /**\r\n * Get text by key with optional fallback.\r\n * Resolution order: overrides → defaults → fallback → key\r\n */\r\n function t(key: string, fallback?: string): string {\r\n return overrides.value[key] ?? defaults[key] ?? fallback ?? key\r\n }\r\n\r\n /**\r\n * Check if a key has an override (from build-time or runtime).\r\n */\r\n function hasOverride(key: string): boolean {\r\n return key in overrides.value\r\n }\r\n\r\n /**\r\n * Get an array of objects from indexed content keys.\r\n * Useful for lists where keys follow the pattern: arrayKey.index.property\r\n * Example: features.1.title, features.1.description, features.2.title, etc.\r\n *\r\n * @param arrayKey - The base key prefix (e.g., 'features', 'items')\r\n * @returns Array of objects with properties extracted from matching keys, sorted by index\r\n *\r\n * @example\r\n * ```ts\r\n * // Given content keys:\r\n * // positions.1.title = \"Software Engineer\"\r\n * // positions.1.description = \"Build cool stuff\"\r\n * // positions.2.title = \"Designer\"\r\n * // positions.2.description = \"Design cool stuff\"\r\n *\r\n * const positions = getArray('positions')\r\n * // Returns:\r\n * // [\r\n * // { _index: 1, title: \"Software Engineer\", description: \"Build cool stuff\" },\r\n * // { _index: 2, title: \"Designer\", description: \"Design cool stuff\" }\r\n * // ]\r\n * ```\r\n */\r\n function getArray(arrayKey: string): Array<Record<string, unknown> & { _index: number }> {\r\n const items: Record<number, Record<string, unknown> & { _index: number }> = {}\r\n const source = { ...defaults, ...overrides.value }\r\n\r\n Object.keys(source).forEach((key) => {\r\n if (key.startsWith(`${arrayKey}.`)) {\r\n const parts = key.split('.')\r\n // Format: arrayKey.index.property (or arrayKey.index.nested.property)\r\n // Example: features.1.title -> index=1, prop=title\r\n if (parts.length >= 3) {\r\n const index = Number.parseInt(parts[1], 10)\r\n const prop = parts.slice(2).join('.')\r\n\r\n if (!Number.isNaN(index)) {\r\n if (!items[index]) items[index] = { _index: index }\r\n items[index][prop] = source[key]\r\n }\r\n }\r\n }\r\n })\r\n\r\n return Object.values(items).sort((a, b) => a._index - b._index)\r\n }\r\n\r\n /**\r\n * Fetch runtime overrides from the API.\r\n * Only runs if mode is 'runtime' and API is configured.\r\n */\r\n async function fetchOverrides(): Promise<void> {\r\n // Skip API fetch in commit mode - use build-time content only\r\n if (mode !== 'runtime') {\r\n return\r\n }\r\n\r\n // Skip if API not configured\r\n if (!apiBaseUrl || !siteSlug) {\r\n console.warn(\r\n '[@duffcloudservices/cms] Runtime mode enabled but VITE_API_BASE_URL or VITE_SITE_SLUG not set'\r\n )\r\n return\r\n }\r\n\r\n // Check cache first\r\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\r\n const cached = fetchCache.get(effectiveCacheKey)\r\n if (cached && cached.expiresAt > Date.now()) {\r\n overrides.value = { ...buildTimeContent, ...cached.data }\r\n return\r\n }\r\n\r\n isLoading.value = true\r\n error.value = null\r\n\r\n try {\r\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/pages/${pageSlug}/text`\r\n const response = await fetch(url, {\r\n headers: {\r\n Accept: 'application/json',\r\n },\r\n })\r\n\r\n if (!response.ok) {\r\n if (response.status === 404) {\r\n // No overrides for this page - that's fine\r\n return\r\n }\r\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\r\n }\r\n\r\n const data = await response.json()\r\n const apiOverrides = data.overrides ?? data.texts ?? {}\r\n\r\n // Cache the result\r\n fetchCache.set(effectiveCacheKey, {\r\n data: apiOverrides,\r\n expiresAt: Date.now() + cacheTtl,\r\n })\r\n\r\n // Merge: build-time content is baseline, API overrides on top\r\n overrides.value = { ...buildTimeContent, ...apiOverrides }\r\n } catch (e) {\r\n error.value = e instanceof Error ? e.message : 'Failed to load text overrides'\r\n console.error('[@duffcloudservices/cms] Failed to fetch text overrides:', e)\r\n } finally {\r\n isLoading.value = false\r\n }\r\n }\r\n\r\n /**\r\n * Manually refresh overrides.\r\n * In commit mode, resets to build-time content.\r\n * In runtime mode, fetches fresh data from API.\r\n */\r\n async function refresh(): Promise<void> {\r\n if (mode !== 'runtime') {\r\n // In commit mode, just reset to build-time content\r\n overrides.value = { ...buildTimeContent }\r\n return\r\n }\r\n\r\n // Clear cache for this key\r\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\r\n fetchCache.delete(effectiveCacheKey)\r\n\r\n await fetchOverrides()\r\n }\r\n\r\n // Fetch on mount if enabled and in runtime mode (browser only)\r\n if (fetchOnMount && mode === 'runtime' && globalThis.window !== undefined) {\r\n onMounted(() => {\r\n fetchOverrides()\r\n })\r\n }\r\n\r\n return {\r\n t,\r\n getArray,\r\n texts,\r\n overrides,\r\n isLoading: readonly(isLoading) as Ref<boolean>,\r\n error: readonly(error) as Ref<string | null>,\r\n refresh,\r\n hasOverride,\r\n hasBuildTimeContent,\r\n mode,\r\n }\r\n}\r\n","/**\r\n * useSEO Composable\r\n *\r\n * Provides SEO configuration with build-time injection support from .dcs/seo.yaml.\r\n * Generates meta tags, Open Graph, Twitter Cards, and JSON-LD structured data.\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup lang=\"ts\">\r\n * import { useSEO } from '@duffcloudservices/cms'\r\n *\r\n * const { applyHead, getSchema, config } = useSEO('home')\r\n *\r\n * // Apply all meta tags\r\n * applyHead()\r\n *\r\n * // Or customize before applying\r\n * applyHead({\r\n * title: 'Custom Override Title',\r\n * schemas: [...getSchema(), customSchema]\r\n * })\r\n * </script>\r\n * ```\r\n */\r\n\r\nimport { computed, type ComputedRef } from 'vue'\r\nimport { useHead } from '@unhead/vue'\r\nimport type {\r\n SeoConfiguration,\r\n GlobalSeoConfig,\r\n SeoOpenGraphConfig,\r\n SeoTwitterConfig,\r\n SeoSchemaConfig,\r\n ResolvedPageSeo,\r\n UseSeoReturn,\r\n HeadOverrides,\r\n} from '../types/seo'\r\n\r\n// Declare the global injected by dcsSeoPlugin\r\ndeclare const __DCS_SEO__: SeoConfiguration | undefined\r\n\r\n/**\r\n * Safely get build-time SEO configuration.\r\n * Returns undefined if not available (no seo.yaml or plugin not configured).\r\n */\r\nfunction getBuildTimeSeo(): SeoConfiguration | undefined {\r\n try {\r\n if (typeof __DCS_SEO__ !== 'undefined' && __DCS_SEO__ !== null) {\r\n return __DCS_SEO__\r\n }\r\n } catch {\r\n // __DCS_SEO__ not defined - that's fine, use defaults\r\n }\r\n return undefined\r\n}\r\n\r\n// =============================================================================\r\n// Meta Tag Generation Utilities\r\n// =============================================================================\r\n\r\ninterface HeadInput {\r\n title?: string\r\n titleTemplate?: string | ((title: string) => string)\r\n meta?: Array<{ name?: string; property?: string; content: string }>\r\n link?: Array<{ rel: string; href: string; hreflang?: string }>\r\n script?: Array<{ type: string; children: string }>\r\n}\r\n\r\n/**\r\n * Generate Open Graph meta tags from config\r\n */\r\nfunction generateOpenGraphMeta(\r\n og: SeoOpenGraphConfig,\r\n global: GlobalSeoConfig,\r\n pageTitle: string,\r\n pageDescription: string,\r\n canonical: string\r\n): Array<{ property: string; content: string }> {\r\n const tags: Array<{ property: string; content: string }> = []\r\n\r\n tags.push({ property: 'og:title', content: og.title || pageTitle })\r\n tags.push({ property: 'og:description', content: og.description || pageDescription })\r\n tags.push({ property: 'og:url', content: og.url || canonical })\r\n tags.push({ property: 'og:type', content: og.type || 'website' })\r\n\r\n const image = og.image || global.images?.ogDefault\r\n if (image) {\r\n tags.push({ property: 'og:image', content: image })\r\n if (og.imageAlt || pageTitle) {\r\n tags.push({ property: 'og:image:alt', content: og.imageAlt || pageTitle })\r\n }\r\n if (og.imageWidth) {\r\n tags.push({ property: 'og:image:width', content: String(og.imageWidth) })\r\n }\r\n if (og.imageHeight) {\r\n tags.push({ property: 'og:image:height', content: String(og.imageHeight) })\r\n }\r\n }\r\n\r\n if (global.siteName) {\r\n tags.push({ property: 'og:site_name', content: global.siteName })\r\n }\r\n\r\n if (global.locale) {\r\n tags.push({ property: 'og:locale', content: global.locale })\r\n }\r\n\r\n // Article-specific tags\r\n if (og.type === 'article') {\r\n if (og.publishedTime) {\r\n tags.push({ property: 'article:published_time', content: og.publishedTime })\r\n }\r\n if (og.modifiedTime) {\r\n tags.push({ property: 'article:modified_time', content: og.modifiedTime })\r\n }\r\n if (og.author) {\r\n tags.push({ property: 'article:author', content: og.author })\r\n }\r\n if (og.section) {\r\n tags.push({ property: 'article:section', content: og.section })\r\n }\r\n if (og.tags) {\r\n og.tags.forEach((tag) => {\r\n tags.push({ property: 'article:tag', content: tag })\r\n })\r\n }\r\n }\r\n\r\n return tags\r\n}\r\n\r\n/**\r\n * Generate Twitter Card meta tags from config\r\n */\r\nfunction generateTwitterMeta(\r\n twitter: SeoTwitterConfig,\r\n global: GlobalSeoConfig,\r\n pageTitle: string,\r\n pageDescription: string\r\n): Array<{ name: string; content: string }> {\r\n const tags: Array<{ name: string; content: string }> = []\r\n\r\n tags.push({ name: 'twitter:card', content: twitter.card || 'summary_large_image' })\r\n tags.push({ name: 'twitter:title', content: twitter.title || pageTitle })\r\n tags.push({ name: 'twitter:description', content: twitter.description || pageDescription })\r\n\r\n const image = twitter.image || global.images?.twitterDefault\r\n if (image) {\r\n tags.push({ name: 'twitter:image', content: image })\r\n if (twitter.imageAlt || pageTitle) {\r\n tags.push({ name: 'twitter:image:alt', content: twitter.imageAlt || pageTitle })\r\n }\r\n }\r\n\r\n const site = twitter.site || global.social?.twitter\r\n if (site) {\r\n tags.push({ name: 'twitter:site', content: site.startsWith('@') ? site : `@${site}` })\r\n }\r\n\r\n if (twitter.creator) {\r\n tags.push({\r\n name: 'twitter:creator',\r\n content: twitter.creator.startsWith('@') ? twitter.creator : `@${twitter.creator}`,\r\n })\r\n }\r\n\r\n return tags\r\n}\r\n\r\n/**\r\n * Generate JSON-LD script content from schemas\r\n */\r\nfunction generateJsonLd(schemas: SeoSchemaConfig[], global: GlobalSeoConfig): object[] {\r\n return schemas.map((schema) => {\r\n const base: Record<string, unknown> = {\r\n '@context': 'https://schema.org',\r\n '@type': schema.type,\r\n }\r\n\r\n // Merge properties\r\n if (schema.properties) {\r\n Object.assign(base, schema.properties)\r\n }\r\n\r\n // Auto-populate common properties from global config\r\n if (schema.type === 'WebSite' && global.siteUrl && !base.url) {\r\n base.url = global.siteUrl\r\n }\r\n if (schema.type === 'WebSite' && global.siteName && !base.name) {\r\n base.name = global.siteName\r\n }\r\n\r\n return base\r\n })\r\n}\r\n\r\n/**\r\n * Resolve page SEO by merging global defaults with page-specific config\r\n */\r\nfunction resolvePageSeo(\r\n pageSlug: string,\r\n pagePath: string | undefined,\r\n seoConfig: SeoConfiguration | undefined\r\n): ResolvedPageSeo {\r\n const global = seoConfig?.global ?? {}\r\n const page = seoConfig?.pages?.[pageSlug] ?? {}\r\n\r\n // Build canonical URL\r\n let canonical = page.canonical || ''\r\n if (!canonical && global.siteUrl) {\r\n const path = pagePath ?? (pageSlug === 'home' ? '/' : `/${pageSlug}`)\r\n canonical = `${global.siteUrl.replace(/\\/$/, '')}${path}`\r\n }\r\n\r\n // Build title\r\n let title = page.title || global.defaultTitle || pageSlug\r\n if (!page.noTitleTemplate && global.titleTemplate) {\r\n title = global.titleTemplate.replace('%s', title)\r\n }\r\n\r\n // Merge Open Graph\r\n const openGraph: ResolvedPageSeo['openGraph'] = {\r\n type: page.openGraph?.type || 'website',\r\n title: page.openGraph?.title || page.title || global.defaultTitle || '',\r\n description: page.openGraph?.description || page.description || global.defaultDescription || '',\r\n ...page.openGraph,\r\n }\r\n\r\n // Merge Twitter\r\n const twitter: ResolvedPageSeo['twitter'] = {\r\n card: page.twitter?.card || 'summary_large_image',\r\n ...page.twitter,\r\n }\r\n\r\n // Combine schemas (global + page)\r\n const schemas = [...(global.schemas ?? []), ...(page.schemas ?? [])]\r\n\r\n return {\r\n title,\r\n description: page.description || global.defaultDescription || '',\r\n canonical,\r\n robots: page.robots || global.robots || 'index, follow',\r\n openGraph,\r\n twitter,\r\n schemas,\r\n alternates: page.alternates ?? [],\r\n }\r\n}\r\n\r\n/**\r\n * useSEO composable for DCS-managed SEO configuration.\r\n *\r\n * @param pageSlug - Page slug matching entry in seo.yaml\r\n * @param pagePath - Optional page path for canonical URL generation\r\n * @returns SEO helpers and state\r\n */\r\nexport function useSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\r\n const seoConfig = getBuildTimeSeo()\r\n const hasBuildTimeSeo = seoConfig !== undefined\r\n\r\n // Computed resolved config\r\n const config: ComputedRef<ResolvedPageSeo> = computed(() =>\r\n resolvePageSeo(pageSlug, pagePath, seoConfig)\r\n )\r\n\r\n /**\r\n * Get JSON-LD schema objects for the page\r\n */\r\n function getSchema(): object[] {\r\n return generateJsonLd(config.value.schemas, seoConfig?.global ?? {})\r\n }\r\n\r\n /**\r\n * Get canonical URL for the page\r\n */\r\n function getCanonical(): string {\r\n return config.value.canonical\r\n }\r\n\r\n /**\r\n * Apply all meta tags via useHead\r\n */\r\n function applyHead(overrides?: HeadOverrides): void {\r\n const resolved = config.value\r\n const global = seoConfig?.global ?? {}\r\n\r\n const title = overrides?.title ?? resolved.title\r\n const description = overrides?.description ?? resolved.description\r\n\r\n // Build meta tags\r\n const meta: HeadInput['meta'] = []\r\n\r\n // Basic meta\r\n meta.push({ name: 'description', content: description })\r\n if (resolved.robots) {\r\n meta.push({ name: 'robots', content: resolved.robots })\r\n }\r\n\r\n // Verification codes\r\n if (global.verification?.google) {\r\n meta.push({ name: 'google-site-verification', content: global.verification.google })\r\n }\r\n if (global.verification?.bing) {\r\n meta.push({ name: 'msvalidate.01', content: global.verification.bing })\r\n }\r\n\r\n // Open Graph\r\n const ogMeta = generateOpenGraphMeta(\r\n resolved.openGraph,\r\n global,\r\n title,\r\n description,\r\n resolved.canonical\r\n )\r\n meta.push(...ogMeta.map((t) => ({ property: t.property, content: t.content })))\r\n\r\n // Twitter\r\n const twitterMeta = generateTwitterMeta(resolved.twitter, global, title, description)\r\n meta.push(...twitterMeta.map((t) => ({ name: t.name, content: t.content })))\r\n\r\n // Additional overrides\r\n if (overrides?.meta) {\r\n meta.push(...overrides.meta)\r\n }\r\n\r\n // Build links\r\n const link: HeadInput['link'] = []\r\n\r\n // Canonical\r\n if (resolved.canonical) {\r\n link.push({ rel: 'canonical', href: resolved.canonical })\r\n }\r\n\r\n // Alternate languages\r\n resolved.alternates.forEach((alt) => {\r\n link.push({ rel: 'alternate', href: alt.href, hreflang: alt.hreflang })\r\n })\r\n\r\n // Build scripts (JSON-LD)\r\n const schemas = overrides?.schemas ?? getSchema()\r\n const script: HeadInput['script'] = schemas.map((schema) => ({\r\n type: 'application/ld+json',\r\n children: JSON.stringify(schema),\r\n }))\r\n\r\n // Apply via useHead\r\n useHead({\r\n title,\r\n meta,\r\n link,\r\n script,\r\n })\r\n }\r\n\r\n return {\r\n config,\r\n applyHead,\r\n getSchema,\r\n getCanonical,\r\n hasBuildTimeSeo,\r\n }\r\n}\r\n\r\n/**\r\n * Create a typed useSEO function with site-specific defaults.\r\n * Useful for creating a site-wide wrapper.\r\n *\r\n * @example\r\n * ```ts\r\n * // composables/useSiteSeo.ts\r\n * import { createSiteSEO } from '@duffcloudservices/cms'\r\n *\r\n * export const useSiteSeo = createSiteSEO({\r\n * siteName: 'My Site',\r\n * siteUrl: 'https://example.com'\r\n * })\r\n * ```\r\n */\r\nexport function createSiteSEO(\r\n _siteDefaults: Partial<GlobalSeoConfig>\r\n): (pageSlug: string, pagePath?: string) => UseSeoReturn {\r\n return function siteUseSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\r\n // Note: siteDefaults would be used if we needed to override at runtime\r\n // but build-time injection handles this via dcsSeoPlugin\r\n return useSEO(pageSlug, pagePath)\r\n }\r\n}\r\n","/**\r\n * useReleaseNotes Composable\r\n *\r\n * Fetches and displays versioned release notes from the DCS Portal API.\r\n * Supports fetching specific versions or the latest release.\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup lang=\"ts\">\r\n * import { useReleaseNotes } from '@duffcloudservices/cms'\r\n * import { useRoute } from 'vue-router'\r\n *\r\n * const route = useRoute()\r\n * const version = route.params.version as string || 'latest'\r\n *\r\n * const { releaseNote, isLoading, error } = useReleaseNotes(version)\r\n * </script>\r\n *\r\n * <template>\r\n * <div v-if=\"isLoading\">Loading...</div>\r\n * <div v-else-if=\"error\">{{ error }}</div>\r\n * <article v-else-if=\"releaseNote\">\r\n * <h1>{{ releaseNote.title }}</h1>\r\n * <p>{{ releaseNote.summary }}</p>\r\n * <div v-html=\"renderedMarkdown\" />\r\n * </article>\r\n * </template>\r\n * ```\r\n */\r\n\r\nimport { ref, onMounted } from 'vue'\r\nimport type { ReleaseNote, ReleaseNotesReturn } from '../types/release-notes'\r\n\r\n/**\r\n * Get environment variable value.\r\n */\r\nfunction getEnvVar(key: string, defaultValue = ''): string {\r\n try {\r\n if (typeof import.meta !== 'undefined' && import.meta.env) {\r\n const value = (import.meta.env as Record<string, string | undefined>)[key]\r\n if (value !== undefined) return value\r\n }\r\n } catch {\r\n // import.meta not available\r\n }\r\n return defaultValue\r\n}\r\n\r\n// Simple cache for release notes\r\nconst releaseNotesCache = new Map<string, { data: ReleaseNote; expiresAt: number }>()\r\nconst CACHE_TTL = 5 * 60 * 1000 // 5 minutes\r\n\r\n/**\r\n * useReleaseNotes composable for fetching release notes from the DCS API.\r\n *\r\n * @param version - Semantic version (e.g., \"1.2.0\") or \"latest\"\r\n * @param options - Optional configuration\r\n * @returns Release notes data and state\r\n */\r\nexport function useReleaseNotes(\r\n version: string,\r\n options: { fetchOnMount?: boolean } = {}\r\n): ReleaseNotesReturn {\r\n const { fetchOnMount = true } = options\r\n\r\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\r\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\r\n\r\n const releaseNote = ref<ReleaseNote | null>(null)\r\n const isLoading = ref(false)\r\n const error = ref<string | null>(null)\r\n\r\n async function fetchReleaseNotes(): Promise<void> {\r\n if (!siteSlug) {\r\n error.value = 'VITE_SITE_SLUG environment variable not set'\r\n return\r\n }\r\n\r\n // Check cache\r\n const cacheKey = `${siteSlug}:${version}`\r\n const cached = releaseNotesCache.get(cacheKey)\r\n if (cached && cached.expiresAt > Date.now()) {\r\n releaseNote.value = cached.data\r\n return\r\n }\r\n\r\n isLoading.value = true\r\n error.value = null\r\n\r\n try {\r\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/release-notes/${version}`\r\n const response = await fetch(url, {\r\n headers: {\r\n Accept: 'application/json',\r\n },\r\n })\r\n\r\n if (!response.ok) {\r\n if (response.status === 404) {\r\n error.value = `Release notes for version ${version} not found`\r\n return\r\n }\r\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\r\n }\r\n\r\n const data = await response.json()\r\n\r\n // Normalize the response\r\n const note: ReleaseNote = {\r\n version: data.version,\r\n title: data.title,\r\n summary: data.summary || '',\r\n notesMarkdown: data.notesMarkdown || data.notes || '',\r\n changeCount: data.changeCount || 0,\r\n releaseDate: data.releaseDate || data.releasedAt || '',\r\n }\r\n\r\n // Cache the result\r\n releaseNotesCache.set(cacheKey, {\r\n data: note,\r\n expiresAt: Date.now() + CACHE_TTL,\r\n })\r\n\r\n releaseNote.value = note\r\n } catch (e) {\r\n error.value = e instanceof Error ? e.message : 'Failed to load release notes'\r\n console.error('[@duffcloudservices/cms] Failed to fetch release notes:', e)\r\n } finally {\r\n isLoading.value = false\r\n }\r\n }\r\n\r\n async function refresh(): Promise<void> {\r\n // Clear cache\r\n const cacheKey = `${siteSlug}:${version}`\r\n releaseNotesCache.delete(cacheKey)\r\n await fetchReleaseNotes()\r\n }\r\n\r\n // Fetch on mount if enabled\r\n if (fetchOnMount && typeof window !== 'undefined') {\r\n onMounted(() => {\r\n fetchReleaseNotes()\r\n })\r\n }\r\n\r\n return {\r\n releaseNote,\r\n isLoading,\r\n error,\r\n refresh,\r\n }\r\n}\r\n","/**\r\n * useSiteVersion Composable\r\n *\r\n * Gets the current site version for footer badges and version displays.\r\n * Fetches the latest release version from the DCS Portal API.\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup lang=\"ts\">\r\n * import { useSiteVersion } from '@duffcloudservices/cms'\r\n *\r\n * const { version, releaseNotesUrl } = useSiteVersion()\r\n * </script>\r\n *\r\n * <template>\r\n * <footer>\r\n * <a v-if=\"version\" :href=\"releaseNotesUrl\" class=\"version-badge\">\r\n * v{{ version }}\r\n * </a>\r\n * </footer>\r\n * </template>\r\n * ```\r\n */\r\n\r\nimport { ref, computed, onMounted } from 'vue'\r\nimport type { SiteVersionReturn } from '../types/release-notes'\r\n\r\n/**\r\n * Get environment variable value.\r\n */\r\nfunction getEnvVar(key: string, defaultValue = ''): string {\r\n try {\r\n if (typeof import.meta !== 'undefined' && import.meta.env) {\r\n const value = (import.meta.env as Record<string, string | undefined>)[key]\r\n if (value !== undefined) return value\r\n }\r\n } catch {\r\n // import.meta not available\r\n }\r\n return defaultValue\r\n}\r\n\r\n// Cache for site version\r\nlet versionCache: { version: string; expiresAt: number } | null = null\r\nconst CACHE_TTL = 10 * 60 * 1000 // 10 minutes\r\n\r\n/**\r\n * useSiteVersion composable for displaying the current site version.\r\n *\r\n * @param options - Optional configuration\r\n * @returns Site version data and computed URL\r\n */\r\nexport function useSiteVersion(options: { fetchOnMount?: boolean } = {}): SiteVersionReturn {\r\n const { fetchOnMount = true } = options\r\n\r\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\r\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\r\n\r\n const version = ref<string | null>(null)\r\n const isLoading = ref(false)\r\n\r\n const releaseNotesUrl = computed(() => {\r\n if (!version.value) return '/releaseNotes/latest'\r\n return `/releaseNotes/${version.value}`\r\n })\r\n\r\n async function fetchVersion(): Promise<void> {\r\n if (!siteSlug) {\r\n return\r\n }\r\n\r\n // Check cache\r\n if (versionCache && versionCache.expiresAt > Date.now()) {\r\n version.value = versionCache.version\r\n return\r\n }\r\n\r\n isLoading.value = true\r\n\r\n try {\r\n // Fetch the latest release notes to get the version\r\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/release-notes/latest`\r\n const response = await fetch(url, {\r\n headers: {\r\n Accept: 'application/json',\r\n },\r\n })\r\n\r\n if (!response.ok) {\r\n // No release notes yet - that's fine\r\n return\r\n }\r\n\r\n const data = await response.json()\r\n const latestVersion = data.version\r\n\r\n // Cache the result\r\n versionCache = {\r\n version: latestVersion,\r\n expiresAt: Date.now() + CACHE_TTL,\r\n }\r\n\r\n version.value = latestVersion\r\n } catch (e) {\r\n console.error('[@duffcloudservices/cms] Failed to fetch site version:', e)\r\n } finally {\r\n isLoading.value = false\r\n }\r\n }\r\n\r\n // Fetch on mount if enabled\r\n if (fetchOnMount && typeof window !== 'undefined') {\r\n onMounted(() => {\r\n fetchVersion()\r\n })\r\n }\r\n\r\n return {\r\n version,\r\n isLoading,\r\n releaseNotesUrl,\r\n }\r\n}\r\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"],"names":["computed","getEnvVar","ref","onMounted","CACHE_TTL"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * useTextContent Composable\r\n *\r\n * Provides text content management with build-time injection support and optional\r\n * runtime API overrides for DCS-managed customer sites.\r\n *\r\n * Content resolution order:\r\n * 1. Runtime API overrides (premium tier only, if mode is 'runtime')\r\n * 2. Build-time content from .dcs/content.yaml (injected via dcsContentPlugin)\r\n * 3. Hardcoded defaults passed to the composable\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup lang=\"ts\">\r\n * import { useTextContent } from '@duffcloudservices/cms'\r\n *\r\n * const { t, isLoading, error } = useTextContent({\r\n * pageSlug: 'home',\r\n * defaults: {\r\n * 'hero.title': 'Welcome to Our Site',\r\n * 'hero.subtitle': 'Build something amazing',\r\n * 'cta.primary': 'Get Started'\r\n * }\r\n * })\r\n * </script>\r\n *\r\n * <template>\r\n * <h1>{{ t('hero.title') }}</h1>\r\n * <p>{{ t('hero.subtitle') }}</p>\r\n * <button>{{ t('cta.primary') }}</button>\r\n * </template>\r\n * ```\r\n */\r\n\r\nimport { ref, computed, readonly, onMounted, type Ref } from 'vue'\r\nimport type { DcsContentFile, TextContentConfig, TextContentReturn } from '../types/content'\r\n\r\n// Declare the global injected by dcsContentPlugin\r\ndeclare const __DCS_CONTENT__: DcsContentFile | undefined\r\n\r\n// Simple in-memory cache for runtime fetches\r\nconst fetchCache = new Map<string, { data: Record<string, string>; expiresAt: number }>()\r\n\r\n/**\r\n * Safely get build-time content configuration.\r\n * Returns undefined if not available (no content.yaml or plugin not configured).\r\n */\r\nfunction getBuildTimeContent(): DcsContentFile | undefined {\r\n try {\r\n if (typeof __DCS_CONTENT__ !== 'undefined' && __DCS_CONTENT__ !== null) {\r\n return __DCS_CONTENT__\r\n }\r\n } catch {\r\n // __DCS_CONTENT__ not defined - that's fine, use defaults\r\n }\r\n return undefined\r\n}\r\n\r\n/**\r\n * Get build-time content for a specific page, merging global and page-specific content.\r\n */\r\nfunction getBuildTimePageContent(pageSlug: string): Record<string, string> {\r\n const content = getBuildTimeContent()\r\n if (!content) return {}\r\n\r\n const global = content.global ?? {}\r\n const page = content.pages?.[pageSlug] ?? {}\r\n\r\n return { ...global, ...page }\r\n}\r\n\r\n/**\r\n * Get environment variable value, handling both Vite and process.env patterns.\r\n */\r\nfunction getEnvVar(key: string, defaultValue = ''): string {\r\n try {\r\n // Vite pattern\r\n if (typeof import.meta !== 'undefined' && import.meta.env) {\r\n const value = (import.meta.env as Record<string, string | undefined>)[key]\r\n if (value !== undefined) return value\r\n }\r\n } catch {\r\n // import.meta not available\r\n }\r\n\r\n try {\r\n // Node.js pattern\r\n if (typeof process !== 'undefined' && process.env) {\r\n const value = process.env[key]\r\n if (value !== undefined) return value\r\n }\r\n } catch {\r\n // process not available\r\n }\r\n\r\n return defaultValue\r\n}\r\n\r\n/**\r\n * useTextContent composable for DCS-managed text content.\r\n *\r\n * @param config - Configuration object\r\n * @returns Text content helpers and state\r\n */\r\nexport function useTextContent(config: TextContentConfig): TextContentReturn {\r\n const {\r\n pageSlug,\r\n defaults,\r\n fetchOnMount = true,\r\n cacheKey,\r\n cacheTtl = 60000,\r\n } = config\r\n\r\n // Get configuration from environment\r\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', '')\r\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\r\n const textOverrideMode = getEnvVar('VITE_TEXT_OVERRIDE_MODE', 'commit')\r\n const mode: 'commit' | 'runtime' = textOverrideMode === 'runtime' ? 'runtime' : 'commit'\r\n\r\n // Get build-time content immediately (synchronous)\r\n const buildTimeContent = getBuildTimePageContent(pageSlug)\r\n const hasBuildTimeContent = Object.keys(buildTimeContent).length > 0\r\n\r\n // State - initialize with build-time content\r\n const overrides = ref<Record<string, string>>({ ...buildTimeContent })\r\n const isLoading = ref(false)\r\n const error = ref<string | null>(null)\r\n\r\n // Computed merged texts (defaults + overrides)\r\n const texts = computed(() => ({ ...defaults, ...overrides.value }))\r\n\r\n /**\r\n * Get text by key with optional fallback.\r\n * Resolution order: overrides → defaults → fallback → key\r\n */\r\n function t(key: string, fallback?: string): string {\r\n return overrides.value[key] ?? defaults[key] ?? fallback ?? key\r\n }\r\n\r\n /**\r\n * Check if a key has an override (from build-time or runtime).\r\n */\r\n function hasOverride(key: string): boolean {\r\n return key in overrides.value\r\n }\r\n\r\n /**\r\n * Get an array of objects from indexed content keys.\r\n * Useful for lists where keys follow the pattern: arrayKey.index.property\r\n * Example: features.1.title, features.1.description, features.2.title, etc.\r\n *\r\n * @param arrayKey - The base key prefix (e.g., 'features', 'items')\r\n * @returns Array of objects with properties extracted from matching keys, sorted by index\r\n *\r\n * @example\r\n * ```ts\r\n * // Given content keys:\r\n * // positions.1.title = \"Software Engineer\"\r\n * // positions.1.description = \"Build cool stuff\"\r\n * // positions.2.title = \"Designer\"\r\n * // positions.2.description = \"Design cool stuff\"\r\n *\r\n * const positions = getArray('positions')\r\n * // Returns:\r\n * // [\r\n * // { _index: 1, title: \"Software Engineer\", description: \"Build cool stuff\" },\r\n * // { _index: 2, title: \"Designer\", description: \"Design cool stuff\" }\r\n * // ]\r\n * ```\r\n */\r\n function getArray(arrayKey: string): Array<Record<string, unknown> & { _index: number }> {\r\n const items: Record<number, Record<string, unknown> & { _index: number }> = {}\r\n const source = { ...defaults, ...overrides.value }\r\n\r\n Object.keys(source).forEach((key) => {\r\n if (key.startsWith(`${arrayKey}.`)) {\r\n const parts = key.split('.')\r\n // Format: arrayKey.index.property (or arrayKey.index.nested.property)\r\n // Example: features.1.title -> index=1, prop=title\r\n if (parts.length >= 3) {\r\n const index = Number.parseInt(parts[1], 10)\r\n const prop = parts.slice(2).join('.')\r\n\r\n if (!Number.isNaN(index)) {\r\n if (!items[index]) items[index] = { _index: index }\r\n items[index][prop] = source[key]\r\n }\r\n }\r\n }\r\n })\r\n\r\n return Object.values(items).sort((a, b) => a._index - b._index)\r\n }\r\n\r\n /**\r\n * Fetch runtime overrides from the API.\r\n * Only runs if mode is 'runtime' and API is configured.\r\n */\r\n async function fetchOverrides(): Promise<void> {\r\n // Skip API fetch in commit mode - use build-time content only\r\n if (mode !== 'runtime') {\r\n return\r\n }\r\n\r\n // Skip if API not configured\r\n if (!apiBaseUrl || !siteSlug) {\r\n console.warn(\r\n '[@duffcloudservices/cms] Runtime mode enabled but VITE_API_BASE_URL or VITE_SITE_SLUG not set'\r\n )\r\n return\r\n }\r\n\r\n // Check cache first\r\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\r\n const cached = fetchCache.get(effectiveCacheKey)\r\n if (cached && cached.expiresAt > Date.now()) {\r\n overrides.value = { ...buildTimeContent, ...cached.data }\r\n return\r\n }\r\n\r\n isLoading.value = true\r\n error.value = null\r\n\r\n try {\r\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/pages/${pageSlug}/text`\r\n const response = await fetch(url, {\r\n headers: {\r\n Accept: 'application/json',\r\n },\r\n })\r\n\r\n if (!response.ok) {\r\n if (response.status === 404) {\r\n // No overrides for this page - that's fine\r\n return\r\n }\r\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\r\n }\r\n\r\n const data = await response.json()\r\n const apiOverrides = data.overrides ?? data.texts ?? {}\r\n\r\n // Cache the result\r\n fetchCache.set(effectiveCacheKey, {\r\n data: apiOverrides,\r\n expiresAt: Date.now() + cacheTtl,\r\n })\r\n\r\n // Merge: build-time content is baseline, API overrides on top\r\n overrides.value = { ...buildTimeContent, ...apiOverrides }\r\n } catch (e) {\r\n error.value = e instanceof Error ? e.message : 'Failed to load text overrides'\r\n console.error('[@duffcloudservices/cms] Failed to fetch text overrides:', e)\r\n } finally {\r\n isLoading.value = false\r\n }\r\n }\r\n\r\n /**\r\n * Manually refresh overrides.\r\n * In commit mode, resets to build-time content.\r\n * In runtime mode, fetches fresh data from API.\r\n */\r\n async function refresh(): Promise<void> {\r\n if (mode !== 'runtime') {\r\n // In commit mode, just reset to build-time content\r\n overrides.value = { ...buildTimeContent }\r\n return\r\n }\r\n\r\n // Clear cache for this key\r\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\r\n fetchCache.delete(effectiveCacheKey)\r\n\r\n await fetchOverrides()\r\n }\r\n\r\n // Fetch on mount if enabled and in runtime mode (browser only)\r\n if (fetchOnMount && mode === 'runtime' && globalThis.window !== undefined) {\r\n onMounted(() => {\r\n fetchOverrides()\r\n })\r\n }\r\n\r\n return {\r\n t,\r\n getArray,\r\n texts,\r\n overrides,\r\n isLoading: readonly(isLoading) as Ref<boolean>,\r\n error: readonly(error) as Ref<string | null>,\r\n refresh,\r\n hasOverride,\r\n hasBuildTimeContent,\r\n mode,\r\n }\r\n}\r\n","/**\r\n * useSEO Composable\r\n *\r\n * Provides SEO configuration with build-time injection support from .dcs/seo.yaml.\r\n * Generates meta tags, Open Graph, Twitter Cards, and JSON-LD structured data.\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup lang=\"ts\">\r\n * import { useSEO } from '@duffcloudservices/cms'\r\n *\r\n * const { applyHead, getSchema, config } = useSEO('home')\r\n *\r\n * // Apply all meta tags\r\n * applyHead()\r\n *\r\n * // Or customize before applying\r\n * applyHead({\r\n * title: 'Custom Override Title',\r\n * schemas: [...getSchema(), customSchema]\r\n * })\r\n * </script>\r\n * ```\r\n */\r\n\r\nimport { computed, type ComputedRef } from 'vue'\r\nimport { useHead } from '@unhead/vue'\r\nimport type {\r\n SeoConfiguration,\r\n GlobalSeoConfig,\r\n SeoOpenGraphConfig,\r\n SeoTwitterConfig,\r\n SeoSchemaConfig,\r\n ResolvedPageSeo,\r\n UseSeoReturn,\r\n HeadOverrides,\r\n} from '../types/seo'\r\n\r\n// Declare the global injected by dcsSeoPlugin\r\ndeclare const __DCS_SEO__: SeoConfiguration | undefined\r\n\r\n/**\r\n * Safely get build-time SEO configuration.\r\n * Returns undefined if not available (no seo.yaml or plugin not configured).\r\n */\r\nfunction getBuildTimeSeo(): SeoConfiguration | undefined {\r\n try {\r\n if (typeof __DCS_SEO__ !== 'undefined' && __DCS_SEO__ !== null) {\r\n return __DCS_SEO__\r\n }\r\n } catch {\r\n // __DCS_SEO__ not defined - that's fine, use defaults\r\n }\r\n return undefined\r\n}\r\n\r\n// =============================================================================\r\n// Meta Tag Generation Utilities\r\n// =============================================================================\r\n\r\ninterface HeadInput {\r\n title?: string\r\n titleTemplate?: string | ((title: string) => string)\r\n meta?: Array<{ name?: string; property?: string; content: string }>\r\n link?: Array<{ rel: string; href: string; hreflang?: string }>\r\n script?: Array<{ type: string; children: string }>\r\n}\r\n\r\n/**\r\n * Generate Open Graph meta tags from config\r\n */\r\nfunction generateOpenGraphMeta(\r\n og: SeoOpenGraphConfig,\r\n global: GlobalSeoConfig,\r\n pageTitle: string,\r\n pageDescription: string,\r\n canonical: string\r\n): Array<{ property: string; content: string }> {\r\n const tags: Array<{ property: string; content: string }> = []\r\n\r\n tags.push({ property: 'og:title', content: og.title || pageTitle })\r\n tags.push({ property: 'og:description', content: og.description || pageDescription })\r\n tags.push({ property: 'og:url', content: og.url || canonical })\r\n tags.push({ property: 'og:type', content: og.type || 'website' })\r\n\r\n const image = og.image || global.images?.ogDefault\r\n if (image) {\r\n tags.push({ property: 'og:image', content: image })\r\n if (og.imageAlt || pageTitle) {\r\n tags.push({ property: 'og:image:alt', content: og.imageAlt || pageTitle })\r\n }\r\n if (og.imageWidth) {\r\n tags.push({ property: 'og:image:width', content: String(og.imageWidth) })\r\n }\r\n if (og.imageHeight) {\r\n tags.push({ property: 'og:image:height', content: String(og.imageHeight) })\r\n }\r\n }\r\n\r\n if (global.siteName) {\r\n tags.push({ property: 'og:site_name', content: global.siteName })\r\n }\r\n\r\n if (global.locale) {\r\n tags.push({ property: 'og:locale', content: global.locale })\r\n }\r\n\r\n // Article-specific tags\r\n if (og.type === 'article') {\r\n if (og.publishedTime) {\r\n tags.push({ property: 'article:published_time', content: og.publishedTime })\r\n }\r\n if (og.modifiedTime) {\r\n tags.push({ property: 'article:modified_time', content: og.modifiedTime })\r\n }\r\n if (og.author) {\r\n tags.push({ property: 'article:author', content: og.author })\r\n }\r\n if (og.section) {\r\n tags.push({ property: 'article:section', content: og.section })\r\n }\r\n if (og.tags) {\r\n og.tags.forEach((tag) => {\r\n tags.push({ property: 'article:tag', content: tag })\r\n })\r\n }\r\n }\r\n\r\n return tags\r\n}\r\n\r\n/**\r\n * Generate Twitter Card meta tags from config\r\n */\r\nfunction generateTwitterMeta(\r\n twitter: SeoTwitterConfig,\r\n global: GlobalSeoConfig,\r\n pageTitle: string,\r\n pageDescription: string\r\n): Array<{ name: string; content: string }> {\r\n const tags: Array<{ name: string; content: string }> = []\r\n\r\n tags.push({ name: 'twitter:card', content: twitter.card || 'summary_large_image' })\r\n tags.push({ name: 'twitter:title', content: twitter.title || pageTitle })\r\n tags.push({ name: 'twitter:description', content: twitter.description || pageDescription })\r\n\r\n const image = twitter.image || global.images?.twitterDefault\r\n if (image) {\r\n tags.push({ name: 'twitter:image', content: image })\r\n if (twitter.imageAlt || pageTitle) {\r\n tags.push({ name: 'twitter:image:alt', content: twitter.imageAlt || pageTitle })\r\n }\r\n }\r\n\r\n const site = twitter.site || global.social?.twitter\r\n if (site) {\r\n tags.push({ name: 'twitter:site', content: site.startsWith('@') ? site : `@${site}` })\r\n }\r\n\r\n if (twitter.creator) {\r\n tags.push({\r\n name: 'twitter:creator',\r\n content: twitter.creator.startsWith('@') ? twitter.creator : `@${twitter.creator}`,\r\n })\r\n }\r\n\r\n return tags\r\n}\r\n\r\n/**\r\n * Generate JSON-LD script content from schemas\r\n */\r\nfunction generateJsonLd(schemas: SeoSchemaConfig[], global: GlobalSeoConfig): object[] {\r\n return schemas.map((schema) => {\r\n const base: Record<string, unknown> = {\r\n '@context': 'https://schema.org',\r\n '@type': schema.type,\r\n }\r\n\r\n // Merge properties\r\n if (schema.properties) {\r\n Object.assign(base, schema.properties)\r\n }\r\n\r\n // Auto-populate common properties from global config\r\n if (schema.type === 'WebSite' && global.siteUrl && !base.url) {\r\n base.url = global.siteUrl\r\n }\r\n if (schema.type === 'WebSite' && global.siteName && !base.name) {\r\n base.name = global.siteName\r\n }\r\n\r\n return base\r\n })\r\n}\r\n\r\n/**\r\n * Resolve page SEO by merging global defaults with page-specific config\r\n */\r\nfunction resolvePageSeo(\r\n pageSlug: string,\r\n pagePath: string | undefined,\r\n seoConfig: SeoConfiguration | undefined\r\n): ResolvedPageSeo {\r\n const global = seoConfig?.global ?? {}\r\n const page = seoConfig?.pages?.[pageSlug] ?? {}\r\n\r\n // Build canonical URL\r\n let canonical = page.canonical || ''\r\n if (!canonical && global.siteUrl) {\r\n const path = pagePath ?? (pageSlug === 'home' ? '/' : `/${pageSlug}`)\r\n canonical = `${global.siteUrl.replace(/\\/$/, '')}${path}`\r\n }\r\n\r\n // Build title\r\n let title = page.title || global.defaultTitle || pageSlug\r\n if (!page.noTitleTemplate && global.titleTemplate) {\r\n title = global.titleTemplate.replace('%s', title)\r\n }\r\n\r\n // Merge Open Graph\r\n const openGraph: ResolvedPageSeo['openGraph'] = {\r\n type: page.openGraph?.type || 'website',\r\n title: page.openGraph?.title || page.title || global.defaultTitle || '',\r\n description: page.openGraph?.description || page.description || global.defaultDescription || '',\r\n ...page.openGraph,\r\n }\r\n\r\n // Merge Twitter\r\n const twitter: ResolvedPageSeo['twitter'] = {\r\n card: page.twitter?.card || 'summary_large_image',\r\n ...page.twitter,\r\n }\r\n\r\n // Combine schemas (global + page)\r\n const schemas = [...(global.schemas ?? []), ...(page.schemas ?? [])]\r\n\r\n return {\r\n title,\r\n description: page.description || global.defaultDescription || '',\r\n canonical,\r\n robots: page.robots || global.robots || 'index, follow',\r\n openGraph,\r\n twitter,\r\n schemas,\r\n alternates: page.alternates ?? [],\r\n }\r\n}\r\n\r\n/**\r\n * useSEO composable for DCS-managed SEO configuration.\r\n *\r\n * @param pageSlug - Page slug matching entry in seo.yaml\r\n * @param pagePath - Optional page path for canonical URL generation\r\n * @returns SEO helpers and state\r\n */\r\nexport function useSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\r\n const seoConfig = getBuildTimeSeo()\r\n const hasBuildTimeSeo = seoConfig !== undefined\r\n\r\n // Computed resolved config\r\n const config: ComputedRef<ResolvedPageSeo> = computed(() =>\r\n resolvePageSeo(pageSlug, pagePath, seoConfig)\r\n )\r\n\r\n /**\r\n * Get JSON-LD schema objects for the page\r\n */\r\n function getSchema(): object[] {\r\n return generateJsonLd(config.value.schemas, seoConfig?.global ?? {})\r\n }\r\n\r\n /**\r\n * Get canonical URL for the page\r\n */\r\n function getCanonical(): string {\r\n return config.value.canonical\r\n }\r\n\r\n /**\r\n * Apply all meta tags via useHead\r\n */\r\n function applyHead(overrides?: HeadOverrides): void {\r\n const resolved = config.value\r\n const global = seoConfig?.global ?? {}\r\n\r\n const title = overrides?.title ?? resolved.title\r\n const description = overrides?.description ?? resolved.description\r\n\r\n // Build meta tags\r\n const meta: HeadInput['meta'] = []\r\n\r\n // Basic meta\r\n meta.push({ name: 'description', content: description })\r\n if (resolved.robots) {\r\n meta.push({ name: 'robots', content: resolved.robots })\r\n }\r\n\r\n // Verification codes\r\n if (global.verification?.google) {\r\n meta.push({ name: 'google-site-verification', content: global.verification.google })\r\n }\r\n if (global.verification?.bing) {\r\n meta.push({ name: 'msvalidate.01', content: global.verification.bing })\r\n }\r\n\r\n // Open Graph\r\n const ogMeta = generateOpenGraphMeta(\r\n resolved.openGraph,\r\n global,\r\n title,\r\n description,\r\n resolved.canonical\r\n )\r\n meta.push(...ogMeta.map((t) => ({ property: t.property, content: t.content })))\r\n\r\n // Twitter\r\n const twitterMeta = generateTwitterMeta(resolved.twitter, global, title, description)\r\n meta.push(...twitterMeta.map((t) => ({ name: t.name, content: t.content })))\r\n\r\n // Additional overrides\r\n if (overrides?.meta) {\r\n meta.push(...overrides.meta)\r\n }\r\n\r\n // Build links\r\n const link: HeadInput['link'] = []\r\n\r\n // Canonical\r\n if (resolved.canonical) {\r\n link.push({ rel: 'canonical', href: resolved.canonical })\r\n }\r\n\r\n // Alternate languages\r\n resolved.alternates.forEach((alt) => {\r\n link.push({ rel: 'alternate', href: alt.href, hreflang: alt.hreflang })\r\n })\r\n\r\n // Build scripts (JSON-LD)\r\n const schemas = overrides?.schemas ?? getSchema()\r\n const script: HeadInput['script'] = schemas.map((schema) => ({\r\n type: 'application/ld+json',\r\n children: JSON.stringify(schema),\r\n }))\r\n\r\n // Apply via useHead\r\n useHead({\r\n title,\r\n meta,\r\n link,\r\n script,\r\n })\r\n }\r\n\r\n return {\r\n config,\r\n applyHead,\r\n getSchema,\r\n getCanonical,\r\n hasBuildTimeSeo,\r\n }\r\n}\r\n\r\n/**\r\n * Create a typed useSEO function with site-specific defaults.\r\n * Useful for creating a site-wide wrapper.\r\n *\r\n * @example\r\n * ```ts\r\n * // composables/useSiteSeo.ts\r\n * import { createSiteSEO } from '@duffcloudservices/cms'\r\n *\r\n * export const useSiteSeo = createSiteSEO({\r\n * siteName: 'My Site',\r\n * siteUrl: 'https://example.com'\r\n * })\r\n * ```\r\n */\r\nexport function createSiteSEO(\r\n _siteDefaults: Partial<GlobalSeoConfig>\r\n): (pageSlug: string, pagePath?: string) => UseSeoReturn {\r\n return function siteUseSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\r\n // Note: siteDefaults would be used if we needed to override at runtime\r\n // but build-time injection handles this via dcsSeoPlugin\r\n return useSEO(pageSlug, pagePath)\r\n }\r\n}\r\n","/**\r\n * useReleaseNotes Composable\r\n *\r\n * Fetches and displays versioned release notes from the DCS Portal API.\r\n * Supports fetching specific versions or the latest release.\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup lang=\"ts\">\r\n * import { useReleaseNotes } from '@duffcloudservices/cms'\r\n * import { useRoute } from 'vue-router'\r\n *\r\n * const route = useRoute()\r\n * const version = route.params.version as string || 'latest'\r\n *\r\n * const { releaseNote, isLoading, error } = useReleaseNotes(version)\r\n * </script>\r\n *\r\n * <template>\r\n * <div v-if=\"isLoading\">Loading...</div>\r\n * <div v-else-if=\"error\">{{ error }}</div>\r\n * <article v-else-if=\"releaseNote\">\r\n * <h1>{{ releaseNote.title }}</h1>\r\n * <p>{{ releaseNote.summary }}</p>\r\n * <div v-html=\"renderedMarkdown\" />\r\n * </article>\r\n * </template>\r\n * ```\r\n */\r\n\r\nimport { ref, onMounted } from 'vue'\r\nimport type { ReleaseNote, ReleaseNotesReturn } from '../types/release-notes'\r\n\r\n/**\r\n * Get environment variable value.\r\n */\r\nfunction getEnvVar(key: string, defaultValue = ''): string {\r\n try {\r\n if (typeof import.meta !== 'undefined' && import.meta.env) {\r\n const value = (import.meta.env as Record<string, string | undefined>)[key]\r\n if (value !== undefined) return value\r\n }\r\n } catch {\r\n // import.meta not available\r\n }\r\n return defaultValue\r\n}\r\n\r\n// Simple cache for release notes\r\nconst releaseNotesCache = new Map<string, { data: ReleaseNote; expiresAt: number }>()\r\nconst CACHE_TTL = 5 * 60 * 1000 // 5 minutes\r\n\r\n/**\r\n * useReleaseNotes composable for fetching release notes from the DCS API.\r\n *\r\n * @param version - Semantic version (e.g., \"1.2.0\") or \"latest\"\r\n * @param options - Optional configuration\r\n * @returns Release notes data and state\r\n */\r\nexport function useReleaseNotes(\r\n version: string,\r\n options: { fetchOnMount?: boolean } = {}\r\n): ReleaseNotesReturn {\r\n const { fetchOnMount = true } = options\r\n\r\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\r\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\r\n\r\n const releaseNote = ref<ReleaseNote | null>(null)\r\n const isLoading = ref(false)\r\n const error = ref<string | null>(null)\r\n\r\n async function fetchReleaseNotes(): Promise<void> {\r\n if (!siteSlug) {\r\n error.value = 'VITE_SITE_SLUG environment variable not set'\r\n return\r\n }\r\n\r\n // Check cache\r\n const cacheKey = `${siteSlug}:${version}`\r\n const cached = releaseNotesCache.get(cacheKey)\r\n if (cached && cached.expiresAt > Date.now()) {\r\n releaseNote.value = cached.data\r\n return\r\n }\r\n\r\n isLoading.value = true\r\n error.value = null\r\n\r\n try {\r\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/release-notes/${version}`\r\n const response = await fetch(url, {\r\n headers: {\r\n Accept: 'application/json',\r\n },\r\n })\r\n\r\n if (!response.ok) {\r\n if (response.status === 404) {\r\n error.value = `Release notes for version ${version} not found`\r\n return\r\n }\r\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\r\n }\r\n\r\n const data = await response.json()\r\n\r\n // Normalize the response\r\n const note: ReleaseNote = {\r\n version: data.version,\r\n title: data.title,\r\n summary: data.summary || '',\r\n notesMarkdown: data.notesMarkdown || data.notes || '',\r\n changeCount: data.changeCount || 0,\r\n releaseDate: data.releaseDate || data.releasedAt || '',\r\n }\r\n\r\n // Cache the result\r\n releaseNotesCache.set(cacheKey, {\r\n data: note,\r\n expiresAt: Date.now() + CACHE_TTL,\r\n })\r\n\r\n releaseNote.value = note\r\n } catch (e) {\r\n error.value = e instanceof Error ? e.message : 'Failed to load release notes'\r\n console.error('[@duffcloudservices/cms] Failed to fetch release notes:', e)\r\n } finally {\r\n isLoading.value = false\r\n }\r\n }\r\n\r\n async function refresh(): Promise<void> {\r\n // Clear cache\r\n const cacheKey = `${siteSlug}:${version}`\r\n releaseNotesCache.delete(cacheKey)\r\n await fetchReleaseNotes()\r\n }\r\n\r\n // Fetch on mount if enabled\r\n if (fetchOnMount && typeof window !== 'undefined') {\r\n onMounted(() => {\r\n fetchReleaseNotes()\r\n })\r\n }\r\n\r\n return {\r\n releaseNote,\r\n isLoading,\r\n error,\r\n refresh,\r\n }\r\n}\r\n","/**\r\n * useSiteVersion Composable\r\n *\r\n * Gets the current site version for footer badges and version displays.\r\n * Fetches the latest release version from the DCS Portal API.\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup lang=\"ts\">\r\n * import { useSiteVersion } from '@duffcloudservices/cms'\r\n *\r\n * const { version, releaseNotesUrl } = useSiteVersion()\r\n * </script>\r\n *\r\n * <template>\r\n * <footer>\r\n * <a v-if=\"version\" :href=\"releaseNotesUrl\" class=\"version-badge\">\r\n * v{{ version }}\r\n * </a>\r\n * </footer>\r\n * </template>\r\n * ```\r\n */\r\n\r\nimport { ref, computed, onMounted } from 'vue'\r\nimport type { SiteVersionReturn } from '../types/release-notes'\r\n\r\n/**\r\n * Get environment variable value.\r\n */\r\nfunction getEnvVar(key: string, defaultValue = ''): string {\r\n try {\r\n if (typeof import.meta !== 'undefined' && import.meta.env) {\r\n const value = (import.meta.env as Record<string, string | undefined>)[key]\r\n if (value !== undefined) return value\r\n }\r\n } catch {\r\n // import.meta not available\r\n }\r\n return defaultValue\r\n}\r\n\r\n// Cache for site version\r\nlet versionCache: { version: string; expiresAt: number } | null = null\r\nconst CACHE_TTL = 10 * 60 * 1000 // 10 minutes\r\n\r\n/**\r\n * useSiteVersion composable for displaying the current site version.\r\n *\r\n * @param options - Optional configuration\r\n * @returns Site version data and computed URL\r\n */\r\nexport function useSiteVersion(options: { fetchOnMount?: boolean } = {}): SiteVersionReturn {\r\n const { fetchOnMount = true } = options\r\n\r\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\r\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\r\n\r\n const version = ref<string | null>(null)\r\n const isLoading = ref(false)\r\n\r\n const releaseNotesUrl = computed(() => {\r\n if (!version.value) return '/releaseNotes/latest'\r\n return `/releaseNotes/${version.value}`\r\n })\r\n\r\n async function fetchVersion(): Promise<void> {\r\n if (!siteSlug) {\r\n return\r\n }\r\n\r\n // Check cache\r\n if (versionCache && versionCache.expiresAt > Date.now()) {\r\n version.value = versionCache.version\r\n return\r\n }\r\n\r\n isLoading.value = true\r\n\r\n try {\r\n // Fetch the latest release notes to get the version\r\n const url = `${apiBaseUrl}/api/v1/sites/${siteSlug}/release-notes/latest`\r\n const response = await fetch(url, {\r\n headers: {\r\n Accept: 'application/json',\r\n },\r\n })\r\n\r\n if (!response.ok) {\r\n // No release notes yet - that's fine\r\n return\r\n }\r\n\r\n const data = await response.json()\r\n const latestVersion = data.version\r\n\r\n // Cache the result\r\n versionCache = {\r\n version: latestVersion,\r\n expiresAt: Date.now() + CACHE_TTL,\r\n }\r\n\r\n version.value = latestVersion\r\n } catch (e) {\r\n console.error('[@duffcloudservices/cms] Failed to fetch site version:', e)\r\n } finally {\r\n isLoading.value = false\r\n }\r\n }\r\n\r\n // Fetch on mount if enabled\r\n if (fetchOnMount && typeof window !== 'undefined') {\r\n onMounted(() => {\r\n fetchVersion()\r\n })\r\n }\r\n\r\n return {\r\n version,\r\n isLoading,\r\n releaseNotesUrl,\r\n }\r\n}\r\n","/**\r\n * useMediaCarousel Composable\r\n *\r\n * Extracts media carousel items from text content keys following the pattern:\r\n * `{prefix}.{N}.url`, `{prefix}.{N}.type`, `{prefix}.{N}.alt`\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup lang=\"ts\">\r\n * import { useTextContent, useMediaCarousel } from '@duffcloudservices/cms'\r\n *\r\n * const { t } = useTextContent({\r\n * pageSlug: 'bio-mackenzie',\r\n * defaults: {\r\n * 'hero.media-carousel.0.url': '/images/staff/mackenzie.webp',\r\n * 'hero.media-carousel.0.type': 'image',\r\n * 'hero.media-carousel.0.alt': 'Mackenzie Kowalick',\r\n * 'hero.media-carousel.1.url': '/videos/intro.mp4',\r\n * 'hero.media-carousel.1.type': 'video',\r\n * 'hero.media-carousel.1.alt': 'Introduction video',\r\n * }\r\n * })\r\n *\r\n * const { items } = useMediaCarousel({\r\n * prefix: 'hero.media-carousel',\r\n * t,\r\n * defaults: [\r\n * { url: '/images/staff/mackenzie.webp', type: 'image', alt: 'Mackenzie Kowalick' }\r\n * ]\r\n * })\r\n * </script>\r\n *\r\n * <template>\r\n * <MediaCarousel :items=\"items\" />\r\n * </template>\r\n * ```\r\n */\r\n\r\nimport { computed, type ComputedRef } from 'vue'\r\n\r\n/**\r\n * Media carousel item representing an image, video, or embed\r\n */\r\nexport interface MediaCarouselItem {\r\n /** URL to the image, video file, or embed URL */\r\n url: string\r\n /** Type of media: 'image', 'video' (direct file), 'youtube', or 'instagram' */\r\n type: 'image' | 'video' | 'youtube' | 'instagram'\r\n /** Accessibility alt text */\r\n alt?: string\r\n}\r\n\r\n/**\r\n * Configuration for useMediaCarousel composable\r\n */\r\nexport interface UseMediaCarouselConfig {\r\n /** Key prefix for carousel items (e.g., 'hero.media-carousel') */\r\n prefix: string\r\n /** The t() function from useTextContent */\r\n t: (key: string, fallback?: string) => string\r\n /** Default items to use if no content keys are found */\r\n defaults?: MediaCarouselItem[]\r\n /** Maximum number of items to look for (default: 10) */\r\n maxItems?: number\r\n}\r\n\r\n/**\r\n * Return type for useMediaCarousel composable\r\n */\r\nexport interface UseMediaCarouselReturn {\r\n /** Computed array of media carousel items */\r\n items: ComputedRef<MediaCarouselItem[]>\r\n /** Whether any items were found from content keys */\r\n hasItems: ComputedRef<boolean>\r\n /** Number of items in the carousel */\r\n count: ComputedRef<number>\r\n}\r\n\r\n/**\r\n * Extract media carousel items from text content keys.\r\n *\r\n * Looks for keys in the format:\r\n * - `{prefix}.{N}.url` - Required URL for the media\r\n * - `{prefix}.{N}.type` - Type: 'image' or 'video' (defaults to 'image')\r\n * - `{prefix}.{N}.alt` - Alt text for accessibility\r\n *\r\n * Items are sorted by index (0, 1, etc.) and only included if they have a valid URL.\r\n *\r\n * @param config - Configuration object\r\n * @returns Media carousel helpers and state\r\n */\r\nexport function useMediaCarousel(config: UseMediaCarouselConfig): UseMediaCarouselReturn {\r\n const {\r\n prefix,\r\n t,\r\n defaults = [],\r\n maxItems = 10,\r\n } = config\r\n\r\n const items = computed<MediaCarouselItem[]>(() => {\r\n const result: MediaCarouselItem[] = []\r\n\r\n // Look for items from 0 to maxItems\r\n for (let i = 0; i < maxItems; i++) {\r\n const urlKey = `${prefix}.${i}.url`\r\n const typeKey = `${prefix}.${i}.type`\r\n const altKey = `${prefix}.${i}.alt`\r\n\r\n // Use a sentinel value to detect if the key exists\r\n const url = t(urlKey, '')\r\n \r\n // Skip if no URL (key doesn't exist or is empty)\r\n if (!url || url === urlKey) {\r\n continue\r\n }\r\n\r\n const typeValue = t(typeKey, 'image')\r\n // Parse type value - support image, video, youtube, instagram\r\n let type: 'image' | 'video' | 'youtube' | 'instagram' = 'image'\r\n if (typeValue === 'video') type = 'video'\r\n else if (typeValue === 'youtube') type = 'youtube'\r\n else if (typeValue === 'instagram') type = 'instagram'\r\n const alt = t(altKey, '')\r\n\r\n result.push({\r\n url,\r\n type,\r\n alt: alt && alt !== altKey ? alt : undefined,\r\n })\r\n }\r\n\r\n // If no items found from content keys, use defaults\r\n if (result.length === 0) {\r\n return defaults\r\n }\r\n\r\n return result\r\n })\r\n\r\n const hasItems = computed(() => items.value.length > 0)\r\n const count = computed(() => items.value.length)\r\n\r\n return {\r\n items,\r\n hasItems,\r\n count,\r\n }\r\n}\r\n"]}
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -96,4 +96,49 @@ interface DcsSeoPluginOptions {
|
|
|
96
96
|
*/
|
|
97
97
|
declare function dcsSeoPlugin(options?: DcsSeoPluginOptions): Plugin;
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
/**
|
|
100
|
+
* DCS Editor Plugin for Vite
|
|
101
|
+
*
|
|
102
|
+
* Injects the editor bridge script into customer sites when running
|
|
103
|
+
* in portal preview mode (inside the visual editor iframe).
|
|
104
|
+
*
|
|
105
|
+
* The bridge enables:
|
|
106
|
+
* - Inline text editing via contenteditable
|
|
107
|
+
* - Section hover highlights and AI ✨ buttons
|
|
108
|
+
* - postMessage communication with the portal
|
|
109
|
+
*
|
|
110
|
+
* This plugin should only be active in development/preview mode.
|
|
111
|
+
* It's safe to include in production builds — it does nothing unless
|
|
112
|
+
* the page detects it's inside an iframe.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* // vite.config.ts
|
|
117
|
+
* import { dcsEditorPlugin } from '@duffcloudservices/cms/plugins'
|
|
118
|
+
*
|
|
119
|
+
* export default defineConfig({
|
|
120
|
+
* plugins: [
|
|
121
|
+
* dcsEditorPlugin()
|
|
122
|
+
* ]
|
|
123
|
+
* })
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
interface DcsEditorPluginOptions {
|
|
128
|
+
/** Enable debug logging */
|
|
129
|
+
debug?: boolean;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Vite plugin that injects the editor bridge script for portal preview integration.
|
|
133
|
+
*
|
|
134
|
+
* The bridge auto-initializes only when the page detects it's running inside an iframe,
|
|
135
|
+
* so it's safe to include in all builds — it's a no-op in standalone browsing.
|
|
136
|
+
*
|
|
137
|
+
* Uses a virtual module (`/__dcs-editor-bridge.js`) served through Vite's dev server
|
|
138
|
+
* so that bare module specifiers (like `@duffcloudservices/cms/editor`) are properly
|
|
139
|
+
* resolved through Vite's module graph rather than hitting the browser's native ESM
|
|
140
|
+
* resolver, which can't handle bare specifiers.
|
|
141
|
+
*/
|
|
142
|
+
declare function dcsEditorPlugin(options?: DcsEditorPluginOptions): Plugin;
|
|
143
|
+
|
|
144
|
+
export { type DcsContentPluginOptions, type DcsEditorPluginOptions, type DcsSeoPluginOptions, dcsContentPlugin, dcsEditorPlugin, dcsSeoPlugin };
|