@elytracms/studio 0.0.63 → 0.0.64
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/dist/components/builder/canvas-chrome-css.d.ts +54 -0
- package/dist/components/builder/canvas-chrome-css.js +164 -0
- package/dist/components/builder/canvas-chrome-css.js.map +1 -0
- package/dist/components/builder/canvas-chrome.d.ts +122 -18
- package/dist/components/builder/canvas-chrome.js +148 -82
- package/dist/components/builder/canvas-chrome.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +13 -0
- package/dist/components/builder/canvas-render-context.js +7 -5
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas-stylesheet.d.ts +1 -1
- package/dist/components/builder/canvas-stylesheet.js +16 -9
- package/dist/components/builder/canvas-stylesheet.js.map +1 -1
- package/dist/components/builder/canvas-theme-notice.d.ts +6 -0
- package/dist/components/builder/canvas-theme-notice.js +46 -0
- package/dist/components/builder/canvas-theme-notice.js.map +1 -0
- package/dist/components/builder/canvas.js +17 -10
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/rich-text-toolbar.d.ts +23 -0
- package/dist/components/builder/rich-text-toolbar.js +24 -17
- package/dist/components/builder/rich-text-toolbar.js.map +1 -1
- package/dist/components/builder/spacing-handles.d.ts +31 -0
- package/dist/components/builder/spacing-handles.js +49 -37
- package/dist/components/builder/spacing-handles.js.map +1 -1
- package/dist/components/components/component-preview.d.ts +2 -1
- package/dist/components/components/component-preview.js +24 -25
- package/dist/components/components/component-preview.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +4 -0
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +4 -0
- package/dist/lib/i18n/messages.en.js +5 -0
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/persistence/backend-mode.d.ts +7 -0
- package/dist/lib/persistence/backend-mode.js +9 -0
- package/dist/lib/persistence/backend-mode.js.map +1 -1
- package/dist/lib/persistence/index.d.ts +1 -1
- package/dist/lib/persistence/index.js +1 -1
- package/dist/lib/persistence/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -9,6 +9,8 @@ import { previewSpecs } from '@elytracms/studio/lib/registry/preview';
|
|
|
9
9
|
import { Badge, Button, Code, Field, Row, Spacer, Stack, TabButton, Text } from '@elytracms/studio/ui';
|
|
10
10
|
import { localizedLabel, translate } from '@elytracms/studio/lib/i18n';
|
|
11
11
|
import { cn } from '@elytracms/studio/lib/utils';
|
|
12
|
+
import { resolveDeploymentCanvasTheme } from '@elytracms/studio/lib/workspace';
|
|
13
|
+
import { wireCanvasChrome } from '../builder/canvas-chrome.js';
|
|
12
14
|
import { getCanvasStylesheetUrl } from '../builder/canvas-stylesheet.js';
|
|
13
15
|
import { useCanvasViewports, viewportWidth } from '../builder/canvas-breakpoints.js';
|
|
14
16
|
import { PropValueControl } from '../cms/prop-fields.js';
|
|
@@ -97,20 +99,23 @@ function ServerOnlyNotice() {
|
|
|
97
99
|
const MIN_PREVIEW_HEIGHT = 360;
|
|
98
100
|
/**
|
|
99
101
|
* The live preview render surface. Reuses the BuilderCanvas styling mechanism
|
|
100
|
-
* (EC-061, `canvas.tsx`)
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
102
|
+
* (EC-061, `canvas.tsx`) — since EC-414 through the SAME code path, `wireCanvasChrome`
|
|
103
|
+
* (closing the EC-389 follow-up): the component renders inside an ISOLATED
|
|
104
|
+
* same-origin iframe that `<link>`s the project's canvas stylesheet
|
|
105
|
+
* (`getCanvasStylesheetUrl()` — the consumer's theme entry; in the legacy merged
|
|
106
|
+
* world also the studio CSS), so a component built with the site's Tailwind theme
|
|
107
|
+
* previews with the site's styles, exactly as it does on the canvas and in
|
|
108
|
+
* delivery. Using the iframe (not the studio DOM) is what keeps the delivery CSS
|
|
109
|
+
* scoped and off the studio chrome.
|
|
106
110
|
*
|
|
107
111
|
* Unlike the builder canvas, this iframe's document is deliberately left OUT of
|
|
108
112
|
* the studio's own dark/light class: this is a standalone-component reference
|
|
109
113
|
* surface (not a full page), so its own negative space around the component must
|
|
110
114
|
* read as a light, site-like backdrop — never the studio chrome's current theme
|
|
111
115
|
* (a black frame is exactly what leaks through when the studio is dark-themed and
|
|
112
|
-
* the iframe root inherits that class). The
|
|
113
|
-
*
|
|
116
|
+
* the iframe root inherits that class). The chrome's world probe supplies that
|
|
117
|
+
* backdrop: the neutral backdrop rules in the new world, the studio `:root`
|
|
118
|
+
* (light) tokens in the legacy one.
|
|
114
119
|
*
|
|
115
120
|
* Sizing: width is driven by the caller (the breakpoint selector); height is
|
|
116
121
|
* measured live from the rendered content via `ResizeObserver` (EC-330) so a
|
|
@@ -128,21 +133,14 @@ function ComponentPreviewFrame({ node, ctx, displayName, width, }) {
|
|
|
128
133
|
const d = iframeRef.current?.contentDocument;
|
|
129
134
|
if (!d || !d.body)
|
|
130
135
|
return;
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
link.rel = 'stylesheet';
|
|
140
|
-
link.href = appCss;
|
|
141
|
-
d.head.appendChild(link);
|
|
142
|
-
}
|
|
143
|
-
d.body.className = 'bg-background text-foreground font-sans';
|
|
144
|
-
d.body.style.margin = '0';
|
|
145
|
-
d.body.style.padding = '24px';
|
|
136
|
+
// ONE code path with the builder canvas (EC-414 AD-B item 4): stylesheet
|
|
137
|
+
// link + chrome css + consumer canvas theme + the world probe. No kit
|
|
138
|
+
// tokens here — this is a registry-level reference surface, not a project
|
|
139
|
+
// page. The dev theme notice is the builder canvas's job, not this pane's.
|
|
140
|
+
wireCanvasChrome(d, {
|
|
141
|
+
stylesheetUrl: getCanvasStylesheetUrl(),
|
|
142
|
+
theme: resolveDeploymentCanvasTheme(),
|
|
143
|
+
});
|
|
146
144
|
setDoc(d);
|
|
147
145
|
}, []);
|
|
148
146
|
// Handle the already-loaded case (srcDoc can load before onLoad attaches).
|
|
@@ -208,10 +206,11 @@ export class PreviewErrorBoundary extends Component {
|
|
|
208
206
|
* The EC-191 fallback panel — component name + "preview unavailable" + the error.
|
|
209
207
|
* Rendered INTO the preview iframe (which carries only the project stylesheet, and
|
|
210
208
|
* may carry none in tests), so it uses inline styles rather than studio UI classes
|
|
211
|
-
* —
|
|
209
|
+
* — the EC-414 AD-B chrome contract, with the `.ec-chrome` containment recipe on
|
|
210
|
+
* its root (walked by the chrome contract test).
|
|
212
211
|
*/
|
|
213
212
|
export function PreviewUnavailable({ displayName, error }) {
|
|
214
|
-
return (_jsxs("div", { "data-preview-unavailable": true, style: {
|
|
213
|
+
return (_jsxs("div", { "data-preview-unavailable": true, className: "ec-chrome", style: {
|
|
215
214
|
boxSizing: 'border-box',
|
|
216
215
|
maxWidth: '42rem',
|
|
217
216
|
margin: '0 auto',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-preview.js","sourceRoot":"","sources":["../../../src/components/components/component-preview.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Component, useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { MonitorOff, RotateCcw } from 'lucide-react'\nimport { renderNode } from '@elytracms/core/runtime-renderer'\nimport type { RenderContext } from '@elytracms/core/runtime-renderer'\nimport { staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode, JsonValue } from '@elytracms/core/project-graph'\nimport type { ComponentManifest, PropField } from '@elytracms/core/component-registry'\nimport { componentStatus, previewContext } from '@elytracms/studio/lib/registry'\nimport type { StudioRegistry } from '@elytracms/studio/lib/registry'\nimport { previewSpecs } from '@elytracms/studio/lib/registry/preview'\nimport type { PreviewSpec } from '@elytracms/studio/lib/registry/preview'\nimport { Badge, Button, Code, Field, Row, Spacer, Stack, TabButton, Text } from '@elytracms/studio/ui'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { getCanvasStylesheetUrl } from '../builder/canvas-stylesheet'\nimport { useCanvasViewports, viewportWidth } from '../builder/canvas-breakpoints'\nimport type { ViewportOption } from '../builder/canvas-breakpoints'\nimport { PropValueControl } from '../cms/prop-fields'\n\n/**\n * EC-320 — the editable component preview. A component's declared preview states\n * (fixtures) select the SAMPLE STATE (slot content, base props); on top of that\n * the user edits individual prop values in the Props sidebar and the preview\n * re-renders live through the SAME runtime-renderer path the Builder and export\n * use (EC-054). Slots follow the selected fixture — full slot-tree editing is out\n * of scope for this reference surface (that's the composition editor's job); the\n * Slots pane documents what a component accepts and which state supplies content.\n */\n\nexport interface PreviewState {\n specs: PreviewSpec[]\n index: number\n setIndex: (i: number) => void\n /** Per-prop static-value overrides applied over the selected fixture. */\n overrides: Record<string, JsonValue>\n setOverride: (name: string, value: JsonValue) => void\n clearOverride: (name: string) => void\n resetAll: () => void\n dirty: boolean\n /** The live node: the selected fixture with prop overrides applied. */\n node: ComponentNode\n}\n\n/** Preview state for a manifest: the fixture selection + the live prop overrides. */\nexport function usePreviewState(manifest: ComponentManifest): PreviewState {\n const specs = useMemo(() => previewSpecs(manifest), [manifest])\n const [index, setIndexRaw] = useState(0)\n const [overrides, setOverrides] = useState<Record<string, JsonValue>>({})\n\n // Switching preview state resets prop edits — they were relative to the old state.\n const setIndex = (i: number) => {\n setIndexRaw(i)\n setOverrides({})\n }\n const setOverride = (name: string, value: JsonValue) =>\n setOverrides((o) => ({ ...o, [name]: value }))\n const clearOverride = (name: string) =>\n setOverrides((o) => {\n const next = { ...o }\n delete next[name]\n return next\n })\n const resetAll = () => setOverrides({})\n\n const base = specs[Math.min(index, specs.length - 1)]?.node\n const node = useMemo<ComponentNode>(() => {\n const fixture = base ?? { id: `preview-${manifest.id}`, componentId: manifest.id, props: {} }\n const props = { ...(fixture.props ?? {}) }\n for (const [name, value] of Object.entries(overrides)) props[name] = staticProp(value)\n return { ...fixture, props }\n }, [base, overrides, manifest.id])\n\n return {\n specs,\n index: Math.min(index, Math.max(specs.length - 1, 0)),\n setIndex,\n overrides,\n setOverride,\n clearOverride,\n resetAll,\n dirty: Object.keys(overrides).length > 0,\n node,\n }\n}\n\n/** Re-id a preview node into the canvas `node-<n>` namespace for embedding (transfer). */\nexport function embedNodeFrom(node: ComponentNode, id: string): ComponentNode {\n return { ...node, id }\n}\n\n/**\n * The live preview surface: the fixture-state selector (when a component declares\n * more than one) plus the rendered node. Server-only components (EC-191\n * `clientRenderable: false`) can't run on the client-rendered canvas, so they show\n * a documented fallback rather than risk a crash; a missing implementation falls\n * back through the renderer itself (the incompatible state).\n */\nexport function ComponentPreview({\n manifest,\n studio,\n state,\n}: {\n manifest: ComponentManifest\n studio: StudioRegistry\n state: PreviewState\n}) {\n const ctx = useMemo(() => previewContext(studio), [studio])\n const serverOnly = manifest.clientRenderable === false\n const status = componentStatus(studio, manifest)\n const displayName = manifest.title ?? manifest.id\n\n // EC-330: the same config-sourced breakpoint set the builder canvas toolbar\n // offers (`canvas-breakpoints.ts`) — reused, not redefined, so a preview and\n // the real canvas never disagree about the site's responsive widths.\n // Defaults to the always-present 'full' (responsive) option.\n const { viewports } = useCanvasViewports()\n const [viewport, setViewport] = useState(viewports[0]!.key)\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <Row gap=\"1\" wrap align=\"center\" className=\"shrink-0 border-b border-border px-3 py-2\">\n {state.specs.length > 1 ? (\n <Row\n gap=\"1\"\n wrap\n role=\"group\"\n aria-label={translate('components.fixtureRunner.statesAria')}\n >\n {state.specs.map((spec, i) => (\n <TabButton key={spec.name} active={i === state.index} onClick={() => state.setIndex(i)}>\n {spec.name}\n </TabButton>\n ))}\n </Row>\n ) : null}\n <Spacer />\n <Row\n gap=\"1\"\n wrap\n role=\"group\"\n aria-label={translate('components.preview.breakpointAria')}\n >\n {viewports.map((option) => (\n <PreviewBreakpointButton\n key={option.key}\n option={option}\n active={option.key === viewport}\n onSelect={() => setViewport(option.key)}\n />\n ))}\n </Row>\n {state.dirty ? (\n <Button variant=\"ghost\" size=\"sm\" onClick={state.resetAll} data-testid=\"preview-reset\">\n <RotateCcw /> {translate('components.preview.reset')}\n </Button>\n ) : null}\n </Row>\n\n <div\n className=\"canvas-grid min-h-0 flex-1 overflow-auto p-6\"\n data-fixture-state={state.specs[state.index]?.name}\n >\n {serverOnly ? (\n <ServerOnlyNotice />\n ) : (\n <ComponentPreviewFrame\n node={state.node}\n ctx={ctx}\n displayName={displayName}\n width={viewportWidth(viewports, viewport)}\n />\n )}\n {status === 'incompatible' && !serverOnly ? (\n <Text size=\"xs\" tone=\"muted\" className=\"mx-auto mt-3 max-w-2xl\">\n {translate('components.preview.incompatibleNote')}\n </Text>\n ) : null}\n </div>\n </div>\n )\n}\n\n/**\n * One breakpoint option in the preview's width switcher. Mirrors the builder\n * canvas toolbar's `ViewportButton` (`composition-field-editor.tsx`) — same\n * `Button`/active-state styling — but drives LOCAL preview state instead of\n * the builder store (this surface has no composition/undo stack).\n *\n * EC-328: evaluated against the shared `TabButton`/`SegmentedControl`\n * primitives and kept separate — it's built on the shared `Button` (not a\n * bare hand-rolled `<button>`, so it wasn't part of the duplication those\n * primitives address), and carries an interpolated `aria-label` plus a\n * conditional px-value `title` tooltip neither primitive's terse API models.\n */\nfunction PreviewBreakpointButton({\n option,\n active,\n onSelect,\n}: {\n option: ViewportOption\n active: boolean\n onSelect: () => void\n}) {\n return (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.viewportPreviewAria', { label: option.label })}\n aria-pressed={active}\n title={\n option.px !== undefined\n ? `${option.label} · ${option.px}px`\n : translate('builder.canvas.viewportWidthTitle', { label: option.label })\n }\n onClick={onSelect}\n className={cn('px-2 text-xs', active && 'bg-accent text-accent-foreground')}\n >\n {option.label}\n </Button>\n )\n}\n\nfunction ServerOnlyNotice() {\n return (\n <div className=\"mx-auto grid max-w-2xl place-items-center rounded-lg border border-dashed border-border bg-muted/30 p-8 text-center\">\n <Stack gap=\"2\" align=\"center\">\n <MonitorOff className=\"size-5 text-muted-foreground\" aria-hidden />\n <Text size=\"sm\" weight=\"medium\">\n {translate('components.preview.serverOnlyTitle')}\n </Text>\n <Text size=\"xs\" tone=\"muted\" className=\"max-w-sm\">\n {translate('components.preview.serverOnlyBody')}\n </Text>\n </Stack>\n </div>\n )\n}\n\n/** Floor for the auto-height iframe — never collapses to a sliver while empty/loading. */\nconst MIN_PREVIEW_HEIGHT = 360\n\n/**\n * The live preview render surface. Reuses the BuilderCanvas styling mechanism\n * (EC-061, `canvas.tsx`): the component renders inside an ISOLATED same-origin\n * iframe that `<link>`s the project's canvas stylesheet\n * (`getCanvasStylesheetUrl()` — the consuming app's delivery CSS + studio CSS), so\n * a component built with the site's Tailwind theme previews with the site's styles,\n * exactly as it does on the canvas and in delivery. Using the iframe (not the\n * studio DOM) is what keeps the delivery CSS scoped and off the studio chrome.\n *\n * Unlike the builder canvas, this iframe's document is deliberately left OUT of\n * the studio's own dark/light class: this is a standalone-component reference\n * surface (not a full page), so its own negative space around the component must\n * read as a light, site-like backdrop — never the studio chrome's current theme\n * (a black frame is exactly what leaks through when the studio is dark-themed and\n * the iframe root inherits that class). The canvas stylesheet's `:root` (light)\n * tokens supply that backdrop for free once the `.dark` class is never added here.\n *\n * Sizing: width is driven by the caller (the breakpoint selector); height is\n * measured live from the rendered content via `ResizeObserver` (EC-330) so a\n * preset switch or a prop edit — both of which change what's portaled into\n * `doc.body` — always show without clipping or dead space.\n *\n * The render is wrapped in {@link PreviewErrorBoundary} so a component that throws\n * shows the EC-191 fallback instead of taking the whole Components view down.\n */\nfunction ComponentPreviewFrame({\n node,\n ctx,\n displayName,\n width,\n}: {\n node: ComponentNode\n ctx: RenderContext\n displayName: string\n /** CSS width for the iframe — '100%' (responsive) or a fixed breakpoint px width. */\n width: string\n}) {\n const iframeRef = useRef<HTMLIFrameElement>(null)\n const [doc, setDoc] = useState<Document | null>(null)\n const [height, setHeight] = useState(MIN_PREVIEW_HEIGHT)\n\n const wireUp = useCallback(() => {\n const d = iframeRef.current?.contentDocument\n if (!d || !d.body) return\n // Deliberately no `.dark` here (see the surface note above) — the light\n // `:root` tokens from the injected stylesheet are the intended backdrop.\n // Unset stylesheet (tests / an app that never called\n // `setCanvasStylesheetUrl`) → skip the link.\n const appCss = getCanvasStylesheetUrl()\n if (appCss && !d.getElementById('ec-app-css')) {\n const link = d.createElement('link')\n link.id = 'ec-app-css'\n link.rel = 'stylesheet'\n link.href = appCss\n d.head.appendChild(link)\n }\n d.body.className = 'bg-background text-foreground font-sans'\n d.body.style.margin = '0'\n d.body.style.padding = '24px'\n setDoc(d)\n }, [])\n\n // Handle the already-loaded case (srcDoc can load before onLoad attaches).\n useEffect(() => {\n if (iframeRef.current?.contentDocument?.readyState === 'complete') wireUp()\n }, [wireUp])\n\n // Auto-height (EC-330): a same-origin iframe exposes its own document, so a\n // `ResizeObserver` on the BODY tracks its live (height: auto) content size —\n // re-firing on its own whenever the portaled render changes (preset switch,\n // prop edit), no manual re-measure wiring needed. Attached per `doc` (re-runs\n // whenever the iframe reloads), not just on mount. Loop-safety: the observed\n // element (the iframe's body) is never the element this effect resizes (the\n // IFRAME itself) — writing height never feeds back into the observed box, and\n // width is never derived from height, so there is no growth loop. Guarded for\n // environments without ResizeObserver (jsdom/tests, older embeds).\n useEffect(() => {\n const body = doc?.body\n if (!body || typeof ResizeObserver === 'undefined') return\n const measure = () => setHeight(Math.max(body.scrollHeight, MIN_PREVIEW_HEIGHT))\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(body)\n return () => observer.disconnect()\n }, [doc])\n\n return (\n <>\n <iframe\n ref={iframeRef}\n onLoad={wireUp}\n title={translate('components.detail.tab.preview')}\n style={{ width, height, maxWidth: '100%' }}\n className=\"mx-auto block rounded-lg border border-border bg-background shadow-sm\"\n srcDoc=\"<!doctype html><html><head></head><body></body></html>\"\n />\n {doc\n ? createPortal(\n <PreviewErrorBoundary displayName={displayName} resetKey={node}>\n <PreviewLiveRender node={node} ctx={ctx} />\n </PreviewErrorBoundary>,\n doc.body,\n )\n : null}\n </>\n )\n}\n\n/** Renders the node through the runtime renderer, INSIDE a component render so the\n * error boundary catches synchronous render throws (mirrors the blocks-panel\n * thumbnail's `LiveRender`). */\nfunction PreviewLiveRender({ node, ctx }: { node: ComponentNode; ctx: RenderContext }) {\n return <>{renderNode(node, ctx)}</>\n}\n\ninterface PreviewBoundaryState {\n error: Error | null\n}\n\n/**\n * EC-191 resilience for the Components view: a component whose render throws — a\n * missing provider/context (e.g. a prop control that needs a document's\n * `CollectionViewSource`), a runtime bug — shows the documented \"preview\n * unavailable\" fallback ({@link PreviewUnavailable}) instead of taking the whole\n * view down. It resets when the previewed `resetKey` (the node) changes, so\n * switching the fixture state or editing a prop retries the render.\n */\nexport class PreviewErrorBoundary extends Component<\n { children: ReactNode; displayName: string; resetKey: unknown },\n PreviewBoundaryState\n> {\n override state: PreviewBoundaryState = { error: null }\n\n static getDerivedStateFromError(error: Error): PreviewBoundaryState {\n return { error }\n }\n\n override componentDidUpdate(prev: Readonly<{ resetKey: unknown }>) {\n if (this.state.error && prev.resetKey !== this.props.resetKey) {\n this.setState({ error: null })\n }\n }\n\n override render(): ReactNode {\n if (this.state.error) {\n return <PreviewUnavailable displayName={this.props.displayName} error={this.state.error} />\n }\n return this.props.children\n }\n}\n\n/**\n * The EC-191 fallback panel — component name + \"preview unavailable\" + the error.\n * Rendered INTO the preview iframe (which carries only the project stylesheet, and\n * may carry none in tests), so it uses inline styles rather than studio UI classes\n * — matching the canvas's own in-iframe placeholder convention.\n */\nexport function PreviewUnavailable({ displayName, error }: { displayName: string; error: Error }) {\n return (\n <div\n data-preview-unavailable\n style={{\n boxSizing: 'border-box',\n maxWidth: '42rem',\n margin: '0 auto',\n border: '1px dashed rgba(120,120,120,0.5)',\n borderRadius: 8,\n padding: '24px 28px',\n background: 'rgba(127,127,127,0.06)',\n color: 'rgba(90,90,90,0.95)',\n font: '14px/1.5 system-ui, -apple-system, sans-serif',\n textAlign: 'center',\n }}\n >\n <div style={{ fontWeight: 600 }}>⚠ {translate('components.preview.unavailableTitle')}</div>\n <div style={{ marginTop: 4, fontWeight: 600, opacity: 0.9 }}>{displayName}</div>\n <div style={{ marginTop: 6, fontSize: 13, opacity: 0.85, wordBreak: 'break-word' }}>\n {error.message}\n </div>\n </div>\n )\n}\n\n/**\n * The editable Props pane (right inspector). Every non-`blocks` prop is edited\n * through the shared {@link PropValueControl} — the exact control set documents\n * use (EC-190/EC-314 phase 4) — so a change re-renders the preview immediately.\n * `blocks` props are a canvas (composition) concern, edited in a document, not here.\n */\nexport function PropsEditorPane({\n manifest,\n state,\n}: {\n manifest: ComponentManifest\n state: PreviewState\n}) {\n const editable = Object.entries(manifest.props).filter(([, spec]) => spec.type !== 'blocks')\n const fixture = state.specs[state.index]?.node\n if (editable.length === 0) {\n return (\n <Text size=\"sm\" tone=\"subtle\" className=\"p-4\">\n {translate('components.fieldInspector.noProps')}\n </Text>\n )\n }\n return (\n <Stack gap=\"4\" className=\"p-4\">\n {editable.map(([name, spec]) => {\n const id = `prop-${name}`\n const overridden = state.overrides[name] !== undefined\n return (\n <Field\n key={name}\n label={\n <Row gap=\"2\">\n <span>{localizedLabel((spec as PropField).form?.label, name)}</span>\n {overridden ? <Badge variant=\"info\">{translate('components.preview.edited')}</Badge> : null}\n </Row>\n }\n htmlFor={id}\n hint={localizedLabel((spec as PropField).form?.description) || undefined}\n >\n <PropValueControl\n id={id}\n name={name}\n spec={spec as PropField}\n value={\n overridden\n ? staticProp(state.overrides[name]!)\n : (fixture?.props?.[name] ?? undefined)\n }\n onSet={(v) => state.setOverride(name, v)}\n onClear={() => state.clearOverride(name)}\n />\n </Field>\n )\n })}\n {state.dirty ? (\n <Button variant=\"outline\" size=\"sm\" onClick={state.resetAll} className=\"self-start\">\n <RotateCcw /> {translate('components.preview.resetProps')}\n </Button>\n ) : null}\n </Stack>\n )\n}\n\n/**\n * The Slots pane (right inspector): the component's declared child regions and,\n * for a fixture that supplies slot content, a note that the preview's slot content\n * follows the selected state. Documents the pragmatic scope call — no slot-tree\n * editing on this reference surface.\n */\nexport function SlotsInfoPane({ manifest }: { manifest: ComponentManifest }) {\n if (manifest.slots.length === 0) {\n return (\n <Text size=\"sm\" tone=\"subtle\" className=\"p-4\">\n {translate('components.fieldInspector.noSlots')}\n </Text>\n )\n }\n return (\n <Stack gap=\"3\" className=\"p-4\">\n <Text size=\"xs\" tone=\"muted\">\n {translate('components.preview.slotsNote')}\n </Text>\n <div className=\"divide-y divide-border rounded-md border border-border\">\n {manifest.slots.map((slot) => (\n <div key={slot.name} className=\"flex flex-col gap-1 p-3\">\n <Row gap=\"2\">\n <Code tone=\"primary\">{slot.name}</Code>\n {slot.required ? (\n <Badge variant=\"warning\">{translate('common.required')}</Badge>\n ) : (\n <Badge variant=\"outline\">{translate('common.optional')}</Badge>\n )}\n </Row>\n {slot.allow && slot.allow.length > 0 ? (\n <Row gap=\"2\" className=\"text-xs\" wrap>\n <Text size=\"xs\" tone=\"subtle\">\n {translate('components.fieldInspector.allows')}\n </Text>\n {slot.allow.map((id) => (\n <Code key={id} tone=\"muted\">\n {id}\n </Code>\n ))}\n </Row>\n ) : null}\n </div>\n ))}\n </div>\n </Stack>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"component-preview.js","sourceRoot":"","sources":["../../../src/components/components/component-preview.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEpF,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAA;AAE7D,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAG1D,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAEhF,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AAErE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACtG,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AACtE,OAAO,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAA;AAChD,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAA;AACrE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAEjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AA0BrD,qFAAqF;AACrF,MAAM,UAAU,eAAe,CAAC,QAA2B;IACzD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC/D,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IACxC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAA4B,EAAE,CAAC,CAAA;IAEzE,mFAAmF;IACnF,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAE,EAAE;QAC7B,WAAW,CAAC,CAAC,CAAC,CAAA;QACd,YAAY,CAAC,EAAE,CAAC,CAAA;IAClB,CAAC,CAAA;IACD,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,KAAgB,EAAE,EAAE,CACrD,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;IAChD,MAAM,aAAa,GAAG,CAAC,IAAY,EAAE,EAAE,CACrC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE;QACjB,MAAM,IAAI,GAAG,EAAE,GAAG,CAAC,EAAE,CAAA;QACrB,OAAO,IAAI,CAAC,IAAI,CAAC,CAAA;QACjB,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CAAA;IACJ,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;IAEvC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAA;IAC3D,MAAM,IAAI,GAAG,OAAO,CAAgB,GAAG,EAAE;QACvC,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,EAAE,EAAE,WAAW,QAAQ,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;QAC7F,MAAM,KAAK,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAA;QAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;QACtF,OAAO,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,CAAA;IAC9B,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;IAElC,OAAO;QACL,KAAK;QACL,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACrD,QAAQ;QACR,SAAS;QACT,WAAW;QACX,aAAa;QACb,QAAQ;QACR,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC;QACxC,IAAI;KACL,CAAA;AACH,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,aAAa,CAAC,IAAmB,EAAE,EAAU;IAC3D,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAA;AACxB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,QAAQ,EACR,MAAM,EACN,KAAK,GAKN;IACC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAC3D,MAAM,UAAU,GAAG,QAAQ,CAAC,gBAAgB,KAAK,KAAK,CAAA;IACtD,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAChD,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,EAAE,CAAA;IAEjD,4EAA4E;IAC5E,6EAA6E;IAC7E,qEAAqE;IACrE,6DAA6D;IAC7D,MAAM,EAAE,SAAS,EAAE,GAAG,kBAAkB,EAAE,CAAA;IAC1C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,GAAG,CAAC,CAAA;IAE3D,OAAO,CACL,eAAK,SAAS,EAAC,8BAA8B,aAC3C,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,IAAI,QAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAC,2CAA2C,aACnF,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACxB,KAAC,GAAG,IACF,GAAG,EAAC,GAAG,EACP,IAAI,QACJ,IAAI,EAAC,OAAO,gBACA,SAAS,CAAC,qCAAqC,CAAC,YAE3D,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAC5B,KAAC,SAAS,IAAiB,MAAM,EAAE,CAAC,KAAK,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,YACnF,IAAI,CAAC,IAAI,IADI,IAAI,CAAC,IAAI,CAEb,CACb,CAAC,GACE,CACP,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,MAAM,KAAG,EACV,KAAC,GAAG,IACF,GAAG,EAAC,GAAG,EACP,IAAI,QACJ,IAAI,EAAC,OAAO,gBACA,SAAS,CAAC,mCAAmC,CAAC,YAEzD,SAAS,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CACzB,KAAC,uBAAuB,IAEtB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,GAAG,KAAK,QAAQ,EAC/B,QAAQ,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,IAHlC,MAAM,CAAC,GAAG,CAIf,CACH,CAAC,GACE,EACL,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CACb,MAAC,MAAM,IAAC,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,iBAAc,eAAe,aACpF,KAAC,SAAS,KAAG,OAAE,SAAS,CAAC,0BAA0B,CAAC,IAC7C,CACV,CAAC,CAAC,CAAC,IAAI,IACJ,EAEN,eACE,SAAS,EAAC,8CAA8C,wBACpC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,aAEjD,UAAU,CAAC,CAAC,CAAC,CACZ,KAAC,gBAAgB,KAAG,CACrB,CAAC,CAAC,CAAC,CACF,KAAC,qBAAqB,IACpB,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,GAAG,EAAE,GAAG,EACR,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,GACzC,CACH,EACA,MAAM,KAAK,cAAc,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAC1C,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,wBAAwB,YAC5D,SAAS,CAAC,qCAAqC,CAAC,GAC5C,CACR,CAAC,CAAC,CAAC,IAAI,IACJ,IACF,CACP,CAAA;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,uBAAuB,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,QAAQ,GAKT;IACC,OAAO,CACL,KAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,OAAO,gBACH,SAAS,CAAC,oCAAoC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,kBACtE,MAAM,EACpB,KAAK,EACH,MAAM,CAAC,EAAE,KAAK,SAAS;YACrB,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,MAAM,MAAM,CAAC,EAAE,IAAI;YACpC,CAAC,CAAC,SAAS,CAAC,mCAAmC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,EAE7E,OAAO,EAAE,QAAQ,EACjB,SAAS,EAAE,EAAE,CAAC,cAAc,EAAE,MAAM,IAAI,kCAAkC,CAAC,YAE1E,MAAM,CAAC,KAAK,GACN,CACV,CAAA;AACH,CAAC;AAED,SAAS,gBAAgB;IACvB,OAAO,CACL,cAAK,SAAS,EAAC,qHAAqH,YAClI,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,QAAQ,aAC3B,KAAC,UAAU,IAAC,SAAS,EAAC,8BAA8B,wBAAe,EACnE,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,QAAQ,YAC5B,SAAS,CAAC,oCAAoC,CAAC,GAC3C,EACP,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,UAAU,YAC9C,SAAS,CAAC,mCAAmC,CAAC,GAC1C,IACD,GACJ,CACP,CAAA;AACH,CAAC;AAED,0FAA0F;AAC1F,MAAM,kBAAkB,GAAG,GAAG,CAAA;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,SAAS,qBAAqB,CAAC,EAC7B,IAAI,EACJ,GAAG,EACH,WAAW,EACX,KAAK,GAON;IACC,MAAM,SAAS,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAA;IACjD,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAkB,IAAI,CAAC,CAAA;IACrD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,kBAAkB,CAAC,CAAA;IAExD,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;QAC9B,MAAM,CAAC,GAAG,SAAS,CAAC,OAAO,EAAE,eAAe,CAAA;QAC5C,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;YAAE,OAAM;QACzB,yEAAyE;QACzE,sEAAsE;QACtE,0EAA0E;QAC1E,2EAA2E;QAC3E,gBAAgB,CAAC,CAAC,EAAE;YAClB,aAAa,EAAE,sBAAsB,EAAE;YACvC,KAAK,EAAE,4BAA4B,EAAE;SACtC,CAAC,CAAA;QACF,MAAM,CAAC,CAAC,CAAC,CAAA;IACX,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,2EAA2E;IAC3E,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,CAAC,OAAO,EAAE,eAAe,EAAE,UAAU,KAAK,UAAU;YAAE,MAAM,EAAE,CAAA;IAC7E,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEZ,4EAA4E;IAC5E,6EAA6E;IAC7E,4EAA4E;IAC5E,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,8EAA8E;IAC9E,8EAA8E;IAC9E,mEAAmE;IACnE,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,IAAI,GAAG,GAAG,EAAE,IAAI,CAAA;QACtB,IAAI,CAAC,IAAI,IAAI,OAAO,cAAc,KAAK,WAAW;YAAE,OAAM;QAC1D,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC,CAAA;QAChF,OAAO,EAAE,CAAA;QACT,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAA;QAC5C,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACtB,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAA;IACpC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;IAET,OAAO,CACL,8BACE,iBACE,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,SAAS,CAAC,+BAA+B,CAAC,EACjD,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAC1C,SAAS,EAAC,uEAAuE,EACjF,MAAM,EAAC,wDAAwD,GAC/D,EACD,GAAG;gBACF,CAAC,CAAC,YAAY,CACV,KAAC,oBAAoB,IAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,YAC5D,KAAC,iBAAiB,IAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,GAAI,GACtB,EACvB,GAAG,CAAC,IAAI,CACT;gBACH,CAAC,CAAC,IAAI,IACP,CACJ,CAAA;AACH,CAAC;AAED;;iCAEiC;AACjC,SAAS,iBAAiB,CAAC,EAAE,IAAI,EAAE,GAAG,EAA+C;IACnF,OAAO,4BAAG,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,GAAI,CAAA;AACrC,CAAC;AAMD;;;;;;;GAOG;AACH,MAAM,OAAO,oBAAqB,SAAQ,SAGzC;IACU,KAAK,GAAyB,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IAEtD,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAE,KAAK,EAAE,CAAA;IAClB,CAAC;IAEQ,kBAAkB,CAAC,IAAqC;QAC/D,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC9D,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QAChC,CAAC;IACH,CAAC;IAEQ,MAAM;QACb,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACrB,OAAO,KAAC,kBAAkB,IAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAI,CAAA;QAC7F,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAA;IAC5B,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAE,WAAW,EAAE,KAAK,EAAyC;IAC9F,OAAO,CACL,iDAEE,SAAS,EAAC,WAAW,EACrB,KAAK,EAAE;YACL,SAAS,EAAE,YAAY;YACvB,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,kCAAkC;YAC1C,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,WAAW;YACpB,UAAU,EAAE,wBAAwB;YACpC,KAAK,EAAE,qBAAqB;YAC5B,IAAI,EAAE,+CAA+C;YACrD,SAAS,EAAE,QAAQ;SACpB,aAED,eAAK,KAAK,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,wBAAK,SAAS,CAAC,qCAAqC,CAAC,IAAO,EAC3F,cAAK,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,YAAG,WAAW,GAAO,EAChF,cAAK,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,YAC/E,KAAK,CAAC,OAAO,GACV,IACF,CACP,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,QAAQ,EACR,KAAK,GAIN;IACC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;IAC5F,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,CAAA;IAC9C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CACL,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAC,KAAK,YAC1C,SAAS,CAAC,mCAAmC,CAAC,GAC1C,CACR,CAAA;IACH,CAAC;IACD,OAAO,CACL,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,KAAK,aAC3B,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;gBAC7B,MAAM,EAAE,GAAG,QAAQ,IAAI,EAAE,CAAA;gBACzB,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,SAAS,CAAA;gBACtD,OAAO,CACL,KAAC,KAAK,IAEJ,KAAK,EACH,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,yBAAO,cAAc,CAAE,IAAkB,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,GAAQ,EACnE,UAAU,CAAC,CAAC,CAAC,KAAC,KAAK,IAAC,OAAO,EAAC,MAAM,YAAE,SAAS,CAAC,2BAA2B,CAAC,GAAS,CAAC,CAAC,CAAC,IAAI,IACvF,EAER,OAAO,EAAE,EAAE,EACX,IAAI,EAAE,cAAc,CAAE,IAAkB,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,SAAS,YAExE,KAAC,gBAAgB,IACf,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,IAAiB,EACvB,KAAK,EACH,UAAU;4BACR,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAE,CAAC;4BACpC,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAE3C,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,EACxC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,GACxC,IArBG,IAAI,CAsBH,CACT,CAAA;YACH,CAAC,CAAC,EACD,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CACb,MAAC,MAAM,IAAC,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAC,YAAY,aACjF,KAAC,SAAS,KAAG,OAAE,SAAS,CAAC,+BAA+B,CAAC,IAClD,CACV,CAAC,CAAC,CAAC,IAAI,IACF,CACT,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,EAAE,QAAQ,EAAmC;IACzE,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,CACL,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAC,KAAK,YAC1C,SAAS,CAAC,mCAAmC,CAAC,GAC1C,CACR,CAAA;IACH,CAAC;IACD,OAAO,CACL,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,KAAK,aAC5B,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,SAAS,CAAC,8BAA8B,CAAC,GACrC,EACP,cAAK,SAAS,EAAC,wDAAwD,YACpE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAC5B,eAAqB,SAAS,EAAC,yBAAyB,aACtD,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,KAAC,IAAI,IAAC,IAAI,EAAC,SAAS,YAAE,IAAI,CAAC,IAAI,GAAQ,EACtC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CACf,KAAC,KAAK,IAAC,OAAO,EAAC,SAAS,YAAE,SAAS,CAAC,iBAAiB,CAAC,GAAS,CAChE,CAAC,CAAC,CAAC,CACF,KAAC,KAAK,IAAC,OAAO,EAAC,SAAS,YAAE,SAAS,CAAC,iBAAiB,CAAC,GAAS,CAChE,IACG,EACL,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACrC,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,SAAS,EAAC,IAAI,mBACnC,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,YAC1B,SAAS,CAAC,kCAAkC,CAAC,GACzC,EACN,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CACtB,KAAC,IAAI,IAAU,IAAI,EAAC,OAAO,YACxB,EAAE,IADM,EAAE,CAEN,CACR,CAAC,IACE,CACP,CAAC,CAAC,CAAC,IAAI,KApBA,IAAI,CAAC,IAAI,CAqBb,CACP,CAAC,GACE,IACA,CACT,CAAA;AACH,CAAC","sourcesContent":["import { Component, useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { MonitorOff, RotateCcw } from 'lucide-react'\nimport { renderNode } from '@elytracms/core/runtime-renderer'\nimport type { RenderContext } from '@elytracms/core/runtime-renderer'\nimport { staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode, JsonValue } from '@elytracms/core/project-graph'\nimport type { ComponentManifest, PropField } from '@elytracms/core/component-registry'\nimport { componentStatus, previewContext } from '@elytracms/studio/lib/registry'\nimport type { StudioRegistry } from '@elytracms/studio/lib/registry'\nimport { previewSpecs } from '@elytracms/studio/lib/registry/preview'\nimport type { PreviewSpec } from '@elytracms/studio/lib/registry/preview'\nimport { Badge, Button, Code, Field, Row, Spacer, Stack, TabButton, Text } from '@elytracms/studio/ui'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { resolveDeploymentCanvasTheme } from '@elytracms/studio/lib/workspace'\nimport { wireCanvasChrome } from '../builder/canvas-chrome'\nimport { getCanvasStylesheetUrl } from '../builder/canvas-stylesheet'\nimport { useCanvasViewports, viewportWidth } from '../builder/canvas-breakpoints'\nimport type { ViewportOption } from '../builder/canvas-breakpoints'\nimport { PropValueControl } from '../cms/prop-fields'\n\n/**\n * EC-320 — the editable component preview. A component's declared preview states\n * (fixtures) select the SAMPLE STATE (slot content, base props); on top of that\n * the user edits individual prop values in the Props sidebar and the preview\n * re-renders live through the SAME runtime-renderer path the Builder and export\n * use (EC-054). Slots follow the selected fixture — full slot-tree editing is out\n * of scope for this reference surface (that's the composition editor's job); the\n * Slots pane documents what a component accepts and which state supplies content.\n */\n\nexport interface PreviewState {\n specs: PreviewSpec[]\n index: number\n setIndex: (i: number) => void\n /** Per-prop static-value overrides applied over the selected fixture. */\n overrides: Record<string, JsonValue>\n setOverride: (name: string, value: JsonValue) => void\n clearOverride: (name: string) => void\n resetAll: () => void\n dirty: boolean\n /** The live node: the selected fixture with prop overrides applied. */\n node: ComponentNode\n}\n\n/** Preview state for a manifest: the fixture selection + the live prop overrides. */\nexport function usePreviewState(manifest: ComponentManifest): PreviewState {\n const specs = useMemo(() => previewSpecs(manifest), [manifest])\n const [index, setIndexRaw] = useState(0)\n const [overrides, setOverrides] = useState<Record<string, JsonValue>>({})\n\n // Switching preview state resets prop edits — they were relative to the old state.\n const setIndex = (i: number) => {\n setIndexRaw(i)\n setOverrides({})\n }\n const setOverride = (name: string, value: JsonValue) =>\n setOverrides((o) => ({ ...o, [name]: value }))\n const clearOverride = (name: string) =>\n setOverrides((o) => {\n const next = { ...o }\n delete next[name]\n return next\n })\n const resetAll = () => setOverrides({})\n\n const base = specs[Math.min(index, specs.length - 1)]?.node\n const node = useMemo<ComponentNode>(() => {\n const fixture = base ?? { id: `preview-${manifest.id}`, componentId: manifest.id, props: {} }\n const props = { ...(fixture.props ?? {}) }\n for (const [name, value] of Object.entries(overrides)) props[name] = staticProp(value)\n return { ...fixture, props }\n }, [base, overrides, manifest.id])\n\n return {\n specs,\n index: Math.min(index, Math.max(specs.length - 1, 0)),\n setIndex,\n overrides,\n setOverride,\n clearOverride,\n resetAll,\n dirty: Object.keys(overrides).length > 0,\n node,\n }\n}\n\n/** Re-id a preview node into the canvas `node-<n>` namespace for embedding (transfer). */\nexport function embedNodeFrom(node: ComponentNode, id: string): ComponentNode {\n return { ...node, id }\n}\n\n/**\n * The live preview surface: the fixture-state selector (when a component declares\n * more than one) plus the rendered node. Server-only components (EC-191\n * `clientRenderable: false`) can't run on the client-rendered canvas, so they show\n * a documented fallback rather than risk a crash; a missing implementation falls\n * back through the renderer itself (the incompatible state).\n */\nexport function ComponentPreview({\n manifest,\n studio,\n state,\n}: {\n manifest: ComponentManifest\n studio: StudioRegistry\n state: PreviewState\n}) {\n const ctx = useMemo(() => previewContext(studio), [studio])\n const serverOnly = manifest.clientRenderable === false\n const status = componentStatus(studio, manifest)\n const displayName = manifest.title ?? manifest.id\n\n // EC-330: the same config-sourced breakpoint set the builder canvas toolbar\n // offers (`canvas-breakpoints.ts`) — reused, not redefined, so a preview and\n // the real canvas never disagree about the site's responsive widths.\n // Defaults to the always-present 'full' (responsive) option.\n const { viewports } = useCanvasViewports()\n const [viewport, setViewport] = useState(viewports[0]!.key)\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <Row gap=\"1\" wrap align=\"center\" className=\"shrink-0 border-b border-border px-3 py-2\">\n {state.specs.length > 1 ? (\n <Row\n gap=\"1\"\n wrap\n role=\"group\"\n aria-label={translate('components.fixtureRunner.statesAria')}\n >\n {state.specs.map((spec, i) => (\n <TabButton key={spec.name} active={i === state.index} onClick={() => state.setIndex(i)}>\n {spec.name}\n </TabButton>\n ))}\n </Row>\n ) : null}\n <Spacer />\n <Row\n gap=\"1\"\n wrap\n role=\"group\"\n aria-label={translate('components.preview.breakpointAria')}\n >\n {viewports.map((option) => (\n <PreviewBreakpointButton\n key={option.key}\n option={option}\n active={option.key === viewport}\n onSelect={() => setViewport(option.key)}\n />\n ))}\n </Row>\n {state.dirty ? (\n <Button variant=\"ghost\" size=\"sm\" onClick={state.resetAll} data-testid=\"preview-reset\">\n <RotateCcw /> {translate('components.preview.reset')}\n </Button>\n ) : null}\n </Row>\n\n <div\n className=\"canvas-grid min-h-0 flex-1 overflow-auto p-6\"\n data-fixture-state={state.specs[state.index]?.name}\n >\n {serverOnly ? (\n <ServerOnlyNotice />\n ) : (\n <ComponentPreviewFrame\n node={state.node}\n ctx={ctx}\n displayName={displayName}\n width={viewportWidth(viewports, viewport)}\n />\n )}\n {status === 'incompatible' && !serverOnly ? (\n <Text size=\"xs\" tone=\"muted\" className=\"mx-auto mt-3 max-w-2xl\">\n {translate('components.preview.incompatibleNote')}\n </Text>\n ) : null}\n </div>\n </div>\n )\n}\n\n/**\n * One breakpoint option in the preview's width switcher. Mirrors the builder\n * canvas toolbar's `ViewportButton` (`composition-field-editor.tsx`) — same\n * `Button`/active-state styling — but drives LOCAL preview state instead of\n * the builder store (this surface has no composition/undo stack).\n *\n * EC-328: evaluated against the shared `TabButton`/`SegmentedControl`\n * primitives and kept separate — it's built on the shared `Button` (not a\n * bare hand-rolled `<button>`, so it wasn't part of the duplication those\n * primitives address), and carries an interpolated `aria-label` plus a\n * conditional px-value `title` tooltip neither primitive's terse API models.\n */\nfunction PreviewBreakpointButton({\n option,\n active,\n onSelect,\n}: {\n option: ViewportOption\n active: boolean\n onSelect: () => void\n}) {\n return (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.viewportPreviewAria', { label: option.label })}\n aria-pressed={active}\n title={\n option.px !== undefined\n ? `${option.label} · ${option.px}px`\n : translate('builder.canvas.viewportWidthTitle', { label: option.label })\n }\n onClick={onSelect}\n className={cn('px-2 text-xs', active && 'bg-accent text-accent-foreground')}\n >\n {option.label}\n </Button>\n )\n}\n\nfunction ServerOnlyNotice() {\n return (\n <div className=\"mx-auto grid max-w-2xl place-items-center rounded-lg border border-dashed border-border bg-muted/30 p-8 text-center\">\n <Stack gap=\"2\" align=\"center\">\n <MonitorOff className=\"size-5 text-muted-foreground\" aria-hidden />\n <Text size=\"sm\" weight=\"medium\">\n {translate('components.preview.serverOnlyTitle')}\n </Text>\n <Text size=\"xs\" tone=\"muted\" className=\"max-w-sm\">\n {translate('components.preview.serverOnlyBody')}\n </Text>\n </Stack>\n </div>\n )\n}\n\n/** Floor for the auto-height iframe — never collapses to a sliver while empty/loading. */\nconst MIN_PREVIEW_HEIGHT = 360\n\n/**\n * The live preview render surface. Reuses the BuilderCanvas styling mechanism\n * (EC-061, `canvas.tsx`) — since EC-414 through the SAME code path, `wireCanvasChrome`\n * (closing the EC-389 follow-up): the component renders inside an ISOLATED\n * same-origin iframe that `<link>`s the project's canvas stylesheet\n * (`getCanvasStylesheetUrl()` — the consumer's theme entry; in the legacy merged\n * world also the studio CSS), so a component built with the site's Tailwind theme\n * previews with the site's styles, exactly as it does on the canvas and in\n * delivery. Using the iframe (not the studio DOM) is what keeps the delivery CSS\n * scoped and off the studio chrome.\n *\n * Unlike the builder canvas, this iframe's document is deliberately left OUT of\n * the studio's own dark/light class: this is a standalone-component reference\n * surface (not a full page), so its own negative space around the component must\n * read as a light, site-like backdrop — never the studio chrome's current theme\n * (a black frame is exactly what leaks through when the studio is dark-themed and\n * the iframe root inherits that class). The chrome's world probe supplies that\n * backdrop: the neutral backdrop rules in the new world, the studio `:root`\n * (light) tokens in the legacy one.\n *\n * Sizing: width is driven by the caller (the breakpoint selector); height is\n * measured live from the rendered content via `ResizeObserver` (EC-330) so a\n * preset switch or a prop edit — both of which change what's portaled into\n * `doc.body` — always show without clipping or dead space.\n *\n * The render is wrapped in {@link PreviewErrorBoundary} so a component that throws\n * shows the EC-191 fallback instead of taking the whole Components view down.\n */\nfunction ComponentPreviewFrame({\n node,\n ctx,\n displayName,\n width,\n}: {\n node: ComponentNode\n ctx: RenderContext\n displayName: string\n /** CSS width for the iframe — '100%' (responsive) or a fixed breakpoint px width. */\n width: string\n}) {\n const iframeRef = useRef<HTMLIFrameElement>(null)\n const [doc, setDoc] = useState<Document | null>(null)\n const [height, setHeight] = useState(MIN_PREVIEW_HEIGHT)\n\n const wireUp = useCallback(() => {\n const d = iframeRef.current?.contentDocument\n if (!d || !d.body) return\n // ONE code path with the builder canvas (EC-414 AD-B item 4): stylesheet\n // link + chrome css + consumer canvas theme + the world probe. No kit\n // tokens here — this is a registry-level reference surface, not a project\n // page. The dev theme notice is the builder canvas's job, not this pane's.\n wireCanvasChrome(d, {\n stylesheetUrl: getCanvasStylesheetUrl(),\n theme: resolveDeploymentCanvasTheme(),\n })\n setDoc(d)\n }, [])\n\n // Handle the already-loaded case (srcDoc can load before onLoad attaches).\n useEffect(() => {\n if (iframeRef.current?.contentDocument?.readyState === 'complete') wireUp()\n }, [wireUp])\n\n // Auto-height (EC-330): a same-origin iframe exposes its own document, so a\n // `ResizeObserver` on the BODY tracks its live (height: auto) content size —\n // re-firing on its own whenever the portaled render changes (preset switch,\n // prop edit), no manual re-measure wiring needed. Attached per `doc` (re-runs\n // whenever the iframe reloads), not just on mount. Loop-safety: the observed\n // element (the iframe's body) is never the element this effect resizes (the\n // IFRAME itself) — writing height never feeds back into the observed box, and\n // width is never derived from height, so there is no growth loop. Guarded for\n // environments without ResizeObserver (jsdom/tests, older embeds).\n useEffect(() => {\n const body = doc?.body\n if (!body || typeof ResizeObserver === 'undefined') return\n const measure = () => setHeight(Math.max(body.scrollHeight, MIN_PREVIEW_HEIGHT))\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(body)\n return () => observer.disconnect()\n }, [doc])\n\n return (\n <>\n <iframe\n ref={iframeRef}\n onLoad={wireUp}\n title={translate('components.detail.tab.preview')}\n style={{ width, height, maxWidth: '100%' }}\n className=\"mx-auto block rounded-lg border border-border bg-background shadow-sm\"\n srcDoc=\"<!doctype html><html><head></head><body></body></html>\"\n />\n {doc\n ? createPortal(\n <PreviewErrorBoundary displayName={displayName} resetKey={node}>\n <PreviewLiveRender node={node} ctx={ctx} />\n </PreviewErrorBoundary>,\n doc.body,\n )\n : null}\n </>\n )\n}\n\n/** Renders the node through the runtime renderer, INSIDE a component render so the\n * error boundary catches synchronous render throws (mirrors the blocks-panel\n * thumbnail's `LiveRender`). */\nfunction PreviewLiveRender({ node, ctx }: { node: ComponentNode; ctx: RenderContext }) {\n return <>{renderNode(node, ctx)}</>\n}\n\ninterface PreviewBoundaryState {\n error: Error | null\n}\n\n/**\n * EC-191 resilience for the Components view: a component whose render throws — a\n * missing provider/context (e.g. a prop control that needs a document's\n * `CollectionViewSource`), a runtime bug — shows the documented \"preview\n * unavailable\" fallback ({@link PreviewUnavailable}) instead of taking the whole\n * view down. It resets when the previewed `resetKey` (the node) changes, so\n * switching the fixture state or editing a prop retries the render.\n */\nexport class PreviewErrorBoundary extends Component<\n { children: ReactNode; displayName: string; resetKey: unknown },\n PreviewBoundaryState\n> {\n override state: PreviewBoundaryState = { error: null }\n\n static getDerivedStateFromError(error: Error): PreviewBoundaryState {\n return { error }\n }\n\n override componentDidUpdate(prev: Readonly<{ resetKey: unknown }>) {\n if (this.state.error && prev.resetKey !== this.props.resetKey) {\n this.setState({ error: null })\n }\n }\n\n override render(): ReactNode {\n if (this.state.error) {\n return <PreviewUnavailable displayName={this.props.displayName} error={this.state.error} />\n }\n return this.props.children\n }\n}\n\n/**\n * The EC-191 fallback panel — component name + \"preview unavailable\" + the error.\n * Rendered INTO the preview iframe (which carries only the project stylesheet, and\n * may carry none in tests), so it uses inline styles rather than studio UI classes\n * — the EC-414 AD-B chrome contract, with the `.ec-chrome` containment recipe on\n * its root (walked by the chrome contract test).\n */\nexport function PreviewUnavailable({ displayName, error }: { displayName: string; error: Error }) {\n return (\n <div\n data-preview-unavailable\n className=\"ec-chrome\"\n style={{\n boxSizing: 'border-box',\n maxWidth: '42rem',\n margin: '0 auto',\n border: '1px dashed rgba(120,120,120,0.5)',\n borderRadius: 8,\n padding: '24px 28px',\n background: 'rgba(127,127,127,0.06)',\n color: 'rgba(90,90,90,0.95)',\n font: '14px/1.5 system-ui, -apple-system, sans-serif',\n textAlign: 'center',\n }}\n >\n <div style={{ fontWeight: 600 }}>⚠ {translate('components.preview.unavailableTitle')}</div>\n <div style={{ marginTop: 4, fontWeight: 600, opacity: 0.9 }}>{displayName}</div>\n <div style={{ marginTop: 6, fontSize: 13, opacity: 0.85, wordBreak: 'break-word' }}>\n {error.message}\n </div>\n </div>\n )\n}\n\n/**\n * The editable Props pane (right inspector). Every non-`blocks` prop is edited\n * through the shared {@link PropValueControl} — the exact control set documents\n * use (EC-190/EC-314 phase 4) — so a change re-renders the preview immediately.\n * `blocks` props are a canvas (composition) concern, edited in a document, not here.\n */\nexport function PropsEditorPane({\n manifest,\n state,\n}: {\n manifest: ComponentManifest\n state: PreviewState\n}) {\n const editable = Object.entries(manifest.props).filter(([, spec]) => spec.type !== 'blocks')\n const fixture = state.specs[state.index]?.node\n if (editable.length === 0) {\n return (\n <Text size=\"sm\" tone=\"subtle\" className=\"p-4\">\n {translate('components.fieldInspector.noProps')}\n </Text>\n )\n }\n return (\n <Stack gap=\"4\" className=\"p-4\">\n {editable.map(([name, spec]) => {\n const id = `prop-${name}`\n const overridden = state.overrides[name] !== undefined\n return (\n <Field\n key={name}\n label={\n <Row gap=\"2\">\n <span>{localizedLabel((spec as PropField).form?.label, name)}</span>\n {overridden ? <Badge variant=\"info\">{translate('components.preview.edited')}</Badge> : null}\n </Row>\n }\n htmlFor={id}\n hint={localizedLabel((spec as PropField).form?.description) || undefined}\n >\n <PropValueControl\n id={id}\n name={name}\n spec={spec as PropField}\n value={\n overridden\n ? staticProp(state.overrides[name]!)\n : (fixture?.props?.[name] ?? undefined)\n }\n onSet={(v) => state.setOverride(name, v)}\n onClear={() => state.clearOverride(name)}\n />\n </Field>\n )\n })}\n {state.dirty ? (\n <Button variant=\"outline\" size=\"sm\" onClick={state.resetAll} className=\"self-start\">\n <RotateCcw /> {translate('components.preview.resetProps')}\n </Button>\n ) : null}\n </Stack>\n )\n}\n\n/**\n * The Slots pane (right inspector): the component's declared child regions and,\n * for a fixture that supplies slot content, a note that the preview's slot content\n * follows the selected state. Documents the pragmatic scope call — no slot-tree\n * editing on this reference surface.\n */\nexport function SlotsInfoPane({ manifest }: { manifest: ComponentManifest }) {\n if (manifest.slots.length === 0) {\n return (\n <Text size=\"sm\" tone=\"subtle\" className=\"p-4\">\n {translate('components.fieldInspector.noSlots')}\n </Text>\n )\n }\n return (\n <Stack gap=\"3\" className=\"p-4\">\n <Text size=\"xs\" tone=\"muted\">\n {translate('components.preview.slotsNote')}\n </Text>\n <div className=\"divide-y divide-border rounded-md border border-border\">\n {manifest.slots.map((slot) => (\n <div key={slot.name} className=\"flex flex-col gap-1 p-3\">\n <Row gap=\"2\">\n <Code tone=\"primary\">{slot.name}</Code>\n {slot.required ? (\n <Badge variant=\"warning\">{translate('common.required')}</Badge>\n ) : (\n <Badge variant=\"outline\">{translate('common.optional')}</Badge>\n )}\n </Row>\n {slot.allow && slot.allow.length > 0 ? (\n <Row gap=\"2\" className=\"text-xs\" wrap>\n <Text size=\"xs\" tone=\"subtle\">\n {translate('components.fieldInspector.allows')}\n </Text>\n {slot.allow.map((id) => (\n <Code key={id} tone=\"muted\">\n {id}\n </Code>\n ))}\n </Row>\n ) : null}\n </div>\n ))}\n </div>\n </Stack>\n )\n}\n"]}
|
|
@@ -885,6 +885,10 @@ export const deMessages = {
|
|
|
885
885
|
'builder.canvas.undo': 'Rückgängig',
|
|
886
886
|
'builder.canvas.redo': 'Wiederholen',
|
|
887
887
|
'builder.canvas.insert': 'Einfügen',
|
|
888
|
+
'builder.canvasTheme.legacyNotice': 'Die canvas.css dieses Projekts bündelt noch das Studio-Designsystem mit dem Site-Theme (die alte zusammengeführte Form). Richten Sie canvas.css stattdessen auf den eigenen Full-Tailwind-Theme-Einstieg der Site — siehe Theming-Leitfaden (docs/theming.md). Bis dahin funktioniert alles weiter.',
|
|
889
|
+
'builder.canvasTheme.stylesheetErrorNotice': 'Das Canvas-Stylesheet konnte nicht geladen werden — Blöcke werden ungestylt dargestellt. Prüfen Sie den canvas.css-Build des Studios.',
|
|
890
|
+
'builder.canvasTheme.noThemeTokensNotice': 'Das Canvas-Stylesheet definiert keine Theme-Tokens. canvas.css sollte den Full-Tailwind-Theme-Einstieg der Site importieren (siehe docs/theming.md).',
|
|
891
|
+
'builder.canvasTheme.noStylesheetNotice': 'Es ist kein Canvas-Stylesheet konfiguriert — Blöcke werden ungestylt dargestellt. Die App muss eines über setCanvasStylesheetUrl bereitstellen (das create-elytra-Template verdrahtet das).',
|
|
888
892
|
'builder.spacing.gapAfterChild': 'Abstand nach Element {index}',
|
|
889
893
|
'builder.richText.toolbarLabel': 'Textformatierung',
|
|
890
894
|
'builder.richText.linkPlaceholder': 'https://… (leer entfernt)',
|