bearminion 1.2.1 → 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,22 @@
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
+
9
+ ## 1.3.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 4de8434: Custom JSON-LD and analytics snippets. A page's `seo.jsonLd` holds parsed JSON-LD blocks that replace the generated `WebPage` markup, and `site.analytics` (`head`, `bodyEnd`) holds raw snippets the template emits on the live site only: not in `astro dev`, and not in Vercel or Netlify preview deployments. Nothing needs configuring; `BEARMINION_ANALYTICS=on|off` overrides the choice for hosts that cannot be told apart. The Pages tab shows an SEO card (search result preview and badges) with an Edit SEO button that opens a dialog for every SEO field, including JSON-LD with validation and script-tag unwrapping. The Site tab has an Analytics & scripts group. `parseJsonLdInput`, `formatJsonLd`, `jsonLdWarnings`, `jsonLdSchema` and `siteAnalyticsSchema` are exported. Themes need to render the two new fields (see the theme docs).
14
+
15
+ ### Patch Changes
16
+
17
+ - 826b0ac: `create-bearminion` speaks in the BearMinion voice: the prompts, progress lines, errors, next steps and `--help` text. The messages say the same things, with fewer words and a grumpier bear.
18
+ - 2032a4a: Template: footer columns stay left-aligned on small screens instead of piling up on the right. They stack in one column on phones, two columns on larger phones, three columns on tablets, and sit beside the brand block from wide layouts up.
19
+
3
20
  ## 1.2.1
4
21
 
5
22
  ### Patch Changes
package/dist/astro.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as SiteDefinition, N as SiteOf, g as SectionRegistry, h as ContentModel, jt as ContentMigrator, t as Page, u as SectionOf } from "./page-BjDiVHTA.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-CDLOxcb4.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/astro.js CHANGED
@@ -1,8 +1,8 @@
1
- import { a as imageFields, c as ContentReadError, l as ContentValidationError, r as loadThemePreview, t as PreviewUnavailableError } from "./preview-BqrZLvQ6.js";
2
- import { t as defaultTheme } from "./theme-q19C8cIc.js";
3
- import { i as isSystemPage, n as createPageSchema$1, r as createTolerantPageSchema, s as withoutOrphanSections, u as isOrphanSection } from "./page-C0l3CIuz.js";
1
+ import { a as imageFields, c as ContentReadError, l as ContentValidationError, r as loadThemePreview, t as PreviewUnavailableError } from "./preview-CTVEBdEY.js";
2
+ import { t as defaultTheme } from "./theme-DMpt3hcT.js";
3
+ import { c as withoutOrphanSections, d as isOrphanSection, i as isSystemPage, n as createPageSchema$1, r as createTolerantPageSchema } from "./page-ON4Jkcm-.js";
4
4
  import { a as DEFAULT_UPLOADS_DIR, i as DEFAULT_CONTENT_DIR, n as createMigrator, r as API_PREFIX, t as SCHEMA_VERSION_KEY } from "./migrate-B56wxCiC.js";
5
- import { a as JSON_BODY_LIMIT, c as isInside, d as resolveDir, f as resolveInside, i as IMAGE_BYTE_LIMIT, l as isLocalRequest, n as contentReloadPlugin, o as MULTIPART_BODY_LIMIT, r as dropEarlyReloadAfterSave, s as asDirectory, u as readSlug } from "./reload-COk8Nncw.js";
5
+ import { a as JSON_BODY_LIMIT, c as isInside, d as resolveDir, f as resolveInside, i as IMAGE_BYTE_LIMIT, l as isLocalRequest, n as contentReloadPlugin, o as MULTIPART_BODY_LIMIT, r as dropEarlyReloadAfterSave, s as asDirectory, u as readSlug } from "./reload-Cq434rtI.js";
6
6
  import { S as isAssetPath, b as describeSiteRef, c as projectId, v as assetRefs, y as describePageRef } from "./draft-store-1Y4-sn45.js";
7
7
  import { z } from "zod";
8
8
  import fs from "node:fs";
package/dist/content.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { h as ContentModel, t as Page, u as SectionOf } from "./page-BjDiVHTA.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/content.js CHANGED
@@ -1,6 +1,6 @@
1
- import { n as createPageSchema, r as createTolerantPageSchema } from "./page-C0l3CIuz.js";
1
+ import { n as createPageSchema, r as createTolerantPageSchema } from "./page-ON4Jkcm-.js";
2
2
  import { n as createMigrator } from "./migrate-B56wxCiC.js";
