bearminion 1.3.0 → 1.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # bearminion
2
2
 
3
+ ## 1.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 5d333af: Cookie banner for analytics. `site.consent` (`enabled`, `title`, `body`, `accept`, `reject`, `settings`, `policyLabel`, `policyHref`) is optional, and the banner is on by default: a site that has analytics snippets now asks visitors first, so one that did not want a banner needs `consent.enabled: false` (Site tab, Cookie banner switch). The template sets Google Consent Mode v2 to denied before any tag, keeps the snippets in `<template>` until the visitor accepts, then sends the update and runs them. A footer link, or a corner cookie icon (`reopen: "icon"`), reopens the banner, and withdrawing clears Google Analytics cookies and reloads. The Site tab has a Cookie banner group with the switch and an Edit banner dialog for the text and a privacy policy link. `siteConsentSchema`, `CONSENT_REOPEN`, `consentCopy`, `CONSENT_COPY`, `CONSENT_LIMITS`, `CONSENT_HREF` and `consentTextIssues` are exported. Themes that emit the snippets should hold them back the same way (see the theme docs).
8
+
3
9
  ## 1.3.0
4
10
 
5
11
  ### Minor Changes
package/dist/astro.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as SiteOf, P as SiteDefinition, Pt as ContentMigrator, _ as SectionRegistry, d as SectionOf, g as ContentModel, t as Page } from "./page-DJ6L8RmH.js";
2
- import { a as StoreKeyError, c as createFileStore, i as FileStoreOptions, n as EditorContext, o as StoreOperation, r as ContentStore, s as UploadStore, t as BearminionOptions } from "./options-BdPRHcqA.js";
1
+ import { F as SiteDefinition, I as SiteOf, It as ContentMigrator, _ as SectionRegistry, d as SectionOf, g as ContentModel, t as Page } from "./page-C87bm-bf.js";
2
+ import { a as StoreKeyError, c as createFileStore, i as FileStoreOptions, n as EditorContext, o as StoreOperation, r as ContentStore, s as UploadStore, t as BearminionOptions } from "./options-qFsKXOuE.js";
3
3
  import { AstroIntegration } from "astro";
4
4
  //#region src/integration.d.ts
