@elytracms/studio 0.0.69 → 0.0.71
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/account/self-account-view-source.js +1 -0
- package/dist/components/account/self-account-view-source.js.map +1 -1
- package/dist/components/app-views/studio-config.d.ts +22 -0
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.js +1 -0
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +91 -29
- package/dist/components/builder/blocks-model.js +83 -54
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +40 -12
- package/dist/components/builder/blocks-panel.js +88 -34
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/fixture-thumbnail.d.ts +13 -23
- package/dist/components/builder/fixture-thumbnail.js +27 -99
- package/dist/components/builder/fixture-thumbnail.js.map +1 -1
- package/dist/components/builder/spacing-handles.d.ts +14 -11
- package/dist/components/builder/spacing-handles.js +33 -57
- package/dist/components/builder/spacing-handles.js.map +1 -1
- package/dist/components/cms/collection-table.js +19 -2
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/detail-editor.js +73 -51
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/duplicate-document.d.ts +45 -0
- package/dist/components/cms/duplicate-document.js +141 -0
- package/dist/components/cms/duplicate-document.js.map +1 -0
- package/dist/components/cms/field-anchors.d.ts +67 -0
- package/dist/components/cms/field-anchors.js +91 -0
- package/dist/components/cms/field-anchors.js.map +1 -0
- package/dist/components/cms/issue-copy.d.ts +45 -0
- package/dist/components/cms/issue-copy.js +89 -0
- package/dist/components/cms/issue-copy.js.map +1 -0
- package/dist/components/cms/issues-pane.d.ts +11 -2
- package/dist/components/cms/issues-pane.js +22 -4
- package/dist/components/cms/issues-pane.js.map +1 -1
- package/dist/components/cms/object-array-field.d.ts +2 -0
- package/dist/components/cms/object-array-field.js +75 -21
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/row-actions.d.ts +10 -3
- package/dist/components/cms/row-actions.js +8 -0
- package/dist/components/cms/row-actions.js.map +1 -1
- package/dist/components/cms/use-field-focus.d.ts +25 -9
- package/dist/components/cms/use-field-focus.js +80 -15
- package/dist/components/cms/use-field-focus.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +1 -0
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/redirects/redirects-collection-view-source.js +1 -0
- package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
- package/dist/components/session-expired-overlay.d.ts +16 -0
- package/dist/components/session-expired-overlay.js +22 -6
- package/dist/components/session-expired-overlay.js.map +1 -1
- package/dist/components/sign-in-view.d.ts +10 -1
- package/dist/components/sign-in-view.js +14 -3
- package/dist/components/sign-in-view.js.map +1 -1
- package/dist/components/users/users-collection-view-source.js +1 -0
- package/dist/components/users/users-collection-view-source.js.map +1 -1
- package/dist/customization.d.ts +18 -1
- package/dist/customization.js +8 -0
- package/dist/customization.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +6 -6
- package/dist/lib/collection-view/collection-view-source.d.ts +8 -0
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +44 -1
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +39 -0
- package/dist/lib/i18n/messages.en.js +47 -1
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/persona/labels.d.ts +5 -1
- package/dist/lib/persona/labels.js +43 -30
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +10 -0
- package/dist/lib/workspace/index.js +11 -0
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/previews/enumerate.d.ts +68 -0
- package/dist/previews/enumerate.js +78 -0
- package/dist/previews/enumerate.js.map +1 -0
- package/dist/previews/harness.d.ts +50 -0
- package/dist/previews/harness.js +123 -0
- package/dist/previews/harness.js.map +1 -0
- package/dist/previews/manifest-format.d.ts +57 -0
- package/dist/previews/manifest-format.js +59 -0
- package/dist/previews/manifest-format.js.map +1 -0
- package/dist/ui/dialog.d.ts +9 -1
- package/dist/ui/dialog.js +8 -2
- package/dist/ui/dialog.js.map +1 -1
- package/dist/ui/field.d.ts +24 -1
- package/dist/ui/field.js +23 -2
- package/dist/ui/field.js.map +1 -1
- package/package.json +10 -6
|
@@ -4,7 +4,11 @@ import type { OperationErrorCode } from '@elytracms/core/operations';
|
|
|
4
4
|
import type { ComponentManifest } from '@elytracms/core/component-registry';
|
|
5
5
|
/** Plain-language title for a graph validation issue code. */
|
|
6
6
|
export declare function graphIssueLabel(code: IssueCode): string;
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Plain-language title for a CMS validation issue code — localized against the
|
|
9
|
+
* active studio-UI locale (EC-443). A runtime-unknown code (impossible typewise;
|
|
10
|
+
* a cast could smuggle one) degrades to its humanized form, never the raw enum.
|
|
11
|
+
*/
|
|
8
12
|
export declare function cmsIssueLabel(code: CmsIssueCode): string;
|
|
9
13
|
/** Plain-language title for an operation failure code. */
|
|
10
14
|
export declare function operationErrorLabel(code: OperationErrorCode): string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveLabel } from '@elytracms/core/cms-core';
|
|
2
|
-
import { localizedLabel } from '@elytracms/studio/lib/i18n';
|
|
2
|
+
import { localizedLabel, translate } from '@elytracms/studio/lib/i18n';
|
|
3
3
|
import { humanizeName } from '../utils.js';
|
|
4
4
|
/**
|
|
5
5
|
* Persona-aware labels (EC-192 item 2, Milestone B promise).
|
|
@@ -35,33 +35,41 @@ const GRAPH_ISSUE_LABELS = {
|
|
|
35
35
|
'invalid-container-columns': 'Invalid columns',
|
|
36
36
|
'invalid-container-responsive': 'Invalid responsive setting',
|
|
37
37
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'missing-
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'
|
|
59
|
-
'
|
|
60
|
-
'
|
|
61
|
-
'
|
|
62
|
-
'
|
|
63
|
-
'
|
|
64
|
-
'unknown-
|
|
38
|
+
/**
|
|
39
|
+
* EC-443: the CMS issue titles live in the EC-241 message catalog
|
|
40
|
+
* (`cms.issue.<code>`, EN source + shipped de), so the Issues pane / for-review
|
|
41
|
+
* lane localize with the rest of the chrome. This map stays the exhaustive
|
|
42
|
+
* `Record` over the Zod enum — a new code still fails typecheck here until it
|
|
43
|
+
* gets an editor-facing catalog key (the persona boundary cannot silently
|
|
44
|
+
* regress); {@link cmsIssueLabel} resolves it through `translate`.
|
|
45
|
+
*/
|
|
46
|
+
const CMS_ISSUE_LABEL_KEYS = {
|
|
47
|
+
'duplicate-collection': 'cms.issue.duplicate-collection',
|
|
48
|
+
'duplicate-field': 'cms.issue.duplicate-field',
|
|
49
|
+
'unknown-collection': 'cms.issue.unknown-collection',
|
|
50
|
+
'invalid-collection-config': 'cms.issue.invalid-collection-config',
|
|
51
|
+
'invalid-field-config': 'cms.issue.invalid-field-config',
|
|
52
|
+
'reserved-field-name': 'cms.issue.reserved-field-name',
|
|
53
|
+
'missing-required-field': 'cms.issue.missing-required-field',
|
|
54
|
+
'invalid-field-value': 'cms.issue.invalid-field-value',
|
|
55
|
+
'unknown-relation-target': 'cms.issue.unknown-relation-target',
|
|
56
|
+
'unpublished-relation-target': 'cms.issue.unpublished-relation-target',
|
|
57
|
+
'cardinality-violation': 'cms.issue.cardinality-violation',
|
|
58
|
+
'unknown-asset': 'cms.issue.unknown-asset',
|
|
59
|
+
'duplicate-document': 'cms.issue.duplicate-document',
|
|
60
|
+
'unknown-locale': 'cms.issue.unknown-locale',
|
|
61
|
+
'missing-localized-value': 'cms.issue.missing-localized-value',
|
|
62
|
+
'route-conflict': 'cms.issue.route-conflict',
|
|
63
|
+
'redirect-loop': 'cms.issue.redirect-loop',
|
|
64
|
+
'unknown-route-target': 'cms.issue.unknown-route-target',
|
|
65
|
+
'hierarchy-cycle': 'cms.issue.hierarchy-cycle',
|
|
66
|
+
'document-nesting-depth': 'cms.issue.document-nesting-depth',
|
|
67
|
+
'singleton-document-drift': 'cms.issue.singleton-document-drift',
|
|
68
|
+
'duplicate-alias': 'cms.issue.duplicate-alias',
|
|
69
|
+
'unknown-alias': 'cms.issue.unknown-alias',
|
|
70
|
+
'unfilled-slot': 'cms.issue.unfilled-slot',
|
|
71
|
+
'custom-validation': 'cms.issue.custom-validation',
|
|
72
|
+
'unknown-version': 'cms.issue.unknown-version',
|
|
65
73
|
};
|
|
66
74
|
const OPERATION_ERROR_LABELS = {
|
|
67
75
|
validation: "Couldn't save",
|
|
@@ -94,9 +102,14 @@ const CONDITION_OPERATOR_LABELS = {
|
|
|
94
102
|
export function graphIssueLabel(code) {
|
|
95
103
|
return GRAPH_ISSUE_LABELS[code];
|
|
96
104
|
}
|
|
97
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* Plain-language title for a CMS validation issue code — localized against the
|
|
107
|
+
* active studio-UI locale (EC-443). A runtime-unknown code (impossible typewise;
|
|
108
|
+
* a cast could smuggle one) degrades to its humanized form, never the raw enum.
|
|
109
|
+
*/
|
|
98
110
|
export function cmsIssueLabel(code) {
|
|
99
|
-
|
|
111
|
+
const key = CMS_ISSUE_LABEL_KEYS[code];
|
|
112
|
+
return key !== undefined ? translate(key) : humanizeName(code);
|
|
100
113
|
}
|
|
101
114
|
/** Plain-language title for an operation failure code. */
|
|
102
115
|
export function operationErrorLabel(code) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/lib/persona/labels.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IssueCode, ConditionOperator } from '@elytracms/core/project-graph'\nimport type { CmsIssueCode } from '@elytracms/core/cms-core'\nimport { resolveLabel } from '@elytracms/core/cms-core'\nimport type { OperationErrorCode } from '@elytracms/core/operations'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { localizedLabel } from '@elytracms/studio/lib/i18n'\nimport { humanizeName } from '../utils'\n\n/**\n * Persona-aware labels (EC-192 item 2, Milestone B promise).\n *\n * A non-technical editor must never see a manifest/binding/path-token/validation\n * identifier on — or one click off — the editor path. The engine speaks in stable\n * machine codes (`issueCodeSchema`, `cmsIssueCodeSchema`, `operationErrorCodeSchema`)\n * and machine ids (`base.Heading`, `r-blog-slug-en`); this module is the\n * single translation layer the editor-facing surfaces route through.\n *\n * The maps are exhaustive `Record<…Code, string>` over the Zod enums, so adding a\n * code in `@elytracms/core/project-graph`, `@elytracms/core/cms-core`, or `@elytracms/core/operations`\n * fails typecheck here until it is given an editor-facing label — the persona\n * boundary cannot silently regress. Engineers keep the raw code: every surface that\n * shows a label also carries the code in a `title=`/`data-*` attribute (inspectable\n * via hover/devtools, never rendered as visible copy).\n */\n\nconst GRAPH_ISSUE_LABELS: Record<IssueCode, string> = {\n 'invalid-component-name': 'Invalid component',\n 'unknown-component': 'Unknown component',\n 'duplicate-node-id': 'Duplicate item',\n 'unknown-prop': 'Unknown setting',\n 'invalid-prop': 'Invalid setting',\n 'binding-not-allowed': 'Data not allowed here',\n 'unresolved-binding': 'Missing data',\n 'missing-required-slot': 'Missing content',\n 'unknown-slot': 'Unknown content area',\n 'forbidden-component': 'Not allowed here',\n 'server-only-component': 'Not available in the editor',\n 'invalid-container-direction': 'Invalid layout',\n 'invalid-container-alignment': 'Invalid alignment',\n 'invalid-container-gap': 'Invalid spacing',\n 'invalid-container-columns': 'Invalid columns',\n 'invalid-container-responsive': 'Invalid responsive setting',\n}\n\nconst CMS_ISSUE_LABELS: Record<CmsIssueCode, string> = {\n 'duplicate-collection': 'Duplicate collection',\n 'duplicate-field': 'Duplicate field',\n 'unknown-collection': 'Unknown collection',\n 'invalid-collection-config': 'Invalid collection setup',\n 'invalid-field-config': 'Invalid field setup',\n 'reserved-field-name': 'Reserved field name',\n 'missing-required-field': 'Missing required field',\n 'invalid-field-value': 'Invalid value',\n 'unknown-relation-target': 'Missing reference',\n 'unpublished-relation-target': 'Unpublished reference',\n 'cardinality-violation': 'Wrong number of values',\n 'unknown-asset': 'Missing asset',\n 'duplicate-document': 'Duplicate entry',\n 'unknown-locale': 'Unknown language',\n 'missing-localized-value': 'Missing translation',\n 'route-conflict': 'Conflicting URL',\n 'redirect-loop': 'Redirect loop',\n 'unknown-route-target': 'Missing page',\n 'hierarchy-cycle': 'Page nesting loop',\n 'document-nesting-depth': 'Content nested too deeply',\n 'singleton-document-drift': 'More than one document',\n 'duplicate-alias': 'Slot used twice',\n 'unknown-alias': 'Unknown slot',\n 'unfilled-slot': 'Empty slot',\n 'custom-validation': 'Validation rule',\n 'unknown-version': 'Unknown version',\n}\n\nconst OPERATION_ERROR_LABELS: Record<OperationErrorCode, string> = {\n validation: \"Couldn't save\",\n 'not-found': 'Not found',\n conflict: 'Already exists',\n 'backend-unavailable': 'Connection problem',\n unauthorized: 'Not allowed',\n unknown: 'Something went wrong',\n}\n\nconst EXPORT_TARGET_LABELS: Record<string, string> = {\n nextjs: 'Next.js',\n}\n\n/**\n * Plain-language labels for condition operators. Exhaustive over\n * `conditionOperatorSchema` — a new operator fails typecheck here until it has an\n * editor-facing label. The raw operator (`eq`, `gte`, …) is a machine token an\n * editor must never read in the inspector.\n */\nconst CONDITION_OPERATOR_LABELS: Record<ConditionOperator, string> = {\n truthy: 'Is set',\n exists: 'Exists',\n eq: 'Equals',\n neq: 'Does not equal',\n gt: 'Greater than',\n gte: 'At least',\n lt: 'Less than',\n lte: 'At most',\n}\n\n/** Plain-language title for a graph validation issue code. */\nexport function graphIssueLabel(code: IssueCode): string {\n return GRAPH_ISSUE_LABELS[code]\n}\n\n/** Plain-language title for a CMS validation issue code. */\nexport function cmsIssueLabel(code: CmsIssueCode): string {\n return CMS_ISSUE_LABELS[code]\n}\n\n/** Plain-language title for an operation failure code. */\nexport function operationErrorLabel(code: OperationErrorCode): string {\n return OPERATION_ERROR_LABELS[code] ?? OPERATION_ERROR_LABELS.unknown\n}\n\n/** Friendly name for a project export target (`nextjs` → `Next.js`). */\nexport function exportTargetLabel(target: string): string {\n return EXPORT_TARGET_LABELS[target] ?? humanizeName(target)\n}\n\n/** Plain-language label for a condition operator (`gte` → `At least`). */\nexport function conditionOperatorLabel(operator: ConditionOperator): string {\n return CONDITION_OPERATOR_LABELS[operator]\n}\n\n/** The leaf of a component id, namespace stripped (`base.Heading` → `Heading`). */\nexport function componentLeafId(componentId: string): string {\n const dot = componentId.lastIndexOf('.')\n return dot === -1 ? componentId : componentId.slice(dot + 1)\n}\n\n/**\n * Strip the `base.`/`project.` namespace prefix from a component id for compact,\n * editor-facing labels (`base.Heading` → `Heading`). Unlike {@link componentLeafId}\n * it removes only those two known prefixes, so a dotted custom id keeps its shape.\n */\nexport function shortId(componentId: string): string {\n return componentId.replace('base.', '').replace('project.', '')\n}\n\n/**\n * The human display name of a component TYPE for editor-facing surfaces: the\n * manifest's authored `title` (EC-407: resolved through {@link resolveLabel}, so a\n * `{ en, de }` localized title renders in the active studio-UI locale) when present,\n * else the humanized leaf of its id (`base.Heading` → `Heading`,\n * `project.MarketingHero` → `Marketing hero`). Never the raw namespaced id.\n *\n * `locale` is optional: omit it (the render-site default) to resolve against the\n * active studio-UI locale non-reactively; pass it explicitly for pure/unit callers.\n */\nexport function componentDisplayName(\n componentId: string,\n manifest?: ComponentManifest | undefined,\n locale?: string,\n): string {\n const resolved =\n locale === undefined ? localizedLabel(manifest?.title) : (resolveLabel(manifest?.title, locale) ?? '')\n return resolved || humanizeName(componentLeafId(componentId))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/lib/persona/labels.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IssueCode, ConditionOperator } from '@elytracms/core/project-graph'\nimport type { CmsIssueCode } from '@elytracms/core/cms-core'\nimport { resolveLabel } from '@elytracms/core/cms-core'\nimport type { OperationErrorCode } from '@elytracms/core/operations'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { humanizeName } from '../utils'\n\n/**\n * Persona-aware labels (EC-192 item 2, Milestone B promise).\n *\n * A non-technical editor must never see a manifest/binding/path-token/validation\n * identifier on — or one click off — the editor path. The engine speaks in stable\n * machine codes (`issueCodeSchema`, `cmsIssueCodeSchema`, `operationErrorCodeSchema`)\n * and machine ids (`base.Heading`, `r-blog-slug-en`); this module is the\n * single translation layer the editor-facing surfaces route through.\n *\n * The maps are exhaustive `Record<…Code, string>` over the Zod enums, so adding a\n * code in `@elytracms/core/project-graph`, `@elytracms/core/cms-core`, or `@elytracms/core/operations`\n * fails typecheck here until it is given an editor-facing label — the persona\n * boundary cannot silently regress. Engineers keep the raw code: every surface that\n * shows a label also carries the code in a `title=`/`data-*` attribute (inspectable\n * via hover/devtools, never rendered as visible copy).\n */\n\nconst GRAPH_ISSUE_LABELS: Record<IssueCode, string> = {\n 'invalid-component-name': 'Invalid component',\n 'unknown-component': 'Unknown component',\n 'duplicate-node-id': 'Duplicate item',\n 'unknown-prop': 'Unknown setting',\n 'invalid-prop': 'Invalid setting',\n 'binding-not-allowed': 'Data not allowed here',\n 'unresolved-binding': 'Missing data',\n 'missing-required-slot': 'Missing content',\n 'unknown-slot': 'Unknown content area',\n 'forbidden-component': 'Not allowed here',\n 'server-only-component': 'Not available in the editor',\n 'invalid-container-direction': 'Invalid layout',\n 'invalid-container-alignment': 'Invalid alignment',\n 'invalid-container-gap': 'Invalid spacing',\n 'invalid-container-columns': 'Invalid columns',\n 'invalid-container-responsive': 'Invalid responsive setting',\n}\n\n/**\n * EC-443: the CMS issue titles live in the EC-241 message catalog\n * (`cms.issue.<code>`, EN source + shipped de), so the Issues pane / for-review\n * lane localize with the rest of the chrome. This map stays the exhaustive\n * `Record` over the Zod enum — a new code still fails typecheck here until it\n * gets an editor-facing catalog key (the persona boundary cannot silently\n * regress); {@link cmsIssueLabel} resolves it through `translate`.\n */\nconst CMS_ISSUE_LABEL_KEYS: Record<CmsIssueCode, string> = {\n 'duplicate-collection': 'cms.issue.duplicate-collection',\n 'duplicate-field': 'cms.issue.duplicate-field',\n 'unknown-collection': 'cms.issue.unknown-collection',\n 'invalid-collection-config': 'cms.issue.invalid-collection-config',\n 'invalid-field-config': 'cms.issue.invalid-field-config',\n 'reserved-field-name': 'cms.issue.reserved-field-name',\n 'missing-required-field': 'cms.issue.missing-required-field',\n 'invalid-field-value': 'cms.issue.invalid-field-value',\n 'unknown-relation-target': 'cms.issue.unknown-relation-target',\n 'unpublished-relation-target': 'cms.issue.unpublished-relation-target',\n 'cardinality-violation': 'cms.issue.cardinality-violation',\n 'unknown-asset': 'cms.issue.unknown-asset',\n 'duplicate-document': 'cms.issue.duplicate-document',\n 'unknown-locale': 'cms.issue.unknown-locale',\n 'missing-localized-value': 'cms.issue.missing-localized-value',\n 'route-conflict': 'cms.issue.route-conflict',\n 'redirect-loop': 'cms.issue.redirect-loop',\n 'unknown-route-target': 'cms.issue.unknown-route-target',\n 'hierarchy-cycle': 'cms.issue.hierarchy-cycle',\n 'document-nesting-depth': 'cms.issue.document-nesting-depth',\n 'singleton-document-drift': 'cms.issue.singleton-document-drift',\n 'duplicate-alias': 'cms.issue.duplicate-alias',\n 'unknown-alias': 'cms.issue.unknown-alias',\n 'unfilled-slot': 'cms.issue.unfilled-slot',\n 'custom-validation': 'cms.issue.custom-validation',\n 'unknown-version': 'cms.issue.unknown-version',\n}\n\nconst OPERATION_ERROR_LABELS: Record<OperationErrorCode, string> = {\n validation: \"Couldn't save\",\n 'not-found': 'Not found',\n conflict: 'Already exists',\n 'backend-unavailable': 'Connection problem',\n unauthorized: 'Not allowed',\n unknown: 'Something went wrong',\n}\n\nconst EXPORT_TARGET_LABELS: Record<string, string> = {\n nextjs: 'Next.js',\n}\n\n/**\n * Plain-language labels for condition operators. Exhaustive over\n * `conditionOperatorSchema` — a new operator fails typecheck here until it has an\n * editor-facing label. The raw operator (`eq`, `gte`, …) is a machine token an\n * editor must never read in the inspector.\n */\nconst CONDITION_OPERATOR_LABELS: Record<ConditionOperator, string> = {\n truthy: 'Is set',\n exists: 'Exists',\n eq: 'Equals',\n neq: 'Does not equal',\n gt: 'Greater than',\n gte: 'At least',\n lt: 'Less than',\n lte: 'At most',\n}\n\n/** Plain-language title for a graph validation issue code. */\nexport function graphIssueLabel(code: IssueCode): string {\n return GRAPH_ISSUE_LABELS[code]\n}\n\n/**\n * Plain-language title for a CMS validation issue code — localized against the\n * active studio-UI locale (EC-443). A runtime-unknown code (impossible typewise;\n * a cast could smuggle one) degrades to its humanized form, never the raw enum.\n */\nexport function cmsIssueLabel(code: CmsIssueCode): string {\n const key = CMS_ISSUE_LABEL_KEYS[code] as string | undefined\n return key !== undefined ? translate(key) : humanizeName(code)\n}\n\n/** Plain-language title for an operation failure code. */\nexport function operationErrorLabel(code: OperationErrorCode): string {\n return OPERATION_ERROR_LABELS[code] ?? OPERATION_ERROR_LABELS.unknown\n}\n\n/** Friendly name for a project export target (`nextjs` → `Next.js`). */\nexport function exportTargetLabel(target: string): string {\n return EXPORT_TARGET_LABELS[target] ?? humanizeName(target)\n}\n\n/** Plain-language label for a condition operator (`gte` → `At least`). */\nexport function conditionOperatorLabel(operator: ConditionOperator): string {\n return CONDITION_OPERATOR_LABELS[operator]\n}\n\n/** The leaf of a component id, namespace stripped (`base.Heading` → `Heading`). */\nexport function componentLeafId(componentId: string): string {\n const dot = componentId.lastIndexOf('.')\n return dot === -1 ? componentId : componentId.slice(dot + 1)\n}\n\n/**\n * Strip the `base.`/`project.` namespace prefix from a component id for compact,\n * editor-facing labels (`base.Heading` → `Heading`). Unlike {@link componentLeafId}\n * it removes only those two known prefixes, so a dotted custom id keeps its shape.\n */\nexport function shortId(componentId: string): string {\n return componentId.replace('base.', '').replace('project.', '')\n}\n\n/**\n * The human display name of a component TYPE for editor-facing surfaces: the\n * manifest's authored `title` (EC-407: resolved through {@link resolveLabel}, so a\n * `{ en, de }` localized title renders in the active studio-UI locale) when present,\n * else the humanized leaf of its id (`base.Heading` → `Heading`,\n * `project.MarketingHero` → `Marketing hero`). Never the raw namespaced id.\n *\n * `locale` is optional: omit it (the render-site default) to resolve against the\n * active studio-UI locale non-reactively; pass it explicitly for pure/unit callers.\n */\nexport function componentDisplayName(\n componentId: string,\n manifest?: ComponentManifest | undefined,\n locale?: string,\n): string {\n const resolved =\n locale === undefined ? localizedLabel(manifest?.title) : (resolveLabel(manifest?.title, locale) ?? '')\n return resolved || humanizeName(componentLeafId(componentId))\n}\n"]}
|
|
@@ -2,6 +2,7 @@ import type { BreakpointConfig, ElytraConfigInput } from '@elytracms/core/studio
|
|
|
2
2
|
import type { CollectionDef, LocaleConfig, RouteRecord } from '@elytracms/core/cms-core';
|
|
3
3
|
import type { SidebarNode } from '../../components/app-views/module.js';
|
|
4
4
|
import type { CustomFieldRenderer, StudioConfig } from '../../components/app-views/studio-config.js';
|
|
5
|
+
import type { GeneratedPreviewLookup } from '../../components/builder/blocks-model.js';
|
|
5
6
|
import type { WorkspaceState } from './workspace-source.js';
|
|
6
7
|
export { resolveWorkspaceState } from './workspace-source.js';
|
|
7
8
|
export type { WorkspaceSources, WorkspaceState } from './workspace-source.js';
|
|
@@ -119,6 +120,15 @@ export declare function resolveDeploymentI18n(): StudioConfig['i18n'];
|
|
|
119
120
|
* and field renderers.
|
|
120
121
|
*/
|
|
121
122
|
export declare function resolveDeploymentAi(): StudioConfig['ai'];
|
|
123
|
+
/**
|
|
124
|
+
* The generated-preview lookup from the studio config (EC-439 AD-2, the seam
|
|
125
|
+
* EC-440 fills): a pure `componentId + fixtureName → URL` function the blocks
|
|
126
|
+
* panel threads into its tile model. Studio-only live code (from `defineStudio`,
|
|
127
|
+
* never the delivery bundle); `undefined` when none — tiles then fall back from
|
|
128
|
+
* a curated `manifest.previewImage` straight to the icon+name placeholder.
|
|
129
|
+
* Studio-wide, like the rail and field renderers.
|
|
130
|
+
*/
|
|
131
|
+
export declare function resolveDeploymentGeneratedPreviews(): GeneratedPreviewLookup | undefined;
|
|
122
132
|
/**
|
|
123
133
|
* The configurable section rail from the studio config (EC-237): an ordered list of
|
|
124
134
|
* `nav.*` nodes (views / groups / separators). Studio-only live code (carries view
|
|
@@ -186,6 +186,17 @@ export function resolveDeploymentI18n() {
|
|
|
186
186
|
export function resolveDeploymentAi() {
|
|
187
187
|
return studioConfig?.ai;
|
|
188
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* The generated-preview lookup from the studio config (EC-439 AD-2, the seam
|
|
191
|
+
* EC-440 fills): a pure `componentId + fixtureName → URL` function the blocks
|
|
192
|
+
* panel threads into its tile model. Studio-only live code (from `defineStudio`,
|
|
193
|
+
* never the delivery bundle); `undefined` when none — tiles then fall back from
|
|
194
|
+
* a curated `manifest.previewImage` straight to the icon+name placeholder.
|
|
195
|
+
* Studio-wide, like the rail and field renderers.
|
|
196
|
+
*/
|
|
197
|
+
export function resolveDeploymentGeneratedPreviews() {
|
|
198
|
+
return studioConfig?.previews?.generated;
|
|
199
|
+
}
|
|
189
200
|
/**
|
|
190
201
|
* The configurable section rail from the studio config (EC-237): an ordered list of
|
|
191
202
|
* `nav.*` nodes (views / groups / separators). Studio-only live code (carries view
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/workspace/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { frontendUrlForEnvironment } from '@elytracms/core/studio-core'\nimport type {\n BreakpointConfig,\n ElytraConfigInput,\n ResolvedWorkspaceProject,\n} from '@elytracms/core/studio-core'\nimport type { CollectionDef, LocaleConfig, RouteRecord } from '@elytracms/core/cms-core'\nimport type { SidebarNode } from '../../components/app-views/module'\nimport type { CustomFieldRenderer, StudioConfig } from '../../components/app-views/studio-config'\nimport { provideStudioEnv } from '../persistence/backend-mode'\nimport { resolveWorkspaceState } from './workspace-source'\nimport type { WorkspaceState } from './workspace-source'\n\nexport { resolveWorkspaceState } from './workspace-source'\nexport type { WorkspaceSources, WorkspaceState } from './workspace-source'\nexport {\n isSingleProjectWorkspace,\n launcherAutoForwardTarget,\n persistedProjects,\n selectSwitcherProjects,\n soleWorkspaceProject,\n unprovisionedProjects,\n} from './switcher-source'\nexport type { SwitcherEntry, SwitcherSourceInput } from './switcher-source'\n\nlet cached: WorkspaceState | null = null\nlet studioConfig: StudioConfig | null = null\n\n/**\n * Mount the studio against a project config (EC-223 mount contract) and an\n * optional studio-only customization config (EC-236). The consumer's entry point\n * does `import config from './elytra.config'` + `import studio from\n * './studio.config'` and calls this — an explicit init, NOT an `import.meta.glob`\n * autoload that reaches into the consumer's filesystem (Oli, 2026-06-19: \"there's\n * always an entry point that can trigger a function call\").\n *\n * Two configs, deliberately: `config` (`elytra.config.ts`) is the project\n * definition, shared with the `frontend/` delivery bundle and projected to data;\n * `studio` (`studio.config.tsx` via `defineStudio`) is live builder UI code — the\n * section rail, custom field renderers, branding — that must NEVER reach delivery.\n * Keeping them separate is the customization boundary principle in the mount\n * signature. Seeds the workspace state + the studio-config holder every later\n * query (`getActiveProjectSlug`, `resolveDeployment*`) reads.\n *\n * EC-357 — `options.env`: the consumer's `import.meta.env`, captured HERE because\n * the entry module is code Vite always transforms live. The engine's own\n * `import.meta.env` reads are unreliable inside dep-optimized package code (Vite ≥8\n * omits custom `VITE_*` values there; Vite 7 froze them at optimize time), which\n * silently broke backend URL / mode / dev-login resolution in real consumers.\n * Always pass it: `createStudio(config, studioConfig, { env: import.meta.env })`.\n */\nexport interface StudioMountOptions {\n /** The consumer entry's `import.meta.env` (see `provideStudioEnv` for the contract). */\n env?: Record<string, unknown>\n}\n\nexport function createStudio(\n config: ElytraConfigInput,\n studio?: StudioConfig,\n options?: StudioMountOptions,\n): void {\n provideStudioEnv(options?.env)\n cached = resolveWorkspaceState({\n fileModule: { default: config },\n fileName: 'elytra.config.ts',\n })\n studioConfig = studio ?? null\n}\n\n/** Test seam: set/clear the studio customization config. Never used by app code. */\nexport function setStudioConfigForTests(next: StudioConfig | null): void {\n studioConfig = next\n}\n\n/**\n * The active project slug (EC-179, vision AD-8). One project per backend, so the\n * active project selects which Convex deployment the studio talks to. The root\n * layout sets this from the URL (`/<slug>`) — or, on the sign-in screen, from\n * the `redirect` target's slug — before the auth/persistence adapters are\n * selected, so the *initial* sign-in authenticates against the project the user\n * is heading for (Sanity-style: authenticate to one workspace, then the full\n * config-based switcher is available). `null` at the launcher → the default\n * (first) configured project's deployment.\n */\nlet activeProjectSlug: string | null = null\n\n/** Set the active project slug; returns whether the resolved deployment changed. */\nexport function setActiveProjectSlug(slug: string | null): boolean {\n const before = resolveDeploymentUrl('')\n activeProjectSlug = slug && slug.trim() !== '' ? slug : null\n return resolveDeploymentUrl('') !== before\n}\n\nexport function getActiveProjectSlug(): string | null {\n return activeProjectSlug\n}\n\n/** Test seam: reset the active project slug. Never used by app code. */\nexport function setActiveProjectSlugForTests(slug: string | null): void {\n activeProjectSlug = slug\n}\n\n/**\n * Reserved first-path segments that are NOT project slugs: the studio's own\n * root routes (EC-220). Project surfaces now live at `/<slug>/...` (no `/p/`\n * prefix), so the studio's root namespace is shared between project slugs and\n * these fixed routes — they must be excluded from slug extraction. Keep in\n * sync with the non-`$projectId` files in `src/routes/`; the launcher (`/`) is\n * the empty segment, excluded by the regex requiring one path char.\n */\nconst RESERVED_ROOT_SEGMENTS: ReadonlySet<string> = new Set(['sign-in', 'styleguide'])\n\n/** Extract a project slug from a studio path (`/<slug>/...`), else `null`. */\nexport function projectSlugFromPath(pathname: string): string | null {\n const match = /^\\/([^/?#]+)/.exec(pathname)\n if (!match) return null\n const segment = decodeURIComponent(match[1]!)\n return RESERVED_ROOT_SEGMENTS.has(segment) ? null : segment\n}\n\n/**\n * The project whose backend the studio should bind for a given location\n * (EC-179). A project path (`/<slug>`) binds that project; the sign-in screen\n * binds the project from its `redirect` target so the *initial* sign-in\n * authenticates against the backend the user is heading for (Sanity-style).\n * Anywhere else (the launcher) → `null`, i.e. the default/first project.\n */\nexport function activeSlugForLocation(pathname: string, redirect?: string): string | null {\n const direct = projectSlugFromPath(pathname)\n if (direct) return direct\n if (pathname === '/sign-in' && redirect) return projectSlugFromPath(redirect)\n return null\n}\n\n/** The active config project (by slug), or the first configured project. */\nfunction activeConfigProject() {\n const state = getWorkspaceState()\n if (state.kind !== 'config') return undefined\n if (activeProjectSlug) {\n const match = state.projects.find((p) => p.slug === activeProjectSlug)\n if (match) return match\n }\n return state.projects[0]\n}\n\n/**\n * The studio's resolved workspace state. Deterministic per boot: the config is\n * static input (bundled file or deployment env), so it is resolved once and\n * cached — every consumer (gate, switcher, launcher) sees the same state.\n */\nexport function getWorkspaceState(): WorkspaceState {\n // Seeded by `createStudio(config)` at the entry point. Until then — or when the\n // consumer wires no config — resolve to the empty `none` state (the single-project\n // golden path), rather than reaching for the filesystem.\n if (!cached) cached = resolveWorkspaceState({})\n return cached\n}\n\n/** Test seam: clear/replace the cached state. Never used by app code. */\nexport function setWorkspaceStateForTests(next: WorkspaceState | null): void {\n cached = next\n}\n\n/**\n * Resolve the Convex deployment URL the studio should connect to (vision AD-8:\n * the backend is wired in config). One project per backend, so the **active**\n * project (see {@link setActiveProjectSlug}) selects the deployment via its\n * `convex.deployment` (or its production environment's). At the launcher (no\n * active project) the first configured project is the default backend. Absent\n * config — or a project without a wired deployment — falls back to\n * `VITE_CONVEX_URL` (`fallbackUrl`) for local single-deployment development.\n */\nexport function resolveDeploymentUrl(fallbackUrl: string): string {\n return projectDeploymentUrl(activeConfigProject(), fallbackUrl)\n}\n\n/** The deployment URL for a specific project slug (per-backend auth checks). */\nexport function deploymentUrlForSlug(slug: string, fallbackUrl: string): string {\n const state = getWorkspaceState()\n const project = state.kind === 'config' ? state.projects.find((p) => p.slug === slug) : undefined\n return projectDeploymentUrl(project, fallbackUrl)\n}\n\n/** A project's wired deployment: per-environment ref → project ref → env fallback. */\nfunction projectDeploymentUrl(\n project: ResolvedWorkspaceProject | undefined,\n fallbackUrl: string,\n): string {\n if (!project) return fallbackUrl\n const envDeployment = project.environments.find((e) => e.convex?.deployment)?.convex?.deployment\n return envDeployment ?? project.convex?.deployment ?? fallbackUrl\n}\n\n/**\n * The slug of the project bound to the active deployment (EC-179). One project\n * per backend, so the active config project's slug identifies the deployment's\n * one project; the Convex adapter stamps it onto deployment-scoped records.\n * Absent config falls back to `fallbackId` (used only when wired by env alone).\n */\nexport function resolveDeploymentProjectId(fallbackId: string): string {\n return activeConfigProject()?.slug ?? fallbackId\n}\n\n/**\n * The CMS collections of the active config project (AD-11, EC-183). CMS\n * collection schemas are config-owned: the studio reads the model from\n * `elytra.config.ts` (served through the persistence port — the backend has no\n * `cmsSchemas` table). `[]` when the active project declares none, or when the\n * studio is wired by env alone with no config.\n */\nexport function resolveDeploymentCollections(): CollectionDef[] {\n return activeConfigProject()?.collections ?? []\n}\n\n/**\n * The canvas preview breakpoints of the active config project (EC-250).\n * Config-sourced (config-as-code, derived from the site's Tailwind `screens`);\n * `undefined` when the project declares none — the canvas then falls back to\n * Tailwind's default screens (see `resolveCanvasViewports`), never a baked-in triple.\n */\nexport function resolveDeploymentBreakpoints(): BreakpointConfig[] | undefined {\n return activeConfigProject()?.breakpoints\n}\n\n/**\n * The custom field renderers from the studio config (EC-238): control name →\n * component. Studio-only live code (from `defineStudio`, never the project schema\n * or the delivery bundle); `undefined` when none — the document editor then uses\n * its built-in control for every field. Studio-wide (not per-project): the rail and\n * renderers customize the studio, not one backend.\n */\nexport function resolveDeploymentFieldRenderers(): Record<string, CustomFieldRenderer> | undefined {\n return studioConfig?.fieldRenderers\n}\n\n/**\n * The studio-UI localization config from the studio config (EC-241): the default\n * locale + imported translation catalogs. Studio-only live code (from `defineStudio`,\n * never the project schema or the delivery bundle); `undefined` when none — the\n * studio then renders its built-in English chrome. Studio-wide, like the rail and\n * field renderers (localizes the studio, not one backend).\n */\nexport function resolveDeploymentI18n(): StudioConfig['i18n'] {\n return studioConfig?.i18n\n}\n\n/**\n * The AI platform config from the studio config (EC-331): provider, knowledge, and\n * limits. Studio-only live code (from `defineStudio`, never the delivery bundle);\n * `undefined` when no `ai` block is configured — the feature is then fully absent\n * (the AI subsystem never boots, so `@elytracms/studio/lib/ai`'s runtime — and the\n * `ai`/`@ai-sdk/*` SDK behind it — is never imported). Studio-wide, like the rail\n * and field renderers.\n */\nexport function resolveDeploymentAi(): StudioConfig['ai'] {\n return studioConfig?.ai\n}\n\n/**\n * The configurable section rail from the studio config (EC-237): an ordered list of\n * `nav.*` nodes (views / groups / separators). Studio-only live code (carries view\n * objects + icons); `undefined` when none — the studio then derives the default\n * rail from its built-in views. Invalid entries surface as visible issues at\n * resolution, never silent.\n */\nexport function resolveDeploymentSidebar(): SidebarNode[] | undefined {\n return studioConfig?.sidebar\n}\n\n/**\n * The canvas page-surface theme from the studio config (EC-389): body classes +\n * raw CSS for the preview iframe, so the canvas wears the SITE's chrome. Studio-only\n * (from `defineStudio`, never the delivery bundle); `undefined` when none — the\n * canvas keeps its built-in defaults (`bg-background text-foreground font-sans`\n * body, kit `tokensCss` only). Studio-wide, like the rail and field renderers.\n */\nexport function resolveDeploymentCanvasTheme(): StudioConfig['canvas'] {\n return studioConfig?.canvas\n}\n\n/**\n * The resolved URL routes of the active config project (EC-207, \"routes as\n * code\"). Routes are config-sourced: the studio reads the materialized\n * `RouteRecord[]` from the active `elytra.config.ts` project — never from the\n * backend (the `routes` system collection is retired). The Routes surface and\n * the per-page URL preview both read through here. `[]` when the active project\n * declares none, or when the studio is wired by env alone with no config.\n */\nexport function resolveDeploymentRoutes(): RouteRecord[] {\n return activeConfigProject()?.routes ?? []\n}\n\n// Redirects are editorial DATA now (EC-303), managed in the studio Redirects rail\n// and served dynamically — no longer resolved from config. `resolveDeploymentRedirects`\n// is retired; static/infra redirects are the dev's job via native next.config/vercel.\n\n/**\n * The configured locales of the active config project (AD-8). The same locale\n * set the host stamps onto its per-locale cache tags — used by the revalidation\n * webhook layer (EC-192) so a publish always invalidates EVERY configured locale\n * (e.g. `/de`, `/fr`), never just the default. `[]` when wired by env alone.\n */\nexport function resolveDeploymentLocales(): string[] {\n return activeConfigProject()?.locales ?? []\n}\n\n/**\n * The active config project's locales as a `LocaleConfig` (EC-357 finding #3).\n * Locales are structure and structure is code: when `elytra.config.ts` declares\n * them, they drive the studio's ambient locale switcher in EVERY mode — the CMS\n * surface must not fall back to the sample `en/de` fixture (playground, or a\n * connected first run with nothing persisted) when the config says otherwise.\n * `undefined` when no config project is active or it declares no locales (env-only\n * wiring keeps the existing kit/sample fallbacks). A missing/invalid explicit\n * default falls back to the first declared locale.\n */\nexport function resolveDeploymentLocaleConfig(): LocaleConfig | undefined {\n const project = activeConfigProject()\n if (!project || project.locales.length === 0) return undefined\n const fallback = project.locales[0]!\n const explicit = project.defaultLocale\n return {\n default: explicit && project.locales.includes(explicit) ? explicit : fallback,\n locales: [...project.locales],\n }\n}\n\n/**\n * The active project's components reference (EC-194, AD-11): where the consuming\n * repo's OWN real components live (`{ package }` or `{ dir }`). When set, the\n * studio registry composes with the repo's components instead of the built-in\n * demo fixtures — so the page builder edits the repo's actual component library.\n * `undefined` → the project ships no code components; the studio falls back to the\n * golden-path fixtures.\n */\nexport function resolveDeploymentComponents() {\n return activeConfigProject()?.components\n}\n\n/**\n * The active project's public site URL (EC-215): the live site's base URL the\n * studio links to (a document's live URL + open-live button, EC-216). Resolved\n * in lockstep with {@link resolveDeploymentUrl} — the environment bound to the\n * active deployment (the first with a wired deployment) provides the URL, falling\n * back to the project-level `frontendUrl`. No active-environment selector exists\n * yet, so the bound environment is the active one. `undefined` when the active\n * project (or env-only wiring) configures no frontend URL.\n */\nexport function resolveDeploymentFrontendUrl(): string | undefined {\n const project = activeConfigProject()\n if (!project) return undefined\n const boundEnv = project.environments.find((e) => e.convex?.deployment)\n return frontendUrlForEnvironment(project, boundEnv?.key)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/workspace/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { frontendUrlForEnvironment } from '@elytracms/core/studio-core'\nimport type {\n BreakpointConfig,\n ElytraConfigInput,\n ResolvedWorkspaceProject,\n} from '@elytracms/core/studio-core'\nimport type { CollectionDef, LocaleConfig, RouteRecord } from '@elytracms/core/cms-core'\nimport type { SidebarNode } from '../../components/app-views/module'\nimport type { CustomFieldRenderer, StudioConfig } from '../../components/app-views/studio-config'\nimport type { GeneratedPreviewLookup } from '../../components/builder/blocks-model'\nimport { provideStudioEnv } from '../persistence/backend-mode'\nimport { resolveWorkspaceState } from './workspace-source'\nimport type { WorkspaceState } from './workspace-source'\n\nexport { resolveWorkspaceState } from './workspace-source'\nexport type { WorkspaceSources, WorkspaceState } from './workspace-source'\nexport {\n isSingleProjectWorkspace,\n launcherAutoForwardTarget,\n persistedProjects,\n selectSwitcherProjects,\n soleWorkspaceProject,\n unprovisionedProjects,\n} from './switcher-source'\nexport type { SwitcherEntry, SwitcherSourceInput } from './switcher-source'\n\nlet cached: WorkspaceState | null = null\nlet studioConfig: StudioConfig | null = null\n\n/**\n * Mount the studio against a project config (EC-223 mount contract) and an\n * optional studio-only customization config (EC-236). The consumer's entry point\n * does `import config from './elytra.config'` + `import studio from\n * './studio.config'` and calls this — an explicit init, NOT an `import.meta.glob`\n * autoload that reaches into the consumer's filesystem (Oli, 2026-06-19: \"there's\n * always an entry point that can trigger a function call\").\n *\n * Two configs, deliberately: `config` (`elytra.config.ts`) is the project\n * definition, shared with the `frontend/` delivery bundle and projected to data;\n * `studio` (`studio.config.tsx` via `defineStudio`) is live builder UI code — the\n * section rail, custom field renderers, branding — that must NEVER reach delivery.\n * Keeping them separate is the customization boundary principle in the mount\n * signature. Seeds the workspace state + the studio-config holder every later\n * query (`getActiveProjectSlug`, `resolveDeployment*`) reads.\n *\n * EC-357 — `options.env`: the consumer's `import.meta.env`, captured HERE because\n * the entry module is code Vite always transforms live. The engine's own\n * `import.meta.env` reads are unreliable inside dep-optimized package code (Vite ≥8\n * omits custom `VITE_*` values there; Vite 7 froze them at optimize time), which\n * silently broke backend URL / mode / dev-login resolution in real consumers.\n * Always pass it: `createStudio(config, studioConfig, { env: import.meta.env })`.\n */\nexport interface StudioMountOptions {\n /** The consumer entry's `import.meta.env` (see `provideStudioEnv` for the contract). */\n env?: Record<string, unknown>\n}\n\nexport function createStudio(\n config: ElytraConfigInput,\n studio?: StudioConfig,\n options?: StudioMountOptions,\n): void {\n provideStudioEnv(options?.env)\n cached = resolveWorkspaceState({\n fileModule: { default: config },\n fileName: 'elytra.config.ts',\n })\n studioConfig = studio ?? null\n}\n\n/** Test seam: set/clear the studio customization config. Never used by app code. */\nexport function setStudioConfigForTests(next: StudioConfig | null): void {\n studioConfig = next\n}\n\n/**\n * The active project slug (EC-179, vision AD-8). One project per backend, so the\n * active project selects which Convex deployment the studio talks to. The root\n * layout sets this from the URL (`/<slug>`) — or, on the sign-in screen, from\n * the `redirect` target's slug — before the auth/persistence adapters are\n * selected, so the *initial* sign-in authenticates against the project the user\n * is heading for (Sanity-style: authenticate to one workspace, then the full\n * config-based switcher is available). `null` at the launcher → the default\n * (first) configured project's deployment.\n */\nlet activeProjectSlug: string | null = null\n\n/** Set the active project slug; returns whether the resolved deployment changed. */\nexport function setActiveProjectSlug(slug: string | null): boolean {\n const before = resolveDeploymentUrl('')\n activeProjectSlug = slug && slug.trim() !== '' ? slug : null\n return resolveDeploymentUrl('') !== before\n}\n\nexport function getActiveProjectSlug(): string | null {\n return activeProjectSlug\n}\n\n/** Test seam: reset the active project slug. Never used by app code. */\nexport function setActiveProjectSlugForTests(slug: string | null): void {\n activeProjectSlug = slug\n}\n\n/**\n * Reserved first-path segments that are NOT project slugs: the studio's own\n * root routes (EC-220). Project surfaces now live at `/<slug>/...` (no `/p/`\n * prefix), so the studio's root namespace is shared between project slugs and\n * these fixed routes — they must be excluded from slug extraction. Keep in\n * sync with the non-`$projectId` files in `src/routes/`; the launcher (`/`) is\n * the empty segment, excluded by the regex requiring one path char.\n */\nconst RESERVED_ROOT_SEGMENTS: ReadonlySet<string> = new Set(['sign-in', 'styleguide'])\n\n/** Extract a project slug from a studio path (`/<slug>/...`), else `null`. */\nexport function projectSlugFromPath(pathname: string): string | null {\n const match = /^\\/([^/?#]+)/.exec(pathname)\n if (!match) return null\n const segment = decodeURIComponent(match[1]!)\n return RESERVED_ROOT_SEGMENTS.has(segment) ? null : segment\n}\n\n/**\n * The project whose backend the studio should bind for a given location\n * (EC-179). A project path (`/<slug>`) binds that project; the sign-in screen\n * binds the project from its `redirect` target so the *initial* sign-in\n * authenticates against the backend the user is heading for (Sanity-style).\n * Anywhere else (the launcher) → `null`, i.e. the default/first project.\n */\nexport function activeSlugForLocation(pathname: string, redirect?: string): string | null {\n const direct = projectSlugFromPath(pathname)\n if (direct) return direct\n if (pathname === '/sign-in' && redirect) return projectSlugFromPath(redirect)\n return null\n}\n\n/** The active config project (by slug), or the first configured project. */\nfunction activeConfigProject() {\n const state = getWorkspaceState()\n if (state.kind !== 'config') return undefined\n if (activeProjectSlug) {\n const match = state.projects.find((p) => p.slug === activeProjectSlug)\n if (match) return match\n }\n return state.projects[0]\n}\n\n/**\n * The studio's resolved workspace state. Deterministic per boot: the config is\n * static input (bundled file or deployment env), so it is resolved once and\n * cached — every consumer (gate, switcher, launcher) sees the same state.\n */\nexport function getWorkspaceState(): WorkspaceState {\n // Seeded by `createStudio(config)` at the entry point. Until then — or when the\n // consumer wires no config — resolve to the empty `none` state (the single-project\n // golden path), rather than reaching for the filesystem.\n if (!cached) cached = resolveWorkspaceState({})\n return cached\n}\n\n/** Test seam: clear/replace the cached state. Never used by app code. */\nexport function setWorkspaceStateForTests(next: WorkspaceState | null): void {\n cached = next\n}\n\n/**\n * Resolve the Convex deployment URL the studio should connect to (vision AD-8:\n * the backend is wired in config). One project per backend, so the **active**\n * project (see {@link setActiveProjectSlug}) selects the deployment via its\n * `convex.deployment` (or its production environment's). At the launcher (no\n * active project) the first configured project is the default backend. Absent\n * config — or a project without a wired deployment — falls back to\n * `VITE_CONVEX_URL` (`fallbackUrl`) for local single-deployment development.\n */\nexport function resolveDeploymentUrl(fallbackUrl: string): string {\n return projectDeploymentUrl(activeConfigProject(), fallbackUrl)\n}\n\n/** The deployment URL for a specific project slug (per-backend auth checks). */\nexport function deploymentUrlForSlug(slug: string, fallbackUrl: string): string {\n const state = getWorkspaceState()\n const project = state.kind === 'config' ? state.projects.find((p) => p.slug === slug) : undefined\n return projectDeploymentUrl(project, fallbackUrl)\n}\n\n/** A project's wired deployment: per-environment ref → project ref → env fallback. */\nfunction projectDeploymentUrl(\n project: ResolvedWorkspaceProject | undefined,\n fallbackUrl: string,\n): string {\n if (!project) return fallbackUrl\n const envDeployment = project.environments.find((e) => e.convex?.deployment)?.convex?.deployment\n return envDeployment ?? project.convex?.deployment ?? fallbackUrl\n}\n\n/**\n * The slug of the project bound to the active deployment (EC-179). One project\n * per backend, so the active config project's slug identifies the deployment's\n * one project; the Convex adapter stamps it onto deployment-scoped records.\n * Absent config falls back to `fallbackId` (used only when wired by env alone).\n */\nexport function resolveDeploymentProjectId(fallbackId: string): string {\n return activeConfigProject()?.slug ?? fallbackId\n}\n\n/**\n * The CMS collections of the active config project (AD-11, EC-183). CMS\n * collection schemas are config-owned: the studio reads the model from\n * `elytra.config.ts` (served through the persistence port — the backend has no\n * `cmsSchemas` table). `[]` when the active project declares none, or when the\n * studio is wired by env alone with no config.\n */\nexport function resolveDeploymentCollections(): CollectionDef[] {\n return activeConfigProject()?.collections ?? []\n}\n\n/**\n * The canvas preview breakpoints of the active config project (EC-250).\n * Config-sourced (config-as-code, derived from the site's Tailwind `screens`);\n * `undefined` when the project declares none — the canvas then falls back to\n * Tailwind's default screens (see `resolveCanvasViewports`), never a baked-in triple.\n */\nexport function resolveDeploymentBreakpoints(): BreakpointConfig[] | undefined {\n return activeConfigProject()?.breakpoints\n}\n\n/**\n * The custom field renderers from the studio config (EC-238): control name →\n * component. Studio-only live code (from `defineStudio`, never the project schema\n * or the delivery bundle); `undefined` when none — the document editor then uses\n * its built-in control for every field. Studio-wide (not per-project): the rail and\n * renderers customize the studio, not one backend.\n */\nexport function resolveDeploymentFieldRenderers(): Record<string, CustomFieldRenderer> | undefined {\n return studioConfig?.fieldRenderers\n}\n\n/**\n * The studio-UI localization config from the studio config (EC-241): the default\n * locale + imported translation catalogs. Studio-only live code (from `defineStudio`,\n * never the project schema or the delivery bundle); `undefined` when none — the\n * studio then renders its built-in English chrome. Studio-wide, like the rail and\n * field renderers (localizes the studio, not one backend).\n */\nexport function resolveDeploymentI18n(): StudioConfig['i18n'] {\n return studioConfig?.i18n\n}\n\n/**\n * The AI platform config from the studio config (EC-331): provider, knowledge, and\n * limits. Studio-only live code (from `defineStudio`, never the delivery bundle);\n * `undefined` when no `ai` block is configured — the feature is then fully absent\n * (the AI subsystem never boots, so `@elytracms/studio/lib/ai`'s runtime — and the\n * `ai`/`@ai-sdk/*` SDK behind it — is never imported). Studio-wide, like the rail\n * and field renderers.\n */\nexport function resolveDeploymentAi(): StudioConfig['ai'] {\n return studioConfig?.ai\n}\n\n/**\n * The generated-preview lookup from the studio config (EC-439 AD-2, the seam\n * EC-440 fills): a pure `componentId + fixtureName → URL` function the blocks\n * panel threads into its tile model. Studio-only live code (from `defineStudio`,\n * never the delivery bundle); `undefined` when none — tiles then fall back from\n * a curated `manifest.previewImage` straight to the icon+name placeholder.\n * Studio-wide, like the rail and field renderers.\n */\nexport function resolveDeploymentGeneratedPreviews(): GeneratedPreviewLookup | undefined {\n return studioConfig?.previews?.generated\n}\n\n/**\n * The configurable section rail from the studio config (EC-237): an ordered list of\n * `nav.*` nodes (views / groups / separators). Studio-only live code (carries view\n * objects + icons); `undefined` when none — the studio then derives the default\n * rail from its built-in views. Invalid entries surface as visible issues at\n * resolution, never silent.\n */\nexport function resolveDeploymentSidebar(): SidebarNode[] | undefined {\n return studioConfig?.sidebar\n}\n\n/**\n * The canvas page-surface theme from the studio config (EC-389): body classes +\n * raw CSS for the preview iframe, so the canvas wears the SITE's chrome. Studio-only\n * (from `defineStudio`, never the delivery bundle); `undefined` when none — the\n * canvas keeps its built-in defaults (`bg-background text-foreground font-sans`\n * body, kit `tokensCss` only). Studio-wide, like the rail and field renderers.\n */\nexport function resolveDeploymentCanvasTheme(): StudioConfig['canvas'] {\n return studioConfig?.canvas\n}\n\n/**\n * The resolved URL routes of the active config project (EC-207, \"routes as\n * code\"). Routes are config-sourced: the studio reads the materialized\n * `RouteRecord[]` from the active `elytra.config.ts` project — never from the\n * backend (the `routes` system collection is retired). The Routes surface and\n * the per-page URL preview both read through here. `[]` when the active project\n * declares none, or when the studio is wired by env alone with no config.\n */\nexport function resolveDeploymentRoutes(): RouteRecord[] {\n return activeConfigProject()?.routes ?? []\n}\n\n// Redirects are editorial DATA now (EC-303), managed in the studio Redirects rail\n// and served dynamically — no longer resolved from config. `resolveDeploymentRedirects`\n// is retired; static/infra redirects are the dev's job via native next.config/vercel.\n\n/**\n * The configured locales of the active config project (AD-8). The same locale\n * set the host stamps onto its per-locale cache tags — used by the revalidation\n * webhook layer (EC-192) so a publish always invalidates EVERY configured locale\n * (e.g. `/de`, `/fr`), never just the default. `[]` when wired by env alone.\n */\nexport function resolveDeploymentLocales(): string[] {\n return activeConfigProject()?.locales ?? []\n}\n\n/**\n * The active config project's locales as a `LocaleConfig` (EC-357 finding #3).\n * Locales are structure and structure is code: when `elytra.config.ts` declares\n * them, they drive the studio's ambient locale switcher in EVERY mode — the CMS\n * surface must not fall back to the sample `en/de` fixture (playground, or a\n * connected first run with nothing persisted) when the config says otherwise.\n * `undefined` when no config project is active or it declares no locales (env-only\n * wiring keeps the existing kit/sample fallbacks). A missing/invalid explicit\n * default falls back to the first declared locale.\n */\nexport function resolveDeploymentLocaleConfig(): LocaleConfig | undefined {\n const project = activeConfigProject()\n if (!project || project.locales.length === 0) return undefined\n const fallback = project.locales[0]!\n const explicit = project.defaultLocale\n return {\n default: explicit && project.locales.includes(explicit) ? explicit : fallback,\n locales: [...project.locales],\n }\n}\n\n/**\n * The active project's components reference (EC-194, AD-11): where the consuming\n * repo's OWN real components live (`{ package }` or `{ dir }`). When set, the\n * studio registry composes with the repo's components instead of the built-in\n * demo fixtures — so the page builder edits the repo's actual component library.\n * `undefined` → the project ships no code components; the studio falls back to the\n * golden-path fixtures.\n */\nexport function resolveDeploymentComponents() {\n return activeConfigProject()?.components\n}\n\n/**\n * The active project's public site URL (EC-215): the live site's base URL the\n * studio links to (a document's live URL + open-live button, EC-216). Resolved\n * in lockstep with {@link resolveDeploymentUrl} — the environment bound to the\n * active deployment (the first with a wired deployment) provides the URL, falling\n * back to the project-level `frontendUrl`. No active-environment selector exists\n * yet, so the bound environment is the active one. `undefined` when the active\n * project (or env-only wiring) configures no frontend URL.\n */\nexport function resolveDeploymentFrontendUrl(): string | undefined {\n const project = activeConfigProject()\n if (!project) return undefined\n const boundEnv = project.environments.find((e) => e.convex?.deployment)\n return frontendUrlForEnvironment(project, boundEnv?.key)\n}\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ComponentFixture, ComponentManifest } from '@elytracms/core/component-registry';
|
|
2
|
+
import type { ComponentImplementations } from '@elytracms/core/runtime-renderer';
|
|
3
|
+
import type { StudioRegistry } from '../lib/registry/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* EC-440 — the fixture-preview enumeration behind `elytra previews`. ONE
|
|
6
|
+
* source of truth for "which previews exist" shared by the CLI's node-side
|
|
7
|
+
* driver (hashing, staleness `--check`) and the browser render harness
|
|
8
|
+
* ({@link import('./harness').mountPreviewHarness}) — both derive the entry
|
|
9
|
+
* set from the consumer's `elytra.config.ts` default export through here, so
|
|
10
|
+
* generation, staleness checking, and rendering can never disagree.
|
|
11
|
+
*
|
|
12
|
+
* DATA-FREE by construction: this module reads the consumer CONFIG (registry
|
|
13
|
+
* manifests + implementations — code), never a persistence adapter, auth, or
|
|
14
|
+
* content data. The entry set mirrors the blocks panel exactly (EC-439): every
|
|
15
|
+
* canvas-eligible manifest, one entry per declared fixture, and a single
|
|
16
|
+
* implicit {@link DEFAULT_VARIANT} entry for fixture-less manifests. Base
|
|
17
|
+
* primitives are included — they are insertable tiles too. Layout-helper
|
|
18
|
+
* presets are NOT manifests and keep their placeholder treatment.
|
|
19
|
+
*/
|
|
20
|
+
/** One renderable preview: a component fixture plus its staleness-hash inputs. */
|
|
21
|
+
export interface PreviewEntrySpec {
|
|
22
|
+
componentId: string;
|
|
23
|
+
/** The fixture name, or {@link DEFAULT_VARIANT} for a fixture-less manifest. */
|
|
24
|
+
fixture: string;
|
|
25
|
+
/**
|
|
26
|
+
* The render-relevant inputs the CLI hashes (with the engine version) for
|
|
27
|
+
* staleness: the manifest's prop + slot shape (defaults live in the prop
|
|
28
|
+
* field-defs, so seeded renders are covered) and the declared fixture's own
|
|
29
|
+
* props/slotChildren (`fixture: null` for the implicit Default variant).
|
|
30
|
+
* Deliberately NOT hashed: the component implementation and the theme —
|
|
31
|
+
* drift there is caught by regeneration runs, not by the hash (the ratified
|
|
32
|
+
* EC-440 staleness split).
|
|
33
|
+
*/
|
|
34
|
+
hashInput: {
|
|
35
|
+
manifest: {
|
|
36
|
+
props: ComponentManifest['props'];
|
|
37
|
+
slots: ComponentManifest['slots'];
|
|
38
|
+
};
|
|
39
|
+
fixture: {
|
|
40
|
+
props: NonNullable<ComponentFixture['props']> | null;
|
|
41
|
+
slotChildren: NonNullable<ComponentFixture['slotChildren']> | null;
|
|
42
|
+
} | null;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The component set a config carries, as the harness registry input: the base
|
|
47
|
+
* primitives plus every resolved project's `components` entries, deduped by
|
|
48
|
+
* component id (first declaration wins — the generated-preview seam is keyed
|
|
49
|
+
* by component id alone, so one image serves every project that registers the
|
|
50
|
+
* id). A config that fails shape-resolution contributes no project components
|
|
51
|
+
* — the primitives still enumerate, and the CLI surfaces config problems
|
|
52
|
+
* through the studio/doctor, not here.
|
|
53
|
+
*/
|
|
54
|
+
export declare function previewComponentsFromConfig(config: unknown): {
|
|
55
|
+
manifests: ComponentManifest[];
|
|
56
|
+
implementations: ComponentImplementations;
|
|
57
|
+
};
|
|
58
|
+
/** The {@link StudioRegistry}-shaped registry the harness renders against. */
|
|
59
|
+
export declare function previewRegistryFromConfig(config: unknown): StudioRegistry;
|
|
60
|
+
/**
|
|
61
|
+
* Enumerate every generated-preview entry for a config: canvas-eligible
|
|
62
|
+
* manifests only (server-only components never render on the client canvas),
|
|
63
|
+
* one entry per declared fixture in declaration order, else one implicit
|
|
64
|
+
* {@link DEFAULT_VARIANT} entry. Components are sorted by id so the output —
|
|
65
|
+
* and everything the CLI derives from it (manifest JSON, file writes) — is
|
|
66
|
+
* deterministic regardless of registration order.
|
|
67
|
+
*/
|
|
68
|
+
export declare function previewEntriesForConfig(config: unknown): PreviewEntrySpec[];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ComponentRegistry, isCanvasEligible } from '@elytracms/core/component-registry';
|
|
2
|
+
import { basePrimitives, getImplementation } from '@elytracms/core/runtime-renderer';
|
|
3
|
+
import { resolveWorkspaceConfig } from '@elytracms/core/studio-core';
|
|
4
|
+
import { DEFAULT_VARIANT } from '../components/builder/blocks-model.js';
|
|
5
|
+
/**
|
|
6
|
+
* The component set a config carries, as the harness registry input: the base
|
|
7
|
+
* primitives plus every resolved project's `components` entries, deduped by
|
|
8
|
+
* component id (first declaration wins — the generated-preview seam is keyed
|
|
9
|
+
* by component id alone, so one image serves every project that registers the
|
|
10
|
+
* id). A config that fails shape-resolution contributes no project components
|
|
11
|
+
* — the primitives still enumerate, and the CLI surfaces config problems
|
|
12
|
+
* through the studio/doctor, not here.
|
|
13
|
+
*/
|
|
14
|
+
export function previewComponentsFromConfig(config) {
|
|
15
|
+
const base = basePrimitives();
|
|
16
|
+
const manifests = [...base.manifests];
|
|
17
|
+
const implementations = {};
|
|
18
|
+
for (const manifest of base.manifests) {
|
|
19
|
+
const implementation = getImplementation(base.implementations, manifest.id);
|
|
20
|
+
if (implementation)
|
|
21
|
+
implementations[manifest.id] = implementation;
|
|
22
|
+
}
|
|
23
|
+
const seen = new Set(manifests.map((m) => m.id));
|
|
24
|
+
const resolved = resolveWorkspaceConfig(config);
|
|
25
|
+
for (const project of resolved.projects) {
|
|
26
|
+
for (const entry of project.components ?? []) {
|
|
27
|
+
if (seen.has(entry.manifest.id))
|
|
28
|
+
continue;
|
|
29
|
+
seen.add(entry.manifest.id);
|
|
30
|
+
manifests.push(entry.manifest);
|
|
31
|
+
implementations[entry.manifest.id] = entry.implementation;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { manifests, implementations };
|
|
35
|
+
}
|
|
36
|
+
/** The {@link StudioRegistry}-shaped registry the harness renders against. */
|
|
37
|
+
export function previewRegistryFromConfig(config) {
|
|
38
|
+
const { manifests, implementations } = previewComponentsFromConfig(config);
|
|
39
|
+
return { registry: new ComponentRegistry(manifests), implementations };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Enumerate every generated-preview entry for a config: canvas-eligible
|
|
43
|
+
* manifests only (server-only components never render on the client canvas),
|
|
44
|
+
* one entry per declared fixture in declaration order, else one implicit
|
|
45
|
+
* {@link DEFAULT_VARIANT} entry. Components are sorted by id so the output —
|
|
46
|
+
* and everything the CLI derives from it (manifest JSON, file writes) — is
|
|
47
|
+
* deterministic regardless of registration order.
|
|
48
|
+
*/
|
|
49
|
+
export function previewEntriesForConfig(config) {
|
|
50
|
+
const { manifests } = previewComponentsFromConfig(config);
|
|
51
|
+
const entries = [];
|
|
52
|
+
for (const manifest of [...manifests].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
53
|
+
if (!isCanvasEligible(manifest))
|
|
54
|
+
continue;
|
|
55
|
+
const manifestShape = { props: manifest.props, slots: manifest.slots };
|
|
56
|
+
if (manifest.fixtures && manifest.fixtures.length > 0) {
|
|
57
|
+
for (const fixture of manifest.fixtures) {
|
|
58
|
+
entries.push({
|
|
59
|
+
componentId: manifest.id,
|
|
60
|
+
fixture: fixture.name,
|
|
61
|
+
hashInput: {
|
|
62
|
+
manifest: manifestShape,
|
|
63
|
+
fixture: { props: fixture.props ?? null, slotChildren: fixture.slotChildren ?? null },
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
entries.push({
|
|
70
|
+
componentId: manifest.id,
|
|
71
|
+
fixture: DEFAULT_VARIANT,
|
|
72
|
+
hashInput: { manifest: manifestShape, fixture: null },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return entries;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=enumerate.js.map
|
|
@@ -0,0 +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 { ComponentFixture, ComponentManifest } from '@elytracms/core/component-registry'\nimport { basePrimitives, getImplementation } from '@elytracms/core/runtime-renderer'\nimport type { ComponentImplementations } 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. 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 render-relevant inputs the CLI hashes (with the engine version) for\n * staleness: the manifest's prop + slot shape (defaults live in the prop\n * field-defs, so seeded renders are covered) and the declared fixture's own\n * props/slotChildren (`fixture: null` for the implicit Default variant).\n * Deliberately NOT hashed: the component implementation and the theme —\n * drift there is caught by regeneration runs, not by the hash (the ratified\n * EC-440 staleness split).\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 */\nexport function previewComponentsFromConfig(config: unknown): {\n manifests: ComponentManifest[]\n implementations: ComponentImplementations\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 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 implementations[entry.manifest.id] = entry.implementation as ComponentType<Record<string, unknown>>\n }\n }\n return { manifests, implementations }\n}\n\n/** The {@link StudioRegistry}-shaped registry the harness renders against. */\nexport function previewRegistryFromConfig(config: unknown): StudioRegistry {\n const { manifests, implementations } = previewComponentsFromConfig(config)\n return { registry: new ComponentRegistry(manifests), 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"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EC-440 — the browser half of the `elytra previews` render harness. The CLI
|
|
3
|
+
* boots the consumer studio app's OWN Vite (programmatically, with a minimal
|
|
4
|
+
* inline config) and serves a page whose entry module does exactly three
|
|
5
|
+
* things: import the consumer's `elytra.config.ts` (the component registry —
|
|
6
|
+
* code), import the consumer THEME (the same standalone Tailwind entry the
|
|
7
|
+
* canvas iframe and delivery load, EC-414 — so preview == canvas == delivery
|
|
8
|
+
* by construction), and call {@link mountPreviewHarness}.
|
|
9
|
+
*
|
|
10
|
+
* DATA-FREE, deliberately (no new trust boundary): no persistence adapter, no
|
|
11
|
+
* auth, no content data, no env secrets — `createStudio` is never called. The
|
|
12
|
+
* registry is built directly from the config's `components` entries plus the
|
|
13
|
+
* base primitives ({@link previewRegistryFromConfig}), and bindings resolve to
|
|
14
|
+
* `undefined` (`previewContext`), so data-dependent components render their
|
|
15
|
+
* honest empty states.
|
|
16
|
+
*
|
|
17
|
+
* The driving CLI talks to the page through `window.__ELYTRA_PREVIEWS__`:
|
|
18
|
+
* `entries` (the same enumeration the CLI derives node-side — a handshake
|
|
19
|
+
* guard) and `render(componentId, fixture)`, which mounts ONE fixture subtree
|
|
20
|
+
* — the exact subtree the blocks panel inserts (`fixtureSubtree`, else the
|
|
21
|
+
* seeded manifest default, EC-439) — into the stage and resolves once the
|
|
22
|
+
* document has settled (fonts loaded, images complete, two animation frames).
|
|
23
|
+
* The CLI then screenshots the viewport.
|
|
24
|
+
*/
|
|
25
|
+
/** The id of the stage element fixtures render into (created when absent). */
|
|
26
|
+
export declare const PREVIEW_STAGE_ID = "elytra-preview-stage";
|
|
27
|
+
export type PreviewRenderResult = {
|
|
28
|
+
ok: true;
|
|
29
|
+
} | {
|
|
30
|
+
ok: false;
|
|
31
|
+
error: string;
|
|
32
|
+
};
|
|
33
|
+
export interface PreviewHarnessApi {
|
|
34
|
+
ready: true;
|
|
35
|
+
entries: Array<{
|
|
36
|
+
componentId: string;
|
|
37
|
+
fixture: string;
|
|
38
|
+
}>;
|
|
39
|
+
render: (componentId: string, fixture: string) => Promise<PreviewRenderResult>;
|
|
40
|
+
}
|
|
41
|
+
declare global {
|
|
42
|
+
interface Window {
|
|
43
|
+
__ELYTRA_PREVIEWS__?: PreviewHarnessApi;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Mount the harness: build the registry from the consumer config, expose the
|
|
48
|
+
* driver API on `window.__ELYTRA_PREVIEWS__`. Idempotent per document.
|
|
49
|
+
*/
|
|
50
|
+
export declare function mountPreviewHarness(config: unknown): PreviewHarnessApi;
|