3
- import { t as CONTENT_COLLECTIONS_MARKER } from "./reload-COk8Nncw.js";
3
+ import { t as CONTENT_COLLECTIONS_MARKER } from "./reload-Cq434rtI.js";
4
4
  import { z } from "zod";
5
5
  import { pathToFileURL } from "node:url";
6
6
  import path from "node:path";
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as textMarkSchema, A as Site, At as ContentMigration, B as themeColorsSchema, C as DEFAULT_MAX_FOOTER_COLUMNS, Ct as toneOption, D as FooterConfig, Dt as FieldIcons, E as FooterColumn, Et as FieldChoices, F as SiteWith, Ft as createMigrator, G as MAX_NAV_DEPTH, H as ButtonStyle, I as Theme, J as Tone, K as RichText, L as brandLogoSchema, M as SiteDefinition, Mt as ContentVersioning, N as SiteOf, Nt as DocumentKind, O as Header, Ot as ShowCondition, P as SiteSeo, Pt as SCHEMA_VERSION_KEY, Q as richTextSchema, R as defineSite, S as BRAND_LOGO_HEIGHT, St as themeModes, T as Footer, Tt as variantOption, U as Image, V as themeSchema, W as Link, X as imageSchema, Y as htmlIdSchema, Z as linkSchema, _ as ThemeConfig, _t as NumberOption, a as isSystemPage, at as SectionDefinition, b as defaultTheme, bt as buttonStyles, c as BaseSection, ct as WireRect, d as anchorSchema, dt as wireKinds, et as textSchema, f as createSectionSchema, ft as wireframeIssues, g as SectionRegistry, gt as EnumOption, h as ContentModel, ht as CountRule, i as createPageSchema, it as SectionConfig, j as SiteConfig, jt as ContentMigrator, k as HeaderConfig, kt as ShowWhen, l as PropsOf, lt as Wireframe, m as API_PREFIX, mt as BooleanOption, n as SYSTEM_PAGES, nt as trackingIdSchema, o as seoSchema, ot as defineSection, p as sectionIdSchema, pt as OptionalContent, q as Text, r as Seo, rt as withEmphasis, s as withoutOrphanSections, st as WireKind, t as Page, tt as toneSchema, u as SectionOf, ut as defineWireframe, v as ThemeDefinition, vt as Option, w as DEFAULT_MAX_HEADER_ACTIONS, wt as tones, x as defineTheme, xt as motionOption, y as ThemeFont, yt as StringOption, z as siteSeoSchema } from "./page-BjDiVHTA.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-CDLOxcb4.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 {
@@ -162,6 +162,74 @@ interface SitePreview {
162
162
  footer: string;
163
163
  }
164
164
  //#endregion
165
+ //#region src/json-ld.d.ts
166
+ /** JSON-LD blocks as a page stores them: parsed, so diffs stay readable and output can be escaped safely. */
167
+ type JsonLdBlock = Record<string, unknown>;
168
+ type JsonLdResult = {
169
+ ok: true;
170
+ value: JsonLdBlock[];
171
+ } | {
172
+ ok: false;
173
+ error: string;
174
+ };
175
+ /**
176
+ * Parses what a person pastes into the JSON-LD box: an object, an array of objects, or either one inside a
177
+ * `<script type="application/ld+json">` tag. Empty input is valid and means "no custom JSON-LD".
178
+ */
179
+ export declare function parseJsonLdInput(raw: string): JsonLdResult;
180
+ /** The text shown in the editor box: one block as an object, several as an array, none as empty. */
181
+ export declare function formatJsonLd(blocks: readonly JsonLdBlock[] | undefined): string;
182
+ /** Advice, not errors: a block without `@type` or `@context` is usually a paste mistake, but Google decides what it accepts. */
183
+ export declare function jsonLdWarnings(blocks: readonly JsonLdBlock[]): string[];
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
165
233
  //#region src/theme-colors.d.ts
166
234
  /**
167
235
  * Site palette from three seed colors.
@@ -224,4 +292,4 @@ interface ContrastCheck {
224
292
  /** Text pairs a visitor reads: text on its background, label on the accent fill, link on its background. */
