@getvitops/emdash 2.0.0-alpha.3 → 2.0.0-alpha.4
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 +37 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +99 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/astro/blocks/Accordion.astro +10 -1
- package/src/astro/blocks/Badges.astro +1 -1
- package/src/astro/blocks/Callout.astro +1 -1
- package/src/astro/blocks/Cards.astro +4 -3
- package/src/astro/blocks/Hero.astro +4 -3
- package/src/astro/blocks/Logos.astro +25 -0
- package/src/astro/blocks/Nav.astro +1 -1
- package/src/astro/blocks/Pricing.astro +4 -0
- package/src/astro/blocks/Reviews.astro +1 -1
- package/src/astro/blocks/Section.astro +41 -6
- package/src/astro/blocks/attrs.test.ts +4 -2
- package/src/astro/blocks/attrs.ts +1 -1
- package/src/astro/blocks/render.container.test.ts +83 -3
- package/src/astro/blocks/tone.test.ts +2 -2
- package/src/astro/blocks/tone.ts +9 -2
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["pkg.version"],"sources":["../src/icon-options.ts","../src/astro/blocks/tone.ts","../src/block-types.ts","../src/blocks.ts","../src/menus-route.ts","../src/tones-route.ts","../src/design-system-route.ts","../src/blocks-doc.ts","../src/astro/blocks/form.ts","../src/form-template.ts","../src/hosting.ts","../package.json","../src/index.ts"],"sourcesContent":["/**\n * Semantic icon names, as Block Kit combobox options.\n *\n * Derived from `@getvitops/utils`' `iconMap` at import time. This used to be a\n * generated copy, so that the package carried no runtime `@getvitops/*`\n * dependency and could version independently of the fixed toolchain group —\n * but that independence was already fiction: the blocks render from the\n * toolchain's SVG sprite (`<use href=\"…/icons.svg#icon-menu\">`), so an emdash\n * built against one icon vocabulary and a sprite built against another produce\n * empty boxes, silently and with no install-time error. Depending on the real\n * map makes the vocabulary one thing rather than two that can drift.\n *\n * `fa7` is the reference collection: every semantic name is defined there, and\n * the other sets are alternate resolutions of the same names.\n */\nimport { iconMap } from '@getvitops/utils';\n\nexport const SEMANTIC_ICON_OPTIONS: { label: string; value: string }[] = Object.keys(iconMap.fa7)\n .sort()\n // Label and value are the same string on purpose: the editor picks a semantic\n // name, and showing the set-specific glyph id would leak the icon set into\n // authored content — the thing the semantic layer exists to prevent.\n .map((name) => ({ label: name, value: name }));\n","/**\n * The shared `tone` convention (UPSTREAM finding 21): every block that colours anything\n * takes one `tone` and, optionally, one `color`.\n *\n * - **`tone`** — a role name from the site's `colors.roles` (`brand-primary`, `danger`, a\n * consumer-named surface role…), or a semantic name. Role first, then its semantic\n * fallbacks, then `neutral`.\n * - **`color`** — the escape hatch: a palette hue (`amber`) or hue and step (`amber-300`).\n * Wins over `tone` when set. Contrast is NOT checked for it — the generator's contrast\n * contract covers roles, not arbitrary hue/step pairs — so it is for decoration. A solid fill\n * reads from the site's `--color-<hue>-<step>-on` and the tint flips with the colour scheme\n * (`light-dark()`); mid steps (300–500) cannot reach APCA Lc 75 with either text colour, so use\n * a role where contrast matters.\n *\n * **Why CSS custom properties, not classes.** A class name built from a field value\n * (`bg-${tone}`) is invisible to Tailwind's scanner, so it generates nothing in that\n * format. The resolver instead returns an inline `style` of `--tone-*` properties that\n * the framework's `.tone` pattern (patterns/tone.css) reads. Each property is a `var()`\n * chain, so the fallback happens in CSS at render time and this module needs NO copy of\n * the site's config — a tone the site never defined degrades to `neutral`, not to\n * nothing.\n *\n * The inputs are editor-supplied strings that land inside a `style` attribute, so both are\n * validated against a closed identifier grammar before use; anything else is dropped.\n */\n\n/** Semantic names every site is expected to define (`REQUIRED_ROLES`), plus common aliases. */\nexport const SEMANTIC_TONES = ['neutral', 'info', 'success', 'warning', 'danger'] as const;\n\n/**\n * When a role is absent, which others to try before `neutral`. Covers the framework's own\n * vocabulary (`ui-*`, `brand-*`) so a site that names only some of them still gets the\n * nearest relative rather than a grey block.\n */\nconst FALLBACKS: Record<string, string[]> = {\n 'brand-primary': ['ui-primary', 'info'],\n 'brand-secondary': ['ui-secondary', 'info'],\n 'ui-primary': ['info'],\n 'ui-secondary': ['info'],\n 'ui-accent': ['warning'],\n surface: ['neutral'],\n error: ['danger'],\n notice: ['warning'],\n note: ['info'],\n // Retired vitops.ca vocabulary some stored content still carries.\n it: ['ui-primary', 'info'],\n mk: ['ui-accent', 'warning'],\n};\n\nconst ROLE = /^[a-z][a-z0-9-]{0,40}$/;\nconst HUE = /^([a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*?)(?:-(\\d{2,3}))?$/;\n\n/** The role chain for a tone: itself, its relatives, then `neutral`. Pure and exported for tests. */\nexport function toneChain(tone: unknown): string[] {\n const name = typeof tone === 'string' ? tone.trim().toLowerCase() : '';\n if (!ROLE.test(name)) return ['neutral'];\n return [...new Set([name, ...(FALLBACKS[name] ?? []), 'neutral'])];\n}\n\n/** `var(--a, var(--b, …))` over a chain of token names; the last link has no fallback. */\nfunction chain(tokens: string[]): string {\n return tokens.reduceRight<string>(\n (inner, token) => (inner ? `var(${token}, ${inner})` : `var(${token})`),\n '',\n );\n}\n\nexport interface ResolvedTone {\n /** Always `tone`; a block adds `tone--solid` itself when it fills rather than tints. */\n className: string;\n /** Inline `style` value of `--tone-*` properties (no trailing semicolon). Empty when unset. */\n style: string;\n}\n\n/**\n * Resolve a block's `tone` / `color` fields.\n *\n * Returns an empty style when neither is set, so an unconfigured block carries no tone at\n * all (rather than a neutral one) and stays transparent.\n */\nexport function resolveTone(tone?: unknown, color?: unknown): ResolvedTone {\n const hasTone = typeof tone === 'string' && tone.trim() !== '';\n const hue = typeof color === 'string' ? HUE.exec(color.trim().toLowerCase()) : null;\n\n if (hue) {\n const [, name, step] = hue;\n const at = (s: string) => `var(--color-${name}-${s})`;\n // The foreground is the site's own: the generator emits `--color-<hue>-<step>-on` for every\n // step, picked by the same APCA rule as `text-on-<role>`, so a fill by hue and step reads\n // without a fixed white and without asking the browser to guess.\n const fill = step ?? '600';\n // Raw hue steps never re-point in dark, so the tint flips here: `light-dark()` follows the\n // `color-scheme` the generator sets under the dark selector.\n const flip = (light: string, dark: string) => `light-dark(${at(light)}, ${at(dark)})`;\n return {\n className: 'tone',\n style: [\n `--tone-bg: ${flip('100', '900')}`,\n `--tone-fg: ${flip('900', '100')}`,\n `--tone-border: ${flip('300', '700')}`,\n `--tone-solid: ${at(fill)}`,\n `--tone-on-solid: ${at(`${fill}-on`)}`,\n ].join('; '),\n };\n }\n\n if (!hasTone) return { className: 'tone', style: '' };\n\n const roles = toneChain(tone);\n const of = (prefix: string, suffix = '') =>\n chain(roles.map((r) => `--color-${prefix}-${r}${suffix}`));\n return {\n className: 'tone',\n style: [\n `--tone-bg: ${of('bg', '-muted')}`,\n `--tone-fg: ${of('text')}`,\n `--tone-border: ${of('border')}`,\n // A surface role has no `-solid`, only a bare `bg-<role>`; the solid chain ends there.\n `--tone-solid: ${chain([...roles.map((r) => `--color-bg-${r}-solid`), ...roles.map((r) => `--color-bg-${r}`)])}`,\n `--tone-on-solid: ${chain(roles.map((r) => `--color-text-on-${r}`))}`,\n ].join('; '),\n };\n}\n\n/**\n * Join a block's own inline style with a resolved tone's, so a renderer can pass one\n * `style` attribute. Empty parts are skipped.\n */\nexport function joinStyle(...parts: (string | undefined | false)[]): string | undefined {\n const out = parts.filter(Boolean).join('; ');\n return out || undefined;\n}\n\n/**\n * A repeater row's own colour: the `style` that overrides the block's `--tone-*` properties for\n * that item, or `undefined` when the row sets none (it then inherits the block's). Only the custom\n * properties are written, so the row picks up the block's own `.tone` surface and just recolours it.\n */\nexport function rowTone(row: Record<string, unknown>): string | undefined {\n return resolveTone(row.tone, row.color).style || undefined;\n}\n\n/**\n * The `theme` every block can carry: a backdrop for the block's own root element, chosen from the\n * site's colour roles. `<role>` is the tinted surface (`bg-<role>-muted`, with the role's text);\n * `<role>:solid` is the filled backdrop with the contrast-computed `text-on-<role>`; `none` or\n * nothing is no theme. Contrast is therefore the generator's guarantee, not the editor's judgement,\n * and a site that styles its roles as raised, glass or flat gets that look, because the tokens are\n * the site's own.\n *\n * Returns the classes and `--tone-*` style to put on the root element, or `undefined` for no theme.\n */\nexport function resolveTheme(theme: unknown): { className: string; style: string } | undefined {\n if (typeof theme !== 'string') return undefined;\n const [role, fill] = theme\n .trim()\n .toLowerCase()\n .replace(/^none$/, '')\n .split(':');\n if (!role || !ROLE.test(role)) return undefined;\n const tone = resolveTone(role);\n if (!tone.style) return undefined;\n return { className: fill === 'solid' ? 'tone tone--solid' : 'tone', style: tone.style };\n}\n","/**\n * Block TYPES (EmDash's schema-level `blocks` field) the plugin ships, as opposed to the\n * Portable Text blocks in `blocks.ts`.\n *\n * **Why `vitops_section` is not in `BLOCKS`.** A plugin Portable Text block is declared\n * with Block Kit fields — scalars, selects, repeaters of scalars — and Block Kit has no\n * rich-text element, so such a block cannot hold other blocks. A section's whole point is a\n * body of other blocks, and the one EmDash feature with a `portableText` field type is the\n * database-backed **block type** a `blocks` field is built from (see EmDash's blocks guide).\n * Those are created through the seed file or the schema API — a plugin cannot register one —\n * so this module exports the definition for the site to put in `seed/seed.json`'s\n * `blockTypes`, and `blockTypeComponents` (in `@getvitops/emdash/astro`) renders it.\n *\n * Inside the section's `body`, every `vitops_*` Portable Text block resolves normally: the\n * body is rendered by EmDash's `<PortableText>`, which merges this plugin's component map.\n *\n * Block types cannot be renamed or hard-deleted once shipped, so the slug follows the\n * framework's pattern name (`section`) with this package's prefix, and the field slugs are\n * the ones the renderer reads. Add fields in a compatible version; never repurpose one.\n */\nimport type { ApplySeedBlockTypeInput, BlockFieldDefinition } from 'emdash';\n\n/** Block-type slugs may not contain a dot, so `vitops_section` is stored as `vitops_section`. */\nexport const SECTION_SLUG = 'vitops_section';\n\nconst select = (\n slug: string,\n label: string,\n options: string[],\n defaultValue: string,\n): BlockFieldDefinition => ({\n slug,\n label,\n type: 'select',\n defaultValue,\n validation: { options },\n});\n\n/**\n * The `theme` select's values: `none`, a role, or `<role>:solid`. Block-type selects are\n * static, so this lists the roles every site defines; any other site role still works through\n * the `tone` field. `<role>:solid` is the filled backdrop with the contrast-computed text.\n */\nexport const THEME_ROLES = [\n 'neutral',\n 'surface',\n 'brand-primary',\n 'brand-secondary',\n 'info',\n 'success',\n 'warning',\n 'danger',\n];\nexport const THEMES = ['none', ...THEME_ROLES, ...THEME_ROLES.map((r) => `${r}:solid`)];\n\nexport const SECTION_FIELDS: BlockFieldDefinition[] = [\n { slug: 'kicker', label: 'Kicker (small line above the heading)', type: 'string' },\n { slug: 'heading', label: 'Heading', type: 'string' },\n select(\n 'level',\n 'Heading level (p: styled like a heading, not in the outline)',\n ['1', '2', '3', '4', '5', '6', 'p'],\n '2',\n ),\n { slug: 'sub', label: 'Lead-in text under the heading', type: 'text' },\n { slug: 'body', label: 'Content (any blocks)', type: 'portableText' },\n select('layout', 'Layout', ['stack', 'center', 'split'], 'stack'),\n select('width', 'Width', ['measure', 'breakout', 'spotlight', 'fullbleed'], 'measure'),\n {\n slug: 'tone',\n label: 'Backdrop tone (a colour role from the site, e.g. surface or brand-primary)',\n type: 'string',\n },\n {\n slug: 'color',\n label: 'Palette colour instead of a tone (e.g. amber or amber-300)',\n type: 'string',\n },\n { slug: 'solid', label: 'Fill with the solid colour', type: 'boolean', defaultValue: false },\n select(\n 'theme',\n 'Theme (backdrop and readable text in one pick; overrides the tone fields)',\n THEMES,\n 'none',\n ),\n { slug: 'anchor', label: 'Anchor id (so a link can point here with #id)', type: 'string' },\n { slug: 'class_name', label: 'Extra CSS classes', type: 'string' },\n { slug: 'style', label: 'CSS custom properties, e.g. --p-card: 2rem', type: 'string' },\n];\n\n/** `vitops_section`: kicker, heading, lead-in, tone/backdrop, optional heading-left layout, and a body of blocks. */\nexport const SECTION_BLOCK_TYPE: ApplySeedBlockTypeInput = {\n slug: SECTION_SLUG,\n label: 'Section',\n description: 'A titled page section whose body can hold any other blocks.',\n category: 'Sections',\n icon: 'layout',\n currentVersion: 1,\n versions: [{ version: 1, fields: SECTION_FIELDS }],\n};\n\n/**\n * Every block type this plugin ships, in the shape `seed.json`'s `blockTypes` takes. Spread\n * it into the seed (and allow the slugs on the collection's `blocks` field), e.g.\n * `blockTypes: [...vitopsBlockTypes()]`.\n */\nexport function vitopsBlockTypes(): ApplySeedBlockTypeInput[] {\n return [SECTION_BLOCK_TYPE];\n}\n","/**\n * Portable Text block-type declarations for the EmDash editor.\n *\n * Fields are EmDash Block Kit elements. Scalars (text_input / number_input / select /\n * toggle / combobox) cover single-valued patterns; `repeater` (rows of scalar\n * sub-fields) covers the repeating ones — cards, gallery, accordion, marquee, carousel\n * slides — and `media_picker` picks one asset from the media library. Both are\n * supported by the Portable Text block editor but not by Block Kit admin pages, which\n * is why the type below names them explicitly rather than accepting any `Element`.\n *\n * Patterns that need other blocks INSIDE them (a section with columns, a card holding a\n * button) cannot be a Portable Text block — a repeater holds scalars only. Those are\n * schema-level block TYPES with a `portableText` field, in `block-types.ts` (`vitops_section`).\n *\n * Every `action_id` here must have a matching prop consumed by the block's\n * .astro component in `src/astro/blocks/` (enforced by blocks.test.ts).\n */\n\nimport type { PortableTextBlockConfig } from 'emdash';\nimport { SEMANTIC_ICON_OPTIONS } from './icon-options.ts';\nimport { SEMANTIC_TONES } from './astro/blocks/tone.ts';\nimport { THEME_ROLES } from './block-types.ts';\n\n/** The subset of EmDash Block Kit field elements these blocks use. */\nexport type BlockKitFieldDef = Extract<\n NonNullable<PortableTextBlockConfig['fields']>[number],\n {\n type:\n | 'text_input'\n | 'number_input'\n | 'select'\n | 'toggle'\n | 'combobox'\n | 'repeater'\n | 'media_picker';\n }\n>;\n\nexport type PortableTextBlockDef = Omit<PortableTextBlockConfig, 'fields'> & {\n fields?: BlockKitFieldDef[];\n};\n\n/** Route that fills every tone select with the site's own roles — see `index.ts`. */\nexport const TONES_ROUTE = 'tones/list';\n/** Lists each colour role twice: tinted (`role`) and filled (`role:solid`), for every block's `theme`. */\nexport const THEMES_ROUTE = 'themes/list';\n\n/** Route that lists the site's EmDash menus for `vitops_nav`'s `menu` select — see `menus-route.ts`. */\nexport const MENUS_ROUTE = 'menus/list';\n\n/**\n * The tone options an editor can always pick, before the site adds its own roles.\n * Static because Block Kit needs a non-empty `options` list even when `optionsRoute` is set\n * (it is the fallback if the route fails, and what non-block-editor renderers read).\n */\nexport const STATIC_TONE_OPTIONS = [\n { label: 'None', value: '' },\n ...SEMANTIC_TONES.map((t) => ({ label: t[0]!.toUpperCase() + t.slice(1), value: t })),\n];\n\n/**\n * The shared tone pair (UPSTREAM 21; resolution lives in `astro/blocks/tone.ts`): a `tone`\n * select that offers the site's roles, and a `color` escape hatch naming a palette hue.\n * Spread into any block that colours something.\n */\nfunction toneFields(): BlockKitFieldDef[] {\n return [\n {\n type: 'select',\n action_id: 'tone',\n label: 'Tone (a colour role from the site)',\n initial_value: '',\n options: STATIC_TONE_OPTIONS,\n optionsRoute: TONES_ROUTE,\n } as BlockKitFieldDef,\n {\n type: 'text_input',\n action_id: 'color',\n label: 'Palette colour instead of a tone (e.g. amber or amber-300)',\n },\n ];\n}\n\n/** Route that lists the site's widget areas for `vitops_widgetArea`'s `area` select. */\nexport const WIDGET_AREAS_ROUTE = 'widget-areas/list';\n\n/**\n * The escape hatches every block carries, appended last by `BLOCKS` (see `astro/blocks/attrs.ts`\n * for how each is validated and why `style` takes custom properties only).\n */\nexport function commonFields(): BlockKitFieldDef[] {\n return [\n {\n type: 'select',\n action_id: 'theme',\n label: 'Theme (a backdrop with readable text, from the site colours)',\n initial_value: '',\n options: [\n { label: 'None', value: '' },\n ...THEME_ROLES.flatMap((r) => [\n { label: r.replace(/-/g, ' '), value: r },\n { label: `${r.replace(/-/g, ' ')} (filled)`, value: `${r}:solid` },\n ]),\n ],\n optionsRoute: THEMES_ROUTE,\n } as BlockKitFieldDef,\n {\n type: 'text_input',\n action_id: 'anchor',\n label: 'Anchor id (so a link can point here with #id)',\n },\n {\n type: 'text_input',\n action_id: 'className',\n label: 'Extra CSS classes (with the tailwind format, only classes the site already uses)',\n },\n {\n type: 'text_input',\n action_id: 'style',\n label: 'CSS custom properties, e.g. --p-card: 2rem; --tone-bg: #fee',\n },\n ];\n}\n\n/** Heading level for a block's own heading; `h1` is reserved for the page title. */\nconst levelField = (initial: string): BlockKitFieldDef => ({\n type: 'select',\n action_id: 'level',\n label: 'Heading level',\n initial_value: initial,\n options: ['2', '3', '4', '5', '6'].map((n) => ({ label: `h${n}`, value: n })),\n});\n\n/**\n * Heading level for a block that can own the page title (hero): `h1`, or `p` for a heading that is\n * visual only and should not appear in the outline.\n */\nconst titleLevelField = (initial: string): BlockKitFieldDef => ({\n type: 'select',\n action_id: 'level',\n label: 'Heading level (h1 is the page title; p is styled like a heading but not in the outline)',\n initial_value: initial,\n options: [\n ...['1', '2', '3', '4', '5', '6'].map((n) => ({ label: `h${n}`, value: n })),\n { label: 'p (not a heading)', value: 'p' },\n ],\n});\n\nconst BASE_BLOCKS: PortableTextBlockDef[] = [\n {\n /**\n * A link or button, optionally with an icon at either end.\n *\n * Flattened on purpose: Block Kit has no object-group element, so a shape\n * like `{ label, url }` becomes sibling fields. Same constraint the\n * marketing-blocks plugin documents.\n *\n * The icons are `combobox` — a searchable typeahead over a STATIC option\n * list, which is exactly what the semantic name list is. It is the closest\n * fit Block Kit offers: its element union is closed (no custom element, no\n * raw HTML), so `<wc-icon-picker>` cannot be mounted inside a block modal\n * however much nicer it would be. Revisit if EmDash opens that union.\n */\n type: 'vitops_actionLink',\n label: 'Link or button',\n icon: 'link',\n placeholder: 'A link or button, with optional icons',\n fields: [\n { type: 'text_input', action_id: 'label', label: 'Label' },\n { type: 'text_input', action_id: 'href', label: 'URL' },\n {\n type: 'select',\n action_id: 'variant',\n label: 'Style',\n initial_value: 'link',\n options: [\n { label: 'Link', value: 'link' },\n { label: 'Button', value: 'btn' },\n { label: 'Call to action', value: 'cta' },\n ],\n },\n {\n type: 'combobox',\n action_id: 'startIcon',\n label: 'Icon before the label',\n placeholder: 'None',\n options: SEMANTIC_ICON_OPTIONS,\n },\n {\n type: 'combobox',\n action_id: 'endIcon',\n label: 'Icon after the label',\n placeholder: 'None',\n options: SEMANTIC_ICON_OPTIONS,\n },\n {\n type: 'select',\n action_id: 'role',\n label: 'Colour role',\n initial_value: '',\n options: [\n { label: 'Default', value: '' },\n { label: 'Success', value: 'success' },\n { label: 'Danger', value: 'danger' },\n { label: 'Warning', value: 'warning' },\n { label: 'Info', value: 'info' },\n ],\n },\n { type: 'toggle', action_id: 'newTab', label: 'Open in a new tab', initial_value: false },\n ],\n },\n {\n type: 'vitops_imageCompare',\n label: 'Image compare',\n placeholder: 'Before/after image slider',\n fields: [\n { type: 'text_input', action_id: 'before', label: 'Before image URL' },\n { type: 'text_input', action_id: 'beforeAlt', label: 'Before alt text' },\n {\n type: 'text_input',\n action_id: 'beforeLabel',\n label: 'Before label',\n initial_value: 'Before',\n },\n { type: 'text_input', action_id: 'after', label: 'After image URL' },\n { type: 'text_input', action_id: 'afterAlt', label: 'After alt text' },\n { type: 'text_input', action_id: 'afterLabel', label: 'After label', initial_value: 'After' },\n ],\n },\n {\n type: 'vitops_copyButton',\n label: 'Copy snippet',\n icon: 'code',\n placeholder: 'Text with a copy-to-clipboard button',\n fields: [\n { type: 'text_input', action_id: 'text', label: 'Text to copy', multiline: true },\n { type: 'text_input', action_id: 'label', label: 'Button label', initial_value: 'Copy' },\n {\n type: 'select',\n action_id: 'display',\n label: 'Display',\n initial_value: 'code-block',\n options: [\n { label: 'Code block', value: 'code-block' },\n { label: 'Inline', value: 'inline' },\n ],\n },\n ],\n },\n {\n type: 'vitops_banner',\n label: 'Banner',\n placeholder: 'Announcement or notice',\n fields: [\n { type: 'text_input', action_id: 'message', label: 'Message', multiline: true },\n {\n type: 'select',\n action_id: 'tone',\n label: 'Tone',\n initial_value: 'info',\n options: [\n { label: 'Info', value: 'info' },\n { label: 'Success', value: 'success' },\n { label: 'Warning', value: 'warning' },\n { label: 'Danger', value: 'danger' },\n ],\n },\n { type: 'toggle', action_id: 'dismissible', label: 'Dismissible', initial_value: true },\n ],\n },\n {\n type: 'vitops_details',\n label: 'Disclosure',\n placeholder: 'Collapsible details/summary',\n fields: [\n { type: 'text_input', action_id: 'summary', label: 'Summary (always visible)' },\n { type: 'text_input', action_id: 'body', label: 'Body (revealed)', multiline: true },\n { type: 'toggle', action_id: 'open', label: 'Open by default', initial_value: false },\n ],\n },\n {\n type: 'vitops_carousel',\n label: 'Carousel',\n placeholder: 'Image carousel',\n category: 'Media',\n fields: [\n {\n type: 'repeater',\n action_id: 'slides',\n label: 'Slides',\n item_label: 'slide',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'src', label: 'Image URL' },\n { type: 'text_input', action_id: 'alt', label: 'Alt text' },\n ],\n },\n {\n type: 'text_input',\n action_id: 'images',\n label: 'Image URLs, one per line (legacy — use Slides)',\n multiline: true,\n },\n {\n type: 'text_input',\n action_id: 'alts',\n label: 'Alt texts, one per line (legacy — use Slides)',\n multiline: true,\n },\n {\n type: 'text_input',\n action_id: 'label',\n label: 'Accessible name',\n initial_value: 'Carousel',\n },\n {\n type: 'number_input',\n action_id: 'autoplay',\n label: 'Autoplay interval (ms, 0 = off)',\n initial_value: 0,\n },\n {\n type: 'toggle',\n action_id: 'loop',\n label: 'Loop (clones the slides — autoplay implies it)',\n initial_value: false,\n },\n {\n type: 'text_input',\n action_id: 'aspect',\n label: 'Slide aspect ratio',\n initial_value: '16 / 9',\n },\n ],\n },\n {\n type: 'vitops_gallery',\n label: 'Gallery',\n placeholder: 'Thumbnail grid that opens a lightbox carousel',\n category: 'Media',\n fields: [\n {\n type: 'text_input',\n action_id: 'label',\n label: 'Accessible name',\n initial_value: 'Gallery',\n },\n {\n type: 'repeater',\n action_id: 'images',\n label: 'Images',\n item_label: 'image',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'src', label: 'Image URL' },\n { type: 'text_input', action_id: 'alt', label: 'Alt text' },\n { type: 'text_input', action_id: 'caption', label: 'Caption' },\n ],\n },\n {\n type: 'text_input',\n action_id: 'aspect',\n label: 'Thumbnail aspect ratio',\n initial_value: '1',\n },\n { type: 'toggle', action_id: 'loop', label: 'Wrap around at the ends', initial_value: false },\n ],\n },\n {\n type: 'vitops_cards',\n label: 'Cards',\n placeholder: 'A grid of cards',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'cards',\n label: 'Cards',\n item_label: 'card',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'title', label: 'Title' },\n {\n type: 'text_input',\n action_id: 'body',\n label: 'Text (write [link text](/url) for an inline link)',\n multiline: true,\n },\n { type: 'text_input', action_id: 'image', label: 'Image URL' },\n { type: 'text_input', action_id: 'imageAlt', label: 'Image alt text' },\n {\n type: 'text_input',\n action_id: 'href',\n label: 'Link URL (makes the whole card clickable)',\n },\n {\n type: 'text_input',\n action_id: 'linkLabel',\n label: 'Link text',\n initial_value: 'Read more',\n },\n {\n type: 'text_input',\n action_id: 'checklist',\n label: 'Checklist (panel style; one item per line)',\n multiline: true,\n },\n { type: 'text_input', action_id: 'ctaLabel', label: 'Button text (panel style)' },\n { type: 'text_input', action_id: 'ctaHref', label: 'Button URL (panel style)' },\n ],\n },\n {\n type: 'select',\n action_id: 'variant',\n label: 'Style',\n initial_value: 'default',\n options: [\n { label: 'Cards', value: 'default' },\n { label: 'Panels (tinted, with checklist and button)', value: 'panel' },\n ],\n },\n levelField('3'),\n ...toneFields(),\n {\n type: 'select',\n action_id: 'columns',\n label: 'Columns',\n initial_value: '3',\n options: [\n { label: '1', value: '1' },\n { label: '2', value: '2' },\n { label: '3', value: '3' },\n { label: '4', value: '4' },\n ],\n },\n ],\n },\n {\n type: 'vitops_accordion',\n label: 'Accordion',\n placeholder: 'A group of collapsible sections',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'items',\n label: 'Items',\n item_label: 'item',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'summary', label: 'Summary (always visible)' },\n { type: 'text_input', action_id: 'body', label: 'Body (revealed)', multiline: true },\n { type: 'toggle', action_id: 'open', label: 'Open by default', initial_value: false },\n ],\n },\n {\n type: 'toggle',\n action_id: 'exclusive',\n label: 'Only one open at a time',\n initial_value: false,\n },\n ],\n },\n {\n type: 'vitops_hero',\n label: 'Hero',\n placeholder: 'Page opener: heading, text, buttons and an optional image',\n category: 'Sections',\n fields: [\n { type: 'text_input', action_id: 'eyebrow', label: 'Eyebrow (small line above)' },\n {\n type: 'text_input',\n action_id: 'heading',\n label: 'Heading (wrap words in *stars* to accent them)',\n },\n titleLevelField('1'),\n { type: 'text_input', action_id: 'body', label: 'Text', multiline: true },\n { type: 'text_input', action_id: 'primaryLabel', label: 'Main button text' },\n { type: 'text_input', action_id: 'primaryHref', label: 'Main button URL' },\n { type: 'text_input', action_id: 'secondaryLabel', label: 'Second link text' },\n { type: 'text_input', action_id: 'secondaryHref', label: 'Second link URL' },\n { type: 'text_input', action_id: 'image', label: 'Image URL (split layout)' },\n { type: 'text_input', action_id: 'imageAlt', label: 'Image alt text' },\n {\n type: 'select',\n action_id: 'layout',\n label: 'Layout',\n initial_value: 'centered',\n options: [\n { label: 'Centered', value: 'centered' },\n { label: 'Text and image side by side', value: 'split' },\n ],\n },\n ...toneFields(),\n {\n type: 'repeater',\n action_id: 'cards',\n label: 'Option cards under the buttons (optional)',\n item_label: 'card',\n fields: [\n { type: 'text_input', action_id: 'title', label: 'Title' },\n {\n type: 'text_input',\n action_id: 'body',\n label: 'Text (write [link text](/url) for an inline link)',\n multiline: true,\n },\n { type: 'text_input', action_id: 'href', label: 'Link URL (makes the card clickable)' },\n {\n type: 'text_input',\n action_id: 'linkLabel',\n label: 'Link text',\n initial_value: 'Learn more',\n },\n ],\n },\n ],\n },\n {\n type: 'vitops_marquee',\n label: 'Marquee',\n placeholder: 'A continuously scrolling row (logos, quotes, badges)',\n category: 'Media',\n fields: [\n {\n type: 'repeater',\n action_id: 'items',\n label: 'Items',\n item_label: 'item',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'text', label: 'Text' },\n { type: 'text_input', action_id: 'image', label: 'Image URL (shown instead of text)' },\n { type: 'text_input', action_id: 'alt', label: 'Image alt text' },\n ],\n },\n { type: 'toggle', action_id: 'pauseOnHover', label: 'Pause on hover', initial_value: true },\n { type: 'toggle', action_id: 'reverse', label: 'Scroll the other way', initial_value: false },\n ],\n },\n {\n type: 'vitops_logos',\n label: 'Logos',\n placeholder: 'A still row of partner or client logos',\n category: 'Media',\n fields: [\n {\n type: 'text_input',\n action_id: 'label',\n label: 'Accessible name',\n initial_value: 'Partners',\n },\n {\n type: 'repeater',\n action_id: 'logos',\n label: 'Logos',\n item_label: 'logo',\n min_items: 1,\n fields: [\n {\n type: 'text_input',\n action_id: 'image',\n label: 'Image URL (leave empty to show the name as text)',\n },\n {\n type: 'text_input',\n action_id: 'alt',\n label: 'Name (alt text, or the text shown with no image)',\n },\n { type: 'text_input', action_id: 'href', label: 'Link URL (optional)' },\n ],\n },\n { type: 'toggle', action_id: 'grayscale', label: 'Grey until hovered', initial_value: false },\n {\n type: 'select',\n action_id: 'size',\n label: 'Logo height',\n initial_value: 'md',\n options: [\n { label: 'Small', value: 'sm' },\n { label: 'Medium', value: 'md' },\n { label: 'Large', value: 'lg' },\n ],\n },\n ],\n },\n {\n type: 'vitops_steps',\n label: 'Steps',\n placeholder: 'A numbered sequence joined by a connector line',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'steps',\n label: 'Steps',\n item_label: 'step',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'title', label: 'Title' },\n {\n type: 'text_input',\n action_id: 'text',\n label: 'Text (write [link text](/url) for an inline link)',\n multiline: true,\n },\n ],\n },\n levelField('3'),\n {\n type: 'select',\n action_id: 'layout',\n label: 'Layout',\n initial_value: 'column',\n options: [\n { label: 'Stacked', value: 'column' },\n { label: 'Side by side when wide', value: 'row' },\n ],\n },\n ...toneFields(),\n ],\n },\n {\n type: 'vitops_stats',\n label: 'Stats',\n placeholder: 'Headline figures that count up, or labelled tiles',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'stats',\n label: 'Figures',\n item_label: 'figure',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'value', label: 'Figure (e.g. 82% or 11 hrs)' },\n { type: 'text_input', action_id: 'label', label: 'Label' },\n {\n type: 'text_input',\n action_id: 'group',\n label: 'Group (tiles style; small line above)',\n },\n {\n type: 'text_input',\n action_id: 'delta',\n label: 'Change (tiles style; e.g. +3 this week)',\n },\n {\n type: 'select',\n action_id: 'state',\n label: 'Change is',\n initial_value: '',\n options: [\n { label: 'Neutral', value: '' },\n { label: 'Good (up)', value: 'up' },\n { label: 'Bad (down)', value: 'down' },\n ],\n },\n ],\n },\n {\n type: 'select',\n action_id: 'variant',\n label: 'Style',\n initial_value: 'figures',\n options: [\n { label: 'Figures', value: 'figures' },\n { label: 'Tiles', value: 'tiles' },\n ],\n },\n {\n type: 'toggle',\n action_id: 'animate',\n label: 'Count up when scrolled into view',\n initial_value: true,\n },\n ...toneFields(),\n ],\n },\n {\n type: 'vitops_callout',\n label: 'Callout',\n placeholder: 'A call-to-action band (not a dismissible banner)',\n category: 'Sections',\n fields: [\n { type: 'text_input', action_id: 'heading', label: 'Heading' },\n levelField('2'),\n {\n type: 'text_input',\n action_id: 'body',\n label: 'Text (write [link text](/url) for an inline link)',\n multiline: true,\n },\n { type: 'text_input', action_id: 'primaryLabel', label: 'Main button text' },\n { type: 'text_input', action_id: 'primaryHref', label: 'Main button URL' },\n { type: 'text_input', action_id: 'secondaryLabel', label: 'Second link text' },\n { type: 'text_input', action_id: 'secondaryHref', label: 'Second link URL' },\n {\n type: 'select',\n action_id: 'align',\n label: 'Layout',\n initial_value: 'start',\n options: [\n { label: 'Message left, buttons right', value: 'start' },\n { label: 'Centered', value: 'center' },\n ],\n },\n {\n type: 'toggle',\n action_id: 'solid',\n label: 'Fill with the solid colour',\n initial_value: false,\n },\n ...toneFields(),\n ],\n },\n {\n type: 'vitops_entries',\n label: 'Entries table',\n placeholder: 'Rows of term/value pairs: a stacked list that becomes a table when wide',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'entries',\n label: 'Entries',\n item_label: 'entry',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'heading', label: 'Heading (row name)' },\n {\n type: 'text_input',\n action_id: 'pairs',\n label: 'Details (one per line: Term | Value)',\n multiline: true,\n },\n ],\n },\n levelField('3'),\n {\n type: 'text_input',\n action_id: 'breakpoint',\n label: 'Becomes a table at this width',\n initial_value: '40rem',\n },\n ],\n },\n {\n type: 'vitops_pricing',\n label: 'Pricing',\n placeholder: 'Plan cards with aligned feature rows',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'plans',\n label: 'Plans',\n item_label: 'plan',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'name', label: 'Plan name' },\n { type: 'text_input', action_id: 'price', label: 'Price (e.g. $49)' },\n { type: 'text_input', action_id: 'period', label: 'Period (e.g. per month)' },\n {\n type: 'text_input',\n action_id: 'features',\n label: 'Features (one per line)',\n multiline: true,\n },\n { type: 'text_input', action_id: 'ctaLabel', label: 'Button text' },\n { type: 'text_input', action_id: 'ctaHref', label: 'Button URL' },\n {\n type: 'toggle',\n action_id: 'featured',\n label: 'Highlight this plan',\n initial_value: false,\n },\n ],\n },\n levelField('3'),\n ],\n },\n {\n type: 'vitops_embed',\n label: 'Embed (video or map)',\n placeholder: 'A video or map that loads only after the visitor allows it',\n category: 'Embeds',\n fields: [\n {\n type: 'select',\n action_id: 'provider',\n label: 'Provider',\n initial_value: 'youtube',\n options: [\n { label: 'YouTube', value: 'youtube' },\n { label: 'Vimeo', value: 'vimeo' },\n { label: 'Map (Google or OpenStreetMap embed address)', value: 'map' },\n { label: 'Other (https address)', value: 'custom' },\n ],\n },\n { type: 'text_input', action_id: 'url', label: 'Address' },\n {\n type: 'text_input',\n action_id: 'title',\n label: 'Title (required: it names the frame for screen readers)',\n },\n {\n type: 'select',\n action_id: 'category',\n label: 'Consent category it waits on',\n initial_value: 'marketing',\n options: [\n { label: 'Marketing', value: 'marketing' },\n { label: 'Preferences', value: 'preferences' },\n { label: 'Analytics', value: 'analytics' },\n ],\n },\n { type: 'text_input', action_id: 'ratio', label: 'Aspect ratio', initial_value: '16 / 9' },\n ],\n },\n {\n type: 'vitops_form',\n label: 'Contact form',\n placeholder: 'An enquiry form that emails you (and your CRM) and works without JavaScript',\n category: 'Forms',\n fields: [\n { type: 'text_input', action_id: 'heading', label: 'Heading' },\n levelField('2'),\n { type: 'text_input', action_id: 'intro', label: 'Text above the form', multiline: true },\n {\n type: 'repeater',\n action_id: 'fields',\n label: 'Fields',\n item_label: 'field',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'label', label: 'Label (what the visitor sees)' },\n {\n type: 'text_input',\n action_id: 'name',\n label: 'Name in the message (optional: letters, digits, underscores)',\n },\n {\n type: 'select',\n action_id: 'type',\n label: 'Kind',\n initial_value: 'text',\n options: [\n { label: 'Short text', value: 'text' },\n { label: 'Email address', value: 'email' },\n { label: 'Phone number', value: 'tel' },\n { label: 'Long text', value: 'textarea' },\n { label: 'Choose one', value: 'select' },\n { label: 'Tick box', value: 'checkbox' },\n { label: 'Tick any number', value: 'checkboxes' },\n ],\n },\n { type: 'toggle', action_id: 'required', label: 'Required', initial_value: false },\n { type: 'text_input', action_id: 'placeholder', label: 'Example text' },\n { type: 'text_input', action_id: 'help', label: 'Help text under the field' },\n {\n type: 'text_input',\n action_id: 'options',\n label: 'Choices (one per line; for \"Choose one\" and \"Tick any number\")',\n multiline: true,\n },\n ],\n },\n { type: 'text_input', action_id: 'submitLabel', label: 'Button text', initial_value: 'Send' },\n {\n type: 'text_input',\n action_id: 'successMessage',\n label: 'Message after sending',\n initial_value: 'Thanks. Your message has been sent.',\n },\n { type: 'text_input', action_id: 'errorMessage', label: 'Message if sending fails' },\n {\n type: 'text_input',\n action_id: 'noJsMessage',\n label: 'Message when JavaScript is off (shown only if the site has a spam check)',\n },\n {\n type: 'text_input',\n action_id: 'action',\n label: 'Where the form posts (leave empty for /api/track)',\n },\n ...toneFields(),\n ],\n },\n {\n /**\n * Google reviews from the committed `.vitops/reviews.json` cache (`vitops reviews`).\n *\n * The data is a BUILD-TIME import (`virtual:getvitops/reviews`, from the `vitops()` Astro\n * integration), never a filesystem read: the renderer runs in a Worker. So a new review\n * appears after the next `vitops reviews` run is committed and the site rebuilt, not when\n * an editor saves. Which reviews show is `selectReviews()` from `@getvitops/utils`, the\n * one place that is decided — these fields are exactly its `ReviewLocationConfig`.\n */\n type: 'vitops_reviews',\n label: 'Google reviews',\n icon: 'star',\n placeholder: 'Customer reviews from the Google Business Profile cache',\n category: 'Sections',\n fields: [\n {\n type: 'text_input',\n action_id: 'label',\n label: 'Name for the group (read by screen readers)',\n initial_value: 'Customer reviews',\n },\n {\n type: 'text_input',\n action_id: 'location',\n label: 'Location key (organization.locations key; blank = every location in the cache)',\n },\n {\n type: 'select',\n action_id: 'layout',\n label: 'Layout',\n initial_value: 'grid',\n options: [\n { label: 'Grid', value: 'grid' },\n { label: 'Carousel', value: 'carousel' },\n { label: 'Marquee (scrolling row)', value: 'marquee' },\n ],\n },\n {\n type: 'select',\n action_id: 'columns',\n label: 'Columns (grid only)',\n initial_value: '3',\n options: ['1', '2', '3', '4'].map((n) => ({ label: n, value: n })),\n },\n {\n type: 'number_input',\n action_id: 'limit',\n label: 'Most reviews to show (0 = all)',\n initial_value: 6,\n },\n {\n type: 'select',\n action_id: 'minRating',\n label: 'Lowest rating to show',\n initial_value: '4',\n options: ['1', '2', '3', '4', '5'].map((n) => ({\n label: n === '1' ? 'Any' : `${n} stars and up`,\n value: n,\n })),\n },\n {\n type: 'select',\n action_id: 'sort',\n label: 'Order',\n initial_value: 'recent',\n options: [\n { label: 'Newest first', value: 'recent' },\n { label: 'Highest rated first', value: 'rating' },\n ],\n },\n {\n type: 'text_input',\n action_id: 'featured',\n label: 'Review ids to show first (comma-separated; they ignore the rating filter)',\n },\n {\n type: 'toggle',\n action_id: 'avatars',\n label: \"Show reviewers' Google photos (loads images from Google for every visitor)\",\n initial_value: false,\n },\n ...toneFields(),\n ],\n },\n {\n /**\n * A named EmDash menu inside page content: a site map, a section's link list, a footer-style\n * set of columns. NOT the header — that is `<SiteNav>` in the layout, which owns the drawer,\n * dropdowns and breakpoints. `variant: sitenav` renders the same markup and pattern for the\n * rare page that wants a real navbar in the body.\n *\n * The menu is chosen from `menus/list` (the site's own menus) with the conventional names as\n * the static fallback, because a select needs a non-empty `options` list.\n */\n type: 'vitops_nav',\n label: 'Menu',\n icon: 'list',\n placeholder: 'An EmDash menu as a list of links',\n category: 'Navigation',\n fields: [\n {\n type: 'select',\n action_id: 'menu',\n label: 'Menu',\n initial_value: 'footer-sitemap',\n options: [\n { label: 'Footer site map', value: 'footer-sitemap' },\n { label: 'Footer legal', value: 'footer-legal' },\n { label: 'Primary', value: 'primary' },\n ],\n optionsRoute: MENUS_ROUTE,\n } as BlockKitFieldDef,\n {\n type: 'text_input',\n action_id: 'label',\n label: 'Name for the navigation (read by screen readers; must differ from other menus)',\n },\n { type: 'text_input', action_id: 'heading', label: 'Visible heading (optional)' },\n levelField('2'),\n {\n type: 'select',\n action_id: 'variant',\n label: 'Layout',\n initial_value: 'list',\n options: [\n { label: 'Vertical list', value: 'list' },\n { label: 'Inline row', value: 'inline' },\n { label: 'Columns (each top-level item with its sub-links)', value: 'columns' },\n { label: 'Navbar (the header pattern, in the page)', value: 'sitenav' },\n ],\n },\n {\n type: 'select',\n action_id: 'depth',\n label: 'Levels to show',\n initial_value: '1',\n options: ['1', '2', '3'].map((n) => ({ label: n, value: n })),\n },\n ...toneFields(),\n ],\n },\n {\n /** A row of static labels (`badge`) or editable/dismissable ones (`tag`). */\n type: 'vitops_badges',\n label: 'Badges',\n icon: 'tag',\n placeholder: 'A row of short labels',\n fields: [\n {\n type: 'repeater',\n action_id: 'items',\n label: 'Labels',\n fields: [\n { type: 'text_input', action_id: 'label', label: 'Label' },\n {\n type: 'select',\n action_id: 'tone',\n label: 'Tone (blank uses the block tone)',\n initial_value: '',\n options: STATIC_TONE_OPTIONS,\n optionsRoute: TONES_ROUTE,\n } as BlockKitFieldDef,\n { type: 'text_input', action_id: 'color', label: 'Palette colour instead (e.g. amber)' },\n {\n type: 'combobox',\n action_id: 'icon',\n label: 'Icon before the label',\n placeholder: 'None',\n options: SEMANTIC_ICON_OPTIONS,\n },\n { type: 'text_input', action_id: 'href', label: 'Link URL (optional)' },\n ],\n } as BlockKitFieldDef,\n {\n type: 'select',\n action_id: 'variant',\n label: 'Style',\n initial_value: 'badge',\n options: [\n { label: 'Badge (static label)', value: 'badge' },\n { label: 'Tag', value: 'tag' },\n ],\n },\n ...toneFields(),\n ],\n },\n {\n /**\n * A widget area: content defined once under Widgets in the admin and shown wherever this\n * block is placed. Edits reach every page it sits on (after the next build, on a\n * prerendered site) — unlike a Section, which is copied.\n */\n type: 'vitops_widgetArea',\n label: 'Widget area',\n icon: 'layout',\n placeholder: 'Shared content managed under Widgets',\n fields: [\n {\n type: 'select',\n action_id: 'area',\n label: 'Widget area',\n initial_value: '',\n options: [{ label: 'Choose…', value: '' }],\n optionsRoute: WIDGET_AREAS_ROUTE,\n } as BlockKitFieldDef,\n levelField('3'),\n ],\n },\n];\n\n/** Every block gets the escape hatches last, so the editor reads content first. */\n/**\n * Repeaters whose rows can carry their own colour. A row's `tone` / `color` win over the block's,\n * and blank falls back to it, so one block can hold a pine card and an amber one without\n * becoming two blocks. (If you want a single colour for the lot, set it on the block.)\n */\nconst ROW_TONE: Record<string, string> = {\n vitops_cards: 'cards',\n vitops_stats: 'stats',\n vitops_steps: 'steps',\n vitops_hero: 'cards',\n};\n\n/** Extra row fields for one repeater, beyond the shared tone pair. */\nconst ROW_EXTRA: Record<string, Record<string, BlockKitFieldDef[]>> = {\n vitops_hero: {\n cards: [\n { type: 'text_input', action_id: 'eyebrow', label: 'Eyebrow (small line above the title)' },\n { type: 'text_input', action_id: 'ctaLabel', label: 'Button text' },\n { type: 'text_input', action_id: 'ctaHref', label: 'Button URL' },\n ],\n },\n};\n\nconst rowToneFields = (): BlockKitFieldDef[] => [\n {\n type: 'select',\n action_id: 'tone',\n label: 'Tone for this item (blank uses the block tone)',\n initial_value: '',\n options: STATIC_TONE_OPTIONS,\n optionsRoute: TONES_ROUTE,\n } as BlockKitFieldDef,\n { type: 'text_input', action_id: 'color', label: 'Palette colour instead (e.g. amber)' },\n];\n\nfunction withRowFields(block: PortableTextBlockDef): BlockKitFieldDef[] {\n const repeater = ROW_TONE[block.type];\n return (block.fields ?? []).map((f) =>\n f.type === 'repeater' && f.action_id === repeater\n ? ({\n ...f,\n fields: [...f.fields, ...(ROW_EXTRA[block.type]?.[repeater] ?? []), ...rowToneFields()],\n } as BlockKitFieldDef)\n : f,\n );\n}\n\nexport const BLOCKS: PortableTextBlockDef[] = BASE_BLOCKS.map((b) => ({\n ...b,\n fields: [...withRowFields(b), ...commonFields()],\n}));\n\n/** Which Vitops blocks a site's editor offers, and any it adds of its own. */\nexport interface BlockSelection {\n /** Offer only these `vitops_*` types. Unknown names throw — a typo must not silently offer nothing. */\n include?: string[];\n /** Drop these `vitops_*` types. Applied after `include`. */\n exclude?: string[];\n /**\n * The site's own blocks. These are EDITOR definitions only: the plugin cannot render a\n * block it has never heard of, so the site maps each `type` to a component with\n * `<PortableText components={{ type: { … } }} />`. A type that collides with an existing\n * one throws, since the later definition would silently replace the earlier one's fields.\n */\n extra?: PortableTextBlockDef[];\n}\n\n/**\n * Resolve {@link BlockSelection} against {@link BLOCKS}.\n *\n * Pure so a misconfiguration fails at plugin construction, with the offending name in the\n * message, rather than as an editor menu that is quietly missing something.\n */\nexport function selectBlocks(selection: BlockSelection = {}): PortableTextBlockDef[] {\n const known = new Set(BLOCKS.map((b) => b.type));\n const check = (names: string[] | undefined, option: string): void => {\n for (const name of names ?? [])\n if (!known.has(name))\n throw new Error(\n `[vitops] blocks.${option}: unknown block '${name}'. Known: ${[...known].join(', ')}.`,\n );\n };\n check(selection.include, 'include');\n check(selection.exclude, 'exclude');\n\n const include = selection.include ? new Set(selection.include) : null;\n const exclude = new Set(selection.exclude);\n const base = BLOCKS.filter((b) => (!include || include.has(b.type)) && !exclude.has(b.type));\n\n const extra = selection.extra ?? [];\n const taken = new Set(BLOCKS.map((b) => b.type));\n for (const block of extra) {\n if (taken.has(block.type))\n throw new Error(`[vitops] blocks.extra: type '${block.type}' is already defined.`);\n taken.add(block.type);\n }\n return [...base, ...extra];\n}\n","/**\n * The `menus/list` plugin route: what fills `vitops_nav`'s `menu` select with the site's own\n * EmDash menus, so an editor picks from what exists instead of typing a name that may not.\n *\n * Same mechanism as `tones-route.ts` and the same failure rule: Block Kit's `select` falls\n * back to its static options if the route throws or answers with no `items`, so a lookup that\n * cannot run (no request context, no database) degrades to the conventional menu names rather\n * than breaking the block editor. The lister is injected so the route is testable without a\n * database; `index.ts` passes EmDash's own `getMenus`.\n */\nimport type { PluginRoute } from 'emdash';\n\nexport interface MenuSummary {\n name: string;\n label: string;\n}\n\nexport interface MenuItemOption {\n id: string;\n name: string;\n}\n\n/** The items the select shows: one per menu, valued by its `name` (what `getMenu()` takes). */\nexport function menuItems(menus: readonly MenuSummary[]): MenuItemOption[] {\n const seen = new Set<string>();\n const out: MenuItemOption[] = [];\n for (const { name, label } of menus) {\n // A menu per locale shares one name; the select needs it once.\n if (!name || seen.has(name)) continue;\n seen.add(name);\n out.push({ id: name, name: label && label !== name ? `${label} (${name})` : name });\n }\n return out;\n}\n\nexport function createMenusRoute(list: () => Promise<readonly MenuSummary[]>): PluginRoute {\n return {\n // An editor opening the block needs this; it reads menu names, never their content.\n permission: 'content:read',\n handler: async () => {\n try {\n return { items: menuItems(await list()) };\n } catch {\n // No `items` => the select keeps its static options.\n return { items: [] };\n }\n },\n };\n}\n","/**\n * The `tones/list` plugin route: what fills every block's `tone` select with the site's own\n * colour roles (UPSTREAM 21 — \"role names the admin never offered\").\n *\n * EmDash's Block Kit `select` takes an `optionsRoute` for exactly this, and falls back to the\n * field's static options if the route fails or answers with no `items`, so a site that\n * configures nothing still gets the semantic names from `STATIC_TONE_OPTIONS`.\n *\n * **Where the roles come from.** Plugin code has no site config at runtime, so the site hands\n * it over: `tones: [...]` verbatim, or — if the design-system editor is already wired with the\n * site's `Config` — the `colors.roles` keys of that config, so there is nothing to keep in\n * step. A role is listed under its own name (`brand-primary`), because that is what\n * `resolveTone()` and the token names (`--color-bg-brand-primary-muted`) both use.\n */\nimport { resolveInput } from '@getvitops/generator/config';\nimport type { PluginRoute } from 'emdash';\nimport { SEMANTIC_TONES } from './astro/blocks/tone.ts';\n\nexport interface ToneItem {\n id: string;\n name: string;\n}\n\n/** Role names of a design system or full config, or `[]` if it cannot be resolved. */\nexport function rolesOf(config: unknown): string[] {\n if (!config || typeof config !== 'object') return [];\n try {\n const { designSystem } = resolveInput(config as never, { siteEnv: 'production' });\n return Object.keys(designSystem.colors?.roles ?? {});\n } catch {\n // A config the editor cannot resolve is the editor's error to report; here it just\n // means the select keeps its static options.\n return [];\n }\n}\n\n/** The items the select shows: the site's roles first, then any semantic name it lacks. */\nexport function toneItems(roles: readonly string[]): ToneItem[] {\n const names = [...new Set([...roles, ...SEMANTIC_TONES])];\n return [{ id: '', name: 'None' }, ...names.map((id) => ({ id, name: id.replace(/-/g, ' ') }))];\n}\n\nexport function createTonesRoute(roles: () => readonly string[]): PluginRoute {\n return {\n // An editor opening a block needs this; it reads names, never content.\n permission: 'content:read',\n handler: async () => ({ items: toneItems(roles()) }),\n };\n}\n\n/** The `theme` select's items: `None`, then each role tinted and filled (`role`, `role:solid`). */\nexport function themeItems(roles: readonly string[]): ToneItem[] {\n const names = [...new Set([...roles, ...SEMANTIC_TONES])];\n const label = (id: string) => id.replace(/-/g, ' ');\n return [\n { id: '', name: 'None' },\n ...names.flatMap((id) => [\n { id, name: label(id) },\n { id: `${id}:solid`, name: `${label(id)} (filled)` },\n ]),\n ];\n}\n\nexport function createThemesRoute(roles: () => readonly string[]): PluginRoute {\n return {\n permission: 'content:read',\n handler: async () => ({ items: themeItems(roles()) }),\n };\n}\n","/**\n * The `vitops.design-system` plugin route — what `<wc-theme-editor>` probes\n * and POSTs its patch to when mounted inside the EmDash admin (see this\n * package's README and AGENTS.md's \"EmDash plugin as the design-system\n * editing surface\" section). Mounted at\n * `/_emdash/api/plugins/vitops/design-system`.\n *\n * Two responsibilities, matching the dev-only `@getvitops/vite` endpoint's\n * contract byte-for-byte (`GET → { writable }`, `POST { patch } → { ok } |\n * { errors }`) so `<wc-theme-editor>` needs no branch for which one it's\n * talking to:\n *\n * 1. **Immediate**: persist the patch to `ctx.settings` — read back by the\n * `page:fragments` hook in `index.ts`, which turns it into a site-wide\n * `:root` override `<style>` on every public page. This is what makes an\n * edit visible without a rebuild.\n * 2. **Canonical**: auto-commit the merged file to `site.deployment.branch`\n * (default `dev`) via `@getvitops/utils/git`'s `commitFile()`, so the edit\n * reaches the git-tracked `site.json` through this repo's own normal\n * `dev → promote → main` review path — never a silent second source of\n * truth. A commit failure (missing token, misconfigured repo, a transient\n * GitHub error) is logged and surfaces as a warning, but never fails the\n * save: the immediate override already works regardless.\n *\n * **Assumes `config` is a full site `Config`** (has `organization`/`site`),\n * not a bare `design-system.json` — true for any real EmDash site (it always\n * has a `site.json`), and the only case this plugin already handles anywhere\n * else (`vitopsHosting()`, `BLOCKS`).\n *\n * The GitHub token is read as `process.env.VITOPS_GITHUB_TOKEN` — matching\n * `hosting.ts`'s own `process.env.HOSTING` read in this same package, rather\n * than a `cloudflare:workers`-specific import: a native EmDash plugin runs\n * in-process with no isolation boundary (EmDash's own docs say plugins \"can\n * access environment variables\" directly), and this package already targets\n * both the Cloudflare and Node hosting targets through one `process.env`\n * convention (`nodejs_compat` on Workers maps bindings into `process.env`).\n */\nimport { pluginResponse } from 'emdash';\nimport type { PageFragmentContribution, PluginContext, PluginRoute, RouteContext } from 'emdash';\nimport {\n designSystemPath,\n formatConfig,\n getAt,\n isConfig,\n mergePatch,\n resolveInput,\n setAt,\n} from '@getvitops/generator/config';\nimport {\n buildReverseIndex,\n patchToOverrideCss,\n validateColorContract,\n} from '@getvitops/generator/tokens';\nimport { commitFile } from '@getvitops/utils/git';\n\nexport interface DesignSystemEditorOptions {\n /**\n * The site's own config, typically `import config from '../../site.json'\n * with { type: 'json' }`. A static, build-time-bundled value — this plugin\n * never reads it from disk at runtime (a deployed Worker has no\n * filesystem), so an edit doesn't take effect here until the *next*\n * deploy; that's exactly the point of the auto-commit half.\n */\n config: unknown;\n /** Which `designSystem.themes` entry this edits. Default: `'default'`. */\n theme?: string;\n}\n\nconst SETTINGS_KEY = 'designSystemPatch';\n\ninterface StoredPatch {\n patch: Record<string, unknown>;\n at: number;\n}\n\nfunction json(status: number, body: unknown) {\n return pluginResponse({\n status,\n headers: { 'content-type': 'application/json' },\n body: { kind: 'text', value: JSON.stringify(body) },\n });\n}\n\n/** Fetches the currently-stored patch, clearing it if the bundled `config`\n * (the last real build) already reflects every value in it — see the\n * module docblock's \"self-clearing\" note in AGENTS.md. */\nasync function currentPatch(\n ctx: Pick<RouteContext, 'settings'>,\n ds: { colors: Record<string, unknown> },\n): Promise<StoredPatch | null> {\n const stored = await ctx.settings.get<StoredPatch>(SETTINGS_KEY);\n if (!stored) return null;\n const patchedColors = stored.patch['colors'];\n if (\n patchedColors != null &&\n JSON.stringify(mergePatch(ds.colors, patchedColors)) === JSON.stringify(ds.colors)\n ) {\n await ctx.settings.delete(SETTINGS_KEY);\n return null;\n }\n return stored;\n}\n\nexport function createDesignSystemRoute(opts: DesignSystemEditorOptions): PluginRoute {\n const theme = opts.theme ?? 'default';\n\n return {\n methods: ['GET', 'POST'],\n response: 'raw',\n permission: 'plugins:manage',\n handler: async (ctx: RouteContext) => {\n // `resolveInput` handles both a bare design-system.json and a full site\n // Config (following `extends`, resolving `themes[theme]`), the same\n // entry point every other consumer of a config goes through.\n const { designSystem: ds, config } = resolveInput(opts.config, {\n theme,\n siteEnv: 'production',\n });\n\n if (ctx.request.method === 'GET') {\n await currentPatch(ctx, ds);\n return json(200, { writable: true });\n }\n\n const body = ctx.input as { patch?: unknown } | undefined;\n const patch = body?.patch;\n if (patch == null || typeof patch !== 'object' || Array.isArray(patch))\n return json(400, { errors: ['body must be { patch: object }'] });\n\n const mergedColors = mergePatch(\n ds.colors,\n (patch as Record<string, unknown>)['colors'] ?? {},\n );\n const contrastErrors = validateColorContract(\n mergedColors as Parameters<typeof validateColorContract>[0],\n );\n if (contrastErrors.length) return json(400, { errors: contrastErrors });\n\n await ctx.settings.set(SETTINGS_KEY, {\n patch: patch as Record<string, unknown>,\n at: Date.now(),\n } satisfies StoredPatch);\n\n const warnings: string[] = [];\n const repoSpec = config?.site.deployment?.repo;\n const token = process.env['VITOPS_GITHUB_TOKEN'];\n if (!repoSpec || !token) {\n warnings.push(\n !repoSpec\n ? 'design-system sync skipped: site.deployment.repo is not set — the change is live but not yet synced to source.'\n : 'design-system sync skipped: no GitHub token configured — the change is live but not yet synced to source.',\n );\n } else {\n const [owner, repo] = repoSpec.split('/');\n if (!owner || !repo) {\n warnings.push(\n `design-system sync skipped: site.deployment.repo \"${repoSpec}\" isn't \"owner/name\".`,\n );\n } else if (!isConfig(opts.config)) {\n warnings.push('design-system sync skipped: config is not a recognised site Config.');\n } else {\n const at = designSystemPath(opts.config, theme);\n const mergedSubtree = mergePatch(getAt(opts.config, at), patch);\n const content = `${formatConfig(setAt(opts.config, at, mergedSubtree))}\\n`;\n const result = await commitFile({\n owner,\n repo,\n branch: config.site.deployment?.branch ?? 'dev',\n path: 'site.json',\n content,\n message: 'Sync design-system edit from EmDash admin',\n token,\n fetchImpl:\n ctx.http && ((url: string, init?: RequestInit) => ctx.http!.fetch(url, init)),\n });\n if (!result.ok) {\n ctx.log.warn('design-system sync commit failed', { message: result.message });\n warnings.push(`design-system sync failed (change is still live): ${result.message}`);\n }\n }\n }\n\n return json(200, { ok: true, warnings });\n },\n };\n}\n\n/**\n * The `page:fragments` contribution serving the stored patch's override\n * CSS site-wide. Returns `null` (no fragment) when there's nothing unsynced\n * — the same \"no gated tag, no cost\" discipline `@getvitops/astro`'s consent\n * gate already follows.\n */\nexport async function designSystemOverrideFragment(\n ctx: Pick<PluginContext, 'settings'>,\n opts: DesignSystemEditorOptions,\n): Promise<PageFragmentContribution | null> {\n const { designSystem: ds } = resolveInput(opts.config, { theme: opts.theme ?? 'default' });\n const stored = await currentPatch(ctx, ds);\n if (!stored) return null;\n\n const css = patchToOverrideCss(stored.patch, buildReverseIndex(ds));\n if (!css) return null;\n return {\n kind: 'html',\n placement: 'head',\n html: `<style id=\"ds-overrides\">${css}</style>`,\n key: 'vitops-ds-overrides',\n };\n}\n","import { BLOCKS, type BlockKitFieldDef, type PortableTextBlockDef } from './blocks.ts';\nimport { vitopsBlockTypes } from './block-types.ts';\n\nconst cell = (s: string | undefined) => (s ?? '').replace(/\\|/g, '\\\\|').replace(/\\n/g, ' ');\n\nfunction describe(f: BlockKitFieldDef): string {\n const bits: string[] = [];\n if ('options' in f && Array.isArray(f.options) && f.options.length) {\n const values = f.options.map((o) => String((o as { value: unknown }).value));\n bits.push(\n values.length > 8\n ? `one of ${values.length} values (e.g. ${values.slice(0, 4).join(', ')})`\n : `one of ${values.join(', ')}`,\n );\n }\n if ('initial_value' in f && f.initial_value !== undefined) {\n bits.push(`default \\`${String(f.initial_value)}\\``);\n }\n if (f.type === 'repeater' && 'min_items' in f && f.min_items)\n bits.push(`min ${f.min_items} rows`);\n return bits.join('; ');\n}\n\nfunction fieldRows(fields: BlockKitFieldDef[], prefix = ''): string[] {\n return fields.flatMap((f) => {\n const own = `| \\`${prefix}${f.action_id}\\` | ${f.type} | ${cell((f as { label?: string }).label)} | ${cell(describe(f))} |`;\n return f.type === 'repeater' ? [own, ...fieldRows(f.fields, `${f.action_id}[].`)] : [own];\n });\n}\n\n/**\n * The block reference an agent reads via `vitops docs blocks`: every Portable Text `_type`,\n * its fields and what each saves. Rendered from the same `BLOCKS` the editor menu uses, so\n * it cannot describe a field the editor doesn't offer.\n */\nexport function renderBlocksDoc(blocks: readonly PortableTextBlockDef[] = BLOCKS): string {\n const out = [\n '---',\n 'type: concept',\n 'title: EmDash blocks',\n 'description: Portable Text block types from @getvitops/emdash, with every field',\n '---',\n '',\n '# EmDash blocks',\n '',\n 'Each block is a Portable Text object: `{ \"_type\": \"vitops_cards\", \"_key\": \"<unique>\", ...fields }`.',\n 'Portable Text blocks are flat — they cannot contain other blocks, and a repeater row holds scalar fields only.',\n 'To put blocks inside something, use the section block TYPE (end of this page), whose body is Portable Text.',\n 'Give every block a unique `_key`; gallery and carousel derive their element id from it.',\n '',\n '## Colour: `tone` and `color`',\n '',\n 'Every block that colours something takes the same two fields. `tone` is a colour role from the',\n \"site's `colors.roles` (or a semantic name); an unknown role falls back through its semantic\",\n 'relatives to `neutral`. `color` is an escape hatch naming a palette hue (`amber`) or hue and step',\n \"(`amber-300`) and wins over `tone`; text on a hue solid is the site's `--color-<hue>-<step>-on`,\",\n 'but contrast is only guaranteed for roles. Renderers emit `--tone-*` custom properties, never a',\n 'class built from the value. Rows of `hero.cards`, `cards`, `stats` and `steps` (and `badges.items`)',\n \"take their own `tone`/`color`; blank inherits the block's, and a row's colour never reaches its\",\n 'siblings, so mixed colours need no extra block. Multiline fields that accept links take',\n '`[text](/url)`; hero headings accent words wrapped in `*stars*`.',\n '',\n '## Theme',\n '',\n 'Every block carries `theme`: a colour role, tinted (`surface`) or filled (`brand-primary:solid`), applied to',\n \"the block's own root with the role's computed readable text. It wins over the block's `tone`.\",\n '',\n '## Type size',\n '',\n 'Titles use the type roles (`font-heading`, `font-display`, `font-eyebrow`). Every role reads its size',\n \"from a custom property, `--<role>-fs` (`--heading-fs`, `--display-fs`), falling back to the role's\",\n 'configured size, so one block can resize its titles through its `style` field',\n '(`--heading-fs: 1.125rem`) with no site CSS; set it on an ancestor to resize a region. It is inherited,',\n 'so it reaches every use of that role inside the block. `style` accepts custom properties only.',\n 'Steps, stats and cards also have per-part hooks: `--steps-title-size` (default one step below the',\n 'heading role), `--stats-figure-size` and `--card-title-size`.',\n '',\n ];\n for (const b of blocks) {\n out.push(`## \\`${b.type}\\` — ${b.label}`, '');\n if (b.placeholder) out.push(`${b.placeholder}.`, '');\n if (b.fields?.length) {\n out.push(\n '| Field | Type | Label | Notes |',\n '| --- | --- | --- | --- |',\n ...fieldRows(b.fields),\n '',\n );\n }\n }\n out.push(\n '## `vitops_form` needs an endpoint',\n '',\n 'The form posts to `/api/track` (the `action` field overrides it), which must be an on-demand route',\n 'built with `createConversionRoute()` from `@getvitops/astro/routes`; the emdash template ships one.',\n 'Field names are submitted as written, except that `phone` becomes `tel` and a few names the route',\n 'reserves (`event`, `website`, `honeypot`, `_honeypot`, `cf-turnstile-response`) are renamed.',\n 'It emails `site.notifications.email` and writes `site.crm` when set; with neither, the route refuses',\n 'the post rather than accept a message nobody receives. Add the spam check with',\n '`vitops setup turnstile`: once `site.security.turnstile.siteKey` is set the block renders',\n '`<wc-turnstile>`, which loads Cloudflare Turnstile only when the visitor first uses the form',\n '(category `necessary`, so it is not behind the consent banner). The secret lives in the',\n '`VITOPS_TURNSTILE_SECRET` Worker secret, never in `site.json`. Use one form per page.',\n '`vitops emdash check-prerender` warns (fails under `--strict`) when a built page has a form',\n 'posting to `/api/track` and `site.json` configures nowhere to deliver it, or lists none of its fields',\n 'under `site.templates`.',\n '',\n 'A `checkboxes` field (\"tick any number\") posts one entry per ticked box under the same name; the route',\n 'joins them with `\", \"`. HTML cannot require \"at least one\", so `required` is not applied to the group.',\n '',\n 'The generated privacy policy reads `site.templates` form entries, not this block. Convert the saved',\n 'block with `formTemplateFromBlock(block)` from `@getvitops/emdash` and paste the result under',\n '`site.templates` in `site.json`, so the policy lists what the form collects.',\n '',\n '## Block types (the `blocks` field)',\n '',\n 'Schema-level block types, not Portable Text blocks: add them to the seed (`blockTypes: [...vitopsBlockTypes()]`),',\n \"allow the slug on the collection's `blocks` field, and render with `blockTypeComponents` from `@getvitops/emdash/astro`.\",\n '',\n );\n for (const t of vitopsBlockTypes()) {\n out.push(`### \\`${t.slug}\\` — ${t.label}`, '', `${t.description ?? ''}`, '');\n const fields = t.versions.find((v) => v.version === t.currentVersion)?.fields ?? [];\n out.push(\n '| Field | Type | Label | Notes |',\n '| --- | --- | --- | --- |',\n ...fields.map((f) => {\n const notes = [\n f.validation?.options ? `one of ${f.validation.options.join(', ')}` : '',\n f.defaultValue !== undefined ? `default \\`${String(f.defaultValue)}\\`` : '',\n f.type === 'portableText' ? 'accepts every `vitops_*` block above' : '',\n ].filter(Boolean);\n return `| \\`${f.slug}\\` | ${f.type} | ${cell(f.label)} | ${cell(notes.join('; '))} |`;\n }),\n '',\n );\n }\n return out.join('\\n');\n}\n","/**\n * Pure decisions for the `vitops_form` block: which of an editor's field rows become real\n * controls, and under what submitted names.\n *\n * Names matter more than they look. `createConversionRoute` (`@getvitops/astro/routes`) is\n * what receives the post, and it reserves a few: `phone` and `event` are the tel:/mailto:\n * click-beacon payload and are dropped from a form submission, `cf-turnstile-response` is\n * the Turnstile token, and `website`/`honeypot`/`_honeypot` are the honeypots (a filled one\n * makes the route answer success and deliver nothing). A visitor-facing field called\n * \"Phone\" must therefore not submit as `phone`, or the number silently vanishes from the\n * notification and the CRM record. `identifySubmitter` reads `tel` for a phone number, so\n * that is where it goes.\n */\n\nexport const FIELD_TYPES = [\n 'text',\n 'email',\n 'tel',\n 'textarea',\n 'select',\n 'checkbox',\n 'checkboxes',\n] as const;\nexport type FieldType = (typeof FIELD_TYPES)[number];\n\n/** Names the route treats specially. A field must never submit under one of these. */\nexport const RESERVED_NAMES = new Set([\n 'event',\n 'phone',\n 'cf-turnstile-response',\n 'website',\n 'honeypot',\n '_honeypot',\n '_next',\n]);\n\n/** The honeypot's name: the first of the route's own defaults, so no option needs passing. */\nexport const HONEYPOT_NAME = 'website';\n\nexport const MAX_FIELDS = 20;\n\nexport interface FormField {\n /** Submitted name. */\n name: string;\n /** DOM id suffix; unique within the form. */\n id: string;\n label: string;\n type: FieldType;\n required: boolean;\n placeholder: string;\n help: string;\n options: string[];\n autocomplete: string | undefined;\n}\n\nconst str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');\n\n/** `Your full name` -> `your_full_name`. Empty when the text holds nothing usable. */\nexport function slugName(input: string): string {\n return input\n .toLowerCase()\n .normalize('NFKD')\n .replace(/[̀-ͯ]/g, '')\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .slice(0, 40);\n}\n\n/** A submitted name for a row: the editor's `name` if usable, else one derived from the label. */\nexport function fieldName(\n raw: unknown,\n label: string,\n type: FieldType,\n taken: Set<string>,\n): string {\n let base = slugName(str(raw)) || slugName(label) || type;\n if (!/^[a-z]/.test(base)) base = `f_${base}`;\n if (base === 'phone') base = 'tel';\n if (RESERVED_NAMES.has(base)) base = `${base}_field`;\n let name = base;\n for (let n = 2; taken.has(name); n++) name = `${base}_${n}`;\n taken.add(name);\n return name;\n}\n\n/** The HTML `autocomplete` token for a field, or undefined when there is no honest one. */\nexport function autocompleteFor(name: string, type: FieldType): string | undefined {\n if (type === 'email') return 'email';\n if (type === 'tel') return 'tel';\n if (type !== 'text') return undefined;\n const map: Record<string, string> = {\n name: 'name',\n full_name: 'name',\n your_name: 'name',\n first_name: 'given-name',\n given_name: 'given-name',\n last_name: 'family-name',\n family_name: 'family-name',\n company: 'organization',\n organization: 'organization',\n organisation: 'organization',\n postal_code: 'postal-code',\n postcode: 'postal-code',\n zip: 'postal-code',\n city: 'address-level2',\n address: 'street-address',\n };\n return map[name];\n}\n\n/** Editor rows to renderable fields. Rows with no label are dropped; at most {@link MAX_FIELDS}. */\nexport function resolveFields(rows: Record<string, unknown>[]): FormField[] {\n const taken = new Set<string>();\n const out: FormField[] = [];\n for (const row of rows) {\n const label = str(row['label']);\n if (!label) continue;\n if (out.length >= MAX_FIELDS) break;\n const rawType = str(row['type']);\n const type = (FIELD_TYPES as readonly string[]).includes(rawType)\n ? (rawType as FieldType)\n : 'text';\n const name = fieldName(row['name'], label, type, taken);\n const options =\n type === 'select' || type === 'checkboxes'\n ? str(row['options'])\n .split('\\n')\n .map((o) => o.trim())\n .filter(Boolean)\n : [];\n // A select with nothing to choose is unusable; render it as free text instead.\n const effective: FieldType =\n (type === 'select' || type === 'checkboxes') && options.length === 0 ? 'text' : type;\n out.push({\n name,\n id: `form-${out.length + 1}-${name}`,\n label,\n type: effective,\n required: row['required'] === true,\n placeholder:\n effective === 'checkbox' || effective === 'select' || effective === 'checkboxes'\n ? ''\n : str(row['placeholder']),\n help: str(row['help']),\n options,\n autocomplete: autocompleteFor(name, effective),\n });\n }\n return out;\n}\n\n/**\n * Where the endpoint may send a visitor back to: a same-site path, nothing else. The form\n * posts its own page path as `_next`; the route that reads it must apply this too, or the\n * field becomes an open redirect.\n */\nexport function safeNext(input: unknown): string | null {\n const v = typeof input === 'string' ? input : '';\n if (!v.startsWith('/') || v.startsWith('//') || v.includes('\\\\') || v.length > 200) return null;\n // Control characters (and a query/fragment) are exactly what a redirect target must not carry.\n // eslint-disable-next-line no-control-regex\n if (/[\\u0000-\\u001f\\u007f#?]/.test(v)) return null;\n return v;\n}\n\n/** The endpoint a form posts to: a same-site path, defaulting to the conversion route. */\nexport function safeAction(input: unknown): string {\n return safeNext(input) ?? '/api/track';\n}\n","/**\n * A saved `vitops_form` block as a `site.templates` form entry.\n *\n * `vitops legal` derives \"what personal information we collect\" from `site.templates` entries\n * of type `form`, and a build cannot read the CMS database the editor's block lives in, so a\n * form added in the admin is invisible to the generated privacy policy. This turns a block's\n * saved value (copy it from the admin's JSON view, or from `emdash` content export) into the\n * entry to paste under `site.templates` in `site.json`.\n *\n * It resolves fields through the SAME `resolveFields` the renderer uses, so the names are the\n * ones the form actually submits (`phone` becomes `tel`, reserved names are renamed) and rows\n * the renderer drops (no label) are dropped here too. A `checkboxes` becomes a `checkbox`\n * with its choices as `options`: the policy's schema has no group type and both read as\n * \"your answers to the questions we ask\".\n */\nimport { resolveFields, safeAction } from './astro/blocks/form.ts';\n\n/** The `fields` row shape of a `site.templates` form entry (`FormFieldSchema`). */\nexport interface FormTemplateField {\n name: string;\n type: 'text' | 'email' | 'tel' | 'textarea' | 'select' | 'checkbox';\n label: string;\n required?: boolean;\n options?: { label: string; value: string }[];\n}\n\nexport interface FormTemplate {\n type: 'form';\n action: string;\n method: 'post';\n honeypot: true;\n fields: FormTemplateField[];\n}\n\n/** `block` is a saved `vitops_form` value (the object with `fields`, `action`, …). */\nexport function formTemplateFromBlock(block: unknown): FormTemplate {\n const value = (block && typeof block === 'object' ? block : {}) as Record<string, unknown>;\n const rawRows = Array.isArray(value['fields']) ? value['fields'] : [];\n const rows = rawRows.filter(\n (r): r is Record<string, unknown> => !!r && typeof r === 'object' && !Array.isArray(r),\n );\n const action = safeAction(value['action']);\n return {\n type: 'form',\n action,\n method: 'post',\n honeypot: true,\n fields: resolveFields(rows).map((f) => ({\n name: f.name,\n type: f.type === 'checkboxes' ? 'checkbox' : f.type,\n label: f.label,\n ...(f.required ? { required: true } : {}),\n ...(f.options.length ? { options: f.options.map((o) => ({ label: o, value: o })) } : {}),\n })),\n };\n}\n","/**\n * Hosting seam — one call in astro.config.mjs resolves the adapter + EmDash\n * database/storage for the chosen hosting target, so a site can start on\n * Cloudflare and move to a Node host (VPS / docker-compose / k8s) — or back —\n * by flipping a single value instead of rewiring its config:\n *\n * ```js\n * import { vitopsEmdash, vitopsHosting } from '@getvitops/emdash';\n * const { adapter, database, storage } = await vitopsHosting();\n * export default defineConfig({\n * output: 'server',\n * adapter,\n * integrations: [react(), emdash({ database, storage, plugins: [vitopsEmdash()] })],\n * });\n * ```\n *\n * Targets:\n * - `'cloudflare'` (default) — `@astrojs/cloudflare` + D1/R2 bindings.\n * Needs `@astrojs/cloudflare` and `@emdash-cms/cloudflare` installed.\n * - `'node'` — `@astrojs/node` (standalone) + SQLite file + local uploads.\n * Needs `@astrojs/node` and `better-sqlite3` installed. Scheduled publishing\n * runs in-process on Node (no worker/cron trigger needed). For production,\n * move storage to S3-compatible (`s3()` from `emdash/astro`) and/or the\n * database to Postgres (`postgres()` from `emdash/db`) via `options.node`.\n *\n * The adapter packages are optional peer dependencies, resolved lazily — a\n * site only installs the stack for the target it uses. Content lives in the\n * database and media in the storage backend, so switching hosts is a data\n * migration (D1 export / EmDash seed round-trip + media copy), not a rewrite.\n */\nimport type { AstroIntegration } from 'astro';\n\nexport type HostingTarget = 'cloudflare' | 'node';\n\nconst TARGETS: HostingTarget[] = ['cloudflare', 'node'];\n\nexport interface VitopsHostingOptions {\n /** Hosting target. Overridden by the `HOSTING` env var. Default 'cloudflare'. */\n target?: HostingTarget;\n cloudflare?: {\n /** D1 binding name in wrangler config (default 'DB'). */\n dbBinding?: string;\n /** R2 binding name in wrangler config (default 'MEDIA'). */\n mediaBinding?: string;\n /** D1 Sessions API read-replication mode (default 'auto'). */\n session?: string;\n /**\n * Batch reads issued together into one D1 round trip (default true).\n * Experimental in emdash: a read and a write started together may run\n * write-first, so await a read before a write that must not affect it.\n * No effect without a session.\n */\n coalesce?: boolean;\n };\n node?: {\n /** SQLite URL (default `DATABASE_PATH` env or 'file:./data/emdash.db'). */\n databaseUrl?: string;\n /** Full database descriptor override (e.g. postgres() from 'emdash/db'). */\n database?: unknown;\n /** Local uploads directory (default './data/uploads'). */\n uploadsDir?: string;\n /** Full storage descriptor override (e.g. s3() from 'emdash/astro'). */\n storage?: unknown;\n };\n}\n\nexport interface VitopsHosting {\n target: HostingTarget;\n adapter: AstroIntegration;\n /** EmDash database descriptor — pass to emdash({ database }). */\n database: unknown;\n /** EmDash storage descriptor — pass to emdash({ storage }). */\n storage: unknown;\n}\n\n/**\n * Import an optional hosting dependency. The specifier is passed as a\n * variable so bundlers/type-checkers leave it as a plain runtime import —\n * these packages are optional peers a consumer installs per target.\n */\nasync function importFor(target: HostingTarget, specifier: string, install: string) {\n try {\n return await import(/* @vite-ignore */ specifier);\n } catch (cause) {\n throw new Error(\n `[vitops] hosting target '${target}' needs ${specifier}, which isn't installed.\\n` +\n `Install the ${target} stack first: ${install}`,\n { cause },\n );\n }\n}\n\nexport async function vitopsHosting(options: VitopsHostingOptions = {}): Promise<VitopsHosting> {\n const target = (process.env.HOSTING as HostingTarget) || options.target || 'cloudflare';\n\n if (target === 'cloudflare') {\n const install = 'pnpm add @astrojs/cloudflare @emdash-cms/cloudflare';\n const cf = options.cloudflare ?? {};\n const { default: cloudflare } = await importFor(target, '@astrojs/cloudflare', install);\n const { d1, r2 } = await importFor(target, '@emdash-cms/cloudflare', install);\n return {\n target,\n adapter: cloudflare(),\n database: d1({\n binding: cf.dbBinding ?? 'DB',\n session: cf.session ?? 'auto',\n coalesce: cf.coalesce ?? true,\n }),\n storage: r2({ binding: cf.mediaBinding ?? 'MEDIA' }),\n };\n }\n\n if (target === 'node') {\n const install = 'pnpm add @astrojs/node better-sqlite3';\n const opts = options.node ?? {};\n const { default: node } = await importFor(target, '@astrojs/node', install);\n // Required peer of this package, so always present — but still routed\n // through importFor: a literal import('emdash/db') would be statically\n // analyzable, and bundling the plugin runtime would drag EmDash's whole\n // integration graph into the site's server/client bundles.\n const { sqlite } = await importFor(target, 'emdash/db', install);\n const { local } = await importFor(target, 'emdash/astro', install);\n const { mkdirSync } = await import('node:fs');\n const { dirname } = await import('node:path');\n\n const databaseUrl = opts.databaseUrl ?? process.env.DATABASE_PATH ?? 'file:./data/emdash.db';\n const uploadsDir = opts.uploadsDir ?? './data/uploads';\n // better-sqlite3 refuses to create parent directories, so make sure the\n // defaults exist (runs at astro config load — dev and build). A deployed\n // host still needs the data directory mounted/persisted as a volume.\n if (!opts.database && databaseUrl.startsWith('file:')) {\n mkdirSync(dirname(databaseUrl.slice('file:'.length)), { recursive: true });\n }\n if (!opts.storage) mkdirSync(uploadsDir, { recursive: true });\n\n return {\n target,\n adapter: node({ mode: 'standalone' }),\n database: opts.database ?? sqlite({ url: databaseUrl }),\n storage: opts.storage ?? local({ directory: uploadsDir, baseUrl: '/_emdash/api/media/file' }),\n };\n }\n\n throw new Error(\n `[vitops] unknown hosting target '${target}' (from ${process.env.HOSTING ? 'HOSTING env var' : 'options.target'}). ` +\n `Valid targets: ${TARGETS.join(', ')}.`,\n );\n}\n","","/**\n * @getvitops/emdash — EmDash CMS native plugin for the Vitops design system.\n *\n * Declares Vitops Portable Text block types (editable from the EmDash admin's\n * slash menu) and ships the Astro components that render them via the\n * `componentsEntry` (`@getvitops/emdash/astro`).\n *\n * Composes with — does not replace — the `vitops()` Astro integration from\n * `@getvitops/astro`: the integration owns design-system CSS generation and\n * copies the web-component bundles into `public/<wcBase>/`; this plugin only\n * adds the editor-facing layer. Register both in the consumer site:\n *\n * ```js\n * // astro.config.mjs\n * import config from './site.json' with { type: 'json' };\n *\n * integrations: [\n * react(),\n * vitops({ css: {...} }),\n * emdash({ database, storage, plugins: [vitopsEmdash({ designSystemEditor: { config } })] }),\n * ]\n * ```\n */\nimport { definePlugin, getMenus, getWidgetAreas } from 'emdash';\nimport type {\n PageFragmentContribution,\n PageFragmentEvent,\n PluginCapability,\n PluginContext,\n PluginDescriptor,\n} from 'emdash';\nimport {\n BLOCKS,\n MENUS_ROUTE,\n THEMES_ROUTE,\n TONES_ROUTE,\n WIDGET_AREAS_ROUTE,\n selectBlocks,\n type BlockSelection,\n} from './blocks.ts';\nimport { createMenusRoute } from './menus-route.ts';\nimport { createThemesRoute, createTonesRoute, rolesOf } from './tones-route.ts';\nimport {\n createDesignSystemRoute,\n designSystemOverrideFragment,\n type DesignSystemEditorOptions,\n} from './design-system-route.ts';\n\nexport { BLOCKS } from './blocks.ts';\nexport { selectBlocks } from './blocks.ts';\nexport { renderBlocksDoc } from './blocks-doc.ts';\nexport { formTemplateFromBlock } from './form-template.ts';\nexport type { FormTemplate, FormTemplateField } from './form-template.ts';\nexport { SECTION_BLOCK_TYPE, SECTION_SLUG, vitopsBlockTypes } from './block-types.ts';\nexport { resolveTone, toneChain } from './astro/blocks/tone.ts';\nexport type { BlockKitFieldDef, BlockSelection, PortableTextBlockDef } from './blocks.ts';\nexport { vitopsHosting } from './hosting.ts';\nexport type { HostingTarget, VitopsHosting, VitopsHostingOptions } from './hosting.ts';\nexport type { DesignSystemEditorOptions } from './design-system-route.ts';\n\n/**\n * Read from package.json rather than hand-maintained.\n *\n * This was a literal with a \"keep in sync\" comment, which drifted on every\n * release: `changeset version` bumps package.json and leaves the literal alone.\n * `blocks.test.ts` asserts they match, but the release chain didn't run tests,\n * so `@getvitops/emdash@0.2.1` shipped a descriptor reporting `0.2.0`. Deriving\n * it removes the failure mode instead of relying on remembering.\n */\nimport pkg from '../package.json' with { type: 'json' };\n\nconst VERSION = pkg.version;\nconst PLUGIN_ID = 'vitops';\nconst FRAGMENTS_CAPABILITY: PluginCapability = 'hooks.page-fragments:register';\n/** For the design-system-editor's GitHub auto-commit — see design-system-route.ts. */\nconst NETWORK_CAPABILITY: PluginCapability = 'network:request';\n\nexport interface VitopsEmdashOptions {\n /**\n * How the web-component runtime scripts reach the page:\n * - `'integration'` (default) — the site's layout renders `<Head />` from\n * `@getvitops/astro`, which emits the script tags; this plugin adds none.\n * - `'fragments'` — for layouts using EmDash's `<EmDashHead/>`/`<EmDashBodyEnd/>`\n * instead: the plugin injects the script tags via the `page:fragments` hook.\n * Either way the bundles themselves must exist under `public/<wcBase>/`,\n * which the `vitops()` integration provides (webComponents: true).\n * Do not enable both delivery paths — the scripts would load twice.\n */\n scripts?: 'integration' | 'fragments';\n /** Public base path of the web-component bundles (default '/vitops'). */\n wcBase?: string;\n /**\n * Mounts a `<wc-theme-editor>` admin page at `/design-system`, backed by a\n * plugin API route — see AGENTS.md's \"EmDash plugin as the design-system\n * editing surface\" section and `design-system-route.ts`'s own docblock.\n * Off by default. Requires the `network:request` capability (declared\n * automatically when this is set) for the GitHub auto-commit half —\n * reviewed once at plugin-install time, same as any other capability.\n */\n designSystemEditor?: DesignSystemEditorOptions | false;\n /**\n * Which blocks the editor offers. All of the `vitops_*` blocks by default. Use `exclude` to\n * drop one a client should not see, `include` for an allowlist, and `extra` to add the\n * site's own — whose renderers the site supplies, see {@link BlockSelection}.\n */\n blocks?: BlockSelection;\n /**\n * The site's colour role names (`Object.keys(site.designSystem.colors.roles)`), offered in\n * every block's `tone` select next to the semantic names. When omitted and\n * `designSystemEditor.config` is set, they are read from that config. With neither, the\n * select offers only the semantic names — an editor can still type a palette colour.\n */\n tones?: string[];\n}\n\n/**\n * Descriptor factory — import in `astro.config.mjs` and pass to the emdash()\n * integration's `plugins` array.\n */\nexport function vitopsEmdash(options: VitopsEmdashOptions = {}): PluginDescriptor {\n return {\n id: PLUGIN_ID,\n version: VERSION,\n format: 'native',\n entrypoint: '@getvitops/emdash',\n componentsEntry: '@getvitops/emdash/astro',\n ...(options.designSystemEditor ? { adminEntry: '@getvitops/emdash/admin' } : {}),\n options,\n capabilities: [\n ...(options.scripts === 'fragments' || options.designSystemEditor\n ? [FRAGMENTS_CAPABILITY]\n : []),\n ...(options.designSystemEditor ? [NETWORK_CAPABILITY] : []),\n ] as PluginCapability[],\n } as PluginDescriptor;\n}\n\n/** Runtime side — EmDash imports and calls this from the descriptor's entrypoint. */\nexport function createPlugin(options: VitopsEmdashOptions = {}) {\n const scripts = options.scripts ?? 'integration';\n const wcBase = options.wcBase ?? '/vitops';\n const editor = options.designSystemEditor || undefined;\n const fragmentsEnabled = scripts === 'fragments' || !!editor;\n // The site's colour roles, for every block's `tone` select: stated, or read from the config\n // the design-system editor already carries — see tones-route.ts.\n const roles = options.tones ?? rolesOf(editor?.config);\n\n // Mirrors @getvitops/astro Head.astro: module polyfills + elements in\n // head, deferred enhancements at end of body. Composed with the\n // design-system-editor's override fragment below — either half can be on\n // independently of the other.\n const pageFragments = async (\n _event: PageFragmentEvent,\n ctx: PluginContext,\n ): Promise<PageFragmentContribution[]> => {\n const contributions: PageFragmentContribution[] = [];\n if (scripts === 'fragments') {\n contributions.push(\n {\n kind: 'html',\n placement: 'head',\n html:\n `<link rel=\"modulepreload\" href=\"${wcBase}/polyfills.js\">` +\n `<script type=\"module\" src=\"${wcBase}/polyfills.js\"></script>` +\n `<script type=\"module\" src=\"${wcBase}/elements.js\"></script>`,\n key: 'vitops-wc',\n },\n {\n kind: 'external-script',\n placement: 'body:end',\n src: `${wcBase}/deferred.js`,\n defer: true,\n key: 'vitops-deferred',\n },\n );\n }\n if (editor) {\n const fragment = await designSystemOverrideFragment(ctx, editor);\n if (fragment) contributions.push(fragment);\n }\n return contributions;\n };\n\n return definePlugin({\n id: PLUGIN_ID,\n version: VERSION,\n capabilities: [\n ...(fragmentsEnabled ? [FRAGMENTS_CAPABILITY] : []),\n ...(editor ? [NETWORK_CAPABILITY] : []),\n ],\n ...(editor ? { allowedHosts: ['api.github.com'] } : {}),\n ...(fragmentsEnabled ? { hooks: { 'page:fragments': pageFragments } } : {}),\n\n admin: {\n portableTextBlocks: selectBlocks(options.blocks),\n ...(editor\n ? {\n entry: '@getvitops/emdash/admin',\n pages: [{ path: '/design-system', label: 'Design System', icon: 'palette' }],\n }\n : {}),\n },\n\n routes: {\n [TONES_ROUTE]: createTonesRoute(() => roles),\n [THEMES_ROUTE]: createThemesRoute(() => roles),\n [MENUS_ROUTE]: createMenusRoute(() => getMenus()),\n // Same shape as a menu summary (`name`, `label`), so the same route factory lists areas.\n [WIDGET_AREAS_ROUTE]: createMenusRoute(() => getWidgetAreas()),\n ...(editor ? { 'design-system': createDesignSystemRoute(editor) } : {}),\n },\n });\n}\n\nexport default createPlugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["pkg.version"],"sources":["../src/icon-options.ts","../src/astro/blocks/tone.ts","../src/block-types.ts","../src/blocks.ts","../src/menus-route.ts","../src/tones-route.ts","../src/design-system-route.ts","../src/blocks-doc.ts","../src/astro/blocks/form.ts","../src/form-template.ts","../src/hosting.ts","../package.json","../src/index.ts"],"sourcesContent":["/**\n * Semantic icon names, as Block Kit combobox options.\n *\n * Derived from `@getvitops/utils`' `iconMap` at import time. This used to be a\n * generated copy, so that the package carried no runtime `@getvitops/*`\n * dependency and could version independently of the fixed toolchain group —\n * but that independence was already fiction: the blocks render from the\n * toolchain's SVG sprite (`<use href=\"…/icons.svg#icon-menu\">`), so an emdash\n * built against one icon vocabulary and a sprite built against another produce\n * empty boxes, silently and with no install-time error. Depending on the real\n * map makes the vocabulary one thing rather than two that can drift.\n *\n * `fa7` is the reference collection: every semantic name is defined there, and\n * the other sets are alternate resolutions of the same names.\n */\nimport { iconMap } from '@getvitops/utils';\n\nexport const SEMANTIC_ICON_OPTIONS: { label: string; value: string }[] = Object.keys(iconMap.fa7)\n .sort()\n // Label and value are the same string on purpose: the editor picks a semantic\n // name, and showing the set-specific glyph id would leak the icon set into\n // authored content — the thing the semantic layer exists to prevent.\n .map((name) => ({ label: name, value: name }));\n","/**\n * The shared `tone` convention (UPSTREAM finding 21): every block that colours anything\n * takes one `tone` and, optionally, one `color`.\n *\n * - **`tone`** — a role name from the site's `colors.roles` (`brand-primary`, `danger`, a\n * consumer-named surface role…), or a semantic name. Role first, then its semantic\n * fallbacks, then `neutral`.\n * - **`color`** — the escape hatch: a palette hue (`amber`) or hue and step (`amber-300`).\n * Wins over `tone` when set. Contrast is NOT checked for it — the generator's contrast\n * contract covers roles, not arbitrary hue/step pairs — so it is for decoration. A solid fill\n * reads from the site's `--color-<hue>-<step>-on` and the tint flips with the colour scheme\n * (`light-dark()`); mid steps (300–500) cannot reach APCA Lc 75 with either text colour, so use\n * a role where contrast matters.\n *\n * **Why CSS custom properties, not classes.** A class name built from a field value\n * (`bg-${tone}`) is invisible to Tailwind's scanner, so it generates nothing in that\n * format. The resolver instead returns an inline `style` of `--tone-*` properties that\n * the framework's `.tone` pattern (patterns/tone.css) reads. Each property is a `var()`\n * chain, so the fallback happens in CSS at render time and this module needs NO copy of\n * the site's config — a tone the site never defined degrades to `neutral`, not to\n * nothing.\n *\n * The inputs are editor-supplied strings that land inside a `style` attribute, so both are\n * validated against a closed identifier grammar before use; anything else is dropped.\n */\n\n/** Semantic names every site is expected to define (`REQUIRED_ROLES`), plus common aliases. */\nexport const SEMANTIC_TONES = ['neutral', 'info', 'success', 'warning', 'danger'] as const;\n\n/**\n * When a role is absent, which others to try before `neutral`. Covers the framework's own\n * vocabulary (`ui-*`, `brand-*`) so a site that names only some of them still gets the\n * nearest relative rather than a grey block.\n */\nconst FALLBACKS: Record<string, string[]> = {\n 'brand-primary': ['ui-primary', 'info'],\n 'brand-secondary': ['ui-secondary', 'info'],\n 'ui-primary': ['info'],\n 'ui-secondary': ['info'],\n 'ui-accent': ['warning'],\n surface: ['neutral'],\n error: ['danger'],\n notice: ['warning'],\n note: ['info'],\n // Retired vitops.ca vocabulary some stored content still carries.\n it: ['ui-primary', 'info'],\n mk: ['ui-accent', 'warning'],\n};\n\nconst ROLE = /^[a-z][a-z0-9-]{0,40}$/;\nconst HUE = /^([a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*?)(?:-(\\d{2,3}))?$/;\n\n/** The role chain for a tone: itself, its relatives, then `neutral`. Pure and exported for tests. */\nexport function toneChain(tone: unknown): string[] {\n const name = typeof tone === 'string' ? tone.trim().toLowerCase() : '';\n if (!ROLE.test(name)) return ['neutral'];\n return [...new Set([name, ...(FALLBACKS[name] ?? []), 'neutral'])];\n}\n\n/** `var(--a, var(--b, …))` over a chain of token names; the last link has no fallback. */\nfunction chain(tokens: string[]): string {\n return tokens.reduceRight<string>(\n (inner, token) => (inner ? `var(${token}, ${inner})` : `var(${token})`),\n '',\n );\n}\n\nexport interface ResolvedTone {\n /** Always `tone`; a block adds `tone--solid` itself when it fills rather than tints. */\n className: string;\n /** `tone tone--tint`: what a block that is a surface (callout, card, band) puts on its root. */\n surface: string;\n /** Inline `style` value of `--tone-*` properties (no trailing semicolon). Empty when unset. */\n style: string;\n}\n\n/**\n * Resolve a block's `tone` / `color` fields.\n *\n * Returns an empty style when neither is set, so an unconfigured block carries no tone at\n * all (rather than a neutral one) and stays transparent.\n */\nexport function resolveTone(tone?: unknown, color?: unknown): ResolvedTone {\n const hasTone = typeof tone === 'string' && tone.trim() !== '';\n const hue = typeof color === 'string' ? HUE.exec(color.trim().toLowerCase()) : null;\n\n if (hue) {\n const [, name, step] = hue;\n const at = (s: string) => `var(--color-${name}-${s})`;\n // The foreground is the site's own: the generator emits `--color-<hue>-<step>-on` for every\n // step, picked by the same APCA rule as `text-on-<role>`, so a fill by hue and step reads\n // without a fixed white and without asking the browser to guess.\n const fill = step ?? '600';\n // Raw hue steps never re-point in dark, so the tint flips here: `light-dark()` follows the\n // `color-scheme` the generator sets under the dark selector.\n const flip = (light: string, dark: string) => `light-dark(${at(light)}, ${at(dark)})`;\n return {\n className: 'tone',\n surface: 'tone tone--tint',\n style: [\n `--tone-bg: ${flip('100', '900')}`,\n `--tone-fg: ${flip('900', '100')}`,\n `--tone-border: ${flip('300', '700')}`,\n `--tone-solid: ${at(fill)}`,\n `--tone-on-solid: ${at(`${fill}-on`)}`,\n ].join('; '),\n };\n }\n\n if (!hasTone) return { className: 'tone', surface: 'tone', style: '' };\n\n const roles = toneChain(tone);\n const of = (prefix: string, suffix = '') =>\n chain(roles.map((r) => `--color-${prefix}-${r}${suffix}`));\n return {\n className: 'tone',\n surface: 'tone tone--tint',\n style: [\n `--tone-bg: ${of('bg', '-muted')}`,\n `--tone-fg: ${of('text')}`,\n `--tone-border: ${of('border')}`,\n // A surface role has no `-solid`, only a bare `bg-<role>`; the solid chain ends there.\n `--tone-solid: ${chain([...roles.map((r) => `--color-bg-${r}-solid`), ...roles.map((r) => `--color-bg-${r}`)])}`,\n `--tone-on-solid: ${chain(roles.map((r) => `--color-text-on-${r}`))}`,\n ].join('; '),\n };\n}\n\n/**\n * Join a block's own inline style with a resolved tone's, so a renderer can pass one\n * `style` attribute. Empty parts are skipped.\n */\nexport function joinStyle(...parts: (string | undefined | false)[]): string | undefined {\n const out = parts.filter(Boolean).join('; ');\n return out || undefined;\n}\n\n/**\n * A repeater row's own colour: the `style` that overrides the block's `--tone-*` properties for\n * that item, or `undefined` when the row sets none (it then inherits the block's). Only the custom\n * properties are written, so the row picks up the block's own `.tone` surface and just recolours it.\n */\nexport function rowTone(row: Record<string, unknown>): string | undefined {\n return resolveTone(row.tone, row.color).style || undefined;\n}\n\n/**\n * The `theme` every block can carry: a backdrop for the block's own root element, chosen from the\n * site's colour roles. `<role>` is the tinted surface (`bg-<role>-muted`, with the role's text);\n * `<role>:solid` is the filled backdrop with the contrast-computed `text-on-<role>`; `none` or\n * nothing is no theme. Contrast is therefore the generator's guarantee, not the editor's judgement,\n * and a site that styles its roles as raised, glass or flat gets that look, because the tokens are\n * the site's own.\n *\n * Returns the classes and `--tone-*` style to put on the root element, or `undefined` for no theme.\n */\nexport function resolveTheme(theme: unknown): { className: string; style: string } | undefined {\n if (typeof theme !== 'string') return undefined;\n const [role, fill] = theme\n .trim()\n .toLowerCase()\n .replace(/^none$/, '')\n .split(':');\n if (!role || !ROLE.test(role)) return undefined;\n const tone = resolveTone(role);\n if (!tone.style) return undefined;\n return {\n className: fill === 'solid' ? 'tone tone--solid' : 'tone tone--tint',\n style: tone.style,\n };\n}\n","/**\n * Block TYPES (EmDash's schema-level `blocks` field) the plugin ships, as opposed to the\n * Portable Text blocks in `blocks.ts`.\n *\n * **Why `vitops_section` is not in `BLOCKS`.** A plugin Portable Text block is declared\n * with Block Kit fields — scalars, selects, repeaters of scalars — and Block Kit has no\n * rich-text element, so such a block cannot hold other blocks. A section's whole point is a\n * body of other blocks, and the one EmDash feature with a `portableText` field type is the\n * database-backed **block type** a `blocks` field is built from (see EmDash's blocks guide).\n * Those are created through the seed file or the schema API — a plugin cannot register one —\n * so this module exports the definition for the site to put in `seed/seed.json`'s\n * `blockTypes`, and `blockTypeComponents` (in `@getvitops/emdash/astro`) renders it.\n *\n * Inside the section's `body`, every `vitops_*` Portable Text block resolves normally: the\n * body is rendered by EmDash's `<PortableText>`, which merges this plugin's component map.\n *\n * Block types cannot be renamed or hard-deleted once shipped, so the slug follows the\n * framework's pattern name (`section`) with this package's prefix, and the field slugs are\n * the ones the renderer reads. Add fields in a compatible version; never repurpose one.\n */\nimport type { ApplySeedBlockTypeInput, BlockFieldDefinition } from 'emdash';\n\n/** Block-type slugs may not contain a dot, so `vitops_section` is stored as `vitops_section`. */\nexport const SECTION_SLUG = 'vitops_section';\n\nconst select = (\n slug: string,\n label: string,\n options: string[],\n defaultValue: string,\n): BlockFieldDefinition => ({\n slug,\n label,\n type: 'select',\n defaultValue,\n validation: { options },\n});\n\n/**\n * The `theme` select's values: `none`, a role, or `<role>:solid`. Block-type selects are\n * static, so this lists the roles every site defines; any other site role still works through\n * the `tone` field. `<role>:solid` is the filled backdrop with the contrast-computed text.\n */\nexport const THEME_ROLES = [\n 'neutral',\n 'surface',\n 'brand-primary',\n 'brand-secondary',\n 'info',\n 'success',\n 'warning',\n 'danger',\n];\nexport const THEMES = ['none', ...THEME_ROLES, ...THEME_ROLES.map((r) => `${r}:solid`)];\n\nexport const SECTION_FIELDS: BlockFieldDefinition[] = [\n { slug: 'kicker', label: 'Kicker (small line above the heading)', type: 'string' },\n { slug: 'heading', label: 'Heading', type: 'string' },\n select(\n 'level',\n 'Heading level (p: styled like a heading, not in the outline)',\n ['1', '2', '3', '4', '5', '6', 'p'],\n '2',\n ),\n { slug: 'sub', label: 'Lead-in text under the heading', type: 'text' },\n { slug: 'cta_label', label: 'Button text (filled)', type: 'string' },\n { slug: 'cta_href', label: 'Button URL', type: 'string' },\n { slug: 'secondary_label', label: 'Second link text', type: 'string' },\n { slug: 'secondary_href', label: 'Second link URL', type: 'string' },\n { slug: 'body', label: 'Content (any blocks)', type: 'portableText' },\n select('layout', 'Layout', ['stack', 'center', 'split'], 'stack'),\n select(\n 'stack',\n 'Spacing (rhythm: head and body use the vertical-rhythm scale, as a page opener or prose stack)',\n ['grid', 'rhythm'],\n 'grid',\n ),\n select('width', 'Width', ['measure', 'breakout', 'spotlight', 'fullbleed'], 'measure'),\n {\n slug: 'tone',\n label: 'Backdrop tone (a colour role from the site, e.g. surface or brand-primary)',\n type: 'string',\n },\n {\n slug: 'color',\n label: 'Palette colour instead of a tone (e.g. amber or amber-300)',\n type: 'string',\n },\n { slug: 'solid', label: 'Fill with the solid colour', type: 'boolean', defaultValue: false },\n select(\n 'theme',\n 'Theme (backdrop and readable text in one pick; overrides the tone fields)',\n THEMES,\n 'none',\n ),\n { slug: 'anchor', label: 'Anchor id (so a link can point here with #id)', type: 'string' },\n { slug: 'class_name', label: 'Extra CSS classes', type: 'string' },\n { slug: 'style', label: 'CSS custom properties, e.g. --p-card: 2rem', type: 'string' },\n];\n\n/** `vitops_section`: kicker, heading, lead-in, tone/backdrop, optional heading-left layout, and a body of blocks. */\nexport const SECTION_BLOCK_TYPE: ApplySeedBlockTypeInput = {\n slug: SECTION_SLUG,\n label: 'Section',\n description: 'A titled page section whose body can hold any other blocks.',\n category: 'Sections',\n icon: 'layout',\n currentVersion: 1,\n versions: [{ version: 1, fields: SECTION_FIELDS }],\n};\n\n/**\n * Every block type this plugin ships, in the shape `seed.json`'s `blockTypes` takes. Spread\n * it into the seed (and allow the slugs on the collection's `blocks` field), e.g.\n * `blockTypes: [...vitopsBlockTypes()]`.\n */\nexport function vitopsBlockTypes(): ApplySeedBlockTypeInput[] {\n return [SECTION_BLOCK_TYPE];\n}\n","/**\n * Portable Text block-type declarations for the EmDash editor.\n *\n * Fields are EmDash Block Kit elements. Scalars (text_input / number_input / select /\n * toggle / combobox) cover single-valued patterns; `repeater` (rows of scalar\n * sub-fields) covers the repeating ones — cards, gallery, accordion, marquee, carousel\n * slides — and `media_picker` picks one asset from the media library. Both are\n * supported by the Portable Text block editor but not by Block Kit admin pages, which\n * is why the type below names them explicitly rather than accepting any `Element`.\n *\n * Patterns that need other blocks INSIDE them (a section with columns, a card holding a\n * button) cannot be a Portable Text block — a repeater holds scalars only. Those are\n * schema-level block TYPES with a `portableText` field, in `block-types.ts` (`vitops_section`).\n *\n * Every `action_id` here must have a matching prop consumed by the block's\n * .astro component in `src/astro/blocks/` (enforced by blocks.test.ts).\n */\n\nimport type { PortableTextBlockConfig } from 'emdash';\nimport { SEMANTIC_ICON_OPTIONS } from './icon-options.ts';\nimport { SEMANTIC_TONES } from './astro/blocks/tone.ts';\nimport { THEME_ROLES } from './block-types.ts';\n\n/** The subset of EmDash Block Kit field elements these blocks use. */\nexport type BlockKitFieldDef = Extract<\n NonNullable<PortableTextBlockConfig['fields']>[number],\n {\n type:\n | 'text_input'\n | 'number_input'\n | 'select'\n | 'toggle'\n | 'combobox'\n | 'repeater'\n | 'media_picker';\n }\n>;\n\nexport type PortableTextBlockDef = Omit<PortableTextBlockConfig, 'fields'> & {\n fields?: BlockKitFieldDef[];\n};\n\n/** Route that fills every tone select with the site's own roles — see `index.ts`. */\nexport const TONES_ROUTE = 'tones/list';\n/** Lists each colour role twice: tinted (`role`) and filled (`role:solid`), for every block's `theme`. */\nexport const THEMES_ROUTE = 'themes/list';\n\n/** Route that lists the site's EmDash menus for `vitops_nav`'s `menu` select — see `menus-route.ts`. */\nexport const MENUS_ROUTE = 'menus/list';\n\n/**\n * The tone options an editor can always pick, before the site adds its own roles.\n * Static because Block Kit needs a non-empty `options` list even when `optionsRoute` is set\n * (it is the fallback if the route fails, and what non-block-editor renderers read).\n */\nexport const STATIC_TONE_OPTIONS = [\n { label: 'None', value: '' },\n ...SEMANTIC_TONES.map((t) => ({ label: t[0]!.toUpperCase() + t.slice(1), value: t })),\n];\n\n/**\n * The shared tone pair (UPSTREAM 21; resolution lives in `astro/blocks/tone.ts`): a `tone`\n * select that offers the site's roles, and a `color` escape hatch naming a palette hue.\n * Spread into any block that colours something.\n */\nfunction toneFields(): BlockKitFieldDef[] {\n return [\n {\n type: 'select',\n action_id: 'tone',\n label: 'Tone (a colour role from the site)',\n initial_value: '',\n options: STATIC_TONE_OPTIONS,\n optionsRoute: TONES_ROUTE,\n } as BlockKitFieldDef,\n {\n type: 'text_input',\n action_id: 'color',\n label: 'Palette colour instead of a tone (e.g. amber or amber-300)',\n },\n ];\n}\n\n/** Route that lists the site's widget areas for `vitops_widgetArea`'s `area` select. */\nexport const WIDGET_AREAS_ROUTE = 'widget-areas/list';\n\n/**\n * The escape hatches every block carries, appended last by `BLOCKS` (see `astro/blocks/attrs.ts`\n * for how each is validated and why `style` takes custom properties only).\n */\nexport function commonFields(): BlockKitFieldDef[] {\n return [\n {\n type: 'select',\n action_id: 'theme',\n label: 'Theme (a backdrop with readable text, from the site colours)',\n initial_value: '',\n options: [\n { label: 'None', value: '' },\n ...THEME_ROLES.flatMap((r) => [\n { label: r.replace(/-/g, ' '), value: r },\n { label: `${r.replace(/-/g, ' ')} (filled)`, value: `${r}:solid` },\n ]),\n ],\n optionsRoute: THEMES_ROUTE,\n } as BlockKitFieldDef,\n {\n type: 'text_input',\n action_id: 'anchor',\n label: 'Anchor id (so a link can point here with #id)',\n },\n {\n type: 'text_input',\n action_id: 'className',\n label: 'Extra CSS classes (with the tailwind format, only classes the site already uses)',\n },\n {\n type: 'text_input',\n action_id: 'style',\n label: 'CSS custom properties, e.g. --p-card: 2rem; --tone-bg: #fee',\n },\n ];\n}\n\n/** Heading level for a block's own heading; `h1` is reserved for the page title. */\nconst levelField = (initial: string): BlockKitFieldDef => ({\n type: 'select',\n action_id: 'level',\n label: 'Heading level',\n initial_value: initial,\n options: ['2', '3', '4', '5', '6'].map((n) => ({ label: `h${n}`, value: n })),\n});\n\n/**\n * Heading level for a block that can own the page title (hero): `h1`, or `p` for a heading that is\n * visual only and should not appear in the outline.\n */\nconst titleLevelField = (initial: string): BlockKitFieldDef => ({\n type: 'select',\n action_id: 'level',\n label: 'Heading level (h1 is the page title; p is styled like a heading but not in the outline)',\n initial_value: initial,\n options: [\n ...['1', '2', '3', '4', '5', '6'].map((n) => ({ label: `h${n}`, value: n })),\n { label: 'p (not a heading)', value: 'p' },\n ],\n});\n\nconst BASE_BLOCKS: PortableTextBlockDef[] = [\n {\n /**\n * A link or button, optionally with an icon at either end.\n *\n * Flattened on purpose: Block Kit has no object-group element, so a shape\n * like `{ label, url }` becomes sibling fields. Same constraint the\n * marketing-blocks plugin documents.\n *\n * The icons are `combobox` — a searchable typeahead over a STATIC option\n * list, which is exactly what the semantic name list is. It is the closest\n * fit Block Kit offers: its element union is closed (no custom element, no\n * raw HTML), so `<wc-icon-picker>` cannot be mounted inside a block modal\n * however much nicer it would be. Revisit if EmDash opens that union.\n */\n type: 'vitops_actionLink',\n label: 'Link or button',\n icon: 'link',\n placeholder: 'A link or button, with optional icons',\n fields: [\n { type: 'text_input', action_id: 'label', label: 'Label' },\n { type: 'text_input', action_id: 'href', label: 'URL' },\n {\n type: 'select',\n action_id: 'variant',\n label: 'Style',\n initial_value: 'link',\n options: [\n { label: 'Link', value: 'link' },\n { label: 'Button', value: 'btn' },\n { label: 'Call to action', value: 'cta' },\n ],\n },\n {\n type: 'combobox',\n action_id: 'startIcon',\n label: 'Icon before the label',\n placeholder: 'None',\n options: SEMANTIC_ICON_OPTIONS,\n },\n {\n type: 'combobox',\n action_id: 'endIcon',\n label: 'Icon after the label',\n placeholder: 'None',\n options: SEMANTIC_ICON_OPTIONS,\n },\n {\n type: 'select',\n action_id: 'role',\n label: 'Colour role',\n initial_value: '',\n options: [\n { label: 'Default', value: '' },\n { label: 'Success', value: 'success' },\n { label: 'Danger', value: 'danger' },\n { label: 'Warning', value: 'warning' },\n { label: 'Info', value: 'info' },\n ],\n },\n { type: 'toggle', action_id: 'newTab', label: 'Open in a new tab', initial_value: false },\n ],\n },\n {\n type: 'vitops_imageCompare',\n label: 'Image compare',\n placeholder: 'Before/after image slider',\n fields: [\n { type: 'text_input', action_id: 'before', label: 'Before image URL' },\n { type: 'text_input', action_id: 'beforeAlt', label: 'Before alt text' },\n {\n type: 'text_input',\n action_id: 'beforeLabel',\n label: 'Before label',\n initial_value: 'Before',\n },\n { type: 'text_input', action_id: 'after', label: 'After image URL' },\n { type: 'text_input', action_id: 'afterAlt', label: 'After alt text' },\n { type: 'text_input', action_id: 'afterLabel', label: 'After label', initial_value: 'After' },\n ],\n },\n {\n type: 'vitops_copyButton',\n label: 'Copy snippet',\n icon: 'code',\n placeholder: 'Text with a copy-to-clipboard button',\n fields: [\n { type: 'text_input', action_id: 'text', label: 'Text to copy', multiline: true },\n { type: 'text_input', action_id: 'label', label: 'Button label', initial_value: 'Copy' },\n {\n type: 'select',\n action_id: 'display',\n label: 'Display',\n initial_value: 'code-block',\n options: [\n { label: 'Code block', value: 'code-block' },\n { label: 'Inline', value: 'inline' },\n ],\n },\n ],\n },\n {\n type: 'vitops_banner',\n label: 'Banner',\n placeholder: 'Announcement or notice',\n fields: [\n { type: 'text_input', action_id: 'message', label: 'Message', multiline: true },\n {\n type: 'select',\n action_id: 'tone',\n label: 'Tone',\n initial_value: 'info',\n options: [\n { label: 'Info', value: 'info' },\n { label: 'Success', value: 'success' },\n { label: 'Warning', value: 'warning' },\n { label: 'Danger', value: 'danger' },\n ],\n },\n { type: 'toggle', action_id: 'dismissible', label: 'Dismissible', initial_value: true },\n ],\n },\n {\n type: 'vitops_details',\n label: 'Disclosure',\n placeholder: 'Collapsible details/summary',\n fields: [\n { type: 'text_input', action_id: 'summary', label: 'Summary (always visible)' },\n { type: 'text_input', action_id: 'body', label: 'Body (revealed)', multiline: true },\n { type: 'toggle', action_id: 'open', label: 'Open by default', initial_value: false },\n ],\n },\n {\n type: 'vitops_carousel',\n label: 'Carousel',\n placeholder: 'Image carousel',\n category: 'Media',\n fields: [\n {\n type: 'repeater',\n action_id: 'slides',\n label: 'Slides',\n item_label: 'slide',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'src', label: 'Image URL' },\n { type: 'text_input', action_id: 'alt', label: 'Alt text' },\n ],\n },\n {\n type: 'text_input',\n action_id: 'images',\n label: 'Image URLs, one per line (legacy — use Slides)',\n multiline: true,\n },\n {\n type: 'text_input',\n action_id: 'alts',\n label: 'Alt texts, one per line (legacy — use Slides)',\n multiline: true,\n },\n {\n type: 'text_input',\n action_id: 'label',\n label: 'Accessible name',\n initial_value: 'Carousel',\n },\n {\n type: 'number_input',\n action_id: 'autoplay',\n label: 'Autoplay interval (ms, 0 = off)',\n initial_value: 0,\n },\n {\n type: 'toggle',\n action_id: 'loop',\n label: 'Loop (clones the slides — autoplay implies it)',\n initial_value: false,\n },\n {\n type: 'text_input',\n action_id: 'aspect',\n label: 'Slide aspect ratio',\n initial_value: '16 / 9',\n },\n ],\n },\n {\n type: 'vitops_gallery',\n label: 'Gallery',\n placeholder: 'Thumbnail grid that opens a lightbox carousel',\n category: 'Media',\n fields: [\n {\n type: 'text_input',\n action_id: 'label',\n label: 'Accessible name',\n initial_value: 'Gallery',\n },\n {\n type: 'repeater',\n action_id: 'images',\n label: 'Images',\n item_label: 'image',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'src', label: 'Image URL' },\n { type: 'text_input', action_id: 'alt', label: 'Alt text' },\n { type: 'text_input', action_id: 'caption', label: 'Caption' },\n ],\n },\n {\n type: 'text_input',\n action_id: 'aspect',\n label: 'Thumbnail aspect ratio',\n initial_value: '1',\n },\n { type: 'toggle', action_id: 'loop', label: 'Wrap around at the ends', initial_value: false },\n ],\n },\n {\n type: 'vitops_cards',\n label: 'Cards',\n placeholder: 'A grid of cards',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'cards',\n label: 'Cards',\n item_label: 'card',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'title', label: 'Title' },\n {\n type: 'text_input',\n action_id: 'body',\n label: 'Text (write [link text](/url) for an inline link)',\n multiline: true,\n },\n { type: 'text_input', action_id: 'image', label: 'Image URL' },\n { type: 'text_input', action_id: 'imageAlt', label: 'Image alt text' },\n {\n type: 'text_input',\n action_id: 'href',\n label: 'Link URL (makes the whole card clickable)',\n },\n {\n type: 'text_input',\n action_id: 'linkLabel',\n label: 'Link text',\n initial_value: 'Read more',\n },\n {\n type: 'text_input',\n action_id: 'checklist',\n label: 'Checklist (panel style; one item per line)',\n multiline: true,\n },\n { type: 'text_input', action_id: 'ctaLabel', label: 'Button text (panel style)' },\n { type: 'text_input', action_id: 'ctaHref', label: 'Button URL (panel style)' },\n ],\n },\n {\n type: 'select',\n action_id: 'variant',\n label: 'Style',\n initial_value: 'default',\n options: [\n { label: 'Cards', value: 'default' },\n { label: 'Panels (tinted, with checklist and button)', value: 'panel' },\n ],\n },\n levelField('3'),\n ...toneFields(),\n {\n type: 'select',\n action_id: 'columns',\n label: 'Columns',\n initial_value: '3',\n options: [\n { label: '1', value: '1' },\n { label: '2', value: '2' },\n { label: '3', value: '3' },\n { label: '4', value: '4' },\n ],\n },\n ],\n },\n {\n type: 'vitops_accordion',\n label: 'Accordion',\n placeholder: 'A group of collapsible sections',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'items',\n label: 'Items',\n item_label: 'item',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'summary', label: 'Summary (always visible)' },\n { type: 'text_input', action_id: 'body', label: 'Body (revealed)', multiline: true },\n { type: 'toggle', action_id: 'open', label: 'Open by default', initial_value: false },\n ],\n },\n {\n type: 'toggle',\n action_id: 'exclusive',\n label: 'Only one open at a time',\n initial_value: false,\n },\n ],\n },\n {\n type: 'vitops_hero',\n label: 'Hero',\n placeholder: 'Page opener: heading, text, buttons and an optional image',\n category: 'Sections',\n fields: [\n { type: 'text_input', action_id: 'eyebrow', label: 'Eyebrow (small line above)' },\n {\n type: 'text_input',\n action_id: 'heading',\n label: 'Heading (wrap words in *stars* to accent them)',\n },\n titleLevelField('1'),\n { type: 'text_input', action_id: 'body', label: 'Text', multiline: true },\n { type: 'text_input', action_id: 'primaryLabel', label: 'Main button text' },\n { type: 'text_input', action_id: 'primaryHref', label: 'Main button URL' },\n { type: 'text_input', action_id: 'secondaryLabel', label: 'Second link text' },\n { type: 'text_input', action_id: 'secondaryHref', label: 'Second link URL' },\n { type: 'text_input', action_id: 'image', label: 'Image URL (split layout)' },\n { type: 'text_input', action_id: 'imageAlt', label: 'Image alt text' },\n {\n type: 'select',\n action_id: 'layout',\n label: 'Layout',\n initial_value: 'centered',\n options: [\n { label: 'Centered', value: 'centered' },\n { label: 'Text and image side by side', value: 'split' },\n ],\n },\n ...toneFields(),\n {\n type: 'repeater',\n action_id: 'cards',\n label: 'Option cards under the buttons (optional)',\n item_label: 'card',\n fields: [\n { type: 'text_input', action_id: 'title', label: 'Title' },\n {\n type: 'text_input',\n action_id: 'body',\n label: 'Text (write [link text](/url) for an inline link)',\n multiline: true,\n },\n { type: 'text_input', action_id: 'href', label: 'Link URL (makes the card clickable)' },\n {\n type: 'text_input',\n action_id: 'linkLabel',\n label: 'Link text',\n initial_value: 'Learn more',\n },\n ],\n },\n ],\n },\n {\n type: 'vitops_marquee',\n label: 'Marquee',\n placeholder: 'A continuously scrolling row (logos, quotes, badges)',\n category: 'Media',\n fields: [\n {\n type: 'repeater',\n action_id: 'items',\n label: 'Items',\n item_label: 'item',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'text', label: 'Text' },\n { type: 'text_input', action_id: 'image', label: 'Image URL (shown instead of text)' },\n { type: 'text_input', action_id: 'alt', label: 'Image alt text' },\n ],\n },\n { type: 'toggle', action_id: 'pauseOnHover', label: 'Pause on hover', initial_value: true },\n { type: 'toggle', action_id: 'reverse', label: 'Scroll the other way', initial_value: false },\n ],\n },\n {\n type: 'vitops_logos',\n label: 'Logos',\n placeholder: 'A still row of partner or client logos',\n category: 'Media',\n fields: [\n {\n type: 'text_input',\n action_id: 'label',\n label: 'Accessible name',\n initial_value: 'Partners',\n },\n { type: 'text_input', action_id: 'kicker', label: 'Line above the row (optional)' },\n { type: 'text_input', action_id: 'note', label: 'Closing note under the row (optional)' },\n { type: 'text_input', action_id: 'linkLabel', label: 'Link text under the row (optional)' },\n { type: 'text_input', action_id: 'linkHref', label: 'Link URL' },\n {\n type: 'repeater',\n action_id: 'logos',\n label: 'Logos',\n item_label: 'logo',\n min_items: 1,\n fields: [\n {\n type: 'text_input',\n action_id: 'image',\n label: 'Image URL (leave empty to show the name as text)',\n },\n {\n type: 'text_input',\n action_id: 'alt',\n label: 'Name (alt text, or the text shown with no image)',\n },\n { type: 'text_input', action_id: 'href', label: 'Link URL (optional)' },\n ],\n },\n { type: 'toggle', action_id: 'grayscale', label: 'Grey until hovered', initial_value: false },\n {\n type: 'select',\n action_id: 'size',\n label: 'Logo height',\n initial_value: 'md',\n options: [\n { label: 'Small', value: 'sm' },\n { label: 'Medium', value: 'md' },\n { label: 'Large', value: 'lg' },\n ],\n },\n ],\n },\n {\n type: 'vitops_steps',\n label: 'Steps',\n placeholder: 'A numbered sequence joined by a connector line',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'steps',\n label: 'Steps',\n item_label: 'step',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'title', label: 'Title' },\n {\n type: 'text_input',\n action_id: 'text',\n label: 'Text (write [link text](/url) for an inline link)',\n multiline: true,\n },\n ],\n },\n levelField('3'),\n {\n type: 'select',\n action_id: 'layout',\n label: 'Layout',\n initial_value: 'column',\n options: [\n { label: 'Stacked', value: 'column' },\n { label: 'Side by side when wide', value: 'row' },\n ],\n },\n ...toneFields(),\n ],\n },\n {\n type: 'vitops_stats',\n label: 'Stats',\n placeholder: 'Headline figures that count up, or labelled tiles',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'stats',\n label: 'Figures',\n item_label: 'figure',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'value', label: 'Figure (e.g. 82% or 11 hrs)' },\n { type: 'text_input', action_id: 'label', label: 'Label' },\n {\n type: 'text_input',\n action_id: 'group',\n label: 'Group (tiles style; small line above)',\n },\n {\n type: 'text_input',\n action_id: 'delta',\n label: 'Change (tiles style; e.g. +3 this week)',\n },\n {\n type: 'select',\n action_id: 'state',\n label: 'Change is',\n initial_value: '',\n options: [\n { label: 'Neutral', value: '' },\n { label: 'Good (up)', value: 'up' },\n { label: 'Bad (down)', value: 'down' },\n ],\n },\n ],\n },\n {\n type: 'select',\n action_id: 'variant',\n label: 'Style',\n initial_value: 'figures',\n options: [\n { label: 'Figures', value: 'figures' },\n { label: 'Tiles', value: 'tiles' },\n ],\n },\n {\n type: 'toggle',\n action_id: 'animate',\n label: 'Count up when scrolled into view',\n initial_value: true,\n },\n ...toneFields(),\n ],\n },\n {\n type: 'vitops_callout',\n label: 'Callout',\n placeholder: 'A call-to-action band (not a dismissible banner)',\n category: 'Sections',\n fields: [\n { type: 'text_input', action_id: 'heading', label: 'Heading' },\n levelField('2'),\n {\n type: 'text_input',\n action_id: 'body',\n label: 'Text (write [link text](/url) for an inline link)',\n multiline: true,\n },\n { type: 'text_input', action_id: 'primaryLabel', label: 'Main button text' },\n { type: 'text_input', action_id: 'primaryHref', label: 'Main button URL' },\n { type: 'text_input', action_id: 'secondaryLabel', label: 'Second link text' },\n { type: 'text_input', action_id: 'secondaryHref', label: 'Second link URL' },\n {\n type: 'select',\n action_id: 'align',\n label: 'Layout',\n initial_value: 'start',\n options: [\n { label: 'Message left, buttons right', value: 'start' },\n { label: 'Centered', value: 'center' },\n ],\n },\n {\n type: 'toggle',\n action_id: 'solid',\n label: 'Fill with the solid colour',\n initial_value: false,\n },\n ...toneFields(),\n ],\n },\n {\n type: 'vitops_entries',\n label: 'Entries table',\n placeholder: 'Rows of term/value pairs: a stacked list that becomes a table when wide',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'entries',\n label: 'Entries',\n item_label: 'entry',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'heading', label: 'Heading (row name)' },\n {\n type: 'text_input',\n action_id: 'pairs',\n label: 'Details (one per line: Term | Value)',\n multiline: true,\n },\n ],\n },\n levelField('3'),\n {\n type: 'text_input',\n action_id: 'breakpoint',\n label: 'Becomes a table at this width',\n initial_value: '40rem',\n },\n ],\n },\n {\n type: 'vitops_pricing',\n label: 'Pricing',\n placeholder: 'Plan cards with aligned feature rows',\n category: 'Sections',\n fields: [\n {\n type: 'repeater',\n action_id: 'plans',\n label: 'Plans',\n item_label: 'plan',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'name', label: 'Plan name' },\n { type: 'text_input', action_id: 'price', label: 'Price (e.g. $49)' },\n { type: 'text_input', action_id: 'period', label: 'Period (e.g. per month)' },\n {\n type: 'text_input',\n action_id: 'yearly',\n label: 'Yearly price line (e.g. $34 paid yearly), shown under the price',\n },\n {\n type: 'text_input',\n action_id: 'note',\n label: 'Note under the plan name (e.g. Best for small teams)',\n },\n {\n type: 'text_input',\n action_id: 'features',\n label: 'Features (one per line)',\n multiline: true,\n },\n { type: 'text_input', action_id: 'ctaLabel', label: 'Button text' },\n { type: 'text_input', action_id: 'ctaHref', label: 'Button URL' },\n {\n type: 'toggle',\n action_id: 'featured',\n label: 'Highlight this plan',\n initial_value: false,\n },\n ],\n },\n levelField('3'),\n ],\n },\n {\n type: 'vitops_embed',\n label: 'Embed (video or map)',\n placeholder: 'A video or map that loads only after the visitor allows it',\n category: 'Embeds',\n fields: [\n {\n type: 'select',\n action_id: 'provider',\n label: 'Provider',\n initial_value: 'youtube',\n options: [\n { label: 'YouTube', value: 'youtube' },\n { label: 'Vimeo', value: 'vimeo' },\n { label: 'Map (Google or OpenStreetMap embed address)', value: 'map' },\n { label: 'Other (https address)', value: 'custom' },\n ],\n },\n { type: 'text_input', action_id: 'url', label: 'Address' },\n {\n type: 'text_input',\n action_id: 'title',\n label: 'Title (required: it names the frame for screen readers)',\n },\n {\n type: 'select',\n action_id: 'category',\n label: 'Consent category it waits on',\n initial_value: 'marketing',\n options: [\n { label: 'Marketing', value: 'marketing' },\n { label: 'Preferences', value: 'preferences' },\n { label: 'Analytics', value: 'analytics' },\n ],\n },\n { type: 'text_input', action_id: 'ratio', label: 'Aspect ratio', initial_value: '16 / 9' },\n ],\n },\n {\n type: 'vitops_form',\n label: 'Contact form',\n placeholder: 'An enquiry form that emails you (and your CRM) and works without JavaScript',\n category: 'Forms',\n fields: [\n { type: 'text_input', action_id: 'heading', label: 'Heading' },\n levelField('2'),\n { type: 'text_input', action_id: 'intro', label: 'Text above the form', multiline: true },\n {\n type: 'repeater',\n action_id: 'fields',\n label: 'Fields',\n item_label: 'field',\n min_items: 1,\n fields: [\n { type: 'text_input', action_id: 'label', label: 'Label (what the visitor sees)' },\n {\n type: 'text_input',\n action_id: 'name',\n label: 'Name in the message (optional: letters, digits, underscores)',\n },\n {\n type: 'select',\n action_id: 'type',\n label: 'Kind',\n initial_value: 'text',\n options: [\n { label: 'Short text', value: 'text' },\n { label: 'Email address', value: 'email' },\n { label: 'Phone number', value: 'tel' },\n { label: 'Long text', value: 'textarea' },\n { label: 'Choose one', value: 'select' },\n { label: 'Tick box', value: 'checkbox' },\n { label: 'Tick any number', value: 'checkboxes' },\n ],\n },\n { type: 'toggle', action_id: 'required', label: 'Required', initial_value: false },\n { type: 'text_input', action_id: 'placeholder', label: 'Example text' },\n { type: 'text_input', action_id: 'help', label: 'Help text under the field' },\n {\n type: 'text_input',\n action_id: 'options',\n label: 'Choices (one per line; for \"Choose one\" and \"Tick any number\")',\n multiline: true,\n },\n ],\n },\n { type: 'text_input', action_id: 'submitLabel', label: 'Button text', initial_value: 'Send' },\n {\n type: 'text_input',\n action_id: 'successMessage',\n label: 'Message after sending',\n initial_value: 'Thanks. Your message has been sent.',\n },\n { type: 'text_input', action_id: 'errorMessage', label: 'Message if sending fails' },\n {\n type: 'text_input',\n action_id: 'noJsMessage',\n label: 'Message when JavaScript is off (shown only if the site has a spam check)',\n },\n {\n type: 'text_input',\n action_id: 'action',\n label: 'Where the form posts (leave empty for /api/track)',\n },\n ...toneFields(),\n ],\n },\n {\n /**\n * Google reviews from the committed `.vitops/reviews.json` cache (`vitops reviews`).\n *\n * The data is a BUILD-TIME import (`virtual:getvitops/reviews`, from the `vitops()` Astro\n * integration), never a filesystem read: the renderer runs in a Worker. So a new review\n * appears after the next `vitops reviews` run is committed and the site rebuilt, not when\n * an editor saves. Which reviews show is `selectReviews()` from `@getvitops/utils`, the\n * one place that is decided — these fields are exactly its `ReviewLocationConfig`.\n */\n type: 'vitops_reviews',\n label: 'Google reviews',\n icon: 'star',\n placeholder: 'Customer reviews from the Google Business Profile cache',\n category: 'Sections',\n fields: [\n {\n type: 'text_input',\n action_id: 'label',\n label: 'Name for the group (read by screen readers)',\n initial_value: 'Customer reviews',\n },\n {\n type: 'text_input',\n action_id: 'location',\n label: 'Location key (organization.locations key; blank = every location in the cache)',\n },\n {\n type: 'select',\n action_id: 'layout',\n label: 'Layout',\n initial_value: 'grid',\n options: [\n { label: 'Grid', value: 'grid' },\n { label: 'Carousel', value: 'carousel' },\n { label: 'Marquee (scrolling row)', value: 'marquee' },\n ],\n },\n {\n type: 'select',\n action_id: 'columns',\n label: 'Columns (grid only)',\n initial_value: '3',\n options: ['1', '2', '3', '4'].map((n) => ({ label: n, value: n })),\n },\n {\n type: 'number_input',\n action_id: 'limit',\n label: 'Most reviews to show (0 = all)',\n initial_value: 6,\n },\n {\n type: 'select',\n action_id: 'minRating',\n label: 'Lowest rating to show',\n initial_value: '4',\n options: ['1', '2', '3', '4', '5'].map((n) => ({\n label: n === '1' ? 'Any' : `${n} stars and up`,\n value: n,\n })),\n },\n {\n type: 'select',\n action_id: 'sort',\n label: 'Order',\n initial_value: 'recent',\n options: [\n { label: 'Newest first', value: 'recent' },\n { label: 'Highest rated first', value: 'rating' },\n ],\n },\n {\n type: 'text_input',\n action_id: 'featured',\n label: 'Review ids to show first (comma-separated; they ignore the rating filter)',\n },\n {\n type: 'toggle',\n action_id: 'avatars',\n label: \"Show reviewers' Google photos (loads images from Google for every visitor)\",\n initial_value: false,\n },\n ...toneFields(),\n ],\n },\n {\n /**\n * A named EmDash menu inside page content: a site map, a section's link list, a footer-style\n * set of columns. NOT the header — that is `<SiteNav>` in the layout, which owns the drawer,\n * dropdowns and breakpoints. `variant: sitenav` renders the same markup and pattern for the\n * rare page that wants a real navbar in the body.\n *\n * The menu is chosen from `menus/list` (the site's own menus) with the conventional names as\n * the static fallback, because a select needs a non-empty `options` list.\n */\n type: 'vitops_nav',\n label: 'Menu',\n icon: 'list',\n placeholder: 'An EmDash menu as a list of links',\n category: 'Navigation',\n fields: [\n {\n type: 'select',\n action_id: 'menu',\n label: 'Menu',\n initial_value: 'footer-sitemap',\n options: [\n { label: 'Footer site map', value: 'footer-sitemap' },\n { label: 'Footer legal', value: 'footer-legal' },\n { label: 'Primary', value: 'primary' },\n ],\n optionsRoute: MENUS_ROUTE,\n } as BlockKitFieldDef,\n {\n type: 'text_input',\n action_id: 'label',\n label: 'Name for the navigation (read by screen readers; must differ from other menus)',\n },\n { type: 'text_input', action_id: 'heading', label: 'Visible heading (optional)' },\n levelField('2'),\n {\n type: 'select',\n action_id: 'variant',\n label: 'Layout',\n initial_value: 'list',\n options: [\n { label: 'Vertical list', value: 'list' },\n { label: 'Inline row', value: 'inline' },\n { label: 'Columns (each top-level item with its sub-links)', value: 'columns' },\n { label: 'Navbar (the header pattern, in the page)', value: 'sitenav' },\n ],\n },\n {\n type: 'select',\n action_id: 'depth',\n label: 'Levels to show',\n initial_value: '1',\n options: ['1', '2', '3'].map((n) => ({ label: n, value: n })),\n },\n ...toneFields(),\n ],\n },\n {\n /** A row of static labels (`badge`) or editable/dismissable ones (`tag`). */\n type: 'vitops_badges',\n label: 'Badges',\n icon: 'tag',\n placeholder: 'A row of short labels',\n fields: [\n {\n type: 'repeater',\n action_id: 'items',\n label: 'Labels',\n fields: [\n { type: 'text_input', action_id: 'label', label: 'Label' },\n {\n type: 'select',\n action_id: 'tone',\n label: 'Tone (blank uses the block tone)',\n initial_value: '',\n options: STATIC_TONE_OPTIONS,\n optionsRoute: TONES_ROUTE,\n } as BlockKitFieldDef,\n { type: 'text_input', action_id: 'color', label: 'Palette colour instead (e.g. amber)' },\n {\n type: 'combobox',\n action_id: 'icon',\n label: 'Icon before the label',\n placeholder: 'None',\n options: SEMANTIC_ICON_OPTIONS,\n },\n { type: 'text_input', action_id: 'href', label: 'Link URL (optional)' },\n ],\n } as BlockKitFieldDef,\n {\n type: 'select',\n action_id: 'variant',\n label: 'Style',\n initial_value: 'badge',\n options: [\n { label: 'Badge (static label)', value: 'badge' },\n { label: 'Tag', value: 'tag' },\n ],\n },\n ...toneFields(),\n ],\n },\n {\n /**\n * A widget area: content defined once under Widgets in the admin and shown wherever this\n * block is placed. Edits reach every page it sits on (after the next build, on a\n * prerendered site) — unlike a Section, which is copied.\n */\n type: 'vitops_widgetArea',\n label: 'Widget area',\n icon: 'layout',\n placeholder: 'Shared content managed under Widgets',\n fields: [\n {\n type: 'select',\n action_id: 'area',\n label: 'Widget area',\n initial_value: '',\n options: [{ label: 'Choose…', value: '' }],\n optionsRoute: WIDGET_AREAS_ROUTE,\n } as BlockKitFieldDef,\n levelField('3'),\n ],\n },\n];\n\n/** Every block gets the escape hatches last, so the editor reads content first. */\n/**\n * Repeaters whose rows can carry their own colour. A row's `tone` / `color` win over the block's,\n * and blank falls back to it, so one block can hold a pine card and an amber one without\n * becoming two blocks. (If you want a single colour for the lot, set it on the block.)\n */\nconst ROW_TONE: Record<string, string> = {\n vitops_cards: 'cards',\n vitops_stats: 'stats',\n vitops_steps: 'steps',\n vitops_hero: 'cards',\n};\n\n/** Extra row fields for one repeater, beyond the shared tone pair. */\nconst rowThemeField = (): BlockKitFieldDef =>\n ({\n type: 'select',\n action_id: 'theme',\n label: 'Backdrop for this item (a tint or fill of a site colour; wins over tone)',\n initial_value: '',\n options: [\n { label: 'None', value: '' },\n ...THEME_ROLES.flatMap((r) => [\n { label: r.replace(/-/g, ' '), value: r },\n { label: `${r.replace(/-/g, ' ')} (filled)`, value: `${r}:solid` },\n ]),\n ],\n optionsRoute: THEMES_ROUTE,\n }) as BlockKitFieldDef;\n\nconst ROW_EXTRA: Record<string, Record<string, BlockKitFieldDef[]>> = {\n vitops_cards: { cards: [rowThemeField()] },\n vitops_hero: {\n cards: [\n rowThemeField(),\n { type: 'text_input', action_id: 'eyebrow', label: 'Eyebrow (small line above the title)' },\n { type: 'text_input', action_id: 'ctaLabel', label: 'Button text' },\n { type: 'text_input', action_id: 'ctaHref', label: 'Button URL' },\n ],\n },\n};\n\nconst rowToneFields = (): BlockKitFieldDef[] => [\n {\n type: 'select',\n action_id: 'tone',\n label: 'Tone for this item (blank uses the block tone)',\n initial_value: '',\n options: STATIC_TONE_OPTIONS,\n optionsRoute: TONES_ROUTE,\n } as BlockKitFieldDef,\n { type: 'text_input', action_id: 'color', label: 'Palette colour instead (e.g. amber)' },\n];\n\nfunction withRowFields(block: PortableTextBlockDef): BlockKitFieldDef[] {\n const repeater = ROW_TONE[block.type];\n return (block.fields ?? []).map((f) =>\n f.type === 'repeater' && f.action_id === repeater\n ? ({\n ...f,\n fields: [...f.fields, ...(ROW_EXTRA[block.type]?.[repeater] ?? []), ...rowToneFields()],\n } as BlockKitFieldDef)\n : f,\n );\n}\n\nexport const BLOCKS: PortableTextBlockDef[] = BASE_BLOCKS.map((b) => ({\n ...b,\n fields: [...withRowFields(b), ...commonFields()],\n}));\n\n/** Which Vitops blocks a site's editor offers, and any it adds of its own. */\nexport interface BlockSelection {\n /** Offer only these `vitops_*` types. Unknown names throw — a typo must not silently offer nothing. */\n include?: string[];\n /** Drop these `vitops_*` types. Applied after `include`. */\n exclude?: string[];\n /**\n * The site's own blocks. These are EDITOR definitions only: the plugin cannot render a\n * block it has never heard of, so the site maps each `type` to a component with\n * `<PortableText components={{ type: { … } }} />`. A type that collides with an existing\n * one throws, since the later definition would silently replace the earlier one's fields.\n */\n extra?: PortableTextBlockDef[];\n}\n\n/**\n * Resolve {@link BlockSelection} against {@link BLOCKS}.\n *\n * Pure so a misconfiguration fails at plugin construction, with the offending name in the\n * message, rather than as an editor menu that is quietly missing something.\n */\nexport function selectBlocks(selection: BlockSelection = {}): PortableTextBlockDef[] {\n const known = new Set(BLOCKS.map((b) => b.type));\n const check = (names: string[] | undefined, option: string): void => {\n for (const name of names ?? [])\n if (!known.has(name))\n throw new Error(\n `[vitops] blocks.${option}: unknown block '${name}'. Known: ${[...known].join(', ')}.`,\n );\n };\n check(selection.include, 'include');\n check(selection.exclude, 'exclude');\n\n const include = selection.include ? new Set(selection.include) : null;\n const exclude = new Set(selection.exclude);\n const base = BLOCKS.filter((b) => (!include || include.has(b.type)) && !exclude.has(b.type));\n\n const extra = selection.extra ?? [];\n const taken = new Set(BLOCKS.map((b) => b.type));\n for (const block of extra) {\n if (taken.has(block.type))\n throw new Error(`[vitops] blocks.extra: type '${block.type}' is already defined.`);\n taken.add(block.type);\n }\n return [...base, ...extra];\n}\n","/**\n * The `menus/list` plugin route: what fills `vitops_nav`'s `menu` select with the site's own\n * EmDash menus, so an editor picks from what exists instead of typing a name that may not.\n *\n * Same mechanism as `tones-route.ts` and the same failure rule: Block Kit's `select` falls\n * back to its static options if the route throws or answers with no `items`, so a lookup that\n * cannot run (no request context, no database) degrades to the conventional menu names rather\n * than breaking the block editor. The lister is injected so the route is testable without a\n * database; `index.ts` passes EmDash's own `getMenus`.\n */\nimport type { PluginRoute } from 'emdash';\n\nexport interface MenuSummary {\n name: string;\n label: string;\n}\n\nexport interface MenuItemOption {\n id: string;\n name: string;\n}\n\n/** The items the select shows: one per menu, valued by its `name` (what `getMenu()` takes). */\nexport function menuItems(menus: readonly MenuSummary[]): MenuItemOption[] {\n const seen = new Set<string>();\n const out: MenuItemOption[] = [];\n for (const { name, label } of menus) {\n // A menu per locale shares one name; the select needs it once.\n if (!name || seen.has(name)) continue;\n seen.add(name);\n out.push({ id: name, name: label && label !== name ? `${label} (${name})` : name });\n }\n return out;\n}\n\nexport function createMenusRoute(list: () => Promise<readonly MenuSummary[]>): PluginRoute {\n return {\n // An editor opening the block needs this; it reads menu names, never their content.\n permission: 'content:read',\n handler: async () => {\n try {\n return { items: menuItems(await list()) };\n } catch {\n // No `items` => the select keeps its static options.\n return { items: [] };\n }\n },\n };\n}\n","/**\n * The `tones/list` plugin route: what fills every block's `tone` select with the site's own\n * colour roles (UPSTREAM 21 — \"role names the admin never offered\").\n *\n * EmDash's Block Kit `select` takes an `optionsRoute` for exactly this, and falls back to the\n * field's static options if the route fails or answers with no `items`, so a site that\n * configures nothing still gets the semantic names from `STATIC_TONE_OPTIONS`.\n *\n * **Where the roles come from.** Plugin code has no site config at runtime, so the site hands\n * it over: `tones: [...]` verbatim, or — if the design-system editor is already wired with the\n * site's `Config` — the `colors.roles` keys of that config, so there is nothing to keep in\n * step. A role is listed under its own name (`brand-primary`), because that is what\n * `resolveTone()` and the token names (`--color-bg-brand-primary-muted`) both use.\n */\nimport { resolveInput } from '@getvitops/generator/config';\nimport type { PluginRoute } from 'emdash';\nimport { SEMANTIC_TONES } from './astro/blocks/tone.ts';\n\nexport interface ToneItem {\n id: string;\n name: string;\n}\n\n/** Role names of a design system or full config, or `[]` if it cannot be resolved. */\nexport function rolesOf(config: unknown): string[] {\n if (!config || typeof config !== 'object') return [];\n try {\n const { designSystem } = resolveInput(config as never, { siteEnv: 'production' });\n return Object.keys(designSystem.colors?.roles ?? {});\n } catch {\n // A config the editor cannot resolve is the editor's error to report; here it just\n // means the select keeps its static options.\n return [];\n }\n}\n\n/** The items the select shows: the site's roles first, then any semantic name it lacks. */\nexport function toneItems(roles: readonly string[]): ToneItem[] {\n const names = [...new Set([...roles, ...SEMANTIC_TONES])];\n return [{ id: '', name: 'None' }, ...names.map((id) => ({ id, name: id.replace(/-/g, ' ') }))];\n}\n\nexport function createTonesRoute(roles: () => readonly string[]): PluginRoute {\n return {\n // An editor opening a block needs this; it reads names, never content.\n permission: 'content:read',\n handler: async () => ({ items: toneItems(roles()) }),\n };\n}\n\n/** The `theme` select's items: `None`, then each role tinted and filled (`role`, `role:solid`). */\nexport function themeItems(roles: readonly string[]): ToneItem[] {\n const names = [...new Set([...roles, ...SEMANTIC_TONES])];\n const label = (id: string) => id.replace(/-/g, ' ');\n return [\n { id: '', name: 'None' },\n ...names.flatMap((id) => [\n { id, name: label(id) },\n { id: `${id}:solid`, name: `${label(id)} (filled)` },\n ]),\n ];\n}\n\nexport function createThemesRoute(roles: () => readonly string[]): PluginRoute {\n return {\n permission: 'content:read',\n handler: async () => ({ items: themeItems(roles()) }),\n };\n}\n","/**\n * The `vitops.design-system` plugin route — what `<wc-theme-editor>` probes\n * and POSTs its patch to when mounted inside the EmDash admin (see this\n * package's README and AGENTS.md's \"EmDash plugin as the design-system\n * editing surface\" section). Mounted at\n * `/_emdash/api/plugins/vitops/design-system`.\n *\n * Two responsibilities, matching the dev-only `@getvitops/vite` endpoint's\n * contract byte-for-byte (`GET → { writable }`, `POST { patch } → { ok } |\n * { errors }`) so `<wc-theme-editor>` needs no branch for which one it's\n * talking to:\n *\n * 1. **Immediate**: persist the patch to `ctx.settings` — read back by the\n * `page:fragments` hook in `index.ts`, which turns it into a site-wide\n * `:root` override `<style>` on every public page. This is what makes an\n * edit visible without a rebuild.\n * 2. **Canonical**: auto-commit the merged file to `site.deployment.branch`\n * (default `dev`) via `@getvitops/utils/git`'s `commitFile()`, so the edit\n * reaches the git-tracked `site.json` through this repo's own normal\n * `dev → promote → main` review path — never a silent second source of\n * truth. A commit failure (missing token, misconfigured repo, a transient\n * GitHub error) is logged and surfaces as a warning, but never fails the\n * save: the immediate override already works regardless.\n *\n * **Assumes `config` is a full site `Config`** (has `organization`/`site`),\n * not a bare `design-system.json` — true for any real EmDash site (it always\n * has a `site.json`), and the only case this plugin already handles anywhere\n * else (`vitopsHosting()`, `BLOCKS`).\n *\n * The GitHub token is read as `process.env.VITOPS_GITHUB_TOKEN` — matching\n * `hosting.ts`'s own `process.env.HOSTING` read in this same package, rather\n * than a `cloudflare:workers`-specific import: a native EmDash plugin runs\n * in-process with no isolation boundary (EmDash's own docs say plugins \"can\n * access environment variables\" directly), and this package already targets\n * both the Cloudflare and Node hosting targets through one `process.env`\n * convention (`nodejs_compat` on Workers maps bindings into `process.env`).\n */\nimport { pluginResponse } from 'emdash';\nimport type { PageFragmentContribution, PluginContext, PluginRoute, RouteContext } from 'emdash';\nimport {\n designSystemPath,\n formatConfig,\n getAt,\n isConfig,\n mergePatch,\n resolveInput,\n setAt,\n} from '@getvitops/generator/config';\nimport {\n buildReverseIndex,\n patchToOverrideCss,\n validateColorContract,\n} from '@getvitops/generator/tokens';\nimport { commitFile } from '@getvitops/utils/git';\n\nexport interface DesignSystemEditorOptions {\n /**\n * The site's own config, typically `import config from '../../site.json'\n * with { type: 'json' }`. A static, build-time-bundled value — this plugin\n * never reads it from disk at runtime (a deployed Worker has no\n * filesystem), so an edit doesn't take effect here until the *next*\n * deploy; that's exactly the point of the auto-commit half.\n */\n config: unknown;\n /** Which `designSystem.themes` entry this edits. Default: `'default'`. */\n theme?: string;\n}\n\nconst SETTINGS_KEY = 'designSystemPatch';\n\ninterface StoredPatch {\n patch: Record<string, unknown>;\n at: number;\n}\n\nfunction json(status: number, body: unknown) {\n return pluginResponse({\n status,\n headers: { 'content-type': 'application/json' },\n body: { kind: 'text', value: JSON.stringify(body) },\n });\n}\n\n/** Fetches the currently-stored patch, clearing it if the bundled `config`\n * (the last real build) already reflects every value in it — see the\n * module docblock's \"self-clearing\" note in AGENTS.md. */\nasync function currentPatch(\n ctx: Pick<RouteContext, 'settings'>,\n ds: { colors: Record<string, unknown> },\n): Promise<StoredPatch | null> {\n const stored = await ctx.settings.get<StoredPatch>(SETTINGS_KEY);\n if (!stored) return null;\n const patchedColors = stored.patch['colors'];\n if (\n patchedColors != null &&\n JSON.stringify(mergePatch(ds.colors, patchedColors)) === JSON.stringify(ds.colors)\n ) {\n await ctx.settings.delete(SETTINGS_KEY);\n return null;\n }\n return stored;\n}\n\nexport function createDesignSystemRoute(opts: DesignSystemEditorOptions): PluginRoute {\n const theme = opts.theme ?? 'default';\n\n return {\n methods: ['GET', 'POST'],\n response: 'raw',\n permission: 'plugins:manage',\n handler: async (ctx: RouteContext) => {\n // `resolveInput` handles both a bare design-system.json and a full site\n // Config (following `extends`, resolving `themes[theme]`), the same\n // entry point every other consumer of a config goes through.\n const { designSystem: ds, config } = resolveInput(opts.config, {\n theme,\n siteEnv: 'production',\n });\n\n if (ctx.request.method === 'GET') {\n await currentPatch(ctx, ds);\n return json(200, { writable: true });\n }\n\n const body = ctx.input as { patch?: unknown } | undefined;\n const patch = body?.patch;\n if (patch == null || typeof patch !== 'object' || Array.isArray(patch))\n return json(400, { errors: ['body must be { patch: object }'] });\n\n const mergedColors = mergePatch(\n ds.colors,\n (patch as Record<string, unknown>)['colors'] ?? {},\n );\n const contrastErrors = validateColorContract(\n mergedColors as Parameters<typeof validateColorContract>[0],\n );\n if (contrastErrors.length) return json(400, { errors: contrastErrors });\n\n await ctx.settings.set(SETTINGS_KEY, {\n patch: patch as Record<string, unknown>,\n at: Date.now(),\n } satisfies StoredPatch);\n\n const warnings: string[] = [];\n const repoSpec = config?.site.deployment?.repo;\n const token = process.env['VITOPS_GITHUB_TOKEN'];\n if (!repoSpec || !token) {\n warnings.push(\n !repoSpec\n ? 'design-system sync skipped: site.deployment.repo is not set — the change is live but not yet synced to source.'\n : 'design-system sync skipped: no GitHub token configured — the change is live but not yet synced to source.',\n );\n } else {\n const [owner, repo] = repoSpec.split('/');\n if (!owner || !repo) {\n warnings.push(\n `design-system sync skipped: site.deployment.repo \"${repoSpec}\" isn't \"owner/name\".`,\n );\n } else if (!isConfig(opts.config)) {\n warnings.push('design-system sync skipped: config is not a recognised site Config.');\n } else {\n const at = designSystemPath(opts.config, theme);\n const mergedSubtree = mergePatch(getAt(opts.config, at), patch);\n const content = `${formatConfig(setAt(opts.config, at, mergedSubtree))}\\n`;\n const result = await commitFile({\n owner,\n repo,\n branch: config.site.deployment?.branch ?? 'dev',\n path: 'site.json',\n content,\n message: 'Sync design-system edit from EmDash admin',\n token,\n fetchImpl:\n ctx.http && ((url: string, init?: RequestInit) => ctx.http!.fetch(url, init)),\n });\n if (!result.ok) {\n ctx.log.warn('design-system sync commit failed', { message: result.message });\n warnings.push(`design-system sync failed (change is still live): ${result.message}`);\n }\n }\n }\n\n return json(200, { ok: true, warnings });\n },\n };\n}\n\n/**\n * The `page:fragments` contribution serving the stored patch's override\n * CSS site-wide. Returns `null` (no fragment) when there's nothing unsynced\n * — the same \"no gated tag, no cost\" discipline `@getvitops/astro`'s consent\n * gate already follows.\n */\nexport async function designSystemOverrideFragment(\n ctx: Pick<PluginContext, 'settings'>,\n opts: DesignSystemEditorOptions,\n): Promise<PageFragmentContribution | null> {\n const { designSystem: ds } = resolveInput(opts.config, { theme: opts.theme ?? 'default' });\n const stored = await currentPatch(ctx, ds);\n if (!stored) return null;\n\n const css = patchToOverrideCss(stored.patch, buildReverseIndex(ds));\n if (!css) return null;\n return {\n kind: 'html',\n placement: 'head',\n html: `<style id=\"ds-overrides\">${css}</style>`,\n key: 'vitops-ds-overrides',\n };\n}\n","import { BLOCKS, type BlockKitFieldDef, type PortableTextBlockDef } from './blocks.ts';\nimport { vitopsBlockTypes } from './block-types.ts';\n\nconst cell = (s: string | undefined) => (s ?? '').replace(/\\|/g, '\\\\|').replace(/\\n/g, ' ');\n\nfunction describe(f: BlockKitFieldDef): string {\n const bits: string[] = [];\n if ('options' in f && Array.isArray(f.options) && f.options.length) {\n const values = f.options.map((o) => String((o as { value: unknown }).value));\n bits.push(\n values.length > 8\n ? `one of ${values.length} values (e.g. ${values.slice(0, 4).join(', ')})`\n : `one of ${values.join(', ')}`,\n );\n }\n if ('initial_value' in f && f.initial_value !== undefined) {\n bits.push(`default \\`${String(f.initial_value)}\\``);\n }\n if (f.type === 'repeater' && 'min_items' in f && f.min_items)\n bits.push(`min ${f.min_items} rows`);\n return bits.join('; ');\n}\n\nfunction fieldRows(fields: BlockKitFieldDef[], prefix = ''): string[] {\n return fields.flatMap((f) => {\n const own = `| \\`${prefix}${f.action_id}\\` | ${f.type} | ${cell((f as { label?: string }).label)} | ${cell(describe(f))} |`;\n return f.type === 'repeater' ? [own, ...fieldRows(f.fields, `${f.action_id}[].`)] : [own];\n });\n}\n\n/**\n * The block reference an agent reads via `vitops docs blocks`: every Portable Text `_type`,\n * its fields and what each saves. Rendered from the same `BLOCKS` the editor menu uses, so\n * it cannot describe a field the editor doesn't offer.\n */\nexport function renderBlocksDoc(blocks: readonly PortableTextBlockDef[] = BLOCKS): string {\n const out = [\n '---',\n 'type: concept',\n 'title: EmDash blocks',\n 'description: Portable Text block types from @getvitops/emdash, with every field',\n '---',\n '',\n '# EmDash blocks',\n '',\n 'Each block is a Portable Text object: `{ \"_type\": \"vitops_cards\", \"_key\": \"<unique>\", ...fields }`.',\n 'Portable Text blocks are flat — they cannot contain other blocks, and a repeater row holds scalar fields only.',\n 'To put blocks inside something, use the section block TYPE (end of this page), whose body is Portable Text.',\n 'Give every block a unique `_key`; gallery and carousel derive their element id from it.',\n '',\n '## Colour: `tone` and `color`',\n '',\n 'Every block that colours something takes the same two fields. `tone` is a colour role from the',\n \"site's `colors.roles` (or a semantic name); an unknown role falls back through its semantic\",\n 'relatives to `neutral`. `color` is an escape hatch naming a palette hue (`amber`) or hue and step',\n \"(`amber-300`) and wins over `tone`; text on a hue solid is the site's `--color-<hue>-<step>-on`,\",\n 'but contrast is only guaranteed for roles. Renderers emit `--tone-*` custom properties, never a',\n 'class built from the value. Rows of `hero.cards`, `cards`, `stats` and `steps` (and `badges.items`)',\n \"take their own `tone`/`color`; blank inherits the block's, and a row's colour never reaches its\",\n 'siblings, so mixed colours need no extra block. Multiline fields that accept links take',\n '`[text](/url)`; hero and section headings accent words wrapped in `*stars*` (coloured by the',\n \"`ui-accent` role, or `--accent-color` in the block's `style`). `hero.cards` and `cards` rows also take a\",\n '`theme` (a tint or fill of a site colour) so one card can differ from its neighbours.',\n '',\n '## Theme',\n '',\n 'Every block carries `theme`: a colour role, tinted (`surface`) or filled (`brand-primary:solid`), applied to',\n \"the block's own root with the role's computed readable text. It wins over the block's `tone`.\",\n '',\n '## Type size',\n '',\n 'Titles use the type roles (`font-heading`, `font-display`, `font-eyebrow`). Every role reads its size',\n \"from a custom property, `--<role>-fs` (`--heading-fs`, `--display-fs`), falling back to the role's\",\n 'configured size, so one block can resize its titles through its `style` field',\n '(`--heading-fs: 1.125rem`) with no site CSS; set it on an ancestor to resize a region. It is inherited,',\n 'so it reaches every use of that role inside the block. `style` accepts custom properties only.',\n 'Steps take two modifiers through `className`: `steps--cards` (each step in a card; its geometry is the',\n '`steps` entry of `patterns.items` in site.json) and `steps--animated` (the connector draws and the marker fills',\n 'on scroll; static with reduced motion). `rhythm-flex` is an opt-in flex stack for any block.',\n 'Steps, stats and cards also have per-part hooks: `--steps-title-size` (default one step below the',\n 'heading role), `--stats-figure-size` and `--card-title-size`.',\n '',\n ];\n for (const b of blocks) {\n out.push(`## \\`${b.type}\\` — ${b.label}`, '');\n if (b.placeholder) out.push(`${b.placeholder}.`, '');\n if (b.fields?.length) {\n out.push(\n '| Field | Type | Label | Notes |',\n '| --- | --- | --- | --- |',\n ...fieldRows(b.fields),\n '',\n );\n }\n }\n out.push(\n '## `vitops_form` needs an endpoint',\n '',\n 'The form posts to `/api/track` (the `action` field overrides it), which must be an on-demand route',\n 'built with `createConversionRoute()` from `@getvitops/astro/routes`; the emdash template ships one.',\n 'Field names are submitted as written, except that `phone` becomes `tel` and a few names the route',\n 'reserves (`event`, `website`, `honeypot`, `_honeypot`, `cf-turnstile-response`) are renamed.',\n 'It emails `site.notifications.email` and writes `site.crm` when set; with neither, the route refuses',\n 'the post rather than accept a message nobody receives. Add the spam check with',\n '`vitops setup turnstile`: once `site.security.turnstile.siteKey` is set the block renders',\n '`<wc-turnstile>`, which loads Cloudflare Turnstile only when the visitor first uses the form',\n '(category `necessary`, so it is not behind the consent banner). The secret lives in the',\n '`VITOPS_TURNSTILE_SECRET` Worker secret, never in `site.json`. Use one form per page.',\n '`vitops emdash check-prerender` warns (fails under `--strict`) when a built page has a form',\n 'posting to `/api/track` and `site.json` configures nowhere to deliver it, or lists none of its fields',\n 'under `site.templates`.',\n '',\n 'A `checkboxes` field (\"tick any number\") posts one entry per ticked box under the same name; the route',\n 'joins them with `\", \"`. HTML cannot require \"at least one\", so `required` is not applied to the group.',\n '',\n 'The generated privacy policy reads `site.templates` form entries, not this block. Convert the saved',\n 'block with `formTemplateFromBlock(block)` from `@getvitops/emdash` and paste the result under',\n '`site.templates` in `site.json`, so the policy lists what the form collects.',\n '',\n '## Block types (the `blocks` field)',\n '',\n 'Schema-level block types, not Portable Text blocks: add them to the seed (`blockTypes: [...vitopsBlockTypes()]`),',\n \"allow the slug on the collection's `blocks` field, and render with `blockTypeComponents` from `@getvitops/emdash/astro`.\",\n '',\n );\n for (const t of vitopsBlockTypes()) {\n out.push(`### \\`${t.slug}\\` — ${t.label}`, '', `${t.description ?? ''}`, '');\n const fields = t.versions.find((v) => v.version === t.currentVersion)?.fields ?? [];\n out.push(\n '| Field | Type | Label | Notes |',\n '| --- | --- | --- | --- |',\n ...fields.map((f) => {\n const notes = [\n f.validation?.options ? `one of ${f.validation.options.join(', ')}` : '',\n f.defaultValue !== undefined ? `default \\`${String(f.defaultValue)}\\`` : '',\n f.type === 'portableText' ? 'accepts every `vitops_*` block above' : '',\n ].filter(Boolean);\n return `| \\`${f.slug}\\` | ${f.type} | ${cell(f.label)} | ${cell(notes.join('; '))} |`;\n }),\n '',\n );\n }\n return out.join('\\n');\n}\n","/**\n * Pure decisions for the `vitops_form` block: which of an editor's field rows become real\n * controls, and under what submitted names.\n *\n * Names matter more than they look. `createConversionRoute` (`@getvitops/astro/routes`) is\n * what receives the post, and it reserves a few: `phone` and `event` are the tel:/mailto:\n * click-beacon payload and are dropped from a form submission, `cf-turnstile-response` is\n * the Turnstile token, and `website`/`honeypot`/`_honeypot` are the honeypots (a filled one\n * makes the route answer success and deliver nothing). A visitor-facing field called\n * \"Phone\" must therefore not submit as `phone`, or the number silently vanishes from the\n * notification and the CRM record. `identifySubmitter` reads `tel` for a phone number, so\n * that is where it goes.\n */\n\nexport const FIELD_TYPES = [\n 'text',\n 'email',\n 'tel',\n 'textarea',\n 'select',\n 'checkbox',\n 'checkboxes',\n] as const;\nexport type FieldType = (typeof FIELD_TYPES)[number];\n\n/** Names the route treats specially. A field must never submit under one of these. */\nexport const RESERVED_NAMES = new Set([\n 'event',\n 'phone',\n 'cf-turnstile-response',\n 'website',\n 'honeypot',\n '_honeypot',\n '_next',\n]);\n\n/** The honeypot's name: the first of the route's own defaults, so no option needs passing. */\nexport const HONEYPOT_NAME = 'website';\n\nexport const MAX_FIELDS = 20;\n\nexport interface FormField {\n /** Submitted name. */\n name: string;\n /** DOM id suffix; unique within the form. */\n id: string;\n label: string;\n type: FieldType;\n required: boolean;\n placeholder: string;\n help: string;\n options: string[];\n autocomplete: string | undefined;\n}\n\nconst str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');\n\n/** `Your full name` -> `your_full_name`. Empty when the text holds nothing usable. */\nexport function slugName(input: string): string {\n return input\n .toLowerCase()\n .normalize('NFKD')\n .replace(/[̀-ͯ]/g, '')\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .slice(0, 40);\n}\n\n/** A submitted name for a row: the editor's `name` if usable, else one derived from the label. */\nexport function fieldName(\n raw: unknown,\n label: string,\n type: FieldType,\n taken: Set<string>,\n): string {\n let base = slugName(str(raw)) || slugName(label) || type;\n if (!/^[a-z]/.test(base)) base = `f_${base}`;\n if (base === 'phone') base = 'tel';\n if (RESERVED_NAMES.has(base)) base = `${base}_field`;\n let name = base;\n for (let n = 2; taken.has(name); n++) name = `${base}_${n}`;\n taken.add(name);\n return name;\n}\n\n/** The HTML `autocomplete` token for a field, or undefined when there is no honest one. */\nexport function autocompleteFor(name: string, type: FieldType): string | undefined {\n if (type === 'email') return 'email';\n if (type === 'tel') return 'tel';\n if (type !== 'text') return undefined;\n const map: Record<string, string> = {\n name: 'name',\n full_name: 'name',\n your_name: 'name',\n first_name: 'given-name',\n given_name: 'given-name',\n last_name: 'family-name',\n family_name: 'family-name',\n company: 'organization',\n organization: 'organization',\n organisation: 'organization',\n postal_code: 'postal-code',\n postcode: 'postal-code',\n zip: 'postal-code',\n city: 'address-level2',\n address: 'street-address',\n };\n return map[name];\n}\n\n/** Editor rows to renderable fields. Rows with no label are dropped; at most {@link MAX_FIELDS}. */\nexport function resolveFields(rows: Record<string, unknown>[]): FormField[] {\n const taken = new Set<string>();\n const out: FormField[] = [];\n for (const row of rows) {\n const label = str(row['label']);\n if (!label) continue;\n if (out.length >= MAX_FIELDS) break;\n const rawType = str(row['type']);\n const type = (FIELD_TYPES as readonly string[]).includes(rawType)\n ? (rawType as FieldType)\n : 'text';\n const name = fieldName(row['name'], label, type, taken);\n const options =\n type === 'select' || type === 'checkboxes'\n ? str(row['options'])\n .split('\\n')\n .map((o) => o.trim())\n .filter(Boolean)\n : [];\n // A select with nothing to choose is unusable; render it as free text instead.\n const effective: FieldType =\n (type === 'select' || type === 'checkboxes') && options.length === 0 ? 'text' : type;\n out.push({\n name,\n id: `form-${out.length + 1}-${name}`,\n label,\n type: effective,\n required: row['required'] === true,\n placeholder:\n effective === 'checkbox' || effective === 'select' || effective === 'checkboxes'\n ? ''\n : str(row['placeholder']),\n help: str(row['help']),\n options,\n autocomplete: autocompleteFor(name, effective),\n });\n }\n return out;\n}\n\n/**\n * Where the endpoint may send a visitor back to: a same-site path, nothing else. The form\n * posts its own page path as `_next`; the route that reads it must apply this too, or the\n * field becomes an open redirect.\n */\nexport function safeNext(input: unknown): string | null {\n const v = typeof input === 'string' ? input : '';\n if (!v.startsWith('/') || v.startsWith('//') || v.includes('\\\\') || v.length > 200) return null;\n // Control characters (and a query/fragment) are exactly what a redirect target must not carry.\n // eslint-disable-next-line no-control-regex\n if (/[\\u0000-\\u001f\\u007f#?]/.test(v)) return null;\n return v;\n}\n\n/** The endpoint a form posts to: a same-site path, defaulting to the conversion route. */\nexport function safeAction(input: unknown): string {\n return safeNext(input) ?? '/api/track';\n}\n","/**\n * A saved `vitops_form` block as a `site.templates` form entry.\n *\n * `vitops legal` derives \"what personal information we collect\" from `site.templates` entries\n * of type `form`, and a build cannot read the CMS database the editor's block lives in, so a\n * form added in the admin is invisible to the generated privacy policy. This turns a block's\n * saved value (copy it from the admin's JSON view, or from `emdash` content export) into the\n * entry to paste under `site.templates` in `site.json`.\n *\n * It resolves fields through the SAME `resolveFields` the renderer uses, so the names are the\n * ones the form actually submits (`phone` becomes `tel`, reserved names are renamed) and rows\n * the renderer drops (no label) are dropped here too. A `checkboxes` becomes a `checkbox`\n * with its choices as `options`: the policy's schema has no group type and both read as\n * \"your answers to the questions we ask\".\n */\nimport { resolveFields, safeAction } from './astro/blocks/form.ts';\n\n/** The `fields` row shape of a `site.templates` form entry (`FormFieldSchema`). */\nexport interface FormTemplateField {\n name: string;\n type: 'text' | 'email' | 'tel' | 'textarea' | 'select' | 'checkbox';\n label: string;\n required?: boolean;\n options?: { label: string; value: string }[];\n}\n\nexport interface FormTemplate {\n type: 'form';\n action: string;\n method: 'post';\n honeypot: true;\n fields: FormTemplateField[];\n}\n\n/** `block` is a saved `vitops_form` value (the object with `fields`, `action`, …). */\nexport function formTemplateFromBlock(block: unknown): FormTemplate {\n const value = (block && typeof block === 'object' ? block : {}) as Record<string, unknown>;\n const rawRows = Array.isArray(value['fields']) ? value['fields'] : [];\n const rows = rawRows.filter(\n (r): r is Record<string, unknown> => !!r && typeof r === 'object' && !Array.isArray(r),\n );\n const action = safeAction(value['action']);\n return {\n type: 'form',\n action,\n method: 'post',\n honeypot: true,\n fields: resolveFields(rows).map((f) => ({\n name: f.name,\n type: f.type === 'checkboxes' ? 'checkbox' : f.type,\n label: f.label,\n ...(f.required ? { required: true } : {}),\n ...(f.options.length ? { options: f.options.map((o) => ({ label: o, value: o })) } : {}),\n })),\n };\n}\n","/**\n * Hosting seam — one call in astro.config.mjs resolves the adapter + EmDash\n * database/storage for the chosen hosting target, so a site can start on\n * Cloudflare and move to a Node host (VPS / docker-compose / k8s) — or back —\n * by flipping a single value instead of rewiring its config:\n *\n * ```js\n * import { vitopsEmdash, vitopsHosting } from '@getvitops/emdash';\n * const { adapter, database, storage } = await vitopsHosting();\n * export default defineConfig({\n * output: 'server',\n * adapter,\n * integrations: [react(), emdash({ database, storage, plugins: [vitopsEmdash()] })],\n * });\n * ```\n *\n * Targets:\n * - `'cloudflare'` (default) — `@astrojs/cloudflare` + D1/R2 bindings.\n * Needs `@astrojs/cloudflare` and `@emdash-cms/cloudflare` installed.\n * - `'node'` — `@astrojs/node` (standalone) + SQLite file + local uploads.\n * Needs `@astrojs/node` and `better-sqlite3` installed. Scheduled publishing\n * runs in-process on Node (no worker/cron trigger needed). For production,\n * move storage to S3-compatible (`s3()` from `emdash/astro`) and/or the\n * database to Postgres (`postgres()` from `emdash/db`) via `options.node`.\n *\n * The adapter packages are optional peer dependencies, resolved lazily — a\n * site only installs the stack for the target it uses. Content lives in the\n * database and media in the storage backend, so switching hosts is a data\n * migration (D1 export / EmDash seed round-trip + media copy), not a rewrite.\n */\nimport type { AstroIntegration } from 'astro';\n\nexport type HostingTarget = 'cloudflare' | 'node';\n\nconst TARGETS: HostingTarget[] = ['cloudflare', 'node'];\n\nexport interface VitopsHostingOptions {\n /** Hosting target. Overridden by the `HOSTING` env var. Default 'cloudflare'. */\n target?: HostingTarget;\n cloudflare?: {\n /** D1 binding name in wrangler config (default 'DB'). */\n dbBinding?: string;\n /** R2 binding name in wrangler config (default 'MEDIA'). */\n mediaBinding?: string;\n /** D1 Sessions API read-replication mode (default 'auto'). */\n session?: string;\n /**\n * Batch reads issued together into one D1 round trip (default true).\n * Experimental in emdash: a read and a write started together may run\n * write-first, so await a read before a write that must not affect it.\n * No effect without a session.\n */\n coalesce?: boolean;\n };\n node?: {\n /** SQLite URL (default `DATABASE_PATH` env or 'file:./data/emdash.db'). */\n databaseUrl?: string;\n /** Full database descriptor override (e.g. postgres() from 'emdash/db'). */\n database?: unknown;\n /** Local uploads directory (default './data/uploads'). */\n uploadsDir?: string;\n /** Full storage descriptor override (e.g. s3() from 'emdash/astro'). */\n storage?: unknown;\n };\n}\n\nexport interface VitopsHosting {\n target: HostingTarget;\n adapter: AstroIntegration;\n /** EmDash database descriptor — pass to emdash({ database }). */\n database: unknown;\n /** EmDash storage descriptor — pass to emdash({ storage }). */\n storage: unknown;\n}\n\n/**\n * Import an optional hosting dependency. The specifier is passed as a\n * variable so bundlers/type-checkers leave it as a plain runtime import —\n * these packages are optional peers a consumer installs per target.\n */\nasync function importFor(target: HostingTarget, specifier: string, install: string) {\n try {\n return await import(/* @vite-ignore */ specifier);\n } catch (cause) {\n throw new Error(\n `[vitops] hosting target '${target}' needs ${specifier}, which isn't installed.\\n` +\n `Install the ${target} stack first: ${install}`,\n { cause },\n );\n }\n}\n\nexport async function vitopsHosting(options: VitopsHostingOptions = {}): Promise<VitopsHosting> {\n const target = (process.env.HOSTING as HostingTarget) || options.target || 'cloudflare';\n\n if (target === 'cloudflare') {\n const install = 'pnpm add @astrojs/cloudflare @emdash-cms/cloudflare';\n const cf = options.cloudflare ?? {};\n const { default: cloudflare } = await importFor(target, '@astrojs/cloudflare', install);\n const { d1, r2 } = await importFor(target, '@emdash-cms/cloudflare', install);\n return {\n target,\n adapter: cloudflare(),\n database: d1({\n binding: cf.dbBinding ?? 'DB',\n session: cf.session ?? 'auto',\n coalesce: cf.coalesce ?? true,\n }),\n storage: r2({ binding: cf.mediaBinding ?? 'MEDIA' }),\n };\n }\n\n if (target === 'node') {\n const install = 'pnpm add @astrojs/node better-sqlite3';\n const opts = options.node ?? {};\n const { default: node } = await importFor(target, '@astrojs/node', install);\n // Required peer of this package, so always present — but still routed\n // through importFor: a literal import('emdash/db') would be statically\n // analyzable, and bundling the plugin runtime would drag EmDash's whole\n // integration graph into the site's server/client bundles.\n const { sqlite } = await importFor(target, 'emdash/db', install);\n const { local } = await importFor(target, 'emdash/astro', install);\n const { mkdirSync } = await import('node:fs');\n const { dirname } = await import('node:path');\n\n const databaseUrl = opts.databaseUrl ?? process.env.DATABASE_PATH ?? 'file:./data/emdash.db';\n const uploadsDir = opts.uploadsDir ?? './data/uploads';\n // better-sqlite3 refuses to create parent directories, so make sure the\n // defaults exist (runs at astro config load — dev and build). A deployed\n // host still needs the data directory mounted/persisted as a volume.\n if (!opts.database && databaseUrl.startsWith('file:')) {\n mkdirSync(dirname(databaseUrl.slice('file:'.length)), { recursive: true });\n }\n if (!opts.storage) mkdirSync(uploadsDir, { recursive: true });\n\n return {\n target,\n adapter: node({ mode: 'standalone' }),\n database: opts.database ?? sqlite({ url: databaseUrl }),\n storage: opts.storage ?? local({ directory: uploadsDir, baseUrl: '/_emdash/api/media/file' }),\n };\n }\n\n throw new Error(\n `[vitops] unknown hosting target '${target}' (from ${process.env.HOSTING ? 'HOSTING env var' : 'options.target'}). ` +\n `Valid targets: ${TARGETS.join(', ')}.`,\n );\n}\n","","/**\n * @getvitops/emdash — EmDash CMS native plugin for the Vitops design system.\n *\n * Declares Vitops Portable Text block types (editable from the EmDash admin's\n * slash menu) and ships the Astro components that render them via the\n * `componentsEntry` (`@getvitops/emdash/astro`).\n *\n * Composes with — does not replace — the `vitops()` Astro integration from\n * `@getvitops/astro`: the integration owns design-system CSS generation and\n * copies the web-component bundles into `public/<wcBase>/`; this plugin only\n * adds the editor-facing layer. Register both in the consumer site:\n *\n * ```js\n * // astro.config.mjs\n * import config from './site.json' with { type: 'json' };\n *\n * integrations: [\n * react(),\n * vitops({ css: {...} }),\n * emdash({ database, storage, plugins: [vitopsEmdash({ designSystemEditor: { config } })] }),\n * ]\n * ```\n */\nimport { definePlugin, getMenus, getWidgetAreas } from 'emdash';\nimport type {\n PageFragmentContribution,\n PageFragmentEvent,\n PluginCapability,\n PluginContext,\n PluginDescriptor,\n} from 'emdash';\nimport {\n BLOCKS,\n MENUS_ROUTE,\n THEMES_ROUTE,\n TONES_ROUTE,\n WIDGET_AREAS_ROUTE,\n selectBlocks,\n type BlockSelection,\n} from './blocks.ts';\nimport { createMenusRoute } from './menus-route.ts';\nimport { createThemesRoute, createTonesRoute, rolesOf } from './tones-route.ts';\nimport {\n createDesignSystemRoute,\n designSystemOverrideFragment,\n type DesignSystemEditorOptions,\n} from './design-system-route.ts';\n\nexport { BLOCKS } from './blocks.ts';\nexport { selectBlocks } from './blocks.ts';\nexport { renderBlocksDoc } from './blocks-doc.ts';\nexport { formTemplateFromBlock } from './form-template.ts';\nexport type { FormTemplate, FormTemplateField } from './form-template.ts';\nexport { SECTION_BLOCK_TYPE, SECTION_SLUG, vitopsBlockTypes } from './block-types.ts';\nexport { resolveTone, toneChain } from './astro/blocks/tone.ts';\nexport type { BlockKitFieldDef, BlockSelection, PortableTextBlockDef } from './blocks.ts';\nexport { vitopsHosting } from './hosting.ts';\nexport type { HostingTarget, VitopsHosting, VitopsHostingOptions } from './hosting.ts';\nexport type { DesignSystemEditorOptions } from './design-system-route.ts';\n\n/**\n * Read from package.json rather than hand-maintained.\n *\n * This was a literal with a \"keep in sync\" comment, which drifted on every\n * release: `changeset version` bumps package.json and leaves the literal alone.\n * `blocks.test.ts` asserts they match, but the release chain didn't run tests,\n * so `@getvitops/emdash@0.2.1` shipped a descriptor reporting `0.2.0`. Deriving\n * it removes the failure mode instead of relying on remembering.\n */\nimport pkg from '../package.json' with { type: 'json' };\n\nconst VERSION = pkg.version;\nconst PLUGIN_ID = 'vitops';\nconst FRAGMENTS_CAPABILITY: PluginCapability = 'hooks.page-fragments:register';\n/** For the design-system-editor's GitHub auto-commit — see design-system-route.ts. */\nconst NETWORK_CAPABILITY: PluginCapability = 'network:request';\n\nexport interface VitopsEmdashOptions {\n /**\n * How the web-component runtime scripts reach the page:\n * - `'integration'` (default) — the site's layout renders `<Head />` from\n * `@getvitops/astro`, which emits the script tags; this plugin adds none.\n * - `'fragments'` — for layouts using EmDash's `<EmDashHead/>`/`<EmDashBodyEnd/>`\n * instead: the plugin injects the script tags via the `page:fragments` hook.\n * Either way the bundles themselves must exist under `public/<wcBase>/`,\n * which the `vitops()` integration provides (webComponents: true).\n * Do not enable both delivery paths — the scripts would load twice.\n */\n scripts?: 'integration' | 'fragments';\n /** Public base path of the web-component bundles (default '/vitops'). */\n wcBase?: string;\n /**\n * Mounts a `<wc-theme-editor>` admin page at `/design-system`, backed by a\n * plugin API route — see AGENTS.md's \"EmDash plugin as the design-system\n * editing surface\" section and `design-system-route.ts`'s own docblock.\n * Off by default. Requires the `network:request` capability (declared\n * automatically when this is set) for the GitHub auto-commit half —\n * reviewed once at plugin-install time, same as any other capability.\n */\n designSystemEditor?: DesignSystemEditorOptions | false;\n /**\n * Which blocks the editor offers. All of the `vitops_*` blocks by default. Use `exclude` to\n * drop one a client should not see, `include` for an allowlist, and `extra` to add the\n * site's own — whose renderers the site supplies, see {@link BlockSelection}.\n */\n blocks?: BlockSelection;\n /**\n * The site's colour role names (`Object.keys(site.designSystem.colors.roles)`), offered in\n * every block's `tone` select next to the semantic names. When omitted and\n * `designSystemEditor.config` is set, they are read from that config. With neither, the\n * select offers only the semantic names — an editor can still type a palette colour.\n */\n tones?: string[];\n}\n\n/**\n * Descriptor factory — import in `astro.config.mjs` and pass to the emdash()\n * integration's `plugins` array.\n */\nexport function vitopsEmdash(options: VitopsEmdashOptions = {}): PluginDescriptor {\n return {\n id: PLUGIN_ID,\n version: VERSION,\n format: 'native',\n entrypoint: '@getvitops/emdash',\n componentsEntry: '@getvitops/emdash/astro',\n ...(options.designSystemEditor ? { adminEntry: '@getvitops/emdash/admin' } : {}),\n options,\n capabilities: [\n ...(options.scripts === 'fragments' || options.designSystemEditor\n ? [FRAGMENTS_CAPABILITY]\n : []),\n ...(options.designSystemEditor ? [NETWORK_CAPABILITY] : []),\n ] as PluginCapability[],\n } as PluginDescriptor;\n}\n\n/** Runtime side — EmDash imports and calls this from the descriptor's entrypoint. */\nexport function createPlugin(options: VitopsEmdashOptions = {}) {\n const scripts = options.scripts ?? 'integration';\n const wcBase = options.wcBase ?? '/vitops';\n const editor = options.designSystemEditor || undefined;\n const fragmentsEnabled = scripts === 'fragments' || !!editor;\n // The site's colour roles, for every block's `tone` select: stated, or read from the config\n // the design-system editor already carries — see tones-route.ts.\n const roles = options.tones ?? rolesOf(editor?.config);\n\n // Mirrors @getvitops/astro Head.astro: module polyfills + elements in\n // head, deferred enhancements at end of body. Composed with the\n // design-system-editor's override fragment below — either half can be on\n // independently of the other.\n const pageFragments = async (\n _event: PageFragmentEvent,\n ctx: PluginContext,\n ): Promise<PageFragmentContribution[]> => {\n const contributions: PageFragmentContribution[] = [];\n if (scripts === 'fragments') {\n contributions.push(\n {\n kind: 'html',\n placement: 'head',\n html:\n `<link rel=\"modulepreload\" href=\"${wcBase}/polyfills.js\">` +\n `<script type=\"module\" src=\"${wcBase}/polyfills.js\"></script>` +\n `<script type=\"module\" src=\"${wcBase}/elements.js\"></script>`,\n key: 'vitops-wc',\n },\n {\n kind: 'external-script',\n placement: 'body:end',\n src: `${wcBase}/deferred.js`,\n defer: true,\n key: 'vitops-deferred',\n },\n );\n }\n if (editor) {\n const fragment = await designSystemOverrideFragment(ctx, editor);\n if (fragment) contributions.push(fragment);\n }\n return contributions;\n };\n\n return definePlugin({\n id: PLUGIN_ID,\n version: VERSION,\n capabilities: [\n ...(fragmentsEnabled ? [FRAGMENTS_CAPABILITY] : []),\n ...(editor ? [NETWORK_CAPABILITY] : []),\n ],\n ...(editor ? { allowedHosts: ['api.github.com'] } : {}),\n ...(fragmentsEnabled ? { hooks: { 'page:fragments': pageFragments } } : {}),\n\n admin: {\n portableTextBlocks: selectBlocks(options.blocks),\n ...(editor\n ? {\n entry: '@getvitops/emdash/admin',\n pages: [{ path: '/design-system', label: 'Design System', icon: 'palette' }],\n }\n : {}),\n },\n\n routes: {\n [TONES_ROUTE]: createTonesRoute(() => roles),\n [THEMES_ROUTE]: createThemesRoute(() => roles),\n [MENUS_ROUTE]: createMenusRoute(() => getMenus()),\n // Same shape as a menu summary (`name`, `label`), so the same route factory lists areas.\n [WIDGET_AREAS_ROUTE]: createMenusRoute(() => getWidgetAreas()),\n ...(editor ? { 'design-system': createDesignSystemRoute(editor) } : {}),\n },\n });\n}\n\nexport default createPlugin;\n"],"mappings":"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"}
|