bearminion 1.4.0 → 1.5.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.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8a428e7: Sitewide JSON-LD. `site.seo.jsonLd` is optional (up to 10 blocks, 50 KB, so no migration) and the default template emits its blocks on every page. A page's own `seo.jsonLd` still replaces only the generated `WebPage` block, and a block with the same `@id` is emitted once, the page's first. With the conventional ids `<url>/#organization` and `<url>/#website` in the site's blocks, the generated `WebPage` gets `isPartOf` and `publisher` as `@id` references. The Site tab has a Structured data group with an Edit JSON-LD dialog and a Generate button that fills an `Organization` and a `WebSite` from the site name, address and language, replacing blocks with the same `@id`. The logo is added only for a full https address. A warning shows while the site address is still `example.com`. `siteJsonLdIds`, `mergeJsonLd`, `hasJsonLdId`, `generateSiteJsonLd`, `upsertJsonLd` and `isPlaceholderSiteUrl` are exported; themes that emit JSON-LD should use `mergeJsonLd` (see the theme docs).
8
+
3
9
  ## 1.4.0
4
10
 
5
11
  ### Minor Changes
package/dist/astro.d.ts CHANGED
@@ -1,5 +1,5 @@
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";
1
+ import { A as SectionRegistry, J as SiteDefinition, Y as SiteOf, Yt as ContentMigrator, k as ContentModel, t as Page, u as SectionOf } from "./page-B5NoN74Q.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-DBfoi9D1.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-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";
1
+ import { a as imageFields, c as ContentReadError, l as ContentValidationError, r as loadThemePreview, t as PreviewUnavailableError } from "./preview--S1QUrMM.js";
2
+ import { t as defaultTheme } from "./theme-DFjHjgFO.js";
3
+ import { i as isSystemPage, n as createPageSchema$1, r as createTolerantPageSchema, s as withoutOrphanSections, u as isOrphanSection } from "./page-NOFVEqn1.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-Cq434rtI.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-D0vwI0OT.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 { d as SectionOf, g as ContentModel, t as Page } from "./page-C87bm-bf.js";
1
+ import { k as ContentModel, t as Page, u as SectionOf } from "./page-B5NoN74Q.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-ON4Jkcm-.js";
1
+ import { n as createPageSchema, r as createTolerantPageSchema } from "./page-NOFVEqn1.js";
2
2
  import { n as createMigrator } from "./migrate-B56wxCiC.js";
3
- import { t as CONTENT_COLLECTIONS_MARKER } from "./reload-Cq434rtI.js";
3
+ import { t as CONTENT_COLLECTIONS_MARKER } from "./reload-D0vwI0OT.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 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";
1
+ import { $ as brandLogoSchema, $t as createMigrator, A as SectionRegistry, At as wireframeIssues, B as FooterColumn, Bt as themeModes, C as jsonLdWarnings, Ct as SectionDefinition, D as upsertJsonLd, Dt as Wireframe, E as siteJsonLdIds, Et as WireRect, F as defineTheme, Ft as NumberOption, G as SiteAnalytics, Gt as FieldIcons, H as Header, Ht as tones, I as BRAND_LOGO_HEIGHT, It as Option, J as SiteDefinition, Jt as ContentMigration, K as SiteConfig, Kt as ShowCondition, L as DEFAULT_MAX_FOOTER_COLUMNS, Lt as StringOption, M as ThemeDefinition, Mt as BooleanOption, N as ThemeFont, Nt as CountRule, O as API_PREFIX, Ot as defineWireframe, P as defaultTheme, Pt as EnumOption, Q as Theme, Qt as SCHEMA_VERSION_KEY, R as DEFAULT_MAX_HEADER_ACTIONS, Rt as buttonStyles, S as jsonLdSchema, St as SectionConfig, T as parseJsonLdInput, Tt as WireKind, U as HeaderConfig, Ut as variantOption, V as FooterConfig, Vt as toneOption, W as Site, Wt as FieldChoices, X as SiteSeo, Xt as ContentVersioning, Y as SiteOf, Yt as ContentMigrator, Z as SiteWith, Zt as DocumentKind, _ as SiteJsonLdSource, _t as textMarkSchema, a as isSystemPage, at as themeSchema, b as hasJsonLdId, bt as trackingIdSchema, c as BaseSection, ct as Link, d as anchorSchema, dt as Text, et as defineSite, f as createSectionSchema, ft as Tone, g as SiteJsonLdIds, gt as richTextSchema, h as JsonLdResult, ht as linkSchema, i as createPageSchema, it as themeColorsSchema, j as ThemeConfig, jt as OptionalContent, k as ContentModel, kt as wireKinds, l as PropsOf, lt as MAX_NAV_DEPTH, m as JsonLdBlock, mt as imageSchema, n as SYSTEM_PAGES, nt as siteConsentSchema, o as seoSchema, ot as ButtonStyle, p as sectionIdSchema, pt as htmlIdSchema, q as SiteConsent, qt as ShowWhen, r as Seo, rt as siteSeoSchema, s as withoutOrphanSections, st as Image, t as Page, tt as siteAnalyticsSchema, u as SectionOf, ut as RichText, v as formatJsonLd, vt as textSchema, w as mergeJsonLd, wt as defineSection, x as isPlaceholderSiteUrl, xt as withEmphasis, y as generateSiteJsonLd, yt as toneSchema, z as Footer, zt as motionOption } from "./page-B5NoN74Q.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-qFsKXOuE.js";
3
+ import "./options-DBfoi9D1.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,26 +162,6 @@ 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
165
  //#region src/consent.d.ts
186
166
  /** Longest copy the consent banner takes, in characters. The editor shows the same limits. */
