@getvitops/emdash 1.0.0 → 2.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +69 -0
- package/dist/index.d.mts +33 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1308 -184
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -7
- package/src/admin/DesignSystemEditorPage.tsx +15 -42
- package/src/astro/blocks/Callout.astro +73 -0
- package/src/astro/blocks/Cards.astro +53 -9
- package/src/astro/blocks/Embed.astro +67 -0
- package/src/astro/blocks/Entries.astro +30 -0
- package/src/astro/blocks/Form.astro +194 -0
- package/src/astro/blocks/Hero.astro +77 -23
- package/src/astro/blocks/Logos.astro +48 -0
- package/src/astro/blocks/Nav.astro +85 -0
- package/src/astro/blocks/NavTree.astro +48 -0
- package/src/astro/blocks/Pricing.astro +66 -0
- package/src/astro/blocks/Reviews.astro +122 -0
- package/src/astro/blocks/RichText.astro +14 -0
- package/src/astro/blocks/Section.astro +84 -0
- package/src/astro/blocks/Stats.astro +59 -0
- package/src/astro/blocks/Steps.astro +48 -0
- package/src/astro/blocks/embed.test.ts +43 -0
- package/src/astro/blocks/embed.ts +84 -0
- package/src/astro/blocks/form.sample.ts +33 -0
- package/src/astro/blocks/form.test.ts +55 -0
- package/src/astro/blocks/form.ts +158 -0
- package/src/astro/blocks/head.stub.ts +2 -0
- package/src/astro/blocks/nav.test.ts +51 -0
- package/src/astro/blocks/nav.ts +65 -0
- package/src/astro/blocks/render.container.test.ts +570 -0
- package/src/astro/blocks/tone.test.ts +53 -0
- package/src/astro/blocks/tone.ts +122 -0
- package/src/astro/blocks/value.test.ts +36 -1
- package/src/astro/blocks/value.ts +78 -1
- package/src/astro/index.ts +30 -0
- package/src/admin/core-editor.d.ts +0 -7
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["pkg.version"],"sources":["../src/icon-options.ts","../src/blocks.ts","../src/design-system-route.ts","../src/blocks-doc.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 * 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 belong in\n * an EmDash `blocks` field; see the README.\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';\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\nexport const 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 { type: 'text_input', action_id: 'body', label: 'Text', multiline: true },\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 },\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 { type: 'text_input', action_id: 'heading', label: 'Heading' },\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 ],\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\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 `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';\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 'Blocks are flat — a block cannot contain other blocks, and a repeater row holds scalar fields only.',\n 'Give every block a unique `_key`; gallery and carousel derive their element id from it.',\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 return out.join('\\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 } from 'emdash';\nimport type {\n PageFragmentContribution,\n PageFragmentEvent,\n PluginCapability,\n PluginContext,\n PluginDescriptor,\n} from 'emdash';\nimport { BLOCKS, selectBlocks, type BlockSelection } from './blocks.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 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\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\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 ...(editor\n ? {\n routes: {\n 'design-system': createDesignSystemRoute(editor),\n },\n }\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/blocks.ts","../src/menus-route.ts","../src/tones-route.ts","../src/design-system-route.ts","../src/block-types.ts","../src/blocks-doc.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.\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 return {\n className: 'tone',\n style: [\n `--tone-bg: ${at('100')}`,\n `--tone-fg: ${at('900')}`,\n `--tone-border: ${at('300')}`,\n `--tone-solid: ${at(step ?? '600')}`,\n `--tone-on-solid: #fff`,\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 * 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';\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\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/** 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\nexport const 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 levelField('2'),\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 { type: 'text_input', action_id: 'image', label: 'Image URL' },\n { type: 'text_input', action_id: 'alt', label: 'Name (alt text)' },\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 ],\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\" only)',\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\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 `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","/**\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\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('level', 'Heading level', ['2', '3', '4', '5', '6'], '2'),\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];\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","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`; its contrast is not checked. Renderers emit `--tone-*` custom',\n 'properties, never a class built from the value. Multiline fields that accept links take',\n '`[text](/url)`; hero headings accent words wrapped in `*stars*`.',\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 '',\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 * 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 } from 'emdash';\nimport type {\n PageFragmentContribution,\n PageFragmentEvent,\n PluginCapability,\n PluginContext,\n PluginDescriptor,\n} from 'emdash';\nimport { BLOCKS, MENUS_ROUTE, TONES_ROUTE, selectBlocks, type BlockSelection } from './blocks.ts';\nimport { createMenusRoute } from './menus-route.ts';\nimport { 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 { 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 [MENUS_ROUTE]: createMenusRoute(() => getMenus()),\n ...(editor ? { 'design-system': createDesignSystemRoute(editor) } : {}),\n },\n });\n}\n\nexport default createPlugin;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAiBA,MAAa,wBAA4D,OAAO,KAAK,QAAQ,GAAG,CAAC,CAC9F,KAAK,CAAC,CAIN,KAAK,UAAU;CAAE,OAAO;CAAM,OAAO;AAAK,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;ACE/C,MAAa,iBAAiB;CAAC;CAAW;CAAQ;CAAW;CAAW;AAAQ;;;;;;AAOhF,MAAM,YAAsC;CAC1C,iBAAiB,CAAC,cAAc,MAAM;CACtC,mBAAmB,CAAC,gBAAgB,MAAM;CAC1C,cAAc,CAAC,MAAM;CACrB,gBAAgB,CAAC,MAAM;CACvB,aAAa,CAAC,SAAS;CACvB,SAAS,CAAC,SAAS;CACnB,OAAO,CAAC,QAAQ;CAChB,QAAQ,CAAC,SAAS;CAClB,MAAM,CAAC,MAAM;CAEb,IAAI,CAAC,cAAc,MAAM;CACzB,IAAI,CAAC,aAAa,SAAS;AAC7B;AAEA,MAAM,OAAO;AACb,MAAM,MAAM;;AAGZ,SAAgB,UAAU,MAAyB;CACjD,MAAM,OAAO,OAAO,SAAS,WAAW,KAAK,KAAK,CAAC,CAAC,YAAY,IAAI;CACpE,IAAI,CAAC,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,SAAS;CACvC,OAAO,CAAC,mBAAG,IAAI,IAAI;EAAC;EAAM,GAAI,UAAU,SAAS,CAAC;EAAI;CAAS,CAAC,CAAC;AACnE;;AAGA,SAAS,MAAM,QAA0B;CACvC,OAAO,OAAO,aACX,OAAO,UAAW,QAAQ,OAAO,MAAM,IAAI,MAAM,KAAK,OAAO,MAAM,IACpE,EACF;AACF;;;;;;;AAeA,SAAgB,YAAY,MAAgB,OAA+B;CACzE,MAAM,UAAU,OAAO,SAAS,YAAY,KAAK,KAAK,MAAM;CAC5D,MAAM,MAAM,OAAO,UAAU,WAAW,IAAI,KAAK,MAAM,KAAK,CAAC,CAAC,YAAY,CAAC,IAAI;CAE/E,IAAI,KAAK;EACP,MAAM,GAAG,MAAM,QAAQ;EACvB,MAAM,MAAM,MAAc,eAAe,KAAK,GAAG,EAAE;EACnD,OAAO;GACL,WAAW;GACX,OAAO;IACL,cAAc,GAAG,KAAK;IACtB,cAAc,GAAG,KAAK;IACtB,kBAAkB,GAAG,KAAK;IAC1B,iBAAiB,GAAG,QAAQ,KAAK;IACjC;GACF,CAAC,CAAC,KAAK,IAAI;EACb;CACF;CAEA,IAAI,CAAC,SAAS,OAAO;EAAE,WAAW;EAAQ,OAAO;CAAG;CAEpD,MAAM,QAAQ,UAAU,IAAI;CAC5B,MAAM,MAAM,QAAgB,SAAS,OACnC,MAAM,MAAM,KAAK,MAAM,WAAW,OAAO,GAAG,IAAI,QAAQ,CAAC;CAC3D,OAAO;EACL,WAAW;EACX,OAAO;GACL,cAAc,GAAG,MAAM,QAAQ;GAC/B,cAAc,GAAG,MAAM;GACvB,kBAAkB,GAAG,QAAQ;GAE7B,iBAAiB,MAAM,CAAC,GAAG,MAAM,KAAK,MAAM,cAAc,EAAE,OAAO,GAAG,GAAG,MAAM,KAAK,MAAM,cAAc,GAAG,CAAC,CAAC;GAC7G,oBAAoB,MAAM,MAAM,KAAK,MAAM,mBAAmB,GAAG,CAAC;EACpE,CAAC,CAAC,KAAK,IAAI;CACb;AACF;;;;ACtEA,MAAa,cAAc;;AAG3B,MAAa,cAAc;;;;;;AAO3B,MAAa,sBAAsB,CACjC;CAAE,OAAO;CAAQ,OAAO;AAAG,GAC3B,GAAG,eAAe,KAAK,OAAO;CAAE,OAAO,EAAE,EAAE,CAAE,YAAY,IAAI,EAAE,MAAM,CAAC;CAAG,OAAO;AAAE,EAAE,CACtF;;;;;;AAOA,SAAS,aAAiC;CACxC,OAAO,CACL;EACE,MAAM;EACN,WAAW;EACX,OAAO;EACP,eAAe;EACf,SAAS;EACT,cAAc;CAChB,GACA;EACE,MAAM;EACN,WAAW;EACX,OAAO;CACT,CACF;AACF;;AAGA,MAAM,cAAc,aAAuC;CACzD,MAAM;CACN,WAAW;CACX,OAAO;CACP,eAAe;CACf,SAAS;EAAC;EAAK;EAAK;EAAK;EAAK;CAAG,CAAC,CAAC,KAAK,OAAO;EAAE,OAAO,IAAI;EAAK,OAAO;CAAE,EAAE;AAC9E;AAEA,MAAa,SAAiC;CAC5C;;;;;;;;;;;;;;EAcE,MAAM;EACN,OAAO;EACP,MAAM;EACN,aAAa;EACb,QAAQ;GACN;IAAE,MAAM;IAAc,WAAW;IAAS,OAAO;GAAQ;GACzD;IAAE,MAAM;IAAc,WAAW;IAAQ,OAAO;GAAM;GACtD;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KACP;MAAE,OAAO;MAAQ,OAAO;KAAO;KAC/B;MAAE,OAAO;MAAU,OAAO;KAAM;KAChC;MAAE,OAAO;MAAkB,OAAO;KAAM;IAC1C;GACF;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,aAAa;IACb,SAAS;GACX;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,aAAa;IACb,SAAS;GACX;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KACP;MAAE,OAAO;MAAW,OAAO;KAAG;KAC9B;MAAE,OAAO;MAAW,OAAO;KAAU;KACrC;MAAE,OAAO;MAAU,OAAO;KAAS;KACnC;MAAE,OAAO;MAAW,OAAO;KAAU;KACrC;MAAE,OAAO;MAAQ,OAAO;KAAO;IACjC;GACF;GACA;IAAE,MAAM;IAAU,WAAW;IAAU,OAAO;IAAqB,eAAe;GAAM;EAC1F;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,QAAQ;GACN;IAAE,MAAM;IAAc,WAAW;IAAU,OAAO;GAAmB;GACrE;IAAE,MAAM;IAAc,WAAW;IAAa,OAAO;GAAkB;GACvE;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA;IAAE,MAAM;IAAc,WAAW;IAAS,OAAO;GAAkB;GACnE;IAAE,MAAM;IAAc,WAAW;IAAY,OAAO;GAAiB;GACrE;IAAE,MAAM;IAAc,WAAW;IAAc,OAAO;IAAe,eAAe;GAAQ;EAC9F;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,MAAM;EACN,aAAa;EACb,QAAQ;GACN;IAAE,MAAM;IAAc,WAAW;IAAQ,OAAO;IAAgB,WAAW;GAAK;GAChF;IAAE,MAAM;IAAc,WAAW;IAAS,OAAO;IAAgB,eAAe;GAAO;GACvF;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS,CACP;KAAE,OAAO;KAAc,OAAO;IAAa,GAC3C;KAAE,OAAO;KAAU,OAAO;IAAS,CACrC;GACF;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,QAAQ;GACN;IAAE,MAAM;IAAc,WAAW;IAAW,OAAO;IAAW,WAAW;GAAK;GAC9E;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KACP;MAAE,OAAO;MAAQ,OAAO;KAAO;KAC/B;MAAE,OAAO;MAAW,OAAO;KAAU;KACrC;MAAE,OAAO;MAAW,OAAO;KAAU;KACrC;MAAE,OAAO;MAAU,OAAO;KAAS;IACrC;GACF;GACA;IAAE,MAAM;IAAU,WAAW;IAAe,OAAO;IAAe,eAAe;GAAK;EACxF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,QAAQ;GACN;IAAE,MAAM;IAAc,WAAW;IAAW,OAAO;GAA2B;GAC9E;IAAE,MAAM;IAAc,WAAW;IAAQ,OAAO;IAAmB,WAAW;GAAK;GACnF;IAAE,MAAM;IAAU,WAAW;IAAQ,OAAO;IAAmB,eAAe;GAAM;EACtF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,YAAY;IACZ,WAAW;IACX,QAAQ,CACN;KAAE,MAAM;KAAc,WAAW;KAAO,OAAO;IAAY,GAC3D;KAAE,MAAM;KAAc,WAAW;KAAO,OAAO;IAAW,CAC5D;GACF;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,WAAW;GACb;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,WAAW;GACb;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,YAAY;IACZ,WAAW;IACX,QAAQ;KACN;MAAE,MAAM;MAAc,WAAW;MAAO,OAAO;KAAY;KAC3D;MAAE,MAAM;MAAc,WAAW;MAAO,OAAO;KAAW;KAC1D;MAAE,MAAM;MAAc,WAAW;MAAW,OAAO;KAAU;IAC/D;GACF;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA;IAAE,MAAM;IAAU,WAAW;IAAQ,OAAO;IAA2B,eAAe;GAAM;EAC9F;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,YAAY;IACZ,WAAW;IACX,QAAQ;KACN;MAAE,MAAM;MAAc,WAAW;MAAS,OAAO;KAAQ;KACzD;MACE,MAAM;MACN,WAAW;MACX,OAAO;MACP,WAAW;KACb;KACA;MAAE,MAAM;MAAc,WAAW;MAAS,OAAO;KAAY;KAC7D;MAAE,MAAM;MAAc,WAAW;MAAY,OAAO;KAAiB;KACrE;MACE,MAAM;MACN,WAAW;MACX,OAAO;KACT;KACA;MACE,MAAM;MACN,WAAW;MACX,OAAO;MACP,eAAe;KACjB;KACA;MACE,MAAM;MACN,WAAW;MACX,OAAO;MACP,WAAW;KACb;KACA;MAAE,MAAM;MAAc,WAAW;MAAY,OAAO;KAA4B;KAChF;MAAE,MAAM;MAAc,WAAW;MAAW,OAAO;KAA2B;IAChF;GACF;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS,CACP;KAAE,OAAO;KAAS,OAAO;IAAU,GACnC;KAAE,OAAO;KAA8C,OAAO;IAAQ,CACxE;GACF;GACA,WAAW,GAAG;GACd,GAAG,WAAW;GACd;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KACP;MAAE,OAAO;MAAK,OAAO;KAAI;KACzB;MAAE,OAAO;MAAK,OAAO;KAAI;KACzB;MAAE,OAAO;MAAK,OAAO;KAAI;KACzB;MAAE,OAAO;MAAK,OAAO;KAAI;IAC3B;GACF;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ,CACN;GACE,MAAM;GACN,WAAW;GACX,OAAO;GACP,YAAY;GACZ,WAAW;GACX,QAAQ;IACN;KAAE,MAAM;KAAc,WAAW;KAAW,OAAO;IAA2B;IAC9E;KAAE,MAAM;KAAc,WAAW;KAAQ,OAAO;KAAmB,WAAW;IAAK;IACnF;KAAE,MAAM;KAAU,WAAW;KAAQ,OAAO;KAAmB,eAAe;IAAM;GACtF;EACF,GACA;GACE,MAAM;GACN,WAAW;GACX,OAAO;GACP,eAAe;EACjB,CACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IAAE,MAAM;IAAc,WAAW;IAAW,OAAO;GAA6B;GAChF;IACE,MAAM;IACN,WAAW;IACX,OAAO;GACT;GACA,WAAW,GAAG;GACd;IAAE,MAAM;IAAc,WAAW;IAAQ,OAAO;IAAQ,WAAW;GAAK;GACxE;IAAE,MAAM;IAAc,WAAW;IAAgB,OAAO;GAAmB;GAC3E;IAAE,MAAM;IAAc,WAAW;IAAe,OAAO;GAAkB;GACzE;IAAE,MAAM;IAAc,WAAW;IAAkB,OAAO;GAAmB;GAC7E;IAAE,MAAM;IAAc,WAAW;IAAiB,OAAO;GAAkB;GAC3E;IAAE,MAAM;IAAc,WAAW;IAAS,OAAO;GAA2B;GAC5E;IAAE,MAAM;IAAc,WAAW;IAAY,OAAO;GAAiB;GACrE;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS,CACP;KAAE,OAAO;KAAY,OAAO;IAAW,GACvC;KAAE,OAAO;KAA+B,OAAO;IAAQ,CACzD;GACF;GACA,GAAG,WAAW;GACd;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,YAAY;IACZ,QAAQ;KACN;MAAE,MAAM;MAAc,WAAW;MAAS,OAAO;KAAQ;KACzD;MACE,MAAM;MACN,WAAW;MACX,OAAO;MACP,WAAW;KACb;KACA;MAAE,MAAM;MAAc,WAAW;MAAQ,OAAO;KAAsC;KACtF;MACE,MAAM;MACN,WAAW;MACX,OAAO;MACP,eAAe;KACjB;IACF;GACF;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,YAAY;IACZ,WAAW;IACX,QAAQ;KACN;MAAE,MAAM;MAAc,WAAW;MAAQ,OAAO;KAAO;KACvD;MAAE,MAAM;MAAc,WAAW;MAAS,OAAO;KAAoC;KACrF;MAAE,MAAM;MAAc,WAAW;MAAO,OAAO;KAAiB;IAClE;GACF;GACA;IAAE,MAAM;IAAU,WAAW;IAAgB,OAAO;IAAkB,eAAe;GAAK;GAC1F;IAAE,MAAM;IAAU,WAAW;IAAW,OAAO;IAAwB,eAAe;GAAM;EAC9F;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,YAAY;IACZ,WAAW;IACX,QAAQ;KACN;MAAE,MAAM;MAAc,WAAW;MAAS,OAAO;KAAY;KAC7D;MAAE,MAAM;MAAc,WAAW;MAAO,OAAO;KAAkB;KACjE;MAAE,MAAM;MAAc,WAAW;MAAQ,OAAO;KAAsB;IACxE;GACF;GACA;IAAE,MAAM;IAAU,WAAW;IAAa,OAAO;IAAsB,eAAe;GAAM;GAC5F;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KACP;MAAE,OAAO;MAAS,OAAO;KAAK;KAC9B;MAAE,OAAO;MAAU,OAAO;KAAK;KAC/B;MAAE,OAAO;MAAS,OAAO;KAAK;IAChC;GACF;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,YAAY;IACZ,WAAW;IACX,QAAQ,CACN;KAAE,MAAM;KAAc,WAAW;KAAS,OAAO;IAAQ,GACzD;KACE,MAAM;KACN,WAAW;KACX,OAAO;KACP,WAAW;IACb,CACF;GACF;GACA,WAAW,GAAG;GACd;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS,CACP;KAAE,OAAO;KAAW,OAAO;IAAS,GACpC;KAAE,OAAO;KAA0B,OAAO;IAAM,CAClD;GACF;GACA,GAAG,WAAW;EAChB;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,YAAY;IACZ,WAAW;IACX,QAAQ;KACN;MAAE,MAAM;MAAc,WAAW;MAAS,OAAO;KAA8B;KAC/E;MAAE,MAAM;MAAc,WAAW;MAAS,OAAO;KAAQ;KACzD;MACE,MAAM;MACN,WAAW;MACX,OAAO;KACT;KACA;MACE,MAAM;MACN,WAAW;MACX,OAAO;KACT;KACA;MACE,MAAM;MACN,WAAW;MACX,OAAO;MACP,eAAe;MACf,SAAS;OACP;QAAE,OAAO;QAAW,OAAO;OAAG;OAC9B;QAAE,OAAO;QAAa,OAAO;OAAK;OAClC;QAAE,OAAO;QAAc,OAAO;OAAO;MACvC;KACF;IACF;GACF;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS,CACP;KAAE,OAAO;KAAW,OAAO;IAAU,GACrC;KAAE,OAAO;KAAS,OAAO;IAAQ,CACnC;GACF;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA,GAAG,WAAW;EAChB;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IAAE,MAAM;IAAc,WAAW;IAAW,OAAO;GAAU;GAC7D,WAAW,GAAG;GACd;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,WAAW;GACb;GACA;IAAE,MAAM;IAAc,WAAW;IAAgB,OAAO;GAAmB;GAC3E;IAAE,MAAM;IAAc,WAAW;IAAe,OAAO;GAAkB;GACzE;IAAE,MAAM;IAAc,WAAW;IAAkB,OAAO;GAAmB;GAC7E;IAAE,MAAM;IAAc,WAAW;IAAiB,OAAO;GAAkB;GAC3E;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS,CACP;KAAE,OAAO;KAA+B,OAAO;IAAQ,GACvD;KAAE,OAAO;KAAY,OAAO;IAAS,CACvC;GACF;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA,GAAG,WAAW;EAChB;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,YAAY;IACZ,WAAW;IACX,QAAQ,CACN;KAAE,MAAM;KAAc,WAAW;KAAW,OAAO;IAAqB,GACxE;KACE,MAAM;KACN,WAAW;KACX,OAAO;KACP,WAAW;IACb,CACF;GACF;GACA,WAAW,GAAG;GACd;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ,CACN;GACE,MAAM;GACN,WAAW;GACX,OAAO;GACP,YAAY;GACZ,WAAW;GACX,QAAQ;IACN;KAAE,MAAM;KAAc,WAAW;KAAQ,OAAO;IAAY;IAC5D;KAAE,MAAM;KAAc,WAAW;KAAS,OAAO;IAAmB;IACpE;KAAE,MAAM;KAAc,WAAW;KAAU,OAAO;IAA0B;IAC5E;KACE,MAAM;KACN,WAAW;KACX,OAAO;KACP,WAAW;IACb;IACA;KAAE,MAAM;KAAc,WAAW;KAAY,OAAO;IAAc;IAClE;KAAE,MAAM;KAAc,WAAW;KAAW,OAAO;IAAa;IAChE;KACE,MAAM;KACN,WAAW;KACX,OAAO;KACP,eAAe;IACjB;GACF;EACF,GACA,WAAW,GAAG,CAChB;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KACP;MAAE,OAAO;MAAW,OAAO;KAAU;KACrC;MAAE,OAAO;MAAS,OAAO;KAAQ;KACjC;MAAE,OAAO;MAA+C,OAAO;KAAM;KACrE;MAAE,OAAO;MAAyB,OAAO;KAAS;IACpD;GACF;GACA;IAAE,MAAM;IAAc,WAAW;IAAO,OAAO;GAAU;GACzD;IACE,MAAM;IACN,WAAW;IACX,OAAO;GACT;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KACP;MAAE,OAAO;MAAa,OAAO;KAAY;KACzC;MAAE,OAAO;MAAe,OAAO;KAAc;KAC7C;MAAE,OAAO;MAAa,OAAO;KAAY;IAC3C;GACF;GACA;IAAE,MAAM;IAAc,WAAW;IAAS,OAAO;IAAgB,eAAe;GAAS;EAC3F;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IAAE,MAAM;IAAc,WAAW;IAAW,OAAO;GAAU;GAC7D,WAAW,GAAG;GACd;IAAE,MAAM;IAAc,WAAW;IAAS,OAAO;IAAuB,WAAW;GAAK;GACxF;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,YAAY;IACZ,WAAW;IACX,QAAQ;KACN;MAAE,MAAM;MAAc,WAAW;MAAS,OAAO;KAAgC;KACjF;MACE,MAAM;MACN,WAAW;MACX,OAAO;KACT;KACA;MACE,MAAM;MACN,WAAW;MACX,OAAO;MACP,eAAe;MACf,SAAS;OACP;QAAE,OAAO;QAAc,OAAO;OAAO;OACrC;QAAE,OAAO;QAAiB,OAAO;OAAQ;OACzC;QAAE,OAAO;QAAgB,OAAO;OAAM;OACtC;QAAE,OAAO;QAAa,OAAO;OAAW;OACxC;QAAE,OAAO;QAAc,OAAO;OAAS;OACvC;QAAE,OAAO;QAAY,OAAO;OAAW;MACzC;KACF;KACA;MAAE,MAAM;MAAU,WAAW;MAAY,OAAO;MAAY,eAAe;KAAM;KACjF;MAAE,MAAM;MAAc,WAAW;MAAe,OAAO;KAAe;KACtE;MAAE,MAAM;MAAc,WAAW;MAAQ,OAAO;KAA4B;KAC5E;MACE,MAAM;MACN,WAAW;MACX,OAAO;MACP,WAAW;KACb;IACF;GACF;GACA;IAAE,MAAM;IAAc,WAAW;IAAe,OAAO;IAAe,eAAe;GAAO;GAC5F;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA;IAAE,MAAM;IAAc,WAAW;IAAgB,OAAO;GAA2B;GACnF;IACE,MAAM;IACN,WAAW;IACX,OAAO;GACT;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;GACT;GACA,GAAG,WAAW;EAChB;CACF;CACA;;;;;;;;;;EAUE,MAAM;EACN,OAAO;EACP,MAAM;EACN,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;GACT;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KACP;MAAE,OAAO;MAAQ,OAAO;KAAO;KAC/B;MAAE,OAAO;MAAY,OAAO;KAAW;KACvC;MAAE,OAAO;MAA2B,OAAO;KAAU;IACvD;GACF;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KAAC;KAAK;KAAK;KAAK;IAAG,CAAC,CAAC,KAAK,OAAO;KAAE,OAAO;KAAG,OAAO;IAAE,EAAE;GACnE;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KAAC;KAAK;KAAK;KAAK;KAAK;IAAG,CAAC,CAAC,KAAK,OAAO;KAC7C,OAAO,MAAM,MAAM,QAAQ,GAAG,EAAE;KAChC,OAAO;IACT,EAAE;GACJ;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS,CACP;KAAE,OAAO;KAAgB,OAAO;IAAS,GACzC;KAAE,OAAO;KAAuB,OAAO;IAAS,CAClD;GACF;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;GACT;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;GACjB;GACA,GAAG,WAAW;EAChB;CACF;CACA;;;;;;;;;;EAUE,MAAM;EACN,OAAO;EACP,MAAM;EACN,aAAa;EACb,UAAU;EACV,QAAQ;GACN;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KACP;MAAE,OAAO;MAAmB,OAAO;KAAiB;KACpD;MAAE,OAAO;MAAgB,OAAO;KAAe;KAC/C;MAAE,OAAO;MAAW,OAAO;KAAU;IACvC;IACA,cAAc;GAChB;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;GACT;GACA;IAAE,MAAM;IAAc,WAAW;IAAW,OAAO;GAA6B;GAChF,WAAW,GAAG;GACd;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KACP;MAAE,OAAO;MAAiB,OAAO;KAAO;KACxC;MAAE,OAAO;MAAc,OAAO;KAAS;KACvC;MAAE,OAAO;MAAoD,OAAO;KAAU;KAC9E;MAAE,OAAO;MAA4C,OAAO;KAAU;IACxE;GACF;GACA;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACP,eAAe;IACf,SAAS;KAAC;KAAK;KAAK;IAAG,CAAC,CAAC,KAAK,OAAO;KAAE,OAAO;KAAG,OAAO;IAAE,EAAE;GAC9D;GACA,GAAG,WAAW;EAChB;CACF;AACF;;;;;;;AAuBA,SAAgB,aAAa,YAA4B,CAAC,GAA2B;CACnF,MAAM,QAAQ,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,IAAI,CAAC;CAC/C,MAAM,SAAS,OAA6B,WAAyB;EACnE,KAAK,MAAM,QAAQ,SAAS,CAAC,GAC3B,IAAI,CAAC,MAAM,IAAI,IAAI,GACjB,MAAM,IAAI,MACR,mBAAmB,OAAO,mBAAmB,KAAK,YAAY,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,EACtF;CACN;CACA,MAAM,UAAU,SAAS,SAAS;CAClC,MAAM,UAAU,SAAS,SAAS;CAElC,MAAM,UAAU,UAAU,UAAU,IAAI,IAAI,UAAU,OAAO,IAAI;CACjE,MAAM,UAAU,IAAI,IAAI,UAAU,OAAO;CACzC,MAAM,OAAO,OAAO,QAAQ,OAAO,CAAC,WAAW,QAAQ,IAAI,EAAE,IAAI,MAAM,CAAC,QAAQ,IAAI,EAAE,IAAI,CAAC;CAE3F,MAAM,QAAQ,UAAU,SAAS,CAAC;CAClC,MAAM,QAAQ,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,IAAI,CAAC;CAC/C,KAAK,MAAM,SAAS,OAAO;EACzB,IAAI,MAAM,IAAI,MAAM,IAAI,GACtB,MAAM,IAAI,MAAM,gCAAgC,MAAM,KAAK,sBAAsB;EACnF,MAAM,IAAI,MAAM,IAAI;CACtB;CACA,OAAO,CAAC,GAAG,MAAM,GAAG,KAAK;AAC3B;;;;AC39BA,SAAgB,UAAU,OAAiD;CACzE,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,MAAwB,CAAC;CAC/B,KAAK,MAAM,EAAE,MAAM,WAAW,OAAO;EAEnC,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,GAAG;EAC7B,KAAK,IAAI,IAAI;EACb,IAAI,KAAK;GAAE,IAAI;GAAM,MAAM,SAAS,UAAU,OAAO,GAAG,MAAM,IAAI,KAAK,KAAK;EAAK,CAAC;CACpF;CACA,OAAO;AACT;AAEA,SAAgB,iBAAiB,MAA0D;CACzF,OAAO;EAEL,YAAY;EACZ,SAAS,YAAY;GACnB,IAAI;IACF,OAAO,EAAE,OAAO,UAAU,MAAM,KAAK,CAAC,EAAE;GAC1C,QAAQ;IAEN,OAAO,EAAE,OAAO,CAAC,EAAE;GACrB;EACF;CACF;AACF;;;;;;;;;;;;;;;;;;ACxBA,SAAgB,QAAQ,QAA2B;CACjD,IAAI,CAAC,UAAU,OAAO,WAAW,UAAU,OAAO,CAAC;CACnD,IAAI;EACF,MAAM,EAAE,iBAAiB,aAAa,QAAiB,EAAE,SAAS,aAAa,CAAC;EAChF,OAAO,OAAO,KAAK,aAAa,QAAQ,SAAS,CAAC,CAAC;CACrD,QAAQ;EAGN,OAAO,CAAC;CACV;AACF;;AAGA,SAAgB,UAAU,OAAsC;CAE9D,OAAO,CAAC;EAAE,IAAI;EAAI,MAAM;CAAO,GAAG,GAAG,CADtB,mBAAG,IAAI,IAAI,CAAC,GAAG,OAAO,GAAG,cAAc,CAAC,CACd,CAAC,CAAC,KAAK,QAAQ;EAAE;EAAI,MAAM,GAAG,QAAQ,MAAM,GAAG;CAAE,EAAE,CAAC;AAC/F;AAEA,SAAgB,iBAAiB,OAA6C;CAC5E,OAAO;EAEL,YAAY;EACZ,SAAS,aAAa,EAAE,OAAO,UAAU,MAAM,CAAC,EAAE;CACpD;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACoBA,MAAM,eAAe;AAOrB,SAAS,KAAK,QAAgB,MAAe;CAC3C,OAAO,eAAe;EACpB;EACA,SAAS,EAAE,gBAAgB,mBAAmB;EAC9C,MAAM;GAAE,MAAM;GAAQ,OAAO,KAAK,UAAU,IAAI;EAAE;CACpD,CAAC;AACH;;;;AAKA,eAAe,aACb,KACA,IAC6B;CAC7B,MAAM,SAAS,MAAM,IAAI,SAAS,IAAiB,YAAY;CAC/D,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,gBAAgB,OAAO,MAAM;CACnC,IACE,iBAAiB,QACjB,KAAK,UAAU,WAAW,GAAG,QAAQ,aAAa,CAAC,MAAM,KAAK,UAAU,GAAG,MAAM,GACjF;EACA,MAAM,IAAI,SAAS,OAAO,YAAY;EACtC,OAAO;CACT;CACA,OAAO;AACT;AAEA,SAAgB,wBAAwB,MAA8C;CACpF,MAAM,QAAQ,KAAK,SAAS;CAE5B,OAAO;EACL,SAAS,CAAC,OAAO,MAAM;EACvB,UAAU;EACV,YAAY;EACZ,SAAS,OAAO,QAAsB;GAIpC,MAAM,EAAE,cAAc,IAAI,WAAW,aAAa,KAAK,QAAQ;IAC7D;IACA,SAAS;GACX,CAAC;GAED,IAAI,IAAI,QAAQ,WAAW,OAAO;IAChC,MAAM,aAAa,KAAK,EAAE;IAC1B,OAAO,KAAK,KAAK,EAAE,UAAU,KAAK,CAAC;GACrC;GAGA,MAAM,QADO,IAAI,OACG;GACpB,IAAI,SAAS,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GACnE,OAAO,KAAK,KAAK,EAAE,QAAQ,CAAC,gCAAgC,EAAE,CAAC;GAEjE,MAAM,eAAe,WACnB,GAAG,QACF,MAAkC,aAAa,CAAC,CACnD;GACA,MAAM,iBAAiB,sBACrB,YACF;GACA,IAAI,eAAe,QAAQ,OAAO,KAAK,KAAK,EAAE,QAAQ,eAAe,CAAC;GAEtE,MAAM,IAAI,SAAS,IAAI,cAAc;IAC5B;IACP,IAAI,KAAK,IAAI;GACf,CAAuB;GAEvB,MAAM,WAAqB,CAAC;GAC5B,MAAM,WAAW,QAAQ,KAAK,YAAY;GAC1C,MAAM,QAAQ,QAAQ,IAAI;GAC1B,IAAI,CAAC,YAAY,CAAC,OAChB,SAAS,KACP,CAAC,WACG,mHACA,2GACN;QACK;IACL,MAAM,CAAC,OAAO,QAAQ,SAAS,MAAM,GAAG;IACxC,IAAI,CAAC,SAAS,CAAC,MACb,SAAS,KACP,qDAAqD,SAAS,sBAChE;SACK,IAAI,CAAC,SAAS,KAAK,MAAM,GAC9B,SAAS,KAAK,qEAAqE;SAC9E;KACL,MAAM,KAAK,iBAAiB,KAAK,QAAQ,KAAK;KAC9C,MAAM,gBAAgB,WAAW,MAAM,KAAK,QAAQ,EAAE,GAAG,KAAK;KAC9D,MAAM,UAAU,GAAG,aAAa,MAAM,KAAK,QAAQ,IAAI,aAAa,CAAC,EAAE;KACvE,MAAM,SAAS,MAAM,WAAW;MAC9B;MACA;MACA,QAAQ,OAAO,KAAK,YAAY,UAAU;MAC1C,MAAM;MACN;MACA,SAAS;MACT;MACA,WACE,IAAI,UAAU,KAAa,SAAuB,IAAI,KAAM,MAAM,KAAK,IAAI;KAC/E,CAAC;KACD,IAAI,CAAC,OAAO,IAAI;MACd,IAAI,IAAI,KAAK,oCAAoC,EAAE,SAAS,OAAO,QAAQ,CAAC;MAC5E,SAAS,KAAK,qDAAqD,OAAO,SAAS;KACrF;IACF;GACF;GAEA,OAAO,KAAK,KAAK;IAAE,IAAI;IAAM;GAAS,CAAC;EACzC;CACF;AACF;;;;;;;AAQA,eAAsB,6BACpB,KACA,MAC0C;CAC1C,MAAM,EAAE,cAAc,OAAO,aAAa,KAAK,QAAQ,EAAE,OAAO,KAAK,SAAS,UAAU,CAAC;CACzF,MAAM,SAAS,MAAM,aAAa,KAAK,EAAE;CACzC,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,MAAM,mBAAmB,OAAO,OAAO,kBAAkB,EAAE,CAAC;CAClE,IAAI,CAAC,KAAK,OAAO;CACjB,OAAO;EACL,MAAM;EACN,WAAW;EACX,MAAM,4BAA4B,IAAI;EACtC,KAAK;CACP;AACF;;;;AC1LA,MAAa,eAAe;AAE5B,MAAM,UACJ,MACA,OACA,SACA,kBAC0B;CAC1B;CACA;CACA,MAAM;CACN;CACA,YAAY,EAAE,QAAQ;AACxB;AAEA,MAAa,iBAAyC;CACpD;EAAE,MAAM;EAAU,OAAO;EAAyC,MAAM;CAAS;CACjF;EAAE,MAAM;EAAW,OAAO;EAAW,MAAM;CAAS;CACpD,OAAO,SAAS,iBAAiB;EAAC;EAAK;EAAK;EAAK;EAAK;CAAG,GAAG,GAAG;CAC/D;EAAE,MAAM;EAAO,OAAO;EAAkC,MAAM;CAAO;CACrE;EAAE,MAAM;EAAQ,OAAO;EAAwB,MAAM;CAAe;CACpE,OAAO,UAAU,UAAU;EAAC;EAAS;EAAU;CAAO,GAAG,OAAO;CAChE,OAAO,SAAS,SAAS;EAAC;EAAW;EAAY;EAAa;CAAW,GAAG,SAAS;CACrF;EACE,MAAM;EACN,OAAO;EACP,MAAM;CACR;CACA;EACE,MAAM;EACN,OAAO;EACP,MAAM;CACR;CACA;EAAE,MAAM;EAAS,OAAO;EAA8B,MAAM;EAAW,cAAc;CAAM;AAC7F;;AAGA,MAAa,qBAA8C;CACzD,MAAM;CACN,OAAO;CACP,aAAa;CACb,UAAU;CACV,MAAM;CACN,gBAAgB;CAChB,UAAU,CAAC;EAAE,SAAS;EAAG,QAAQ;CAAe,CAAC;AACnD;;;;;;AAOA,SAAgB,mBAA8C;CAC5D,OAAO,CAAC,kBAAkB;AAC5B;;;AC1EA,MAAM,QAAQ,OAA2B,KAAK,GAAE,CAAE,QAAQ,OAAO,KAAK,CAAC,CAAC,QAAQ,OAAO,GAAG;AAE1F,SAAS,SAAS,GAA6B;CAC7C,MAAM,OAAiB,CAAC;CACxB,IAAI,aAAa,KAAK,MAAM,QAAQ,EAAE,OAAO,KAAK,EAAE,QAAQ,QAAQ;EAClE,MAAM,SAAS,EAAE,QAAQ,KAAK,MAAM,OAAQ,EAAyB,KAAK,CAAC;EAC3E,KAAK,KACH,OAAO,SAAS,IACZ,UAAU,OAAO,OAAO,gBAAgB,OAAO,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,KACtE,UAAU,OAAO,KAAK,IAAI,GAChC;CACF;CACA,IAAI,mBAAmB,KAAK,EAAE,kBAAkB,QAC9C,KAAK,KAAK,aAAa,OAAO,EAAE,aAAa,EAAE,GAAG;CAEpD,IAAI,EAAE,SAAS,cAAc,eAAe,KAAK,EAAE,WACjD,KAAK,KAAK,OAAO,EAAE,UAAU,MAAM;CACrC,OAAO,KAAK,KAAK,IAAI;AACvB;AAEA,SAAS,UAAU,QAA4B,SAAS,IAAc;CACpE,OAAO,OAAO,SAAS,MAAM;EAC3B,MAAM,MAAM,OAAO,SAAS,EAAE,UAAU,OAAO,EAAE,KAAK,KAAK,KAAM,EAAyB,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,EAAE;EACxH,OAAO,EAAE,SAAS,aAAa,CAAC,KAAK,GAAG,UAAU,EAAE,QAAQ,GAAG,EAAE,UAAU,IAAI,CAAC,IAAI,CAAC,GAAG;CAC1F,CAAC;AACH;;;;;;AAOA,SAAgB,gBAAgB,SAA0C,QAAgB;CACxF,MAAM,MAAM;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,KAAK,MAAM,KAAK,QAAQ;EACtB,IAAI,KAAK,QAAQ,EAAE,KAAK,OAAO,EAAE,SAAS,EAAE;EAC5C,IAAI,EAAE,aAAa,IAAI,KAAK,GAAG,EAAE,YAAY,IAAI,EAAE;EACnD,IAAI,EAAE,QAAQ,QACZ,IAAI,KACF,oCACA,6BACA,GAAG,UAAU,EAAE,MAAM,GACrB,EACF;CAEJ;CACA,IAAI,KACF,sCACA,IACA,sGACA,uGACA,qGACA,gGACA,wGACA,kFACA,6FACA,gGACA,2FACA,yFACA,IACA,uCACA,IACA,qHACA,4HACA,EACF;CACA,KAAK,MAAM,KAAK,iBAAiB,GAAG;EAClC,IAAI,KAAK,SAAS,EAAE,KAAK,OAAO,EAAE,SAAS,IAAI,GAAG,EAAE,eAAe,MAAM,EAAE;EAC3E,MAAM,SAAS,EAAE,SAAS,MAAM,MAAM,EAAE,YAAY,EAAE,cAAc,CAAC,EAAE,UAAU,CAAC;EAClF,IAAI,KACF,oCACA,6BACA,GAAG,OAAO,KAAK,MAAM;GACnB,MAAM,QAAQ;IACZ,EAAE,YAAY,UAAU,UAAU,EAAE,WAAW,QAAQ,KAAK,IAAI,MAAM;IACtE,EAAE,iBAAiB,SAAY,aAAa,OAAO,EAAE,YAAY,EAAE,MAAM;IACzE,EAAE,SAAS,iBAAiB,yCAAyC;GACvE,CAAC,CAAC,OAAO,OAAO;GAChB,OAAO,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,KAAK,EAAE,KAAK,EAAE,KAAK,KAAK,MAAM,KAAK,IAAI,CAAC,EAAE;EACpF,CAAC,GACD,EACF;CACF;CACA,OAAO,IAAI,KAAK,IAAI;AACtB;;;AC5EA,MAAM,UAA2B,CAAC,cAAc,MAAM;;;;;;AA8CtD,eAAe,UAAU,QAAuB,WAAmB,SAAiB;CAClF,IAAI;EACF,OAAO,MAAM;;GAA0B;;CACzC,SAAS,OAAO;EACd,MAAM,IAAI,MACR,4BAA4B,OAAO,UAAU,UAAU,wCACtC,OAAO,iBAAiB,WACzC,EAAE,MAAM,CACV;CACF;AACF;AAEA,eAAsB,cAAc,UAAgC,CAAC,GAA2B;CAC9F,MAAM,SAAU,QAAQ,IAAI,WAA6B,QAAQ,UAAU;CAE3E,IAAI,WAAW,cAAc;EAC3B,MAAM,UAAU;EAChB,MAAM,KAAK,QAAQ,cAAc,CAAC;EAClC,MAAM,EAAE,SAAS,eAAe,MAAM,UAAU,QAAQ,uBAAuB,OAAO;EACtF,MAAM,EAAE,IAAI,OAAO,MAAM,UAAU,QAAQ,0BAA0B,OAAO;EAC5E,OAAO;GACL;GACA,SAAS,WAAW;GACpB,UAAU,GAAG;IACX,SAAS,GAAG,aAAa;IACzB,SAAS,GAAG,WAAW;IACvB,UAAU,GAAG,YAAY;GAC3B,CAAC;GACD,SAAS,GAAG,EAAE,SAAS,GAAG,gBAAgB,QAAQ,CAAC;EACrD;CACF;CAEA,IAAI,WAAW,QAAQ;EACrB,MAAM,UAAU;EAChB,MAAM,OAAO,QAAQ,QAAQ,CAAC;EAC9B,MAAM,EAAE,SAAS,SAAS,MAAM,UAAU,QAAQ,iBAAiB,OAAO;EAK1E,MAAM,EAAE,WAAW,MAAM,UAAU,QAAQ,aAAa,OAAO;EAC/D,MAAM,EAAE,UAAU,MAAM,UAAU,QAAQ,gBAAgB,OAAO;EACjE,MAAM,EAAE,cAAc,MAAM,OAAO;EACnC,MAAM,EAAE,YAAY,MAAM,OAAO;EAEjC,MAAM,cAAc,KAAK,eAAe,QAAQ,IAAI,iBAAiB;EACrE,MAAM,aAAa,KAAK,cAAc;EAItC,IAAI,CAAC,KAAK,YAAY,YAAY,WAAW,OAAO,GAClD,UAAU,QAAQ,YAAY,MAAM,CAAc,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;EAE3E,IAAI,CAAC,KAAK,SAAS,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;EAE5D,OAAO;GACL;GACA,SAAS,KAAK,EAAE,MAAM,aAAa,CAAC;GACpC,UAAU,KAAK,YAAY,OAAO,EAAE,KAAK,YAAY,CAAC;GACtD,SAAS,KAAK,WAAW,MAAM;IAAE,WAAW;IAAY,SAAS;GAA0B,CAAC;EAC9F;CACF;CAEA,MAAM,IAAI,MACR,oCAAoC,OAAO,UAAU,QAAQ,IAAI,UAAU,oBAAoB,iBAAiB,oBAC5F,QAAQ,KAAK,IAAI,EAAE,EACzC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AEtFA,MAAM,UAAUA;AAChB,MAAM,YAAY;AAClB,MAAM,uBAAyC;;AAE/C,MAAM,qBAAuC;;;;;AA4C7C,SAAgB,aAAa,UAA+B,CAAC,GAAqB;CAChF,OAAO;EACL,IAAI;EACJ,SAAS;EACT,QAAQ;EACR,YAAY;EACZ,iBAAiB;EACjB,GAAI,QAAQ,qBAAqB,EAAE,YAAY,0BAA0B,IAAI,CAAC;EAC9E;EACA,cAAc,CACZ,GAAI,QAAQ,YAAY,eAAe,QAAQ,qBAC3C,CAAC,oBAAoB,IACrB,CAAC,GACL,GAAI,QAAQ,qBAAqB,CAAC,kBAAkB,IAAI,CAAC,CAC3D;CACF;AACF;;AAGA,SAAgB,aAAa,UAA+B,CAAC,GAAG;CAC9D,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,SAAS,QAAQ,sBAAsB;CAC7C,MAAM,mBAAmB,YAAY,eAAe,CAAC,CAAC;CAGtD,MAAM,QAAQ,QAAQ,SAAS,QAAQ,QAAQ,MAAM;CAMrD,MAAM,gBAAgB,OACpB,QACA,QACwC;EACxC,MAAM,gBAA4C,CAAC;EACnD,IAAI,YAAY,aACd,cAAc,KACZ;GACE,MAAM;GACN,WAAW;GACX,MACE,mCAAmC,OAAO,4CACZ,OAAO,sDACP,OAAO;GACvC,KAAK;EACP,GACA;GACE,MAAM;GACN,WAAW;GACX,KAAK,GAAG,OAAO;GACf,OAAO;GACP,KAAK;EACP,CACF;EAEF,IAAI,QAAQ;GACV,MAAM,WAAW,MAAM,6BAA6B,KAAK,MAAM;GAC/D,IAAI,UAAU,cAAc,KAAK,QAAQ;EAC3C;EACA,OAAO;CACT;CAEA,OAAO,aAAa;EAClB,IAAI;EACJ,SAAS;EACT,cAAc,CACZ,GAAI,mBAAmB,CAAC,oBAAoB,IAAI,CAAC,GACjD,GAAI,SAAS,CAAC,kBAAkB,IAAI,CAAC,CACvC;EACA,GAAI,SAAS,EAAE,cAAc,CAAC,gBAAgB,EAAE,IAAI,CAAC;EACrD,GAAI,mBAAmB,EAAE,OAAO,EAAE,kBAAkB,cAAc,EAAE,IAAI,CAAC;EAEzE,OAAO;GACL,oBAAoB,aAAa,QAAQ,MAAM;GAC/C,GAAI,SACA;IACE,OAAO;IACP,OAAO,CAAC;KAAE,MAAM;KAAkB,OAAO;KAAiB,MAAM;IAAU,CAAC;GAC7E,IACA,CAAC;EACP;EAEA,QAAQ;IACL,cAAc,uBAAuB,KAAK;IAC1C,cAAc,uBAAuB,SAAS,CAAC;GAChD,GAAI,SAAS,EAAE,iBAAiB,wBAAwB,MAAM,EAAE,IAAI,CAAC;EACvE;CACF,CAAC;AACH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getvitops/emdash",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-alpha.0",
|
|
4
4
|
"description": "EmDash CMS native plugin for the Vitops design system — Portable Text block types editors can insert from the admin, rendered with the design-system web components and CSS framework.",
|
|
5
5
|
"homepage": "https://docs.vitops.ca",
|
|
6
6
|
"bugs": "https://github.com/getvitops/design-system/issues",
|
|
@@ -31,15 +31,15 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@getvitops/generator": "8.
|
|
35
|
-
"@getvitops/utils": "8.
|
|
34
|
+
"@getvitops/generator": "8.3.0-alpha.0",
|
|
35
|
+
"@getvitops/utils": "8.3.0-alpha.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@astrojs/cloudflare": "^14.0.0",
|
|
39
39
|
"@astrojs/react": "^7.0.0",
|
|
40
40
|
"@emdash-cms/cloudflare": "^1.4.0",
|
|
41
|
-
"@getvitops/astro": "8.
|
|
42
|
-
"@getvitops/core": "8.
|
|
41
|
+
"@getvitops/astro": "8.3.0-alpha.0",
|
|
42
|
+
"@getvitops/core": "8.3.0-alpha.0",
|
|
43
43
|
"@types/react": "^19.3.0",
|
|
44
44
|
"@types/react-dom": "^19.3.0",
|
|
45
45
|
"astro": "^7.4.4",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"@astrojs/cloudflare": ">=14",
|
|
52
52
|
"@astrojs/react": ">=7",
|
|
53
53
|
"@emdash-cms/cloudflare": ">=1.3.0 <2",
|
|
54
|
-
"@getvitops/astro": ">=8.
|
|
55
|
-
"@getvitops/core": ">=8.
|
|
54
|
+
"@getvitops/astro": ">=8.3.0-alpha.0",
|
|
55
|
+
"@getvitops/core": ">=8.3.0-alpha.0",
|
|
56
56
|
"astro": ">=7",
|
|
57
57
|
"emdash": ">=1.3.0 <2",
|
|
58
58
|
"react": ">=19",
|
|
@@ -80,5 +80,8 @@
|
|
|
80
80
|
},
|
|
81
81
|
"engines": {
|
|
82
82
|
"node": ">=24"
|
|
83
|
+
},
|
|
84
|
+
"scripts": {
|
|
85
|
+
"test:render": "vitest --config vitest.container.config.ts run"
|
|
83
86
|
}
|
|
84
87
|
}
|
|
@@ -1,51 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `/design-system` admin page —
|
|
3
|
-
* component, `@getvitops/core/editor`)
|
|
4
|
-
*
|
|
5
|
-
* `../design-system-route.ts` and AGENTS.md's
|
|
6
|
-
* design-system editing surface" section).
|
|
2
|
+
* The `/design-system` admin page — embeds `<wc-theme-editor>` (a Lit web
|
|
3
|
+
* component, `@getvitops/core/editor`) in an iframe onto the site's own
|
|
4
|
+
* `/vitops/design-system-editor.html`, which the Astro integration writes under
|
|
5
|
+
* `vitops({ editorManifest: true })` (see `../design-system-route.ts` and AGENTS.md's
|
|
6
|
+
* "EmDash plugin as the design-system editing surface" section).
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* editor', props)` needs no such augmentation and works identically.
|
|
13
|
-
*
|
|
14
|
-
* The editor itself needs no rework: it already reads the `manifest`
|
|
15
|
-
* attribute and (as of this release) a `save-endpoint` attribute for where
|
|
16
|
-
* to probe/POST — this page only has to supply both and load the module
|
|
17
|
-
* that registers the custom element.
|
|
8
|
+
* An iframe, not a mounted element, because the editor renders in light DOM and
|
|
9
|
+
* borrows the framework's own CSS (drawer, details, forms, buttons). Loading that
|
|
10
|
+
* into the admin restyles EmDash's own UI; scoped to a frame it can't reach it, and
|
|
11
|
+
* the admin's styles can't reach the editor. The save endpoint is passed as `?save=`.
|
|
18
12
|
*/
|
|
19
13
|
import * as React from 'react';
|
|
20
14
|
|
|
21
|
-
const
|
|
15
|
+
const EDITOR_URL = '/vitops/design-system-editor.html';
|
|
22
16
|
const SAVE_ENDPOINT = '/_emdash/api/plugins/vitops/design-system';
|
|
23
17
|
|
|
24
18
|
export function DesignSystemEditorPage(): React.ReactElement {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// on load. Dynamic, and guarded against a fast unmount, since this runs
|
|
31
|
-
// in a browser-only admin page with no SSR concern.
|
|
32
|
-
void import('@getvitops/core/editor').then(() => {
|
|
33
|
-
if (!cancelled) setReady(true);
|
|
34
|
-
});
|
|
35
|
-
return () => {
|
|
36
|
-
cancelled = true;
|
|
37
|
-
};
|
|
38
|
-
}, []);
|
|
39
|
-
|
|
40
|
-
return React.createElement(
|
|
41
|
-
'section',
|
|
42
|
-
{ className: 'space-y-4' },
|
|
43
|
-
React.createElement('h1', { className: 'text-2xl font-semibold' }, 'Design System'),
|
|
44
|
-
ready
|
|
45
|
-
? React.createElement('wc-theme-editor', {
|
|
46
|
-
manifest: MANIFEST_URL,
|
|
47
|
-
'save-endpoint': SAVE_ENDPOINT,
|
|
48
|
-
})
|
|
49
|
-
: React.createElement('p', null, 'Loading editor…'),
|
|
50
|
-
);
|
|
19
|
+
return React.createElement('iframe', {
|
|
20
|
+
title: 'Design system editor',
|
|
21
|
+
src: `${EDITOR_URL}?save=${encodeURIComponent(SAVE_ENDPOINT)}`,
|
|
22
|
+
style: { width: '100%', height: 'calc(100vh - 8rem)', border: 0 },
|
|
23
|
+
});
|
|
51
24
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* A call-to-action band on the framework's `.callout` pattern. Not `vitops.banner`: that is
|
|
4
|
+
* a dismissible announcement, this is content that stays. The heading is a real heading at
|
|
5
|
+
* the editor's `level`; colour is the shared tone convention, tinted by default and filled
|
|
6
|
+
* with the role's solid when `solid` is on.
|
|
7
|
+
*/
|
|
8
|
+
import RichText from './RichText.astro';
|
|
9
|
+
import { resolveTone, joinStyle } from './tone.ts';
|
|
10
|
+
import { headingTag, resolveBlockValue, safeHref, text } from './value.ts';
|
|
11
|
+
|
|
12
|
+
interface Value {
|
|
13
|
+
heading?: string;
|
|
14
|
+
level?: string | number;
|
|
15
|
+
body?: string;
|
|
16
|
+
primaryLabel?: string;
|
|
17
|
+
primaryHref?: string;
|
|
18
|
+
secondaryLabel?: string;
|
|
19
|
+
secondaryHref?: string;
|
|
20
|
+
align?: string;
|
|
21
|
+
solid?: boolean;
|
|
22
|
+
tone?: string;
|
|
23
|
+
color?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const value = resolveBlockValue<Value>(Astro.props);
|
|
27
|
+
const heading = text(value.heading);
|
|
28
|
+
const body = text(value.body);
|
|
29
|
+
const primaryLabel = text(value.primaryLabel);
|
|
30
|
+
const primaryHref = safeHref(value.primaryHref);
|
|
31
|
+
const secondaryLabel = text(value.secondaryLabel);
|
|
32
|
+
const secondaryHref = safeHref(value.secondaryHref);
|
|
33
|
+
const hasActions = Boolean((primaryLabel && primaryHref) || (secondaryLabel && secondaryHref));
|
|
34
|
+
const Heading = headingTag(value.level, 2);
|
|
35
|
+
const tone = resolveTone(value.tone, value.color);
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
{
|
|
39
|
+
(heading || body) && (
|
|
40
|
+
<div
|
|
41
|
+
class:list={[
|
|
42
|
+
'callout',
|
|
43
|
+
tone.className,
|
|
44
|
+
value.solid && 'tone--solid',
|
|
45
|
+
value.align === 'center' && 'callout--center',
|
|
46
|
+
]}
|
|
47
|
+
style={joinStyle(tone.style)}
|
|
48
|
+
>
|
|
49
|
+
<div class="callout__body">
|
|
50
|
+
{heading && <Heading class="callout__title font-title">{heading}</Heading>}
|
|
51
|
+
{body && (
|
|
52
|
+
<p class="callout__text">
|
|
53
|
+
<RichText text={body} />
|
|
54
|
+
</p>
|
|
55
|
+
)}
|
|
56
|
+
</div>
|
|
57
|
+
{hasActions && (
|
|
58
|
+
<p class="callout__actions cluster">
|
|
59
|
+
{primaryLabel && primaryHref && (
|
|
60
|
+
<a class="cta" href={primaryHref}>
|
|
61
|
+
{primaryLabel}
|
|
62
|
+
</a>
|
|
63
|
+
)}
|
|
64
|
+
{secondaryLabel && secondaryHref && (
|
|
65
|
+
<a class="link" href={secondaryHref}>
|
|
66
|
+
{secondaryLabel}
|
|
67
|
+
</a>
|
|
68
|
+
)}
|
|
69
|
+
</p>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
)
|
|
73
|
+
}
|