@gradial/aci 0.1.27 → 0.1.29

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 (86) hide show
  1. package/README.md +47 -0
  2. package/bin/aci-darwin-arm64 +0 -0
  3. package/dist/assets/index.d.ts +1 -1
  4. package/dist/astro/index.d.ts +1 -0
  5. package/dist/astro/index.js +7 -0
  6. package/dist/collect/api.d.ts +24 -0
  7. package/dist/collect/api.js +63 -0
  8. package/dist/collect/batcher.d.ts +65 -0
  9. package/dist/collect/batcher.js +127 -0
  10. package/dist/collect/config.d.ts +30 -0
  11. package/dist/collect/config.js +53 -0
  12. package/dist/collect/consent/adapters/c15t.d.ts +2 -0
  13. package/dist/collect/consent/adapters/c15t.js +43 -0
  14. package/dist/collect/consent/adapters/cookiebot.d.ts +2 -0
  15. package/dist/collect/consent/adapters/cookiebot.js +48 -0
  16. package/dist/collect/consent/adapters/onetrust.d.ts +2 -0
  17. package/dist/collect/consent/adapters/onetrust.js +54 -0
  18. package/dist/collect/consent/adapters/trustarc.d.ts +2 -0
  19. package/dist/collect/consent/adapters/trustarc.js +65 -0
  20. package/dist/collect/consent/envelope.d.ts +49 -0
  21. package/dist/collect/consent/envelope.js +100 -0
  22. package/dist/collect/consent/mapping.d.ts +62 -0
  23. package/dist/collect/consent/mapping.js +129 -0
  24. package/dist/collect/declarative.d.ts +35 -0
  25. package/dist/collect/declarative.js +100 -0
  26. package/dist/collect/gapbuffer.d.ts +55 -0
  27. package/dist/collect/gapbuffer.js +134 -0
  28. package/dist/collect/index.d.ts +6 -0
  29. package/dist/collect/index.js +154 -0
  30. package/dist/collect/linkclicks.d.ts +12 -0
  31. package/dist/collect/linkclicks.js +35 -0
  32. package/dist/collect/pageviews.d.ts +22 -0
  33. package/dist/collect/pageviews.js +42 -0
  34. package/dist/collect/transport.d.ts +22 -0
  35. package/dist/collect/transport.js +52 -0
  36. package/dist/collect/ulid.d.ts +6 -0
  37. package/dist/collect/ulid.js +36 -0
  38. package/dist/collect/variationviews.d.ts +31 -0
  39. package/dist/collect/variationviews.js +80 -0
  40. package/dist/collect/webvitals.d.ts +38 -0
  41. package/dist/collect/webvitals.js +99 -0
  42. package/dist/content/index.d.ts +1 -0
  43. package/dist/content/index.js +1 -0
  44. package/dist/content/personalization.d.ts +57 -0
  45. package/dist/content/personalization.js +54 -0
  46. package/dist/content/types.d.ts +3 -0
  47. package/dist/index.d.ts +1 -1
  48. package/dist/next/asset-route.js +16 -3
  49. package/dist/next/collect-script.d.ts +12 -0
  50. package/dist/next/collect-script.js +15 -0
  51. package/dist/next/config.js +1 -0
  52. package/dist/next/index.d.ts +3 -0
  53. package/dist/next/index.js +3 -0
  54. package/dist/next/page.d.ts +2 -0
  55. package/dist/next/page.js +5 -2
  56. package/dist/react/VideoPlayer.d.ts +3 -3
  57. package/dist/react/index.d.ts +4 -4
  58. package/dist/react/index.js +2 -1
  59. package/dist/types/data.d.ts +2 -0
  60. package/dist/types/image.d.ts +37 -11
  61. package/dist/types/image.js +20 -17
  62. package/dist/types/media.d.ts +14 -2
  63. package/dist/types/page.d.ts +2 -0
  64. package/dist/types/video.d.ts +7 -1
  65. package/package.json +6 -1
  66. package/src/types/data.ts +3 -0
  67. package/src/types/image.ts +44 -29
  68. package/src/types/page.ts +2 -0
  69. package/templates/astro/template/package.json.tmpl +1 -1
  70. package/templates/astro/template/src/fixtures/sections.ts +2 -1
  71. package/templates/astro/template/src/layouts/SiteLayout.astro +77 -1
  72. package/templates/astro/template/src/pages/[...slug].astro +3 -2
  73. package/templates/common/.agents/AGENTS.md +5 -0
  74. package/templates/common/.agents/skills/authoring/SKILL.md +31 -0
  75. package/templates/common/.agents/skills/authoring/references/asset-rendering.md +110 -0
  76. package/templates/common/.agents/skills/authoring/references/block-slots.md +140 -0
  77. package/templates/common/.agents/skills/authoring/references/rich-text.md +75 -0
  78. package/templates/common/.agents/skills/authoring/references/sdk-server-functions.md +192 -0
  79. package/templates/common/.agents/skills/authoring/references/type-safety.md +141 -0
  80. package/templates/common/.content/config/site.json +5 -0
  81. package/templates/common/.content/schemas/site.schema.json +15 -0
  82. package/templates/common/README.md.tmpl +15 -0
  83. package/templates/nextjs/template/package.json.tmpl +3 -1
  84. package/templates/nextjs/template/src/app/_components/ConsentManager.tsx +132 -0
  85. package/templates/nextjs/template/src/app/layout.tsx +3 -1
  86. package/templates/nextjs/template/src/components/sections/HomeHero.tsx +1 -1
