@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
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Component } from 'react';
|
|
3
|
+
import { createRoot } from 'react-dom/client';
|
|
4
|
+
import { flushSync } from 'react-dom';
|
|
5
|
+
import { renderNode } from '@elytracms/core/runtime-renderer';
|
|
6
|
+
import { DEFAULT_VARIANT, fixtureSubtree } from '../components/builder/blocks-model.js';
|
|
7
|
+
import { seededNodeFromManifest } from '../components/builder/node-factory.js';
|
|
8
|
+
import { previewContext } from '../lib/registry/index.js';
|
|
9
|
+
import { previewEntriesForConfig, previewRegistryFromConfig } from './enumerate.js';
|
|
10
|
+
/**
|
|
11
|
+
* EC-440 — the browser half of the `elytra previews` render harness. The CLI
|
|
12
|
+
* boots the consumer studio app's OWN Vite (programmatically, with a minimal
|
|
13
|
+
* inline config) and serves a page whose entry module does exactly three
|
|
14
|
+
* things: import the consumer's `elytra.config.ts` (the component registry —
|
|
15
|
+
* code), import the consumer THEME (the same standalone Tailwind entry the
|
|
16
|
+
* canvas iframe and delivery load, EC-414 — so preview == canvas == delivery
|
|
17
|
+
* by construction), and call {@link mountPreviewHarness}.
|
|
18
|
+
*
|
|
19
|
+
* DATA-FREE, deliberately (no new trust boundary): no persistence adapter, no
|
|
20
|
+
* auth, no content data, no env secrets — `createStudio` is never called. The
|
|
21
|
+
* registry is built directly from the config's `components` entries plus the
|
|
22
|
+
* base primitives ({@link previewRegistryFromConfig}), and bindings resolve to
|
|
23
|
+
* `undefined` (`previewContext`), so data-dependent components render their
|
|
24
|
+
* honest empty states.
|
|
25
|
+
*
|
|
26
|
+
* The driving CLI talks to the page through `window.__ELYTRA_PREVIEWS__`:
|
|
27
|
+
* `entries` (the same enumeration the CLI derives node-side — a handshake
|
|
28
|
+
* guard) and `render(componentId, fixture)`, which mounts ONE fixture subtree
|
|
29
|
+
* — the exact subtree the blocks panel inserts (`fixtureSubtree`, else the
|
|
30
|
+
* seeded manifest default, EC-439) — into the stage and resolves once the
|
|
31
|
+
* document has settled (fonts loaded, images complete, two animation frames).
|
|
32
|
+
* The CLI then screenshots the viewport.
|
|
33
|
+
*/
|
|
34
|
+
/** The id of the stage element fixtures render into (created when absent). */
|
|
35
|
+
export const PREVIEW_STAGE_ID = 'elytra-preview-stage';
|
|
36
|
+
/** Catches a fixture whose render throws — the entry fails visibly, the page survives. */
|
|
37
|
+
class HarnessErrorBoundary extends Component {
|
|
38
|
+
state = { failed: false };
|
|
39
|
+
static getDerivedStateFromError() {
|
|
40
|
+
return { failed: true };
|
|
41
|
+
}
|
|
42
|
+
componentDidCatch(error) {
|
|
43
|
+
this.props.onError(error);
|
|
44
|
+
}
|
|
45
|
+
render() {
|
|
46
|
+
return this.state.failed ? null : this.props.children;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Resolve once the document is visually settled: fonts, images, two frames. */
|
|
50
|
+
async function settled(doc) {
|
|
51
|
+
try {
|
|
52
|
+
await doc.fonts?.ready;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Font-loading failures must not wedge the run — the render is still shot.
|
|
56
|
+
}
|
|
57
|
+
await Promise.all(Array.from(doc.images).map((img) => img.complete
|
|
58
|
+
? Promise.resolve()
|
|
59
|
+
: new Promise((resolve) => {
|
|
60
|
+
img.addEventListener('load', () => resolve(), { once: true });
|
|
61
|
+
img.addEventListener('error', () => resolve(), { once: true });
|
|
62
|
+
})));
|
|
63
|
+
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
|
|
64
|
+
}
|
|
65
|
+
/** Build the node a fixture renders — the exact subtree the blocks panel inserts. */
|
|
66
|
+
function nodeForEntry(studio, componentId, fixture) {
|
|
67
|
+
const manifest = studio.registry.getManifest(componentId);
|
|
68
|
+
if (!manifest)
|
|
69
|
+
return { error: `Unknown component "${componentId}".` };
|
|
70
|
+
const declared = manifest.fixtures?.find((f) => f.name === fixture);
|
|
71
|
+
if (declared)
|
|
72
|
+
return { node: fixtureSubtree(studio, manifest, declared, `preview-${componentId}`).node };
|
|
73
|
+
if (fixture === DEFAULT_VARIANT && (manifest.fixtures ?? []).length === 0) {
|
|
74
|
+
return { node: seededNodeFromManifest(manifest) };
|
|
75
|
+
}
|
|
76
|
+
return { error: `Unknown fixture "${fixture}" for component "${componentId}".` };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Mount the harness: build the registry from the consumer config, expose the
|
|
80
|
+
* driver API on `window.__ELYTRA_PREVIEWS__`. Idempotent per document.
|
|
81
|
+
*/
|
|
82
|
+
export function mountPreviewHarness(config) {
|
|
83
|
+
const studio = previewRegistryFromConfig(config);
|
|
84
|
+
const ctx = previewContext(studio);
|
|
85
|
+
const entries = previewEntriesForConfig(config).map(({ componentId, fixture }) => ({
|
|
86
|
+
componentId,
|
|
87
|
+
fixture,
|
|
88
|
+
}));
|
|
89
|
+
let stage = document.getElementById(PREVIEW_STAGE_ID);
|
|
90
|
+
if (!stage) {
|
|
91
|
+
stage = document.createElement('div');
|
|
92
|
+
stage.id = PREVIEW_STAGE_ID;
|
|
93
|
+
document.body.appendChild(stage);
|
|
94
|
+
}
|
|
95
|
+
let root = null;
|
|
96
|
+
const render = async (componentId, fixture) => {
|
|
97
|
+
const built = nodeForEntry(studio, componentId, fixture);
|
|
98
|
+
if ('error' in built)
|
|
99
|
+
return { ok: false, error: built.error };
|
|
100
|
+
// A fresh root per fixture: no state carryover between shots.
|
|
101
|
+
root?.unmount();
|
|
102
|
+
root = createRoot(stage);
|
|
103
|
+
let renderError = null;
|
|
104
|
+
try {
|
|
105
|
+
flushSync(() => {
|
|
106
|
+
root.render(_jsx(HarnessErrorBoundary, { onError: (error) => {
|
|
107
|
+
renderError = error;
|
|
108
|
+
}, children: renderNode(built.node, ctx) }));
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
renderError = error instanceof Error ? error : new Error(String(error));
|
|
113
|
+
}
|
|
114
|
+
if (renderError !== null)
|
|
115
|
+
return { ok: false, error: renderError.message };
|
|
116
|
+
await settled(document);
|
|
117
|
+
return { ok: true };
|
|
118
|
+
};
|
|
119
|
+
const api = { ready: true, entries, render };
|
|
120
|
+
window.__ELYTRA_PREVIEWS__ = api;
|
|
121
|
+
return api;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=harness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../../src/previews/harness.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,EAAE,UAAU,EAAa,MAAM,kBAAkB,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAA;AAG7D,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AACpF,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAA;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAEhF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,8EAA8E;AAC9E,MAAM,CAAC,MAAM,gBAAgB,GAAG,sBAAsB,CAAA;AAgBtD,0FAA0F;AAC1F,MAAM,oBAAqB,SAAQ,SAGlC;IACU,KAAK,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;IAElC,MAAM,CAAC,wBAAwB;QAC7B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IACzB,CAAC;IAEQ,iBAAiB,CAAC,KAAY;QACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;IAEQ,MAAM;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAA;IACvD,CAAC;CACF;AAED,gFAAgF;AAChF,KAAK,UAAU,OAAO,CAAC,GAAa;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,KAAK,EAAE,KAAK,CAAA;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,2EAA2E;IAC7E,CAAC;IACD,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACjC,GAAG,CAAC,QAAQ;QACV,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE;QACnB,CAAC,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC5B,GAAG,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;YAC7D,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QAChE,CAAC,CAAC,CACP,CACF,CAAA;IACD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAClC,qBAAqB,CAAC,GAAG,EAAE,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CACpE,CAAA;AACH,CAAC;AAED,qFAAqF;AACrF,SAAS,YAAY,CACnB,MAAsB,EACtB,WAAmB,EACnB,OAAe;IAEf,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;IACzD,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,sBAAsB,WAAW,IAAI,EAAE,CAAA;IACtE,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAA;IACnE,IAAI,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,WAAW,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IACxG,IAAI,OAAO,KAAK,eAAe,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1E,OAAO,EAAE,IAAI,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EAAE,CAAA;IACnD,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,oBAAoB,OAAO,oBAAoB,WAAW,IAAI,EAAE,CAAA;AAClF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAe;IACjD,MAAM,MAAM,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAA;IAChD,MAAM,GAAG,GAAkB,cAAc,CAAC,MAAM,CAAC,CAAA;IACjD,MAAM,OAAO,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;QACjF,WAAW;QACX,OAAO;KACR,CAAC,CAAC,CAAA;IAEH,IAAI,KAAK,GAAG,QAAQ,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAA;IACrD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;QACrC,KAAK,CAAC,EAAE,GAAG,gBAAgB,CAAA;QAC3B,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;IAClC,CAAC;IAED,IAAI,IAAI,GAAgB,IAAI,CAAA;IAE5B,MAAM,MAAM,GAAG,KAAK,EAAE,WAAmB,EAAE,OAAe,EAAgC,EAAE;QAC1F,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAAA;QACxD,IAAI,OAAO,IAAI,KAAK;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAA;QAE9D,8DAA8D;QAC9D,IAAI,EAAE,OAAO,EAAE,CAAA;QACf,IAAI,GAAG,UAAU,CAAC,KAAM,CAAC,CAAA;QACzB,IAAI,WAAW,GAAiB,IAAI,CAAA;QACpC,IAAI,CAAC;YACH,SAAS,CAAC,GAAG,EAAE;gBACb,IAAK,CAAC,MAAM,CACV,KAAC,oBAAoB,IACnB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;wBACjB,WAAW,GAAG,KAAK,CAAA;oBACrB,CAAC,YAEA,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,GACP,CACxB,CAAA;YACH,CAAC,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACzE,CAAC;QACD,IAAI,WAAW,KAAK,IAAI;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAG,WAAqB,CAAC,OAAO,EAAE,CAAA;QACrF,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAA;QACvB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;IACrB,CAAC,CAAA;IAED,MAAM,GAAG,GAAsB,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAA;IAC/D,MAAM,CAAC,mBAAmB,GAAG,GAAG,CAAA;IAChC,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["import { Component } from 'react'\nimport type { ReactNode } from 'react'\nimport { createRoot, type Root } from 'react-dom/client'\nimport { flushSync } from 'react-dom'\nimport { renderNode } from '@elytracms/core/runtime-renderer'\nimport type { RenderContext } from '@elytracms/core/runtime-renderer'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { DEFAULT_VARIANT, fixtureSubtree } from '../components/builder/blocks-model'\nimport { seededNodeFromManifest } from '../components/builder/node-factory'\nimport { previewContext } from '../lib/registry'\nimport type { StudioRegistry } from '../lib/registry'\nimport { previewEntriesForConfig, previewRegistryFromConfig } from './enumerate'\n\n/**\n * EC-440 — the browser half of the `elytra previews` render harness. The CLI\n * boots the consumer studio app's OWN Vite (programmatically, with a minimal\n * inline config) and serves a page whose entry module does exactly three\n * things: import the consumer's `elytra.config.ts` (the component registry —\n * code), import the consumer THEME (the same standalone Tailwind entry the\n * canvas iframe and delivery load, EC-414 — so preview == canvas == delivery\n * by construction), and call {@link mountPreviewHarness}.\n *\n * DATA-FREE, deliberately (no new trust boundary): no persistence adapter, no\n * auth, no content data, no env secrets — `createStudio` is never called. The\n * registry is built directly from the config's `components` entries plus the\n * base primitives ({@link previewRegistryFromConfig}), and bindings resolve to\n * `undefined` (`previewContext`), so data-dependent components render their\n * honest empty states.\n *\n * The driving CLI talks to the page through `window.__ELYTRA_PREVIEWS__`:\n * `entries` (the same enumeration the CLI derives node-side — a handshake\n * guard) and `render(componentId, fixture)`, which mounts ONE fixture subtree\n * — the exact subtree the blocks panel inserts (`fixtureSubtree`, else the\n * seeded manifest default, EC-439) — into the stage and resolves once the\n * document has settled (fonts loaded, images complete, two animation frames).\n * The CLI then screenshots the viewport.\n */\n\n/** The id of the stage element fixtures render into (created when absent). */\nexport const PREVIEW_STAGE_ID = 'elytra-preview-stage'\n\nexport type PreviewRenderResult = { ok: true } | { ok: false; error: string }\n\nexport interface PreviewHarnessApi {\n ready: true\n entries: Array<{ componentId: string; fixture: string }>\n render: (componentId: string, fixture: string) => Promise<PreviewRenderResult>\n}\n\ndeclare global {\n interface Window {\n __ELYTRA_PREVIEWS__?: PreviewHarnessApi\n }\n}\n\n/** Catches a fixture whose render throws — the entry fails visibly, the page survives. */\nclass HarnessErrorBoundary extends Component<\n { children: ReactNode; onError: (error: Error) => void },\n { failed: boolean }\n> {\n override state = { failed: false }\n\n static getDerivedStateFromError(): { failed: boolean } {\n return { failed: true }\n }\n\n override componentDidCatch(error: Error): void {\n this.props.onError(error)\n }\n\n override render(): ReactNode {\n return this.state.failed ? null : this.props.children\n }\n}\n\n/** Resolve once the document is visually settled: fonts, images, two frames. */\nasync function settled(doc: Document): Promise<void> {\n try {\n await doc.fonts?.ready\n } catch {\n // Font-loading failures must not wedge the run — the render is still shot.\n }\n await Promise.all(\n Array.from(doc.images).map((img) =>\n img.complete\n ? Promise.resolve()\n : new Promise<void>((resolve) => {\n img.addEventListener('load', () => resolve(), { once: true })\n img.addEventListener('error', () => resolve(), { once: true })\n }),\n ),\n )\n await new Promise<void>((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(() => resolve())),\n )\n}\n\n/** Build the node a fixture renders — the exact subtree the blocks panel inserts. */\nfunction nodeForEntry(\n studio: StudioRegistry,\n componentId: string,\n fixture: string,\n): { node: ComponentNode } | { error: string } {\n const manifest = studio.registry.getManifest(componentId)\n if (!manifest) return { error: `Unknown component \"${componentId}\".` }\n const declared = manifest.fixtures?.find((f) => f.name === fixture)\n if (declared) return { node: fixtureSubtree(studio, manifest, declared, `preview-${componentId}`).node }\n if (fixture === DEFAULT_VARIANT && (manifest.fixtures ?? []).length === 0) {\n return { node: seededNodeFromManifest(manifest) }\n }\n return { error: `Unknown fixture \"${fixture}\" for component \"${componentId}\".` }\n}\n\n/**\n * Mount the harness: build the registry from the consumer config, expose the\n * driver API on `window.__ELYTRA_PREVIEWS__`. Idempotent per document.\n */\nexport function mountPreviewHarness(config: unknown): PreviewHarnessApi {\n const studio = previewRegistryFromConfig(config)\n const ctx: RenderContext = previewContext(studio)\n const entries = previewEntriesForConfig(config).map(({ componentId, fixture }) => ({\n componentId,\n fixture,\n }))\n\n let stage = document.getElementById(PREVIEW_STAGE_ID)\n if (!stage) {\n stage = document.createElement('div')\n stage.id = PREVIEW_STAGE_ID\n document.body.appendChild(stage)\n }\n\n let root: Root | null = null\n\n const render = async (componentId: string, fixture: string): Promise<PreviewRenderResult> => {\n const built = nodeForEntry(studio, componentId, fixture)\n if ('error' in built) return { ok: false, error: built.error }\n\n // A fresh root per fixture: no state carryover between shots.\n root?.unmount()\n root = createRoot(stage!)\n let renderError: Error | null = null\n try {\n flushSync(() => {\n root!.render(\n <HarnessErrorBoundary\n onError={(error) => {\n renderError = error\n }}\n >\n {renderNode(built.node, ctx)}\n </HarnessErrorBoundary>,\n )\n })\n } catch (error) {\n renderError = error instanceof Error ? error : new Error(String(error))\n }\n if (renderError !== null) return { ok: false, error: (renderError as Error).message }\n await settled(document)\n return { ok: true }\n }\n\n const api: PreviewHarnessApi = { ready: true, entries, render }\n window.__ELYTRA_PREVIEWS__ = api\n return api\n}\n"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { GeneratedPreviewLookup } from '../components/builder/blocks-model.js';
|
|
2
|
+
/**
|
|
3
|
+
* The previews-manifest format (EC-440) — the JSON contract between the
|
|
4
|
+
* `elytra previews` CLI (the writer) and a consumer's `studio.config.tsx`
|
|
5
|
+
* (the reader, via {@link generatedPreviewsFromManifest}).
|
|
6
|
+
*
|
|
7
|
+
* The CLI renders every canvas-eligible component's fixtures headlessly
|
|
8
|
+
* against the consumer theme, writes the images to the studio app's
|
|
9
|
+
* `public/elytra-previews/` (served at `/elytra-previews/...`), and writes
|
|
10
|
+
* this manifest next to `studio.config.tsx` (`studio/elytra-previews.json`)
|
|
11
|
+
* so it is importable as a plain JSON module. Everything in it is
|
|
12
|
+
* deterministic — no timestamps — so an unchanged re-run rewrites nothing.
|
|
13
|
+
*/
|
|
14
|
+
/** Format version the CLI writes; bump on incompatible schema/hash changes. */
|
|
15
|
+
export declare const PREVIEWS_MANIFEST_VERSION = 1;
|
|
16
|
+
/** One generated preview artifact: a component fixture and its rendered image. */
|
|
17
|
+
export interface GeneratedPreviewsManifestEntry {
|
|
18
|
+
componentId: string;
|
|
19
|
+
/** The fixture name, or `'Default'` for a fixture-less component's implicit variant. */
|
|
20
|
+
fixture: string;
|
|
21
|
+
/** Root-relative image URL (the file lives in the studio app's `public/`). */
|
|
22
|
+
url: string;
|
|
23
|
+
/**
|
|
24
|
+
* Input hash (sha-256 hex over the stable-serialized inputs: manifest prop +
|
|
25
|
+
* slot shape, fixture props/slotChildren, engine version). `elytra previews`
|
|
26
|
+
* skips entries whose hash is unchanged; `elytra previews --check` flags
|
|
27
|
+
* entries whose hash drifted as stale. Deliberately NOT part of the lookup —
|
|
28
|
+
* the studio only reads `componentId`/`fixture`/`url`.
|
|
29
|
+
*/
|
|
30
|
+
hash: string;
|
|
31
|
+
}
|
|
32
|
+
/** The manifest document `elytra previews` writes (`studio/elytra-previews.json`). */
|
|
33
|
+
export interface GeneratedPreviewsManifest {
|
|
34
|
+
version: number;
|
|
35
|
+
/** The `@elytracms/studio` version the images were rendered with. */
|
|
36
|
+
engine: string;
|
|
37
|
+
entries: GeneratedPreviewsManifestEntry[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Adapt a previews manifest (a plain JSON import) into the EC-439
|
|
41
|
+
* {@link GeneratedPreviewLookup} seam. The whole consumer wiring is ~2 lines:
|
|
42
|
+
*
|
|
43
|
+
* ```tsx
|
|
44
|
+
* // studio.config.tsx
|
|
45
|
+
* import previewsManifest from './elytra-previews.json'
|
|
46
|
+
* export default defineStudio({
|
|
47
|
+
* previews: { generated: generatedPreviewsFromManifest(previewsManifest) },
|
|
48
|
+
* })
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* Tolerant by design (the input is a generated file a human may hand-edit or
|
|
52
|
+
* delete): a malformed document or entry yields lookup misses — the blocks
|
|
53
|
+
* panel then falls back to the icon+name placeholder — never a crash. A
|
|
54
|
+
* fixture-less component's single implicit variant is looked up (and written
|
|
55
|
+
* by the CLI) under the fixture name `'Default'`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function generatedPreviewsFromManifest(manifest: unknown): GeneratedPreviewLookup;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The previews-manifest format (EC-440) — the JSON contract between the
|
|
3
|
+
* `elytra previews` CLI (the writer) and a consumer's `studio.config.tsx`
|
|
4
|
+
* (the reader, via {@link generatedPreviewsFromManifest}).
|
|
5
|
+
*
|
|
6
|
+
* The CLI renders every canvas-eligible component's fixtures headlessly
|
|
7
|
+
* against the consumer theme, writes the images to the studio app's
|
|
8
|
+
* `public/elytra-previews/` (served at `/elytra-previews/...`), and writes
|
|
9
|
+
* this manifest next to `studio.config.tsx` (`studio/elytra-previews.json`)
|
|
10
|
+
* so it is importable as a plain JSON module. Everything in it is
|
|
11
|
+
* deterministic — no timestamps — so an unchanged re-run rewrites nothing.
|
|
12
|
+
*/
|
|
13
|
+
/** Format version the CLI writes; bump on incompatible schema/hash changes. */
|
|
14
|
+
export const PREVIEWS_MANIFEST_VERSION = 1;
|
|
15
|
+
/** Tolerant entry guard — a malformed entry is skipped, never a crash. */
|
|
16
|
+
function isManifestEntry(value) {
|
|
17
|
+
if (typeof value !== 'object' || value === null)
|
|
18
|
+
return false;
|
|
19
|
+
const entry = value;
|
|
20
|
+
return (typeof entry.componentId === 'string' &&
|
|
21
|
+
typeof entry.fixture === 'string' &&
|
|
22
|
+
typeof entry.url === 'string');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Adapt a previews manifest (a plain JSON import) into the EC-439
|
|
26
|
+
* {@link GeneratedPreviewLookup} seam. The whole consumer wiring is ~2 lines:
|
|
27
|
+
*
|
|
28
|
+
* ```tsx
|
|
29
|
+
* // studio.config.tsx
|
|
30
|
+
* import previewsManifest from './elytra-previews.json'
|
|
31
|
+
* export default defineStudio({
|
|
32
|
+
* previews: { generated: generatedPreviewsFromManifest(previewsManifest) },
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Tolerant by design (the input is a generated file a human may hand-edit or
|
|
37
|
+
* delete): a malformed document or entry yields lookup misses — the blocks
|
|
38
|
+
* panel then falls back to the icon+name placeholder — never a crash. A
|
|
39
|
+
* fixture-less component's single implicit variant is looked up (and written
|
|
40
|
+
* by the CLI) under the fixture name `'Default'`.
|
|
41
|
+
*/
|
|
42
|
+
export function generatedPreviewsFromManifest(manifest) {
|
|
43
|
+
const byKey = new Map();
|
|
44
|
+
if (typeof manifest === 'object' && manifest !== null) {
|
|
45
|
+
const entries = manifest.entries;
|
|
46
|
+
if (Array.isArray(entries)) {
|
|
47
|
+
for (const entry of entries) {
|
|
48
|
+
if (isManifestEntry(entry))
|
|
49
|
+
byKey.set(lookupKey(entry.componentId, entry.fixture), entry.url);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return (componentId, fixtureName) => byKey.get(lookupKey(componentId, fixtureName));
|
|
54
|
+
}
|
|
55
|
+
function lookupKey(componentId, fixture) {
|
|
56
|
+
// NUL (\u0000) can appear in neither a component id nor a fixture name.
|
|
57
|
+
return `${componentId}\u0000${fixture}`;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=manifest-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-format.js","sourceRoot":"","sources":["../../src/previews/manifest-format.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAA;AA2B1C,0EAA0E;AAC1E,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAC7D,MAAM,KAAK,GAAG,KAAgC,CAAA;IAC9C,OAAO,CACL,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ;QACrC,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,CAC9B,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,6BAA6B,CAAC,QAAiB;IAC7D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IACvC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtD,MAAM,OAAO,GAAI,QAAkC,CAAC,OAAO,CAAA;QAC3D,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,eAAe,CAAC,KAAK,CAAC;oBAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;YAC/F,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,WAAW,EAAE,WAAW,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAA;AACrF,CAAC;AAED,SAAS,SAAS,CAAC,WAAmB,EAAE,OAAe;IACrD,wEAAwE;IACxE,OAAO,GAAG,WAAW,SAAS,OAAO,EAAE,CAAA;AACzC,CAAC","sourcesContent":["import type { GeneratedPreviewLookup } from '../components/builder/blocks-model'\n\n/**\n * The previews-manifest format (EC-440) — the JSON contract between the\n * `elytra previews` CLI (the writer) and a consumer's `studio.config.tsx`\n * (the reader, via {@link generatedPreviewsFromManifest}).\n *\n * The CLI renders every canvas-eligible component's fixtures headlessly\n * against the consumer theme, writes the images to the studio app's\n * `public/elytra-previews/` (served at `/elytra-previews/...`), and writes\n * this manifest next to `studio.config.tsx` (`studio/elytra-previews.json`)\n * so it is importable as a plain JSON module. Everything in it is\n * deterministic — no timestamps — so an unchanged re-run rewrites nothing.\n */\n\n/** Format version the CLI writes; bump on incompatible schema/hash changes. */\nexport const PREVIEWS_MANIFEST_VERSION = 1\n\n/** One generated preview artifact: a component fixture and its rendered image. */\nexport interface GeneratedPreviewsManifestEntry {\n componentId: string\n /** The fixture name, or `'Default'` for a fixture-less component's implicit variant. */\n fixture: string\n /** Root-relative image URL (the file lives in the studio app's `public/`). */\n url: string\n /**\n * Input hash (sha-256 hex over the stable-serialized inputs: manifest prop +\n * slot shape, fixture props/slotChildren, engine version). `elytra previews`\n * skips entries whose hash is unchanged; `elytra previews --check` flags\n * entries whose hash drifted as stale. Deliberately NOT part of the lookup —\n * the studio only reads `componentId`/`fixture`/`url`.\n */\n hash: string\n}\n\n/** The manifest document `elytra previews` writes (`studio/elytra-previews.json`). */\nexport interface GeneratedPreviewsManifest {\n version: number\n /** The `@elytracms/studio` version the images were rendered with. */\n engine: string\n entries: GeneratedPreviewsManifestEntry[]\n}\n\n/** Tolerant entry guard — a malformed entry is skipped, never a crash. */\nfunction isManifestEntry(value: unknown): value is GeneratedPreviewsManifestEntry {\n if (typeof value !== 'object' || value === null) return false\n const entry = value as Record<string, unknown>\n return (\n typeof entry.componentId === 'string' &&\n typeof entry.fixture === 'string' &&\n typeof entry.url === 'string'\n )\n}\n\n/**\n * Adapt a previews manifest (a plain JSON import) into the EC-439\n * {@link GeneratedPreviewLookup} seam. The whole consumer wiring is ~2 lines:\n *\n * ```tsx\n * // studio.config.tsx\n * import previewsManifest from './elytra-previews.json'\n * export default defineStudio({\n * previews: { generated: generatedPreviewsFromManifest(previewsManifest) },\n * })\n * ```\n *\n * Tolerant by design (the input is a generated file a human may hand-edit or\n * delete): a malformed document or entry yields lookup misses — the blocks\n * panel then falls back to the icon+name placeholder — never a crash. A\n * fixture-less component's single implicit variant is looked up (and written\n * by the CLI) under the fixture name `'Default'`.\n */\nexport function generatedPreviewsFromManifest(manifest: unknown): GeneratedPreviewLookup {\n const byKey = new Map<string, string>()\n if (typeof manifest === 'object' && manifest !== null) {\n const entries = (manifest as { entries?: unknown }).entries\n if (Array.isArray(entries)) {\n for (const entry of entries) {\n if (isManifestEntry(entry)) byKey.set(lookupKey(entry.componentId, entry.fixture), entry.url)\n }\n }\n }\n return (componentId, fixtureName) => byKey.get(lookupKey(componentId, fixtureName))\n}\n\nfunction lookupKey(componentId: string, fixture: string): string {\n // NUL (\\u0000) can appear in neither a component id nor a fixture name.\n return `${componentId}\\u0000${fixture}`\n}\n"]}
|
package/dist/ui/dialog.d.ts
CHANGED
|
@@ -19,8 +19,16 @@ export declare function DialogContent({ className, children, ...props }: Compone
|
|
|
19
19
|
* layer — its Escape handler `preventDefault`s, which the `ExpandableSurface`
|
|
20
20
|
* window-bubble handler reads as "handled above me", so sheets open beneath
|
|
21
21
|
* the blocker stay open instead of swallowing keys.
|
|
22
|
+
*
|
|
23
|
+
* `overlayClassName` is presentation-only (EC-437): it restyles the backdrop
|
|
24
|
+
* element (the session-expired re-auth overlay swaps in the stage family's
|
|
25
|
+
* translucent blurred treatment so the studio beneath stays visible). Every
|
|
26
|
+
* blocking mechanic — the overlay element itself, the consumed dismissal
|
|
27
|
+
* events, focus trap, scroll lock — is untouched by it.
|
|
22
28
|
*/
|
|
23
|
-
export declare function DialogBlockingContent({ className, children, ...props }: ComponentProps<typeof DialogPrimitive.Content>
|
|
29
|
+
export declare function DialogBlockingContent({ className, children, overlayClassName, ...props }: ComponentProps<typeof DialogPrimitive.Content> & {
|
|
30
|
+
overlayClassName?: string;
|
|
31
|
+
}): import("react").JSX.Element;
|
|
24
32
|
export declare function DialogHeader({ className, ...props }: ComponentProps<'div'>): import("react").JSX.Element;
|
|
25
33
|
export declare function DialogFooter({ className, ...props }: ComponentProps<'div'>): import("react").JSX.Element;
|
|
26
34
|
export declare function DialogTitle({ className, ...props }: ComponentProps<typeof DialogPrimitive.Title>): import("react").JSX.Element;
|
package/dist/ui/dialog.js
CHANGED
|
@@ -25,9 +25,15 @@ export function DialogContent({ className, children, ...props }) {
|
|
|
25
25
|
* layer — its Escape handler `preventDefault`s, which the `ExpandableSurface`
|
|
26
26
|
* window-bubble handler reads as "handled above me", so sheets open beneath
|
|
27
27
|
* the blocker stay open instead of swallowing keys.
|
|
28
|
+
*
|
|
29
|
+
* `overlayClassName` is presentation-only (EC-437): it restyles the backdrop
|
|
30
|
+
* element (the session-expired re-auth overlay swaps in the stage family's
|
|
31
|
+
* translucent blurred treatment so the studio beneath stays visible). Every
|
|
32
|
+
* blocking mechanic — the overlay element itself, the consumed dismissal
|
|
33
|
+
* events, focus trap, scroll lock — is untouched by it.
|
|
28
34
|
*/
|
|
29
|
-
export function DialogBlockingContent({ className, children, ...props }) {
|
|
30
|
-
return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsx(DialogPrimitive.Content, { onEscapeKeyDown: (event) => event.preventDefault(), onPointerDownOutside: (event) => event.preventDefault(), onInteractOutside: (event) => event.preventDefault(), className: cn('fixed inset-0 z-50 overflow-y-auto outline-none', className), ...props, children: children })] }));
|
|
35
|
+
export function DialogBlockingContent({ className, children, overlayClassName, ...props }) {
|
|
36
|
+
return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, { className: overlayClassName }), _jsx(DialogPrimitive.Content, { onEscapeKeyDown: (event) => event.preventDefault(), onPointerDownOutside: (event) => event.preventDefault(), onInteractOutside: (event) => event.preventDefault(), className: cn('fixed inset-0 z-50 overflow-y-auto outline-none', className), ...props, children: children })] }));
|
|
31
37
|
}
|
|
32
38
|
export function DialogHeader({ className, ...props }) {
|
|
33
39
|
return _jsx("div", { className: cn('flex flex-col gap-1.5', className), ...props });
|
package/dist/ui/dialog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.js","sourceRoot":"","sources":["../../src/ui/dialog.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAEhC,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAEjC,MAAM,CAAC,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAA;AAC1C,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC,OAAO,CAAA;AACpD,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC,KAAK,CAAA;AAChD,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAA;AAElD,MAAM,UAAU,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAkD;IACnG,OAAO,CACL,KAAC,eAAe,CAAC,OAAO,IACtB,SAAS,EAAE,EAAE,CACX,4KAA4K,EAC5K,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAA;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAC5B,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACuC;IAC/C,OAAO,CACL,MAAC,YAAY,eACX,KAAC,aAAa,KAAG,EACjB,MAAC,eAAe,CAAC,OAAO,IACtB,SAAS,EAAE,EAAE,CACX,4KAA4K,EAC5K,uFAAuF,EACvF,gGAAgG,EAChG,SAAS,CACV,KACG,KAAK,aAER,QAAQ,EACT,MAAC,eAAe,CAAC,KAAK,IAAC,SAAS,EAAC,0KAA0K,aACzM,KAAC,CAAC,IAAC,SAAS,EAAC,QAAQ,GAAG,EACxB,eAAM,SAAS,EAAC,SAAS,sBAAa,IAChB,IACA,IACb,CAChB,CAAA;AACH,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"dialog.js","sourceRoot":"","sources":["../../src/ui/dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as DialogPrimitive from '@radix-ui/react-dialog'\nimport { X } from 'lucide-react'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../lib/utils'\n\nexport const Dialog = DialogPrimitive.Root\nexport const DialogTrigger = DialogPrimitive.Trigger\nexport const DialogClose = DialogPrimitive.Close\nexport const DialogPortal = DialogPrimitive.Portal\n\nexport function DialogOverlay({ className, ...props }: ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n className={cn(\n 'fixed inset-0 z-50 bg-black/55 backdrop-blur-[2px] data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0',\n className,\n )}\n {...props}\n />\n )\n}\n\nexport function DialogContent({\n className,\n children,\n ...props\n}: ComponentProps<typeof DialogPrimitive.Content>) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n className={cn(\n 'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl border border-border bg-popover p-5 text-popover-foreground shadow-2xl',\n 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',\n 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',\n className,\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-3.5 top-3.5 rounded-sm text-muted-foreground opacity-70 transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring/60 outline-none\">\n <X className=\"size-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n}\n\n/**\n * Full-viewport, NON-dismissible modal content (EC-403: the blocking\n * session-expired re-auth overlay). Escape, backdrop clicks, and outside\n * interaction are all consumed — the only way out is the flow rendered inside\n * (or the owner clearing the controlled `open`). Radix still provides the\n * modal guarantees the blocking treatment relies on: focus trap, scroll lock,\n * `aria-modal`, and outside pointer-event lockout, so the studio beneath stays\n * fully inert while the React tree (and all unsaved editor state) lives on.\n *\n * Stacking (EC-392 discipline): mounted last and modal, it is the topmost\n * layer — its Escape handler `preventDefault`s, which the `ExpandableSurface`\n * window-bubble handler reads as \"handled above me\", so sheets open beneath\n * the blocker stay open instead of swallowing keys.\n *\n * `overlayClassName` is presentation-only (EC-437): it restyles the backdrop\n * element (the session-expired re-auth overlay swaps in the stage family's\n * translucent blurred treatment so the studio beneath stays visible). Every\n * blocking mechanic — the overlay element itself, the consumed dismissal\n * events, focus trap, scroll lock — is untouched by it.\n */\nexport function DialogBlockingContent({\n className,\n children,\n overlayClassName,\n ...props\n}: ComponentProps<typeof DialogPrimitive.Content> & { overlayClassName?: string }) {\n return (\n <DialogPortal>\n <DialogOverlay className={overlayClassName} />\n <DialogPrimitive.Content\n onEscapeKeyDown={(event) => event.preventDefault()}\n onPointerDownOutside={(event) => event.preventDefault()}\n onInteractOutside={(event) => event.preventDefault()}\n className={cn('fixed inset-0 z-50 overflow-y-auto outline-none', className)}\n {...props}\n >\n {children}\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n}\n\nexport function DialogHeader({ className, ...props }: ComponentProps<'div'>) {\n return <div className={cn('flex flex-col gap-1.5', className)} {...props} />\n}\n\nexport function DialogFooter({ className, ...props }: ComponentProps<'div'>) {\n return <div className={cn('flex items-center justify-end gap-2 pt-2', className)} {...props} />\n}\n\nexport function DialogTitle({ className, ...props }: ComponentProps<typeof DialogPrimitive.Title>) {\n return <DialogPrimitive.Title className={cn('text-base font-semibold tracking-tight', className)} {...props} />\n}\n\nexport function DialogDescription({\n className,\n ...props\n}: ComponentProps<typeof DialogPrimitive.Description>) {\n return <DialogPrimitive.Description className={cn('text-sm text-muted-foreground', className)} {...props} />\n}\n"]}
|
package/dist/ui/field.d.ts
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import type { LabelHTMLAttributes, ReactNode, SelectHTMLAttributes } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* EC-443 — THE nested-field anchor convention (one convention, used by both the
|
|
4
|
+
* stamping and the lookup — do not invent a second):
|
|
5
|
+
*
|
|
6
|
+
* data-ec-field-path="<field>(/<token>)*"
|
|
7
|
+
*
|
|
8
|
+
* Slash-joined tokens from the TOP-LEVEL field name down into the value —
|
|
9
|
+
* array indices are numeric tokens, object subfields their names — e.g.
|
|
10
|
+
* `links`, `links/3`, `links/3/badge`, `links/3/badge/text`. This mirrors the
|
|
11
|
+
* `values/…` reference-index path with the `values`/`localized/<locale>` prefix
|
|
12
|
+
* stripped: the locale axis is the studio's ambient locale switch, never part
|
|
13
|
+
* of the DOM anchor.
|
|
14
|
+
*
|
|
15
|
+
* Stamped by: the document form's top-level field rows (`detail-editor`'s
|
|
16
|
+
* `renderFieldRow` via the `fieldPath` prop below) and the object/repeater
|
|
17
|
+
* controls (`object-array-field`: item cards, table rows/cells, and nested
|
|
18
|
+
* subfield rows). Read by: `use-field-focus`'s deepest-available-anchor
|
|
19
|
+
* resolution (jump-to-field / `?field=` deep links) via the pure helpers in
|
|
20
|
+
* `components/cms/field-anchors.ts`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const FIELD_PATH_ATTR = "data-ec-field-path";
|
|
2
23
|
/** A labelled form field group: label, control, and optional hint/error. */
|
|
3
24
|
export interface FieldProps {
|
|
4
25
|
label: ReactNode;
|
|
@@ -8,8 +29,10 @@ export interface FieldProps {
|
|
|
8
29
|
required?: boolean;
|
|
9
30
|
children: ReactNode;
|
|
10
31
|
className?: string;
|
|
32
|
+
/** Stamps the {@link FIELD_PATH_ATTR} jump-to-field anchor (EC-443, see above). */
|
|
33
|
+
fieldPath?: string;
|
|
11
34
|
}
|
|
12
|
-
export declare function Field({ label, htmlFor, hint, error, required, children, className }: FieldProps): import("react").JSX.Element;
|
|
35
|
+
export declare function Field({ label, htmlFor, hint, error, required, children, className, fieldPath }: FieldProps): import("react").JSX.Element;
|
|
13
36
|
export declare function FieldLabel({ className, ...props }: LabelHTMLAttributes<HTMLLabelElement>): import("react").JSX.Element;
|
|
14
37
|
/** Native select styled to match Input — accessible and SSR-friendly. */
|
|
15
38
|
export declare function Select({ className, children, ...props }: SelectHTMLAttributes<HTMLSelectElement>): import("react").JSX.Element;
|
package/dist/ui/field.js
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ChevronDown } from 'lucide-react';
|
|
3
3
|
import { cn } from '../lib/utils.js';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* EC-443 — THE nested-field anchor convention (one convention, used by both the
|
|
6
|
+
* stamping and the lookup — do not invent a second):
|
|
7
|
+
*
|
|
8
|
+
* data-ec-field-path="<field>(/<token>)*"
|
|
9
|
+
*
|
|
10
|
+
* Slash-joined tokens from the TOP-LEVEL field name down into the value —
|
|
11
|
+
* array indices are numeric tokens, object subfields their names — e.g.
|
|
12
|
+
* `links`, `links/3`, `links/3/badge`, `links/3/badge/text`. This mirrors the
|
|
13
|
+
* `values/…` reference-index path with the `values`/`localized/<locale>` prefix
|
|
14
|
+
* stripped: the locale axis is the studio's ambient locale switch, never part
|
|
15
|
+
* of the DOM anchor.
|
|
16
|
+
*
|
|
17
|
+
* Stamped by: the document form's top-level field rows (`detail-editor`'s
|
|
18
|
+
* `renderFieldRow` via the `fieldPath` prop below) and the object/repeater
|
|
19
|
+
* controls (`object-array-field`: item cards, table rows/cells, and nested
|
|
20
|
+
* subfield rows). Read by: `use-field-focus`'s deepest-available-anchor
|
|
21
|
+
* resolution (jump-to-field / `?field=` deep links) via the pure helpers in
|
|
22
|
+
* `components/cms/field-anchors.ts`.
|
|
23
|
+
*/
|
|
24
|
+
export const FIELD_PATH_ATTR = 'data-ec-field-path';
|
|
25
|
+
export function Field({ label, htmlFor, hint, error, required, children, className, fieldPath }) {
|
|
26
|
+
return (_jsxs("div", { className: cn('flex flex-col gap-1.5', className), ...(fieldPath !== undefined ? { [FIELD_PATH_ATTR]: fieldPath } : {}), children: [_jsxs(FieldLabel, { htmlFor: htmlFor, children: [label, required ? _jsx("span", { className: "text-destructive", children: " *" }) : null] }), children, error ? (_jsx("p", { className: "text-xs font-medium text-destructive", role: "alert", children: error })) : hint ? (_jsx("p", { className: "text-xs text-muted-foreground", children: hint })) : null] }));
|
|
6
27
|
}
|
|
7
28
|
export function FieldLabel({ className, ...props }) {
|
|
8
29
|
return (_jsx("label", { className: cn('text-xs font-medium text-foreground/90', className), ...props }));
|
package/dist/ui/field.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field.js","sourceRoot":"","sources":["../../src/ui/field.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"field.js","sourceRoot":"","sources":["../../src/ui/field.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAEjC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,oBAAoB,CAAA;AAenD,MAAM,UAAU,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAc;IACzG,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CAAC,uBAAuB,EAAE,SAAS,CAAC,KAC7C,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAErE,MAAC,UAAU,IAAC,OAAO,EAAE,OAAO,aACzB,KAAK,EACL,QAAQ,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,kBAAkB,mBAAU,CAAC,CAAC,CAAC,IAAI,IACpD,EACZ,QAAQ,EACR,KAAK,CAAC,CAAC,CAAC,CACP,YAAG,SAAS,EAAC,sCAAsC,EAAC,IAAI,EAAC,OAAO,YAC7D,KAAK,GACJ,CACL,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACT,YAAG,SAAS,EAAC,+BAA+B,YAAE,IAAI,GAAK,CACxD,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAA;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAyC;IACvF,OAAO,CACL,gBACE,SAAS,EAAE,EAAE,CAAC,wCAAwC,EAAE,SAAS,CAAC,KAC9D,KAAK,GACT,CACH,CAAA;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,MAAM,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAA2C;IAC/F,OAAO,CACL,eAAK,SAAS,EAAC,UAAU,aACvB,iBACE,SAAS,EAAE,EAAE,CACX,8IAA8I,EAC9I,wFAAwF,EACxF,iDAAiD,EACjD,SAAS,CACV,KACG,KAAK,YAER,QAAQ,GACF,EACT,KAAC,WAAW,IAAC,SAAS,EAAC,gGAAgG,GAAG,IACtH,CACP,CAAA;AACH,CAAC","sourcesContent":["import { ChevronDown } from 'lucide-react'\nimport type { LabelHTMLAttributes, ReactNode, SelectHTMLAttributes } from 'react'\nimport { cn } from '../lib/utils'\n\n/**\n * EC-443 — THE nested-field anchor convention (one convention, used by both the\n * stamping and the lookup — do not invent a second):\n *\n * data-ec-field-path=\"<field>(/<token>)*\"\n *\n * Slash-joined tokens from the TOP-LEVEL field name down into the value —\n * array indices are numeric tokens, object subfields their names — e.g.\n * `links`, `links/3`, `links/3/badge`, `links/3/badge/text`. This mirrors the\n * `values/…` reference-index path with the `values`/`localized/<locale>` prefix\n * stripped: the locale axis is the studio's ambient locale switch, never part\n * of the DOM anchor.\n *\n * Stamped by: the document form's top-level field rows (`detail-editor`'s\n * `renderFieldRow` via the `fieldPath` prop below) and the object/repeater\n * controls (`object-array-field`: item cards, table rows/cells, and nested\n * subfield rows). Read by: `use-field-focus`'s deepest-available-anchor\n * resolution (jump-to-field / `?field=` deep links) via the pure helpers in\n * `components/cms/field-anchors.ts`.\n */\nexport const FIELD_PATH_ATTR = 'data-ec-field-path'\n\n/** A labelled form field group: label, control, and optional hint/error. */\nexport interface FieldProps {\n label: ReactNode\n htmlFor?: string\n hint?: ReactNode\n error?: ReactNode\n required?: boolean\n children: ReactNode\n className?: string\n /** Stamps the {@link FIELD_PATH_ATTR} jump-to-field anchor (EC-443, see above). */\n fieldPath?: string\n}\n\nexport function Field({ label, htmlFor, hint, error, required, children, className, fieldPath }: FieldProps) {\n return (\n <div\n className={cn('flex flex-col gap-1.5', className)}\n {...(fieldPath !== undefined ? { [FIELD_PATH_ATTR]: fieldPath } : {})}\n >\n <FieldLabel htmlFor={htmlFor}>\n {label}\n {required ? <span className=\"text-destructive\"> *</span> : null}\n </FieldLabel>\n {children}\n {error ? (\n <p className=\"text-xs font-medium text-destructive\" role=\"alert\">\n {error}\n </p>\n ) : hint ? (\n <p className=\"text-xs text-muted-foreground\">{hint}</p>\n ) : null}\n </div>\n )\n}\n\nexport function FieldLabel({ className, ...props }: LabelHTMLAttributes<HTMLLabelElement>) {\n return (\n <label\n className={cn('text-xs font-medium text-foreground/90', className)}\n {...props}\n />\n )\n}\n\n/** Native select styled to match Input — accessible and SSR-friendly. */\nexport function Select({ className, children, ...props }: SelectHTMLAttributes<HTMLSelectElement>) {\n return (\n <div className=\"relative\">\n <select\n className={cn(\n 'flex h-8 w-full appearance-none rounded-md border border-input bg-background pl-2.5 pr-8 text-sm text-foreground shadow-sm transition-colors',\n 'focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n className,\n )}\n {...props}\n >\n {children}\n </select>\n <ChevronDown className=\"pointer-events-none absolute right-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n </div>\n )\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elytracms/studio",
|
|
3
3
|
"author": "finaldream <hi@finaldream.de>",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.71",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "The Elytra studio engine (UI + lib + routes + data-only Convex schema) as an importable package. Extraction staged in EXTRACTION.md (EC-223); Stages 1-2 = UI primitives + router-free lib leaves.",
|
|
7
7
|
"license": "MIT",
|
|
@@ -32,10 +32,6 @@
|
|
|
32
32
|
"types": "./dist/components/builder/canvas-stylesheet.d.ts",
|
|
33
33
|
"default": "./dist/components/builder/canvas-stylesheet.js"
|
|
34
34
|
},
|
|
35
|
-
"./components/sections": {
|
|
36
|
-
"types": "./dist/components/sections.d.ts",
|
|
37
|
-
"default": "./dist/components/sections.js"
|
|
38
|
-
},
|
|
39
35
|
"./components/*": {
|
|
40
36
|
"types": "./dist/components/*.d.ts",
|
|
41
37
|
"default": "./dist/components/*.js"
|
|
@@ -108,6 +104,14 @@
|
|
|
108
104
|
"types": "./dist/lib/*.d.ts",
|
|
109
105
|
"default": "./dist/lib/*.js"
|
|
110
106
|
},
|
|
107
|
+
"./previews/enumerate": {
|
|
108
|
+
"types": "./dist/previews/enumerate.d.ts",
|
|
109
|
+
"default": "./dist/previews/enumerate.js"
|
|
110
|
+
},
|
|
111
|
+
"./previews/harness": {
|
|
112
|
+
"types": "./dist/previews/harness.d.ts",
|
|
113
|
+
"default": "./dist/previews/harness.js"
|
|
114
|
+
},
|
|
111
115
|
"./convex/*": "./convex/*.ts"
|
|
112
116
|
},
|
|
113
117
|
"files": [
|
|
@@ -146,7 +150,7 @@
|
|
|
146
150
|
"lucide-react": "^1.23.0",
|
|
147
151
|
"tailwind-merge": "^3.6.0",
|
|
148
152
|
"zod": "^4.4.3",
|
|
149
|
-
"@elytracms/core": "^0.0.
|
|
153
|
+
"@elytracms/core": "^0.0.71"
|
|
150
154
|
},
|
|
151
155
|
"peerDependencies": {
|
|
152
156
|
"@tanstack/react-router": "^1.170.0",
|