187
167
  export declare const CONSENT_LIMITS: {
@@ -292,4 +272,4 @@ interface ContrastCheck {
292
272
  /** Text pairs a visitor reads: text on its background, label on the accent fill, link on its background. */
293
273
  export declare function contrastChecks(colors: ThemeColors | undefined): ContrastCheck[];
294
274
  //#endregion
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 };
275
+ 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 SiteJsonLdIds, type SiteJsonLdSource, 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, formatJsonLd, generateSiteJsonLd, hasJsonLdId, htmlIdSchema, imageSchema, isPlaceholderSiteUrl, isSystemPage, jsonLdSchema, jsonLdWarnings, linkSchema, markClass, marksFromEmphasis, mergeJsonLd, motionOption, parseJsonLdInput, reconcileMarks, renderMarkedHtml, richTextSchema, sectionIdSchema, seoSchema, siteAnalyticsSchema, siteConsentSchema, siteJsonLdIds, siteSeoSchema, splitMarkedText, textMarkSchema, textSchema, themeColorsSchema, themeModes, themeSchema, toneOption, toneSchema, tones, trackingIdSchema, upsertJsonLd, 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-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";
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--S1QUrMM.js";
2
+ import { n as defineTheme, t as defaultTheme } from "./theme-DFjHjgFO.js";
3
+ import { A as textSchema, C as MAX_NAV_DEPTH, D as linkSchema, E as imageSchema, F as DEFAULT_IMAGE_POSITION, G as tones, H as motionOption, I as IMAGE_FITS, K as variantOption, L as IMAGE_POSITIONS, M as trackingIdSchema, N as withEmphasis, O as richTextSchema, P as DEFAULT_IMAGE_FIT, R as isImageFit, S as upsertJsonLd, T as htmlIdSchema, U as themeModes, V as buttonStyles, W as toneOption, _ as jsonLdSchema, a as seoSchema, b as parseJsonLdInput, c as anchorSchema, d as sectionIdSchema, f as walkLinkIds, g as isPlaceholderSiteUrl, h as hasJsonLdId, i as isSystemPage, j as toneSchema, k as textMarkSchema, l as createSectionSchema, m as generateSiteJsonLd, n as createPageSchema, p as formatJsonLd, s as withoutOrphanSections, t as SYSTEM_PAGES, v as jsonLdWarnings, w as addMarkIssues, x as siteJsonLdIds, y as mergeJsonLd, z as isImagePosition } from "./page-NOFVEqn1.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 { _ 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";
6
+ import { _ as consentCopy, a as contrastRatio, 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 } from "./theme-colors-CbCyVANH.js";
7
7
  import { z } from "zod";
8
8
  //#region src/wireframe.ts
9
9
  /**
@@ -239,7 +239,9 @@ const siteSeoSchema = z.object({
239
239
  /** `%s` is replaced with the page SEO title. */
240
240
  titleTemplate: z.string().min(1).optional(),
241
241
  description: z.string().optional(),
242
- ogImage: z.string().min(1).optional()
242
+ ogImage: z.string().min(1).optional(),
243
+ /** Structured data emitted on every page, such as `Organization` and `WebSite`. A page's own blocks add to it. */
244
+ jsonLd: jsonLdSchema.optional()
243
245
  }).strict();