@@ -0,0 +1,132 @@
1
+ 'use client';
2
+
3
+ import {
4
+ ConsentBanner,
5
+ ConsentDialog,
6
+ ConsentManagerProvider,
7
+ useConsentManager,
8
+ type ConsentManagerProviderProps,
9
+ } from '@c15t/nextjs';
10
+ import { useEffect, useMemo, type ReactNode } from 'react';
11
+
12
+ // Hosted c15t backend (consent.io or self-hosted). Unset = offline mode:
13
+ // consent is stored client-side and no network calls are made.
14
+ const C15T_BACKEND_URL = process.env.NEXT_PUBLIC_C15T_BACKEND_URL;
15
+
16
+ // The CMP's own categories. The mapping from categories to the Gradial
17
+ // personalization purpose lives in .content/config/site.json
18
+ // (personalization.consentCategories), not here.
19
+ const CONSENT_CATEGORIES = ['necessary', 'measurement', 'marketing'] as const;
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Gradial collect bridge
23
+ //
24
+ // The Gradial collect SDK's c15t preset (the site config's
25
+ // `personalization.consentPreset: "c15t"`, emitted as
26
+ // `data-aci-consent="c15t"` on the collect tag) reads
27
+ // `window.c15t = { consents, onConsentChange }`. c15t itself does not
28
+ // publish that shape, so this bridge mirrors the store onto it — only after
29
+ // a real visitor decision (`consentInfo` set), never for pre-decision
30
+ // defaults, so the SDK cannot mistake an untouched banner for a withdrawal.
31
+ // ---------------------------------------------------------------------------
32
+
33
+ type Consents = Record<string, boolean>;
34
+ type ConsentListener = (payload: { consents: Consents }) => void;
35
+
36
+ declare global {
37
+ interface Window {
38
+ c15t?: {
39
+ consents?: Consents;
40
+ onConsentChange?: (cb: ConsentListener) => void;
41
+ };
42
+ }
43
+ }
44
+
45
+ const consentListeners: ConsentListener[] = [];
46
+
47
+ function publishAciConsents(consents: Consents): void {
48
+ const bridge = (window.c15t ??= {});
49
+ bridge.consents = consents;
50
+ bridge.onConsentChange ??= (cb: ConsentListener) => {
51
+ consentListeners.push(cb);
52
+ };
53
+ for (const listener of [...consentListeners]) {
54
+ listener({ consents });
55
+ }
56
+ }
57
+
58
+ function AciConsentBridge() {
59
+ const { consents, consentInfo } = useConsentManager();
60
+ useEffect(() => {
61
+ if (!consentInfo || !consents) return;
62
+ publishAciConsents(consents as Consents);
63
+ }, [consents, consentInfo]);
64
+ return null;
65
+ }
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Consent manager
69
+ // ---------------------------------------------------------------------------
70
+
71
+ function getConsentOptions(): ConsentManagerProviderProps['options'] {
72
+ const baseOptions = {
73
+ consentCategories: [...CONSENT_CATEGORIES],
74
+ legalLinks: {
75
+ privacyPolicy: {
76
+ href: '/privacy/',
77
+ target: '_self' as const,
78
+ label: 'Privacy policy',
79
+ },
80
+ },
81
+ };
82
+
83
+ if (C15T_BACKEND_URL) {
84
+ return {
85
+ ...baseOptions,
86
+ mode: 'hosted',
87
+ backendURL: C15T_BACKEND_URL,
88
+ };
89
+ }
90
+
91
+ return {
92
+ ...baseOptions,
93
+ mode: 'offline',
94
+ offlinePolicy: {
95
+ policyPacks: [
96
+ {
97
+ id: 'default-opt-in',
98
+ match: { isDefault: true },
99
+ consent: {
100
+ model: 'opt-in',
101
+ categories: [...CONSENT_CATEGORIES],
102
+ },
103
+ ui: { mode: 'banner' },
104
+ },
105
+ ],
106
+ },
107
+ };
108
+ }
109
+
110
+ export function ConsentManager({ children }: { children: ReactNode }) {
111
+ const options = useMemo(() => getConsentOptions(), []);
112
+
113
+ return (
114
+ <ConsentManagerProvider options={options}>
115
+ {children}
116
+ <AciConsentBridge />
117
+ <ConsentBanner
118
+ title="Cookie preferences"
119
+ description="We use cookies to keep the site working, measure performance, and support marketing."
120
+ acceptButtonText="Accept all"
121
+ rejectButtonText="Reject all"
122
+ customizeButtonText="Customize"
123
+ legalLinks={['privacyPolicy']}
124
+ layout={[['reject', 'accept'], 'customize']}
125
+ primaryButton="accept"
126
+ scrollLock={false}
127
+ hideBranding
128
+ />
129
+ <ConsentDialog legalLinks={['privacyPolicy']} scrollLock trapFocus hideBranding />
130
+ </ConsentManagerProvider>
131
+ );
132
+ }
@@ -1,6 +1,8 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import { Inter, Space_Grotesk } from 'next/font/google';
3
3
  import { DevRefresh } from '@gradial/aci/next';
4
+ import { ConsentManager } from '@/app/_components/ConsentManager';
5
+ import '@c15t/nextjs/styles.css';
4
6
  import '@/design-system/styles.css';
5
7
 
6
8
  const inter = Inter({
@@ -21,7 +23,7 @@ export default function Layout({ children }: { children: ReactNode }) {
21
23
  return (
22
24
  <html lang="en" className={`${inter.variable} ${spaceGrotesk.variable}`}>
23
25
  <body>
24
- {children}
26
+ <ConsentManager>{children}</ConsentManager>
25
27
  <DevRefresh />
26
28
  </body>
27
29
  </html>
@@ -57,6 +57,6 @@ export function HomeHero({ eyebrow = '', headline = '', description = '', ctaLab
57
57
  }
58
58
 
59
59
  function heroImageSrc(image?: HeroImage) {
60
- if (!image || image.sources.length === 0) return '';
60
+ if (!image) return '';
61
61
  return primaryImageSource(image).src;
62
62
  }