@elytracms/studio 0.0.68 → 0.0.70
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/app-views/studio-config.d.ts +22 -0
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/assets/asset-detail.d.ts +4 -2
- package/dist/components/assets/asset-detail.js +4 -2
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +30 -19
- package/dist/components/assets/assets-collection-view-source.js +55 -23
- 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/canvas-chrome-css.js +6 -1
- package/dist/components/builder/canvas-chrome-css.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/selection-overlay.d.ts +17 -4
- package/dist/components/builder/selection-overlay.js +17 -15
- package/dist/components/builder/selection-overlay.js.map +1 -1
- package/dist/components/builder/spacing-handles.d.ts +34 -6
- package/dist/components/builder/spacing-handles.js +70 -31
- package/dist/components/builder/spacing-handles.js.map +1 -1
- package/dist/components/cms/bulk-bar.d.ts +11 -1
- package/dist/components/cms/bulk-bar.js +10 -3
- package/dist/components/cms/bulk-bar.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/field-control.js +5 -1
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/prop-fields.js +5 -1
- package/dist/components/cms/prop-fields.js.map +1 -1
- package/dist/components/cms/reference-confirm.js +5 -1
- package/dist/components/cms/reference-confirm.js.map +1 -1
- package/dist/components/cms/rich-text/editor.d.ts +24 -5
- package/dist/components/cms/rich-text/editor.js +23 -8
- package/dist/components/cms/rich-text/editor.js.map +1 -1
- package/dist/components/rail-view-source.d.ts +50 -1
- package/dist/components/rail-view-source.js +56 -2
- package/dist/components/rail-view-source.js.map +1 -1
- package/dist/components/redirects/redirects-collection-view-source.d.ts +3 -2
- package/dist/components/redirects/redirects-collection-view-source.js +4 -3
- 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 -1
- package/dist/components/users/users-collection-view-source.js.map +1 -1
- package/dist/customization.d.ts +9 -1
- 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 +12 -0
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/dnd/gap-drag.d.ts +8 -1
- package/dist/lib/dnd/gap-drag.js +4 -0
- package/dist/lib/dnd/gap-drag.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +8 -0
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +6 -0
- package/dist/lib/i18n/messages.en.js +9 -0
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/references/references-store.d.ts +8 -1
- package/dist/lib/references/references-store.js +12 -2
- package/dist/lib/references/references-store.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/ui/dialog.d.ts +9 -1
- package/dist/ui/dialog.js +8 -2
- package/dist/ui/dialog.js.map +1 -1
- package/dist/ui/surface-geometry.d.ts +8 -5
- package/dist/ui/surface-geometry.js +8 -5
- package/dist/ui/surface-geometry.js.map +1 -1
- package/package.json +2 -2
|
@@ -37,8 +37,15 @@ export declare function loadReferencePanelData(query: ReferencePanelQuery): Prom
|
|
|
37
37
|
* pass (one index flush, one label-context load) — the pre-flight data of the
|
|
38
38
|
* EC-173 confirmation dialogs, including the bulk flows. Returns entries in
|
|
39
39
|
* `targets` order; pair with `buildReferenceImpact` for break/block semantics.
|
|
40
|
+
*
|
|
41
|
+
* `toTarget` maps each acted-on ref to its reference-index target shape
|
|
42
|
+
* (EC-433): the default is the document shape every `cmsDocuments` collection
|
|
43
|
+
* indexes under; a rail whose records live under a different target type (the
|
|
44
|
+
* assets rail, `{ type: 'asset' }`) supplies its own via
|
|
45
|
+
* `CollectionViewSource.referenceTarget` — otherwise its pre-flight would
|
|
46
|
+
* query the wrong shape and report referenced records as "not referenced".
|
|
40
47
|
*/
|
|
41
|
-
export declare function loadInboundReferenceViews(projectId: string, targets: readonly DocumentRef[]): Promise<{
|
|
48
|
+
export declare function loadInboundReferenceViews(projectId: string, targets: readonly DocumentRef[], toTarget?: (ref: DocumentRef) => ReferenceTarget): Promise<{
|
|
42
49
|
ref: DocumentRef;
|
|
43
50
|
inbound: ReferenceEntryView[];
|
|
44
51
|
}[]>;
|
|
@@ -133,8 +133,18 @@ export async function loadReferencePanelData(query) {
|
|
|
133
133
|
* pass (one index flush, one label-context load) — the pre-flight data of the
|
|
134
134
|
* EC-173 confirmation dialogs, including the bulk flows. Returns entries in
|
|
135
135
|
* `targets` order; pair with `buildReferenceImpact` for break/block semantics.
|
|
136
|
+
*
|
|
137
|
+
* `toTarget` maps each acted-on ref to its reference-index target shape
|
|
138
|
+
* (EC-433): the default is the document shape every `cmsDocuments` collection
|
|
139
|
+
* indexes under; a rail whose records live under a different target type (the
|
|
140
|
+
* assets rail, `{ type: 'asset' }`) supplies its own via
|
|
141
|
+
* `CollectionViewSource.referenceTarget` — otherwise its pre-flight would
|
|
142
|
+
* query the wrong shape and report referenced records as "not referenced".
|
|
136
143
|
*/
|
|
137
|
-
export async function loadInboundReferenceViews(projectId, targets) {
|
|
144
|
+
export async function loadInboundReferenceViews(projectId, targets, toTarget = (ref) => ({
|
|
145
|
+
type: 'document',
|
|
146
|
+
id: documentKey(ref),
|
|
147
|
+
})) {
|
|
138
148
|
const studio = await getStudioOperations();
|
|
139
149
|
await ensureReferenceIndex(studio, projectId);
|
|
140
150
|
// Observe a settled index: maintained-on-write updates run async after events.
|
|
@@ -146,7 +156,7 @@ export async function loadInboundReferenceViews(projectId, targets) {
|
|
|
146
156
|
const recordsPerTarget = await operations.references
|
|
147
157
|
.inboundMany({
|
|
148
158
|
projectId,
|
|
149
|
-
targets: targets.map((ref) => (
|
|
159
|
+
targets: targets.map((ref) => toTarget(ref)),
|
|
150
160
|
})
|
|
151
161
|
.then((result) => {
|
|
152
162
|
if (!result.ok)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"references-store.js","sourceRoot":"","sources":["../../../src/lib/references/references-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react'\nimport { documentKey } from '@elytracms/core/cms-core'\nimport { useOperationQuery } from '@elytracms/studio/lib/query'\nimport type { CmsDocument, DocumentRef } from '@elytracms/core/cms-core'\nimport type {\n ReferenceEntryRecord,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\nimport { localizedLabel } from '../i18n'\nimport { getStudioOperations } from '../operations'\nimport type { OperationEvent, OperationsClient, StudioOperations } from '../operations'\nimport { getStudioRegistry } from '../registry'\nimport { buildReferenceEntryViews } from './reference-model'\nimport type { ReferenceEntryView, ReferenceLabelContext } from './reference-model'\n\n/**\n * Data layer for the EC-172 references panels. All reads go through\n * `@elytracms/core/operations` queries (`references.outbound` / `references.inbound`\n * plus the labeling queries) — UI code never touches persistence repositories\n * or index tables. The hook keeps panels live: any succeeded command in a\n * content-bearing domain re-queries after the EC-155 maintained-on-write\n * attachment has flushed.\n */\n\nexport interface ReferencePanelQuery {\n projectId: string\n /** Enables the outbound (\"references\") section. */\n source?: ReferenceSource | undefined\n /** Enables the inbound (\"used by\") section. */\n target?: ReferenceTarget | undefined\n}\n\nexport interface ReferencePanelData {\n outbound: ReferenceEntryView[]\n inbound: ReferenceEntryView[]\n /**\n * Per-direction read error (EC-205): a direction whose query returns a\n * structured non-ok degrades to an empty list plus this message, surfaced as a\n * visible per-section error — instead of collapsing the whole panel into its\n * hard-error state. `null` when the direction loaded (or was not requested).\n */\n outboundError: string | null\n inboundError: string | null\n}\n\n// --- Index bootstrap -----------------------------------------------------------\n\n/**\n * The EC-155 index is maintained on write, but seeded/imported projects start\n * with an empty index. {@link ensureReferenceIndex} seeds it once per project per\n * operations client; failures (e.g. a viewer's command denial) degrade to\n * whatever the maintained-on-write index already holds.\n */\nconst backfillsByClient = new WeakMap<StudioOperations, Map<string, Promise<void>>>()\n\n/**\n * Seed the index once if it has never been backfilled, then never again — the\n * EC-269 mutation-free read path. The index is maintained on write, so the\n * (clear + rebuild) `backfill` is only needed to seed a never-built index. A\n * persisted marker (`references.buildMarker`, a read) records completion; when\n * present this returns after a single query with ZERO mutations — so opening the\n * References panel no longer rewrites the whole index every session (the normal\n * Convex case: the EC-233 import already backfilled). Only a genuinely empty/\n * never-built project still pays the one-time backfill (and a viewer who cannot\n * run it degrades to the maintained-on-write index, as before).\n */\nasync function seedReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n try {\n const marker = await studio.operations.references.buildMarker({ projectId })\n if (marker.ok && marker.value !== null) return\n } catch {\n // A marker-read failure falls through to the backfill (best-effort seed).\n }\n await studio.operations.references.backfill({ projectId }).then(\n () => undefined,\n () => undefined,\n )\n}\n\nexport function ensureReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n let byProject = backfillsByClient.get(studio)\n if (!byProject) {\n byProject = new Map()\n backfillsByClient.set(studio, byProject)\n }\n let pending = byProject.get(projectId)\n if (!pending) {\n pending = seedReferenceIndex(studio, projectId)\n byProject.set(projectId, pending)\n }\n return pending\n}\n\n// --- Label context loading -------------------------------------------------------\n\nasync function loadLabelContext(\n operations: OperationsClient,\n projectId: string,\n): Promise<ReferenceLabelContext> {\n const [schemaResult, documentsResult, assetsResult] = await Promise.all([\n operations.schema.get({ projectId }),\n operations.documents.list({ projectId }),\n operations.assets.list({ projectId }),\n ])\n\n const collections = schemaResult.ok ? schemaResult.value.collections : []\n const documents = new Map<string, CmsDocument>()\n if (documentsResult.ok) {\n for (const record of documentsResult.value) {\n documents.set(\n documentKey({ collection: record.document.collection, id: record.document.id }),\n record.document,\n )\n }\n }\n // EC-287: the project graph is retired; reference source labels resolve from\n // CMS documents only. `page`/`layout` graph sources no longer exist.\n const assets = new Map<string, { filename: string; title?: string | undefined }>()\n if (assetsResult.ok) {\n for (const asset of assetsResult.value) {\n assets.set(asset.id, { filename: asset.filename, title: asset.title })\n }\n }\n const registry = getStudioRegistry().registry\n return {\n collections,\n documents,\n assets,\n // EC-407: `title` is a LocalizedString — resolve it to the active UI locale\n // (a plain string is unchanged), never leak a `{ en, de }` object to the label.\n componentTitle: (componentId) => {\n const title = registry.getManifest(componentId)?.title\n return title === undefined ? undefined : localizedLabel(title) || undefined\n },\n }\n}\n\n// --- Panel data loading -----------------------------------------------------------\n\n/** Load both directions + labels for one panel (operations queries only). */\nexport async function loadReferencePanelData(\n query: ReferencePanelQuery,\n): Promise<ReferencePanelData> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, query.projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n // EC-205: load each direction INDEPENDENTLY. A structured non-ok from one\n // direction is a degradable operation result (it carries an OperationError —\n // the documented contract), not an exception: degrade that direction to empty\n // and surface its error per-section (references-panel) while the other still\n // renders, instead of throwing the whole panel into its hard-error state. A\n // genuinely broken backend still throws from getStudioOperations/flush above.\n const [outbound, inbound] = await Promise.all([\n query.source\n ? operations.references.outbound({ projectId: query.projectId, source: query.source })\n : null,\n query.target\n ? operations.references.inbound({ projectId: query.projectId, target: query.target })\n : null,\n ])\n\n type DirectionResult = Awaited<ReturnType<typeof operations.references.outbound>>\n const settle = (\n result: DirectionResult | null,\n ): { records: ReferenceEntryRecord[]; error: string | null } =>\n result === null\n ? { records: [], error: null }\n : result.ok\n ? { records: result.value, error: null }\n : { records: [], error: result.error.message }\n const out = settle(outbound)\n const inb = settle(inbound)\n\n // EC-302: nothing referenced in either direction → no sources/targets to label,\n // so skip the label-context load entirely (schema + ALL documents + ALL assets).\n // Common for leaf content; mirrors the asset short-circuit in\n // `loadInboundAssetReferenceViews`.\n if (out.records.length === 0 && inb.records.length === 0) {\n return { outbound: [], inbound: [], outboundError: out.error, inboundError: inb.error }\n }\n\n const context = await loadLabelContext(operations, query.projectId)\n return {\n outbound: buildReferenceEntryViews(out.records, context),\n inbound: buildReferenceEntryViews(inb.records, context),\n outboundError: out.error,\n inboundError: inb.error,\n }\n}\n\n// --- Impact loading (EC-173 safe unpublish/delete) -------------------------------\n\n/**\n * Load the labelled inbound entry views for a set of target documents in ONE\n * pass (one index flush, one label-context load) — the pre-flight data of the\n * EC-173 confirmation dialogs, including the bulk flows. Returns entries in\n * `targets` order; pair with `buildReferenceImpact` for break/block semantics.\n */\nexport async function loadInboundReferenceViews(\n projectId: string,\n targets: readonly DocumentRef[],\n): Promise<{ ref: DocumentRef; inbound: ReferenceEntryView[] }[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n // EC-308: one batched query for ALL targets (the operation loops the by_target\n // index server-side) instead of a per-target `inbound` fan-out. Returns one\n // record list per target, in `targets` order.\n const recordsPerTarget = await operations.references\n .inboundMany({\n projectId,\n targets: targets.map((ref) => ({ type: 'document', id: documentKey(ref) }) as const),\n })\n .then((result) => {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n })\n // EC-302: no inbound refs for ANY target → no sources to label, so skip the\n // label-context load (schema + ALL documents + ALL assets). The common case when\n // deleting/unpublishing unreferenced documents (incl. bulk); mirrors the EC-301\n // short-circuit already in `loadInboundAssetReferenceViews`.\n if (recordsPerTarget.every((records) => records.length === 0)) {\n return targets.map((ref) => ({ ref, inbound: [] }))\n }\n const context = await loadLabelContext(operations, projectId)\n return targets.map((ref, index) => ({\n ref,\n inbound: buildReferenceEntryViews(recordsPerTarget[index] ?? [], context),\n }))\n}\n\n/**\n * Inbound reference views for ONE asset (EC-202): the \"what uses this asset\"\n * pre-flight of the asset-delete dialog, index-accurate (the EC-155 reference\n * index, not the retired `find-asset-usages` string scan). Mirrors\n * {@link loadInboundReferenceViews} for a single `{ type: 'asset' }` target —\n * the source labels resolve the referencing documents/graph-nodes identically.\n */\nexport async function loadInboundAssetReferenceViews(\n projectId: string,\n assetId: string,\n): Promise<ReferenceEntryView[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n const result = await operations.references.inbound({\n projectId,\n target: { type: 'asset', id: assetId },\n })\n if (!result.ok) throw new Error(result.error.message)\n // No inbound references → no sources to label, so skip the label-context load\n // entirely (schema + ALL documents + ALL assets). This is the common case for a\n // deletable asset, making the delete dialog's pre-flight a single indexed query\n // instead of three full-table lists (EC-301 perf).\n if (result.value.length === 0) return []\n const context = await loadLabelContext(operations, projectId)\n return buildReferenceEntryViews(result.value, context)\n}\n\n// --- React hook ----------------------------------------------------------------\n\n/**\n * Domains whose successful commands can change the reference index or labels and\n * should refresh an open panel. `references` is deliberately ABSENT (EC-269):\n * reacting to reference-index command events (the `backfill`) would make the\n * panel re-trigger on its OWN index maintenance — a self-cascade that, compounded\n * with the per-keystroke `documents` events, produced hundreds of redundant\n * reloads per document. The index is maintained on write; the panel reacts to the\n * CONTENT domains that feed it, never to its own index writes.\n */\nconst REFERENCE_REFRESH_TRIGGERS = [\n 'documents',\n 'graph',\n 'publishing',\n 'assets',\n 'schema',\n] as const\n\nconst REFRESH_DOMAINS: ReadonlySet<string> = new Set(REFERENCE_REFRESH_TRIGGERS)\n\n/**\n * Whether one operation event should refresh an open reference panel. Exported\n * for tests — pins the EC-269 contract that reference-index commands (and any\n * non-succeeded / non-command event) never trigger a reload.\n */\nexport function isReferenceRefreshEvent(event: OperationEvent): boolean {\n return (\n event.kind === 'command' && event.status === 'succeeded' && REFRESH_DOMAINS.has(event.domain)\n )\n}\n\nexport type ReferencePanelStatus = 'loading' | 'ready' | 'error'\n\nexport interface ReferencePanelState {\n status: ReferencePanelStatus\n data: ReferencePanelData | null\n errorMessage: string | null\n refresh: () => void\n}\n\n/**\n * Live panel state for one entity (EC-308/AD-17): loads + caches through the one\n * `useOperationQuery` seam, and re-queries whenever a content-bearing command\n * succeeds anywhere in the studio (the index is maintained on write). Per-direction\n * read errors degrade INSIDE `loadReferencePanelData` (carried on the data); a hard\n * failure (broken backend) surfaces as the query's error → `status: 'error'`.\n */\nexport function useReferencePanel(query: ReferencePanelQuery): ReferencePanelState {\n const { projectId } = query\n const sourceType = query.source?.type\n const sourceId = query.source?.id\n const targetType = query.target?.type\n const targetId = query.target?.id\n\n const stableQuery = useMemo<ReferencePanelQuery>(\n () => ({\n projectId,\n ...(sourceType && sourceId ? { source: { type: sourceType, id: sourceId } } : {}),\n ...(targetType && targetId ? { target: { type: targetType, id: targetId } } : {}),\n }),\n [projectId, sourceType, sourceId, targetType, targetId],\n )\n\n const result = useOperationQuery<ReferencePanelData>({\n queryKey: [\n 'referencePanel',\n projectId,\n sourceType ?? '',\n sourceId ?? '',\n targetType ?? '',\n targetId ?? '',\n ],\n loader: () => loadReferencePanelData(stableQuery),\n invalidateOn: REFERENCE_REFRESH_TRIGGERS,\n })\n\n const status: ReferencePanelStatus = result.isPending\n ? 'loading'\n : result.isError\n ? 'error'\n : 'ready'\n return {\n status,\n data: result.data ?? null,\n errorMessage: result.error instanceof Error ? result.error.message : null,\n refresh: () => void result.refetch(),\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"references-store.js","sourceRoot":"","sources":["../../../src/lib/references/references-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react'\nimport { documentKey } from '@elytracms/core/cms-core'\nimport { useOperationQuery } from '@elytracms/studio/lib/query'\nimport type { CmsDocument, DocumentRef } from '@elytracms/core/cms-core'\nimport type {\n ReferenceEntryRecord,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\nimport { localizedLabel } from '../i18n'\nimport { getStudioOperations } from '../operations'\nimport type { OperationEvent, OperationsClient, StudioOperations } from '../operations'\nimport { getStudioRegistry } from '../registry'\nimport { buildReferenceEntryViews } from './reference-model'\nimport type { ReferenceEntryView, ReferenceLabelContext } from './reference-model'\n\n/**\n * Data layer for the EC-172 references panels. All reads go through\n * `@elytracms/core/operations` queries (`references.outbound` / `references.inbound`\n * plus the labeling queries) — UI code never touches persistence repositories\n * or index tables. The hook keeps panels live: any succeeded command in a\n * content-bearing domain re-queries after the EC-155 maintained-on-write\n * attachment has flushed.\n */\n\nexport interface ReferencePanelQuery {\n projectId: string\n /** Enables the outbound (\"references\") section. */\n source?: ReferenceSource | undefined\n /** Enables the inbound (\"used by\") section. */\n target?: ReferenceTarget | undefined\n}\n\nexport interface ReferencePanelData {\n outbound: ReferenceEntryView[]\n inbound: ReferenceEntryView[]\n /**\n * Per-direction read error (EC-205): a direction whose query returns a\n * structured non-ok degrades to an empty list plus this message, surfaced as a\n * visible per-section error — instead of collapsing the whole panel into its\n * hard-error state. `null` when the direction loaded (or was not requested).\n */\n outboundError: string | null\n inboundError: string | null\n}\n\n// --- Index bootstrap -----------------------------------------------------------\n\n/**\n * The EC-155 index is maintained on write, but seeded/imported projects start\n * with an empty index. {@link ensureReferenceIndex} seeds it once per project per\n * operations client; failures (e.g. a viewer's command denial) degrade to\n * whatever the maintained-on-write index already holds.\n */\nconst backfillsByClient = new WeakMap<StudioOperations, Map<string, Promise<void>>>()\n\n/**\n * Seed the index once if it has never been backfilled, then never again — the\n * EC-269 mutation-free read path. The index is maintained on write, so the\n * (clear + rebuild) `backfill` is only needed to seed a never-built index. A\n * persisted marker (`references.buildMarker`, a read) records completion; when\n * present this returns after a single query with ZERO mutations — so opening the\n * References panel no longer rewrites the whole index every session (the normal\n * Convex case: the EC-233 import already backfilled). Only a genuinely empty/\n * never-built project still pays the one-time backfill (and a viewer who cannot\n * run it degrades to the maintained-on-write index, as before).\n */\nasync function seedReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n try {\n const marker = await studio.operations.references.buildMarker({ projectId })\n if (marker.ok && marker.value !== null) return\n } catch {\n // A marker-read failure falls through to the backfill (best-effort seed).\n }\n await studio.operations.references.backfill({ projectId }).then(\n () => undefined,\n () => undefined,\n )\n}\n\nexport function ensureReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n let byProject = backfillsByClient.get(studio)\n if (!byProject) {\n byProject = new Map()\n backfillsByClient.set(studio, byProject)\n }\n let pending = byProject.get(projectId)\n if (!pending) {\n pending = seedReferenceIndex(studio, projectId)\n byProject.set(projectId, pending)\n }\n return pending\n}\n\n// --- Label context loading -------------------------------------------------------\n\nasync function loadLabelContext(\n operations: OperationsClient,\n projectId: string,\n): Promise<ReferenceLabelContext> {\n const [schemaResult, documentsResult, assetsResult] = await Promise.all([\n operations.schema.get({ projectId }),\n operations.documents.list({ projectId }),\n operations.assets.list({ projectId }),\n ])\n\n const collections = schemaResult.ok ? schemaResult.value.collections : []\n const documents = new Map<string, CmsDocument>()\n if (documentsResult.ok) {\n for (const record of documentsResult.value) {\n documents.set(\n documentKey({ collection: record.document.collection, id: record.document.id }),\n record.document,\n )\n }\n }\n // EC-287: the project graph is retired; reference source labels resolve from\n // CMS documents only. `page`/`layout` graph sources no longer exist.\n const assets = new Map<string, { filename: string; title?: string | undefined }>()\n if (assetsResult.ok) {\n for (const asset of assetsResult.value) {\n assets.set(asset.id, { filename: asset.filename, title: asset.title })\n }\n }\n const registry = getStudioRegistry().registry\n return {\n collections,\n documents,\n assets,\n // EC-407: `title` is a LocalizedString — resolve it to the active UI locale\n // (a plain string is unchanged), never leak a `{ en, de }` object to the label.\n componentTitle: (componentId) => {\n const title = registry.getManifest(componentId)?.title\n return title === undefined ? undefined : localizedLabel(title) || undefined\n },\n }\n}\n\n// --- Panel data loading -----------------------------------------------------------\n\n/** Load both directions + labels for one panel (operations queries only). */\nexport async function loadReferencePanelData(\n query: ReferencePanelQuery,\n): Promise<ReferencePanelData> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, query.projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n // EC-205: load each direction INDEPENDENTLY. A structured non-ok from one\n // direction is a degradable operation result (it carries an OperationError —\n // the documented contract), not an exception: degrade that direction to empty\n // and surface its error per-section (references-panel) while the other still\n // renders, instead of throwing the whole panel into its hard-error state. A\n // genuinely broken backend still throws from getStudioOperations/flush above.\n const [outbound, inbound] = await Promise.all([\n query.source\n ? operations.references.outbound({ projectId: query.projectId, source: query.source })\n : null,\n query.target\n ? operations.references.inbound({ projectId: query.projectId, target: query.target })\n : null,\n ])\n\n type DirectionResult = Awaited<ReturnType<typeof operations.references.outbound>>\n const settle = (\n result: DirectionResult | null,\n ): { records: ReferenceEntryRecord[]; error: string | null } =>\n result === null\n ? { records: [], error: null }\n : result.ok\n ? { records: result.value, error: null }\n : { records: [], error: result.error.message }\n const out = settle(outbound)\n const inb = settle(inbound)\n\n // EC-302: nothing referenced in either direction → no sources/targets to label,\n // so skip the label-context load entirely (schema + ALL documents + ALL assets).\n // Common for leaf content; mirrors the asset short-circuit in\n // `loadInboundAssetReferenceViews`.\n if (out.records.length === 0 && inb.records.length === 0) {\n return { outbound: [], inbound: [], outboundError: out.error, inboundError: inb.error }\n }\n\n const context = await loadLabelContext(operations, query.projectId)\n return {\n outbound: buildReferenceEntryViews(out.records, context),\n inbound: buildReferenceEntryViews(inb.records, context),\n outboundError: out.error,\n inboundError: inb.error,\n }\n}\n\n// --- Impact loading (EC-173 safe unpublish/delete) -------------------------------\n\n/**\n * Load the labelled inbound entry views for a set of target documents in ONE\n * pass (one index flush, one label-context load) — the pre-flight data of the\n * EC-173 confirmation dialogs, including the bulk flows. Returns entries in\n * `targets` order; pair with `buildReferenceImpact` for break/block semantics.\n *\n * `toTarget` maps each acted-on ref to its reference-index target shape\n * (EC-433): the default is the document shape every `cmsDocuments` collection\n * indexes under; a rail whose records live under a different target type (the\n * assets rail, `{ type: 'asset' }`) supplies its own via\n * `CollectionViewSource.referenceTarget` — otherwise its pre-flight would\n * query the wrong shape and report referenced records as \"not referenced\".\n */\nexport async function loadInboundReferenceViews(\n projectId: string,\n targets: readonly DocumentRef[],\n toTarget: (ref: DocumentRef) => ReferenceTarget = (ref) => ({\n type: 'document',\n id: documentKey(ref),\n }),\n): Promise<{ ref: DocumentRef; inbound: ReferenceEntryView[] }[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n // EC-308: one batched query for ALL targets (the operation loops the by_target\n // index server-side) instead of a per-target `inbound` fan-out. Returns one\n // record list per target, in `targets` order.\n const recordsPerTarget = await operations.references\n .inboundMany({\n projectId,\n targets: targets.map((ref) => toTarget(ref)),\n })\n .then((result) => {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n })\n // EC-302: no inbound refs for ANY target → no sources to label, so skip the\n // label-context load (schema + ALL documents + ALL assets). The common case when\n // deleting/unpublishing unreferenced documents (incl. bulk); mirrors the EC-301\n // short-circuit already in `loadInboundAssetReferenceViews`.\n if (recordsPerTarget.every((records) => records.length === 0)) {\n return targets.map((ref) => ({ ref, inbound: [] }))\n }\n const context = await loadLabelContext(operations, projectId)\n return targets.map((ref, index) => ({\n ref,\n inbound: buildReferenceEntryViews(recordsPerTarget[index] ?? [], context),\n }))\n}\n\n/**\n * Inbound reference views for ONE asset (EC-202): the \"what uses this asset\"\n * pre-flight of the asset-delete dialog, index-accurate (the EC-155 reference\n * index, not the retired `find-asset-usages` string scan). Mirrors\n * {@link loadInboundReferenceViews} for a single `{ type: 'asset' }` target —\n * the source labels resolve the referencing documents/graph-nodes identically.\n */\nexport async function loadInboundAssetReferenceViews(\n projectId: string,\n assetId: string,\n): Promise<ReferenceEntryView[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n const result = await operations.references.inbound({\n projectId,\n target: { type: 'asset', id: assetId },\n })\n if (!result.ok) throw new Error(result.error.message)\n // No inbound references → no sources to label, so skip the label-context load\n // entirely (schema + ALL documents + ALL assets). This is the common case for a\n // deletable asset, making the delete dialog's pre-flight a single indexed query\n // instead of three full-table lists (EC-301 perf).\n if (result.value.length === 0) return []\n const context = await loadLabelContext(operations, projectId)\n return buildReferenceEntryViews(result.value, context)\n}\n\n// --- React hook ----------------------------------------------------------------\n\n/**\n * Domains whose successful commands can change the reference index or labels and\n * should refresh an open panel. `references` is deliberately ABSENT (EC-269):\n * reacting to reference-index command events (the `backfill`) would make the\n * panel re-trigger on its OWN index maintenance — a self-cascade that, compounded\n * with the per-keystroke `documents` events, produced hundreds of redundant\n * reloads per document. The index is maintained on write; the panel reacts to the\n * CONTENT domains that feed it, never to its own index writes.\n */\nconst REFERENCE_REFRESH_TRIGGERS = [\n 'documents',\n 'graph',\n 'publishing',\n 'assets',\n 'schema',\n] as const\n\nconst REFRESH_DOMAINS: ReadonlySet<string> = new Set(REFERENCE_REFRESH_TRIGGERS)\n\n/**\n * Whether one operation event should refresh an open reference panel. Exported\n * for tests — pins the EC-269 contract that reference-index commands (and any\n * non-succeeded / non-command event) never trigger a reload.\n */\nexport function isReferenceRefreshEvent(event: OperationEvent): boolean {\n return (\n event.kind === 'command' && event.status === 'succeeded' && REFRESH_DOMAINS.has(event.domain)\n )\n}\n\nexport type ReferencePanelStatus = 'loading' | 'ready' | 'error'\n\nexport interface ReferencePanelState {\n status: ReferencePanelStatus\n data: ReferencePanelData | null\n errorMessage: string | null\n refresh: () => void\n}\n\n/**\n * Live panel state for one entity (EC-308/AD-17): loads + caches through the one\n * `useOperationQuery` seam, and re-queries whenever a content-bearing command\n * succeeds anywhere in the studio (the index is maintained on write). Per-direction\n * read errors degrade INSIDE `loadReferencePanelData` (carried on the data); a hard\n * failure (broken backend) surfaces as the query's error → `status: 'error'`.\n */\nexport function useReferencePanel(query: ReferencePanelQuery): ReferencePanelState {\n const { projectId } = query\n const sourceType = query.source?.type\n const sourceId = query.source?.id\n const targetType = query.target?.type\n const targetId = query.target?.id\n\n const stableQuery = useMemo<ReferencePanelQuery>(\n () => ({\n projectId,\n ...(sourceType && sourceId ? { source: { type: sourceType, id: sourceId } } : {}),\n ...(targetType && targetId ? { target: { type: targetType, id: targetId } } : {}),\n }),\n [projectId, sourceType, sourceId, targetType, targetId],\n )\n\n const result = useOperationQuery<ReferencePanelData>({\n queryKey: [\n 'referencePanel',\n projectId,\n sourceType ?? '',\n sourceId ?? '',\n targetType ?? '',\n targetId ?? '',\n ],\n loader: () => loadReferencePanelData(stableQuery),\n invalidateOn: REFERENCE_REFRESH_TRIGGERS,\n })\n\n const status: ReferencePanelStatus = result.isPending\n ? 'loading'\n : result.isError\n ? 'error'\n : 'ready'\n return {\n status,\n data: result.data ?? null,\n errorMessage: result.error instanceof Error ? result.error.message : null,\n refresh: () => void result.refetch(),\n }\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"]}
|
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"]}
|
|
@@ -26,15 +26,18 @@ import type { CSSProperties } from 'react';
|
|
|
26
26
|
* The stage width presets — one per surface family (ratified AD-A). Consumers
|
|
27
27
|
* pick a preset, never a raw width, so stage surfaces stay mutually consistent:
|
|
28
28
|
*
|
|
29
|
-
* - `prose` — distraction-free writing (the rich-text expand, EC-431/AD-B).
|
|
30
|
-
* ratified `min(920px, 92vw)`;
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* - `prose` — distraction-free writing (the rich-text expand, EC-431/AD-B).
|
|
30
|
+
* EC-431 ratified `min(920px, 92vw)`; owner feedback on 0.0.68 (EC-436,
|
|
31
|
+
* 2026-07-16 — "the dialog + text looks a bit narrow on a big screen")
|
|
32
|
+
* SUPERSEDED that number: the panel widened to `min(1160px, 94vw)` and the
|
|
33
|
+
* caller's CONTENT column grew in step (~78ch,
|
|
34
|
+
* `RICH_TEXT_STAGE_CONTENT_CLASS`). The column stays constrained — the stage
|
|
35
|
+
* being wider than the text is the point, not a defect.
|
|
33
36
|
* - `grid` — large centered browsing grids (the EC-432 media browser).
|
|
34
37
|
* - `form` — a single-column form measure (the EC-430 embed inspector).
|
|
35
38
|
*/
|
|
36
39
|
export declare const STAGE_WIDTHS: {
|
|
37
|
-
readonly prose: "min(
|
|
40
|
+
readonly prose: "min(1160px, 94vw)";
|
|
38
41
|
readonly grid: "min(1240px, 94vw)";
|
|
39
42
|
readonly form: "min(720px, 92vw)";
|
|
40
43
|
};
|
|
@@ -25,15 +25,18 @@
|
|
|
25
25
|
* The stage width presets — one per surface family (ratified AD-A). Consumers
|
|
26
26
|
* pick a preset, never a raw width, so stage surfaces stay mutually consistent:
|
|
27
27
|
*
|
|
28
|
-
* - `prose` — distraction-free writing (the rich-text expand, EC-431/AD-B).
|
|
29
|
-
* ratified `min(920px, 92vw)`;
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* - `prose` — distraction-free writing (the rich-text expand, EC-431/AD-B).
|
|
29
|
+
* EC-431 ratified `min(920px, 92vw)`; owner feedback on 0.0.68 (EC-436,
|
|
30
|
+
* 2026-07-16 — "the dialog + text looks a bit narrow on a big screen")
|
|
31
|
+
* SUPERSEDED that number: the panel widened to `min(1160px, 94vw)` and the
|
|
32
|
+
* caller's CONTENT column grew in step (~78ch,
|
|
33
|
+
* `RICH_TEXT_STAGE_CONTENT_CLASS`). The column stays constrained — the stage
|
|
34
|
+
* being wider than the text is the point, not a defect.
|
|
32
35
|
* - `grid` — large centered browsing grids (the EC-432 media browser).
|
|
33
36
|
* - `form` — a single-column form measure (the EC-430 embed inspector).
|
|
34
37
|
*/
|
|
35
38
|
export const STAGE_WIDTHS = {
|
|
36
|
-
prose: 'min(
|
|
39
|
+
prose: 'min(1160px, 94vw)',
|
|
37
40
|
grid: 'min(1240px, 94vw)',
|
|
38
41
|
form: 'min(720px, 92vw)',
|
|
39
42
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface-geometry.js","sourceRoot":"","sources":["../../src/ui/surface-geometry.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH
|
|
1
|
+
{"version":3,"file":"surface-geometry.js","sourceRoot":"","sources":["../../src/ui/surface-geometry.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,KAAK,EAAE,mBAAmB;IAC1B,IAAI,EAAE,mBAAmB;IACzB,IAAI,EAAE,kBAAkB;CAChB,CAAA;AAkCV,MAAM,UAAU,eAAe,CAAC,KAA2B;IACzD,IAAI,KAAK,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;QAC9B,OAAO;YACL,oEAAoE;YACpE,mEAAmE;YACnE,2CAA2C;YAC3C,SAAS,EAAE,gEAAgE;YAC3E,QAAQ,EACN,sFAAsF;YACxF,KAAK,EACH,kIAAkI;YACpI,UAAU,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE;SAClD,CAAA;IACH,CAAC;IACD,6EAA6E;IAC7E,mEAAmE;IACnE,OAAO;QACL,SAAS,EAAE,yBAAyB;QACpC,QAAQ,EAAE,0CAA0C;QACpD,KAAK,EAAE,gEAAgE;QACvE,UAAU,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE;KACnC,CAAA;AACH,CAAC","sourcesContent":["import type { CSSProperties } from 'react'\n\n/**\n * EC-431 (AD-A) — the pure geometry core for {@link ExpandableSurface}: which\n * classes/width a surface mounts with, per variant. Extracted (the EC-391\n * class-contract pattern) so the geometry is pinnable in the node test env,\n * where the portal itself renders `null`.\n *\n * Two geometries, one mechanic set (portal-to-body, backdrop, header close,\n * layer-stacked Escape):\n *\n * - `side` — the right-anchored sheet. Reserved for genuinely PERIPHERAL review\n * surfaces (a side panel semantically means \"detail next to the primary\n * activity\"); today exactly one: the AI proposal review card. Byte-for-byte\n * the EC-392 shipped shape — no visual change for that caller.\n * - `stage` — the centered takeover for PRIMARY-ACTIVITY surfaces (ratified\n * AD-A, 2026-07-16): centered, near-full height, growing from center with a\n * subtle scale/opacity entrance — never an edge slide. Width comes from a\n * per-surface preset ({@link STAGE_WIDTHS}).\n *\n * Motion: only the stage has an entrance transition, and every animated class\n * is `motion-safe:`-gated — under `prefers-reduced-motion` the stage appears\n * instantly (pinned by `surface-geometry.test.ts`). The side sheet keeps its\n * shipped no-transition behavior.\n */\n\n/**\n * The stage width presets — one per surface family (ratified AD-A). Consumers\n * pick a preset, never a raw width, so stage surfaces stay mutually consistent:\n *\n * - `prose` — distraction-free writing (the rich-text expand, EC-431/AD-B).\n * EC-431 ratified `min(920px, 92vw)`; owner feedback on 0.0.68 (EC-436,\n * 2026-07-16 — \"the dialog + text looks a bit narrow on a big screen\")\n * SUPERSEDED that number: the panel widened to `min(1160px, 94vw)` and the\n * caller's CONTENT column grew in step (~78ch,\n * `RICH_TEXT_STAGE_CONTENT_CLASS`). The column stays constrained — the stage\n * being wider than the text is the point, not a defect.\n * - `grid` — large centered browsing grids (the EC-432 media browser).\n * - `form` — a single-column form measure (the EC-430 embed inspector).\n */\nexport const STAGE_WIDTHS = {\n prose: 'min(1160px, 94vw)',\n grid: 'min(1240px, 94vw)',\n form: 'min(720px, 92vw)',\n} as const\n\nexport type StageWidthPreset = keyof typeof STAGE_WIDTHS\n\n/**\n * The geometry-discriminating props ({@link ExpandableSurface} spreads these\n * into its own): `side` callers keep passing their shipped CSS width; `stage`\n * callers pick a preset.\n */\nexport type SurfaceGeometryProps =\n | {\n /** The right-anchored sheet (default) — peripheral review surfaces only. */\n variant?: 'side'\n /** CSS width of the sheet panel (callers keep their shipped widths). */\n width: string\n }\n | {\n /** The centered stage — primary-activity surfaces (ratified AD-A). */\n variant: 'stage'\n /** Per-surface stage width preset. */\n preset: StageWidthPreset\n }\n\nexport interface SurfaceGeometry {\n /** The fixed full-viewport container. */\n container: string\n /** The dismiss backdrop (paint order keeps it under the panel). */\n backdrop: string\n /** The `role=\"dialog\"` panel. */\n panel: string\n /** Inline style carrying the resolved panel width (deterministic, no class scanning). */\n panelStyle: CSSProperties\n}\n\nexport function surfaceGeometry(props: SurfaceGeometryProps): SurfaceGeometry {\n if (props.variant === 'stage') {\n return {\n // Near-full height falls out of the container padding: the panel is\n // `h-full` inside the padded fixed container, so it always keeps a\n // breathing gutter to every viewport edge.\n container: 'fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6',\n backdrop:\n 'absolute inset-0 bg-background/70 backdrop-blur-sm motion-safe:animate-ec-stage-fade',\n panel:\n 'relative flex h-full flex-col overflow-hidden rounded-xl border border-border bg-card shadow-2xl motion-safe:animate-ec-stage-in',\n panelStyle: { width: STAGE_WIDTHS[props.preset] },\n }\n }\n // The EC-392 shipped sheet shape, byte-for-byte — the proposal card must not\n // change visually (ratified: side survives for peripheral review).\n return {\n container: 'fixed inset-0 z-50 flex',\n backdrop: 'flex-1 bg-background/70 backdrop-blur-sm',\n panel: 'flex h-full flex-col border-l border-border bg-card shadow-2xl',\n panelStyle: { width: props.width },\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.70",
|
|
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",
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"lucide-react": "^1.23.0",
|
|
147
147
|
"tailwind-merge": "^3.6.0",
|
|
148
148
|
"zod": "^4.4.3",
|
|
149
|
-
"@elytracms/core": "^0.0.
|
|
149
|
+
"@elytracms/core": "^0.0.70"
|
|
150
150
|
},
|
|
151
151
|
"peerDependencies": {
|
|
152
152
|
"@tanstack/react-router": "^1.170.0",
|