244
246
  /**
245
247
  * Raw HTML snippets for analytics and similar tags. A theme renders `head` at the end of `<head>` and
@@ -439,4 +441,4 @@ function defineSite(config) {
439
441
  };
440
442
  }
441
443
  //#endregion
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 };
444
+ 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, generateSiteJsonLd, hasJsonLdId, hasOverrides, hasThemeColors, hexToToken, htmlIdSchema, imageSchema, isImageFit, isImagePosition, isPlaceholderSiteUrl, isSystemPage, jsonLdSchema, jsonLdWarnings, linkSchema, markClass, marksFromEmphasis, mergeJsonLd, motionOption, parseJsonLdInput, reconcileMarks, renderMarkedHtml, richTextSchema, sectionIdSchema, seoSchema, siteAnalyticsSchema, siteConsentSchema, siteJsonLdIds, siteSeoSchema, splitMarkedText, textMarkSchema, textSchema, themeColorsSchema, themeCss, themeModes, themeSchema, tokenToHex, toneOption, toneSchema, tones, trackingIdSchema, upsertJsonLd, variantOption, weightMarks, wireKinds, wireframeIssues, withEmphasis, withoutOrphanSections };
@@ -1,4 +1,4 @@
1
- import { F as SiteDefinition, It as ContentMigrator, _ as SectionRegistry, g as ContentModel, y as ThemeDefinition } from "./page-C87bm-bf.js";
1
+ import { A as SectionRegistry, J as SiteDefinition, M as ThemeDefinition, Yt as ContentMigrator, k as ContentModel } from "./page-B5NoN74Q.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";
275
274
  regular: "regular";
276
275
  medium: "medium";
277
276
  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";
319
318
  regular: "regular";
320
319
  medium: "medium";
321
320
  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";
337
336
  regular: "regular";
338
337
  medium: "medium";
339
338
  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";
357
356
  regular: "regular";
358
357
  medium: "medium";
359
358
  semibold: "semibold";
359
+ bold: "bold";
360
360
  }>>;
361
361
  italic: z.ZodOptional<z.ZodBoolean>;
362
362
  font: z.ZodOptional<z.ZodEnum<{
@@ -567,6 +567,7 @@ declare const siteSeoSchema: z.ZodObject<{
567
567
  titleTemplate: z.ZodOptional<z.ZodString>;
568
568
  description: z.ZodOptional<z.ZodString>;
569
569
  ogImage: z.ZodOptional<z.ZodString>;
570
+ jsonLd: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
570
571
  }, z.core.$strict>;
571
572
  /**
572
573
  * Raw HTML snippets for analytics and similar tags. A theme renders `head` at the end of `<head>` and
@@ -612,10 +613,10 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
612
613
  start: z.ZodNumber;
613
614
  end: z.ZodNumber;
614
615
  weight: z.ZodOptional<z.ZodEnum<{
615
- bold: "bold";
616
616
  regular: "regular";
617
617
  medium: "medium";
618
618
  semibold: "semibold";
619
+ bold: "bold";
619
620
  }>>;
620
621
  italic: z.ZodOptional<z.ZodBoolean>;
621
622
  font: z.ZodOptional<z.ZodEnum<{
@@ -677,6 +678,7 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
677
678
  titleTemplate: z.ZodOptional<z.ZodString>;
678
679
  description: z.ZodOptional<z.ZodString>;
679
680
  ogImage: z.ZodOptional<z.ZodString>;
681
+ jsonLd: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
680
682
  }, z.core.$strict>;
681
683
  analytics: z.ZodOptional<z.ZodObject<{
682
684
  head: z.ZodOptional<z.ZodString>;
@@ -846,7 +848,7 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
846
848
  nameMarks: {
847
849
  start: number;
848
850
  end: number;
849
- weight?: "bold" | "regular" | "medium" | "semibold" | undefined;
851
+ weight?: "regular" | "medium" | "semibold" | "bold" | undefined;
850
852
  italic?: boolean | undefined;
851
853
  font?: "sans" | "serif" | "mono" | undefined;
852
854
  }[] | undefined;
@@ -856,6 +858,7 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
856
858
  titleTemplate?: string | undefined;
857
859
  description?: string | undefined;
858
860
  ogImage?: string | undefined;
861
+ jsonLd?: Record<string, unknown>[] | undefined;
859
862
  };
860
863
  theme: {
861
864
  mode: "light" | "dark";
@@ -991,6 +994,7 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
991
994
  titleTemplate?: string | undefined;
992
995
  description?: string | undefined;
993
996
  ogImage?: string | undefined;
997
+ jsonLd?: Record<string, unknown>[] | undefined;
994
998
  };
995
999
  theme: {
996
1000
  mode: "light" | "dark";
@@ -1102,7 +1106,7 @@ declare function createSiteSchema(header: HeaderParts, footer: FooterParts): z.Z
1102
1106
  nameMarks?: {
1103
1107
  start: number;
1104
1108
  end: number;
1105
- weight?: "bold" | "regular" | "medium" | "semibold" | undefined;
1109
+ weight?: "regular" | "medium" | "semibold" | "bold" | undefined;
1106
1110
  italic?: boolean | undefined;
1107
1111
  font?: "sans" | "serif" | "mono" | undefined;
1108
1112
  }[] | undefined;
@@ -1268,6 +1272,57 @@ interface ContentModel extends ContentVersioning {
1268
1272
  theme?: ThemeDefinition;
1269
1273
  }
1270
1274
  //#endregion
1275
+ //#region src/json-ld.d.ts
1276
+ /** JSON-LD blocks as a page stores them: parsed, so diffs stay readable and output can be escaped safely. */
1277
+ type JsonLdBlock = Record<string, unknown>;
1278
+ type JsonLdResult = {
1279
+ ok: true;
1280
+ value: JsonLdBlock[];
1281
+ } | {
1282
+ ok: false;
1283
+ error: string;
1284
+ };
1285
+ /**
1286
+ * Parses what a person pastes into the JSON-LD box: an object, an array of objects, or either one inside a
1287
+ * `<script type="application/ld+json">` tag. Empty input is valid and means "no custom JSON-LD".
1288
+ */
1289
+ declare function parseJsonLdInput(raw: string): JsonLdResult;
1290
+ /** The text shown in the editor box: one block as an object, several as an array, none as empty. */
1291
+ declare function formatJsonLd(blocks: readonly JsonLdBlock[] | undefined): string;
1292
+ /** Advice, not errors: a block without `@type` or `@context` is usually a paste mistake, but Google decides what it accepts. */
1293
+ declare function jsonLdWarnings(blocks: readonly JsonLdBlock[]): string[];
1294
+ /** Custom structured data. At most 10 blocks and 50 KB, so a stray paste cannot bloat every page. */
1295
+ declare const jsonLdSchema: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1296
+ /** The `@id` the generated site blocks use, so a page's markup can point at them. */
1297
+ interface SiteJsonLdIds {
1298
+ organization: string;
1299
+ website: string;
1300
+ }
1301
+ /** `<site.url>/#organization` and `<site.url>/#website`, with trailing slashes on the site URL ignored. */
1302
+ declare function siteJsonLdIds(siteUrl: string): SiteJsonLdIds;
1303
+ /** Whether a block with this `@id` is in the list. */
1304
+ declare function hasJsonLdId(blocks: readonly JsonLdBlock[] | undefined, id: string): boolean;
1305
+ /**
1306
+ * The blocks a page emits: every site block, then the page's own. A block with the same `@id` is
1307
+ * emitted once and the page's copy wins, so a page can refine what the site says without repeating it.
1308
+ */
1309
+ declare function mergeJsonLd(site: readonly JsonLdBlock[] | undefined, page: readonly JsonLdBlock[]): JsonLdBlock[];
1310
+ /** What the Generate button reads from the site settings. */
1311
+ interface SiteJsonLdSource {
1312
+ name: string;
1313
+ url: string;
1314
+ /** BCP 47 tag. Default `en`. */
1315
+ lang?: string;
1316
+ /** An absolute https address. Local images get their address at build time, so they cannot go here. */
1317
+ logo?: string;
1318
+ }
1319
+ /** An `Organization` and a `WebSite` block for the site, in the `@id` convention of {@link siteJsonLdIds}. */
1320
+ declare function generateSiteJsonLd(source: SiteJsonLdSource): JsonLdBlock[];
1321
+ /** Puts generated blocks into a list: a block with the same `@id` is replaced in place, the rest is kept. */
1322
+ declare function upsertJsonLd(blocks: readonly JsonLdBlock[], generated: readonly JsonLdBlock[]): JsonLdBlock[];
1323
+ /** True while the site URL is still the template's `example.com` placeholder. */
1324
+ declare function isPlaceholderSiteUrl(siteUrl: string): boolean;
1325
+ //#endregion
1271
1326
  //#region src/section-schema.d.ts
1272
1327
  declare const sectionIdSchema: z.ZodString;
1273
1328
  declare const anchorSchema: z.ZodString;