225
293
  export declare function contrastChecks(colors: ThemeColors | undefined): ContrastCheck[];
226
294
  //#endregion
227
- 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 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 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, linkSchema, markClass, marksFromEmphasis, motionOption, reconcileMarks, renderMarkedHtml, richTextSchema, sectionIdSchema, seoSchema, 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
@@ -1,9 +1,9 @@
1
- import { c as ContentReadError, i as enumFields, l as ContentValidationError, n as defineThemePreview, o as schemaProps, s as ContentError, u as formatContentIssues } from "./preview-BqrZLvQ6.js";
2
- import { n as defineTheme, t as defaultTheme } from "./theme-q19C8cIc.js";
3
- import { C as withEmphasis, D as IMAGE_POSITIONS, E as IMAGE_FITS, F as tones, I as variantOption, M as motionOption, N as themeModes, O as isImageFit, P as toneOption, S as trackingIdSchema, T as DEFAULT_IMAGE_POSITION, _ as linkSchema, a as seoSchema, b as textSchema, c as anchorSchema, d as sectionIdSchema, f as walkLinkIds, g as imageSchema, h as htmlIdSchema, i as isSystemPage, j as buttonStyles, k as isImagePosition, l as createSectionSchema, m as addMarkIssues, n as createPageSchema, p as MAX_NAV_DEPTH, s as withoutOrphanSections, t as SYSTEM_PAGES, v as richTextSchema, w as DEFAULT_IMAGE_FIT, x as toneSchema, y as textMarkSchema } from "./page-C0l3CIuz.js";
1
+ import { c as ContentReadError, i as enumFields, l as ContentValidationError, n as defineThemePreview, o as schemaProps, s as ContentError, u as formatContentIssues } from "./preview-CTVEBdEY.js";
2
+ import { n as defineTheme, t as defaultTheme } from "./theme-DMpt3hcT.js";
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, i as contrastChecks, l as hexToToken, n as HEX_COLOR, o as derivePalette, s as hasOverrides, u as themeCss } from "./theme-colors-BneXpLYG.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
  /**
@@ -241,6 +241,33 @@ const siteSeoSchema = z.object({
241
241
  description: z.string().optional(),
242
242
  ogImage: z.string().min(1).optional()
243
243
  }).strict();
244
+ /**
245
+ * Raw HTML snippets for analytics and similar tags. A theme renders `head` at the end of `<head>` and
246
+ * `bodyEnd` before `</body>`, in production builds only.
247
+ */
248
+ const siteAnalyticsSchema = z.object({
249
+ head: z.string().max(2e4).optional(),
250
+ bodyEnd: z.string().max(2e4).optional()
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();
244
271
  /** Height range in px. Unset means the wordmark's own height, 28. */
245
272
  const BRAND_LOGO_HEIGHT = {
246
273
  min: 16,
@@ -274,6 +301,8 @@ function createSiteSchema(header, footer) {
274
301
  /** Image shown in the header and footer in place of the name. */
275
302
  brandLogo: brandLogoSchema.optional(),
276
303
  seo: siteSeoSchema,
304
+ analytics: siteAnalyticsSchema.optional(),
305
+ consent: siteConsentSchema.optional(),
277
306
  theme: themeSchema.default({
278
307
  mode: "light",
279
308
  motion: true
@@ -410,4 +439,4 @@ function defineSite(config) {
410
439
  };
411
440
  }
412
441
  //#endregion
413
- 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, hasOverrides, hasThemeColors, hexToToken, htmlIdSchema, imageSchema, isImageFit, isImagePosition, isSystemPage, linkSchema, markClass, marksFromEmphasis, motionOption, reconcileMarks, renderMarkedHtml, richTextSchema, sectionIdSchema, seoSchema, 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 { M as SiteDefinition, g as SectionRegistry, h as ContentModel, jt as ContentMigrator, v as ThemeDefinition } from "./page-BjDiVHTA.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 {
@@ -271,10 +271,10 @@ declare const textMarkSchema: z.ZodObject<{
271
271
  start: z.ZodNumber;
272
272
  end: z.ZodNumber;
273
273
  weight: z.ZodOptional<z.ZodEnum<{
274
+ bold: "bold";
274
275
  regular: "regular";
275
276
  medium: "medium";
276
277
  semibold: "semibold";
277
- bold: "bold";
278
278
  }>>;
279
279
  italic: z.ZodOptional<z.ZodBoolean>;
280
280
  font: z.ZodOptional<z.ZodEnum<{
@@ -315,10 +315,10 @@ declare function withEmphasis<T extends z.ZodRawShape>(shape: T): z.ZodPipe<z.Zo
315
315
  start: z.ZodNumber;
316
316
  end: z.ZodNumber;
317
317
  weight: z.ZodOptional<z.ZodEnum<{
318
+ bold: "bold";
318
319
  regular: "regular";
319
320
  medium: "medium";
320
321
  semibold: "semibold";
321
- bold: "bold";
322
322
  }>>;
323
323
  italic: z.ZodOptional<z.ZodBoolean>;
324
324
  font: z.ZodOptional<z.ZodEnum<{
@@ -333,10 +333,10 @@ declare function withEmphasis<T extends z.ZodRawShape>(shape: T): z.ZodPipe<z.Zo
333
333
  start: z.ZodNumber;
334
334
  end: z.ZodNumber;
335
335
  weight: z.ZodOptional<z.ZodEnum<{
336
+ bold: "bold";
336
337
  regular: "regular";
337
338
  medium: "medium";
338
339
  semibold: "semibold";
339
- bold: "bold";
340
340
  }>>;
341
341
  italic: z.ZodOptional<z.ZodBoolean>;
342
342
  font: z.ZodOptional<z.ZodEnum<{
@@ -353,10 +353,10 @@ declare function withEmphasis<T extends z.ZodRawShape>(shape: T): z.ZodPipe<z.Zo
353
353
  start: z.ZodNumber;
354
354
  end: z.ZodNumber;
355
355
  weight: z.ZodOptional<z.ZodEnum<{
356
+ bold: "bold";
356
357
  regular: "regular";
357
358
  medium: "medium";
358
359
  semibold: "semibold";
359
- bold: "bold";
360
360
  }>>;
361
361
  italic: z.ZodOptional<z.ZodBoolean>;
362
362
  font: z.ZodOptional<z.ZodEnum<{
@@ -568,6 +568,32 @@ declare const siteSeoSchema: z.ZodObject<{
568
568
  description: z.ZodOptional<z.ZodString>;
569
569
  ogImage: z.ZodOptional<z.ZodString>;
570
570
  }, z.core.$strict>;
571
+ /**
572
+ * Raw HTML snippets for analytics and similar tags. A theme renders `head` at the end of `<head>` and
573
+ * `bodyEnd` before `</body>`, in production builds only.
574
+ */
575
+ declare const siteAnalyticsSchema: z.ZodObject<{
576
+ head: z.ZodOptional<z.ZodString>;
577
+ bodyEnd: z.ZodOptional<z.ZodString>;
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>;
571
597
  /** Height range in px. Unset means the wordmark's own height, 28. */
572
598
  declare const BRAND_LOGO_HEIGHT: {
573
599
  readonly min: 16;
@@ -586,10 +612,10 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
586
612
  start: z.ZodNumber;
587
613
  end: z.ZodNumber;
588
614
  weight: z.ZodOptional<z.ZodEnum<{
615
+ bold: "bold";
589
616
  regular: "regular";
590
617
  medium: "medium";
591
618
  semibold: "semibold";
592
- bold: "bold";
593
619
  }>>;
594
620
  italic: z.ZodOptional<z.ZodBoolean>;
595
621
  font: z.ZodOptional<z.ZodEnum<{
@@ -652,6 +678,24 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
652
678
  description: z.ZodOptional<z.ZodString>;
653
679
  ogImage: z.ZodOptional<z.ZodString>;
654
680
  }, z.core.$strict>;
681
+ analytics: z.ZodOptional<z.ZodObject<{
682
+ head: z.ZodOptional<z.ZodString>;
683
+ bodyEnd: z.ZodOptional<z.ZodString>;
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>>;
655
699
  theme: z.ZodDefault<z.ZodObject<{
656
700
  mode: z.ZodPreprocess<z.ZodDefault<z.ZodEnum<{
657
701
  light: "light";
@@ -802,7 +846,7 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
802
846
  nameMarks: {
803
847
  start: number;
804
848
  end: number;
805
- weight?: "regular" | "medium" | "semibold" | "bold" | undefined;
849
+ weight?: "bold" | "regular" | "medium" | "semibold" | undefined;
806
850
  italic?: boolean | undefined;
807
851
  font?: "sans" | "serif" | "mono" | undefined;
808
852
  }[] | undefined;
@@ -925,6 +969,21 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
925
969
  alt: string;
926
970
  height?: number | undefined;
927
971
  } | undefined;
972
+ analytics?: {
973
+ head?: string | undefined;
974
+ bodyEnd?: string | undefined;
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;
928
987
  }, {
929
988
  name: string;
930
989
  url: string;
@@ -1043,7 +1102,7 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
1043
1102
  nameMarks?: {
1044
1103
  start: number;
1045
1104
  end: number;
1046
- weight?: "regular" | "medium" | "semibold" | "bold" | undefined;
1105
+ weight?: "bold" | "regular" | "medium" | "semibold" | undefined;
1047
1106
  italic?: boolean | undefined;
1048
1107
  font?: "sans" | "serif" | "mono" | undefined;
1049
1108
  }[] | undefined;
@@ -1067,10 +1126,27 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
1067
1126
  alt: string;
1068
1127
  height?: number | undefined;
1069
1128
  } | undefined;
1129
+ analytics?: {
1130
+ head?: string | undefined;
1131
+ bodyEnd?: string | undefined;
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;
1070
1144
  }>>;
1071
1145
  type Theme = z.infer<typeof themeSchema>;
1072
1146
  type FooterColumn = z.infer<typeof footerColumnSchema>;
1073
1147
  type SiteSeo = z.infer<typeof siteSeoSchema>;
1148
+ type SiteAnalytics = z.infer<typeof siteAnalyticsSchema>;
1149
+ type SiteConsent = z.infer<typeof siteConsentSchema>;
1074
1150
  type AnySite = z.output<ReturnType<typeof createSiteSchema>>;
1075
1151
  /** A site with header and footer variants as plain strings. {@link SiteWith} narrows them. */
1076
1152
  type Header = AnySite['header'];
@@ -1225,6 +1301,8 @@ type PropsOf<TRegistry extends SectionRegistry, TKey extends keyof TRegistry & s
1225
1301
  declare function createSectionSchema<TRegistry extends SectionRegistry>(registry: TRegistry): z.ZodType<SectionOf<TRegistry>>;
1226
1302
  //#endregion
1227
1303
  //#region src/page.d.ts
1304
+ /** Custom structured data. At most 10 blocks and 50 KB, so a stray paste cannot bloat every page. */
1305
+ declare const jsonLdSchema: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1228
1306
  declare const seoSchema: z.ZodObject<{
1229
1307
  title: z.ZodString;
1230
1308
  description: z.ZodString;
@@ -1232,6 +1310,7 @@ declare const seoSchema: z.ZodObject<{
1232
1310
  noindex: z.ZodDefault<z.ZodBoolean>;
1233
1311
  nofollow: z.ZodDefault<z.ZodBoolean>;
1234
1312
  canonical: z.ZodOptional<z.ZodString>;
1313
+ jsonLd: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1235
1314
  }, z.core.$strict>;
1236
1315
  /**
1237
1316
  * Pages the site needs but the user does not create: the editor lists them apart, edits their content,
@@ -1257,4 +1336,4 @@ declare function withoutOrphanSections<TPage extends {
1257
1336
  sections: readonly unknown[];
1258
1337
  }>(page: TPage, registry: SectionRegistry): TPage;
1259
1338
  //#endregion
1260
- export { textMarkSchema as $, Site as A, ContentMigration as At, themeColorsSchema as B, DEFAULT_MAX_FOOTER_COLUMNS as C, toneOption as Ct, FooterConfig as D, FieldIcons as Dt, FooterColumn as E, FieldChoices as Et, SiteWith as F, createMigrator as Ft, MAX_NAV_DEPTH as G, ButtonStyle as H, Theme as I, Tone as J, RichText as K, brandLogoSchema as L, SiteDefinition as M, ContentVersioning as Mt, SiteOf as N, DocumentKind as Nt, Header as O, ShowCondition as Ot, SiteSeo as P, SCHEMA_VERSION_KEY as Pt, richTextSchema as Q, defineSite as R, BRAND_LOGO_HEIGHT as S, themeModes as St, Footer as T, variantOption as Tt, Image as U, themeSchema as V, Link as W, imageSchema as X, htmlIdSchema as Y, linkSchema as Z, ThemeConfig as _, NumberOption as _t, isSystemPage as a, SectionDefinition as at, defaultTheme as b, buttonStyles as bt, BaseSection as c, WireRect as ct, anchorSchema as d, wireKinds as dt, textSchema as et, createSectionSchema as f, wireframeIssues as ft, SectionRegistry as g, EnumOption as gt, ContentModel as h, CountRule as ht, createPageSchema as i, SectionConfig as it, SiteConfig as j, ContentMigrator as jt, HeaderConfig as k, ShowWhen as kt, PropsOf as l, Wireframe as lt, API_PREFIX as m, BooleanOption as mt, SYSTEM_PAGES as n, trackingIdSchema as nt, seoSchema as o, defineSection as ot, sectionIdSchema as p, OptionalContent as pt, Text as q, Seo as r, withEmphasis as rt, withoutOrphanSections as s, WireKind as st, Page as t, toneSchema as tt, SectionOf as u, defineWireframe as ut, ThemeDefinition as v, Option as vt, DEFAULT_MAX_HEADER_ACTIONS as w, tones as wt, defineTheme as x, motionOption as xt, ThemeFont as y, StringOption as yt, siteSeoSchema 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 };
@@ -362,6 +362,8 @@ function addSectionIdentityIssues(sections, ctx) {
362
362
  });
363
363
  addLinkIdIssues(sections, ctx);
364
364
  }
365
+ /** Custom structured data. At most 10 blocks and 50 KB, so a stray paste cannot bloat every page. */
366
+ const jsonLdSchema = z.array(z.record(z.string(), z.unknown())).max(10).refine((blocks) => JSON.stringify(blocks).length <= 5e4, "JSON-LD is limited to 50 KB");
365
367
  const seoSchema = z.object({
366
368
  title: textSchema,
367
369
  description: z.string(),
@@ -370,7 +372,9 @@ const seoSchema = z.object({
370
372
  noindex: z.boolean().default(false),
371
373
  nofollow: z.boolean().default(false),
372
374
  /** Absolute URL or site-relative path. Overrides the generated canonical. */
373
- canonical: z.string().min(1).optional()
375
+ canonical: z.string().min(1).optional(),
376
+ /** Replaces the generated `WebPage` JSON-LD when set. */
377
+ jsonLd: jsonLdSchema.optional()
374
378
  }).strict();
375
379
  /**
376
380
  * Pages the site needs but the user does not create: the editor lists them apart, edits their content,
@@ -421,4 +425,4 @@ function withoutOrphanSections(page, registry) {
421
425
  };
422
426
  }
423
427
  //#endregion
424
- export { withImageLayout as A, withEmphasis as C, IMAGE_POSITIONS as D, IMAGE_FITS as E, tones as F, variantOption as I, motionOption as M, themeModes as N, isImageFit as O, toneOption as P, trackingIdSchema as S, DEFAULT_IMAGE_POSITION as T, linkSchema as _, seoSchema as a, textSchema as b, anchorSchema as c, sectionIdSchema as d, walkLinkIds as f, imageSchema as g, htmlIdSchema as h, isSystemPage as i, buttonStyles as j, isImagePosition as k, createSectionSchema as l, addMarkIssues as m, createPageSchema as n, slugSchema as o, MAX_NAV_DEPTH as p, createTolerantPageSchema as r, withoutOrphanSections as s, SYSTEM_PAGES as t, isOrphanSection as u, richTextSchema as v, DEFAULT_IMAGE_FIT as w, toneSchema as x, textMarkSchema as y };
428
+ export { isImagePosition as A, trackingIdSchema as C, IMAGE_FITS as D, DEFAULT_IMAGE_POSITION as E, toneOption as F, tones as I, variantOption as L, buttonStyles as M, motionOption as N, IMAGE_POSITIONS as O, themeModes as P, toneSchema as S, DEFAULT_IMAGE_FIT as T, imageSchema as _, jsonLdSchema as a, textMarkSchema as b, withoutOrphanSections as c, isOrphanSection as d, sectionIdSchema as f, htmlIdSchema as g, addMarkIssues as h, isSystemPage as i, withImageLayout as j, isImageFit as k, anchorSchema as l, MAX_NAV_DEPTH as m, createPageSchema as n, seoSchema as o, walkLinkIds as p, createTolerantPageSchema as r, slugSchema as s, SYSTEM_PAGES as t, createSectionSchema as u, linkSchema as v, withEmphasis as w, textSchema as x, richTextSchema as y };
@@ -1,4 +1,4 @@
1
- import { r as fieldKey } from "./theme-q19C8cIc.js";
1
+ import { r as fieldKey } from "./theme-DMpt3hcT.js";
2
2
  //#region src/errors.ts
3
3
  function formatContentIssues(issues) {
4
4
  return issues.map((issue) => {
@@ -1,4 +1,4 @@
1
- import { o as slugSchema } from "./page-C0l3CIuz.js";
1
+ import { s as slugSchema } from "./page-ON4Jkcm-.js";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import path from "node:path";
4
4
  //#region src/security.ts
@@ -1,4 +1,4 @@
1
- import { F as tones, N as themeModes, j as buttonStyles } from "./page-C0l3CIuz.js";
1
+ import { I as tones, M as buttonStyles, P as themeModes } from "./page-ON4Jkcm-.js";
2
2
  import { fontMarks } from "./marks.js";
3
3
  //#region src/field-rules.ts
4
4
  /**
@@ -1,3 +1,114 @@
1
+ //#region src/json-ld.ts
2
+ /** What generators hand out: the JSON wrapped in its own script tag. */
3
+ const SCRIPT_WRAPPER = /^\s*<script\b[^>]*\bapplication\/ld\+json\b[^>]*>([\s\S]*?)<\/script\s*>\s*$/i;
4
+ /**
5
+ * Parses what a person pastes into the JSON-LD box: an object, an array of objects, or either one inside a
6
+ * `<script type="application/ld+json">` tag. Empty input is valid and means "no custom JSON-LD".
7
+ */
8
+ function parseJsonLdInput(raw) {
9
+ const body = (SCRIPT_WRAPPER.exec(raw)?.[1] ?? raw).trim();
10
+ if (body === "") return {
11
+ ok: true,
12
+ value: []
13
+ };
14
+ let data;
15
+ try {
16
+ data = JSON.parse(body);
17
+ } catch (error) {
18
+ return {
19
+ ok: false,
20
+ error: error instanceof Error ? error.message : "Invalid JSON"
21
+ };
22
+ }
23
+ const items = Array.isArray(data) ? data : [data];
24
+ if (items.length === 0) return {
25
+ ok: true,
26
+ value: []
27
+ };
28
+ if (!items.every(isBlock)) return {
29
+ ok: false,
30
+ error: "JSON-LD must be an object or an array of objects."
31
+ };
32
+ return {
33
+ ok: true,
34
+ value: items
35
+ };
36
+ }
37
+ /** The text shown in the editor box: one block as an object, several as an array, none as empty. */
38
+ function formatJsonLd(blocks) {
39
+ if (!blocks || blocks.length === 0) return "";
40
+ return JSON.stringify(blocks.length === 1 ? blocks[0] : blocks, null, 2);
41
+ }
42
+ /** Advice, not errors: a block without `@type` or `@context` is usually a paste mistake, but Google decides what it accepts. */
43
+ function jsonLdWarnings(blocks) {
44
+ const warnings = [];
45
+ blocks.forEach((block, index) => {
46
+ const name = blocks.length === 1 ? "The block" : `Block ${index + 1}`;
47
+ if (!("@context" in block)) warnings.push(`${name} has no @context. Search engines expect "https://schema.org".`);
48
+ if (!("@type" in block) && !("@graph" in block)) warnings.push(`${name} has no @type.`);
49
+ });
50
+ return warnings;
51
+ }
52
+ function isBlock(value) {
53
+ return typeof value === "object" && value !== null && !Array.isArray(value);
54
+ }
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
1
112
  //#region src/theme-colors.ts
2
113
  /**
3
114
  * Site palette from three seed colors.
@@ -422,4 +533,4 @@ function contrastChecks(colors) {
422
533
  return checks;
423
534
  }
424
535
  //#endregion
425
- export { contrastRatio as a, hasThemeColors as c, tokenToHex as d, contrastChecks as i, hexToToken as l, HEX_COLOR as n, derivePalette as o, 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 };