@elytracms/components 0.0.92 → 0.0.93

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.
@@ -53,9 +53,11 @@ declare const HeadingManifest: ComponentManifest;
53
53
  /**
54
54
  * The Image `src` may be a resolved asset (EC-195: an `asset`-typed prop the
55
55
  * renderer resolved from an asset id) or a plain URL string. Normalize to
56
- * `{ url, width, height, alt }`, letting the asset's intrinsic dimensions/alt
57
- * fill in when the explicit props are unset — so an uploaded image reserves the
58
- * right box (no layout shift) and carries its alt text by default.
56
+ * `{ url, width, height, alt }`, letting the asset's intrinsic dimensions fill in
57
+ * when the explicit props are unset — so an uploaded image reserves the right box
58
+ * (no layout shift). The alt is the ASSET's for an asset source (EC-587, EC-569
59
+ * reader 3: one alt per asset, read one way on every surface); the `alt` prop is the
60
+ * alt of an external image (a literal URL `src`) only.
59
61
  */
60
62
  export interface ImageSource {
61
63
  url: string;
@@ -260,7 +260,7 @@ export function normalizeImageSource(props) {
260
260
  const url = asset ? asString(asset.url) : asString(src);
261
261
  const width = asPositiveInt(props.width) ?? (asset ? asPositiveInt(asset.width) : undefined);
262
262
  const height = asPositiveInt(props.height) ?? (asset ? asPositiveInt(asset.height) : undefined);
263
- const alt = asString(props.alt) || (asset ? asString(asset.alt) : '');
263
+ const alt = asset ? asString(asset.alt) : asString(props.alt);
264
264
  const focalPoint = asset ? asFocalPoint(asset.focalPoint) : null;
265
265
  // EC-420: a resolved asset may carry an LQIP data URI → next/image blurDataURL.
266
266
  const lqip = asset && typeof asset.lqip === 'string' && asset.lqip.length > 0 ? asset.lqip : null;
@@ -280,6 +280,14 @@ function Image(props) {
280
280
  }
281
281
  return (_jsx("img", { "data-ec-primitive": "Image", ...ecProp('src'), src: url, alt: alt, ...(width !== undefined ? { width } : {}), ...(height !== undefined ? { height } : {}), ...(objectPosition ? { style: { objectPosition } } : {}) }));
282
282
  }
283
+ /**
284
+ * Is a stored `src` an asset envelope (`{ type: 'asset', id }`, EC-277)? Structural:
285
+ * the components package may not import cms-core (EC-591's render-surface rule), and
286
+ * the self-describing `type` discriminator is exactly what makes this readable without it.
287
+ */
288
+ function holdsAssetEnvelope(value) {
289
+ return typeof value === 'object' && value !== null && value.type === 'asset';
290
+ }
283
291
  const ImageManifest = defineComponent({
284
292
  id: 'Image',
285
293
  // EC-591: the image block (asset drop; the Next host's `next/image` swap).
@@ -299,7 +307,14 @@ const ImageManifest = defineComponent({
299
307
  alt: {
300
308
  type: 'text',
301
309
  default: '',
302
- form: { label: 'Alt text' },
310
+ form: {
311
+ label: 'Alt text',
312
+ // EC-587: an uploaded image's alt is the asset's (edited on the asset, shared by
313
+ // every use); this prop is the alt of an external image (a URL `src`) only, so
314
+ // it is not offered — and is cleared on publish — while `src` is an asset.
315
+ description: 'For an image by URL. An uploaded image uses the alt text of its asset.',
316
+ hidden: ({ values }) => holdsAssetEnvelope(values.src),
317
+ },
303
318
  },
304
319
  width: {
305
320
  type: 'number',
@@ -1 +1 @@
1
- {"version":3,"file":"primitives.js","sourceRoot":"","sources":["../src/primitives.tsx"],"names":[],"mappings":"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","sourcesContent":["import { createElement } from 'react'\nimport type { ComponentType, CSSProperties, ReactNode } from 'react'\nimport {\n CONTAINER_ALIGNMENTS,\n GRID_MAX_COLUMNS,\n GRID_MIN_COLUMNS,\n SPACING_TOKEN_NAMES,\n spacingTokenCss,\n} from '@elytracms/core/project-graph'\nimport type { ContainerAlignment } from '@elytracms/core/project-graph'\nimport { defineComponent } from '@elytracms/core/component-registry'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { ecProp } from '@elytracms/core/runtime-renderer'\nimport { RichText, RichTextManifest } from './rich-text'\n\n/**\n * The components Elytra ships (EC-591) — moved verbatim from\n * `@elytracms/core/runtime-renderer`'s primitives, where the engine registered\n * them itself under `base.*` ids. Now nothing registers them: they are\n * `{ manifest, implementation }` entries a config opts into by listing them\n * (`components: [...elytraComponents, ...siteComponents]`), a subset, or none.\n * Plain ids, the developer's to keep or replace; the five canvas roles are\n * declared on the manifests that fill them (`Stack` layout.stack, `Grid`\n * layout.grid, `Text` text, `RichText` richText, `Image` image).\n */\n\n/**\n * Common shape for primitive implementations: resolved props (arbitrary keys) plus React\n * children for the default slot and any named-slot props.\n */\ntype PrimitiveProps = Record<string, unknown> & { children?: ReactNode }\n\nfunction asString(value: unknown, fallback = ''): string {\n return typeof value === 'string' ? value : fallback\n}\n\n// ---------------------------------------------------------------------------\n// Layout-container styling (EC-161) — gap/alignment resolve from the canonical\n// design-token spacing scale; legacy numeric gaps (`n` → `n * 4px`) stay\n// renderable so pre-token graphs keep their layout.\n// ---------------------------------------------------------------------------\n\nfunction gapCss(value: unknown): string | undefined {\n if (typeof value === 'string') return spacingTokenCss(value)\n if (typeof value === 'number' && Number.isFinite(value) && value > 0) return `${value * 4}px`\n return undefined\n}\n\nconst ALIGN_ITEMS: Record<ContainerAlignment, CSSProperties['alignItems']> = {\n start: 'flex-start',\n center: 'center',\n end: 'flex-end',\n stretch: 'stretch',\n}\n\n/** `alignItems` for a container `align` prop; undefined for `stretch` (the CSS default) or junk. */\nfunction alignItemsCss(value: unknown): CSSProperties['alignItems'] {\n if (value === 'start' || value === 'center' || value === 'end') return ALIGN_ITEMS[value]\n return undefined\n}\n\nconst SPACING_OPTIONS = SPACING_TOKEN_NAMES.map((name) => ({ label: name, value: name }))\nconst ALIGN_OPTIONS = CONTAINER_ALIGNMENTS.map((name) => ({ label: name, value: name }))\n\n// ---------------------------------------------------------------------------\n// Container — a generic block wrapper with a single default slot.\n// ---------------------------------------------------------------------------\n\nfunction Container(props: PrimitiveProps): ReactNode {\n // The default `children` slot and the layout `outlet` slot both render here; the outlet\n // is where `renderCompositionInLayout` injects the page body when this Container is a layout root.\n return (\n <div data-ec-primitive=\"Container\">\n {props.children}\n {props.outlet as ReactNode}\n </div>\n )\n}\n\nconst ContainerManifest = defineComponent({\n id: 'Container',\n title: 'Container',\n category: 'layout',\n props: {},\n // The outlet is filled by page injection, so it is not authored-required.\n slots: [{ name: 'children' }, { name: 'outlet' }],\n})\n\n// ---------------------------------------------------------------------------\n// Stack — flex layout container (EC-161): direction, token-bound gap, alignment.\n// ---------------------------------------------------------------------------\n\nfunction Stack(props: PrimitiveProps): ReactNode {\n const style: CSSProperties = {\n display: 'flex',\n flexDirection: props.direction === 'horizontal' ? 'row' : 'column',\n }\n const gap = gapCss(props.gap)\n if (gap !== undefined) style.gap = gap\n const alignItems = alignItemsCss(props.align)\n if (alignItems !== undefined) style.alignItems = alignItems\n return (\n <div data-ec-primitive=\"Stack\" style={style}>\n {props.children}\n </div>\n )\n}\n\nconst StackManifest = defineComponent({\n id: 'Stack',\n // EC-591: implicit layout's flow container and the canvas's synthetic root.\n role: 'layout.stack',\n title: 'Stack',\n category: 'layout',\n props: {\n direction: {\n type: 'select',\n default: 'vertical',\n options: [\n { value: 'vertical', label: 'Vertical' },\n { value: 'horizontal', label: 'Horizontal' },\n ],\n form: { label: 'Direction' },\n },\n gap: {\n // A design-token spacing step name (EC-161); legacy numeric gaps still render.\n type: 'select',\n default: '4',\n options: SPACING_OPTIONS,\n form: { label: 'Gap' },\n },\n align: {\n type: 'select',\n default: 'stretch',\n options: ALIGN_OPTIONS,\n form: { label: 'Align' },\n },\n },\n slots: [{ name: 'children' }],\n})\n\n// ---------------------------------------------------------------------------\n// Grid — CSS grid container (EC-161): column count, token-bound gap, alignment.\n// ---------------------------------------------------------------------------\n\nfunction asColumns(value: unknown): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return GRID_MIN_COLUMNS\n return Math.min(Math.max(GRID_MIN_COLUMNS, Math.floor(value)), GRID_MAX_COLUMNS)\n}\n\n/**\n * Responsive defaults (EC-163): below this viewport width a Grid's columns\n * stack into one. 640px (the Tailwind `sm` boundary) sits between the\n * builder's deterministic preview widths — phone (390px) stacks, tablet\n * (768px) keeps columns.\n */\nexport const GRID_MOBILE_BREAKPOINT_PX = 640\n\n/**\n * The stack-on-mobile rule a stacking Grid carries with it. A **viewport media\n * query** (not a container query), deliberately: the builder canvas renders\n * into an iframe — a true viewport — so constraining the iframe to the phone\n * width makes this exact rule fire in the preview; and the identical markup\n * server-renders in `@elytracms/next` (App Router SSR) with no client JS, no\n * extra stylesheet wiring, and no `container-type` context that every consumer\n * would otherwise have to guarantee around every grid. The rule is emitted as\n * an inline `<style>` child of the grid itself (style elements generate no\n * grid item), so the behavior is self-contained wherever the implementation\n * renders — canvas iframe portal, static markup, or RSC payload — and the\n * `!important` lets the stylesheet rule beat the grid's own inline column\n * template. Opted-out grids (`stackOnMobile: false`) carry neither the\n * attribute nor the rule.\n */\nexport const GRID_STACK_CSS = `@media (max-width: ${GRID_MOBILE_BREAKPOINT_PX}px){[data-ec-grid-stack]{grid-template-columns:minmax(0,1fr) !important}}`\n\nfunction Grid(props: PrimitiveProps): ReactNode {\n const style: CSSProperties = {\n display: 'grid',\n gridTemplateColumns: `repeat(${asColumns(props.columns)}, minmax(0, 1fr))`,\n }\n const gap = gapCss(props.gap)\n if (gap !== undefined) style.gap = gap\n const alignItems = alignItemsCss(props.align)\n if (alignItems !== undefined) style.alignItems = alignItems\n // Stack-on-mobile is the default; only the explicit opt-out keeps columns.\n const stacksOnMobile = props.stackOnMobile !== false\n return (\n <div\n data-ec-primitive=\"Grid\"\n {...(stacksOnMobile ? { 'data-ec-grid-stack': '' } : {})}\n style={style}\n >\n {stacksOnMobile ? <style>{GRID_STACK_CSS}</style> : null}\n {props.children}\n </div>\n )\n}\n\nconst GridManifest = defineComponent({\n id: 'Grid',\n // EC-591: the column container the \"drop beside\" gesture wraps into.\n role: 'layout.grid',\n title: 'Grid',\n category: 'layout',\n props: {\n columns: {\n type: 'number',\n default: 2,\n validation: { min: GRID_MIN_COLUMNS, max: GRID_MAX_COLUMNS },\n form: { label: 'Columns' },\n },\n gap: {\n type: 'select',\n default: '4',\n options: SPACING_OPTIONS,\n form: { label: 'Gap' },\n },\n align: {\n type: 'select',\n default: 'stretch',\n options: ALIGN_OPTIONS,\n form: { label: 'Align' },\n },\n stackOnMobile: {\n // Responsive default (EC-163): stacking is on unless explicitly opted out.\n type: 'boolean',\n default: true,\n form: { label: 'Stack on phones' },\n },\n },\n slots: [{ name: 'children' }],\n})\n\n// ---------------------------------------------------------------------------\n// Text — a paragraph of text driven by the `value` prop.\n// ---------------------------------------------------------------------------\n\nfunction Text(props: PrimitiveProps): ReactNode {\n // EC-408: the paragraph is the `value` prop's clickable element (studio only).\n return (\n <p data-ec-primitive=\"Text\" {...ecProp('value')}>\n {asString(props.value)}\n </p>\n )\n}\n\nconst TextManifest = defineComponent({\n id: 'Text',\n // EC-591: the block a seeded insert (layout presets) fills with copy (`value`).\n role: 'text',\n title: 'Text',\n category: 'content',\n props: {\n value: {\n type: 'text',\n default: '',\n // Plain-text content prop, editable in place on the canvas (EC-158).\n form: { label: 'Text', control: 'textarea', inlineEditable: true },\n },\n },\n slots: [],\n})\n\n// ---------------------------------------------------------------------------\n// Heading — h1..h6 heading driven by `text` + `level`.\n// ---------------------------------------------------------------------------\n\nfunction Heading(props: PrimitiveProps): ReactNode {\n const level =\n typeof props.level === 'number' && props.level >= 1 && props.level <= 6 ? props.level : 2\n const tag = `h${level}`\n // EC-408: the heading element is the `text` prop's clickable element.\n return createElement(tag, { 'data-ec-primitive': 'Heading', ...ecProp('text') }, asString(props.text))\n}\n\nconst HeadingManifest = defineComponent({\n id: 'Heading',\n title: 'Heading',\n category: 'content',\n props: {\n text: {\n type: 'text',\n default: '',\n // Plain-text content prop, editable in place on the canvas (EC-158).\n form: { label: 'Text', inlineEditable: true },\n },\n level: {\n // A numeric heading level 1–6 (the impl clamps out-of-range to 2).\n type: 'number',\n default: 2,\n validation: { min: 1, max: 6 },\n form: { label: 'Level' },\n },\n },\n slots: [],\n})\n\n// ---------------------------------------------------------------------------\n// Image — an <img> with src/alt plus intrinsic dimensions (EC-152). Width and\n// height come from the asset metadata record (bindable to the delivery\n// shape's `width`/`height`) so rendered images reserve layout space — no\n// layout shift. A plain <img> is correct for non-Next contexts (the builder\n// canvas runs on Vite); a Next host opts into `withNextImage` from\n// `@elytracms/next`, which swaps this implementation for `ElytraImage`\n// (next/image) by the `image` role, keeping this manifest.\n// ---------------------------------------------------------------------------\n\nfunction asPositiveInt(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : undefined\n}\n\n/**\n * The Image `src` may be a resolved asset (EC-195: an `asset`-typed prop the\n * renderer resolved from an asset id) or a plain URL string. Normalize to\n * `{ url, width, height, alt }`, letting the asset's intrinsic dimensions/alt\n * fill in when the explicit props are unset — so an uploaded image reserves the\n * right box (no layout shift) and carries its alt text by default.\n */\nexport interface ImageSource {\n url: string\n width: number | undefined\n height: number | undefined\n alt: string\n /** Normalized focal point `{ x, y }` 0–1 (EC-230) → CSS object-position; null when none. */\n focalPoint: { x: number; y: number } | null\n /** LQIP blur-placeholder data URI (EC-420) → `next/image` blurDataURL; null when none. */\n lqip: string | null\n}\n\n/** Read a resolved asset's `focalPoint` defensively (a `{ x, y }` pair in 0–1). */\nfunction asFocalPoint(value: unknown): { x: number; y: number } | null {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const point = value as { x?: unknown; y?: unknown }\n if (typeof point.x === 'number' && typeof point.y === 'number') {\n return { x: point.x, y: point.y }\n }\n }\n return null\n}\n\nexport function normalizeImageSource(props: PrimitiveProps): ImageSource {\n const src = props.src\n const asset =\n src && typeof src === 'object' && !Array.isArray(src)\n ? (src as {\n url?: unknown\n width?: unknown\n height?: unknown\n alt?: unknown\n focalPoint?: unknown\n lqip?: unknown\n })\n : null\n const url = asset ? asString(asset.url) : asString(src)\n const width = asPositiveInt(props.width) ?? (asset ? asPositiveInt(asset.width) : undefined)\n const height = asPositiveInt(props.height) ?? (asset ? asPositiveInt(asset.height) : undefined)\n const alt = asString(props.alt) || (asset ? asString(asset.alt) : '')\n const focalPoint = asset ? asFocalPoint(asset.focalPoint) : null\n // EC-420: a resolved asset may carry an LQIP data URI → next/image blurDataURL.\n const lqip = asset && typeof asset.lqip === 'string' && asset.lqip.length > 0 ? asset.lqip : null\n return { url, width, height, alt, focalPoint, lqip }\n}\n\n/** EC-230: a focal point becomes a CSS `object-position` string (percent of each axis). */\nexport function focalPointObjectPosition(focalPoint: { x: number; y: number } | null): string | undefined {\n return focalPoint ? `${focalPoint.x * 100}% ${focalPoint.y * 100}%` : undefined\n}\n\nfunction Image(props: PrimitiveProps): ReactNode {\n const { url, width, height, alt, focalPoint } = normalizeImageSource(props)\n const objectPosition = focalPointObjectPosition(focalPoint)\n // No resolved url (no asset picked yet, or an unresolved id): a visible\n // placeholder, never a broken `<img src=\"\">` (EC-195 / EC-015).\n if (url === '') {\n return (\n <span\n data-ec-primitive=\"Image\"\n data-ec-image=\"empty\"\n // EC-408: `src` is the Image's primary/clickable prop (of src/alt/width/…).\n {...ecProp('src')}\n role=\"img\"\n aria-label={alt || 'No image selected'}\n >\n No image selected\n </span>\n )\n }\n return (\n <img\n data-ec-primitive=\"Image\"\n // EC-408: `src` is the Image's primary/clickable prop (of src/alt/width/…).\n {...ecProp('src')}\n src={url}\n alt={alt}\n {...(width !== undefined ? { width } : {})}\n {...(height !== undefined ? { height } : {})}\n {...(objectPosition ? { style: { objectPosition } } : {})}\n />\n )\n}\n\nconst ImageManifest = defineComponent({\n id: 'Image',\n // EC-591: the image block (asset drop; the Next host's `next/image` swap).\n role: 'image',\n title: 'Image',\n category: 'media',\n props: {\n // EC-195: `asset`-typed, so the editor picks an uploaded asset (the renderer\n // resolves the id to a url + intrinsic dimensions at delivery) — a plain URL\n // string still works (the resolver passes URL-like values through).\n src: {\n type: 'asset',\n multiple: false,\n default: '',\n form: { label: 'Image' },\n },\n alt: {\n type: 'text',\n default: '',\n form: { label: 'Alt text' },\n },\n width: {\n type: 'number',\n validation: { min: 1 },\n form: { label: 'Width' },\n },\n height: {\n type: 'number',\n validation: { min: 1 },\n form: { label: 'Height' },\n },\n // EC-195: above-the-fold images load eagerly (`next/image priority`) so the\n // host can opt the hero image out of lazy-loading.\n priority: {\n type: 'boolean',\n default: false,\n form: { label: 'Load eagerly (above the fold)' },\n },\n },\n slots: [],\n})\n\n// ---------------------------------------------------------------------------\n// Button — labelled button with an optional href.\n// ---------------------------------------------------------------------------\n\nfunction Button(props: PrimitiveProps): ReactNode {\n const label = asString(props.label, 'Button')\n if (typeof props.href === 'string' && props.href.length > 0) {\n // EC-408: `label` is the Button's clickable/visible prop (of label/href).\n return (\n <a data-ec-primitive=\"Button\" {...ecProp('label')} href={props.href} role=\"button\">\n {label}\n </a>\n )\n }\n return (\n <button data-ec-primitive=\"Button\" {...ecProp('label')} type=\"button\">\n {label}\n </button>\n )\n}\n\nconst ButtonManifest = defineComponent({\n id: 'Button',\n title: 'Button',\n category: 'content',\n props: {\n label: {\n type: 'text',\n default: 'Button',\n form: { label: 'Label' },\n },\n href: {\n type: 'text',\n form: { label: 'Link' },\n },\n },\n slots: [],\n})\n\n// ---------------------------------------------------------------------------\n// Repeater — renders its `item` slot template once per item (iteration is done by the\n// renderer; this implementation only frames the rendered items).\n// ---------------------------------------------------------------------------\n\nfunction Repeater(props: PrimitiveProps): ReactNode {\n return <div data-ec-primitive=\"Repeater\">{props.children}</div>\n}\n\nconst RepeaterManifest = defineComponent({\n id: 'Repeater',\n title: 'Repeater',\n category: 'data',\n // EC-190 DD7: iterate-over-a-source is a binding construct designed out of v1.\n // The `items` value (an array) has no static field-def type; it rides node.props\n // undeclared (passing through the renderer) until the binding/iteration removal in\n // slice 5 decides Repeater's fate. No editable static prop is exposed meanwhile.\n props: {},\n slots: [{ name: 'item', label: 'Item template', required: true }],\n iterate: { itemsProp: 'items', itemSlot: 'item' },\n})\n\n// ---------------------------------------------------------------------------\n// Switch — renders exactly one named case slot, chosen by the `case` prop/binding.\n// ---------------------------------------------------------------------------\n\nfunction Switch(props: PrimitiveProps): ReactNode {\n const key = asString(props.case)\n const selected = props[`case:${key}`] ?? props.fallback\n return <div data-ec-primitive=\"Switch\">{selected as ReactNode}</div>\n}\n\nconst SwitchManifest = defineComponent({\n id: 'Switch',\n title: 'Switch',\n category: 'data',\n props: {\n case: {\n type: 'text',\n default: '',\n form: { label: 'Case' },\n },\n },\n // Named case slots are dynamic; `fallback` renders when no case matches.\n slots: [{ name: 'fallback', label: 'Fallback' }],\n})\n\n// ---------------------------------------------------------------------------\n// Aggregation\n// ---------------------------------------------------------------------------\n\n/**\n * One shipped component: its manifest plus the real implementation. Strictly\n * typed (the implementation IS a component), and assignable to the config's\n * `HostComponentEntry` and core's `ComponentEntry`, so the entries list straight\n * into `elytra.config.ts`'s `components`.\n */\nexport interface ElytraComponent {\n manifest: ComponentManifest\n implementation: ComponentType<Record<string, unknown>>\n}\n\nfunction entry(manifest: ComponentManifest, implementation: ComponentType<PrimitiveProps>): ElytraComponent {\n return { manifest, implementation: implementation as ComponentType<Record<string, unknown>> }\n}\n\nexport const containerComponent = entry(ContainerManifest, Container)\nexport const stackComponent = entry(StackManifest, Stack)\nexport const gridComponent = entry(GridManifest, Grid)\nexport const textComponent = entry(TextManifest, Text)\nexport const headingComponent = entry(HeadingManifest, Heading)\n// Rich-text content component (EC-159) — content prop is the stored\n// `@elytracms/core/rich-text` value, flagged inline-editable.\nexport const richTextComponent = entry(RichTextManifest, RichText)\nexport const imageComponent = entry(ImageManifest, Image)\nexport const buttonComponent = entry(ButtonManifest, Button)\nexport const repeaterComponent = entry(RepeaterManifest, Repeater)\nexport const switchComponent = entry(SwitchManifest, Switch)\n\n/**\n * Every component Elytra ships, in palette order — the list a config spreads\n * into its `components` (`[...elytraComponents, ...siteComponents]`). Pick a\n * subset by listing the individual entries instead; replace one by listing your\n * own component under the id (and leaving the shipped entry out — the list names\n * each id once, a second entry is a `duplicate-id` issue).\n */\nexport const elytraComponents: readonly ElytraComponent[] = [\n containerComponent,\n stackComponent,\n gridComponent,\n textComponent,\n headingComponent,\n richTextComponent,\n imageComponent,\n buttonComponent,\n repeaterComponent,\n switchComponent,\n]\n\nexport {\n Container,\n Stack,\n Grid,\n Text,\n Heading,\n Image,\n Button,\n Repeater,\n Switch,\n ContainerManifest,\n StackManifest,\n GridManifest,\n TextManifest,\n HeadingManifest,\n ImageManifest,\n ButtonManifest,\n RepeaterManifest,\n SwitchManifest,\n}\n"]}
1
+ {"version":3,"file":"primitives.js","sourceRoot":"","sources":["../src/primitives.tsx"],"names":[],"mappings":"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","sourcesContent":["import { createElement } from 'react'\nimport type { ComponentType, CSSProperties, ReactNode } from 'react'\nimport {\n CONTAINER_ALIGNMENTS,\n GRID_MAX_COLUMNS,\n GRID_MIN_COLUMNS,\n SPACING_TOKEN_NAMES,\n spacingTokenCss,\n} from '@elytracms/core/project-graph'\nimport type { ContainerAlignment } from '@elytracms/core/project-graph'\nimport { defineComponent } from '@elytracms/core/component-registry'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { ecProp } from '@elytracms/core/runtime-renderer'\nimport { RichText, RichTextManifest } from './rich-text'\n\n/**\n * The components Elytra ships (EC-591) — moved verbatim from\n * `@elytracms/core/runtime-renderer`'s primitives, where the engine registered\n * them itself under `base.*` ids. Now nothing registers them: they are\n * `{ manifest, implementation }` entries a config opts into by listing them\n * (`components: [...elytraComponents, ...siteComponents]`), a subset, or none.\n * Plain ids, the developer's to keep or replace; the five canvas roles are\n * declared on the manifests that fill them (`Stack` layout.stack, `Grid`\n * layout.grid, `Text` text, `RichText` richText, `Image` image).\n */\n\n/**\n * Common shape for primitive implementations: resolved props (arbitrary keys) plus React\n * children for the default slot and any named-slot props.\n */\ntype PrimitiveProps = Record<string, unknown> & { children?: ReactNode }\n\nfunction asString(value: unknown, fallback = ''): string {\n return typeof value === 'string' ? value : fallback\n}\n\n// ---------------------------------------------------------------------------\n// Layout-container styling (EC-161) — gap/alignment resolve from the canonical\n// design-token spacing scale; legacy numeric gaps (`n` → `n * 4px`) stay\n// renderable so pre-token graphs keep their layout.\n// ---------------------------------------------------------------------------\n\nfunction gapCss(value: unknown): string | undefined {\n if (typeof value === 'string') return spacingTokenCss(value)\n if (typeof value === 'number' && Number.isFinite(value) && value > 0) return `${value * 4}px`\n return undefined\n}\n\nconst ALIGN_ITEMS: Record<ContainerAlignment, CSSProperties['alignItems']> = {\n start: 'flex-start',\n center: 'center',\n end: 'flex-end',\n stretch: 'stretch',\n}\n\n/** `alignItems` for a container `align` prop; undefined for `stretch` (the CSS default) or junk. */\nfunction alignItemsCss(value: unknown): CSSProperties['alignItems'] {\n if (value === 'start' || value === 'center' || value === 'end') return ALIGN_ITEMS[value]\n return undefined\n}\n\nconst SPACING_OPTIONS = SPACING_TOKEN_NAMES.map((name) => ({ label: name, value: name }))\nconst ALIGN_OPTIONS = CONTAINER_ALIGNMENTS.map((name) => ({ label: name, value: name }))\n\n// ---------------------------------------------------------------------------\n// Container — a generic block wrapper with a single default slot.\n// ---------------------------------------------------------------------------\n\nfunction Container(props: PrimitiveProps): ReactNode {\n // The default `children` slot and the layout `outlet` slot both render here; the outlet\n // is where `renderCompositionInLayout` injects the page body when this Container is a layout root.\n return (\n <div data-ec-primitive=\"Container\">\n {props.children}\n {props.outlet as ReactNode}\n </div>\n )\n}\n\nconst ContainerManifest = defineComponent({\n id: 'Container',\n title: 'Container',\n category: 'layout',\n props: {},\n // The outlet is filled by page injection, so it is not authored-required.\n slots: [{ name: 'children' }, { name: 'outlet' }],\n})\n\n// ---------------------------------------------------------------------------\n// Stack — flex layout container (EC-161): direction, token-bound gap, alignment.\n// ---------------------------------------------------------------------------\n\nfunction Stack(props: PrimitiveProps): ReactNode {\n const style: CSSProperties = {\n display: 'flex',\n flexDirection: props.direction === 'horizontal' ? 'row' : 'column',\n }\n const gap = gapCss(props.gap)\n if (gap !== undefined) style.gap = gap\n const alignItems = alignItemsCss(props.align)\n if (alignItems !== undefined) style.alignItems = alignItems\n return (\n <div data-ec-primitive=\"Stack\" style={style}>\n {props.children}\n </div>\n )\n}\n\nconst StackManifest = defineComponent({\n id: 'Stack',\n // EC-591: implicit layout's flow container and the canvas's synthetic root.\n role: 'layout.stack',\n title: 'Stack',\n category: 'layout',\n props: {\n direction: {\n type: 'select',\n default: 'vertical',\n options: [\n { value: 'vertical', label: 'Vertical' },\n { value: 'horizontal', label: 'Horizontal' },\n ],\n form: { label: 'Direction' },\n },\n gap: {\n // A design-token spacing step name (EC-161); legacy numeric gaps still render.\n type: 'select',\n default: '4',\n options: SPACING_OPTIONS,\n form: { label: 'Gap' },\n },\n align: {\n type: 'select',\n default: 'stretch',\n options: ALIGN_OPTIONS,\n form: { label: 'Align' },\n },\n },\n slots: [{ name: 'children' }],\n})\n\n// ---------------------------------------------------------------------------\n// Grid — CSS grid container (EC-161): column count, token-bound gap, alignment.\n// ---------------------------------------------------------------------------\n\nfunction asColumns(value: unknown): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return GRID_MIN_COLUMNS\n return Math.min(Math.max(GRID_MIN_COLUMNS, Math.floor(value)), GRID_MAX_COLUMNS)\n}\n\n/**\n * Responsive defaults (EC-163): below this viewport width a Grid's columns\n * stack into one. 640px (the Tailwind `sm` boundary) sits between the\n * builder's deterministic preview widths — phone (390px) stacks, tablet\n * (768px) keeps columns.\n */\nexport const GRID_MOBILE_BREAKPOINT_PX = 640\n\n/**\n * The stack-on-mobile rule a stacking Grid carries with it. A **viewport media\n * query** (not a container query), deliberately: the builder canvas renders\n * into an iframe — a true viewport — so constraining the iframe to the phone\n * width makes this exact rule fire in the preview; and the identical markup\n * server-renders in `@elytracms/next` (App Router SSR) with no client JS, no\n * extra stylesheet wiring, and no `container-type` context that every consumer\n * would otherwise have to guarantee around every grid. The rule is emitted as\n * an inline `<style>` child of the grid itself (style elements generate no\n * grid item), so the behavior is self-contained wherever the implementation\n * renders — canvas iframe portal, static markup, or RSC payload — and the\n * `!important` lets the stylesheet rule beat the grid's own inline column\n * template. Opted-out grids (`stackOnMobile: false`) carry neither the\n * attribute nor the rule.\n */\nexport const GRID_STACK_CSS = `@media (max-width: ${GRID_MOBILE_BREAKPOINT_PX}px){[data-ec-grid-stack]{grid-template-columns:minmax(0,1fr) !important}}`\n\nfunction Grid(props: PrimitiveProps): ReactNode {\n const style: CSSProperties = {\n display: 'grid',\n gridTemplateColumns: `repeat(${asColumns(props.columns)}, minmax(0, 1fr))`,\n }\n const gap = gapCss(props.gap)\n if (gap !== undefined) style.gap = gap\n const alignItems = alignItemsCss(props.align)\n if (alignItems !== undefined) style.alignItems = alignItems\n // Stack-on-mobile is the default; only the explicit opt-out keeps columns.\n const stacksOnMobile = props.stackOnMobile !== false\n return (\n <div\n data-ec-primitive=\"Grid\"\n {...(stacksOnMobile ? { 'data-ec-grid-stack': '' } : {})}\n style={style}\n >\n {stacksOnMobile ? <style>{GRID_STACK_CSS}</style> : null}\n {props.children}\n </div>\n )\n}\n\nconst GridManifest = defineComponent({\n id: 'Grid',\n // EC-591: the column container the \"drop beside\" gesture wraps into.\n role: 'layout.grid',\n title: 'Grid',\n category: 'layout',\n props: {\n columns: {\n type: 'number',\n default: 2,\n validation: { min: GRID_MIN_COLUMNS, max: GRID_MAX_COLUMNS },\n form: { label: 'Columns' },\n },\n gap: {\n type: 'select',\n default: '4',\n options: SPACING_OPTIONS,\n form: { label: 'Gap' },\n },\n align: {\n type: 'select',\n default: 'stretch',\n options: ALIGN_OPTIONS,\n form: { label: 'Align' },\n },\n stackOnMobile: {\n // Responsive default (EC-163): stacking is on unless explicitly opted out.\n type: 'boolean',\n default: true,\n form: { label: 'Stack on phones' },\n },\n },\n slots: [{ name: 'children' }],\n})\n\n// ---------------------------------------------------------------------------\n// Text — a paragraph of text driven by the `value` prop.\n// ---------------------------------------------------------------------------\n\nfunction Text(props: PrimitiveProps): ReactNode {\n // EC-408: the paragraph is the `value` prop's clickable element (studio only).\n return (\n <p data-ec-primitive=\"Text\" {...ecProp('value')}>\n {asString(props.value)}\n </p>\n )\n}\n\nconst TextManifest = defineComponent({\n id: 'Text',\n // EC-591: the block a seeded insert (layout presets) fills with copy (`value`).\n role: 'text',\n title: 'Text',\n category: 'content',\n props: {\n value: {\n type: 'text',\n default: '',\n // Plain-text content prop, editable in place on the canvas (EC-158).\n form: { label: 'Text', control: 'textarea', inlineEditable: true },\n },\n },\n slots: [],\n})\n\n// ---------------------------------------------------------------------------\n// Heading — h1..h6 heading driven by `text` + `level`.\n// ---------------------------------------------------------------------------\n\nfunction Heading(props: PrimitiveProps): ReactNode {\n const level =\n typeof props.level === 'number' && props.level >= 1 && props.level <= 6 ? props.level : 2\n const tag = `h${level}`\n // EC-408: the heading element is the `text` prop's clickable element.\n return createElement(tag, { 'data-ec-primitive': 'Heading', ...ecProp('text') }, asString(props.text))\n}\n\nconst HeadingManifest = defineComponent({\n id: 'Heading',\n title: 'Heading',\n category: 'content',\n props: {\n text: {\n type: 'text',\n default: '',\n // Plain-text content prop, editable in place on the canvas (EC-158).\n form: { label: 'Text', inlineEditable: true },\n },\n level: {\n // A numeric heading level 1–6 (the impl clamps out-of-range to 2).\n type: 'number',\n default: 2,\n validation: { min: 1, max: 6 },\n form: { label: 'Level' },\n },\n },\n slots: [],\n})\n\n// ---------------------------------------------------------------------------\n// Image — an <img> with src/alt plus intrinsic dimensions (EC-152). Width and\n// height come from the asset metadata record (bindable to the delivery\n// shape's `width`/`height`) so rendered images reserve layout space — no\n// layout shift. A plain <img> is correct for non-Next contexts (the builder\n// canvas runs on Vite); a Next host opts into `withNextImage` from\n// `@elytracms/next`, which swaps this implementation for `ElytraImage`\n// (next/image) by the `image` role, keeping this manifest.\n// ---------------------------------------------------------------------------\n\nfunction asPositiveInt(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : undefined\n}\n\n/**\n * The Image `src` may be a resolved asset (EC-195: an `asset`-typed prop the\n * renderer resolved from an asset id) or a plain URL string. Normalize to\n * `{ url, width, height, alt }`, letting the asset's intrinsic dimensions fill in\n * when the explicit props are unset — so an uploaded image reserves the right box\n * (no layout shift). The alt is the ASSET's for an asset source (EC-587, EC-569\n * reader 3: one alt per asset, read one way on every surface); the `alt` prop is the\n * alt of an external image (a literal URL `src`) only.\n */\nexport interface ImageSource {\n url: string\n width: number | undefined\n height: number | undefined\n alt: string\n /** Normalized focal point `{ x, y }` 0–1 (EC-230) → CSS object-position; null when none. */\n focalPoint: { x: number; y: number } | null\n /** LQIP blur-placeholder data URI (EC-420) → `next/image` blurDataURL; null when none. */\n lqip: string | null\n}\n\n/** Read a resolved asset's `focalPoint` defensively (a `{ x, y }` pair in 0–1). */\nfunction asFocalPoint(value: unknown): { x: number; y: number } | null {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const point = value as { x?: unknown; y?: unknown }\n if (typeof point.x === 'number' && typeof point.y === 'number') {\n return { x: point.x, y: point.y }\n }\n }\n return null\n}\n\nexport function normalizeImageSource(props: PrimitiveProps): ImageSource {\n const src = props.src\n const asset =\n src && typeof src === 'object' && !Array.isArray(src)\n ? (src as {\n url?: unknown\n width?: unknown\n height?: unknown\n alt?: unknown\n focalPoint?: unknown\n lqip?: unknown\n })\n : null\n const url = asset ? asString(asset.url) : asString(src)\n const width = asPositiveInt(props.width) ?? (asset ? asPositiveInt(asset.width) : undefined)\n const height = asPositiveInt(props.height) ?? (asset ? asPositiveInt(asset.height) : undefined)\n const alt = asset ? asString(asset.alt) : asString(props.alt)\n const focalPoint = asset ? asFocalPoint(asset.focalPoint) : null\n // EC-420: a resolved asset may carry an LQIP data URI → next/image blurDataURL.\n const lqip = asset && typeof asset.lqip === 'string' && asset.lqip.length > 0 ? asset.lqip : null\n return { url, width, height, alt, focalPoint, lqip }\n}\n\n/** EC-230: a focal point becomes a CSS `object-position` string (percent of each axis). */\nexport function focalPointObjectPosition(focalPoint: { x: number; y: number } | null): string | undefined {\n return focalPoint ? `${focalPoint.x * 100}% ${focalPoint.y * 100}%` : undefined\n}\n\nfunction Image(props: PrimitiveProps): ReactNode {\n const { url, width, height, alt, focalPoint } = normalizeImageSource(props)\n const objectPosition = focalPointObjectPosition(focalPoint)\n // No resolved url (no asset picked yet, or an unresolved id): a visible\n // placeholder, never a broken `<img src=\"\">` (EC-195 / EC-015).\n if (url === '') {\n return (\n <span\n data-ec-primitive=\"Image\"\n data-ec-image=\"empty\"\n // EC-408: `src` is the Image's primary/clickable prop (of src/alt/width/…).\n {...ecProp('src')}\n role=\"img\"\n aria-label={alt || 'No image selected'}\n >\n No image selected\n </span>\n )\n }\n return (\n <img\n data-ec-primitive=\"Image\"\n // EC-408: `src` is the Image's primary/clickable prop (of src/alt/width/…).\n {...ecProp('src')}\n src={url}\n alt={alt}\n {...(width !== undefined ? { width } : {})}\n {...(height !== undefined ? { height } : {})}\n {...(objectPosition ? { style: { objectPosition } } : {})}\n />\n )\n}\n\n/**\n * Is a stored `src` an asset envelope (`{ type: 'asset', id }`, EC-277)? Structural:\n * the components package may not import cms-core (EC-591's render-surface rule), and\n * the self-describing `type` discriminator is exactly what makes this readable without it.\n */\nfunction holdsAssetEnvelope(value: unknown): boolean {\n return typeof value === 'object' && value !== null && (value as { type?: unknown }).type === 'asset'\n}\n\nconst ImageManifest = defineComponent({\n id: 'Image',\n // EC-591: the image block (asset drop; the Next host's `next/image` swap).\n role: 'image',\n title: 'Image',\n category: 'media',\n props: {\n // EC-195: `asset`-typed, so the editor picks an uploaded asset (the renderer\n // resolves the id to a url + intrinsic dimensions at delivery) — a plain URL\n // string still works (the resolver passes URL-like values through).\n src: {\n type: 'asset',\n multiple: false,\n default: '',\n form: { label: 'Image' },\n },\n alt: {\n type: 'text',\n default: '',\n form: {\n label: 'Alt text',\n // EC-587: an uploaded image's alt is the asset's (edited on the asset, shared by\n // every use); this prop is the alt of an external image (a URL `src`) only, so\n // it is not offered — and is cleared on publish — while `src` is an asset.\n description: 'For an image by URL. An uploaded image uses the alt text of its asset.',\n hidden: ({ values }) => holdsAssetEnvelope(values.src),\n },\n },\n width: {\n type: 'number',\n validation: { min: 1 },\n form: { label: 'Width' },\n },\n height: {\n type: 'number',\n validation: { min: 1 },\n form: { label: 'Height' },\n },\n // EC-195: above-the-fold images load eagerly (`next/image priority`) so the\n // host can opt the hero image out of lazy-loading.\n priority: {\n type: 'boolean',\n default: false,\n form: { label: 'Load eagerly (above the fold)' },\n },\n },\n slots: [],\n})\n\n// ---------------------------------------------------------------------------\n// Button — labelled button with an optional href.\n// ---------------------------------------------------------------------------\n\nfunction Button(props: PrimitiveProps): ReactNode {\n const label = asString(props.label, 'Button')\n if (typeof props.href === 'string' && props.href.length > 0) {\n // EC-408: `label` is the Button's clickable/visible prop (of label/href).\n return (\n <a data-ec-primitive=\"Button\" {...ecProp('label')} href={props.href} role=\"button\">\n {label}\n </a>\n )\n }\n return (\n <button data-ec-primitive=\"Button\" {...ecProp('label')} type=\"button\">\n {label}\n </button>\n )\n}\n\nconst ButtonManifest = defineComponent({\n id: 'Button',\n title: 'Button',\n category: 'content',\n props: {\n label: {\n type: 'text',\n default: 'Button',\n form: { label: 'Label' },\n },\n href: {\n type: 'text',\n form: { label: 'Link' },\n },\n },\n slots: [],\n})\n\n// ---------------------------------------------------------------------------\n// Repeater — renders its `item` slot template once per item (iteration is done by the\n// renderer; this implementation only frames the rendered items).\n// ---------------------------------------------------------------------------\n\nfunction Repeater(props: PrimitiveProps): ReactNode {\n return <div data-ec-primitive=\"Repeater\">{props.children}</div>\n}\n\nconst RepeaterManifest = defineComponent({\n id: 'Repeater',\n title: 'Repeater',\n category: 'data',\n // EC-190 DD7: iterate-over-a-source is a binding construct designed out of v1.\n // The `items` value (an array) has no static field-def type; it rides node.props\n // undeclared (passing through the renderer) until the binding/iteration removal in\n // slice 5 decides Repeater's fate. No editable static prop is exposed meanwhile.\n props: {},\n slots: [{ name: 'item', label: 'Item template', required: true }],\n iterate: { itemsProp: 'items', itemSlot: 'item' },\n})\n\n// ---------------------------------------------------------------------------\n// Switch — renders exactly one named case slot, chosen by the `case` prop/binding.\n// ---------------------------------------------------------------------------\n\nfunction Switch(props: PrimitiveProps): ReactNode {\n const key = asString(props.case)\n const selected = props[`case:${key}`] ?? props.fallback\n return <div data-ec-primitive=\"Switch\">{selected as ReactNode}</div>\n}\n\nconst SwitchManifest = defineComponent({\n id: 'Switch',\n title: 'Switch',\n category: 'data',\n props: {\n case: {\n type: 'text',\n default: '',\n form: { label: 'Case' },\n },\n },\n // Named case slots are dynamic; `fallback` renders when no case matches.\n slots: [{ name: 'fallback', label: 'Fallback' }],\n})\n\n// ---------------------------------------------------------------------------\n// Aggregation\n// ---------------------------------------------------------------------------\n\n/**\n * One shipped component: its manifest plus the real implementation. Strictly\n * typed (the implementation IS a component), and assignable to the config's\n * `HostComponentEntry` and core's `ComponentEntry`, so the entries list straight\n * into `elytra.config.ts`'s `components`.\n */\nexport interface ElytraComponent {\n manifest: ComponentManifest\n implementation: ComponentType<Record<string, unknown>>\n}\n\nfunction entry(manifest: ComponentManifest, implementation: ComponentType<PrimitiveProps>): ElytraComponent {\n return { manifest, implementation: implementation as ComponentType<Record<string, unknown>> }\n}\n\nexport const containerComponent = entry(ContainerManifest, Container)\nexport const stackComponent = entry(StackManifest, Stack)\nexport const gridComponent = entry(GridManifest, Grid)\nexport const textComponent = entry(TextManifest, Text)\nexport const headingComponent = entry(HeadingManifest, Heading)\n// Rich-text content component (EC-159) — content prop is the stored\n// `@elytracms/core/rich-text` value, flagged inline-editable.\nexport const richTextComponent = entry(RichTextManifest, RichText)\nexport const imageComponent = entry(ImageManifest, Image)\nexport const buttonComponent = entry(ButtonManifest, Button)\nexport const repeaterComponent = entry(RepeaterManifest, Repeater)\nexport const switchComponent = entry(SwitchManifest, Switch)\n\n/**\n * Every component Elytra ships, in palette order — the list a config spreads\n * into its `components` (`[...elytraComponents, ...siteComponents]`). Pick a\n * subset by listing the individual entries instead; replace one by listing your\n * own component under the id (and leaving the shipped entry out — the list names\n * each id once, a second entry is a `duplicate-id` issue).\n */\nexport const elytraComponents: readonly ElytraComponent[] = [\n containerComponent,\n stackComponent,\n gridComponent,\n textComponent,\n headingComponent,\n richTextComponent,\n imageComponent,\n buttonComponent,\n repeaterComponent,\n switchComponent,\n]\n\nexport {\n Container,\n Stack,\n Grid,\n Text,\n Heading,\n Image,\n Button,\n Repeater,\n Switch,\n ContainerManifest,\n StackManifest,\n GridManifest,\n TextManifest,\n HeadingManifest,\n ImageManifest,\n ButtonManifest,\n RepeaterManifest,\n SwitchManifest,\n}\n"]}
package/dist/rich-text.js CHANGED
@@ -68,7 +68,7 @@ const RICH_TEXT_FIXTURE_VALUE = {
68
68
  };
69
69
  export function RichText(props) {
70
70
  // The embed renderer (EC-189/EC-547), injected by the renderer as a prop (bound
71
- // to `renderComposition`). A prop — not a `'use client'` React context — keeps
71
+ // to `renderDocumentBody`). A prop — not a `'use client'` React context — keeps
72
72
  // this server-safe: the renderer runs in the host's RSC pass, and a function
73
73
  // cannot cross the server→client boundary into a context provider. When present,
74
74
  // `elytra/component` nodes render through the same pipeline as a page/block
@@ -1 +1 @@
1
- {"version":3,"file":"rich-text.js","sourceRoot":"","sources":["../src/rich-text.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ReactNode } from 'react'\nimport { defineComponent } from '@elytracms/core/component-registry'\nimport { parseRichTextValue, renderRichTextValue } from '@elytracms/core/rich-text'\nimport type { RichTextValue, RichTextEmbedRenderer, RichTextRefResolver } from '@elytracms/core/rich-text'\nimport type { JsonValue } from '@elytracms/core/project-graph'\nimport { ecProp, richTextPrimitiveRegistry } from '@elytracms/core/runtime-renderer'\n\n/**\n * RichText — the rich-text content component (EC-159), filling the `richText`\n * role (EC-591). Its `content` prop is a stored `@elytracms/core/rich-text` value\n * (the versioned, ProseMirror-shaped storage JSON); rendering goes through the\n * package's server-safe renderer with the project's custom block/inline registry\n * (`richTextPrimitiveRegistry`, the same one a document body's prose renders\n * with), so custom blocks render identically on the canvas, in fixtures, and in an\n * exported app. Unknown node types degrade to the renderer's visible fallback —\n * never a crash, never silent.\n *\n * The prop is flagged `inlineEditable`: the builder canvas edits the rendered\n * element in place (EC-158 semantics extended to rich text), committing back\n * to the same storage format.\n *\n * EC-591: moved from `@elytracms/core/runtime-renderer` (where it was registered\n * by the engine as `base.RichText`) into this opt-in package; id `RichText`.\n */\n\n/**\n * Stored rich-text values are plain JSON by construction (Zod-validated, no\n * runtime handles), but `RichTextNode` is an interface without an index\n * signature, so the structural `JsonValue` assignment needs this one cast.\n */\nfunction asJson(value: RichTextValue): JsonValue {\n return value as unknown as JsonValue\n}\n\n/**\n * The implementation's own content for an absent `content` prop — what a freshly\n * inserted RichText renders (EC-591/2f: an absent prop is `undefined` and THIS is\n * the implementation's default; the manifest `default` below is only the\n * inspector's hint).\n */\nexport const RICH_TEXT_DEFAULT_VALUE: RichTextValue = {\n version: 2,\n content: [\n {\n type: 'paragraph',\n content: [{ type: 'text', text: 'Rich text. Select to format, double-click to edit.' }],\n },\n ],\n}\n\n/** A fixture document exercising marks, a link, a list, and the custom blocks. */\nconst RICH_TEXT_FIXTURE_VALUE: RichTextValue = {\n version: 2,\n content: [\n { type: 'heading', attrs: { level: 3 }, content: [{ type: 'text', text: 'Rich text block' }] },\n {\n type: 'paragraph',\n content: [\n { type: 'text', text: 'Formatted ' },\n { type: 'text', text: 'bold', marks: [{ type: 'bold' }] },\n { type: 'text', text: ' and ' },\n { type: 'text', text: 'italic', marks: [{ type: 'italic' }] },\n { type: 'text', text: ' copy with a ' },\n { type: 'text', text: 'link', marks: [{ type: 'link', attrs: { href: 'https://example.com' } }] },\n { type: 'text', text: '.' },\n ],\n },\n {\n type: 'callout',\n attrs: { tone: 'info' },\n content: [{ type: 'text', text: 'A custom block, rendered by the project registry.' }],\n },\n ],\n}\n\nexport function RichText(props: Record<string, unknown> & { children?: ReactNode }): ReactNode {\n // The embed renderer (EC-189/EC-547), injected by the renderer as a prop (bound\n // to `renderComposition`). A prop — not a `'use client'` React context — keeps\n // this server-safe: the renderer runs in the host's RSC pass, and a function\n // cannot cross the server→client boundary into a context provider. When present,\n // `elytra/component` nodes render through the same pipeline as a page/block\n // field; when absent (a bare render), components degrade to the visible fallback.\n const renderEmbed =\n typeof props.renderEmbed === 'function'\n ? (props.renderEmbed as RichTextEmbedRenderer)\n : undefined\n // EC-290: the ref resolver, injected by the renderer as a prop (bound to the\n // host's `resolveAsset`/`resolveLink`), turns self-typed reference links/asset\n // pins (`attrs.ref`, EC-277) into real URLs. Absent → reference links degrade\n // visibly with an `unresolved-ref` issue, never a silent `#`.\n const resolveRef =\n typeof props.resolveRef === 'function'\n ? (props.resolveRef as RichTextRefResolver)\n : undefined\n // An absent `content` renders the implementation's own default (the renderer\n // passes absent props as `undefined` — EC-591/2f).\n const parsed = parseRichTextValue(props.content ?? RICH_TEXT_DEFAULT_VALUE)\n if (!parsed.ok) {\n // The renderer withholds a schema-invalid static value (EC-591: it arrives\n // absent, so this branch sees only values it could not judge) — visibly.\n return (\n <div data-ec-primitive=\"RichText\" className=\"rich-text-invalid\" role=\"note\">\n Invalid rich-text content.\n </div>\n )\n }\n return (\n // EC-408: the RichText region is the `content` prop's element. `content` is\n // inline-editable, so a click defers to inline editing (field-focus is skipped);\n // the marker still drives the reverse focus→highlight and node-scoped resolution.\n <div data-ec-primitive=\"RichText\" {...ecProp('content')}>\n {renderRichTextValue(parsed.value, { registry: richTextPrimitiveRegistry, renderEmbed, resolveRef })}\n </div>\n )\n}\n\nexport const RichTextManifest = defineComponent({\n id: 'RichText',\n role: 'richText',\n title: 'Rich Text',\n category: 'content',\n description: 'Formatted long-form content stored in the rich-text format.',\n props: {\n content: {\n // The structured @elytracms/rich-text storage value (EC-159); validated by\n // the renderer's parse guard rather than a scalar string check.\n type: 'richText',\n // A hint only (EC-591/2f) — the inspector shows it greyed; the engine never\n // writes it into a node.\n default: asJson(RICH_TEXT_DEFAULT_VALUE),\n // Rich-text content prop, editable in place on the canvas (EC-159).\n form: { label: 'Content', inlineEditable: true },\n },\n },\n slots: [],\n fixtures: [\n { name: 'Default' },\n { name: 'Formatted', props: { content: asJson(RICH_TEXT_FIXTURE_VALUE) } },\n ],\n})\n"]}
1
+ {"version":3,"file":"rich-text.js","sourceRoot":"","sources":["../src/rich-text.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ReactNode } from 'react'\nimport { defineComponent } from '@elytracms/core/component-registry'\nimport { parseRichTextValue, renderRichTextValue } from '@elytracms/core/rich-text'\nimport type { RichTextValue, RichTextEmbedRenderer, RichTextRefResolver } from '@elytracms/core/rich-text'\nimport type { JsonValue } from '@elytracms/core/project-graph'\nimport { ecProp, richTextPrimitiveRegistry } from '@elytracms/core/runtime-renderer'\n\n/**\n * RichText — the rich-text content component (EC-159), filling the `richText`\n * role (EC-591). Its `content` prop is a stored `@elytracms/core/rich-text` value\n * (the versioned, ProseMirror-shaped storage JSON); rendering goes through the\n * package's server-safe renderer with the project's custom block/inline registry\n * (`richTextPrimitiveRegistry`, the same one a document body's prose renders\n * with), so custom blocks render identically on the canvas, in fixtures, and in an\n * exported app. Unknown node types degrade to the renderer's visible fallback —\n * never a crash, never silent.\n *\n * The prop is flagged `inlineEditable`: the builder canvas edits the rendered\n * element in place (EC-158 semantics extended to rich text), committing back\n * to the same storage format.\n *\n * EC-591: moved from `@elytracms/core/runtime-renderer` (where it was registered\n * by the engine as `base.RichText`) into this opt-in package; id `RichText`.\n */\n\n/**\n * Stored rich-text values are plain JSON by construction (Zod-validated, no\n * runtime handles), but `RichTextNode` is an interface without an index\n * signature, so the structural `JsonValue` assignment needs this one cast.\n */\nfunction asJson(value: RichTextValue): JsonValue {\n return value as unknown as JsonValue\n}\n\n/**\n * The implementation's own content for an absent `content` prop — what a freshly\n * inserted RichText renders (EC-591/2f: an absent prop is `undefined` and THIS is\n * the implementation's default; the manifest `default` below is only the\n * inspector's hint).\n */\nexport const RICH_TEXT_DEFAULT_VALUE: RichTextValue = {\n version: 2,\n content: [\n {\n type: 'paragraph',\n content: [{ type: 'text', text: 'Rich text. Select to format, double-click to edit.' }],\n },\n ],\n}\n\n/** A fixture document exercising marks, a link, a list, and the custom blocks. */\nconst RICH_TEXT_FIXTURE_VALUE: RichTextValue = {\n version: 2,\n content: [\n { type: 'heading', attrs: { level: 3 }, content: [{ type: 'text', text: 'Rich text block' }] },\n {\n type: 'paragraph',\n content: [\n { type: 'text', text: 'Formatted ' },\n { type: 'text', text: 'bold', marks: [{ type: 'bold' }] },\n { type: 'text', text: ' and ' },\n { type: 'text', text: 'italic', marks: [{ type: 'italic' }] },\n { type: 'text', text: ' copy with a ' },\n { type: 'text', text: 'link', marks: [{ type: 'link', attrs: { href: 'https://example.com' } }] },\n { type: 'text', text: '.' },\n ],\n },\n {\n type: 'callout',\n attrs: { tone: 'info' },\n content: [{ type: 'text', text: 'A custom block, rendered by the project registry.' }],\n },\n ],\n}\n\nexport function RichText(props: Record<string, unknown> & { children?: ReactNode }): ReactNode {\n // The embed renderer (EC-189/EC-547), injected by the renderer as a prop (bound\n // to `renderDocumentBody`). A prop — not a `'use client'` React context — keeps\n // this server-safe: the renderer runs in the host's RSC pass, and a function\n // cannot cross the server→client boundary into a context provider. When present,\n // `elytra/component` nodes render through the same pipeline as a page/block\n // field; when absent (a bare render), components degrade to the visible fallback.\n const renderEmbed =\n typeof props.renderEmbed === 'function'\n ? (props.renderEmbed as RichTextEmbedRenderer)\n : undefined\n // EC-290: the ref resolver, injected by the renderer as a prop (bound to the\n // host's `resolveAsset`/`resolveLink`), turns self-typed reference links/asset\n // pins (`attrs.ref`, EC-277) into real URLs. Absent → reference links degrade\n // visibly with an `unresolved-ref` issue, never a silent `#`.\n const resolveRef =\n typeof props.resolveRef === 'function'\n ? (props.resolveRef as RichTextRefResolver)\n : undefined\n // An absent `content` renders the implementation's own default (the renderer\n // passes absent props as `undefined` — EC-591/2f).\n const parsed = parseRichTextValue(props.content ?? RICH_TEXT_DEFAULT_VALUE)\n if (!parsed.ok) {\n // The renderer withholds a schema-invalid static value (EC-591: it arrives\n // absent, so this branch sees only values it could not judge) — visibly.\n return (\n <div data-ec-primitive=\"RichText\" className=\"rich-text-invalid\" role=\"note\">\n Invalid rich-text content.\n </div>\n )\n }\n return (\n // EC-408: the RichText region is the `content` prop's element. `content` is\n // inline-editable, so a click defers to inline editing (field-focus is skipped);\n // the marker still drives the reverse focus→highlight and node-scoped resolution.\n <div data-ec-primitive=\"RichText\" {...ecProp('content')}>\n {renderRichTextValue(parsed.value, { registry: richTextPrimitiveRegistry, renderEmbed, resolveRef })}\n </div>\n )\n}\n\nexport const RichTextManifest = defineComponent({\n id: 'RichText',\n role: 'richText',\n title: 'Rich Text',\n category: 'content',\n description: 'Formatted long-form content stored in the rich-text format.',\n props: {\n content: {\n // The structured @elytracms/rich-text storage value (EC-159); validated by\n // the renderer's parse guard rather than a scalar string check.\n type: 'richText',\n // A hint only (EC-591/2f) — the inspector shows it greyed; the engine never\n // writes it into a node.\n default: asJson(RICH_TEXT_DEFAULT_VALUE),\n // Rich-text content prop, editable in place on the canvas (EC-159).\n form: { label: 'Content', inlineEditable: true },\n },\n },\n slots: [],\n fixtures: [\n { name: 'Default' },\n { name: 'Formatted', props: { content: asJson(RICH_TEXT_FIXTURE_VALUE) } },\n ],\n})\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elytracms/components",
3
3
  "author": "finaldream <hi@finaldream.de>",
4
- "version": "0.0.92",
4
+ "version": "0.0.93",
5
5
  "engines": {
6
6
  "node": ">=22.18.0"
7
7
  },
@@ -16,7 +16,7 @@
16
16
  }
17
17
  },
18
18
  "dependencies": {
19
- "@elytracms/core": "^0.0.92"
19
+ "@elytracms/core": "^0.0.93"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "react": "^19.0.0",