@@ -1301,8 +1356,6 @@ type PropsOf<TRegistry extends SectionRegistry, TKey extends keyof TRegistry & s
1301
1356
  declare function createSectionSchema<TRegistry extends SectionRegistry>(registry: TRegistry): z.ZodType<SectionOf<TRegistry>>;
1302
1357
  //#endregion
1303
1358
  //#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>>;
1306
1359
  declare const seoSchema: z.ZodObject<{
1307
1360
  title: z.ZodString;
1308
1361
  description: z.ZodString;
@@ -1336,4 +1389,4 @@ declare function withoutOrphanSections<TPage extends {
1336
1389
  sections: readonly unknown[];
1337
1390
  }>(page: TPage, registry: SectionRegistry): TPage;
1338
1391
  //#endregion
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 };
1392
+ export { brandLogoSchema as $, createMigrator as $t, SectionRegistry as A, wireframeIssues as At, FooterColumn as B, themeModes as Bt, jsonLdWarnings as C, SectionDefinition as Ct, upsertJsonLd as D, Wireframe as Dt, siteJsonLdIds as E, WireRect as Et, defineTheme as F, NumberOption as Ft, SiteAnalytics as G, FieldIcons as Gt, Header as H, tones as Ht, BRAND_LOGO_HEIGHT as I, Option as It, SiteDefinition as J, ContentMigration as Jt, SiteConfig as K, ShowCondition as Kt, DEFAULT_MAX_FOOTER_COLUMNS as L, StringOption as Lt, ThemeDefinition as M, BooleanOption as Mt, ThemeFont as N, CountRule as Nt, API_PREFIX as O, defineWireframe as Ot, defaultTheme as P, EnumOption as Pt, Theme as Q, SCHEMA_VERSION_KEY as Qt, DEFAULT_MAX_HEADER_ACTIONS as R, buttonStyles as Rt, jsonLdSchema as S, SectionConfig as St, parseJsonLdInput as T, WireKind as Tt, HeaderConfig as U, variantOption as Ut, FooterConfig as V, toneOption as Vt, Site as W, FieldChoices as Wt, SiteSeo as X, ContentVersioning as Xt, SiteOf as Y, ContentMigrator as Yt, SiteWith as Z, DocumentKind as Zt, SiteJsonLdSource as _, textMarkSchema as _t, isSystemPage as a, themeSchema as at, hasJsonLdId as b, trackingIdSchema as bt, BaseSection as c, Link as ct, anchorSchema as d, Text as dt, defineSite as et, createSectionSchema as f, Tone as ft, SiteJsonLdIds as g, richTextSchema as gt, JsonLdResult as h, linkSchema as ht, createPageSchema as i, themeColorsSchema as it, ThemeConfig as j, OptionalContent as jt, ContentModel as k, wireKinds as kt, PropsOf as l, MAX_NAV_DEPTH as lt, JsonLdBlock as m, imageSchema as mt, SYSTEM_PAGES as n, siteConsentSchema as nt, seoSchema as o, ButtonStyle as ot, sectionIdSchema as p, htmlIdSchema as pt, SiteConsent as q, ShowWhen as qt, Seo as r, siteSeoSchema as rt, withoutOrphanSections as s, Image as st, Page as t, siteAnalyticsSchema as tt, SectionOf as u, RichText as ut, formatJsonLd as v, textSchema as vt, mergeJsonLd as w, defineSection as wt, isPlaceholderSiteUrl as x, withEmphasis as xt, generateSiteJsonLd as y, toneSchema as yt, Footer as z, motionOption as zt };
@@ -203,6 +203,130 @@ function addMarkIssues(text, marks, ctx, path = ["marks"]) {
203
203
  });
204
204
  }
205
205
  //#endregion