5
5
  interface BearminionIntegration extends AstroIntegration {
package/dist/content.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { d as SectionOf, g as ContentModel, t as Page } from "./page-DJ6L8RmH.js";
1
+ import { d as SectionOf, g as ContentModel, t as Page } from "./page-C87bm-bf.js";
2
2
  import { z } from "zod";
3
3
  //#region src/content.d.ts
4
4
  export interface ContentCollectionsOptions {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as imageSchema, A as HeaderConfig, At as FieldIcons, B as defineSite, C as BRAND_LOGO_HEIGHT, Ct as buttonStyles, D as FooterColumn, Dt as tones, E as Footer, Et as toneOption, F as SiteOf, Ft as ContentVersioning, G as ButtonStyle, H as siteSeoSchema, I as SiteSeo, It as DocumentKind, J as MAX_NAV_DEPTH, K as Image, L as SiteWith, Lt as SCHEMA_VERSION_KEY, M as SiteAnalytics, Mt as ShowWhen, N as SiteConfig, Nt as ContentMigration, O as FooterConfig, Ot as variantOption, P as SiteDefinition, Pt as ContentMigrator, Q as htmlIdSchema, R as Theme, Rt as createMigrator, S as defineTheme, St as StringOption, T as DEFAULT_MAX_HEADER_ACTIONS, Tt as themeModes, U as themeColorsSchema, V as siteAnalyticsSchema, W as themeSchema, X as Text, Y as RichText, Z as Tone, _ as SectionRegistry, _t as BooleanOption, a as isSystemPage, at as trackingIdSchema, b as ThemeFont, bt as NumberOption, c as withoutOrphanSections, ct as SectionDefinition, d as SectionOf, dt as WireRect, et as linkSchema, f as anchorSchema, ft as Wireframe, g as ContentModel, gt as OptionalContent, h as API_PREFIX, ht as wireframeIssues, i as createPageSchema, it as toneSchema, j as Site, jt as ShowCondition, k as Header, kt as FieldChoices, l as BaseSection, lt as defineSection, m as sectionIdSchema, mt as wireKinds, n as SYSTEM_PAGES, nt as textMarkSchema, o as jsonLdSchema, ot as withEmphasis, p as createSectionSchema, pt as defineWireframe, q as Link, r as Seo, rt as textSchema, s as seoSchema, st as SectionConfig, t as Page, tt as richTextSchema, u as PropsOf, ut as WireKind, v as ThemeConfig, vt as CountRule, w as DEFAULT_MAX_FOOTER_COLUMNS, wt as motionOption, x as defaultTheme, xt as Option, y as ThemeDefinition, yt as EnumOption, z as brandLogoSchema } from "./page-DJ6L8RmH.js";
1
+ import { $ as Tone, A as HeaderConfig, At as variantOption, B as brandLogoSchema, Bt as createMigrator, C as BRAND_LOGO_HEIGHT, Ct as Option, D as FooterColumn, Dt as themeModes, E as Footer, Et as motionOption, F as SiteDefinition, Ft as ContentMigration, G as themeColorsSchema, H as siteAnalyticsSchema, I as SiteOf, It as ContentMigrator, J as Image, K as themeSchema, L as SiteSeo, Lt as ContentVersioning, M as SiteAnalytics, Mt as FieldIcons, N as SiteConfig, Nt as ShowCondition, O as FooterConfig, Ot as toneOption, P as SiteConsent, Pt as ShowWhen, Q as Text, R as SiteWith, Rt as DocumentKind, S as defineTheme, St as NumberOption, T as DEFAULT_MAX_HEADER_ACTIONS, Tt as buttonStyles, U as siteConsentSchema, V as defineSite, W as siteSeoSchema, X as MAX_NAV_DEPTH, Y as Link, Z as RichText, _ as SectionRegistry, _t as wireframeIssues, a as isSystemPage, at as textSchema, b as ThemeFont, bt as CountRule, c as withoutOrphanSections, ct as withEmphasis, d as SectionOf, dt as defineSection, et as htmlIdSchema, f as anchorSchema, ft as WireKind, g as ContentModel, gt as wireKinds, h as API_PREFIX, ht as defineWireframe, i as createPageSchema, it as textMarkSchema, j as Site, jt as FieldChoices, k as Header, kt as tones, l as BaseSection, lt as SectionConfig, m as sectionIdSchema, mt as Wireframe, n as SYSTEM_PAGES, nt as linkSchema, o as jsonLdSchema, ot as toneSchema, p as createSectionSchema, pt as WireRect, q as ButtonStyle, r as Seo, rt as richTextSchema, s as seoSchema, st as trackingIdSchema, t as Page, tt as imageSchema, u as PropsOf, ut as SectionDefinition, v as ThemeConfig, vt as OptionalContent, w as DEFAULT_MAX_FOOTER_COLUMNS, wt as StringOption, x as defaultTheme, xt as EnumOption, y as ThemeDefinition, yt as BooleanOption, z as Theme, zt as SCHEMA_VERSION_KEY } from "./page-C87bm-bf.js";
2
2
  import { _ as renderMarkedHtml, a as WeightMark, f as markClass, g as reconcileMarks, i as TextRun, l as fontMarks, m as marksFromEmphasis, o as applyTextMark, r as TextMark, t as FontMark, v as splitMarkedText, y as weightMarks } from "./marks-BzLa48Y9.js";
3
- import "./options-BdPRHcqA.js";
3
+ import "./options-qFsKXOuE.js";
4
4
  //#region src/errors.d.ts
5
5
  /** One schema problem, addressed by a dot path such as `sections.0.props.heading`. */
6
6
  interface ContentIssue {
@@ -182,6 +182,54 @@ export declare function formatJsonLd(blocks: readonly JsonLdBlock[] | undefined)
182
182
  /** Advice, not errors: a block without `@type` or `@context` is usually a paste mistake, but Google decides what it accepts. */
183
183
  export declare function jsonLdWarnings(blocks: readonly JsonLdBlock[]): string[];
184
184
  //#endregion
185
+ //#region src/consent.d.ts
186
+ /** Longest copy the consent banner takes, in characters. The editor shows the same limits. */
187
+ export declare const CONSENT_LIMITS: {
188
+ readonly title: 80;
189
+ readonly body: 400;
190
+ readonly button: 30;
191
+ readonly link: 40;
192
+ };
193
+ /** Where visitors reopen the banner: a link in the footer, or a small fixed icon in the bottom-left corner. */
194
+ export declare const CONSENT_REOPEN: readonly ["footer", "icon"];
195
+ type ConsentReopen = (typeof CONSENT_REOPEN)[number];
196
+ /** A policy link points to a path on this site, an anchor or an http(s) address, so it cannot run script. */
197
+ export declare const CONSENT_HREF: RegExp;
198
+ /** The texts of the banner. Each one is optional in `site.consent`; a theme falls back to {@link CONSENT_COPY}. */
199
+ interface ConsentText {
200
+ title?: string;
201
+ body?: string;
202
+ accept?: string;
203
+ reject?: string;
204
+ /** The footer link that reopens the banner, and the label of the corner icon. */
205
+ settings?: string;
206
+ policyLabel?: string;
207
+ policyHref?: string;
208
+ }
209
+ type ConsentTextKey = keyof ConsentText;
210
+ interface ConsentCopy {
211
+ title: string;
212
+ body: string;
213
+ accept: string;
214
+ reject: string;
215
+ settings: string;
216
+ policyLabel: string;
217
+ policyHref?: string;
218
+ }
219
+ /** The banner's words when `site.consent` does not set its own. The editor shows them, and themes render them. */
220
+ export declare const CONSENT_COPY: {
221
+ readonly title: "Cookies";
222
+ readonly body: "This site counts visits with analytics cookies. Nothing is loaded until you accept.";
223
+ readonly accept: "Accept";
224
+ readonly reject: "Reject";
225
+ readonly settings: "Cookie settings";
226
+ readonly policyLabel: "Privacy policy";
227
+ };
228
+ /** `site.consent` laid over the defaults. */
229
+ export declare function consentCopy(consent?: ConsentText): ConsentCopy;
230
+ /** What is wrong with a draft of the banner copy, per field. The same rules as `siteConsentSchema`, without zod. */
231
+ export declare function consentTextIssues(copy: ConsentText): Partial<Record<ConsentTextKey, string>>;
232
+ //#endregion
185
233
  //#region src/theme-colors.d.ts
186
234
  /**
187
235
  * Site palette from three seed colors.
@@ -244,4 +292,4 @@ interface ContrastCheck {
244
292
  /** Text pairs a visitor reads: text on its background, label on the accent fill, link on its background. */
245
293
  export declare function contrastChecks(colors: ThemeColors | undefined): ContrastCheck[];
246
294
  //#endregion
247
- export { API_PREFIX, type ApiIssue, type ApiProblem, BRAND_LOGO_HEIGHT, type BaseSection, type BooleanOption, type ButtonStyle, type ChangedFile, type CommitRequest, type CommitResult, type ContentIssue, type ContentMigration, type ContentMigrator, type ContentModel, type ContentVersioning, type ContrastCheck, type CountRule, type CreatePageRequest, DEFAULT_MAX_FOOTER_COLUMNS, DEFAULT_MAX_HEADER_ACTIONS, type DocumentKind, type EnumOption, type FieldChoices, type FieldIcons, type FontMark, type Footer, type FooterColumn, type FooterConfig, type GitInfo, type Header, type HeaderConfig, type HistoryEntry, type Image, type ImageFit, type ImagePosition, type JsonLdBlock, type JsonLdResult, type Link, MAX_NAV_DEPTH, type NumberOption, type Option, type OptionalContent, type Page, type PageListItem, type PropsOf, type RichText, SCHEMA_VERSION_KEY, SYSTEM_PAGES, type SaveRequest, type SaveResponse, type SectionConfig, type SectionDefinition, type SectionOf, type SectionRegistry, type Seo, type ShowCondition, type ShowWhen, type Site, type SiteAnalytics, type SiteConfig, type SiteDefinition, type SiteOf, type SitePreview, type SitePreviewHtml, type SiteSeo, type SiteWith, type StringOption, type Text, type TextMark, type TextRun, type Theme, type ThemeColors, type ThemeConfig, type ThemeDefinition, type ThemeFont, type ThemePreview, type Tone, type UploadedImage, type WeightMark, type WireKind, type WireRect, type Wireframe, anchorSchema, applyTextMark, brandLogoSchema, buttonStyles, createMigrator, createPageSchema, createSectionSchema, defaultTheme, defineSection, defineSite, defineTheme, defineWireframe, fontMarks, htmlIdSchema, imageSchema, isSystemPage, jsonLdSchema, linkSchema, markClass, marksFromEmphasis, motionOption, reconcileMarks, renderMarkedHtml, richTextSchema, sectionIdSchema, seoSchema, siteAnalyticsSchema, siteSeoSchema, splitMarkedText, textMarkSchema, textSchema, themeColorsSchema, themeModes, themeSchema, toneOption, toneSchema, tones, trackingIdSchema, variantOption, weightMarks, wireKinds, wireframeIssues, withEmphasis, withoutOrphanSections };
295
+ export { API_PREFIX, type ApiIssue, type ApiProblem, BRAND_LOGO_HEIGHT, type BaseSection, type BooleanOption, type ButtonStyle, type ChangedFile, type CommitRequest, type CommitResult, type ConsentCopy, type ConsentReopen, type ConsentText, type ConsentTextKey, type ContentIssue, type ContentMigration, type ContentMigrator, type ContentModel, type ContentVersioning, type ContrastCheck, type CountRule, type CreatePageRequest, DEFAULT_MAX_FOOTER_COLUMNS, DEFAULT_MAX_HEADER_ACTIONS, type DocumentKind, type EnumOption, type FieldChoices, type FieldIcons, type FontMark, type Footer, type FooterColumn, type FooterConfig, type GitInfo, type Header, type HeaderConfig, type HistoryEntry, type Image, type ImageFit, type ImagePosition, type JsonLdBlock, type JsonLdResult, type Link, MAX_NAV_DEPTH, type NumberOption, type Option, type OptionalContent, type Page, type PageListItem, type PropsOf, type RichText, SCHEMA_VERSION_KEY, SYSTEM_PAGES, type SaveRequest, type SaveResponse, type SectionConfig, type SectionDefinition, type SectionOf, type SectionRegistry, type Seo, type ShowCondition, type ShowWhen, type Site, type SiteAnalytics, type SiteConfig, type SiteConsent, type SiteDefinition, type SiteOf, type SitePreview, type SitePreviewHtml, type SiteSeo, type SiteWith, type StringOption, type Text, type TextMark, type TextRun, type Theme, type ThemeColors, type ThemeConfig, type ThemeDefinition, type ThemeFont, type ThemePreview, type Tone, type UploadedImage, type WeightMark, type WireKind, type WireRect, type Wireframe, anchorSchema, applyTextMark, brandLogoSchema, buttonStyles, createMigrator, createPageSchema, createSectionSchema, defaultTheme, defineSection, defineSite, defineTheme, defineWireframe, fontMarks, htmlIdSchema, imageSchema, isSystemPage, jsonLdSchema, linkSchema, markClass, marksFromEmphasis, motionOption, reconcileMarks, renderMarkedHtml, richTextSchema, sectionIdSchema, seoSchema, siteAnalyticsSchema, siteConsentSchema, siteSeoSchema, splitMarkedText, textMarkSchema, textSchema, themeColorsSchema, themeModes, themeSchema, toneOption, toneSchema, tones, trackingIdSchema, variantOption, weightMarks, wireKinds, wireframeIssues, withEmphasis, withoutOrphanSections };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { n as defineTheme, t as defaultTheme } from "./theme-DMpt3hcT.js";
3
3
  import { A as isImagePosition, C as trackingIdSchema, D as IMAGE_FITS, E as DEFAULT_IMAGE_POSITION, F as toneOption, I as tones, L as variantOption, M as buttonStyles, N as motionOption, O as IMAGE_POSITIONS, P as themeModes, S as toneSchema, T as DEFAULT_IMAGE_FIT, _ as imageSchema, a as jsonLdSchema, b as textMarkSchema, c as withoutOrphanSections, f as sectionIdSchema, g as htmlIdSchema, h as addMarkIssues, i as isSystemPage, k as isImageFit, l as anchorSchema, m as MAX_NAV_DEPTH, n as createPageSchema, o as seoSchema, p as walkLinkIds, t as SYSTEM_PAGES, u as createSectionSchema, v as linkSchema, w as withEmphasis, x as textSchema, y as richTextSchema } from "./page-ON4Jkcm-.js";
4
4
  import { applyTextMark, fontMarks, markClass, marksFromEmphasis, reconcileMarks, renderMarkedHtml, splitMarkedText, weightMarks } from "./marks.js";
5
5
  import { n as createMigrator, r as API_PREFIX, t as SCHEMA_VERSION_KEY } from "./migrate-B56wxCiC.js";
6
- import { a as contrastRatio, c as hasThemeColors, d as tokenToHex, f as formatJsonLd, i as contrastChecks, l as hexToToken, m as parseJsonLdInput, n as HEX_COLOR, o as derivePalette, p as jsonLdWarnings, s as hasOverrides, u as themeCss } from "./theme-colors-B2kAO2n6.js";
6
+ import { _ as consentCopy, a as contrastRatio, b as jsonLdWarnings, c as hasThemeColors, d as tokenToHex, f as CONSENT_COPY, g as CONSENT_REOPEN, h as CONSENT_LIMITS, i as contrastChecks, l as hexToToken, m as CONSENT_HREF_MESSAGE, n as HEX_COLOR, o as derivePalette, p as CONSENT_HREF, s as hasOverrides, u as themeCss, v as consentTextIssues, x as parseJsonLdInput, y as formatJsonLd } from "./theme-colors-zICpze42.js";
7
7
  import { z } from "zod";
8
8
  //#region src/wireframe.ts
9
9
  /**
@@ -249,6 +249,25 @@ const siteAnalyticsSchema = z.object({
249
249
  head: z.string().max(2e4).optional(),
250
250
  bodyEnd: z.string().max(2e4).optional()
251
251
  }).strict();
252
+ /**
253
+ * The cookie consent banner. A theme shows it whenever it ships analytics snippets, unless `enabled` is
254
+ * `false`, and holds the snippets back until the visitor accepts. Every text is optional: the theme has a default.
255
+ */
256
+ const siteConsentSchema = z.object({
257
+ /** Only `false` turns the banner off. Absent means on. */
258
+ enabled: z.boolean().optional(),
259
+ /** Where visitors reopen the banner. Absent means a link in the footer. */
260
+ reopen: z.enum(CONSENT_REOPEN).optional(),
261
+ title: z.string().min(1).max(CONSENT_LIMITS.title).optional(),
262
+ body: z.string().min(1).max(CONSENT_LIMITS.body).optional(),
263
+ accept: z.string().min(1).max(CONSENT_LIMITS.button).optional(),
264
+ reject: z.string().min(1).max(CONSENT_LIMITS.button).optional(),
265
+ /** The footer link that reopens the banner, and the label of the corner icon. */
266
+ settings: z.string().min(1).max(CONSENT_LIMITS.link).optional(),
267
+ policyLabel: z.string().min(1).max(CONSENT_LIMITS.link).optional(),
268
+ /** A path on this site or an http(s) address. Nothing else, so the link cannot run script. */
269
+ policyHref: z.string().min(1).max(500).regex(CONSENT_HREF, CONSENT_HREF_MESSAGE).optional()
270
+ }).strict();
252
271
  /** Height range in px. Unset means the wordmark's own height, 28. */
253
272
  const BRAND_LOGO_HEIGHT = {
254
273
  min: 16,
@@ -283,6 +302,7 @@ function createSiteSchema(header, footer) {
283
302
  brandLogo: brandLogoSchema.optional(),
284
303
  seo: siteSeoSchema,
285
304
  analytics: siteAnalyticsSchema.optional(),
305
+ consent: siteConsentSchema.optional(),
286
306
  theme: themeSchema.default({
287
307
  mode: "light",
288
308
  motion: true
@@ -419,4 +439,4 @@ function defineSite(config) {
419
439
  };
420
440
  }
421
441
  //#endregion
422
- export { API_PREFIX, BRAND_LOGO_HEIGHT, ContentError, ContentReadError, ContentValidationError, DEFAULT_IMAGE_FIT, DEFAULT_IMAGE_POSITION, DEFAULT_MAX_FOOTER_COLUMNS, DEFAULT_MAX_HEADER_ACTIONS, HEX_COLOR, IMAGE_FITS, IMAGE_POSITIONS, MAX_NAV_DEPTH, SCHEMA_VERSION_KEY, SYSTEM_PAGES, anchorSchema, applyTextMark, brandLogoSchema, buttonStyles, contrastChecks, contrastRatio, createMigrator, createPageSchema, createSectionSchema, defaultTheme, defineSection, defineSite, defineTheme, defineThemePreview, defineWireframe, derivePalette, fontMarks, formatJsonLd, hasOverrides, hasThemeColors, hexToToken, htmlIdSchema, imageSchema, isImageFit, isImagePosition, isSystemPage, jsonLdSchema, jsonLdWarnings, linkSchema, markClass, marksFromEmphasis, motionOption, parseJsonLdInput, reconcileMarks, renderMarkedHtml, richTextSchema, sectionIdSchema, seoSchema, siteAnalyticsSchema, siteSeoSchema, splitMarkedText, textMarkSchema, textSchema, themeColorsSchema, themeCss, themeModes, themeSchema, tokenToHex, toneOption, toneSchema, tones, trackingIdSchema, variantOption, weightMarks, wireKinds, wireframeIssues, withEmphasis, withoutOrphanSections };
442
+ export { API_PREFIX, BRAND_LOGO_HEIGHT, CONSENT_COPY, CONSENT_HREF, CONSENT_LIMITS, CONSENT_REOPEN, ContentError, ContentReadError, ContentValidationError, DEFAULT_IMAGE_FIT, DEFAULT_IMAGE_POSITION, DEFAULT_MAX_FOOTER_COLUMNS, DEFAULT_MAX_HEADER_ACTIONS, HEX_COLOR, IMAGE_FITS, IMAGE_POSITIONS, MAX_NAV_DEPTH, SCHEMA_VERSION_KEY, SYSTEM_PAGES, anchorSchema, applyTextMark, brandLogoSchema, buttonStyles, consentCopy, consentTextIssues, contrastChecks, contrastRatio, createMigrator, createPageSchema, createSectionSchema, defaultTheme, defineSection, defineSite, defineTheme, defineThemePreview, defineWireframe, derivePalette, fontMarks, formatJsonLd, hasOverrides, hasThemeColors, hexToToken, htmlIdSchema, imageSchema, isImageFit, isImagePosition, isSystemPage, jsonLdSchema, jsonLdWarnings, linkSchema, markClass, marksFromEmphasis, motionOption, parseJsonLdInput, reconcileMarks, renderMarkedHtml, richTextSchema, sectionIdSchema, seoSchema, siteAnalyticsSchema, siteConsentSchema, siteSeoSchema, splitMarkedText, textMarkSchema, textSchema, themeColorsSchema, themeCss, themeModes, themeSchema, tokenToHex, toneOption, toneSchema, tones, trackingIdSchema, variantOption, weightMarks, wireKinds, wireframeIssues, withEmphasis, withoutOrphanSections };
@@ -1,4 +1,4 @@
1
- import { P as SiteDefinition, Pt as ContentMigrator, _ as SectionRegistry, g as ContentModel, y as ThemeDefinition } from "./page-DJ6L8RmH.js";
1
+ import { F as SiteDefinition, It as ContentMigrator, _ as SectionRegistry, g as ContentModel, y as ThemeDefinition } from "./page-C87bm-bf.js";
2
2
  //#region src/images.d.ts
3
3
  type ImageKind = 'png' | 'jpeg' | 'webp' | 'gif' | 'avif';
4
4
  interface SavedImage {
@@ -576,6 +576,24 @@ declare const siteAnalyticsSchema: z.ZodObject<{
576
576
  head: z.ZodOptional<z.ZodString>;
577
577
  bodyEnd: z.ZodOptional<z.ZodString>;
578
578
  }, z.core.$strict>;
579
+ /**
580
+ * The cookie consent banner. A theme shows it whenever it ships analytics snippets, unless `enabled` is
581
+ * `false`, and holds the snippets back until the visitor accepts. Every text is optional: the theme has a default.
582
+ */
583
+ declare const siteConsentSchema: z.ZodObject<{
584
+ enabled: z.ZodOptional<z.ZodBoolean>;
585
+ reopen: z.ZodOptional<z.ZodEnum<{
586
+ footer: "footer";
587
+ icon: "icon";
588
+ }>>;
589
+ title: z.ZodOptional<z.ZodString>;
590
+ body: z.ZodOptional<z.ZodString>;
591
+ accept: z.ZodOptional<z.ZodString>;
592
+ reject: z.ZodOptional<z.ZodString>;
593
+ settings: z.ZodOptional<z.ZodString>;
594
+ policyLabel: z.ZodOptional<z.ZodString>;
595
+ policyHref: z.ZodOptional<z.ZodString>;
596
+ }, z.core.$strict>;
579
597
  /** Height range in px. Unset means the wordmark's own height, 28. */
580
598
  declare const BRAND_LOGO_HEIGHT: {
581
599
  readonly min: 16;
@@ -664,6 +682,20 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
664
682
  head: z.ZodOptional<z.ZodString>;
665
683
  bodyEnd: z.ZodOptional<z.ZodString>;
666
684
  }, z.core.$strict>>;
685
+ consent: z.ZodOptional<z.ZodObject<{
686
+ enabled: z.ZodOptional<z.ZodBoolean>;
687
+ reopen: z.ZodOptional<z.ZodEnum<{
688
+ footer: "footer";
689
+ icon: "icon";
690
+ }>>;
691
+ title: z.ZodOptional<z.ZodString>;
692
+ body: z.ZodOptional<z.ZodString>;
693
+ accept: z.ZodOptional<z.ZodString>;
694
+ reject: z.ZodOptional<z.ZodString>;
695
+ settings: z.ZodOptional<z.ZodString>;
696
+ policyLabel: z.ZodOptional<z.ZodString>;
697
+ policyHref: z.ZodOptional<z.ZodString>;
698
+ }, z.core.$strict>>;
667
699
  theme: z.ZodDefault<z.ZodObject<{
668
700
  mode: z.ZodPreprocess<z.ZodDefault<z.ZodEnum<{
669
701
  light: "light";
@@ -941,6 +973,17 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
941
973
  head?: string | undefined;
942
974
  bodyEnd?: string | undefined;
943
975
  } | undefined;
976
+ consent?: {
977
+ enabled?: boolean | undefined;
978
+ reopen?: "footer" | "icon" | undefined;
979
+ title?: string | undefined;
980
+ body?: string | undefined;
981
+ accept?: string | undefined;
982
+ reject?: string | undefined;
983
+ settings?: string | undefined;
984
+ policyLabel?: string | undefined;
985
+ policyHref?: string | undefined;
986
+ } | undefined;
944
987
  }, {
945
988
  name: string;
946
989
  url: string;
@@ -1087,11 +1130,23 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
1087
1130
  head?: string | undefined;
1088
1131
  bodyEnd?: string | undefined;
1089
1132
  } | undefined;
1133
+ consent?: {
1134
+ enabled?: boolean | undefined;
1135
+ reopen?: "footer" | "icon" | undefined;
1136
+ title?: string | undefined;
1137
+ body?: string | undefined;
1138
+ accept?: string | undefined;
1139
+ reject?: string | undefined;
1140
+ settings?: string | undefined;
1141
+ policyLabel?: string | undefined;
1142
+ policyHref?: string | undefined;
1143
+ } | undefined;
1090
1144
  }>>;
1091
1145
  type Theme = z.infer<typeof themeSchema>;
1092
1146
  type FooterColumn = z.infer<typeof footerColumnSchema>;
1093
1147
  type SiteSeo = z.infer<typeof siteSeoSchema>;
1094
1148
  type SiteAnalytics = z.infer<typeof siteAnalyticsSchema>;
1149
+ type SiteConsent = z.infer<typeof siteConsentSchema>;
1095
1150
  type AnySite = z.output<ReturnType<typeof createSiteSchema>>;
1096
1151
  /** A site with header and footer variants as plain strings. {@link SiteWith} narrows them. */
1097
1152
  type Header = AnySite['header'];
@@ -1281,4 +1336,4 @@ declare function withoutOrphanSections<TPage extends {
1281
1336
  sections: readonly unknown[];
1282
1337
  }>(page: TPage, registry: SectionRegistry): TPage;
1283
1338
  //#endregion
1284
- export { imageSchema as $, HeaderConfig as A, FieldIcons as At, defineSite as B, BRAND_LOGO_HEIGHT as C, buttonStyles as Ct, FooterColumn as D, tones as Dt, Footer as E, toneOption as Et, SiteOf as F, ContentVersioning as Ft, ButtonStyle as G, siteSeoSchema as H, SiteSeo as I, DocumentKind as It, MAX_NAV_DEPTH as J, Image as K, SiteWith as L, SCHEMA_VERSION_KEY as Lt, SiteAnalytics as M, ShowWhen as Mt, SiteConfig as N, ContentMigration as Nt, FooterConfig as O, variantOption as Ot, SiteDefinition as P, ContentMigrator as Pt, htmlIdSchema as Q, Theme as R, createMigrator as Rt, defineTheme as S, StringOption as St, DEFAULT_MAX_HEADER_ACTIONS as T, themeModes as Tt, themeColorsSchema as U, siteAnalyticsSchema as V, themeSchema as W, Text as X, RichText as Y, Tone as Z, SectionRegistry as _, BooleanOption as _t, isSystemPage as a, trackingIdSchema as at, ThemeFont as b, NumberOption as bt, withoutOrphanSections as c, SectionDefinition as ct, SectionOf as d, WireRect as dt, linkSchema as et, anchorSchema as f, Wireframe as ft, ContentModel as g, OptionalContent as gt, API_PREFIX as h, wireframeIssues as ht, createPageSchema as i, toneSchema as it, Site as j, ShowCondition as jt, Header as k, FieldChoices as kt, BaseSection as l, defineSection as lt, sectionIdSchema as m, wireKinds as mt, SYSTEM_PAGES as n, textMarkSchema as nt, jsonLdSchema as o, withEmphasis as ot, createSectionSchema as p, defineWireframe as pt, Link as q, Seo as r, textSchema as rt, seoSchema as s, SectionConfig as st, Page as t, richTextSchema as tt, PropsOf as u, WireKind as ut, ThemeConfig as v, CountRule as vt, DEFAULT_MAX_FOOTER_COLUMNS as w, motionOption as wt, defaultTheme as x, Option as xt, ThemeDefinition as y, EnumOption as yt, brandLogoSchema as z };
1339
+ export { Tone as $, HeaderConfig as A, variantOption as At, brandLogoSchema as B, createMigrator as Bt, BRAND_LOGO_HEIGHT as C, Option as Ct, FooterColumn as D, themeModes as Dt, Footer as E, motionOption as Et, SiteDefinition as F, ContentMigration as Ft, themeColorsSchema as G, siteAnalyticsSchema as H, SiteOf as I, ContentMigrator as It, Image as J, themeSchema as K, SiteSeo as L, ContentVersioning as Lt, SiteAnalytics as M, FieldIcons as Mt, SiteConfig as N, ShowCondition as Nt, FooterConfig as O, toneOption as Ot, SiteConsent as P, ShowWhen as Pt, Text as Q, SiteWith as R, DocumentKind as Rt, defineTheme as S, NumberOption as St, DEFAULT_MAX_HEADER_ACTIONS as T, buttonStyles as Tt, siteConsentSchema as U, defineSite as V, siteSeoSchema as W, MAX_NAV_DEPTH as X, Link as Y, RichText as Z, SectionRegistry as _, wireframeIssues as _t, isSystemPage as a, textSchema as at, ThemeFont as b, CountRule as bt, withoutOrphanSections as c, withEmphasis as ct, SectionOf as d, defineSection as dt, htmlIdSchema as et, anchorSchema as f, WireKind as ft, ContentModel as g, wireKinds as gt, API_PREFIX as h, defineWireframe as ht, createPageSchema as i, textMarkSchema as it, Site as j, FieldChoices as jt, Header as k, tones as kt, BaseSection as l, SectionConfig as lt, sectionIdSchema as m, Wireframe as mt, SYSTEM_PAGES as n, linkSchema as nt, jsonLdSchema as o, toneSchema as ot, createSectionSchema as p, WireRect as pt, ButtonStyle as q, Seo as r, richTextSchema as rt, seoSchema as s, trackingIdSchema as st, Page as t, imageSchema as tt, PropsOf as u, SectionDefinition as ut, ThemeConfig as v, OptionalContent as vt, DEFAULT_MAX_FOOTER_COLUMNS as w, StringOption as wt, defaultTheme as x, EnumOption as xt, ThemeDefinition as y, BooleanOption as yt, Theme as z, SCHEMA_VERSION_KEY as zt };
@@ -53,6 +53,62 @@ function isBlock(value) {
53
53
  return typeof value === "object" && value !== null && !Array.isArray(value);
54
54
  }
55
55
  //#endregion
56
+ //#region src/consent.ts
57
+ /** Longest copy the consent banner takes, in characters. The editor shows the same limits. */
58
+ const CONSENT_LIMITS = {
59
+ title: 80,
60
+ body: 400,
61
+ button: 30,
62
+ link: 40
63
+ };
64
+ /** Where visitors reopen the banner: a link in the footer, or a small fixed icon in the bottom-left corner. */
65
+ const CONSENT_REOPEN = ["footer", "icon"];
66
+ /** A policy link points to a path on this site, an anchor or an http(s) address, so it cannot run script. */
67
+ const CONSENT_HREF = /^(\/|#|https?:\/\/)/;
68
+ const CONSENT_HREF_MESSAGE = "Use a path on this site or an http(s) address";
69
+ /** The banner's words when `site.consent` does not set its own. The editor shows them, and themes render them. */
70
+ const CONSENT_COPY = {
71
+ title: "Cookies",
72
+ body: "This site counts visits with analytics cookies. Nothing is loaded until you accept.",
73
+ accept: "Accept",
74
+ reject: "Reject",
75
+ settings: "Cookie settings",
76
+ policyLabel: "Privacy policy"
77
+ };
78
+ /** `site.consent` laid over the defaults. */
79
+ function consentCopy(consent) {
80
+ return {
81
+ title: consent?.title ?? CONSENT_COPY.title,
82
+ body: consent?.body ?? CONSENT_COPY.body,
83
+ accept: consent?.accept ?? CONSENT_COPY.accept,
84
+ reject: consent?.reject ?? CONSENT_COPY.reject,
85
+ settings: consent?.settings ?? CONSENT_COPY.settings,
86
+ policyLabel: consent?.policyLabel ?? CONSENT_COPY.policyLabel,
87
+ policyHref: consent?.policyHref
88
+ };
89
+ }
90
+ const LIMIT_OF = {
91
+ title: CONSENT_LIMITS.title,
92
+ body: CONSENT_LIMITS.body,
93
+ accept: CONSENT_LIMITS.button,
94
+ reject: CONSENT_LIMITS.button,
95
+ settings: CONSENT_LIMITS.link,
96
+ policyLabel: CONSENT_LIMITS.link
97
+ };
98
+ /** What is wrong with a draft of the banner copy, per field. The same rules as `siteConsentSchema`, without zod. */
99
+ function consentTextIssues(copy) {
100
+ const issues = {};
101
+ for (const key of Object.keys(LIMIT_OF)) {
102
+ const value = copy[key];
103
+ if (value === void 0) continue;
104
+ if (value.trim() === "") issues[key] = "Cannot be empty";
105
+ else if (value.length > LIMIT_OF[key]) issues[key] = `At most ${LIMIT_OF[key]} characters`;
106
+ }
107
+ const href = copy.policyHref;
108
+ if (href !== void 0 && (href.length > 500 || !CONSENT_HREF.test(href))) issues.policyHref = CONSENT_HREF_MESSAGE;
109
+ return issues;
110
+ }
111
+ //#endregion
56
112
  //#region src/theme-colors.ts
57
113
  /**
58
114
  * Site palette from three seed colors.
@@ -477,4 +533,4 @@ function contrastChecks(colors) {
477
533
  return checks;
478
534
  }
479
535
  //#endregion
480
- export { contrastRatio as a, hasThemeColors as c, tokenToHex as d, formatJsonLd as f, contrastChecks as i, hexToToken as l, parseJsonLdInput as m, HEX_COLOR as n, derivePalette as o, jsonLdWarnings as p, OVERRIDE_TOKENS as r, hasOverrides as s, DEFAULT_BRAND as t, themeCss as u };
536
+ export { consentCopy as _, contrastRatio as a, jsonLdWarnings as b, hasThemeColors as c, tokenToHex as d, CONSENT_COPY as f, CONSENT_REOPEN as g, CONSENT_LIMITS as h, contrastChecks as i, hexToToken as l, CONSENT_HREF_MESSAGE as m, HEX_COLOR as n, derivePalette as o, CONSENT_HREF as p, OVERRIDE_TOKENS as r, hasOverrides as s, DEFAULT_BRAND as t, themeCss as u, consentTextIssues as v, parseJsonLdInput as x, formatJsonLd as y };