@duffcloudservices/cms 0.10.0 → 0.12.0

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.
Files changed (34) hide show
  1. package/README.md +59 -2
  2. package/dist/chunk-DAYLLSEE.js +3 -0
  3. package/dist/{chunk-KCWMS7P4.js.map → chunk-DAYLLSEE.js.map} +1 -1
  4. package/dist/{chunk-UPAMLKOQ.js → chunk-F3EIWEZD.js} +158 -11
  5. package/dist/chunk-F3EIWEZD.js.map +1 -0
  6. package/dist/editor/editorBridge.d.ts +41 -1
  7. package/dist/editor/editorBridge.js +68 -2
  8. package/dist/editor/editorBridge.js.map +1 -1
  9. package/dist/index.d.ts +78 -6
  10. package/dist/index.js +91 -7
  11. package/dist/index.js.map +1 -1
  12. package/dist/plugins/index.d.ts +174 -3
  13. package/dist/plugins/index.js +448 -87
  14. package/dist/plugins/index.js.map +1 -1
  15. package/dist/seo/index.d.ts +132 -226
  16. package/dist/seo/index.js +2 -2
  17. package/dist/spliceHeadHtml-CsBEucGy.d.ts +254 -0
  18. package/dist/{vitepressTransform-DeEzgGWU.d.ts → vitepressTransform-DfmABXmK.d.ts} +53 -2
  19. package/package.json +25 -14
  20. package/src/components/DcsCallButton.test.ts +126 -0
  21. package/src/components/DcsCallButton.vue +185 -0
  22. package/src/components/DcsReviewShowcase.vue +18 -3
  23. package/src/components/LiteMediaEmbed.test.ts +229 -0
  24. package/src/components/LiteMediaEmbed.vue +399 -0
  25. package/src/components/ManagedImage.test.ts +60 -0
  26. package/src/components/ManagedImage.vue +53 -6
  27. package/src/composables/useMediaCarousel.ts +6 -1
  28. package/src/composables/useResponsiveImage.ts +6 -0
  29. package/src/composables/useReviewContent.test.ts +86 -0
  30. package/src/composables/useReviewContent.ts +34 -2
  31. package/src/composables/useSiteVisitorSession.test.ts +120 -0
  32. package/src/composables/useSiteVisitorSession.ts +160 -0
  33. package/dist/chunk-KCWMS7P4.js +0 -3
  34. package/dist/chunk-UPAMLKOQ.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { Plugin, HtmlTagDescriptor } from 'vite';
2
- import { D as DcsRobotsOptions, P as PageRouteEntry, S as SeoConfiguration, C as ContentConfig } from '../vitepressTransform-DeEzgGWU.js';
3
- export { a as CreateSeoTransformPageDataOptions, R as ResolvedPageOverrides, e as SeoPageContext, f as SeoPageTypeRule, g as VitePressHeadConfig, V as VitePressPageData, b as buildVitePressSeoHead, c as createSeoTransformPageData, d as defaultRelativePathToRoute } from '../vitepressTransform-DeEzgGWU.js';
2
+ import { D as DcsRobotsOptions, Y as PageRouteEntry, H as SeoConfiguration, E as ContentConfig } from '../vitepressTransform-DfmABXmK.js';
3
+ export { C as CreateSeoTransformPageDataOptions, R as ResolvedPageOverrides, S as SeoPageContext, r as SeoPageTypeRule, t as VitePressHeadConfig, V as VitePressPageData, b as buildVitePressSeoHead, c as createSeoTransformPageData, d as defaultRelativePathToRoute } from '../vitepressTransform-DfmABXmK.js';
4
4
  import { Component, Plugin as Plugin$1 } from 'vue';
5
5
  import MarkdownIt from 'markdown-it';
6
6
 