206
+ //#region src/json-ld.ts
207
+ /** What generators hand out: the JSON wrapped in its own script tag. */
208
+ const SCRIPT_WRAPPER = /^\s*<script\b[^>]*\bapplication\/ld\+json\b[^>]*>([\s\S]*?)<\/script\s*>\s*$/i;
209
+ /**
210
+ * Parses what a person pastes into the JSON-LD box: an object, an array of objects, or either one inside a
211
+ * `<script type="application/ld+json">` tag. Empty input is valid and means "no custom JSON-LD".
212
+ */
213
+ function parseJsonLdInput(raw) {
214
+ const body = (SCRIPT_WRAPPER.exec(raw)?.[1] ?? raw).trim();
215
+ if (body === "") return {
216
+ ok: true,
217
+ value: []
218
+ };
219
+ let data;
220
+ try {
221
+ data = JSON.parse(body);
222
+ } catch (error) {
223
+ return {
224
+ ok: false,
225
+ error: error instanceof Error ? error.message : "Invalid JSON"
226
+ };
227
+ }
228
+ const items = Array.isArray(data) ? data : [data];
229
+ if (items.length === 0) return {
230
+ ok: true,
231
+ value: []
232
+ };
233
+ if (!items.every(isBlock)) return {
234
+ ok: false,
235
+ error: "JSON-LD must be an object or an array of objects."
236
+ };
237
+ return {
238
+ ok: true,
239
+ value: items
240
+ };
241
+ }
242
+ /** The text shown in the editor box: one block as an object, several as an array, none as empty. */
243
+ function formatJsonLd(blocks) {
244
+ if (!blocks || blocks.length === 0) return "";
245
+ return JSON.stringify(blocks.length === 1 ? blocks[0] : blocks, null, 2);
246
+ }
247
+ /** Advice, not errors: a block without `@type` or `@context` is usually a paste mistake, but Google decides what it accepts. */
248
+ function jsonLdWarnings(blocks) {
249
+ const warnings = [];
250
+ blocks.forEach((block, index) => {
251
+ const name = blocks.length === 1 ? "The block" : `Block ${index + 1}`;
252
+ if (!("@context" in block)) warnings.push(`${name} has no @context. Search engines expect "https://schema.org".`);
253
+ if (!("@type" in block) && !("@graph" in block)) warnings.push(`${name} has no @type.`);
254
+ });
255
+ return warnings;
256
+ }
257
+ function isBlock(value) {
258
+ return typeof value === "object" && value !== null && !Array.isArray(value);
259
+ }
260
+ /** Custom structured data. At most 10 blocks and 50 KB, so a stray paste cannot bloat every page. */
261
+ 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");
262
+ /** `<site.url>/#organization` and `<site.url>/#website`, with trailing slashes on the site URL ignored. */
263
+ function siteJsonLdIds(siteUrl) {
264
+ const base = siteUrl.replace(/\/+$/, "");
265
+ return {
266
+ organization: `${base}/#organization`,
267
+ website: `${base}/#website`
268
+ };
269
+ }
270
+ function blockId(block) {
271
+ const id = block["@id"];
272
+ return typeof id === "string" ? id : void 0;
273
+ }
274
+ /** Whether a block with this `@id` is in the list. */
275
+ function hasJsonLdId(blocks, id) {
276
+ return (blocks ?? []).some((block) => blockId(block) === id);
277
+ }
278
+ /**
279
+ * The blocks a page emits: every site block, then the page's own. A block with the same `@id` is
280
+ * emitted once and the page's copy wins, so a page can refine what the site says without repeating it.
281
+ */
282
+ function mergeJsonLd(site, page) {
283
+ const taken = new Set(page.map(blockId).filter((id) => id !== void 0));
284
+ return [...(site ?? []).filter((block) => {
285
+ const id = blockId(block);
286
+ return id === void 0 || !taken.has(id);
287
+ }), ...page];
288
+ }
289
+ /** An `Organization` and a `WebSite` block for the site, in the `@id` convention of {@link siteJsonLdIds}. */
290
+ function generateSiteJsonLd(source) {
291
+ const ids = siteJsonLdIds(source.url);
292
+ const url = `${source.url.replace(/\/+$/, "")}/`;
293
+ return [{
294
+ "@context": "https://schema.org",
295
+ "@type": "Organization",
296
+ "@id": ids.organization,
297
+ name: source.name,
298
+ url,
299
+ ...source.logo ? { logo: source.logo } : {}
300
+ }, {
301
+ "@context": "https://schema.org",
302
+ "@type": "WebSite",
303
+ "@id": ids.website,
304
+ name: source.name,
305
+ url,
306
+ inLanguage: source.lang || "en"
307
+ }];
308
+ }
309
+ /** Puts generated blocks into a list: a block with the same `@id` is replaced in place, the rest is kept. */
310
+ function upsertJsonLd(blocks, generated) {
311
+ const next = [...blocks];
312
+ for (const block of generated) {
313
+ const id = blockId(block);
314
+ const at = id === void 0 ? -1 : next.findIndex((item) => blockId(item) === id);
315
+ if (at >= 0) next[at] = block;
316
+ else next.push(block);
317
+ }
318
+ return next;
319
+ }
320
+ /** True while the site URL is still the template's `example.com` placeholder. */
321
+ function isPlaceholderSiteUrl(siteUrl) {
322
+ try {
323
+ const host = new URL(siteUrl).hostname.toLowerCase();
324
+ return host === "example.com" || host.endsWith(".example.com");
325
+ } catch {
326
+ return false;
327
+ }
328
+ }
329
+ //#endregion
206
330
  //#region src/link-ids.ts
207
331
  /** Walk objects that look like links (`label` + `href`) and report each `id`. */
208
332
  function walkLinkIds(value, path, visit) {
@@ -362,8 +486,6 @@ function addSectionIdentityIssues(sections, ctx) {
362
486
  });
363
487
  addLinkIdIssues(sections, ctx);
364
488
  }
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");
367
489
  const seoSchema = z.object({
368
490
  title: textSchema,
369
491
  description: z.string(),
@@ -425,4 +547,4 @@ function withoutOrphanSections(page, registry) {
425
547
  };
426
548
  }
427
549
  //#endregion
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 };
550
+ export { textSchema as A, withImageLayout as B, MAX_NAV_DEPTH as C, linkSchema as D, imageSchema as E, DEFAULT_IMAGE_POSITION as F, tones as G, motionOption as H, IMAGE_FITS as I, variantOption as K, IMAGE_POSITIONS as L, trackingIdSchema as M, withEmphasis as N, richTextSchema as O, DEFAULT_IMAGE_FIT as P, isImageFit as R, upsertJsonLd as S, htmlIdSchema as T, themeModes as U, buttonStyles as V, toneOption as W, jsonLdSchema as _, seoSchema as a, parseJsonLdInput as b, anchorSchema as c, sectionIdSchema as d, walkLinkIds as f, isPlaceholderSiteUrl as g, hasJsonLdId as h, isSystemPage as i, toneSchema as j, textMarkSchema as k, createSectionSchema as l, generateSiteJsonLd as m, createPageSchema as n, slugSchema as o, formatJsonLd as p, createTolerantPageSchema as r, withoutOrphanSections as s, SYSTEM_PAGES as t, isOrphanSection as u, jsonLdWarnings as v, addMarkIssues as w, siteJsonLdIds as x, mergeJsonLd as y, isImagePosition as z };
@@ -1,4 +1,4 @@
1
- import { r as fieldKey } from "./theme-DMpt3hcT.js";
1
+ import { r as fieldKey } from "./theme-DFjHjgFO.js";
2
2
  //#region src/errors.ts
