@vectoriox/iox-ui 4.38.0 → 4.40.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.
@@ -350,7 +350,9 @@ function buildSeoSchemaProperties() {
350
350
  structuredData: {
351
351
  type: 'object',
352
352
  label: 'Structured Data (JSON-LD)',
353
- controller: { type: 'json' },
353
+ // `code`, not `json`: the client registers `code` (CodeMirror) and has no `json`
354
+ // controller, so a `json` declaration silently falls back to a plain text box.
355
+ controller: { type: 'code', options: { language: 'json' } },
354
356
  },
355
357
  sitemap: {
356
358
  type: 'object',
@@ -1 +1 @@
1
- {"version":3,"file":"vectoriox-iox-ui-seo-core.mjs","sources":["../../../projects/iox-ui/src/seo-core/seo.model.ts","../../../projects/iox-ui/src/seo-core/normalize.ts","../../../projects/iox-ui/src/seo-core/tags.ts","../../../projects/iox-ui/src/seo-core/schema.ts","../../../projects/iox-ui/src/seo-core/public-api.ts","../../../projects/iox-ui/src/seo-core/vectoriox-iox-ui-seo-core.ts"],"sourcesContent":["/**\n * The canonical shape of a page's SEO metadata, plus the flat tag list both renderers emit.\n *\n * ⚠️ This file is the ONLY declaration of that shape. Before it existed, `iox-cms` stored\n * `seo.og.title` while the SSR engine read a flat `seo.ogTitle`, so no Open Graph tag had ever been\n * emitted, and `iox-cms-client` carried a third, hand-copied variant of the same schema. See\n * iox-ai-guidance `architecture/cms/seo-plan.md`.\n */\n\n/** Open Graph subset we author and emit. */\nexport interface SeoOg {\n title?: string;\n description?: string;\n image?: string;\n}\n\n/** Twitter-card subset. Emitted only when `card` is set — see `seoToTags`. */\nexport interface SeoTwitter {\n card?: string;\n title?: string;\n description?: string;\n image?: string;\n}\n\n/** Sitemap participation for this page. Consumed by the sitemap route (plan phase 3). */\nexport interface SeoSitemap {\n include?: boolean;\n priority?: number;\n changeFreq?: string;\n}\n\n/**\n * A page's stored SEO metadata, normalized.\n *\n * `keywords` has no authoring field — search engines have ignored the tag for over a decade — but\n * it is read and emitted when a document already carries one, because the engine used to read it.\n */\nexport interface SeoDoc {\n title?: string;\n description?: string;\n keywords?: string;\n canonicalUrl?: string;\n robots?: string;\n og?: SeoOg;\n twitter?: SeoTwitter;\n structuredData?: unknown;\n sitemap?: SeoSitemap;\n}\n\n/**\n * A binding on an SEO field. Structurally the resolvable part of an `IoxBinding` — `source` picks an\n * entry in the page's resolved data (`$current` on a template page), `path` walks into it, `pipes`\n * are the render-time transforms. Typed loosely because the pipe catalog lives in the main\n * `@vectoriox/iox-ui` entry point, which this framework-free one must not import.\n */\nexport interface SeoBinding {\n source: string;\n path?: string;\n pipes?: readonly unknown[];\n}\n\n/**\n * One SEO field as stored once it carries more than a typed string.\n *\n * The ladder is `override` → `binding` → auto; `auto: false` opts that field out of the automatic\n * value, leaving it genuinely empty. A bare `string` in a stored document means `{ override }`, so\n * nothing has to be migrated.\n */\nexport interface SeoFieldValue {\n override?: string;\n binding?: SeoBinding;\n auto?: false;\n}\n\n/** A stored SEO document, before resolution: any text field may be a plain string or a value object. */\nexport type SeoRawDoc = Record<string, unknown>;\n\n/**\n * The text fields that can carry a binding, as dotted paths into the document.\n *\n * One list, used by the resolver and (phase 2) by the panel that draws the bind affordance — so a\n * field can never be bindable in the UI and ignored at render time. `robots`, `sitemap.*` and\n * `structuredData` are deliberately absent: they are pick-lists and blobs, not prose.\n */\nexport const SEO_BINDABLE_FIELDS: readonly string[] = [\n 'title',\n 'description',\n 'canonicalUrl',\n 'og.title',\n 'og.description',\n 'og.image',\n 'twitter.title',\n 'twitter.description',\n 'twitter.image',\n];\n\n/** Where an automatic value comes from when a field has neither an override nor a binding. */\nexport type SeoAutoSource = 'contentTitle' | 'contentDescription' | 'contentImage' | 'pageUrl';\n\n/**\n * The bottom rung of the ladder, as a table rather than a chain of ifs.\n *\n * Deliberately short. `og:title` and `twitter:*` are NOT here — `seoToTags` already falls back to\n * the page title for them, and materialising a copy of a value we did not author would only make\n * later drift harder to see.\n */\nexport const SEO_AUTO_SOURCES: ReadonlyArray<{ field: string; source: SeoAutoSource }> = [\n { field: 'title', source: 'contentTitle' },\n { field: 'description', source: 'contentDescription' },\n { field: 'og.image', source: 'contentImage' },\n { field: 'canonicalUrl', source: 'pageUrl' },\n];\n\n/** `<title>`. */\nexport interface SeoTitleTag {\n kind: 'title';\n content: string;\n}\n\n/**\n * A `<meta>` tag. `attr` says which attribute identifies it — Open Graph is addressed by\n * `property`, everything else by `name`. Both renderers need that distinction to update rather\n * than duplicate a tag.\n */\nexport interface SeoMetaTag {\n kind: 'meta';\n attr: 'name' | 'property';\n key: string;\n content: string;\n}\n\n/** A `<link>`, currently only `rel=\"canonical\"`. */\nexport interface SeoLinkTag {\n kind: 'link';\n rel: string;\n href: string;\n}\n\nexport type SeoTag = SeoTitleTag | SeoMetaTag | SeoLinkTag;\n\n/** Context that lets `seoToTags` fill gaps without reaching for the DOM or a data source. */\nexport interface SeoTagContext {\n /** Used as `<title>` when the document has none — usually the page name. */\n fallbackTitle?: string;\n}\n\n/** Recommended maximum before search results truncate the title. */\nexport const SEO_TITLE_MAX = 60;\n\n/** Recommended maximum before search results truncate the description. */\nexport const SEO_DESCRIPTION_MAX = 160;\n\nexport const SEO_ROBOTS_OPTIONS: ReadonlyArray<{ label: string; value: string }> = [\n { label: 'Index, Follow', value: 'index, follow' },\n { label: 'Index, No Follow', value: 'index, nofollow' },\n { label: 'No Index, Follow', value: 'noindex, follow' },\n { label: 'No Index, No Follow', value: 'noindex, nofollow' },\n];\n\nexport const SEO_TWITTER_CARD_OPTIONS: ReadonlyArray<{ label: string; value: string }> = [\n { label: 'Summary', value: 'summary' },\n { label: 'Summary Large Image', value: 'summary_large_image' },\n { label: 'App', value: 'app' },\n { label: 'Player', value: 'player' },\n];\n\nexport const SEO_CHANGE_FREQ_OPTIONS: ReadonlyArray<{ label: string; value: string }> = [\n { label: 'Always', value: 'always' },\n { label: 'Hourly', value: 'hourly' },\n { label: 'Daily', value: 'daily' },\n { label: 'Weekly', value: 'weekly' },\n { label: 'Monthly', value: 'monthly' },\n { label: 'Yearly', value: 'yearly' },\n { label: 'Never', value: 'never' },\n];\n","import { SeoDoc, SeoOg, SeoSitemap, SeoTwitter } from './seo.model';\n\n/**\n * Coerce whatever a page document happens to hold into a `SeoDoc`.\n *\n * Every consumer calls this before reading `seo`, because four shapes are in the wild:\n * • the current nested shape (`og.title`);\n * • the flat shape the SSR engine used to read (`ogTitle`) — never actually written, but a\n * document hand-edited against the old engine code could carry it;\n * • `\"\"` for \"not set\", which must not become an empty `<meta>`;\n * • `{ override: '…' }`, the per-field value object that plan phase 2 introduces — tolerated here\n * so the ladder can land without a second normalizer.\n *\n * Empty branches are dropped rather than left as `{}`, so `if (seo.og)` is a meaningful test.\n */\nexport function normalizeSeoDoc(raw: unknown): SeoDoc {\n const src = asRecord(raw);\n if (!src) return {};\n\n const og = compact<SeoOg>({\n title: seoString(pick(src, 'og', 'title') ?? src['ogTitle']),\n description: seoString(pick(src, 'og', 'description') ?? src['ogDescription']),\n image: seoString(pick(src, 'og', 'image') ?? src['ogImage']),\n });\n\n const twitter = compact<SeoTwitter>({\n card: seoString(pick(src, 'twitter', 'card')),\n title: seoString(pick(src, 'twitter', 'title')),\n description: seoString(pick(src, 'twitter', 'description')),\n image: seoString(pick(src, 'twitter', 'image')),\n });\n\n const sitemap = compact<SeoSitemap>({\n include: bool(pick(src, 'sitemap', 'include')),\n priority: num(pick(src, 'sitemap', 'priority')),\n changeFreq: seoString(pick(src, 'sitemap', 'changeFreq')),\n });\n\n const structuredData = structured(src['structuredData']);\n\n const doc = compact<SeoDoc>({\n title: seoString(src['title']),\n description: seoString(src['description']),\n keywords: seoString(src['keywords']),\n canonicalUrl: seoString(src['canonicalUrl']),\n robots: seoString(src['robots']),\n og,\n twitter,\n sitemap,\n structuredData,\n });\n\n return doc ?? {};\n}\n\n/** Read `src[branch][leaf]` without assuming the branch exists or is an object. */\nfunction pick(src: Record<string, unknown>, branch: string, leaf: string): unknown {\n const nested = asRecord(src[branch]);\n return nested ? nested[leaf] : undefined;\n}\n\n/**\n * A field's value as a trimmed string, or `undefined`. Exported because the resolver needs the exact\n * same coercion for a value it has just pulled out of a data source.\n *\n * Unwraps the value object: a bound or auto field has no `override`, so it correctly resolves to\n * `undefined` here and `resolveSeo` fills it in from the lower rungs.\n */\nexport function seoString(value: unknown): string | undefined {\n const wrapper = asRecord(value);\n const raw = wrapper ? wrapper['override'] : value;\n if (typeof raw === 'number') return String(raw);\n if (typeof raw !== 'string') return undefined;\n const trimmed = raw.trim();\n return trimmed.length ? trimmed : undefined;\n}\n\n/**\n * `UIFormToDTO` can hand back `\"true\"` / `\"false\"` for a switch (see\n * `architecture/cms/shop.md` — the boolean/number coercion pitfall), so accept both.\n */\nfunction bool(value: unknown): boolean | undefined {\n if (typeof value === 'boolean') return value;\n if (value === 'true') return true;\n if (value === 'false') return false;\n return undefined;\n}\n\nfunction num(value: unknown): number | undefined {\n if (typeof value === 'number') return Number.isFinite(value) ? value : undefined;\n if (typeof value !== 'string' || !value.trim().length) return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\n/** JSON-LD is stored as an object or as a JSON string from the `json` controller. */\nfunction structured(value: unknown): unknown {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (!trimmed.length) return undefined;\n try {\n return JSON.parse(trimmed);\n } catch {\n return undefined;\n }\n }\n if (Array.isArray(value)) return value.length ? value : undefined;\n const record = asRecord(value);\n return record && Object.keys(record).length ? record : undefined;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\n/** Drop `undefined` values and empty nested objects, so absence is always `undefined`. */\nfunction compact<T extends object>(value: Record<string, unknown>): T | undefined {\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (entry === undefined) continue;\n out[key] = entry;\n }\n return (Object.keys(out).length ? out : undefined) as T | undefined;\n}\n","import { SeoDoc, SeoTag, SeoTagContext } from './seo.model';\nimport { normalizeSeoDoc } from './normalize';\n\n/**\n * Turn a page's SEO document into the flat list of head tags to emit.\n *\n * Both renderers call this, so a page cannot describe itself differently to a crawler than the\n * builder previews. The order is fixed and part of the contract: title, description, keywords,\n * canonical, Open Graph, Twitter, robots.\n *\n * Fallbacks are deliberately shallow — `og:title` reuses the page title, `twitter:*` reuses the\n * Open Graph values — because a social scraper that finds no `og:title` falls back to `<title>`\n * itself, and duplicating a value we did not author only makes the drift harder to see. The\n * \"derive from the page content\" half of the ladder (first h1, first paragraph, first image) is\n * `resolveSeo`, which runs BEFORE this and hands its output in as a filled-in `SeoDoc`.\n */\nexport function seoToTags(seo: SeoDoc | unknown, ctx: SeoTagContext = {}): SeoTag[] {\n const doc = normalizeSeoDoc(seo);\n const tags: SeoTag[] = [];\n\n const title = doc.title ?? trimmed(ctx.fallbackTitle);\n if (title) tags.push({ kind: 'title', content: title });\n if (doc.description) tags.push({ kind: 'meta', attr: 'name', key: 'description', content: doc.description });\n if (doc.keywords) tags.push({ kind: 'meta', attr: 'name', key: 'keywords', content: doc.keywords });\n if (doc.canonicalUrl) tags.push({ kind: 'link', rel: 'canonical', href: doc.canonicalUrl });\n\n // Open Graph. `og:url` reuses the canonical URL, which is what it means.\n const ogTitle = doc.og?.title ?? title;\n const ogDescription = doc.og?.description ?? doc.description;\n if (ogTitle) tags.push({ kind: 'meta', attr: 'property', key: 'og:title', content: ogTitle });\n if (ogDescription) tags.push({ kind: 'meta', attr: 'property', key: 'og:description', content: ogDescription });\n if (doc.og?.image) tags.push({ kind: 'meta', attr: 'property', key: 'og:image', content: doc.og.image });\n if (doc.canonicalUrl) tags.push({ kind: 'meta', attr: 'property', key: 'og:url', content: doc.canonicalUrl });\n\n // Twitter. Emitted ONLY when a card type is chosen: without `twitter:card` the other tags are\n // ignored anyway, so writing them would be noise that looks like configuration.\n if (doc.twitter?.card) {\n tags.push({ kind: 'meta', attr: 'name', key: 'twitter:card', content: doc.twitter.card });\n const twitterTitle = doc.twitter.title ?? ogTitle;\n const twitterDescription = doc.twitter.description ?? ogDescription;\n const twitterImage = doc.twitter.image ?? doc.og?.image;\n if (twitterTitle) tags.push({ kind: 'meta', attr: 'name', key: 'twitter:title', content: twitterTitle });\n if (twitterDescription) {\n tags.push({ kind: 'meta', attr: 'name', key: 'twitter:description', content: twitterDescription });\n }\n if (twitterImage) tags.push({ kind: 'meta', attr: 'name', key: 'twitter:image', content: twitterImage });\n }\n\n if (doc.robots) tags.push({ kind: 'meta', attr: 'name', key: 'robots', content: doc.robots });\n\n return tags;\n}\n\nfunction trimmed(value: string | undefined): string | undefined {\n const out = value?.trim();\n return out?.length ? out : undefined;\n}\n","import {\n SEO_CHANGE_FREQ_OPTIONS,\n SEO_DESCRIPTION_MAX,\n SEO_ROBOTS_OPTIONS,\n SEO_TITLE_MAX,\n SEO_TWITTER_CARD_OPTIONS,\n} from './seo.model';\n\n/**\n * The authoring schema for the `seo` object, in the platform's standard property dialect: `label`\n * at property level, rendering metadata under `controller`.\n *\n * ONE builder, two callers: `iox-cms` spreads it into `PagesSchema` (the stored schema) and\n * `iox-cms-client` feeds it to `<dynamic-form>`. Those used to be two hand-written copies that had\n * already drifted — different controller types for the same field, and the client's copy missing\n * `structuredData` entirely.\n *\n * `keywords` is deliberately absent: the tag is emitted when a document carries one, but there is\n * no reason to invite new ones.\n */\nexport function buildSeoSchemaProperties(): Record<string, unknown> {\n return {\n title: {\n type: 'string',\n label: 'Meta Title',\n controller: {\n type: 'text',\n placeholder: 'Page title for search engines',\n maxLength: SEO_TITLE_MAX,\n },\n },\n description: {\n type: 'string',\n label: 'Meta Description',\n controller: {\n type: 'textarea',\n placeholder: 'Brief description for search results',\n maxLength: SEO_DESCRIPTION_MAX,\n },\n },\n canonicalUrl: {\n type: 'string',\n label: 'Canonical URL',\n controller: {\n type: 'text',\n placeholder: 'https://example.com/page',\n },\n },\n robots: {\n type: 'string',\n label: 'Robots',\n controller: {\n type: 'select',\n options: SEO_ROBOTS_OPTIONS.map(option => ({ ...option })),\n },\n },\n og: {\n type: 'object',\n label: 'Open Graph',\n controller: { type: 'object' },\n properties: {\n title: {\n type: 'string',\n label: 'OG Title',\n controller: { type: 'text', placeholder: 'Title for social sharing' },\n },\n description: {\n type: 'string',\n label: 'OG Description',\n controller: { type: 'textarea', placeholder: 'Description for social sharing' },\n },\n image: {\n type: 'string',\n label: 'OG Image',\n controller: { type: 'image' },\n },\n },\n },\n twitter: {\n type: 'object',\n label: 'Twitter Card',\n controller: { type: 'object' },\n properties: {\n card: {\n type: 'string',\n label: 'Card Type',\n controller: {\n type: 'select',\n options: SEO_TWITTER_CARD_OPTIONS.map(option => ({ ...option })),\n },\n },\n title: {\n type: 'string',\n label: 'Twitter Title',\n controller: { type: 'text' },\n },\n description: {\n type: 'string',\n label: 'Twitter Description',\n controller: { type: 'textarea' },\n },\n image: {\n type: 'string',\n label: 'Twitter Image',\n controller: { type: 'image' },\n },\n },\n },\n structuredData: {\n type: 'object',\n label: 'Structured Data (JSON-LD)',\n controller: { type: 'json' },\n },\n sitemap: {\n type: 'object',\n label: 'Sitemap Settings',\n controller: { type: 'object' },\n properties: {\n include: {\n type: 'boolean',\n label: 'Include in Sitemap',\n controller: { type: 'switch' },\n },\n priority: {\n type: 'number',\n label: 'Priority',\n controller: { type: 'range', options: { min: 0, max: 1, step: 0.1 } },\n },\n changeFreq: {\n type: 'string',\n label: 'Change Frequency',\n controller: {\n type: 'select',\n options: SEO_CHANGE_FREQ_OPTIONS.map(option => ({ ...option })),\n },\n },\n },\n },\n };\n}\n","/*\n * @vectoriox/iox-ui/seo-core — the shared, FRAMEWORK-FREE core of page SEO.\n *\n * ⚠️ Nothing in this entry point may import Angular, RxJS, the DOM, or Node. It is consumed by\n * THREE runtimes: the builder panel (Angular), the SSR engine (Angular), and `iox-cms` (NestJS,\n * plain Node) — the same reason `forms-core` exists.\n *\n * A page describes itself to a crawler exactly once: `seoToTags` is the only place that decides\n * which head tags a page emits, and `buildSeoSchemaProperties` the only place that declares what an\n * author can fill in. See iox-ai-guidance `architecture/cms/seo-plan.md`.\n */\n\nexport * from './seo.model';\nexport * from './normalize';\nexport * from './tags';\nexport * from './schema';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vectoriox-iox-ui-seo-core.mjs","sources":["../../../projects/iox-ui/src/seo-core/seo.model.ts","../../../projects/iox-ui/src/seo-core/normalize.ts","../../../projects/iox-ui/src/seo-core/tags.ts","../../../projects/iox-ui/src/seo-core/schema.ts","../../../projects/iox-ui/src/seo-core/public-api.ts","../../../projects/iox-ui/src/seo-core/vectoriox-iox-ui-seo-core.ts"],"sourcesContent":["/**\n * The canonical shape of a page's SEO metadata, plus the flat tag list both renderers emit.\n *\n * ⚠️ This file is the ONLY declaration of that shape. Before it existed, `iox-cms` stored\n * `seo.og.title` while the SSR engine read a flat `seo.ogTitle`, so no Open Graph tag had ever been\n * emitted, and `iox-cms-client` carried a third, hand-copied variant of the same schema. See\n * iox-ai-guidance `architecture/cms/seo-plan.md`.\n */\n\n/** Open Graph subset we author and emit. */\nexport interface SeoOg {\n title?: string;\n description?: string;\n image?: string;\n}\n\n/** Twitter-card subset. Emitted only when `card` is set — see `seoToTags`. */\nexport interface SeoTwitter {\n card?: string;\n title?: string;\n description?: string;\n image?: string;\n}\n\n/** Sitemap participation for this page. Consumed by the sitemap route (plan phase 3). */\nexport interface SeoSitemap {\n include?: boolean;\n priority?: number;\n changeFreq?: string;\n}\n\n/**\n * A page's stored SEO metadata, normalized.\n *\n * `keywords` has no authoring field — search engines have ignored the tag for over a decade — but\n * it is read and emitted when a document already carries one, because the engine used to read it.\n */\nexport interface SeoDoc {\n title?: string;\n description?: string;\n keywords?: string;\n canonicalUrl?: string;\n robots?: string;\n og?: SeoOg;\n twitter?: SeoTwitter;\n structuredData?: unknown;\n sitemap?: SeoSitemap;\n}\n\n/**\n * A binding on an SEO field. Structurally the resolvable part of an `IoxBinding` — `source` picks an\n * entry in the page's resolved data (`$current` on a template page), `path` walks into it, `pipes`\n * are the render-time transforms. Typed loosely because the pipe catalog lives in the main\n * `@vectoriox/iox-ui` entry point, which this framework-free one must not import.\n */\nexport interface SeoBinding {\n source: string;\n path?: string;\n pipes?: readonly unknown[];\n}\n\n/**\n * One SEO field as stored once it carries more than a typed string.\n *\n * The ladder is `override` → `binding` → auto; `auto: false` opts that field out of the automatic\n * value, leaving it genuinely empty. A bare `string` in a stored document means `{ override }`, so\n * nothing has to be migrated.\n */\nexport interface SeoFieldValue {\n override?: string;\n binding?: SeoBinding;\n auto?: false;\n}\n\n/** A stored SEO document, before resolution: any text field may be a plain string or a value object. */\nexport type SeoRawDoc = Record<string, unknown>;\n\n/**\n * The text fields that can carry a binding, as dotted paths into the document.\n *\n * One list, used by the resolver and (phase 2) by the panel that draws the bind affordance — so a\n * field can never be bindable in the UI and ignored at render time. `robots`, `sitemap.*` and\n * `structuredData` are deliberately absent: they are pick-lists and blobs, not prose.\n */\nexport const SEO_BINDABLE_FIELDS: readonly string[] = [\n 'title',\n 'description',\n 'canonicalUrl',\n 'og.title',\n 'og.description',\n 'og.image',\n 'twitter.title',\n 'twitter.description',\n 'twitter.image',\n];\n\n/** Where an automatic value comes from when a field has neither an override nor a binding. */\nexport type SeoAutoSource = 'contentTitle' | 'contentDescription' | 'contentImage' | 'pageUrl';\n\n/**\n * The bottom rung of the ladder, as a table rather than a chain of ifs.\n *\n * Deliberately short. `og:title` and `twitter:*` are NOT here — `seoToTags` already falls back to\n * the page title for them, and materialising a copy of a value we did not author would only make\n * later drift harder to see.\n */\nexport const SEO_AUTO_SOURCES: ReadonlyArray<{ field: string; source: SeoAutoSource }> = [\n { field: 'title', source: 'contentTitle' },\n { field: 'description', source: 'contentDescription' },\n { field: 'og.image', source: 'contentImage' },\n { field: 'canonicalUrl', source: 'pageUrl' },\n];\n\n/** `<title>`. */\nexport interface SeoTitleTag {\n kind: 'title';\n content: string;\n}\n\n/**\n * A `<meta>` tag. `attr` says which attribute identifies it — Open Graph is addressed by\n * `property`, everything else by `name`. Both renderers need that distinction to update rather\n * than duplicate a tag.\n */\nexport interface SeoMetaTag {\n kind: 'meta';\n attr: 'name' | 'property';\n key: string;\n content: string;\n}\n\n/** A `<link>`, currently only `rel=\"canonical\"`. */\nexport interface SeoLinkTag {\n kind: 'link';\n rel: string;\n href: string;\n}\n\nexport type SeoTag = SeoTitleTag | SeoMetaTag | SeoLinkTag;\n\n/** Context that lets `seoToTags` fill gaps without reaching for the DOM or a data source. */\nexport interface SeoTagContext {\n /** Used as `<title>` when the document has none — usually the page name. */\n fallbackTitle?: string;\n}\n\n/** Recommended maximum before search results truncate the title. */\nexport const SEO_TITLE_MAX = 60;\n\n/** Recommended maximum before search results truncate the description. */\nexport const SEO_DESCRIPTION_MAX = 160;\n\nexport const SEO_ROBOTS_OPTIONS: ReadonlyArray<{ label: string; value: string }> = [\n { label: 'Index, Follow', value: 'index, follow' },\n { label: 'Index, No Follow', value: 'index, nofollow' },\n { label: 'No Index, Follow', value: 'noindex, follow' },\n { label: 'No Index, No Follow', value: 'noindex, nofollow' },\n];\n\nexport const SEO_TWITTER_CARD_OPTIONS: ReadonlyArray<{ label: string; value: string }> = [\n { label: 'Summary', value: 'summary' },\n { label: 'Summary Large Image', value: 'summary_large_image' },\n { label: 'App', value: 'app' },\n { label: 'Player', value: 'player' },\n];\n\nexport const SEO_CHANGE_FREQ_OPTIONS: ReadonlyArray<{ label: string; value: string }> = [\n { label: 'Always', value: 'always' },\n { label: 'Hourly', value: 'hourly' },\n { label: 'Daily', value: 'daily' },\n { label: 'Weekly', value: 'weekly' },\n { label: 'Monthly', value: 'monthly' },\n { label: 'Yearly', value: 'yearly' },\n { label: 'Never', value: 'never' },\n];\n","import { SeoDoc, SeoOg, SeoSitemap, SeoTwitter } from './seo.model';\n\n/**\n * Coerce whatever a page document happens to hold into a `SeoDoc`.\n *\n * Every consumer calls this before reading `seo`, because four shapes are in the wild:\n * • the current nested shape (`og.title`);\n * • the flat shape the SSR engine used to read (`ogTitle`) — never actually written, but a\n * document hand-edited against the old engine code could carry it;\n * • `\"\"` for \"not set\", which must not become an empty `<meta>`;\n * • `{ override: '…' }`, the per-field value object that plan phase 2 introduces — tolerated here\n * so the ladder can land without a second normalizer.\n *\n * Empty branches are dropped rather than left as `{}`, so `if (seo.og)` is a meaningful test.\n */\nexport function normalizeSeoDoc(raw: unknown): SeoDoc {\n const src = asRecord(raw);\n if (!src) return {};\n\n const og = compact<SeoOg>({\n title: seoString(pick(src, 'og', 'title') ?? src['ogTitle']),\n description: seoString(pick(src, 'og', 'description') ?? src['ogDescription']),\n image: seoString(pick(src, 'og', 'image') ?? src['ogImage']),\n });\n\n const twitter = compact<SeoTwitter>({\n card: seoString(pick(src, 'twitter', 'card')),\n title: seoString(pick(src, 'twitter', 'title')),\n description: seoString(pick(src, 'twitter', 'description')),\n image: seoString(pick(src, 'twitter', 'image')),\n });\n\n const sitemap = compact<SeoSitemap>({\n include: bool(pick(src, 'sitemap', 'include')),\n priority: num(pick(src, 'sitemap', 'priority')),\n changeFreq: seoString(pick(src, 'sitemap', 'changeFreq')),\n });\n\n const structuredData = structured(src['structuredData']);\n\n const doc = compact<SeoDoc>({\n title: seoString(src['title']),\n description: seoString(src['description']),\n keywords: seoString(src['keywords']),\n canonicalUrl: seoString(src['canonicalUrl']),\n robots: seoString(src['robots']),\n og,\n twitter,\n sitemap,\n structuredData,\n });\n\n return doc ?? {};\n}\n\n/** Read `src[branch][leaf]` without assuming the branch exists or is an object. */\nfunction pick(src: Record<string, unknown>, branch: string, leaf: string): unknown {\n const nested = asRecord(src[branch]);\n return nested ? nested[leaf] : undefined;\n}\n\n/**\n * A field's value as a trimmed string, or `undefined`. Exported because the resolver needs the exact\n * same coercion for a value it has just pulled out of a data source.\n *\n * Unwraps the value object: a bound or auto field has no `override`, so it correctly resolves to\n * `undefined` here and `resolveSeo` fills it in from the lower rungs.\n */\nexport function seoString(value: unknown): string | undefined {\n const wrapper = asRecord(value);\n const raw = wrapper ? wrapper['override'] : value;\n if (typeof raw === 'number') return String(raw);\n if (typeof raw !== 'string') return undefined;\n const trimmed = raw.trim();\n return trimmed.length ? trimmed : undefined;\n}\n\n/**\n * `UIFormToDTO` can hand back `\"true\"` / `\"false\"` for a switch (see\n * `architecture/cms/shop.md` — the boolean/number coercion pitfall), so accept both.\n */\nfunction bool(value: unknown): boolean | undefined {\n if (typeof value === 'boolean') return value;\n if (value === 'true') return true;\n if (value === 'false') return false;\n return undefined;\n}\n\nfunction num(value: unknown): number | undefined {\n if (typeof value === 'number') return Number.isFinite(value) ? value : undefined;\n if (typeof value !== 'string' || !value.trim().length) return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\n/** JSON-LD is stored as an object or as a JSON string from the `json` controller. */\nfunction structured(value: unknown): unknown {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (!trimmed.length) return undefined;\n try {\n return JSON.parse(trimmed);\n } catch {\n return undefined;\n }\n }\n if (Array.isArray(value)) return value.length ? value : undefined;\n const record = asRecord(value);\n return record && Object.keys(record).length ? record : undefined;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\n/** Drop `undefined` values and empty nested objects, so absence is always `undefined`. */\nfunction compact<T extends object>(value: Record<string, unknown>): T | undefined {\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (entry === undefined) continue;\n out[key] = entry;\n }\n return (Object.keys(out).length ? out : undefined) as T | undefined;\n}\n","import { SeoDoc, SeoTag, SeoTagContext } from './seo.model';\nimport { normalizeSeoDoc } from './normalize';\n\n/**\n * Turn a page's SEO document into the flat list of head tags to emit.\n *\n * Both renderers call this, so a page cannot describe itself differently to a crawler than the\n * builder previews. The order is fixed and part of the contract: title, description, keywords,\n * canonical, Open Graph, Twitter, robots.\n *\n * Fallbacks are deliberately shallow — `og:title` reuses the page title, `twitter:*` reuses the\n * Open Graph values — because a social scraper that finds no `og:title` falls back to `<title>`\n * itself, and duplicating a value we did not author only makes the drift harder to see. The\n * \"derive from the page content\" half of the ladder (first h1, first paragraph, first image) is\n * `resolveSeo`, which runs BEFORE this and hands its output in as a filled-in `SeoDoc`.\n */\nexport function seoToTags(seo: SeoDoc | unknown, ctx: SeoTagContext = {}): SeoTag[] {\n const doc = normalizeSeoDoc(seo);\n const tags: SeoTag[] = [];\n\n const title = doc.title ?? trimmed(ctx.fallbackTitle);\n if (title) tags.push({ kind: 'title', content: title });\n if (doc.description) tags.push({ kind: 'meta', attr: 'name', key: 'description', content: doc.description });\n if (doc.keywords) tags.push({ kind: 'meta', attr: 'name', key: 'keywords', content: doc.keywords });\n if (doc.canonicalUrl) tags.push({ kind: 'link', rel: 'canonical', href: doc.canonicalUrl });\n\n // Open Graph. `og:url` reuses the canonical URL, which is what it means.\n const ogTitle = doc.og?.title ?? title;\n const ogDescription = doc.og?.description ?? doc.description;\n if (ogTitle) tags.push({ kind: 'meta', attr: 'property', key: 'og:title', content: ogTitle });\n if (ogDescription) tags.push({ kind: 'meta', attr: 'property', key: 'og:description', content: ogDescription });\n if (doc.og?.image) tags.push({ kind: 'meta', attr: 'property', key: 'og:image', content: doc.og.image });\n if (doc.canonicalUrl) tags.push({ kind: 'meta', attr: 'property', key: 'og:url', content: doc.canonicalUrl });\n\n // Twitter. Emitted ONLY when a card type is chosen: without `twitter:card` the other tags are\n // ignored anyway, so writing them would be noise that looks like configuration.\n if (doc.twitter?.card) {\n tags.push({ kind: 'meta', attr: 'name', key: 'twitter:card', content: doc.twitter.card });\n const twitterTitle = doc.twitter.title ?? ogTitle;\n const twitterDescription = doc.twitter.description ?? ogDescription;\n const twitterImage = doc.twitter.image ?? doc.og?.image;\n if (twitterTitle) tags.push({ kind: 'meta', attr: 'name', key: 'twitter:title', content: twitterTitle });\n if (twitterDescription) {\n tags.push({ kind: 'meta', attr: 'name', key: 'twitter:description', content: twitterDescription });\n }\n if (twitterImage) tags.push({ kind: 'meta', attr: 'name', key: 'twitter:image', content: twitterImage });\n }\n\n if (doc.robots) tags.push({ kind: 'meta', attr: 'name', key: 'robots', content: doc.robots });\n\n return tags;\n}\n\nfunction trimmed(value: string | undefined): string | undefined {\n const out = value?.trim();\n return out?.length ? out : undefined;\n}\n","import {\n SEO_CHANGE_FREQ_OPTIONS,\n SEO_DESCRIPTION_MAX,\n SEO_ROBOTS_OPTIONS,\n SEO_TITLE_MAX,\n SEO_TWITTER_CARD_OPTIONS,\n} from './seo.model';\n\n/**\n * The authoring schema for the `seo` object, in the platform's standard property dialect: `label`\n * at property level, rendering metadata under `controller`.\n *\n * ONE builder, two callers: `iox-cms` spreads it into `PagesSchema` (the stored schema) and\n * `iox-cms-client` feeds it to `<dynamic-form>`. Those used to be two hand-written copies that had\n * already drifted — different controller types for the same field, and the client's copy missing\n * `structuredData` entirely.\n *\n * `keywords` is deliberately absent: the tag is emitted when a document carries one, but there is\n * no reason to invite new ones.\n */\nexport function buildSeoSchemaProperties(): Record<string, unknown> {\n return {\n title: {\n type: 'string',\n label: 'Meta Title',\n controller: {\n type: 'text',\n placeholder: 'Page title for search engines',\n maxLength: SEO_TITLE_MAX,\n },\n },\n description: {\n type: 'string',\n label: 'Meta Description',\n controller: {\n type: 'textarea',\n placeholder: 'Brief description for search results',\n maxLength: SEO_DESCRIPTION_MAX,\n },\n },\n canonicalUrl: {\n type: 'string',\n label: 'Canonical URL',\n controller: {\n type: 'text',\n placeholder: 'https://example.com/page',\n },\n },\n robots: {\n type: 'string',\n label: 'Robots',\n controller: {\n type: 'select',\n options: SEO_ROBOTS_OPTIONS.map(option => ({ ...option })),\n },\n },\n og: {\n type: 'object',\n label: 'Open Graph',\n controller: { type: 'object' },\n properties: {\n title: {\n type: 'string',\n label: 'OG Title',\n controller: { type: 'text', placeholder: 'Title for social sharing' },\n },\n description: {\n type: 'string',\n label: 'OG Description',\n controller: { type: 'textarea', placeholder: 'Description for social sharing' },\n },\n image: {\n type: 'string',\n label: 'OG Image',\n controller: { type: 'image' },\n },\n },\n },\n twitter: {\n type: 'object',\n label: 'Twitter Card',\n controller: { type: 'object' },\n properties: {\n card: {\n type: 'string',\n label: 'Card Type',\n controller: {\n type: 'select',\n options: SEO_TWITTER_CARD_OPTIONS.map(option => ({ ...option })),\n },\n },\n title: {\n type: 'string',\n label: 'Twitter Title',\n controller: { type: 'text' },\n },\n description: {\n type: 'string',\n label: 'Twitter Description',\n controller: { type: 'textarea' },\n },\n image: {\n type: 'string',\n label: 'Twitter Image',\n controller: { type: 'image' },\n },\n },\n },\n structuredData: {\n type: 'object',\n label: 'Structured Data (JSON-LD)',\n // `code`, not `json`: the client registers `code` (CodeMirror) and has no `json`\n // controller, so a `json` declaration silently falls back to a plain text box.\n controller: { type: 'code', options: { language: 'json' } },\n },\n sitemap: {\n type: 'object',\n label: 'Sitemap Settings',\n controller: { type: 'object' },\n properties: {\n include: {\n type: 'boolean',\n label: 'Include in Sitemap',\n controller: { type: 'switch' },\n },\n priority: {\n type: 'number',\n label: 'Priority',\n controller: { type: 'range', options: { min: 0, max: 1, step: 0.1 } },\n },\n changeFreq: {\n type: 'string',\n label: 'Change Frequency',\n controller: {\n type: 'select',\n options: SEO_CHANGE_FREQ_OPTIONS.map(option => ({ ...option })),\n },\n },\n },\n },\n };\n}\n","/*\n * @vectoriox/iox-ui/seo-core — the shared, FRAMEWORK-FREE core of page SEO.\n *\n * ⚠️ Nothing in this entry point may import Angular, RxJS, the DOM, or Node. It is consumed by\n * THREE runtimes: the builder panel (Angular), the SSR engine (Angular), and `iox-cms` (NestJS,\n * plain Node) — the same reason `forms-core` exists.\n *\n * A page describes itself to a crawler exactly once: `seoToTags` is the only place that decides\n * which head tags a page emits, and `buildSeoSchemaProperties` the only place that declares what an\n * author can fill in. See iox-ai-guidance `architecture/cms/seo-plan.md`.\n */\n\nexport * from './seo.model';\nexport * from './normalize';\nexport * from './tags';\nexport * from './schema';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -5162,6 +5162,58 @@ function buildTargetedStateSelector(scopeCssId, triggerCssId, styledCssId, base)
5162
5162
  return `iox-node-${scopeCssId}:has(.iox-node-${triggerCssId}:${base}) .iox-node-${styledCssId}`;
5163
5163
  }
5164
5164
 
5165
+ /**
5166
+ * page-style — the ONE compiler that turns a page's style settings into CSS.
5167
+ *
5168
+ * A page's background / typography is authored once (builder Page panel) and has to render
5169
+ * in BOTH renderers: the builder canvas frame and the SSR engine's `<body>`. Before this
5170
+ * existed the engine applied it with hand-written `body.style.x = …` assignments buried in
5171
+ * the FONT LOADER, and the builder applied nothing at all — so every background an author
5172
+ * set was invisible until the site was published. See
5173
+ * iox-ai-guidance `architecture/builder/render-parity.md`.
5174
+ *
5175
+ * Pure and transport-agnostic on purpose: it returns CSS declarations and nothing else.
5176
+ * Each consumer applies them its own way (engine → `document.body`, builder → the canvas
5177
+ * viewport element). Kebab-case keys so the result works with both `el.style.setProperty`
5178
+ * and Angular's `[ngStyle]`.
5179
+ */
5180
+ /** Background fallback when a gradient/image type is selected but its value is empty. */
5181
+ const EMPTY_BACKGROUND = '#fff';
5182
+ /**
5183
+ * Compile page style settings into CSS declarations.
5184
+ *
5185
+ * Only keys the author actually set are emitted, so a consumer's own default (the builder's
5186
+ * white canvas, the engine's stylesheet) shows through untouched when a page says nothing.
5187
+ *
5188
+ * `backgroundType` selects which background field wins — it is derived from the author's
5189
+ * colour/image choice rather than picked directly (see the builder's page-background.util).
5190
+ */
5191
+ function compilePageStyle(style) {
5192
+ if (!style)
5193
+ return {};
5194
+ const css = {};
5195
+ if (style.fontFamily)
5196
+ css['font-family'] = style.fontFamily;
5197
+ if (style.textColor)
5198
+ css['color'] = style.textColor;
5199
+ switch (style.backgroundType) {
5200
+ case 'gradient':
5201
+ css['background'] = style.backgroundGradient || EMPTY_BACKGROUND;
5202
+ break;
5203
+ case 'image':
5204
+ css['background'] = style.backgroundImage
5205
+ ? `url(${style.backgroundImage}) center/cover no-repeat`
5206
+ : EMPTY_BACKGROUND;
5207
+ break;
5208
+ default:
5209
+ // 'solid' and legacy pages with no type at all.
5210
+ if (style.backgroundColor)
5211
+ css['background-color'] = style.backgroundColor;
5212
+ break;
5213
+ }
5214
+ return css;
5215
+ }
5216
+
5165
5217
  /**
5166
5218
  * Shared RELATIVE (route-context) data resolving — the SINGLE source of truth for how a detail page
5167
5219
  * derives values FROM its current route item, consumed by BOTH the page builder (iox-cms-client) and
@@ -5878,7 +5930,9 @@ function buildSeoSchemaProperties() {
5878
5930
  structuredData: {
5879
5931
  type: 'object',
5880
5932
  label: 'Structured Data (JSON-LD)',
5881
- controller: { type: 'json' },
5933
+ // `code`, not `json`: the client registers `code` (CodeMirror) and has no `json`
5934
+ // controller, so a `json` declaration silently falls back to a plain text box.
5935
+ controller: { type: 'code', options: { language: 'json' } },
5882
5936
  },
5883
5937
  sitemap: {
5884
5938
  type: 'object',
@@ -6091,6 +6145,22 @@ function writeField$1(doc, field, value) {
6091
6145
  doc[branch] = nested;
6092
6146
  }
6093
6147
 
6148
+ /** field path → its declared controller, flattened out of the schema once. */
6149
+ function controllerMap() {
6150
+ const out = {};
6151
+ const walk = (properties, prefix) => {
6152
+ for (const [key, property] of Object.entries(properties ?? {})) {
6153
+ const path = prefix ? `${prefix}.${key}` : key;
6154
+ if (property?.properties)
6155
+ walk(property.properties, path);
6156
+ else if (property?.controller) {
6157
+ out[path] = { type: property.controller.type, options: property.controller.options };
6158
+ }
6159
+ }
6160
+ };
6161
+ walk(buildSeoSchemaProperties(), '');
6162
+ return out;
6163
+ }
6094
6164
  const MAX_LENGTHS = {
6095
6165
  title: SEO_TITLE_MAX,
6096
6166
  description: SEO_DESCRIPTION_MAX,
@@ -6108,6 +6178,7 @@ const MAX_LENGTHS = {
6108
6178
  function describeSeoFields(seo, ctx = {}) {
6109
6179
  const raw = asRecord(seo) ?? {};
6110
6180
  const resolvedDoc = resolveSeo(seo, ctx);
6181
+ const controllers = controllerMap();
6111
6182
  return SEO_BINDABLE_FIELDS.map(field => {
6112
6183
  const stored = fieldValue(raw, field);
6113
6184
  const override = seoString(readField(raw, field)) ?? '';
@@ -6130,6 +6201,8 @@ function describeSeoFields(seo, ctx = {}) {
6130
6201
  resolved: source === 'bound' ? '' : resolved,
6131
6202
  source,
6132
6203
  maxLength: MAX_LENGTHS[field],
6204
+ control: controllers[field]?.type ?? 'text',
6205
+ options: controllers[field]?.options,
6133
6206
  };
6134
6207
  });
6135
6208
  }
@@ -6926,5 +6999,5 @@ function effectiveTransitionId(anim) {
6926
6999
  * Generated bundle index. Do not edit.
6927
7000
  */
6928
7001
 
6929
- export { AOSService, AnalyticsService, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, CMSClientInfraModule, CONTEXT_ACTION_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ConsentService, ContainerComponent, ContentService, ContextStore, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DeclarationContext, ENVIRONMENT, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, LinkedContainerComponent, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PrivacyModalComponent, PrivacyModalService, PrivacyModelEvent, RESERVED_ALIAS_PREFIX, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_RESPONSIVE_TRAITS, SectionComponent, TRANSITION_FEELS, TextBlockComponent, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, WebSettingsService, activeActions, applyBindingPipes, applyFeel, breakpointDef, breakpointForWidth, breakpointQueryFor, buildDataSourceFilter, buildRelatedFilter, buildSwiperBreakpoints, buildTargetedStateSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, collectContextSlotKeys, collectReferencedAliases, compileDeclarations, composeVirtualTraits, computeRelative, contextActionEffect, contextSlotKeys, countOverrides, dataSourcePlanToRequest, declaredInputNames, defaultMessageResolver, deriveSeoFromContent, describeSeoFields, effectiveTransitionId, excludeSelf, feelFor, getByPath, getSeoValue, initialSlotValues, isActionEnabled, isContextAction, isInputOverriddenAt, isOverriddenAt, isReservedAlias, isValidSlotKey, legacyEnterToTransition, matchRouteParams, mediaQueryFor, mergeSwiperBreakpoints, nearestCommonAncestorId, nodeCssId, nodeUsesContext, normalizeCollectionResult, normalizeGalleryImage, normalizeIconClass, parentBreakpoint, parseUrlList, planDataSource, planResponsiveRules, referencedDataSources, referencedDataSourcesDeep, renderDefaultDeclarations, resetSeoField, resolveBoundValue, resolveContextValue, resolveDerivedInto, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveRelativeInto, resolveRepeaterItems, resolveSeo, resolveSiblings, resolveSingleItemId, resolveStateStyles, resolveStyleProps, resolveValueChain, rewriteViewportUnits, safeSetInput, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, stateNamesIn, stripSuppressedProps, styleKeyToKebab, variableAliases, variableItemsFrom, withContextSlots, wrapInBreakpointQuery, wrapInMediaQuery };
7002
+ export { AOSService, AnalyticsService, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, CMSClientInfraModule, CONTEXT_ACTION_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ConsentService, ContainerComponent, ContentService, ContextStore, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DeclarationContext, ENVIRONMENT, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, LinkedContainerComponent, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PrivacyModalComponent, PrivacyModalService, PrivacyModelEvent, RESERVED_ALIAS_PREFIX, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_RESPONSIVE_TRAITS, SectionComponent, TRANSITION_FEELS, TextBlockComponent, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, WebSettingsService, activeActions, applyBindingPipes, applyFeel, breakpointDef, breakpointForWidth, breakpointQueryFor, buildDataSourceFilter, buildRelatedFilter, buildSwiperBreakpoints, buildTargetedStateSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, collectContextSlotKeys, collectReferencedAliases, compileDeclarations, compilePageStyle, composeVirtualTraits, computeRelative, contextActionEffect, contextSlotKeys, countOverrides, dataSourcePlanToRequest, declaredInputNames, defaultMessageResolver, deriveSeoFromContent, describeSeoFields, effectiveTransitionId, excludeSelf, feelFor, getByPath, getSeoValue, initialSlotValues, isActionEnabled, isContextAction, isInputOverriddenAt, isOverriddenAt, isReservedAlias, isValidSlotKey, legacyEnterToTransition, matchRouteParams, mediaQueryFor, mergeSwiperBreakpoints, nearestCommonAncestorId, nodeCssId, nodeUsesContext, normalizeCollectionResult, normalizeGalleryImage, normalizeIconClass, parentBreakpoint, parseUrlList, planDataSource, planResponsiveRules, referencedDataSources, referencedDataSourcesDeep, renderDefaultDeclarations, resetSeoField, resolveBoundValue, resolveContextValue, resolveDerivedInto, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveRelativeInto, resolveRepeaterItems, resolveSeo, resolveSiblings, resolveSingleItemId, resolveStateStyles, resolveStyleProps, resolveValueChain, rewriteViewportUnits, safeSetInput, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, stateNamesIn, stripSuppressedProps, styleKeyToKebab, variableAliases, variableItemsFrom, withContextSlots, wrapInBreakpointQuery, wrapInMediaQuery };
6930
7003
  //# sourceMappingURL=vectoriox-iox-ui.mjs.map