@elytracms/studio 0.0.90 → 0.0.91
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/convex/asset-revalidate.test.ts +14 -1
- package/convex/cms.ts +3 -4
- package/convex/document-revalidate.test.ts +14 -1
- package/convex/redirect-revalidate.test.ts +14 -1
- package/dist/components/assets/asset-selection.d.ts +8 -9
- package/dist/components/assets/asset-selection.js +26 -16
- package/dist/components/assets/asset-selection.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +7 -5
- package/dist/components/builder/blocks-model.js +11 -9
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.js +1 -1
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +11 -3
- package/dist/components/builder/builder-store.js +12 -5
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +7 -1
- package/dist/components/builder/canvas-dnd.js +6 -3
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +13 -1
- package/dist/components/builder/canvas-render-context.js +28 -18
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas.js +1 -1
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +0 -6
- package/dist/components/builder/clipboard-actions.js +9 -4
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +22 -0
- package/dist/components/builder/composition-field-editor.js +47 -8
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/inline-edit.d.ts +7 -4
- package/dist/components/builder/inline-edit.js +11 -8
- package/dist/components/builder/inline-edit.js.map +1 -1
- package/dist/components/builder/insert-context.js +9 -5
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/layout-inspector.d.ts +2 -2
- package/dist/components/builder/layout-inspector.js +8 -6
- package/dist/components/builder/layout-inspector.js.map +1 -1
- package/dist/components/builder/node-factory.d.ts +31 -14
- package/dist/components/builder/node-factory.js +73 -93
- package/dist/components/builder/node-factory.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +3 -1
- package/dist/components/builder/page-tree.js +6 -4
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/property-inspector.js +2 -1
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/reuse.d.ts +1 -1
- package/dist/components/builder/reuse.js +8 -5
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/builder/selection-overlay.d.ts +2 -2
- package/dist/components/builder/selection-overlay.js +3 -3
- package/dist/components/builder/selection-overlay.js.map +1 -1
- package/dist/components/builder/spacing-handles.d.ts +2 -2
- package/dist/components/builder/spacing-handles.js +3 -3
- package/dist/components/builder/spacing-handles.js.map +1 -1
- package/dist/components/cms/asset-field.d.ts +1 -1
- package/dist/components/cms/asset-field.js +15 -13
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker-core.js +3 -4
- package/dist/components/cms/asset-picker-core.js.map +1 -1
- package/dist/components/cms/detail-editor.js +10 -5
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/discard-draft.d.ts +1 -2
- package/dist/components/cms/discard-draft.js +1 -1
- package/dist/components/cms/discard-draft.js.map +1 -1
- package/dist/components/cms/editable-cell.js +11 -7
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/field-control-lib.d.ts +10 -0
- package/dist/components/cms/field-control-lib.js +20 -0
- package/dist/components/cms/field-control-lib.js.map +1 -1
- package/dist/components/cms/field-control.js +3 -3
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/object-array-field.js +3 -5
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/prop-fields.d.ts +16 -3
- package/dist/components/cms/prop-fields.js +57 -12
- package/dist/components/cms/prop-fields.js.map +1 -1
- package/dist/components/cms/rich-text/embed-props.d.ts +6 -1
- package/dist/components/cms/rich-text/embed-props.js +7 -12
- package/dist/components/cms/rich-text/embed-props.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +13 -6
- package/dist/components/cms/rich-text/insert.js +16 -17
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/use-cell-edit.js +7 -12
- package/dist/components/cms/use-cell-edit.js.map +1 -1
- package/dist/components/cms/use-discard-controller.d.ts +1 -2
- package/dist/components/cms/use-discard-controller.js +1 -2
- package/dist/components/cms/use-discard-controller.js.map +1 -1
- package/dist/components/cms/versions-pane.js +2 -2
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/components/component-detail.d.ts +1 -1
- package/dist/components/components/component-detail.js +2 -4
- package/dist/components/components/component-detail.js.map +1 -1
- package/dist/components/components/component-preview.js +9 -4
- package/dist/components/components/component-preview.js.map +1 -1
- package/dist/components/components/components-index.d.ts +2 -2
- package/dist/components/components/components-index.js +3 -6
- package/dist/components/components/components-index.js.map +1 -1
- package/dist/components/components/field-inspector.js +5 -2
- package/dist/components/components/field-inspector.js.map +1 -1
- package/dist/components/components/registry-browser.d.ts +6 -5
- package/dist/components/components/registry-browser.js +11 -37
- package/dist/components/components/registry-browser.js.map +1 -1
- package/dist/components/components/taxonomy.d.ts +14 -21
- package/dist/components/components/taxonomy.js +5 -29
- package/dist/components/components/taxonomy.js.map +1 -1
- package/dist/components/components/transfer.d.ts +10 -8
- package/dist/components/components/transfer.js +17 -13
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/lib/ai/constraints.d.ts +16 -14
- package/dist/lib/ai/constraints.js +64 -48
- package/dist/lib/ai/constraints.js.map +1 -1
- package/dist/lib/ai/update-fields.js +5 -3
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/assets/asset-placements.d.ts +3 -10
- package/dist/lib/assets/asset-placements.js +17 -8
- package/dist/lib/assets/asset-placements.js.map +1 -1
- package/dist/lib/clipboard/paste-target.js +3 -3
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms-views/view-model.js +11 -8
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/data/collection-content.d.ts +10 -7
- package/dist/lib/data/collection-content.js +34 -15
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/dnd/drop-rules.d.ts +22 -7
- package/dist/lib/dnd/drop-rules.js +21 -14
- package/dist/lib/dnd/drop-rules.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +25 -9
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +21 -7
- package/dist/lib/i18n/messages.en.js +28 -9
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/cms-persistence.js +7 -4
- package/dist/lib/operations/cms-persistence.js.map +1 -1
- package/dist/lib/persona/labels.d.ts +4 -9
- package/dist/lib/persona/labels.js +5 -12
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/references/reference-model.d.ts +0 -2
- package/dist/lib/references/reference-model.js +3 -5
- package/dist/lib/references/reference-model.js.map +1 -1
- package/dist/lib/registry/__fixtures__/demo-components.d.ts +32 -0
- package/dist/lib/registry/{project-components.js → __fixtures__/demo-components.js} +18 -5
- package/dist/lib/registry/__fixtures__/demo-components.js.map +1 -0
- package/dist/lib/registry/index.d.ts +9 -13
- package/dist/lib/registry/index.js +34 -61
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/registry/preview.d.ts +22 -7
- package/dist/lib/registry/preview.js +26 -89
- package/dist/lib/registry/preview.js.map +1 -1
- package/dist/previews/enumerate.d.ts +23 -27
- package/dist/previews/enumerate.js +27 -48
- package/dist/previews/enumerate.js.map +1 -1
- package/dist/previews/harness.js +1 -1
- package/dist/previews/harness.js.map +1 -1
- package/package.json +4 -3
- package/dist/lib/registry/project-components.d.ts +0 -16
- package/dist/lib/registry/project-components.js.map +0 -1
|
@@ -1,72 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { projectComponentImplementations, projectComponentManifests } from './project-components.js';
|
|
1
|
+
import { manifestPropFields } from '@elytracms/core/component-registry';
|
|
2
|
+
import { getImplementation, registerComponents } from '@elytracms/core/runtime-renderer';
|
|
3
|
+
import { resolveDeploymentComponents } from '../workspace/index.js';
|
|
5
4
|
/**
|
|
6
|
-
* EC-
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* EC-194/EC-208/EC-223, reshaped by EC-591: the consuming repo's component list,
|
|
6
|
+
* INJECTED through config-as-code (the mount contract) — the active project's
|
|
7
|
+
* `components`, the live (manifest + implementation) pairs its `elytra.config.ts`
|
|
8
|
+
* hands in (the components Elytra ships are an opt-in package listed there:
|
|
9
|
+
* `[...elytraComponents, ...siteComponents]`). The studio registers NOTHING of its
|
|
10
|
+
* own: no base primitives and no demo fallback. A project that lists no components
|
|
11
|
+
* gets an empty palette — the canvas says why (its role notices) — never a set of
|
|
12
|
+
* demo components the delivery host would render as missing-component fallbacks
|
|
13
|
+
* (the EC-591 worst row).
|
|
14
|
+
*
|
|
15
|
+
* The list goes through core's ONE registration step `registerComponents` — the
|
|
16
|
+
* same call the Next host and the preview harness make — so a duplicate id, a role
|
|
17
|
+
* claim or an `invalid-implementation` is the identical issue on every surface, in
|
|
18
|
+
* `registry.issues` (the one record the status readers below consult).
|
|
12
19
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
issues: [],
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
/** Compose a StudioRegistry from the base primitives plus a project component set. */
|
|
26
|
-
function buildStudioRegistry(project) {
|
|
27
|
-
const base = basePrimitives();
|
|
28
|
-
// Inline-editable opt-ins (EC-158) live directly on the base-primitive
|
|
29
|
-
// manifests (`Heading.text`, `Text.value`) in `@elytracms/core/runtime-renderer`.
|
|
30
|
-
const registry = new ComponentRegistry([...base.manifests, ...project.manifests]);
|
|
31
|
-
// EC-555: implementation issues join the registry's OWN list, because that is
|
|
32
|
-
// the one list every studio status reader consults — `componentStatus` below
|
|
33
|
-
// (→ 'invalid'), the Components surface, and the blocks panel tile, which
|
|
34
|
-
// prints each issue's message. A second list would be a second reader.
|
|
35
|
-
registry.issues.push(...project.issues);
|
|
36
|
-
return {
|
|
37
|
-
registry,
|
|
38
|
-
// Project wins over base by spread (a project entry under a `base.*` id
|
|
39
|
-
// replaces the primitive; a WITHHELD one leaves the primitive in place).
|
|
40
|
-
implementations: { ...base.implementations, ...project.implementations },
|
|
41
|
-
};
|
|
20
|
+
let componentsForTests = null;
|
|
21
|
+
/**
|
|
22
|
+
* Test seam: register this list instead of the active project's config list
|
|
23
|
+
* (`null` restores the config). Never used by app code — the studio's components
|
|
24
|
+
* come from `elytra.config.ts`.
|
|
25
|
+
*/
|
|
26
|
+
export function setStudioComponentsForTests(entries) {
|
|
27
|
+
componentsForTests = entries;
|
|
28
|
+
cached = null;
|
|
42
29
|
}
|
|
30
|
+
const NO_COMPONENTS = [];
|
|
43
31
|
let cached = null;
|
|
44
|
-
let
|
|
32
|
+
let cachedList = null;
|
|
45
33
|
export function getStudioRegistry() {
|
|
46
|
-
// Re-derive when the
|
|
47
|
-
//
|
|
48
|
-
const
|
|
49
|
-
if (cached &&
|
|
34
|
+
// Re-derive when the list changes — a different active project (per-project
|
|
35
|
+
// registry, AD-8) or a re-mounted config hands over a different list.
|
|
36
|
+
const list = componentsForTests ?? resolveDeploymentComponents() ?? NO_COMPONENTS;
|
|
37
|
+
if (cached && cachedList === list)
|
|
50
38
|
return cached;
|
|
51
|
-
cached =
|
|
52
|
-
|
|
39
|
+
cached = registerComponents(list);
|
|
40
|
+
cachedList = list;
|
|
53
41
|
return cached;
|
|
54
42
|
}
|
|
55
|
-
/**
|
|
56
|
-
* The built-in demo fixtures as a StudioRegistry, independent of the active
|
|
57
|
-
* project's `components` config ref. This is the golden-path component set
|
|
58
|
-
* (`project.MarketingHero`, `FeatureCard`, `PricingCallout`, `LegacyBanner`) used
|
|
59
|
-
* when a project declares no ref — and the deterministic registry that blocks /
|
|
60
|
-
* clipboard / thumbnail tests pin to, so they assert against a known component set
|
|
61
|
-
* regardless of which library `elytra.config.ts` happens to select (EC-194).
|
|
62
|
-
*/
|
|
63
|
-
export function fixtureStudioRegistry() {
|
|
64
|
-
return buildStudioRegistry({
|
|
65
|
-
manifests: projectComponentManifests,
|
|
66
|
-
implementations: projectComponentImplementations,
|
|
67
|
-
issues: [],
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
43
|
/**
|
|
71
44
|
* Adapt the live registry to the cms-core {@link PropFieldLookup} contract (EC-277):
|
|
72
45
|
* a component id → its prop field-defs, name-injected. Threaded into the studio's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/registry/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAG1F,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAA;AAE7G,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAA;AAChF,OAAO,EAAE,+BAA+B,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAA;AAgCjG;;;;;;;GAOG;AACH,SAAS,iBAAiB;IACxB,MAAM,UAAU,GAAG,2BAA2B,EAAE,CAAA;IAChD,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAA;QACvE,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,eAAe,EAAE,MAAM,EAAE,CAAA;IAClF,CAAC;IACD,OAAO;QACL,SAAS,EAAE,yBAAyB;QACpC,eAAe,EAAE,+BAA+B;QAChD,MAAM,EAAE,EAAE;KACX,CAAA;AACH,CAAC;AAED,sFAAsF;AACtF,SAAS,mBAAmB,CAAC,OAA4B;IACvD,MAAM,IAAI,GAAG,cAAc,EAAE,CAAA;IAC7B,uEAAuE;IACvE,kFAAkF;IAClF,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAA;IACjF,8EAA8E;IAC9E,6EAA6E;IAC7E,0EAA0E;IAC1E,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACvC,OAAO;QACL,QAAQ;QACR,wEAAwE;QACxE,yEAAyE;QACzE,eAAe,EAAE,EAAE,GAAG,IAAI,CAAC,eAAe,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE;KACzE,CAAA;AACH,CAAC;AAED,IAAI,MAAM,GAA0B,IAAI,CAAA;AACxC,IAAI,UAAU,GAAkB,IAAI,CAAA;AAEpC,MAAM,UAAU,iBAAiB;IAC/B,8EAA8E;IAC9E,8DAA8D;IAC9D,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAA;IACnC,IAAI,MAAM,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IAChD,MAAM,GAAG,mBAAmB,CAAC,iBAAiB,EAAE,CAAC,CAAA;IACjD,UAAU,GAAG,IAAI,CAAA;IACjB,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,mBAAmB,CAAC;QACzB,SAAS,EAAE,yBAAyB;QACpC,eAAe,EAAE,+BAA+B;QAChD,MAAM,EAAE,EAAE;KACX,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,EAAE,QAAQ,EAAE,GAAG,iBAAiB,EAAE,CAAA;IACxC,OAAO,CAAC,WAAW,EAAE,EAAE;QACrB,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;QAClD,IAAI,CAAC,QAAQ;YAAE,OAAO,SAAS,CAAA;QAC/B,OAAO,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;IAC7F,CAAC,CAAA;AACH,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,CAAA;AAC/E,CAAC;AAYD,MAAM,UAAU,eAAe,CAC7B,MAAsB,EACtB,QAA2B;IAE3B,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAA;IACvF,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,cAAc,CAAA;IAClF,OAAO,OAAO,CAAA;AAChB,CAAC;AASD,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;IAClC,MAAM,MAAM,GAAmB,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAA;IAC3F,KAAK,MAAM,CAAC,IAAI,GAAG;QAAE,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IAC5D,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import { ComponentRegistry, manifestPropFields } from '@elytracms/core/component-registry'\nimport type { ComponentIssue, ComponentManifest } from '@elytracms/core/component-registry'\nimport type { PropFieldLookup } from '@elytracms/core/cms-core'\nimport { basePrimitives, getImplementation, registerImplementations } from '@elytracms/core/runtime-renderer'\nimport type { ComponentImplementations, RenderContext } from '@elytracms/core/runtime-renderer'\nimport { getActiveProjectSlug, resolveDeploymentComponents } from '../workspace'\nimport { projectComponentImplementations, projectComponentManifests } from './project-components'\n\n/**\n * The Studio's view of a project's component registry (brief §7.2, §8.3): the\n * platform primitives plus the project's own components, with their\n * implementations for live previews. Deterministic singleton, built in-process\n * from code-owned manifests — component manifests live in TS next to the\n * components, so local/CI/browser runs need no backend.\n */\nexport interface StudioRegistry {\n registry: ComponentRegistry\n implementations: ComponentImplementations\n}\n\n/**\n * EC-194/EC-208/EC-223: the consuming repo's OWN real components, INJECTED through\n * config-as-code (the mount contract). The active project's `components` field holds\n * the live (manifest + implementation) pairs the consumer's `elytra.config.ts` handed\n * in by dependency injection — for Aurora these live in\n * `apps/example-site/components/marketing` (Next-free, so they render identically in\n * the studio canvas and the host). No `@site` alias and no static codegen seam: the\n * registry reads the live set off the resolved config. Absent any, the studio falls\n * back to the built-in demo fixtures (golden path).\n */\n/** A project component set, ready to layer over the base primitives. */\ninterface ProjectComponentSet {\n manifests: ComponentManifest[]\n implementations: ComponentImplementations\n /** Registration issues the registry itself cannot see (`invalid-implementation`, EC-555). */\n issues: readonly ComponentIssue[]\n}\n\n/**\n * EC-555: the config's implementations are `unknown` (`HostComponentEntry`), so\n * they go through core's one registration step — the same one the Next host\n * uses (EC-541) — instead of a cast: a non-component is withheld (its nodes\n * take the renderer's missing-component path, never `createElement`) and\n * recorded as an `invalid-implementation` issue. The fixture set below is\n * engine-owned and typed as components, so it needs no runtime check.\n */\nfunction projectComponents(): ProjectComponentSet {\n const components = resolveDeploymentComponents()\n if (components && components.length > 0) {\n const { implementations, issues } = registerImplementations(components)\n return { manifests: components.map((c) => c.manifest), implementations, issues }\n }\n return {\n manifests: projectComponentManifests,\n implementations: projectComponentImplementations,\n issues: [],\n }\n}\n\n/** Compose a StudioRegistry from the base primitives plus a project component set. */\nfunction buildStudioRegistry(project: ProjectComponentSet): StudioRegistry {\n const base = basePrimitives()\n // Inline-editable opt-ins (EC-158) live directly on the base-primitive\n // manifests (`Heading.text`, `Text.value`) in `@elytracms/core/runtime-renderer`.\n const registry = new ComponentRegistry([...base.manifests, ...project.manifests])\n // EC-555: implementation issues join the registry's OWN list, because that is\n // the one list every studio status reader consults — `componentStatus` below\n // (→ 'invalid'), the Components surface, and the blocks panel tile, which\n // prints each issue's message. A second list would be a second reader.\n registry.issues.push(...project.issues)\n return {\n registry,\n // Project wins over base by spread (a project entry under a `base.*` id\n // replaces the primitive; a WITHHELD one leaves the primitive in place).\n implementations: { ...base.implementations, ...project.implementations },\n }\n}\n\nlet cached: StudioRegistry | null = null\nlet cachedSlug: string | null = null\n\nexport function getStudioRegistry(): StudioRegistry {\n // Re-derive when the active project changes — different projects may register\n // different component libraries (per-project registry, AD-8).\n const slug = getActiveProjectSlug()\n if (cached && cachedSlug === slug) return cached\n cached = buildStudioRegistry(projectComponents())\n cachedSlug = slug\n return cached\n}\n\n/**\n * The built-in demo fixtures as a StudioRegistry, independent of the active\n * project's `components` config ref. This is the golden-path component set\n * (`project.MarketingHero`, `FeatureCard`, `PricingCallout`, `LegacyBanner`) used\n * when a project declares no ref — and the deterministic registry that blocks /\n * clipboard / thumbnail tests pin to, so they assert against a known component set\n * regardless of which library `elytra.config.ts` happens to select (EC-194).\n */\nexport function fixtureStudioRegistry(): StudioRegistry {\n return buildStudioRegistry({\n manifests: projectComponentManifests,\n implementations: projectComponentImplementations,\n issues: [],\n })\n}\n\n/**\n * Adapt the live registry to the cms-core {@link PropFieldLookup} contract (EC-277):\n * a component id → its prop field-defs, name-injected. Threaded into the studio's\n * write-time self-typing lens so relation/asset props inside compositions are\n * self-typed on write (\"schema leads, component adopts\"). Manifests are code (AD-11);\n * persistence/cms-core never depend on the registry — the studio supplies this shape.\n */\nexport function studioManifestLookup(): PropFieldLookup {\n const { registry } = getStudioRegistry()\n return (componentId) => {\n const manifest = registry.getManifest(componentId)\n if (!manifest) return undefined\n return Object.fromEntries(manifestPropFields(manifest).map((field) => [field.name, field]))\n }\n}\n\n/** A RenderContext for fixture previews (static props only; no asset/relation resolvers). */\nexport function previewContext(studio: StudioRegistry): RenderContext {\n return { registry: studio.registry, implementations: studio.implementations }\n}\n\n/**\n * Per-component status (brief §4.11, §8.3, EC-055):\n * - `invalid`: the registry reported a structured issue for this id — including\n * an `invalid-implementation` (EC-555: the entry HAS an implementation, it is\n * just not a component, so it is not merely missing).\n * - `incompatible`: a manifest exists but no implementation is wired up.\n * - `valid`: healthy.\n */\nexport type ComponentStatus = 'valid' | 'incompatible' | 'invalid'\n\nexport function componentStatus(\n studio: StudioRegistry,\n manifest: ComponentManifest,\n): ComponentStatus {\n if (studio.registry.issues.some((i) => i.componentId === manifest.id)) return 'invalid'\n if (!getImplementation(studio.implementations, manifest.id)) return 'incompatible'\n return 'valid'\n}\n\nexport interface RegistryHealth {\n total: number\n valid: number\n incompatible: number\n invalid: number\n}\n\nexport function registryHealth(studio: StudioRegistry): RegistryHealth {\n const all = studio.registry.list()\n const health: RegistryHealth = { total: all.length, valid: 0, incompatible: 0, invalid: 0 }\n for (const m of all) health[componentStatus(studio, m)] += 1\n return health\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/registry/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAGvE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAExF,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAA;AAc1D;;;;;;;;;;;;;;;GAeG;AACH,IAAI,kBAAkB,GAAqC,IAAI,CAAA;AAE/D;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,OAAyC;IACnF,kBAAkB,GAAG,OAAO,CAAA;IAC5B,MAAM,GAAG,IAAI,CAAA;AACf,CAAC;AAED,MAAM,aAAa,GAA8B,EAAE,CAAA;AACnD,IAAI,MAAM,GAA0B,IAAI,CAAA;AACxC,IAAI,UAAU,GAAqC,IAAI,CAAA;AAEvD,MAAM,UAAU,iBAAiB;IAC/B,4EAA4E;IAC5E,sEAAsE;IACtE,MAAM,IAAI,GAAG,kBAAkB,IAAI,2BAA2B,EAAE,IAAI,aAAa,CAAA;IACjF,IAAI,MAAM,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IAChD,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAA;IACjC,UAAU,GAAG,IAAI,CAAA;IACjB,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,EAAE,QAAQ,EAAE,GAAG,iBAAiB,EAAE,CAAA;IACxC,OAAO,CAAC,WAAW,EAAE,EAAE;QACrB,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;QAClD,IAAI,CAAC,QAAQ;YAAE,OAAO,SAAS,CAAA;QAC/B,OAAO,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;IAC7F,CAAC,CAAA;AACH,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,CAAA;AAC/E,CAAC;AAYD,MAAM,UAAU,eAAe,CAC7B,MAAsB,EACtB,QAA2B;IAE3B,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAA;IACvF,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,cAAc,CAAA;IAClF,OAAO,OAAO,CAAA;AAChB,CAAC;AASD,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;IAClC,MAAM,MAAM,GAAmB,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAA;IAC3F,KAAK,MAAM,CAAC,IAAI,GAAG;QAAE,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IAC5D,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import { manifestPropFields } from '@elytracms/core/component-registry'\nimport type { ComponentManifest, ComponentRegistry } from '@elytracms/core/component-registry'\nimport type { PropFieldLookup } from '@elytracms/core/cms-core'\nimport { getImplementation, registerComponents } from '@elytracms/core/runtime-renderer'\nimport type { ComponentEntry, ComponentImplementations, RenderContext } from '@elytracms/core/runtime-renderer'\nimport { resolveDeploymentComponents } from '../workspace'\n\n/**\n * The Studio's view of a project's component registry (brief §7.2, §8.3): the\n * project's component LIST — exactly what its config's `components` names — with\n * the implementations for live previews. Deterministic singleton, built in-process\n * from code-owned manifests — component manifests live in TS next to the\n * components, so local/CI/browser runs need no backend.\n */\nexport interface StudioRegistry {\n registry: ComponentRegistry\n implementations: ComponentImplementations\n}\n\n/**\n * EC-194/EC-208/EC-223, reshaped by EC-591: the consuming repo's component list,\n * INJECTED through config-as-code (the mount contract) — the active project's\n * `components`, the live (manifest + implementation) pairs its `elytra.config.ts`\n * hands in (the components Elytra ships are an opt-in package listed there:\n * `[...elytraComponents, ...siteComponents]`). The studio registers NOTHING of its\n * own: no base primitives and no demo fallback. A project that lists no components\n * gets an empty palette — the canvas says why (its role notices) — never a set of\n * demo components the delivery host would render as missing-component fallbacks\n * (the EC-591 worst row).\n *\n * The list goes through core's ONE registration step `registerComponents` — the\n * same call the Next host and the preview harness make — so a duplicate id, a role\n * claim or an `invalid-implementation` is the identical issue on every surface, in\n * `registry.issues` (the one record the status readers below consult).\n */\nlet componentsForTests: readonly ComponentEntry[] | null = null\n\n/**\n * Test seam: register this list instead of the active project's config list\n * (`null` restores the config). Never used by app code — the studio's components\n * come from `elytra.config.ts`.\n */\nexport function setStudioComponentsForTests(entries: readonly ComponentEntry[] | null): void {\n componentsForTests = entries\n cached = null\n}\n\nconst NO_COMPONENTS: readonly ComponentEntry[] = []\nlet cached: StudioRegistry | null = null\nlet cachedList: readonly ComponentEntry[] | null = null\n\nexport function getStudioRegistry(): StudioRegistry {\n // Re-derive when the list changes — a different active project (per-project\n // registry, AD-8) or a re-mounted config hands over a different list.\n const list = componentsForTests ?? resolveDeploymentComponents() ?? NO_COMPONENTS\n if (cached && cachedList === list) return cached\n cached = registerComponents(list)\n cachedList = list\n return cached\n}\n\n/**\n * Adapt the live registry to the cms-core {@link PropFieldLookup} contract (EC-277):\n * a component id → its prop field-defs, name-injected. Threaded into the studio's\n * write-time self-typing lens so relation/asset props inside compositions are\n * self-typed on write (\"schema leads, component adopts\"). Manifests are code (AD-11);\n * persistence/cms-core never depend on the registry — the studio supplies this shape.\n */\nexport function studioManifestLookup(): PropFieldLookup {\n const { registry } = getStudioRegistry()\n return (componentId) => {\n const manifest = registry.getManifest(componentId)\n if (!manifest) return undefined\n return Object.fromEntries(manifestPropFields(manifest).map((field) => [field.name, field]))\n }\n}\n\n/** A RenderContext for fixture previews (static props only; no asset/relation resolvers). */\nexport function previewContext(studio: StudioRegistry): RenderContext {\n return { registry: studio.registry, implementations: studio.implementations }\n}\n\n/**\n * Per-component status (brief §4.11, §8.3, EC-055):\n * - `invalid`: the registry reported a structured issue for this id — including\n * an `invalid-implementation` (EC-555: the entry HAS an implementation, it is\n * just not a component, so it is not merely missing).\n * - `incompatible`: a manifest exists but no implementation is wired up.\n * - `valid`: healthy.\n */\nexport type ComponentStatus = 'valid' | 'incompatible' | 'invalid'\n\nexport function componentStatus(\n studio: StudioRegistry,\n manifest: ComponentManifest,\n): ComponentStatus {\n if (studio.registry.issues.some((i) => i.componentId === manifest.id)) return 'invalid'\n if (!getImplementation(studio.implementations, manifest.id)) return 'incompatible'\n return 'valid'\n}\n\nexport interface RegistryHealth {\n total: number\n valid: number\n incompatible: number\n invalid: number\n}\n\nexport function registryHealth(studio: StudioRegistry): RegistryHealth {\n const all = studio.registry.list()\n const health: RegistryHealth = { total: all.length, valid: 0, incompatible: 0, invalid: 0 }\n for (const m of all) health[componentStatus(studio, m)] += 1\n return health\n}\n"]}
|
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import type { ComponentNode, JsonValue } from '@elytracms/core/project-graph';
|
|
2
|
-
import type { ComponentManifest } from '@elytracms/core/component-registry';
|
|
2
|
+
import type { ComponentManifest, ComponentRegistry } from '@elytracms/core/component-registry';
|
|
3
3
|
/** A named, renderable preview state for a component. */
|
|
4
4
|
export interface PreviewSpec {
|
|
5
5
|
name: string;
|
|
6
6
|
node: ComponentNode;
|
|
7
7
|
}
|
|
8
|
-
/** Build a node from a manifest's defaults, overlaying any provided prop values. */
|
|
9
|
-
export declare function nodeFromProps(manifest: ComponentManifest, overrides: Record<string, JsonValue>, id: string): ComponentNode;
|
|
10
8
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* A node carrying exactly the given prop values (EC-591 / ratified 2f): a prop
|
|
10
|
+
* the caller does not set stays ABSENT — the implementation's own default applies
|
|
11
|
+
* — and the manifest `default` is never copied in (it is an authoring hint). Until
|
|
12
|
+
* EC-591 this overlaid the overrides on every manifest default, so a fixture
|
|
13
|
+
* insert stored — and rendered — something other than the same fixture pasted
|
|
14
|
+
* from "copy exact configuration" (COMPOSITION #19).
|
|
15
|
+
*
|
|
16
|
+
* Node ids are derived deterministically from a stable base (manifest id +
|
|
17
|
+
* fixture index) so previews are byte-identical across SSR and client hydration
|
|
18
|
+
* and reproducible across runs — no module-level counter, no time/random.
|
|
14
19
|
*/
|
|
15
|
-
export declare function
|
|
20
|
+
export declare function nodeFromProps(manifest: ComponentManifest, values: Record<string, JsonValue>, id: string): ComponentNode;
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the preview states for a component (brief §7.2, §8.3, EC-054): the
|
|
23
|
+
* declared manifest fixtures; otherwise the single `Default` — the SAME seeded
|
|
24
|
+
* node the blocks panel inserts and `elytra previews` renders for a fixture-less
|
|
25
|
+
* component (a layout container comes pre-filled through its role, EC-383).
|
|
26
|
+
* EC-591: there are no authored previews keyed by engine ids any more — a
|
|
27
|
+
* component that wants a richer preview declares `fixtures`, like any other.
|
|
28
|
+
* Deterministic ids throughout.
|
|
29
|
+
*/
|
|
30
|
+
export declare function previewSpecs(manifest: ComponentManifest, registry: ComponentRegistry): PreviewSpec[];
|
|
@@ -1,110 +1,47 @@
|
|
|
1
|
-
import { staticProp } from '@elytracms/core/project-graph';
|
|
1
|
+
import { cloneWithNewIds, staticProp } from '@elytracms/core/project-graph';
|
|
2
|
+
import { seededNodeFromManifest } from '../../components/builder/node-factory.js';
|
|
2
3
|
/**
|
|
4
|
+
* A node carrying exactly the given prop values (EC-591 / ratified 2f): a prop
|
|
5
|
+
* the caller does not set stays ABSENT — the implementation's own default applies
|
|
6
|
+
* — and the manifest `default` is never copied in (it is an authoring hint). Until
|
|
7
|
+
* EC-591 this overlaid the overrides on every manifest default, so a fixture
|
|
8
|
+
* insert stored — and rendered — something other than the same fixture pasted
|
|
9
|
+
* from "copy exact configuration" (COMPOSITION #19).
|
|
10
|
+
*
|
|
3
11
|
* Node ids are derived deterministically from a stable base (manifest id +
|
|
4
12
|
* fixture index) so previews are byte-identical across SSR and client hydration
|
|
5
13
|
* and reproducible across runs — no module-level counter, no time/random.
|
|
6
14
|
*/
|
|
7
|
-
function
|
|
8
|
-
return { id, componentId: 'base.Text', props: { value: staticProp(value) } };
|
|
9
|
-
}
|
|
10
|
-
/** Build a node from a manifest's defaults, overlaying any provided prop values. */
|
|
11
|
-
export function nodeFromProps(manifest, overrides, id) {
|
|
15
|
+
export function nodeFromProps(manifest, values, id) {
|
|
12
16
|
const props = {};
|
|
13
|
-
for (const
|
|
14
|
-
const value =
|
|
17
|
+
for (const name of Object.keys(manifest.props)) {
|
|
18
|
+
const value = values[name];
|
|
15
19
|
if (value !== undefined)
|
|
16
20
|
props[name] = staticProp(value);
|
|
17
21
|
}
|
|
18
22
|
return { id, componentId: manifest.id, props };
|
|
19
23
|
}
|
|
20
|
-
function nodeFromPropsRaw(componentId, props, id) {
|
|
21
|
-
const out = {};
|
|
22
|
-
for (const [k, v] of Object.entries(props))
|
|
23
|
-
out[k] = staticProp(v);
|
|
24
|
-
return { id, componentId, props: out };
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Authored preview nodes for the base primitives (which ship no fixtures of their
|
|
28
|
-
* own). Slot-bearing primitives get sample children so previews aren't blank.
|
|
29
|
-
* Each builder receives a deterministic `base` id prefix.
|
|
30
|
-
*/
|
|
31
|
-
const PRIMITIVE_PREVIEWS = {
|
|
32
|
-
'base.Container': (base) => [
|
|
33
|
-
{
|
|
34
|
-
name: 'With content',
|
|
35
|
-
node: {
|
|
36
|
-
id: base,
|
|
37
|
-
componentId: 'base.Container',
|
|
38
|
-
slots: { children: [text('Container content', `${base}-c0`)] },
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
'base.Stack': (base) => [
|
|
43
|
-
{
|
|
44
|
-
name: 'Gap 4',
|
|
45
|
-
node: {
|
|
46
|
-
id: base,
|
|
47
|
-
componentId: 'base.Stack',
|
|
48
|
-
props: { gap: staticProp(4) },
|
|
49
|
-
slots: {
|
|
50
|
-
children: [
|
|
51
|
-
text('Item one', `${base}-c0`),
|
|
52
|
-
text('Item two', `${base}-c1`),
|
|
53
|
-
text('Item three', `${base}-c2`),
|
|
54
|
-
],
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
'base.Grid': (base) => [
|
|
60
|
-
{
|
|
61
|
-
name: '2 columns',
|
|
62
|
-
node: {
|
|
63
|
-
id: base,
|
|
64
|
-
componentId: 'base.Grid',
|
|
65
|
-
props: { columns: staticProp(2), gap: staticProp(3) },
|
|
66
|
-
slots: {
|
|
67
|
-
children: [
|
|
68
|
-
text('Cell A', `${base}-c0`),
|
|
69
|
-
text('Cell B', `${base}-c1`),
|
|
70
|
-
text('Cell C', `${base}-c2`),
|
|
71
|
-
text('Cell D', `${base}-c3`),
|
|
72
|
-
],
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
'base.Text': (base) => [
|
|
78
|
-
{ name: 'Sample', node: nodeFromPropsRaw('base.Text', { value: 'The quick brown fox.' }, base) },
|
|
79
|
-
],
|
|
80
|
-
'base.Heading': (base) => [
|
|
81
|
-
{ name: 'Sample', node: nodeFromPropsRaw('base.Heading', { value: 'A section heading' }, base) },
|
|
82
|
-
],
|
|
83
|
-
'base.Image': (base) => [
|
|
84
|
-
{
|
|
85
|
-
name: 'Placeholder',
|
|
86
|
-
node: nodeFromPropsRaw('base.Image', { src: 'https://placehold.co/320x180', alt: 'Placeholder image' }, base),
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
'base.Button': (base) => [
|
|
90
|
-
{ name: 'Sample', node: nodeFromPropsRaw('base.Button', { label: 'Click me' }, base) },
|
|
91
|
-
],
|
|
92
|
-
};
|
|
93
24
|
/**
|
|
94
|
-
* Resolve the preview states for a component (brief §7.2, §8.3, EC-054):
|
|
95
|
-
* declared manifest fixtures
|
|
96
|
-
*
|
|
25
|
+
* Resolve the preview states for a component (brief §7.2, §8.3, EC-054): the
|
|
26
|
+
* declared manifest fixtures; otherwise the single `Default` — the SAME seeded
|
|
27
|
+
* node the blocks panel inserts and `elytra previews` renders for a fixture-less
|
|
28
|
+
* component (a layout container comes pre-filled through its role, EC-383).
|
|
29
|
+
* EC-591: there are no authored previews keyed by engine ids any more — a
|
|
30
|
+
* component that wants a richer preview declares `fixtures`, like any other.
|
|
31
|
+
* Deterministic ids throughout.
|
|
97
32
|
*/
|
|
98
|
-
export function previewSpecs(manifest) {
|
|
33
|
+
export function previewSpecs(manifest, registry) {
|
|
99
34
|
if (manifest.fixtures && manifest.fixtures.length > 0) {
|
|
100
35
|
return manifest.fixtures.map((fixture, i) => ({
|
|
101
36
|
name: fixture.name,
|
|
102
37
|
node: nodeFromProps(manifest, fixture.props ?? {}, `preview-${manifest.id}-${i}`),
|
|
103
38
|
}));
|
|
104
39
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
40
|
+
// The seed's placeholder ids are re-minted deterministically (root `…-0`, then
|
|
41
|
+
// `…-0-1`, `…-0-2` …) so every node in the preview tree has a distinct, stable id.
|
|
42
|
+
const base = `preview-${manifest.id}-0`;
|
|
43
|
+
let n = 0;
|
|
44
|
+
const node = cloneWithNewIds(seededNodeFromManifest(manifest, registry), () => (n++ === 0 ? base : `${base}-${n - 1}`));
|
|
45
|
+
return [{ name: 'Default', node }];
|
|
109
46
|
}
|
|
110
47
|
//# sourceMappingURL=preview.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/lib/registry/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;
|
|
1
|
+
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/lib/registry/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAG3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAA;AAQ9E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAC3B,QAA2B,EAC3B,MAAiC,EACjC,EAAU;IAEV,MAAM,KAAK,GAA2B,EAAE,CAAA;IACxC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;QAC1B,IAAI,KAAK,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;IAC1D,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,CAAA;AAChD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,QAA2B,EAAE,QAA2B;IACnF,IAAI,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtD,OAAO,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5C,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE,WAAW,QAAQ,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC;SAClF,CAAC,CAAC,CAAA;IACL,CAAC;IACD,+EAA+E;IAC/E,mFAAmF;IACnF,MAAM,IAAI,GAAG,WAAW,QAAQ,CAAC,EAAE,IAAI,CAAA;IACvC,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,MAAM,IAAI,GAAG,eAAe,CAAC,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IACvH,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;AACpC,CAAC","sourcesContent":["import { cloneWithNewIds, staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode, JsonValue } from '@elytracms/core/project-graph'\nimport type { ComponentManifest, ComponentRegistry } from '@elytracms/core/component-registry'\nimport { seededNodeFromManifest } from '../../components/builder/node-factory'\n\n/** A named, renderable preview state for a component. */\nexport interface PreviewSpec {\n name: string\n node: ComponentNode\n}\n\n/**\n * A node carrying exactly the given prop values (EC-591 / ratified 2f): a prop\n * the caller does not set stays ABSENT — the implementation's own default applies\n * — and the manifest `default` is never copied in (it is an authoring hint). Until\n * EC-591 this overlaid the overrides on every manifest default, so a fixture\n * insert stored — and rendered — something other than the same fixture pasted\n * from \"copy exact configuration\" (COMPOSITION #19).\n *\n * Node ids are derived deterministically from a stable base (manifest id +\n * fixture index) so previews are byte-identical across SSR and client hydration\n * and reproducible across runs — no module-level counter, no time/random.\n */\nexport function nodeFromProps(\n manifest: ComponentManifest,\n values: Record<string, JsonValue>,\n id: string,\n): ComponentNode {\n const props: ComponentNode['props'] = {}\n for (const name of Object.keys(manifest.props)) {\n const value = values[name]\n if (value !== undefined) props[name] = staticProp(value)\n }\n return { id, componentId: manifest.id, props }\n}\n\n/**\n * Resolve the preview states for a component (brief §7.2, §8.3, EC-054): the\n * declared manifest fixtures; otherwise the single `Default` — the SAME seeded\n * node the blocks panel inserts and `elytra previews` renders for a fixture-less\n * component (a layout container comes pre-filled through its role, EC-383).\n * EC-591: there are no authored previews keyed by engine ids any more — a\n * component that wants a richer preview declares `fixtures`, like any other.\n * Deterministic ids throughout.\n */\nexport function previewSpecs(manifest: ComponentManifest, registry: ComponentRegistry): PreviewSpec[] {\n if (manifest.fixtures && manifest.fixtures.length > 0) {\n return manifest.fixtures.map((fixture, i) => ({\n name: fixture.name,\n node: nodeFromProps(manifest, fixture.props ?? {}, `preview-${manifest.id}-${i}`),\n }))\n }\n // The seed's placeholder ids are re-minted deterministically (root `…-0`, then\n // `…-0-1`, `…-0-2` …) so every node in the preview tree has a distinct, stable id.\n const base = `preview-${manifest.id}-0`\n let n = 0\n const node = cloneWithNewIds(seededNodeFromManifest(manifest, registry), () => (n++ === 0 ? base : `${base}-${n - 1}`))\n return [{ name: 'Default', node }]\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ComponentFixture,
|
|
2
|
-
import type {
|
|
1
|
+
import type { ComponentFixture, ComponentManifest } from '@elytracms/core/component-registry';
|
|
2
|
+
import type { ComponentEntry } from '@elytracms/core/runtime-renderer';
|
|
3
3
|
import type { StudioRegistry } from '../lib/registry/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* EC-440 — the fixture-preview enumeration behind `elytra previews`. ONE
|
|
@@ -13,9 +13,10 @@ import type { StudioRegistry } from '../lib/registry/index.js';
|
|
|
13
13
|
* manifests + implementations — code), never a persistence adapter, auth, or
|
|
14
14
|
* content data. The entry set mirrors the blocks panel exactly (EC-439): every
|
|
15
15
|
* canvas-eligible manifest, one entry per declared fixture, and a single
|
|
16
|
-
* implicit {@link DEFAULT_VARIANT} entry for fixture-less manifests.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* implicit {@link DEFAULT_VARIANT} entry for fixture-less manifests. Exactly the
|
|
17
|
+
* config's components — the engine adds none (EC-591; the shipped components are
|
|
18
|
+
* there when the config lists `@elytracms/components`). Layout-helper presets are
|
|
19
|
+
* NOT manifests and keep their placeholder treatment.
|
|
19
20
|
*/
|
|
20
21
|
/** One renderable preview: a component fixture plus its staleness-hash inputs. */
|
|
21
22
|
export interface PreviewEntrySpec {
|
|
@@ -49,30 +50,25 @@ export interface PreviewEntrySpec {
|
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
/**
|
|
52
|
-
* The component
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* id
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* fallback, never `createElement`) and returned as an `invalid-implementation`
|
|
64
|
-
* issue. The first-wins dedupe runs BEFORE the check, so a withheld entry still
|
|
65
|
-
* claims its id.
|
|
53
|
+
* The component list a config carries, as the harness registry input: every
|
|
54
|
+
* resolved project's `components`, concatenated in project order. An entry two
|
|
55
|
+
* projects share (the same `{ manifest, implementation }` object — e.g. both list
|
|
56
|
+
* `...elytraComponents`) is the SAME component and is kept once; two DIFFERENT
|
|
57
|
+
* entries under one id are a real conflict — the generated-preview seam is keyed
|
|
58
|
+
* by component id alone, so one image would serve both — and go to the one
|
|
59
|
+
* registration rule like any duplicate: the first is kept whole, the second is a
|
|
60
|
+
* `duplicate-id` issue (EC-591; before, previews skipped it silently and showed
|
|
61
|
+
* the first project's render in the second project's tile — COMPOSITION #13).
|
|
62
|
+
* A config that fails shape-resolution contributes no components; the CLI
|
|
63
|
+
* surfaces config problems through the studio/doctor, not here.
|
|
66
64
|
*/
|
|
67
|
-
export declare function previewComponentsFromConfig(config: unknown):
|
|
68
|
-
manifests: ComponentManifest[];
|
|
69
|
-
implementations: ComponentImplementations;
|
|
70
|
-
issues: ComponentIssue[];
|
|
71
|
-
};
|
|
65
|
+
export declare function previewComponentsFromConfig(config: unknown): readonly ComponentEntry[];
|
|
72
66
|
/**
|
|
73
|
-
* The {@link StudioRegistry}-shaped registry the harness renders against
|
|
74
|
-
*
|
|
75
|
-
* studio registry
|
|
67
|
+
* The {@link StudioRegistry}-shaped registry the harness renders against: the
|
|
68
|
+
* config's list through core's ONE registration step (`registerComponents`, the
|
|
69
|
+
* same call the studio registry and the Next host make — EC-591), so a duplicate,
|
|
70
|
+
* a role claim and an `invalid-implementation` are the identical issue in
|
|
71
|
+
* `registry.issues` on every surface.
|
|
76
72
|
*/
|
|
77
73
|
export declare function previewRegistryFromConfig(config: unknown): StudioRegistry;
|
|
78
74
|
/**
|
|
@@ -1,63 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isCanvasEligible } from '@elytracms/core/component-registry';
|
|
2
|
+
import { registerComponents } from '@elytracms/core/runtime-renderer';
|
|
3
3
|
import { resolveWorkspaceConfig } from '@elytracms/core/studio-core';
|
|
4
4
|
import { DEFAULT_VARIANT } from '../components/builder/blocks-model.js';
|
|
5
5
|
/**
|
|
6
|
-
* The component
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* id
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* fallback, never `createElement`) and returned as an `invalid-implementation`
|
|
18
|
-
* issue. The first-wins dedupe runs BEFORE the check, so a withheld entry still
|
|
19
|
-
* claims its id.
|
|
6
|
+
* The component list a config carries, as the harness registry input: every
|
|
7
|
+
* resolved project's `components`, concatenated in project order. An entry two
|
|
8
|
+
* projects share (the same `{ manifest, implementation }` object — e.g. both list
|
|
9
|
+
* `...elytraComponents`) is the SAME component and is kept once; two DIFFERENT
|
|
10
|
+
* entries under one id are a real conflict — the generated-preview seam is keyed
|
|
11
|
+
* by component id alone, so one image would serve both — and go to the one
|
|
12
|
+
* registration rule like any duplicate: the first is kept whole, the second is a
|
|
13
|
+
* `duplicate-id` issue (EC-591; before, previews skipped it silently and showed
|
|
14
|
+
* the first project's render in the second project's tile — COMPOSITION #13).
|
|
15
|
+
* A config that fails shape-resolution contributes no components; the CLI
|
|
16
|
+
* surfaces config problems through the studio/doctor, not here.
|
|
20
17
|
*/
|
|
21
18
|
export function previewComponentsFromConfig(config) {
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
for (const manifest of base.manifests) {
|
|
26
|
-
const implementation = getImplementation(base.implementations, manifest.id);
|
|
27
|
-
if (implementation)
|
|
28
|
-
implementations[manifest.id] = implementation;
|
|
29
|
-
}
|
|
30
|
-
const seen = new Set(manifests.map((m) => m.id));
|
|
31
|
-
const kept = [];
|
|
32
|
-
const resolved = resolveWorkspaceConfig(config);
|
|
33
|
-
for (const project of resolved.projects) {
|
|
19
|
+
const seen = new Set();
|
|
20
|
+
const entries = [];
|
|
21
|
+
for (const project of resolveWorkspaceConfig(config).projects) {
|
|
34
22
|
for (const entry of project.components ?? []) {
|
|
35
|
-
if (seen.has(entry
|
|
23
|
+
if (seen.has(entry))
|
|
36
24
|
continue;
|
|
37
|
-
seen.add(entry
|
|
38
|
-
|
|
39
|
-
kept.push(entry);
|
|
25
|
+
seen.add(entry);
|
|
26
|
+
entries.push(entry);
|
|
40
27
|
}
|
|
41
28
|
}
|
|
42
|
-
|
|
43
|
-
// so the merge order below decides nothing.
|
|
44
|
-
const project = registerImplementations(kept);
|
|
45
|
-
return {
|
|
46
|
-
manifests,
|
|
47
|
-
implementations: { ...implementations, ...project.implementations },
|
|
48
|
-
issues: project.issues,
|
|
49
|
-
};
|
|
29
|
+
return entries;
|
|
50
30
|
}
|
|
51
31
|
/**
|
|
52
|
-
* The {@link StudioRegistry}-shaped registry the harness renders against
|
|
53
|
-
*
|
|
54
|
-
* studio registry
|
|
32
|
+
* The {@link StudioRegistry}-shaped registry the harness renders against: the
|
|
33
|
+
* config's list through core's ONE registration step (`registerComponents`, the
|
|
34
|
+
* same call the studio registry and the Next host make — EC-591), so a duplicate,
|
|
35
|
+
* a role claim and an `invalid-implementation` are the identical issue in
|
|
36
|
+
* `registry.issues` on every surface.
|
|
55
37
|
*/
|
|
56
38
|
export function previewRegistryFromConfig(config) {
|
|
57
|
-
|
|
58
|
-
const registry = new ComponentRegistry(manifests);
|
|
59
|
-
registry.issues.push(...issues);
|
|
60
|
-
return { registry, implementations };
|
|
39
|
+
return registerComponents(previewComponentsFromConfig(config));
|
|
61
40
|
}
|
|
62
41
|
/**
|
|
63
42
|
* Enumerate every generated-preview entry for a config: canvas-eligible
|
|
@@ -68,7 +47,7 @@ export function previewRegistryFromConfig(config) {
|
|
|
68
47
|
* deterministic regardless of registration order.
|
|
69
48
|
*/
|
|
70
49
|
export function previewEntriesForConfig(config) {
|
|
71
|
-
const
|
|
50
|
+
const manifests = previewRegistryFromConfig(config).registry.list();
|
|
72
51
|
const entries = [];
|
|
73
52
|
for (const manifest of [...manifests].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
74
53
|
if (!isCanvasEligible(manifest))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enumerate.js","sourceRoot":"","sources":["../../src/previews/enumerate.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType } from 'react'\nimport { ComponentRegistry, isCanvasEligible } from '@elytracms/core/component-registry'\nimport type {\n ComponentFixture,\n ComponentIssue,\n ComponentManifest,\n} from '@elytracms/core/component-registry'\nimport { basePrimitives, getImplementation, registerImplementations } from '@elytracms/core/runtime-renderer'\nimport type { ComponentImplementations } from '@elytracms/core/runtime-renderer'\nimport { resolveWorkspaceConfig } from '@elytracms/core/studio-core'\nimport type { HostComponentEntry } from '@elytracms/core/studio-core'\nimport { DEFAULT_VARIANT } from '../components/builder/blocks-model'\nimport type { StudioRegistry } from '../lib/registry'\n\n/**\n * EC-440 — the fixture-preview enumeration behind `elytra previews`. ONE\n * source of truth for \"which previews exist\" shared by the CLI's node-side\n * driver (hashing, staleness `--check`) and the browser render harness\n * ({@link import('./harness').mountPreviewHarness}) — both derive the entry\n * set from the consumer's `elytra.config.ts` default export through here, so\n * generation, staleness checking, and rendering can never disagree.\n *\n * DATA-FREE by construction: this module reads the consumer CONFIG (registry\n * manifests + implementations — code), never a persistence adapter, auth, or\n * content data. The entry set mirrors the blocks panel exactly (EC-439): every\n * canvas-eligible manifest, one entry per declared fixture, and a single\n * implicit {@link DEFAULT_VARIANT} entry for fixture-less manifests. Base\n * primitives are included — they are insertable tiles too. Layout-helper\n * presets are NOT manifests and keep their placeholder treatment.\n */\n\n/** One renderable preview: a component fixture plus its staleness-hash inputs. */\nexport interface PreviewEntrySpec {\n componentId: string\n /** The fixture name, or {@link DEFAULT_VARIANT} for a fixture-less manifest. */\n fixture: string\n /**\n * The DECLARED render inputs the CLI hashes per entry for staleness: the\n * manifest's prop + slot shape (defaults live in the prop field-defs, so\n * seeded renders are covered) and the declared fixture's own\n * props/slotChildren (`fixture: null` for the implicit Default variant). A\n * change here re-renders one entry.\n *\n * NOT in here: the component implementation and the theme — a config arrives\n * evaluated, so neither has a per-entry identity. The CLI covers them as\n * manifest-level render dependencies instead (EC-600: content hashes of the\n * project source files the config imports plus the theme `@import` chain),\n * and a change there re-renders every entry. Before EC-600 this comment said\n * regeneration caught such drift; it did not — regeneration only rendered what\n * this hash called stale.\n */\n hashInput: {\n manifest: { props: ComponentManifest['props']; slots: ComponentManifest['slots'] }\n fixture: {\n props: NonNullable<ComponentFixture['props']> | null\n slotChildren: NonNullable<ComponentFixture['slotChildren']> | null\n } | null\n }\n}\n\n/**\n * The component set a config carries, as the harness registry input: the base\n * primitives plus every resolved project's `components` entries, deduped by\n * component id (first declaration wins — the generated-preview seam is keyed\n * by component id alone, so one image serves every project that registers the\n * id). A config that fails shape-resolution contributes no project components\n * — the primitives still enumerate, and the CLI surfaces config problems\n * through the studio/doctor, not here.\n *\n * EC-555: the kept entries' implementations (`unknown` in the config) go through\n * core's one registration step, like the studio registry's and the Next host's —\n * a non-component is withheld (its preview renders the missing-component\n * fallback, never `createElement`) and returned as an `invalid-implementation`\n * issue. The first-wins dedupe runs BEFORE the check, so a withheld entry still\n * claims its id.\n */\nexport function previewComponentsFromConfig(config: unknown): {\n manifests: ComponentManifest[]\n implementations: ComponentImplementations\n issues: ComponentIssue[]\n} {\n const base = basePrimitives()\n const manifests: ComponentManifest[] = [...base.manifests]\n const implementations: Record<string, ComponentType<Record<string, unknown>>> = {}\n for (const manifest of base.manifests) {\n const implementation = getImplementation(base.implementations, manifest.id)\n if (implementation) implementations[manifest.id] = implementation\n }\n const seen = new Set(manifests.map((m) => m.id))\n\n const kept: HostComponentEntry[] = []\n const resolved = resolveWorkspaceConfig(config)\n for (const project of resolved.projects) {\n for (const entry of project.components ?? []) {\n if (seen.has(entry.manifest.id)) continue\n seen.add(entry.manifest.id)\n manifests.push(entry.manifest)\n kept.push(entry)\n }\n }\n // Ids are unique by construction here (deduped against the primitives too),\n // so the merge order below decides nothing.\n const project = registerImplementations(kept)\n return {\n manifests,\n implementations: { ...implementations, ...project.implementations },\n issues: project.issues,\n }\n}\n\n/**\n * The {@link StudioRegistry}-shaped registry the harness renders against. Its\n * `registry.issues` carries the implementation issues too — the same list the\n * studio registry records them into (EC-555).\n */\nexport function previewRegistryFromConfig(config: unknown): StudioRegistry {\n const { manifests, implementations, issues } = previewComponentsFromConfig(config)\n const registry = new ComponentRegistry(manifests)\n registry.issues.push(...issues)\n return { registry, implementations }\n}\n\n/**\n * Enumerate every generated-preview entry for a config: canvas-eligible\n * manifests only (server-only components never render on the client canvas),\n * one entry per declared fixture in declaration order, else one implicit\n * {@link DEFAULT_VARIANT} entry. Components are sorted by id so the output —\n * and everything the CLI derives from it (manifest JSON, file writes) — is\n * deterministic regardless of registration order.\n */\nexport function previewEntriesForConfig(config: unknown): PreviewEntrySpec[] {\n const { manifests } = previewComponentsFromConfig(config)\n const entries: PreviewEntrySpec[] = []\n for (const manifest of [...manifests].sort((a, b) => a.id.localeCompare(b.id))) {\n if (!isCanvasEligible(manifest)) continue\n const manifestShape = { props: manifest.props, slots: manifest.slots }\n if (manifest.fixtures && manifest.fixtures.length > 0) {\n for (const fixture of manifest.fixtures) {\n entries.push({\n componentId: manifest.id,\n fixture: fixture.name,\n hashInput: {\n manifest: manifestShape,\n fixture: { props: fixture.props ?? null, slotChildren: fixture.slotChildren ?? null },\n },\n })\n }\n } else {\n entries.push({\n componentId: manifest.id,\n fixture: DEFAULT_VARIANT,\n hashInput: { manifest: manifestShape, fixture: null },\n })\n }\n }\n return entries\n}\n"]}
|
|
1
|
+
{"version":3,"file":"enumerate.js","sourceRoot":"","sources":["../../src/previews/enumerate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAA;AAErE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAErE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AAkDpE;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,2BAA2B,CAAC,MAAe;IACzD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAA;IACtC,MAAM,OAAO,GAAqB,EAAE,CAAA;IACpC,KAAK,MAAM,OAAO,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9D,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;YAC7C,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,SAAQ;YAC7B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YACf,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAe;IACvD,OAAO,kBAAkB,CAAC,2BAA2B,CAAC,MAAM,CAAC,CAAC,CAAA;AAChE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAe;IACrD,MAAM,SAAS,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;IACnE,MAAM,OAAO,GAAuB,EAAE,CAAA;IACtC,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QAC/E,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC;YAAE,SAAQ;QACzC,MAAM,aAAa,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAA;QACtE,IAAI,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtD,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;gBACxC,OAAO,CAAC,IAAI,CAAC;oBACX,WAAW,EAAE,QAAQ,CAAC,EAAE;oBACxB,OAAO,EAAE,OAAO,CAAC,IAAI;oBACrB,SAAS,EAAE;wBACT,QAAQ,EAAE,aAAa;wBACvB,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,IAAI,EAAE;qBACtF;iBACF,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC;gBACX,WAAW,EAAE,QAAQ,CAAC,EAAE;gBACxB,OAAO,EAAE,eAAe;gBACxB,SAAS,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE;aACtD,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC","sourcesContent":["import { isCanvasEligible } from '@elytracms/core/component-registry'\nimport type { ComponentFixture, ComponentManifest } from '@elytracms/core/component-registry'\nimport { registerComponents } from '@elytracms/core/runtime-renderer'\nimport type { ComponentEntry } from '@elytracms/core/runtime-renderer'\nimport { resolveWorkspaceConfig } from '@elytracms/core/studio-core'\nimport { DEFAULT_VARIANT } from '../components/builder/blocks-model'\nimport type { StudioRegistry } from '../lib/registry'\n\n/**\n * EC-440 — the fixture-preview enumeration behind `elytra previews`. ONE\n * source of truth for \"which previews exist\" shared by the CLI's node-side\n * driver (hashing, staleness `--check`) and the browser render harness\n * ({@link import('./harness').mountPreviewHarness}) — both derive the entry\n * set from the consumer's `elytra.config.ts` default export through here, so\n * generation, staleness checking, and rendering can never disagree.\n *\n * DATA-FREE by construction: this module reads the consumer CONFIG (registry\n * manifests + implementations — code), never a persistence adapter, auth, or\n * content data. The entry set mirrors the blocks panel exactly (EC-439): every\n * canvas-eligible manifest, one entry per declared fixture, and a single\n * implicit {@link DEFAULT_VARIANT} entry for fixture-less manifests. Exactly the\n * config's components — the engine adds none (EC-591; the shipped components are\n * there when the config lists `@elytracms/components`). Layout-helper presets are\n * NOT manifests and keep their placeholder treatment.\n */\n\n/** One renderable preview: a component fixture plus its staleness-hash inputs. */\nexport interface PreviewEntrySpec {\n componentId: string\n /** The fixture name, or {@link DEFAULT_VARIANT} for a fixture-less manifest. */\n fixture: string\n /**\n * The DECLARED render inputs the CLI hashes per entry for staleness: the\n * manifest's prop + slot shape (defaults live in the prop field-defs, so\n * seeded renders are covered) and the declared fixture's own\n * props/slotChildren (`fixture: null` for the implicit Default variant). A\n * change here re-renders one entry.\n *\n * NOT in here: the component implementation and the theme — a config arrives\n * evaluated, so neither has a per-entry identity. The CLI covers them as\n * manifest-level render dependencies instead (EC-600: content hashes of the\n * project source files the config imports plus the theme `@import` chain),\n * and a change there re-renders every entry. Before EC-600 this comment said\n * regeneration caught such drift; it did not — regeneration only rendered what\n * this hash called stale.\n */\n hashInput: {\n manifest: { props: ComponentManifest['props']; slots: ComponentManifest['slots'] }\n fixture: {\n props: NonNullable<ComponentFixture['props']> | null\n slotChildren: NonNullable<ComponentFixture['slotChildren']> | null\n } | null\n }\n}\n\n/**\n * The component list a config carries, as the harness registry input: every\n * resolved project's `components`, concatenated in project order. An entry two\n * projects share (the same `{ manifest, implementation }` object — e.g. both list\n * `...elytraComponents`) is the SAME component and is kept once; two DIFFERENT\n * entries under one id are a real conflict — the generated-preview seam is keyed\n * by component id alone, so one image would serve both — and go to the one\n * registration rule like any duplicate: the first is kept whole, the second is a\n * `duplicate-id` issue (EC-591; before, previews skipped it silently and showed\n * the first project's render in the second project's tile — COMPOSITION #13).\n * A config that fails shape-resolution contributes no components; the CLI\n * surfaces config problems through the studio/doctor, not here.\n */\nexport function previewComponentsFromConfig(config: unknown): readonly ComponentEntry[] {\n const seen = new Set<ComponentEntry>()\n const entries: ComponentEntry[] = []\n for (const project of resolveWorkspaceConfig(config).projects) {\n for (const entry of project.components ?? []) {\n if (seen.has(entry)) continue\n seen.add(entry)\n entries.push(entry)\n }\n }\n return entries\n}\n\n/**\n * The {@link StudioRegistry}-shaped registry the harness renders against: the\n * config's list through core's ONE registration step (`registerComponents`, the\n * same call the studio registry and the Next host make — EC-591), so a duplicate,\n * a role claim and an `invalid-implementation` are the identical issue in\n * `registry.issues` on every surface.\n */\nexport function previewRegistryFromConfig(config: unknown): StudioRegistry {\n return registerComponents(previewComponentsFromConfig(config))\n}\n\n/**\n * Enumerate every generated-preview entry for a config: canvas-eligible\n * manifests only (server-only components never render on the client canvas),\n * one entry per declared fixture in declaration order, else one implicit\n * {@link DEFAULT_VARIANT} entry. Components are sorted by id so the output —\n * and everything the CLI derives from it (manifest JSON, file writes) — is\n * deterministic regardless of registration order.\n */\nexport function previewEntriesForConfig(config: unknown): PreviewEntrySpec[] {\n const manifests = previewRegistryFromConfig(config).registry.list()\n const entries: PreviewEntrySpec[] = []\n for (const manifest of [...manifests].sort((a, b) => a.id.localeCompare(b.id))) {\n if (!isCanvasEligible(manifest)) continue\n const manifestShape = { props: manifest.props, slots: manifest.slots }\n if (manifest.fixtures && manifest.fixtures.length > 0) {\n for (const fixture of manifest.fixtures) {\n entries.push({\n componentId: manifest.id,\n fixture: fixture.name,\n hashInput: {\n manifest: manifestShape,\n fixture: { props: fixture.props ?? null, slotChildren: fixture.slotChildren ?? null },\n },\n })\n }\n } else {\n entries.push({\n componentId: manifest.id,\n fixture: DEFAULT_VARIANT,\n hashInput: { manifest: manifestShape, fixture: null },\n })\n }\n }\n return entries\n}\n"]}
|
package/dist/previews/harness.js
CHANGED
|
@@ -71,7 +71,7 @@ function nodeForEntry(studio, componentId, fixture) {
|
|
|
71
71
|
if (declared)
|
|
72
72
|
return { node: fixtureSubtree(studio, manifest, declared, `preview-${componentId}`).node };
|
|
73
73
|
if (fixture === DEFAULT_VARIANT && (manifest.fixtures ?? []).length === 0) {
|
|
74
|
-
return { node: seededNodeFromManifest(manifest) };
|
|
74
|
+
return { node: seededNodeFromManifest(manifest, studio.registry) };
|
|
75
75
|
}
|
|
76
76
|
return { error: `Unknown fixture "${fixture}" for component "${componentId}".` };
|
|
77
77
|
}
|