3
3
  function formatContentIssues(issues) {
4
4
  return issues.map((issue) => {
@@ -1,4 +1,4 @@
1
- import { s as slugSchema } from "./page-ON4Jkcm-.js";
1
+ import { o as slugSchema } from "./page-NOFVEqn1.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 { I as tones, M as buttonStyles, P as themeModes } from "./page-ON4Jkcm-.js";
1
+ import { G as tones, U as themeModes, V as buttonStyles } from "./page-NOFVEqn1.js";
2
2
  import { fontMarks } from "./marks.js";
3
3
  //#region src/field-rules.ts
4
4
  /**
@@ -1,58 +1,3 @@
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
1
  //#region src/consent.ts
57
2
  /** Longest copy the consent banner takes, in characters. The editor shows the same limits. */
58
3
  const CONSENT_LIMITS = {
@@ -533,4 +478,4 @@ function contrastChecks(colors) {
533
478
  return checks;
534
479
  }
535
480
  //#endregion
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 };
481
+ export { consentCopy as _, contrastRatio as a, 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 };
package/dist/ui/mount.js CHANGED
@@ -1,7 +1,7 @@
1
- import { a as sectionFieldKey, i as isPropShown, t as defaultTheme } from "../theme-DMpt3hcT.js";
2
- import { A as isImagePosition, D as IMAGE_FITS, E as DEFAULT_IMAGE_POSITION, O as IMAGE_POSITIONS, T as DEFAULT_IMAGE_FIT, i as isSystemPage, j as withImageLayout, k as isImageFit } from "../page-ON4Jkcm-.js";
1
+ import { a as sectionFieldKey, i as isPropShown, t as defaultTheme } from "../theme-DFjHjgFO.js";
2
+ import { B as withImageLayout, F as DEFAULT_IMAGE_POSITION, I as IMAGE_FITS, L as IMAGE_POSITIONS, P as DEFAULT_IMAGE_FIT, R as isImageFit, S as upsertJsonLd, b as parseJsonLdInput, g as isPlaceholderSiteUrl, i as isSystemPage, m as generateSiteJsonLd, p as formatJsonLd, v as jsonLdWarnings, z as isImagePosition } from "../page-NOFVEqn1.js";
3
3
  import { applyTextMark, fontMarks, marksFromEmphasis, reconcileMarks, renderMarkedHtml } from "../marks.js";
4
- import { _ as consentCopy, b as jsonLdWarnings, c as hasThemeColors, d as tokenToHex, f as CONSENT_COPY, h as CONSENT_LIMITS, i as contrastChecks, n as HEX_COLOR, o as derivePalette, r as OVERRIDE_TOKENS, s as hasOverrides, t as DEFAULT_BRAND, u as themeCss, v as consentTextIssues, x as parseJsonLdInput, y as formatJsonLd } from "../theme-colors-zICpze42.js";
4
+ import { _ as consentCopy, c as hasThemeColors, d as tokenToHex, f as CONSENT_COPY, h as CONSENT_LIMITS, i as contrastChecks, n as HEX_COLOR, o as derivePalette, r as OVERRIDE_TOKENS, s as hasOverrides, t as DEFAULT_BRAND, u as themeCss, v as consentTextIssues } from "../theme-colors-CbCyVANH.js";
5
5
  import { _ as writeSaveNotice, a as localDraftStorage, d as removeDraft, f as savedNoticeFor, g as writeDraftRecord, h as sweepExpiredDrafts, i as isForeignSave, l as readDraftRecord, m as siteSavedKey, n as draftBannerText, o as pageDraftKey, p as siteDraftKey, r as hashText, s as pageSavedKey, t as assessStoredDraft, u as reloadDecision, x as draftImageUsages } from "../draft-store-1Y4-sn45.js";
6
6
  import { isSafeHref, markdownFromHtml, renderRichText, richLinkAt, sanitizeRichHtml, stripPlainRich, updateRichLink } from "../rich-text.js";
7
7
  import { Component, Fragment, createContext, isValidElement, options, render } from "preact";
@@ -13867,6 +13867,24 @@ function hasAnalyticsSnippet(site) {
13867
13867
  function consentReopenOf(site) {
13868
13868
  return getAt(site, "consent.reopen") === "icon" ? "icon" : "footer";
13869
13869
  }
13870
+ /** The JSON-LD blocks every page carries, as the draft holds them. */
13871
+ function siteJsonLdOf(site) {
13872
+ const stored = getAt(site, "seo.jsonLd");
13873
+ return Array.isArray(stored) ? stored.filter(isRecord$2) : [];
13874
+ }
13875
+ /** What Generate reads from the draft. The logo counts only when it is a full https address. */
13876
+ function siteJsonLdSourceOf(site) {
13877
+ const name = getAt(site, "name");
13878
+ const url = getAt(site, "url");
13879
+ const lang = getAt(site, "lang");
13880
+ const logo = getAt(site, "brandLogo.src");
13881
+ return {
13882
+ name: typeof name === "string" ? name : "",
13883
+ url: typeof url === "string" ? url : "",
13884
+ lang: typeof lang === "string" && lang !== "" ? lang : void 0,
13885
+ logo: typeof logo === "string" && /^https:\/\//i.test(logo) ? logo : void 0
13886
+ };
13887
+ }
13870
13888
  //#endregion
13871
13889
  //#region src/ui/controllers/reactions.ts
13872
13890
  /** Signal effects that keep the page, storage and history in step with the editor state. */
@@ -18468,6 +18486,151 @@ function SeoDialog({ page, siteName, siteUrl, system, images, issues, onUploaded
18468
18486
  });
18469
18487
  }
18470
18488
  //#endregion
18489
+ //#region src/ui/dialogs/site-jsonld-dialog.tsx
18490
+ /**
18491
+ * The structured data every page carries: an `Organization` and a `WebSite`, or any other markup. It edits a
18492
+ * copy: Apply writes it to the site draft in one step, Cancel and Escape drop it. Nothing reaches disk until save.
18493
+ */
18494
+ function SiteJsonLdDialog({ blocks, source, onApply, onClose }) {
18495
+ const root = useRef(null);
18496
+ const [jsonText, setJsonText] = useState(() => formatJsonLd(blocks));
18497
+ const parsed = parseJsonLdInput(jsonText);
18498
+ const warnings = parsed.ok ? jsonLdWarnings(parsed.value) : [];
18499
+ const count = parsed.ok ? parsed.value.length : 0;
18500
+ const placeholder = isPlaceholderSiteUrl(source.url);
18501
+ useEffect(() => {
18502
+ root.current?.querySelector("textarea")?.focus();
18503
+ }, []);
18504
+ function generate() {
18505
+ if (!parsed.ok) return;
18506
+ setJsonText(formatJsonLd(upsertJsonLd(parsed.value, generateSiteJsonLd(source))));
18507
+ }
18508
+ return /* @__PURE__ */ jsx("div", {
18509
+ class: "bm-scrim",
18510
+ children: /* @__PURE__ */ jsxs("div", {
18511
+ class: "bm-dialog bm-seo",
18512
+ role: "dialog",
18513
+ "aria-modal": "true",
18514
+ "aria-label": "Edit site JSON-LD",
18515
+ ref: root,
18516
+ children: [
18517
+ /* @__PURE__ */ jsxs("div", {
18518
+ class: "bm-dialog-head",
18519
+ children: [/* @__PURE__ */ jsxs("div", {
18520
+ class: "bm-dialog-heading",
18521
+ children: [/* @__PURE__ */ jsx("h2", {
18522
+ class: "bm-dialog-title",
18523
+ children: "Structured data · every page"
18524
+ }), /* @__PURE__ */ jsx("p", {
18525
+ class: "bm-dialog-sub",
18526
+ children: "Applied to the site draft. Nothing is written until you save."
18527
+ })]
18528
+ }), /* @__PURE__ */ jsx(DialogClose, { onClose })]
18529
+ }),
18530
+ /* @__PURE__ */ jsxs("div", {
18531
+ class: "bm-seo-body",
18532
+ children: [/* @__PURE__ */ jsxs("div", {
18533
+ class: "bm-stack",
18534
+ children: [
18535
+ /* @__PURE__ */ jsx("p", {
18536
+ class: "bm-group",
18537
+ children: "Generate"
18538
+ }),
18539
+ /* @__PURE__ */ jsx("button", {
18540
+ type: "button",
18541
+ class: "bm-tertiary bm-seo-format",
18542
+ disabled: !parsed.ok,
18543
+ onClick: generate,
18544
+ children: "Generate Organization and WebSite"
18545
+ }),
18546
+ /* @__PURE__ */ jsxs("p", {
18547
+ class: "bm-hint",
18548
+ children: ["Filled from the site name, address and language. Blocks you already have with the same @id are replaced, the rest are kept.", source.logo ? "" : " No logo is added: paste a full https address as \"logo\" by hand, because a local image only gets its address when the site is built."]
18549
+ }),
18550
+ placeholder && /* @__PURE__ */ jsxs("span", {
18551
+ class: "bm-error",
18552
+ role: "alert",
18553
+ children: [
18554
+ "The site address is still ",
18555
+ source.url,
18556
+ ". Set the real one under Identity before you generate, or the markup names the wrong site."
18557
+ ]
18558
+ })
18559
+ ]
18560
+ }), /* @__PURE__ */ jsxs("div", {
18561
+ class: "bm-stack",
18562
+ children: [
18563
+ /* @__PURE__ */ jsx("p", {
18564
+ class: "bm-group",
18565
+ children: "JSON-LD"
18566
+ }),
18567
+ /* @__PURE__ */ jsxs("label", {
18568
+ class: "bm-label",
18569
+ children: [
18570
+ /* @__PURE__ */ jsxs("span", {
18571
+ class: "bm-label-row",
18572
+ children: [/* @__PURE__ */ jsxs("span", {
18573
+ class: "bm-label-text",
18574
+ children: ["Blocks", /* @__PURE__ */ jsx(Info, {
18575
+ label: "About site JSON-LD",
18576
+ text: "Added to every page. A page's own JSON-LD replaces only the generated page markup, and a page block with the same @id wins over the site's. Paste an object, an array of objects, or a whole script tag."
18577
+ })]
18578
+ }), /* @__PURE__ */ jsx("span", {
18579
+ class: "bm-meta-tag",
18580
+ children: parsed.ok ? `${count} ${count === 1 ? "block" : "blocks"}` : "invalid"
18581
+ })]
18582
+ }),
18583
+ /* @__PURE__ */ jsx("textarea", {
18584
+ class: "bm-mono bm-seo-json",
18585
+ rows: 14,
18586
+ spellcheck: false,
18587
+ autocomplete: "off",
18588
+ placeholder: "{\n \"@context\": \"https://schema.org\",\n \"@type\": \"Organization\",\n \"name\": \"Example\"\n}",
18589
+ "aria-invalid": !parsed.ok,
18590
+ value: jsonText,
18591
+ onInput: (event) => setJsonText(event.currentTarget.value)
18592
+ }),
18593
+ !parsed.ok && /* @__PURE__ */ jsx("span", {
18594
+ class: "bm-error",
18595
+ role: "alert",
18596
+ children: parsed.error
18597
+ }),
18598
+ warnings.map((warning) => /* @__PURE__ */ jsx("span", {
18599
+ class: "bm-hint",
18600
+ children: warning
18601
+ }, warning))
18602
+ ]
18603
+ }),
18604
+ /* @__PURE__ */ jsx("button", {
18605
+ type: "button",
18606
+ class: "bm-tertiary bm-seo-format",
18607
+ disabled: !parsed.ok || parsed.value.length === 0,
18608
+ onClick: () => parsed.ok && setJsonText(formatJsonLd(parsed.value)),
18609
+ children: "Format JSON"
18610
+ })
18611
+ ]
18612
+ })]
18613
+ }),
18614
+ /* @__PURE__ */ jsxs("div", {
18615
+ class: "bm-actions",
18616
+ children: [/* @__PURE__ */ jsx("button", {
18617
+ type: "button",
18618
+ class: "bm-primary",
18619
+ disabled: !parsed.ok,
18620
+ onClick: () => parsed.ok && onApply(parsed.value),
18621
+ children: "Apply"
18622
+ }), /* @__PURE__ */ jsx("button", {
18623
+ type: "button",
18624
+ class: "bm-tertiary",
18625
+ onClick: onClose,
18626
+ children: "Cancel"
18627
+ })]
18628
+ })
18629
+ ]
18630
+ })
18631
+ });
18632
+ }
18633
+ //#endregion
18471
18634
  //#region src/ui/dialogs/editor-dialogs.tsx
18472
18635
  function parseJson(text) {
18473
18636
  if (!text) return null;
@@ -18598,6 +18761,19 @@ function SessionDialog({ editor, dialog }) {
18598
18761
  onClose: close
18599
18762
  });
18600
18763
  }
18764
+ case "siteJsonLd": {
18765
+ const site = doc.site.value;
18766
+ if (!site) return null;
18767
+ return /* @__PURE__ */ jsx(SiteJsonLdDialog, {
18768
+ blocks: siteJsonLdOf(site),
18769
+ source: siteJsonLdSourceOf(site),
18770
+ onApply: (blocks) => {
18771
+ edits.writeField("seo.jsonLd", blocks.length > 0 ? blocks : void 0);
18772
+ close();
18773
+ },
18774
+ onClose: close
18775
+ });
18776
+ }
18601
18777
  case "discard": return /* @__PURE__ */ jsx(ConfirmDialog, {
18602
18778
  label: "Discard changes",
18603
18779
  body: "Throw away every unsaved change to this page and the site, and reload both from the saved files?",
@@ -21312,7 +21488,7 @@ function siteOptionControl(site, option, onField) {
21312
21488
  onChange: (value) => onField(option.name, value)
21313
21489
  }, option.name);
21314
21490
  }
21315
- function SiteForm({ site, options, images, onField, onOptional, onConsentEnabled, onEditConsent, onMarks, onUploaded, onDeleted }) {
21491
+ function SiteForm({ site, options, images, onField, onOptional, onConsentEnabled, onEditConsent, onEditJsonLd, onMarks, onUploaded, onDeleted }) {
21316
21492
  const logo = getAt(site, "favicon") ?? getAt(site, "logo");
21317
21493
  const groupOf = (option) => SITE_GROUPS.find((group) => option.name.startsWith(group.prefix));
21318
21494
  const shown = options.filter((option) => !option.name.startsWith("footer.") && !option.name.startsWith("header.") && option.name !== "theme.mode");
@@ -21392,6 +21568,10 @@ function SiteForm({ site, options, images, onField, onOptional, onConsentEnabled
21392
21568
  })
21393
21569
  ]
21394
21570
  }),
21571
+ /* @__PURE__ */ jsx(JsonLdGroup, {
21572
+ site,
21573
+ onEdit: onEditJsonLd
21574
+ }),
21395
21575
  /* @__PURE__ */ jsxs("div", {
21396
21576
  class: "bm-stack",
21397
21577
  children: [
@@ -21512,6 +21692,33 @@ function ConsentGroup({ enabled, snippets, onEnabled, onEdit }) {
21512
21692
  ]
21513
21693
  });
21514
21694
  }
21695
+ /** Structured data on every page: a summary here, the blocks in a dialog. */
21696
+ function JsonLdGroup({ site, onEdit }) {
21697
+ const blocks = siteJsonLdOf(site);
21698
+ const types = blocks.map((block) => typeof block["@type"] === "string" ? block["@type"] : "block");
21699
+ return /* @__PURE__ */ jsxs("div", {
21700
+ class: "bm-stack",
21701
+ children: [
21702
+ /* @__PURE__ */ jsx("p", {
21703
+ class: "bm-group",
21704
+ children: "Structured data"
21705
+ }),
21706
+ /* @__PURE__ */ jsx("p", {
21707
+ class: "bm-hint",
21708
+ children: blocks.length === 0 ? "None. Add an Organization and a WebSite so search engines know who runs the site." : `On every page: ${types.join(", ")}.`
21709
+ }),
21710
+ /* @__PURE__ */ jsxs("button", {
21711
+ type: "button",
21712
+ class: "bm-tertiary bm-consent-edit",
21713
+ onClick: onEdit,
21714
+ children: [/* @__PURE__ */ jsx(Icon, {
21715
+ name: "settings",
21716
+ size: 14
21717
+ }), "Edit JSON-LD"]
21718
+ })
21719
+ ]
21720
+ });
21721
+ }
21515
21722
  //#endregion
21516
21723
  //#region src/ui/forms/footer-form.tsx
21517
21724
  /** Footer tab: layout, text, and the columns with their links. */
@@ -22430,6 +22637,9 @@ function SitePanel({ site, catalog }) {
22430
22637
  onEditConsent: () => {
22431
22638
  ui.dialog.value = { kind: "consent" };
22432
22639
  },
22640
+ onEditJsonLd: () => {
22641
+ ui.dialog.value = { kind: "siteJsonLd" };
22642
+ },
22433
22643
  onMarks: edits.applyMarksAt,
22434
22644
  onUploaded: doc.addImage,
22435
22645
  onDeleted: doc.dropImage
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bearminion",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "A git-based, no-database CMS for Astro: JSON content in your repo, edited inline during astro dev.",
5
5
  "license": "MIT",
6
6
  "author": "Jakub Lange",
@@ -71,8 +71,8 @@
71
71
  "devDependencies": {
72
72
  "@preact/signals": "2.11.3",
73
73
  "@types/node": "^22.19.0",
74
- "astro": "^7.3.8",
75
- "happy-dom": "^21.0.1",
74
+ "astro": "^7.4.1",
75
+ "happy-dom": "^21.0.3",
76
76
  "prosemirror-commands": "^1.7.2",
77
77
  "prosemirror-history": "^1.5.1",
78
78
  "prosemirror-keymap": "^1.2.3",
@@ -83,7 +83,7 @@
83
83
  "tsdown": "0.23.1",
84
84
  "typescript": "~5.9.3",
85
85
  "vite": "^8.3.1",
86
- "vitest": "^5.0.4",
86
+ "vitest": "^5.0.5",
87
87
  "zod": "^4.6.7"
88
88
  },
89
89
  "scripts": {