@@ -109,6 +109,22 @@ interface DcsSeoPluginOptions {
109
109
  * site that bakes its own SEO are unaffected when this is off.
110
110
  */
111
111
  emitStaticHtml?: boolean;
112
+ /**
113
+ * Build-time BODY prerender: after the per-route `<head>` is emitted, render
114
+ * each indexable route's real DOM from the just-built SPA (headless Chromium
115
+ * via the site's `playwright`) and splice it into the mount container so non-JS
116
+ * AI crawlers see the page's body prose — not just `<div id="app"></div>`.
117
+ *
118
+ * Defaults to whatever `emitStaticHtml` is, so every SPA already emitting
119
+ * per-route HTML gains crawler-visible bodies on a cms bump with no per-site
120
+ * edit. Set `false` here to keep head-only emission. It is ALSO overridable
121
+ * per-site (no code change / no cms republish) via `.dcs/seo.yaml`
122
+ * `prerenderBody: false`. noindex/auth-gated routes are never body-prerendered;
123
+ * a route that crashes at render time fails the build loud rather than shipping
124
+ * a broken body. Requires `playwright` in the site (already the fleet default);
125
+ * absent ⇒ graceful no-op (head + JSON-LD still emitted).
126
+ */
127
+ prerenderBody?: boolean;
112
128
  /**
113
129
  * Path to the route manifest relative to project root, used only when
114
130
  * `emitStaticHtml` is true. Default `'.dcs/pages.yaml'`.
@@ -467,6 +483,89 @@ declare function dcsCdnBuildEnd(options?: DcsCdnBuildEndOptions): (siteConfig: {
467
483
 
468
484
  declare function responsiveImagePlugin(md: MarkdownIt): void;
469
485
 
486
+ /**
487
+ * Build-time responsive-variant lint for DCS customer sites.
488
+ *
489
+ * The 2026-07-03 flagship-excellence review (P7) found desktop pages shipping
490
+ * un-suffixed **full-size** CDN images (e.g. a 321.9 KB hero `47af1e8f….webp`)
491
+ * instead of a `-md`/`-lg` responsive step. That happens when an image reaches
492
+ * the output HTML through a path that does NOT run the `<img>` → `<picture>`
493
+ * transform — content injected via `__DCS_CONTENT__` and then swapped to the
494
+ * base `cdnUrl` by `dcsCdnBuildEnd`, or a hand-authored `<img src>` pointing at
495
+ * a base CDN asset URL.
496
+ *
497
+ * A DCS CDN asset URL has the shape
498
+ * `https://files.<host>/[content/]<slug>/assets/[<dir>/]<uuid>.<ext>`
499
+ * Responsive variants insert a `-sm`/`-md`/`-lg` suffix before the extension
500
+ * (`…<uuid>-md.webp`). Those suffixes contain non-hex letters, so a URL that
501
+ * still matches the *base* pattern below is, by definition, an un-suffixed
502
+ * full-size asset. This lint scans the built HTML's `<img src>` and
503
+ * `<source srcset>` attributes for such URLs and reports (or fails on) them.
504
+ *
505
+ * It is intentionally a **static-output** lint (VitePress generates HTML after
506
+ * both Vite builds finish, so a Vite plugin hook cannot see the SSG output) and
507
+ * is registered via VitePress `buildEnd`.
508
+ *
509
+ * @example
510
+ * ```ts
511
+ * // .vitepress/config.ts
512
+ * import { dcsCdnBuildEnd, dcsResponsiveImageLint, chainBuildEnd } from '@duffcloudservices/cms/plugins'
513
+ *
514
+ * export default defineConfig({
515
+ * // run the CDN rewrite first, then lint the result
516
+ * buildEnd: chainBuildEnd(dcsCdnBuildEnd(), dcsResponsiveImageLint()),
517
+ * })
518
+ * ```
519
+ */
520
+ interface ResponsiveImageViolation {
521
+ /** The un-suffixed full-size CDN URL that should have been a responsive variant. */
522
+ url: string;
523
+ /** Which element referenced it. */
524
+ tag: 'img' | 'source';
525
+ }
526
+ /** True when `url` is a base (un-suffixed, full-size) DCS CDN asset URL. */
527
+ declare function isFullSizeCdnUrl(url: string): boolean;
528
+ /**
529
+ * Scan an HTML string and return every `<img src>` / `<source srcset>` reference
530
+ * that resolves to a base (un-suffixed) full-size DCS CDN asset. `srcset`
531
+ * candidates are split on commas and only the URL portion (before the width /
532
+ * density descriptor) is tested. `onerror` and other attributes are ignored —
533
+ * only the actual `src` / `srcset` sources are inspected.
534
+ */
535
+ declare function findFullSizeCdnImages(html: string): ResponsiveImageViolation[];
536
+ interface DcsResponsiveImageLintOptions {
537
+ /**
538
+ * Throw (fail the build) when full-size images are found. Default `false`
539
+ * (warn only) so the guard can land ahead of the per-site cleanup; flip to
540
+ * `true` in CI once a site's output is clean to prevent regressions.
541
+ */
542
+ failOnViolation?: boolean;
543
+ /** File extensions to scan in the output directory. Default `['.html']`. */
544
+ extensions?: string[];
545
+ /** Enable per-file debug logging. */
546
+ debug?: boolean;
547
+ }
548
+ interface ResponsiveImageLintReport {
549
+ filesScanned: number;
550
+ violations: Array<ResponsiveImageViolation & {
551
+ file: string;
552
+ }>;
553
+ }
554
+ /**
555
+ * VitePress `buildEnd` factory that lints the generated HTML for un-suffixed
556
+ * full-size CDN images. Returns the report (also useful for tests) and either
557
+ * warns or throws depending on `failOnViolation`.
558
+ */
559
+ declare function dcsResponsiveImageLint(options?: DcsResponsiveImageLintOptions): (siteConfig: {
560
+ outDir: string;
561
+ }) => Promise<ResponsiveImageLintReport>;
562
+ /**
563
+ * Compose multiple VitePress `buildEnd` hooks into one (VitePress accepts a
564
+ * single `buildEnd`). Hooks run sequentially in the order given, so a rewrite
565
+ * hook (`dcsCdnBuildEnd`) can precede a lint (`dcsResponsiveImageLint`).
566
+ */
567
+ declare function chainBuildEnd<T>(...hooks: Array<((siteConfig: T) => unknown | Promise<unknown>) | undefined | null>): (siteConfig: T) => Promise<void>;
568
+
470
569
  /**
471
570
  * DCS motion tokens — the customer-site copy of the shared motion vocabulary.
472
571
  *
@@ -489,4 +588,76 @@ declare const DCS_MOTION_TOKENS_CSS = ":root {\n --motion-duration-stagger: 40m
489
588
  */
490
589
  declare function dcsMotionTokensStyleTag(): HtmlTagDescriptor;
491
590
 
492
- export { DCS_MOTION_TOKENS_CSS, type DcsCdnBuildEndOptions, type DcsCdnImagePluginOptions, type DcsContentPluginOptions, type DcsEditorPluginOptions, type DcsPreviewPluginOptions, type DcsSeoPluginOptions, dcsCdnBuildEnd, dcsCdnImagePlugin, dcsContentPlugin, dcsEditorPlugin, dcsMotionTokensStyleTag, dcsPreviewPlugin, dcsSeoPlugin, emitSiteFiles, emitStaticSeoHtml, responsiveImagePlugin };
591
+ /**
592
+ * DCS AI-assistant widget loader — the cms head-inject seam for Phase 3 of the
593
+ * Managed Site AI Agents plan (.docs/plans/managed-site-ai-agents/plan.md).
594
+ *
595
+ * This rides the same always-on `dcsContentPlugin.transformIndexHtml` seam that
596
+ * ships the motion tokens: every DCS site already wires `dcsContentPlugin`, so a
597
+ * cms version bump delivers this loader to the whole fleet with ZERO per-site
598
+ * source edits, and the portal per-site toggle turns the widget on/off at
599
+ * runtime with no rebuild.
600
+ *
601
+ * What gets injected is a tiny (<2 KB) inline `<script>` — NOT the widget. On
602
+ * idle it fetches the public agent config for this site; only if the agent is
603
+ * enabled (HTTP 200) does it publish `window.__DCS_AGENT_BOOTSTRAP__` and inject
604
+ * the real widget bundle from the CDN. The loader is **fail-silent**: a disabled
605
+ * agent (404), a dead backend, a blocked request, or a malformed response all
606
+ * result in no mount and no console error, so the assistant can never break a
607
+ * customer page.
608
+ *
609
+ * Build-only: the loader is injected into the production build output (the fleet
610
+ * delivery), not into `vite dev`. In local dev the config fetch would hit the
611
+ * prod API from a non-whitelisted `localhost` origin and log an uncatchable CORS
612
+ * error for every site developer — so, unlike the constant motion-token block,
613
+ * this network-touching loader stays out of the dev server.
614
+ */
615
+
616
+ /** DCS API origin the loader calls (matches the cms runtime composables). */
617
+ declare const DEFAULT_AGENT_API_BASE_URL = "https://portal.duffcloudservices.com";
618
+ /**
619
+ * Versioned CDN URL of the widget bundle. Both `cdn.` (branding CDN, CorsAllowAll)
620
+ * and the already-live `files.` route serve the same `sgdcs` blob origin with an
621
+ * identical `/agent-widget/v1/...` layout, so this is a plain classic-script
622
+ * include that needs no CORS on the bundle host. Defaults to `files.` because the
623
+ * `cdn.` Front Door route is still provisioning-gated (enableCdnDomain=false) —
624
+ * flip the default only after that domain is live, or pass cdnScriptUrl.
625
+ */
626
+ declare const DEFAULT_AGENT_WIDGET_SCRIPT_URL = "https://files.duffcloudservices.com/agent-widget/v1/dcs-agent-widget.js";
627
+ interface DcsAgentWidgetLoaderOptions {
628
+ /** Owning site slug — baked into the loader so it can build the config URL. */
629
+ siteSlug: string;
630
+ /** DCS API base (default: the shared portal origin). */
631
+ apiBaseUrl?: string;
632
+ /** Full URL of the CDN widget bundle. */
633
+ cdnScriptUrl?: string;
634
+ }
635
+ /**
636
+ * Builds the inline loader script body. Slug + URLs are HTML-safe-encoded (see
637
+ * encodeForInlineScript) so no value can break out of the string literal OR the
638
+ * `<script>` element. Kept intentionally compact to stay under the ~2 KB perf
639
+ * budget (G4).
640
+ */
641
+ declare function buildAgentWidgetLoaderScript(options: DcsAgentWidgetLoaderOptions): string;
642
+ /**
643
+ * Reads `site_slug` from the site's `.dcs/site.yaml` (the same identity file the
644
+ * announcements/provisioning path uses). Returns null when no site.yaml exists
645
+ * or it carries no slug — the caller then injects nothing (silence for non-DCS
646
+ * builds).
647
+ */
648
+ declare function readSiteSlug(projectRoot: string): string | null;
649
+ interface DcsAgentWidgetTagOptions {
650
+ /** Vite project root used to resolve the site slug. */
651
+ projectRoot: string;
652
+ /** Override the DCS API base (default: process.env.VITE_API_BASE_URL, else the shared origin). */
653
+ apiBaseUrl?: string;
654
+ /** Override the CDN bundle URL. */
655
+ cdnScriptUrl?: string;
656
+ }
657
+ /**
658
+ * The `<head>` tag descriptor for the loader, or `null` when this build is not a
659
+ * DCS-managed site (no resolvable slug) — in which case nothing is injected.
660
+ */
661
+ declare function dcsAgentWidgetLoaderTag(options: DcsAgentWidgetTagOptions): HtmlTagDescriptor | null;
662
+
663
+ export { DCS_MOTION_TOKENS_CSS, DEFAULT_AGENT_API_BASE_URL, DEFAULT_AGENT_WIDGET_SCRIPT_URL, type DcsAgentWidgetLoaderOptions, type DcsAgentWidgetTagOptions, type DcsCdnBuildEndOptions, type DcsCdnImagePluginOptions, type DcsContentPluginOptions, type DcsEditorPluginOptions, type DcsPreviewPluginOptions, type DcsResponsiveImageLintOptions, type DcsSeoPluginOptions, type ResponsiveImageLintReport, type ResponsiveImageViolation, buildAgentWidgetLoaderScript, chainBuildEnd, dcsAgentWidgetLoaderTag, dcsCdnBuildEnd, dcsCdnImagePlugin, dcsContentPlugin, dcsEditorPlugin, dcsMotionTokensStyleTag, dcsPreviewPlugin, dcsResponsiveImageLint, dcsSeoPlugin, emitSiteFiles, emitStaticSeoHtml, findFullSizeCdnImages, isFullSizeCdnUrl, readSiteSlug, responsiveImagePlugin };