@elytracms/studio 0.0.74 → 0.0.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/cms.ts +166 -5
- package/convex/schema.ts +21 -0
- package/dist/components/app-views/default-modules.js +4 -4
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +37 -2
- package/dist/components/cms/cms-store.js +121 -8
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +34 -17
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/column-width.d.ts +8 -0
- package/dist/components/cms/column-width.js +8 -0
- package/dist/components/cms/column-width.js.map +1 -1
- package/dist/components/cms/doc-state.d.ts +53 -0
- package/dist/components/cms/doc-state.js +58 -1
- package/dist/components/cms/doc-state.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +8 -4
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/project-list.js +6 -2
- package/dist/components/project-list.js.map +1 -1
- package/dist/components/studio-project-screen.js +4 -2
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +29 -11
- package/dist/lib/cms-views/view-model.js +43 -16
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +16 -5
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +4 -0
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +2 -0
- package/dist/lib/i18n/messages.en.js +7 -0
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/cms-persistence.d.ts +8 -0
- package/dist/lib/operations/cms-persistence.js +9 -1
- package/dist/lib/operations/cms-persistence.js.map +1 -1
- package/dist/lib/operations/session-hydration.d.ts +22 -0
- package/dist/lib/operations/session-hydration.js +12 -0
- package/dist/lib/operations/session-hydration.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.js +14 -1
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/router/index.d.ts +7 -0
- package/dist/router/index.js +12 -1
- package/dist/router/index.js.map +1 -1
- package/dist/router/navigate-once.d.ts +77 -0
- package/dist/router/navigate-once.js +104 -0
- package/dist/router/navigate-once.js.map +1 -0
- package/dist/router/root.js +7 -3
- package/dist/router/root.js.map +1 -1
- package/dist/ui/status-dot.d.ts +17 -6
- package/dist/ui/status-dot.js +9 -7
- package/dist/ui/status-dot.js.map +1 -1
- package/package.json +2 -2
package/convex/cms.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { v } from 'convex/values'
|
|
2
|
-
import { foldDocument, unfoldDocument } from '@elytracms/core/cms-core'
|
|
2
|
+
import { documentsSemanticEqual, foldDocument, unfoldDocument } from '@elytracms/core/cms-core'
|
|
3
3
|
import type { CmsDocument } from '@elytracms/core/cms-core'
|
|
4
|
-
import {
|
|
4
|
+
import { backfillHasUnpublishedChanges, backfillPublishedAt } from '@elytracms/core/persistence'
|
|
5
|
+
import { internalMutation, mutation, query } from './functions'
|
|
5
6
|
import { requireCapability } from './guard'
|
|
6
7
|
import { assertWithinWriteLimits } from './write-limits'
|
|
7
8
|
|
|
@@ -59,9 +60,39 @@ export const upsertDocument = mutation({
|
|
|
59
60
|
.withIndex('by_doc', (q) => q.eq('collection', args.collection).eq('docId', args.docId))
|
|
60
61
|
.first()
|
|
61
62
|
if (existing) {
|
|
63
|
+
// EC-449: a draft save while PUBLISHED re-materializes `hasUnpublishedChanges`
|
|
64
|
+
// by comparing the incoming draft (unfolded, off the arg wire) against the
|
|
65
|
+
// pinned published snapshot — the mutation has both sides, so the compare runs
|
|
66
|
+
// ONCE here, in-transaction (never per list render). A no-op save compares
|
|
67
|
+
// equal → `false` (never flips to true); a revert back to the published
|
|
68
|
+
// content clears a previously-true flag the same way. Schema-less lens
|
|
69
|
+
// (AD-11 — the deployment stores documents only); the compare is the editor's
|
|
70
|
+
// exact `documentsSemanticEqual`. Unpublished doc / missing pinned row →
|
|
71
|
+
// absent-key patch preserves the row as-is (the flag doesn't apply).
|
|
72
|
+
let changedFlag = {}
|
|
73
|
+
if (existing.publishedVersion !== undefined) {
|
|
74
|
+
const pinned = await ctx.db
|
|
75
|
+
.query('documentVersions')
|
|
76
|
+
.withIndex('by_doc_version', (q) =>
|
|
77
|
+
q
|
|
78
|
+
.eq('collection', args.collection)
|
|
79
|
+
.eq('docId', args.docId)
|
|
80
|
+
.eq('version', existing.publishedVersion as number),
|
|
81
|
+
)
|
|
82
|
+
.unique()
|
|
83
|
+
if (pinned) {
|
|
84
|
+
changedFlag = {
|
|
85
|
+
hasUnpublishedChanges: !documentsSemanticEqual(
|
|
86
|
+
undefined,
|
|
87
|
+
unfoldDocument(pinned.snapshot as CmsDocument),
|
|
88
|
+
args.document as CmsDocument,
|
|
89
|
+
),
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
62
93
|
// EC-153 parity: updates preserve `createdAt`, exactly like in-memory.
|
|
63
94
|
// EC-286: the write-chokepoint trigger reindexes references in-transaction.
|
|
64
|
-
const patch = { ...stored, createdAt: existing.createdAt }
|
|
95
|
+
const patch = { ...stored, createdAt: existing.createdAt, ...changedFlag }
|
|
65
96
|
await ctx.db.patch(existing._id, patch)
|
|
66
97
|
// EC-322: return the merged row directly instead of re-reading the full
|
|
67
98
|
// (up-to-~20 KB) document body via `ctx.db.get`. `patch` only overwrites the
|
|
@@ -241,6 +272,9 @@ export const publishDocumentVersion = mutation({
|
|
|
241
272
|
docId: v.string(),
|
|
242
273
|
version: v.number(),
|
|
243
274
|
updatedAt: v.string(),
|
|
275
|
+
// EC-447: the candidate first-publish instant (a fresh publish-action timestamp,
|
|
276
|
+
// generated client-side like `updatedAt`). The handler writes it SET-ONCE below.
|
|
277
|
+
publishedAt: v.string(),
|
|
244
278
|
},
|
|
245
279
|
handler: async (ctx, args) => {
|
|
246
280
|
await requireCapability(ctx, 'content.write') // EC-150
|
|
@@ -252,7 +286,17 @@ export const publishDocumentVersion = mutation({
|
|
|
252
286
|
// EC-286: setting the live pin (`publishedVersion`) is a change the
|
|
253
287
|
// write-chokepoint trigger sees — it reindexes the published reference rows
|
|
254
288
|
// and fires host revalidation (EC-146 instant publish). No manual calls here.
|
|
255
|
-
|
|
289
|
+
// EC-447: materialize `publishedAt` SET-ONCE — only when the row has none yet, so
|
|
290
|
+
// the FIRST-publish instant is captured and PRESERVED across republishes (the
|
|
291
|
+
// guard is server-side + in-transaction, so it can't race).
|
|
292
|
+
// EC-449: the pinned version IS the published baseline now — publish always
|
|
293
|
+
// resolves `hasUnpublishedChanges` to `false`.
|
|
294
|
+
await ctx.db.patch(doc._id, {
|
|
295
|
+
publishedVersion: args.version,
|
|
296
|
+
updatedAt: args.updatedAt,
|
|
297
|
+
...(doc.publishedAt === undefined ? { publishedAt: args.publishedAt } : {}),
|
|
298
|
+
hasUnpublishedChanges: false,
|
|
299
|
+
})
|
|
256
300
|
const row = await ctx.db.get(doc._id)
|
|
257
301
|
return row ? withUnfoldedDocument(row) : row // EC-260: unfold before returning
|
|
258
302
|
},
|
|
@@ -270,8 +314,125 @@ export const unpublishDocument = mutation({
|
|
|
270
314
|
// Convex patch removes a field set to `undefined` — clears the live pin.
|
|
271
315
|
// EC-286: clearing the pin is a change the write-chokepoint trigger sees — it
|
|
272
316
|
// drops the published index rows and revalidates the host. No manual calls.
|
|
273
|
-
|
|
317
|
+
// EC-447: unpublish CLEARS `publishedAt` too (a later republish records a fresh
|
|
318
|
+
// first-publish stamp); `createdAt` is untouched.
|
|
319
|
+
// EC-449: `hasUnpublishedChanges` is cleared too — with no published baseline
|
|
320
|
+
// there is nothing to differ from (the dot reads `draft`/muted anyway).
|
|
321
|
+
await ctx.db.patch(doc._id, {
|
|
322
|
+
publishedVersion: undefined,
|
|
323
|
+
publishedAt: undefined,
|
|
324
|
+
hasUnpublishedChanges: undefined,
|
|
325
|
+
updatedAt: args.updatedAt,
|
|
326
|
+
})
|
|
274
327
|
const row = await ctx.db.get(doc._id)
|
|
275
328
|
return row ? withUnfoldedDocument(row) : row // EC-260: unfold before returning
|
|
276
329
|
},
|
|
277
330
|
})
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* EC-447 one-time backfill — close-reconstruct `publishedAt` on documents published
|
|
334
|
+
* BEFORE it was materialized. For each currently-published doc that has NONE, sets
|
|
335
|
+
* `publishedAt` to its PINNED published version's `createdAt` (the best-available
|
|
336
|
+
* approximation of the real publish moment, owner-blessed). The decision is the pure,
|
|
337
|
+
* unit-tested `backfillPublishedAt` (`@elytracms/core/persistence`) — one source of
|
|
338
|
+
* truth: an already-stamped doc, an unpublished doc, or one whose pinned version row
|
|
339
|
+
* is missing is SKIPPED, so a re-run performs ZERO writes (idempotent). Touches ONLY
|
|
340
|
+
* `publishedAt` (never `createdAt`/`updatedAt`/the pin), so the write-chokepoint
|
|
341
|
+
* trigger fires no revalidation (`publishedVersion` is unchanged — see triggers.ts).
|
|
342
|
+
*
|
|
343
|
+
* INTERNAL (scheduler/CLI only, EC-150): the operator runs it once per deployment
|
|
344
|
+
* after `convex deploy` (e.g. `npx convex run cms:backfillPublishedAtColumn`); it is
|
|
345
|
+
* not client-callable. Returns a small audit summary (`{ scanned, stamped }`).
|
|
346
|
+
*/
|
|
347
|
+
export const backfillPublishedAtColumn = internalMutation({
|
|
348
|
+
args: {},
|
|
349
|
+
handler: async (ctx) => {
|
|
350
|
+
const docs = await ctx.db.query('cmsDocuments').collect()
|
|
351
|
+
let scanned = 0
|
|
352
|
+
let stamped = 0
|
|
353
|
+
for (const doc of docs) {
|
|
354
|
+
scanned += 1
|
|
355
|
+
// Skip the version read for the certain-skip cases (draft / already-stamped);
|
|
356
|
+
// otherwise resolve the pinned version and let the pure helper decide.
|
|
357
|
+
if (doc.publishedVersion === undefined || doc.publishedAt !== undefined) continue
|
|
358
|
+
const pinned = await ctx.db
|
|
359
|
+
.query('documentVersions')
|
|
360
|
+
.withIndex('by_doc_version', (q) =>
|
|
361
|
+
q
|
|
362
|
+
.eq('collection', doc.collection)
|
|
363
|
+
.eq('docId', doc.docId)
|
|
364
|
+
.eq('version', doc.publishedVersion as number),
|
|
365
|
+
)
|
|
366
|
+
.unique()
|
|
367
|
+
const publishedAt = backfillPublishedAt(
|
|
368
|
+
{ publishedVersion: doc.publishedVersion, publishedAt: doc.publishedAt },
|
|
369
|
+
pinned?.createdAt,
|
|
370
|
+
)
|
|
371
|
+
if (publishedAt === undefined) continue
|
|
372
|
+
await ctx.db.patch(doc._id, { publishedAt })
|
|
373
|
+
stamped += 1
|
|
374
|
+
}
|
|
375
|
+
return { scanned, stamped }
|
|
376
|
+
},
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* EC-449 one-time backfill — materialize `hasUnpublishedChanges` on documents
|
|
381
|
+
* published BEFORE the flag was recorded, so the amber "changed" dot is correct
|
|
382
|
+
* immediately after the repoint instead of understating until the next save. For
|
|
383
|
+
* each currently-published doc that has NO flag yet, runs the SAME semantic
|
|
384
|
+
* compare the write chokepoint runs going forward (`documentsSemanticEqual`,
|
|
385
|
+
* schema-less lens) of the stored draft against its pinned published snapshot —
|
|
386
|
+
* the full compare was chosen over the ratified default-`false` fallback because
|
|
387
|
+
* it costs one indexed version read + one compare per unstamped published doc
|
|
388
|
+
* (the same order of work as `cms:backfillPublishedAtColumn`). The decision is
|
|
389
|
+
* the pure, unit-tested `backfillHasUnpublishedChanges`
|
|
390
|
+
* (`@elytracms/core/persistence`): an already-flagged doc, an unpublished doc,
|
|
391
|
+
* or one whose pinned version row is missing is SKIPPED, so a re-run performs
|
|
392
|
+
* ZERO writes (idempotent). Touches ONLY `hasUnpublishedChanges` (never the
|
|
393
|
+
* pin/`document`/timestamps), so the write-chokepoint trigger fires no host
|
|
394
|
+
* revalidation (`publishedVersion` unchanged — see triggers.ts).
|
|
395
|
+
*
|
|
396
|
+
* INTERNAL (scheduler/CLI only, EC-150): the operator runs it once per deployment
|
|
397
|
+
* after `convex deploy` (e.g. `npx convex run cms:backfillHasUnpublishedChangesColumn`);
|
|
398
|
+
* it is not client-callable. Returns a small audit summary (`{ scanned, stamped }`).
|
|
399
|
+
*/
|
|
400
|
+
export const backfillHasUnpublishedChangesColumn = internalMutation({
|
|
401
|
+
args: {},
|
|
402
|
+
handler: async (ctx) => {
|
|
403
|
+
const docs = await ctx.db.query('cmsDocuments').collect()
|
|
404
|
+
let scanned = 0
|
|
405
|
+
let stamped = 0
|
|
406
|
+
for (const doc of docs) {
|
|
407
|
+
scanned += 1
|
|
408
|
+
// Skip the version read for the certain-skip cases (draft / already-flagged);
|
|
409
|
+
// otherwise resolve the pinned snapshot, compare once, and let the pure
|
|
410
|
+
// helper decide.
|
|
411
|
+
if (doc.publishedVersion === undefined || doc.hasUnpublishedChanges !== undefined) continue
|
|
412
|
+
const pinned = await ctx.db
|
|
413
|
+
.query('documentVersions')
|
|
414
|
+
.withIndex('by_doc_version', (q) =>
|
|
415
|
+
q
|
|
416
|
+
.eq('collection', doc.collection)
|
|
417
|
+
.eq('docId', doc.docId)
|
|
418
|
+
.eq('version', doc.publishedVersion as number),
|
|
419
|
+
)
|
|
420
|
+
.unique()
|
|
421
|
+
const differs = pinned
|
|
422
|
+
? !documentsSemanticEqual(
|
|
423
|
+
undefined,
|
|
424
|
+
unfoldDocument(pinned.snapshot as CmsDocument),
|
|
425
|
+
unfoldDocument(doc.document as CmsDocument),
|
|
426
|
+
)
|
|
427
|
+
: undefined
|
|
428
|
+
const hasUnpublishedChanges = backfillHasUnpublishedChanges(
|
|
429
|
+
{ publishedVersion: doc.publishedVersion, hasUnpublishedChanges: doc.hasUnpublishedChanges },
|
|
430
|
+
differs,
|
|
431
|
+
)
|
|
432
|
+
if (hasUnpublishedChanges === undefined) continue
|
|
433
|
+
await ctx.db.patch(doc._id, { hasUnpublishedChanges })
|
|
434
|
+
stamped += 1
|
|
435
|
+
}
|
|
436
|
+
return { scanned, stamped }
|
|
437
|
+
},
|
|
438
|
+
})
|
package/convex/schema.ts
CHANGED
|
@@ -139,6 +139,27 @@ export default defineSchema({
|
|
|
139
139
|
docId: v.string(),
|
|
140
140
|
document: v.any(),
|
|
141
141
|
publishedVersion: v.optional(v.number()),
|
|
142
|
+
/**
|
|
143
|
+
* EC-447: the FIRST-publish instant of the current lifetime — a fresh
|
|
144
|
+
* publish-action ISO timestamp captured AT publish (NOT a revision `createdAt`),
|
|
145
|
+
* written set-once on publish and cleared on unpublish. A distinct materialized
|
|
146
|
+
* value alongside `createdAt`/`updatedAt`, backing the `$publishedAt` list column.
|
|
147
|
+
* Optional (additive): existing rows stay valid; a one-time backfill
|
|
148
|
+
* (`cms:backfillPublishedAt`) close-reconstructs it for pre-EC-447 published docs.
|
|
149
|
+
*/
|
|
150
|
+
publishedAt: v.optional(v.string()),
|
|
151
|
+
/**
|
|
152
|
+
* EC-449: does the working draft carry unpublished changes? Materialized at
|
|
153
|
+
* the write chokepoint (the amber "changed" list dot): a draft save while
|
|
154
|
+
* published re-computes it in the upsert mutation via ONE semantic compare
|
|
155
|
+
* against the pinned published snapshot; publish writes `false`; unpublish
|
|
156
|
+
* clears it. NOT derived from `updatedAt > publishedAt` (set-once EC-447
|
|
157
|
+
* semantics would false-positive on every republish). Optional (additive):
|
|
158
|
+
* existing rows stay valid; a one-time backfill
|
|
159
|
+
* (`cms:backfillHasUnpublishedChangesColumn`) materializes it for
|
|
160
|
+
* pre-EC-449 published docs.
|
|
161
|
+
*/
|
|
162
|
+
hasUnpublishedChanges: v.optional(v.boolean()),
|
|
142
163
|
createdAt: v.string(),
|
|
143
164
|
updatedAt: v.string(),
|
|
144
165
|
})
|
|
@@ -3,7 +3,7 @@ import { useEffect } from 'react';
|
|
|
3
3
|
import { Blocks, Database, FileCog, Image, LayoutGrid, Puzzle, Settings as SettingsIcon, Signpost, Users, } from 'lucide-react';
|
|
4
4
|
import { assetCollection, extendCollectionDef, isSingleton, resolveSingletonDocument, sampleDocuments, } from '@elytracms/core/cms-core';
|
|
5
5
|
import { reconcileSeedDocumentLocalization } from '@elytracms/core/persistence';
|
|
6
|
-
import {
|
|
6
|
+
import { NavigateOnce, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router';
|
|
7
7
|
import { Text } from '@elytracms/studio/ui';
|
|
8
8
|
import { localizedLabel, translate } from '@elytracms/studio/lib/i18n';
|
|
9
9
|
import { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session';
|
|
@@ -178,7 +178,7 @@ export function assetsView(config = {}) {
|
|
|
178
178
|
icon: config.icon ?? Image,
|
|
179
179
|
availabilitySection: 'assets',
|
|
180
180
|
parsePath: assetsDeepLink,
|
|
181
|
-
render: (ctx) => (_jsx(SectionSurface, { project: ctx.project, section: "assets", forcedState: ctx.search.state, children: ctx.deepLink.assetId === null && ctx.search.asset ? (_jsx(
|
|
181
|
+
render: (ctx) => (_jsx(SectionSurface, { project: ctx.project, section: "assets", forcedState: ctx.search.state, children: ctx.deepLink.assetId === null && ctx.search.asset ? (_jsx(NavigateOnce, { to: STUDIO_PROJECT_ROUTE, params: { projectId: ctx.projectId, _splat: studioPath.asset(ctx.search.asset) }, replace: true })) : (_jsx(CmsSessionGate, { children: (cms) => {
|
|
182
182
|
const kit = getInstantiatedKit(ctx.projectId);
|
|
183
183
|
const schema = resolveAssetsSchema(resolveCmsInitialCollections(cms, kit), config);
|
|
184
184
|
return (_jsx(AssetsModule, { projectId: ctx.projectId, assetId: ctx.deepLink.assetId ?? null, schema: schema }));
|
|
@@ -247,7 +247,7 @@ export function builderView() {
|
|
|
247
247
|
description: 'Legacy builder route — redirects to the page document editor.',
|
|
248
248
|
icon: Blocks,
|
|
249
249
|
hidden: true,
|
|
250
|
-
render: (ctx) => (_jsx(
|
|
250
|
+
render: (ctx) => (_jsx(NavigateOnce, { to: STUDIO_PROJECT_ROUTE, params: {
|
|
251
251
|
projectId: ctx.projectId,
|
|
252
252
|
_splat: ctx.search.page
|
|
253
253
|
? studioPath.cmsDocument('page', ctx.search.page)
|
|
@@ -453,7 +453,7 @@ function CmsScreen({ project, collectionId, documentId, state, field, focusColle
|
|
|
453
453
|
// EC-424: resolved once so `initialDocuments`' reconciliation (below)
|
|
454
454
|
// stays in lockstep with the SAME collections `initialCollections` uses.
|
|
455
455
|
const initialCollections = resolveCmsInitialCollections(cms, kit);
|
|
456
|
-
return (_jsx(CmsProvider, { projectId: projectId, initialCollections: initialCollections, initialDocuments: resolveCmsInitialDocuments(cms, kit, initialCollections), initialPublishedKeys: cms?.publishedKeys, initialTimestamps: cms?.timestamps, initialLocaleConfig: resolveCmsInitialLocaleConfig(cms, kit, project), children: _jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx(CmsPersistErrorBanner, {}), _jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: [_jsx("div", { className: "min-w-0 flex-1 overflow-hidden", children: _jsx(CmsContent, { projectId: projectId, collectionId: collectionId, documentId: documentId, field: field, focusCollections: focusCollections }) }), _jsx(AiCmsPanel, {})] })] }) }, projectId));
|
|
456
|
+
return (_jsx(CmsProvider, { projectId: projectId, initialCollections: initialCollections, initialDocuments: resolveCmsInitialDocuments(cms, kit, initialCollections), initialPublishedKeys: cms?.publishedKeys, initialTimestamps: cms?.timestamps, initialPublishTimestamps: cms?.publishTimestamps, initialChangedKeys: cms?.changedKeys, initialLocaleConfig: resolveCmsInitialLocaleConfig(cms, kit, project), children: _jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx(CmsPersistErrorBanner, {}), _jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: [_jsx("div", { className: "min-w-0 flex-1 overflow-hidden", children: _jsx(CmsContent, { projectId: projectId, collectionId: collectionId, documentId: documentId, field: field, focusCollections: focusCollections }) }), _jsx(AiCmsPanel, {})] })] }) }, projectId));
|
|
457
457
|
} }));
|
|
458
458
|
}
|
|
459
459
|
/** Resolves the active CMS screen inside the provider (needs the live store). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-modules.js","sourceRoot":"","sources":["../../../src/components/app-views/default-modules.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect } from 'react'\nimport type { ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport {\n Blocks,\n Database,\n FileCog,\n Image,\n LayoutGrid,\n Puzzle,\n Settings as SettingsIcon,\n Signpost,\n Users,\n} from 'lucide-react'\nimport {\n assetCollection,\n extendCollectionDef,\n isSingleton,\n resolveSingletonDocument,\n sampleDocuments,\n} from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n CollectionFieldOverride,\n CollectionFormMeta,\n LocaleConfig,\n} from '@elytracms/core/cms-core'\nimport { reconcileSeedDocumentLocalization } from '@elytracms/core/persistence'\nimport type { Project, SectionId, SectionState } from '@elytracms/core/studio-core'\nimport { Navigate, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport { Text } from '@elytracms/studio/ui'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session'\nimport type { InstantiatedKit } from '@elytracms/core/starter-kits'\nimport { isPlaygroundMode } from '@elytracms/studio/lib/persistence'\nimport { resolveDeploymentCollections, resolveDeploymentLocaleConfig } from '@elytracms/studio/lib/workspace'\nimport type { HydratedCms } from '@elytracms/studio/lib/operations'\nimport {\n cmsDeepLink,\n componentsDeepLink,\n usersDeepLink,\n redirectsDeepLink,\n assetsDeepLink,\n groupNode,\n separatorNode,\n viewNode,\n type AppViewContext,\n type AppViewDeepLink,\n type AppViewModule,\n type NavPermission,\n type SidebarViewNode,\n} from './module'\nimport { SectionSurface } from '../section-surface'\nimport { SectionStateView } from '../section-state'\nimport { SettingsForm } from '../settings-form'\nimport { AssetsModule } from '../assets/assets-module'\nimport { UsersModule } from '../users/users-module'\nimport { RedirectsModule } from '../redirects/redirects-module'\nimport { redirectsCollectionDef } from '../redirects/redirects-schema'\nimport { ExtensionsView } from '../extensions/extensions-view'\nimport { RegistryBrowser } from '../components/registry-browser'\nimport { ComponentsIndex } from '../components/components-index'\nimport { ComponentDetail } from '../components/component-detail'\nimport { CmsSessionGate } from '../project-session'\nimport { CmsProvider, useCms } from '../cms/cms-store'\nimport { AiCmsPanel } from '../ai/ai-cms-panel'\nimport { focusCollectionList } from '../cms/collection-focus'\nimport { CmsIndex } from '../cms/cms-index'\nimport { CollectionTable } from '../cms/collection-table'\nimport { DetailEditor } from '../cms/detail-editor'\nimport { OperationErrorBanner } from '../operation-error'\n\n/**\n * EC-237/239 — the studio's BUILT-IN views + the consumer-facing `nav` namespace,\n * an instance of the EC-236 customization seam.\n *\n * Each built-in surface (the former `StudioSectionResolver` case body) is promoted\n * to a self-contained {@link AppViewModule} factory. The `nav` namespace is the\n * single, typed builder a consuming repo's `studio.config.tsx` uses to declare its\n * section rail — `nav.cms(...)`, `nav.group(...)`, `nav.view(...)` — passing the\n * view objects (and a focus config) DIRECTLY, never a string id. The CMS surface\n * is a factory ({@link cmsView}) so a focused view (\"Catalog\", \"Categories\") is the\n * SAME surface instantiated with config, not a copy — the EC-239 reuse proof.\n */\n\n// --- The CMS surface, reusable via a config factory (the EC-239 reuse seam) ---\n\n/** Config for a CMS-backed view — `collections`/`collection` focus the lens (EC-239). */\nexport interface CmsViewConfig {\n /** Explicit stable id (the route segment). Omitted → derived from the focus. */\n id?: string\n label?: string\n description?: string\n icon?: LucideIcon\n /**\n * Scope the rail auto-expansion (EC-293) to these collection ids, in order. The\n * parent expands into one focused-lens child per id; the parent index shows the\n * same subset. Omitted → every collection. Ignored for a single-collection lens.\n */\n collections?: readonly string[]\n /** Single-collection lens: a LEAF rail item that opens straight into this collection. */\n collection?: string\n /**\n * EC-293 escape hatch: explicit rail children for this CMS parent, overriding the\n * default per-collection auto-expansion. Ignored for a single-collection lens.\n */\n children?: SidebarViewNode[]\n /** The availability section this view rides on (EC-221); defaults to `cms`. */\n availabilitySection?: SectionId\n /** OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18). See {@link BuiltinViewConfig.visible}. */\n visible?: NavPermission\n}\n\n/** Common label/icon overrides for a built-in view (`nav.redirects({ label: 'URLs' })`). */\nexport interface BuiltinViewConfig {\n label?: string\n icon?: LucideIcon\n /**\n * OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18): hide this\n * rail item for principals it returns `false` for; absent → always visible. The\n * policy (which roles) lives in the consuming project, never the engine. E.g.\n * `nav.users({ visible: ({ role }) => role === 'admin' })`.\n */\n visible?: NavPermission\n}\n\n/**\n * Config for the **redirects** rail (EC-311) — a built-in view whose engine-owned\n * schema is customizable. Beyond the shared label/icon/visible, a mount may rename\n * the rail (`form`) and **override built-in fields by equal name** (`fields`):\n * naming, permissions (`form.readOnly`), rendering (`form.control` → `fieldRenderers`)\n * and validation. The canonical `from`/`to`/`permanent`/`enabled` names+types are\n * contract-locked (a type change on one is a loud config error), but a *net-new*\n * field is allowed (EC-311 Tier 2): it's appended and persists in the redirect\n * record's `meta` bag — editorial only, so it never reaches delivery/routing.\n */\nexport interface RedirectsViewConfig extends BuiltinViewConfig {\n /** Rename the rail (e.g. `{ label: 'URLs' }`). */\n form?: Partial<CollectionFormMeta>\n /** Override built-in redirect fields, matched by `name`. */\n fields?: readonly CollectionFieldOverride[]\n}\n\n/**\n * Config for the **assets** rail (EC-319), customizable by the SAME EC-311\n * mechanism as redirects (`form`/`fields`) — but the BASE schema it overrides is\n * different in kind: redirects' base is an engine-owned, fixed `CollectionDef`\n * known at config-build time; the assets base is the DEPLOYMENT's own\n * `kind:'asset'` collection (a project's `cms/collections/asset.ts`), only\n * knowable once the deployment resolves (hydrated session → kit → playground\n * config — see {@link resolveAssetsSchema}). So the merge runs at RENDER time\n * (inside `assetsView`'s `CmsSessionGate`), not at `nav.assets(config)` call\n * time: a locked-violation override (e.g. changing a declared field's type)\n * surfaces the first time the Assets section renders, not at `defineStudio`\n * mount — the one place this rail's customization contract differs from\n * redirects', a direct consequence of its base schema being project-declared.\n */\nexport interface AssetsViewConfig extends BuiltinViewConfig {\n /** Rename the rail (e.g. `{ label: 'Media' }`). */\n form?: Partial<CollectionFormMeta>\n /** Override the project's declared asset fields, matched by `name`. */\n fields?: readonly CollectionFieldOverride[]\n}\n\n/** Config for a fully custom view (`nav.view({...})`) — the net-new surface escape hatch. */\nexport interface CustomViewConfig {\n /** Explicit stable id (the route segment). Omitted → a slug of the label. */\n id?: string\n label: string\n description?: string\n icon?: LucideIcon\n render: (ctx: AppViewContext) => ReactNode\n parsePath?: (segments: string[]) => AppViewDeepLink | null\n availabilitySection?: SectionId\n /** OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18). See {@link BuiltinViewConfig.visible}. */\n visible?: NavPermission\n}\n\nfunction slugify(label: string): string {\n return label.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'view'\n}\n\n/** Derive a stable, deterministic id from the CMS focus (overridable via `config.id`). */\nfunction cmsViewId(config: CmsViewConfig): string {\n if (config.id) return config.id\n if (config.collection) return `cms:${config.collection}`\n if (config.collections && config.collections.length > 0) {\n return `cms:${[...config.collections].join('-')}`\n }\n return 'cms'\n}\n\n/**\n * Build a CMS view — the default `cms` view, or a focused instance. Every instance\n * shares one surface ({@link CmsScreen}); `collections` narrows the sidebar + index\n * to a subset, `collection` is a single-collection lens that opens straight into\n * that collection's list. The id is derived from the focus (deterministic, stable\n * across rail reorders) unless `id` is given — so two focused views never collide.\n */\nexport function cmsView(config: CmsViewConfig = {}): AppViewModule {\n const single = config.collection\n const focus = single ? [single] : config.collections\n return {\n id: cmsViewId(config),\n // Lazy label/description (EC-241): the sidebar is built when the consumer's\n // `studio.config` module loads — BEFORE `createStudio` seeds the i18n config — so\n // resolving here would bake English. A getter defers resolution to render, when\n // the config is present and the browser locale is known.\n get label() {\n return config.label ?? translate('nav.cms.label')\n },\n get description() {\n return config.description ?? translate('nav.cms.description')\n },\n icon: config.icon ?? Database,\n availabilitySection: config.availabilitySection ?? 'cms',\n // A single-collection lens routes `[]`→that collection, `[d]`→its document;\n // an unfocused / multi-collection view uses the full index/collection/document arity.\n parsePath: single\n ? (segments) =>\n segments.length === 0\n ? { collectionId: single, documentId: null }\n : segments.length === 1\n ? { collectionId: single, documentId: segments[0]! }\n : null\n : cmsDeepLink,\n render: (ctx) => (\n <CmsScreen\n project={ctx.project}\n collectionId={ctx.deepLink.collectionId ?? null}\n documentId={ctx.deepLink.documentId ?? null}\n state={ctx.search.state}\n field={ctx.search.field}\n focusCollections={focus}\n />\n ),\n }\n}\n\n/**\n * A focused-lens rail child for one collection (EC-293) — the SAME `cms:<id>` view a\n * single-collection `nav.cms({ collection })` produces, carrying the collection's own\n * label + a singleton-aware icon (the former `CollectionsSidebar` row, lifted into\n * the rail). CMS auto-expansion maps the project's collections through this.\n */\nfunction cmsCollectionLeaf(collection: CollectionDef): SidebarViewNode {\n const singleton = isSingleton(collection)\n return viewNode(\n cmsView({\n collection: collection.id,\n // Non-hook `localizedLabel` resolves the (possibly `{ en, de }`) collection label\n // against the active UI locale, so a German editor sees German collection names.\n label: localizedLabel(collection.form?.label, collection.id),\n icon: singleton ? FileCog : collection.kind === 'asset' ? Image : Database,\n }),\n )\n}\n\n/** The components registry browser view. */\nexport function componentsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'components',\n get label() {\n return config.label ?? translate('nav.components.label')\n },\n get description() {\n return translate('nav.components.description')\n },\n icon: config.icon ?? Blocks,\n availabilitySection: 'components',\n parsePath: componentsDeepLink,\n render: (ctx) => (\n <ComponentsScreen\n projectId={ctx.projectId}\n componentId={ctx.deepLink.componentId ?? null}\n state={ctx.search.state}\n />\n ),\n }\n}\n\n/**\n * The Redirects view (EC-303) — editorial redirects as DATA, an editor-owned CRUD\n * surface (create/edit/delete) over the portable list/edit surfaces. Routes are no\n * longer shown in the admin (they're code; native `next.config`/`vercel` own static\n * forwards, evaluated before these). Rides on the CMS availability (a content/\n * routing concern). `[]` is the list; `[redirectId]` opens that redirect's detail.\n */\nexport function redirectsView(config: RedirectsViewConfig = {}): AppViewModule {\n // EC-311: merge any field/label overrides over the engine-owned def ONCE, at config\n // build (config-as-code — a locked violation throws here, at `defineStudio` mount).\n // Tier 2: `additiveFields: 'allow'` — a net-new field is appended and persists in the\n // redirect record's `meta` bag (the canonical from/to/permanent/enabled stay locked).\n const schema = extendCollectionDef(redirectsCollectionDef, config, { additiveFields: 'allow' })\n return {\n id: 'redirects',\n get label() {\n // Prefer an explicit rail label, then a consumer-*customized* collection label,\n // else the localized catalog nav label. Never let the built-in English schema\n // label ('Redirects') preempt translation — compare against the built-in default.\n const customized = localizedLabel(schema.form?.label)\n const builtin = localizedLabel(redirectsCollectionDef.form?.label)\n return (\n config.label ??\n (customized && customized !== builtin ? customized : translate('nav.redirects.label'))\n )\n },\n get description() {\n return translate('nav.redirects.description')\n },\n icon: config.icon ?? Signpost,\n availabilitySection: 'cms',\n parsePath: redirectsDeepLink,\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title={translate('nav.redirects.label')} />\n ) : (\n <RedirectsModule\n projectId={ctx.projectId}\n redirectId={ctx.deepLink.redirectId ?? null}\n schema={schema}\n />\n ),\n }\n}\n\n/**\n * Resolve the assets rail's EFFECTIVE schema (EC-319/EC-311): the deployment's\n * OWN `kind:'asset'` collection — hydrated session → kit → playground config,\n * the exact same precedence {@link resolveCmsInitialCollections} gives every\n * other CMS collection — falling back to the engine's `assetCollection` fixture\n * only when nothing resolves (mirroring `CmsProvider`'s own fallback). A mount's\n * `nav.assets({ form, fields })` then merges over THAT base (EC-311, additive\n * fields allowed — a project's asset record already carries an open `meta` bag,\n * EC-301). Pure — no React — so it's unit-testable without `CmsSessionGate`.\n */\nexport function resolveAssetsSchema(\n collections: readonly CollectionDef[] | undefined,\n config: AssetsViewConfig,\n): CollectionDef {\n const base = collections?.find((c) => c.kind === 'asset') ?? assetCollection\n return extendCollectionDef(base, config, { additiveFields: 'allow' })\n}\n\n/**\n * The Assets view (EC-319) — the universal list (+ grid/list toggle) and a\n * bespoke per-asset detail surface, over the asset library store. `[]` is the\n * list; `[assetId]` opens that asset's detail (routed via `studioPath.asset`).\n * A `?asset=` deep link (EC-172 \"used by\" links) redirects straight to the\n * asset's detail — the surface those links used to scroll a card into and\n * auto-open, now a first-class route.\n */\nexport function assetsView(config: AssetsViewConfig = {}): AppViewModule {\n return {\n id: 'assets',\n get label() {\n return config.label ?? translate('nav.assets.label')\n },\n get description() {\n return translate('nav.assets.description')\n },\n icon: config.icon ?? Image,\n availabilitySection: 'assets',\n parsePath: assetsDeepLink,\n render: (ctx) => (\n <SectionSurface project={ctx.project} section=\"assets\" forcedState={ctx.search.state}>\n {ctx.deepLink.assetId === null && ctx.search.asset ? (\n <Navigate\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: ctx.projectId, _splat: studioPath.asset(ctx.search.asset) }}\n replace\n />\n ) : (\n // The AssetsProvider is mounted at the project level (EC-195).\n <CmsSessionGate>\n {(cms) => {\n const kit = getInstantiatedKit(ctx.projectId)\n const schema = resolveAssetsSchema(resolveCmsInitialCollections(cms, kit), config)\n return (\n <AssetsModule\n projectId={ctx.projectId}\n assetId={ctx.deepLink.assetId ?? null}\n schema={schema}\n />\n )\n }}\n </CmsSessionGate>\n )}\n </SectionSurface>\n ),\n }\n}\n\n/**\n * The studio Users view (EC-240) — an admin-only roster of who has access to this\n * deployment and their role. A first-class section (its own rail entry), modeled\n * like the Assets library; the surface itself gates to admins and renders a\n * \"restricted\" state otherwise. Rides on the `settings` availability (an\n * admin/config concern, so it appears/hides with a project's settings surface).\n */\nexport function usersView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'users',\n get label() {\n return config.label ?? translate('nav.users.label')\n },\n get description() {\n return translate('nav.users.description')\n },\n icon: config.icon ?? Users,\n availabilitySection: 'users',\n // EC-240: `[]` is the roster; `[userId]` opens that member in the portable edit\n // shell (AD-19 reuse — the same `DetailEditor` body over the users source).\n parsePath: usersDeepLink,\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title={translate('nav.users.label')} />\n ) : (\n <UsersModule projectId={ctx.projectId} userId={ctx.deepLink.userId ?? null} />\n ),\n }\n}\n\n/** The extensions marketplace view. */\nexport function extensionsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'extensions',\n get label() {\n return config.label ?? translate('nav.extensions.label')\n },\n get description() {\n return translate('nav.extensions.description')\n },\n icon: config.icon ?? Puzzle,\n availabilitySection: 'extensions',\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title={translate('nav.extensions.label')} />\n ) : (\n <ExtensionsView />\n ),\n }\n}\n\n/** The project settings view. */\nexport function settingsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'settings',\n get label() {\n return config.label ?? translate('nav.settings.label')\n },\n get description() {\n return translate('nav.settings.description')\n },\n icon: config.icon ?? SettingsIcon,\n availabilitySection: 'settings',\n render: (ctx) => (\n <SectionSurface project={ctx.project} section=\"settings\" forcedState={ctx.search.state}>\n <SettingsForm project={ctx.project} />\n </SectionSurface>\n ),\n }\n}\n\n/** The legacy `builder` redirect — a hidden, routable view (so `/builder?page=` still lands). */\nexport function builderView(): AppViewModule {\n return {\n id: 'builder',\n label: 'Builder',\n description: 'Legacy builder route — redirects to the page document editor.',\n icon: Blocks,\n hidden: true,\n render: (ctx) => (\n // Retired surface (EC-187 slice 6): a page is a document in the `page`\n // collection. `?page=<id>` lands on that document; else the page list.\n <Navigate\n to={STUDIO_PROJECT_ROUTE}\n params={{\n projectId: ctx.projectId,\n _splat: ctx.search.page\n ? studioPath.cmsDocument('page', ctx.search.page)\n : studioPath.cmsCollection('page'),\n }}\n replace\n />\n ),\n }\n}\n\n/** Build a fully custom view (the `nav.view(...)` escape hatch). */\nexport function customView(config: CustomViewConfig): AppViewModule {\n return {\n id: config.id ?? slugify(config.label),\n label: config.label,\n description: config.description ?? config.label,\n icon: config.icon ?? LayoutGrid,\n render: config.render,\n ...(config.parsePath ? { parsePath: config.parsePath } : {}),\n ...(config.availabilitySection ? { availabilitySection: config.availabilitySection } : {}),\n }\n}\n\n/**\n * The studio's built-in views in canonical rail order (components · CMS · routes ·\n * assets · extensions · settings), plus the hidden `builder` redirect (routable but\n * unlisted). The default rail (no `defineStudio({ sidebar })`) is these as plain\n * entries; the router registry always includes them so deep-links resolve even\n * when a consumer's rail omits one.\n */\nexport const DEFAULT_VIEWS: readonly AppViewModule[] = [\n componentsView(),\n cmsView(),\n redirectsView(),\n assetsView(),\n extensionsView(),\n usersView(),\n settingsView(),\n builderView(),\n]\n\n/**\n * The `nav` namespace — the single typed builder for `studio.config.tsx`'s section\n * rail. Each call returns a {@link SidebarViewNode} (or a group / separator) that\n * carries its view DIRECTLY: `nav.cms({ collection })` is a focused CMS view,\n * `nav.view({ render })` a net-new surface, `nav.group(label, [...])` a header with\n * sub-views, `nav.separator()` a divider. No string ids, no `moduleId` — the rail\n * is the views.\n */\n/**\n * Forward the OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18)\n * from a `nav.*` config onto the built view, so gating is settable on EVERY nav\n * item — not just Users. Absent → the view is unchanged (always visible).\n */\nfunction withNavPermission(view: AppViewModule, config: { visible?: NavPermission }): AppViewModule {\n if (!config.visible) return view\n // Copy property DESCRIPTORS (not a spread) so a built-in view's lazy `label`/\n // `description` getters (EC-241) survive — a spread would invoke them at build time,\n // baking English before `createStudio` seeds the i18n config.\n return Object.defineProperties({} as AppViewModule, {\n ...Object.getOwnPropertyDescriptors(view),\n visible: { value: config.visible, enumerable: true, configurable: true, writable: true },\n })\n}\n\nexport const nav = {\n /**\n * The CMS view. `nav.cms()` / `nav.cms({ collections })` is a PARENT that nests its\n * collections in the rail (each a focused `cms:<id>` lens); `nav.cms({ collection })`\n * is a single-collection LEAF; `nav.cms({ children })` supplies explicit children.\n */\n cms: (config: CmsViewConfig = {}): SidebarViewNode => {\n const view = withNavPermission(cmsView(config), config)\n // A single-collection lens is a leaf — it opens that collection directly.\n if (config.collection) return viewNode(view)\n // Otherwise a parent that expands its collections as nested rail children (EC-293).\n const node: SidebarViewNode = { kind: 'view', view }\n if (config.children) node.children = config.children\n else\n node.expand = (collections) =>\n focusCollectionList(collections, config.collections).map(cmsCollectionLeaf)\n return node\n },\n /** The component registry browser. */\n components: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(componentsView(config), config)),\n /** The assets rail — the universal list/grid + detail (EC-319); built-in\n * fields customizable by name (EC-311), the same shape as `nav.redirects`. */\n assets: (config: AssetsViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(assetsView(config), config)),\n /** Editorial redirects (EC-303); built-in fields customizable by name (EC-311). */\n redirects: (config: RedirectsViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(redirectsView(config), config)),\n /** Project settings. */\n settings: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(settingsView(config), config)),\n /** The extensions marketplace. */\n extensions: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(extensionsView(config), config)),\n /** The admin users roster (EC-240). */\n users: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(usersView(config), config)),\n /** A fully custom view — pass your own `render` (and optional `parsePath`). */\n view: (config: CustomViewConfig): SidebarViewNode =>\n viewNode(withNavPermission(customView(config), config)),\n /** A labelled group of views (a header with sub-entries). */\n group: (label: string, children: SidebarViewNode[]) => groupNode(label, children),\n /** A divider between rail sections. */\n separator: () => separatorNode(),\n} as const\n\n// --- Surfaces (the view render bodies; CMS gains a focus lens) ---\n\n/**\n * Components section (EC-050): a registry-browser sidebar beside the active\n * screen — the index (no selection) or a component's detail. Forced states\n * (?state=) replace the whole surface.\n */\nfunction ComponentsScreen({\n projectId,\n componentId,\n state,\n}: {\n projectId: string\n componentId: string | null\n state?: SectionState\n}) {\n if (state && state !== 'working') {\n return <SectionStateView state={state} title={translate('nav.components.label')} />\n }\n return (\n <div className=\"flex h-full min-h-0\">\n <aside className=\"hidden h-full min-h-0 w-[300px] shrink-0 border-r border-border md:block\">\n <RegistryBrowser projectId={projectId} activeComponentId={componentId ?? undefined} />\n </aside>\n <div className=\"min-h-0 flex-1 overflow-auto\">\n {componentId === null ? (\n <ComponentsIndex projectId={projectId} />\n ) : (\n <ComponentDetail projectId={projectId} componentId={componentId} />\n )}\n </div>\n </div>\n )\n}\n\n/**\n * The CMS schema source for a project mount (bug fix, playground-collections):\n * a hydrated session wins (Convex), then a kit's imported schema (EC-119), then —\n * in playground mode only — the active project's config-declared collections\n * (AD-11, `resolveDeploymentCollections`). Without this last fallback, a non-kit\n * playground project has no hydrated session (`null`, by design — see\n * `project-session.tsx`) and no kit, so `CmsProvider` fell through to the core's\n * hardcoded `sampleFilterableCollections` fixture instead of the project's own\n * on-disk schema. Convex mode's first-run `null` is untouched — it still seeds\n * from the fixture there, as before. `playground` defaults to the live\n * {@link isPlaygroundMode} but is overridable so this stays unit testable\n * without stubbing the env (same seam as `resolveBackendConfig`).\n */\nexport function resolveCmsInitialCollections(\n cms: HydratedCms | null,\n kit: InstantiatedKit | undefined,\n playground: boolean = isPlaygroundMode(),\n): CollectionDef[] | undefined {\n return cms?.collections ?? kit?.collections ?? (playground ? resolveDeploymentCollections() : undefined)\n}\n\n/**\n * The CMS documents source for a project mount (EC-424) — the document-side\n * twin of {@link resolveCmsInitialCollections}, same ladder: a hydrated session\n * wins (Convex), then a kit's imported documents, then — in playground mode\n * only, and only when a config project actually resolved collections — the\n * engine's own `sampleDocuments` fixture RECONCILED against them.\n *\n * Without this, a config project whose OWN schema diverges from the shared\n * fixture's localization (e.g. this repo's demo declares `post.slug` localized,\n * EC-315, while the engine's `postCollection` fixture deliberately does not)\n * left `CmsProvider` falling straight through to the un-reconciled\n * `sampleDocuments` default — the CMS surfaces' `initialCollections` already\n * reflected the config schema (via the fallback above), but `initialDocuments`\n * did not, so every seeded post showed EC-390's shape-aware validation's\n * `plain-value` warning + `missing-localized-value` error, standing noise a\n * project never actually wrote.\n *\n * This is the STATIC-baseline twin of `reconcileSeedDocumentLocalization`\n * (`@elytracms/core/persistence`, wired into the seeded persistence adapter for\n * Convex-mode/delivery reads): no adapter round-trip, so the EC-138/EC-320\n * synchronous-first-render contract (`project-session.tsx`: \"CMS surfaces keep\n * their fixture baseline\") is untouched. `collections` defaults to the SAME\n * resolution `resolveCmsInitialCollections` returns, so the two stay in lockstep\n * by construction; only reconciles when that resolved to a non-empty config\n * schema (env-only playground, with nothing config-declared, is unaffected).\n */\nexport function resolveCmsInitialDocuments(\n cms: HydratedCms | null,\n kit: InstantiatedKit | undefined,\n collections: CollectionDef[] | undefined = resolveCmsInitialCollections(cms, kit),\n): CmsDocument[] | undefined {\n if (cms) return cms.documents\n if (kit) return kit.documents\n if (!collections || collections.length === 0) return undefined\n const collectionsById = new Map(collections.map((collection) => [collection.id, collection]))\n return sampleDocuments.map((doc) => {\n const collection = collectionsById.get(doc.collection)\n return collection ? reconcileSeedDocumentLocalization(doc, collection) : doc\n })\n}\n\n/**\n * The CMS locale config for a project mount (EC-357 finding #3). Locales are\n * structure and structure is code: when the active `elytra.config.ts` project\n * declares locales, they drive the ambient locale switcher in EVERY mode. The old\n * wiring only applied the project's locale settings on a HYDRATED session, so a\n * playground boot — or a connected FIRST RUN with nothing persisted yet — fell\n * back to a kit's locales or the core's sample `en/de` fixture and the switcher\n * showed `EN·/DE` against a `locales: ['de']` config. Without a config project\n * (env-only wiring) the previous ladder is unchanged: hydrated project settings,\n * then the kit's locales, then the sample fixture (via `CmsProvider`'s default).\n * `configLocales` is a parameter purely so the ladder is unit-testable (same seam\n * pattern as {@link resolveCmsInitialCollections}).\n */\nexport function resolveCmsInitialLocaleConfig(\n cms: HydratedCms | null,\n kit: InstantiatedKit | undefined,\n project: Project,\n configLocales: LocaleConfig | undefined = resolveDeploymentLocaleConfig(),\n): LocaleConfig | undefined {\n if (configLocales) return configLocales\n if (cms) return { default: project.settings.defaultLocale, locales: [...project.settings.locales] }\n return kit?.localeConfig\n}\n\n/**\n * CMS section: hydrates the persisted (or kit-seeded) collections + documents, then\n * renders the active screen full-width (index, a collection table, or a document\n * editor). EC-293: the collection list lives in the section rail (nested under the\n * CMS view), so there is no in-surface collections sidebar. Forced states (?state=)\n * replace the whole surface. `focusCollections` (EC-239) narrows the index to a\n * subset, so a focused view (\"Catalog\") reuses this surface with config.\n */\nfunction CmsScreen({\n project,\n collectionId,\n documentId,\n state,\n field,\n focusCollections,\n}: {\n project: Project\n collectionId: string | null\n documentId: string | null\n state?: SectionState\n field?: string\n focusCollections?: readonly string[]\n}) {\n const projectId = project.id\n if (state && state !== 'working') {\n return <SectionStateView state={state} title={translate('nav.cms.label')} />\n }\n\n // A project created from a starter kit (EC-119) opens the CMS on the kit's schemas + content.\n const kit = getInstantiatedKit(projectId)\n\n return (\n // EC-177 gap 1: in Convex mode every CMS mount loads the CURRENT persisted\n // schema + documents (fixtures only when nothing is persisted — first run).\n // Playground renders synchronously with `null` → fixture seeds, as before.\n <CmsSessionGate>\n {(cms) => {\n // EC-424: resolved once so `initialDocuments`' reconciliation (below)\n // stays in lockstep with the SAME collections `initialCollections` uses.\n const initialCollections = resolveCmsInitialCollections(cms, kit)\n return (\n <CmsProvider\n key={projectId}\n projectId={projectId}\n initialCollections={initialCollections}\n initialDocuments={resolveCmsInitialDocuments(cms, kit, initialCollections)}\n initialPublishedKeys={cms?.publishedKeys}\n initialTimestamps={cms?.timestamps}\n initialLocaleConfig={resolveCmsInitialLocaleConfig(cms, kit, project)}\n >\n <div className=\"flex h-full min-h-0 flex-col\">\n <CmsPersistErrorBanner />\n {/* EC-293: the collection list now lives in the section rail (nested\n under the CMS view), so the surface is full-width — the old 240px\n CollectionsSidebar aside is gone. EC-199: the AI assistant mounts as\n a right-hand sibling (both list + editor), so the content is a row. */}\n <div className=\"flex min-h-0 flex-1 overflow-hidden\">\n <div className=\"min-w-0 flex-1 overflow-hidden\">\n <CmsContent\n projectId={projectId}\n collectionId={collectionId}\n documentId={documentId}\n field={field}\n focusCollections={focusCollections}\n />\n </div>\n <AiCmsPanel />\n </div>\n </div>\n </CmsProvider>\n )\n }}\n </CmsSessionGate>\n )\n}\n\n/** Resolves the active CMS screen inside the provider (needs the live store). */\nfunction CmsContent({\n projectId,\n collectionId,\n documentId,\n field,\n focusCollections,\n}: {\n projectId: string\n collectionId: string | null\n documentId: string | null\n field?: string\n focusCollections?: readonly string[]\n}) {\n const cms = useCms()\n\n if (collectionId === null) return <CmsIndex projectId={projectId} focusCollections={focusCollections} />\n\n if (documentId === null) {\n const collection = cms.getCollection(collectionId)\n // EC-274: a singleton is a UI lens — at the collection URL it opens its one\n // document directly *while in its expected shape*, but degrades to the list\n // (with a \"for review\" lane) on drift. SingletonGate decides per the data.\n if (collection && isSingleton(collection)) {\n return <SingletonGate projectId={projectId} collectionId={collectionId} />\n }\n return <CollectionTable projectId={projectId} collectionId={collectionId} />\n }\n\n return (\n <DetailEditor\n projectId={projectId}\n collectionId={collectionId}\n documentId={documentId}\n focusPath={field}\n />\n )\n}\n\n/**\n * EC-274 — the singleton lens gate. At a singleton collection's URL, resolve its\n * lone document and open it directly; on drift (> 1 document) fall back to the\n * list with the \"for review\" lane so an editor can resolve the conflict.\n */\nfunction SingletonGate({ projectId, collectionId }: { projectId: string; collectionId: string }) {\n const cms = useCms()\n const collection = cms.getCollection(collectionId)\n const docs = cms.documentsOf(collectionId)\n const resolution = collection\n ? resolveSingletonDocument(collection, docs)\n : ({ kind: 'empty' } as const)\n\n // Materialize the lone document on first visit (an empty draft, persisted like\n // any create). Idempotent in the store; only fires while genuinely empty.\n useEffect(() => {\n if (resolution.kind === 'empty') cms.ensureSingletonDocument(collectionId)\n }, [resolution.kind, cms, collectionId])\n\n if (resolution.kind === 'ready') {\n return (\n <DetailEditor projectId={projectId} collectionId={collectionId} documentId={resolution.doc.id} />\n )\n }\n if (resolution.kind === 'drift') {\n return <CollectionTable projectId={projectId} collectionId={collectionId} />\n }\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Text size=\"sm\" tone=\"muted\">\n {translate('appViews.singleton.opening', {\n label: localizedLabel(collection?.form?.label, collectionId),\n })}\n </Text>\n </div>\n )\n}\n\n/** Failed/denied persisted CMS mutations are always visible (EC-141). */\nfunction CmsPersistErrorBanner() {\n const cms = useCms()\n if (!cms.persistError) return null\n return (\n <div className=\"shrink-0 border-b border-border p-2\">\n <OperationErrorBanner error={cms.persistError} onDismiss={cms.clearPersistError} />\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"default-modules.js","sourceRoot":"","sources":["../../../src/components/app-views/default-modules.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect } from 'react'\nimport type { ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport {\n Blocks,\n Database,\n FileCog,\n Image,\n LayoutGrid,\n Puzzle,\n Settings as SettingsIcon,\n Signpost,\n Users,\n} from 'lucide-react'\nimport {\n assetCollection,\n extendCollectionDef,\n isSingleton,\n resolveSingletonDocument,\n sampleDocuments,\n} from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n CollectionFieldOverride,\n CollectionFormMeta,\n LocaleConfig,\n} from '@elytracms/core/cms-core'\nimport { reconcileSeedDocumentLocalization } from '@elytracms/core/persistence'\nimport type { Project, SectionId, SectionState } from '@elytracms/core/studio-core'\nimport { NavigateOnce, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport { Text } from '@elytracms/studio/ui'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session'\nimport type { InstantiatedKit } from '@elytracms/core/starter-kits'\nimport { isPlaygroundMode } from '@elytracms/studio/lib/persistence'\nimport { resolveDeploymentCollections, resolveDeploymentLocaleConfig } from '@elytracms/studio/lib/workspace'\nimport type { HydratedCms } from '@elytracms/studio/lib/operations'\nimport {\n cmsDeepLink,\n componentsDeepLink,\n usersDeepLink,\n redirectsDeepLink,\n assetsDeepLink,\n groupNode,\n separatorNode,\n viewNode,\n type AppViewContext,\n type AppViewDeepLink,\n type AppViewModule,\n type NavPermission,\n type SidebarViewNode,\n} from './module'\nimport { SectionSurface } from '../section-surface'\nimport { SectionStateView } from '../section-state'\nimport { SettingsForm } from '../settings-form'\nimport { AssetsModule } from '../assets/assets-module'\nimport { UsersModule } from '../users/users-module'\nimport { RedirectsModule } from '../redirects/redirects-module'\nimport { redirectsCollectionDef } from '../redirects/redirects-schema'\nimport { ExtensionsView } from '../extensions/extensions-view'\nimport { RegistryBrowser } from '../components/registry-browser'\nimport { ComponentsIndex } from '../components/components-index'\nimport { ComponentDetail } from '../components/component-detail'\nimport { CmsSessionGate } from '../project-session'\nimport { CmsProvider, useCms } from '../cms/cms-store'\nimport { AiCmsPanel } from '../ai/ai-cms-panel'\nimport { focusCollectionList } from '../cms/collection-focus'\nimport { CmsIndex } from '../cms/cms-index'\nimport { CollectionTable } from '../cms/collection-table'\nimport { DetailEditor } from '../cms/detail-editor'\nimport { OperationErrorBanner } from '../operation-error'\n\n/**\n * EC-237/239 — the studio's BUILT-IN views + the consumer-facing `nav` namespace,\n * an instance of the EC-236 customization seam.\n *\n * Each built-in surface (the former `StudioSectionResolver` case body) is promoted\n * to a self-contained {@link AppViewModule} factory. The `nav` namespace is the\n * single, typed builder a consuming repo's `studio.config.tsx` uses to declare its\n * section rail — `nav.cms(...)`, `nav.group(...)`, `nav.view(...)` — passing the\n * view objects (and a focus config) DIRECTLY, never a string id. The CMS surface\n * is a factory ({@link cmsView}) so a focused view (\"Catalog\", \"Categories\") is the\n * SAME surface instantiated with config, not a copy — the EC-239 reuse proof.\n */\n\n// --- The CMS surface, reusable via a config factory (the EC-239 reuse seam) ---\n\n/** Config for a CMS-backed view — `collections`/`collection` focus the lens (EC-239). */\nexport interface CmsViewConfig {\n /** Explicit stable id (the route segment). Omitted → derived from the focus. */\n id?: string\n label?: string\n description?: string\n icon?: LucideIcon\n /**\n * Scope the rail auto-expansion (EC-293) to these collection ids, in order. The\n * parent expands into one focused-lens child per id; the parent index shows the\n * same subset. Omitted → every collection. Ignored for a single-collection lens.\n */\n collections?: readonly string[]\n /** Single-collection lens: a LEAF rail item that opens straight into this collection. */\n collection?: string\n /**\n * EC-293 escape hatch: explicit rail children for this CMS parent, overriding the\n * default per-collection auto-expansion. Ignored for a single-collection lens.\n */\n children?: SidebarViewNode[]\n /** The availability section this view rides on (EC-221); defaults to `cms`. */\n availabilitySection?: SectionId\n /** OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18). See {@link BuiltinViewConfig.visible}. */\n visible?: NavPermission\n}\n\n/** Common label/icon overrides for a built-in view (`nav.redirects({ label: 'URLs' })`). */\nexport interface BuiltinViewConfig {\n label?: string\n icon?: LucideIcon\n /**\n * OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18): hide this\n * rail item for principals it returns `false` for; absent → always visible. The\n * policy (which roles) lives in the consuming project, never the engine. E.g.\n * `nav.users({ visible: ({ role }) => role === 'admin' })`.\n */\n visible?: NavPermission\n}\n\n/**\n * Config for the **redirects** rail (EC-311) — a built-in view whose engine-owned\n * schema is customizable. Beyond the shared label/icon/visible, a mount may rename\n * the rail (`form`) and **override built-in fields by equal name** (`fields`):\n * naming, permissions (`form.readOnly`), rendering (`form.control` → `fieldRenderers`)\n * and validation. The canonical `from`/`to`/`permanent`/`enabled` names+types are\n * contract-locked (a type change on one is a loud config error), but a *net-new*\n * field is allowed (EC-311 Tier 2): it's appended and persists in the redirect\n * record's `meta` bag — editorial only, so it never reaches delivery/routing.\n */\nexport interface RedirectsViewConfig extends BuiltinViewConfig {\n /** Rename the rail (e.g. `{ label: 'URLs' }`). */\n form?: Partial<CollectionFormMeta>\n /** Override built-in redirect fields, matched by `name`. */\n fields?: readonly CollectionFieldOverride[]\n}\n\n/**\n * Config for the **assets** rail (EC-319), customizable by the SAME EC-311\n * mechanism as redirects (`form`/`fields`) — but the BASE schema it overrides is\n * different in kind: redirects' base is an engine-owned, fixed `CollectionDef`\n * known at config-build time; the assets base is the DEPLOYMENT's own\n * `kind:'asset'` collection (a project's `cms/collections/asset.ts`), only\n * knowable once the deployment resolves (hydrated session → kit → playground\n * config — see {@link resolveAssetsSchema}). So the merge runs at RENDER time\n * (inside `assetsView`'s `CmsSessionGate`), not at `nav.assets(config)` call\n * time: a locked-violation override (e.g. changing a declared field's type)\n * surfaces the first time the Assets section renders, not at `defineStudio`\n * mount — the one place this rail's customization contract differs from\n * redirects', a direct consequence of its base schema being project-declared.\n */\nexport interface AssetsViewConfig extends BuiltinViewConfig {\n /** Rename the rail (e.g. `{ label: 'Media' }`). */\n form?: Partial<CollectionFormMeta>\n /** Override the project's declared asset fields, matched by `name`. */\n fields?: readonly CollectionFieldOverride[]\n}\n\n/** Config for a fully custom view (`nav.view({...})`) — the net-new surface escape hatch. */\nexport interface CustomViewConfig {\n /** Explicit stable id (the route segment). Omitted → a slug of the label. */\n id?: string\n label: string\n description?: string\n icon?: LucideIcon\n render: (ctx: AppViewContext) => ReactNode\n parsePath?: (segments: string[]) => AppViewDeepLink | null\n availabilitySection?: SectionId\n /** OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18). See {@link BuiltinViewConfig.visible}. */\n visible?: NavPermission\n}\n\nfunction slugify(label: string): string {\n return label.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'view'\n}\n\n/** Derive a stable, deterministic id from the CMS focus (overridable via `config.id`). */\nfunction cmsViewId(config: CmsViewConfig): string {\n if (config.id) return config.id\n if (config.collection) return `cms:${config.collection}`\n if (config.collections && config.collections.length > 0) {\n return `cms:${[...config.collections].join('-')}`\n }\n return 'cms'\n}\n\n/**\n * Build a CMS view — the default `cms` view, or a focused instance. Every instance\n * shares one surface ({@link CmsScreen}); `collections` narrows the sidebar + index\n * to a subset, `collection` is a single-collection lens that opens straight into\n * that collection's list. The id is derived from the focus (deterministic, stable\n * across rail reorders) unless `id` is given — so two focused views never collide.\n */\nexport function cmsView(config: CmsViewConfig = {}): AppViewModule {\n const single = config.collection\n const focus = single ? [single] : config.collections\n return {\n id: cmsViewId(config),\n // Lazy label/description (EC-241): the sidebar is built when the consumer's\n // `studio.config` module loads — BEFORE `createStudio` seeds the i18n config — so\n // resolving here would bake English. A getter defers resolution to render, when\n // the config is present and the browser locale is known.\n get label() {\n return config.label ?? translate('nav.cms.label')\n },\n get description() {\n return config.description ?? translate('nav.cms.description')\n },\n icon: config.icon ?? Database,\n availabilitySection: config.availabilitySection ?? 'cms',\n // A single-collection lens routes `[]`→that collection, `[d]`→its document;\n // an unfocused / multi-collection view uses the full index/collection/document arity.\n parsePath: single\n ? (segments) =>\n segments.length === 0\n ? { collectionId: single, documentId: null }\n : segments.length === 1\n ? { collectionId: single, documentId: segments[0]! }\n : null\n : cmsDeepLink,\n render: (ctx) => (\n <CmsScreen\n project={ctx.project}\n collectionId={ctx.deepLink.collectionId ?? null}\n documentId={ctx.deepLink.documentId ?? null}\n state={ctx.search.state}\n field={ctx.search.field}\n focusCollections={focus}\n />\n ),\n }\n}\n\n/**\n * A focused-lens rail child for one collection (EC-293) — the SAME `cms:<id>` view a\n * single-collection `nav.cms({ collection })` produces, carrying the collection's own\n * label + a singleton-aware icon (the former `CollectionsSidebar` row, lifted into\n * the rail). CMS auto-expansion maps the project's collections through this.\n */\nfunction cmsCollectionLeaf(collection: CollectionDef): SidebarViewNode {\n const singleton = isSingleton(collection)\n return viewNode(\n cmsView({\n collection: collection.id,\n // Non-hook `localizedLabel` resolves the (possibly `{ en, de }`) collection label\n // against the active UI locale, so a German editor sees German collection names.\n label: localizedLabel(collection.form?.label, collection.id),\n icon: singleton ? FileCog : collection.kind === 'asset' ? Image : Database,\n }),\n )\n}\n\n/** The components registry browser view. */\nexport function componentsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'components',\n get label() {\n return config.label ?? translate('nav.components.label')\n },\n get description() {\n return translate('nav.components.description')\n },\n icon: config.icon ?? Blocks,\n availabilitySection: 'components',\n parsePath: componentsDeepLink,\n render: (ctx) => (\n <ComponentsScreen\n projectId={ctx.projectId}\n componentId={ctx.deepLink.componentId ?? null}\n state={ctx.search.state}\n />\n ),\n }\n}\n\n/**\n * The Redirects view (EC-303) — editorial redirects as DATA, an editor-owned CRUD\n * surface (create/edit/delete) over the portable list/edit surfaces. Routes are no\n * longer shown in the admin (they're code; native `next.config`/`vercel` own static\n * forwards, evaluated before these). Rides on the CMS availability (a content/\n * routing concern). `[]` is the list; `[redirectId]` opens that redirect's detail.\n */\nexport function redirectsView(config: RedirectsViewConfig = {}): AppViewModule {\n // EC-311: merge any field/label overrides over the engine-owned def ONCE, at config\n // build (config-as-code — a locked violation throws here, at `defineStudio` mount).\n // Tier 2: `additiveFields: 'allow'` — a net-new field is appended and persists in the\n // redirect record's `meta` bag (the canonical from/to/permanent/enabled stay locked).\n const schema = extendCollectionDef(redirectsCollectionDef, config, { additiveFields: 'allow' })\n return {\n id: 'redirects',\n get label() {\n // Prefer an explicit rail label, then a consumer-*customized* collection label,\n // else the localized catalog nav label. Never let the built-in English schema\n // label ('Redirects') preempt translation — compare against the built-in default.\n const customized = localizedLabel(schema.form?.label)\n const builtin = localizedLabel(redirectsCollectionDef.form?.label)\n return (\n config.label ??\n (customized && customized !== builtin ? customized : translate('nav.redirects.label'))\n )\n },\n get description() {\n return translate('nav.redirects.description')\n },\n icon: config.icon ?? Signpost,\n availabilitySection: 'cms',\n parsePath: redirectsDeepLink,\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title={translate('nav.redirects.label')} />\n ) : (\n <RedirectsModule\n projectId={ctx.projectId}\n redirectId={ctx.deepLink.redirectId ?? null}\n schema={schema}\n />\n ),\n }\n}\n\n/**\n * Resolve the assets rail's EFFECTIVE schema (EC-319/EC-311): the deployment's\n * OWN `kind:'asset'` collection — hydrated session → kit → playground config,\n * the exact same precedence {@link resolveCmsInitialCollections} gives every\n * other CMS collection — falling back to the engine's `assetCollection` fixture\n * only when nothing resolves (mirroring `CmsProvider`'s own fallback). A mount's\n * `nav.assets({ form, fields })` then merges over THAT base (EC-311, additive\n * fields allowed — a project's asset record already carries an open `meta` bag,\n * EC-301). Pure — no React — so it's unit-testable without `CmsSessionGate`.\n */\nexport function resolveAssetsSchema(\n collections: readonly CollectionDef[] | undefined,\n config: AssetsViewConfig,\n): CollectionDef {\n const base = collections?.find((c) => c.kind === 'asset') ?? assetCollection\n return extendCollectionDef(base, config, { additiveFields: 'allow' })\n}\n\n/**\n * The Assets view (EC-319) — the universal list (+ grid/list toggle) and a\n * bespoke per-asset detail surface, over the asset library store. `[]` is the\n * list; `[assetId]` opens that asset's detail (routed via `studioPath.asset`).\n * A `?asset=` deep link (EC-172 \"used by\" links) redirects straight to the\n * asset's detail — the surface those links used to scroll a card into and\n * auto-open, now a first-class route.\n */\nexport function assetsView(config: AssetsViewConfig = {}): AppViewModule {\n return {\n id: 'assets',\n get label() {\n return config.label ?? translate('nav.assets.label')\n },\n get description() {\n return translate('nav.assets.description')\n },\n icon: config.icon ?? Image,\n availabilitySection: 'assets',\n parsePath: assetsDeepLink,\n render: (ctx) => (\n <SectionSurface project={ctx.project} section=\"assets\" forcedState={ctx.search.state}>\n {ctx.deepLink.assetId === null && ctx.search.asset ? (\n // EC-441: `NavigateOnce` — render-time redirects fire once per intent.\n <NavigateOnce\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: ctx.projectId, _splat: studioPath.asset(ctx.search.asset) }}\n replace\n />\n ) : (\n // The AssetsProvider is mounted at the project level (EC-195).\n <CmsSessionGate>\n {(cms) => {\n const kit = getInstantiatedKit(ctx.projectId)\n const schema = resolveAssetsSchema(resolveCmsInitialCollections(cms, kit), config)\n return (\n <AssetsModule\n projectId={ctx.projectId}\n assetId={ctx.deepLink.assetId ?? null}\n schema={schema}\n />\n )\n }}\n </CmsSessionGate>\n )}\n </SectionSurface>\n ),\n }\n}\n\n/**\n * The studio Users view (EC-240) — an admin-only roster of who has access to this\n * deployment and their role. A first-class section (its own rail entry), modeled\n * like the Assets library; the surface itself gates to admins and renders a\n * \"restricted\" state otherwise. Rides on the `settings` availability (an\n * admin/config concern, so it appears/hides with a project's settings surface).\n */\nexport function usersView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'users',\n get label() {\n return config.label ?? translate('nav.users.label')\n },\n get description() {\n return translate('nav.users.description')\n },\n icon: config.icon ?? Users,\n availabilitySection: 'users',\n // EC-240: `[]` is the roster; `[userId]` opens that member in the portable edit\n // shell (AD-19 reuse — the same `DetailEditor` body over the users source).\n parsePath: usersDeepLink,\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title={translate('nav.users.label')} />\n ) : (\n <UsersModule projectId={ctx.projectId} userId={ctx.deepLink.userId ?? null} />\n ),\n }\n}\n\n/** The extensions marketplace view. */\nexport function extensionsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'extensions',\n get label() {\n return config.label ?? translate('nav.extensions.label')\n },\n get description() {\n return translate('nav.extensions.description')\n },\n icon: config.icon ?? Puzzle,\n availabilitySection: 'extensions',\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title={translate('nav.extensions.label')} />\n ) : (\n <ExtensionsView />\n ),\n }\n}\n\n/** The project settings view. */\nexport function settingsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'settings',\n get label() {\n return config.label ?? translate('nav.settings.label')\n },\n get description() {\n return translate('nav.settings.description')\n },\n icon: config.icon ?? SettingsIcon,\n availabilitySection: 'settings',\n render: (ctx) => (\n <SectionSurface project={ctx.project} section=\"settings\" forcedState={ctx.search.state}>\n <SettingsForm project={ctx.project} />\n </SectionSurface>\n ),\n }\n}\n\n/** The legacy `builder` redirect — a hidden, routable view (so `/builder?page=` still lands). */\nexport function builderView(): AppViewModule {\n return {\n id: 'builder',\n label: 'Builder',\n description: 'Legacy builder route — redirects to the page document editor.',\n icon: Blocks,\n hidden: true,\n render: (ctx) => (\n // Retired surface (EC-187 slice 6): a page is a document in the `page`\n // collection. `?page=<id>` lands on that document; else the page list.\n // EC-441: `NavigateOnce` — render-time redirects fire once per intent.\n <NavigateOnce\n to={STUDIO_PROJECT_ROUTE}\n params={{\n projectId: ctx.projectId,\n _splat: ctx.search.page\n ? studioPath.cmsDocument('page', ctx.search.page)\n : studioPath.cmsCollection('page'),\n }}\n replace\n />\n ),\n }\n}\n\n/** Build a fully custom view (the `nav.view(...)` escape hatch). */\nexport function customView(config: CustomViewConfig): AppViewModule {\n return {\n id: config.id ?? slugify(config.label),\n label: config.label,\n description: config.description ?? config.label,\n icon: config.icon ?? LayoutGrid,\n render: config.render,\n ...(config.parsePath ? { parsePath: config.parsePath } : {}),\n ...(config.availabilitySection ? { availabilitySection: config.availabilitySection } : {}),\n }\n}\n\n/**\n * The studio's built-in views in canonical rail order (components · CMS · routes ·\n * assets · extensions · settings), plus the hidden `builder` redirect (routable but\n * unlisted). The default rail (no `defineStudio({ sidebar })`) is these as plain\n * entries; the router registry always includes them so deep-links resolve even\n * when a consumer's rail omits one.\n */\nexport const DEFAULT_VIEWS: readonly AppViewModule[] = [\n componentsView(),\n cmsView(),\n redirectsView(),\n assetsView(),\n extensionsView(),\n usersView(),\n settingsView(),\n builderView(),\n]\n\n/**\n * The `nav` namespace — the single typed builder for `studio.config.tsx`'s section\n * rail. Each call returns a {@link SidebarViewNode} (or a group / separator) that\n * carries its view DIRECTLY: `nav.cms({ collection })` is a focused CMS view,\n * `nav.view({ render })` a net-new surface, `nav.group(label, [...])` a header with\n * sub-views, `nav.separator()` a divider. No string ids, no `moduleId` — the rail\n * is the views.\n */\n/**\n * Forward the OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18)\n * from a `nav.*` config onto the built view, so gating is settable on EVERY nav\n * item — not just Users. Absent → the view is unchanged (always visible).\n */\nfunction withNavPermission(view: AppViewModule, config: { visible?: NavPermission }): AppViewModule {\n if (!config.visible) return view\n // Copy property DESCRIPTORS (not a spread) so a built-in view's lazy `label`/\n // `description` getters (EC-241) survive — a spread would invoke them at build time,\n // baking English before `createStudio` seeds the i18n config.\n return Object.defineProperties({} as AppViewModule, {\n ...Object.getOwnPropertyDescriptors(view),\n visible: { value: config.visible, enumerable: true, configurable: true, writable: true },\n })\n}\n\nexport const nav = {\n /**\n * The CMS view. `nav.cms()` / `nav.cms({ collections })` is a PARENT that nests its\n * collections in the rail (each a focused `cms:<id>` lens); `nav.cms({ collection })`\n * is a single-collection LEAF; `nav.cms({ children })` supplies explicit children.\n */\n cms: (config: CmsViewConfig = {}): SidebarViewNode => {\n const view = withNavPermission(cmsView(config), config)\n // A single-collection lens is a leaf — it opens that collection directly.\n if (config.collection) return viewNode(view)\n // Otherwise a parent that expands its collections as nested rail children (EC-293).\n const node: SidebarViewNode = { kind: 'view', view }\n if (config.children) node.children = config.children\n else\n node.expand = (collections) =>\n focusCollectionList(collections, config.collections).map(cmsCollectionLeaf)\n return node\n },\n /** The component registry browser. */\n components: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(componentsView(config), config)),\n /** The assets rail — the universal list/grid + detail (EC-319); built-in\n * fields customizable by name (EC-311), the same shape as `nav.redirects`. */\n assets: (config: AssetsViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(assetsView(config), config)),\n /** Editorial redirects (EC-303); built-in fields customizable by name (EC-311). */\n redirects: (config: RedirectsViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(redirectsView(config), config)),\n /** Project settings. */\n settings: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(settingsView(config), config)),\n /** The extensions marketplace. */\n extensions: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(extensionsView(config), config)),\n /** The admin users roster (EC-240). */\n users: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(usersView(config), config)),\n /** A fully custom view — pass your own `render` (and optional `parsePath`). */\n view: (config: CustomViewConfig): SidebarViewNode =>\n viewNode(withNavPermission(customView(config), config)),\n /** A labelled group of views (a header with sub-entries). */\n group: (label: string, children: SidebarViewNode[]) => groupNode(label, children),\n /** A divider between rail sections. */\n separator: () => separatorNode(),\n} as const\n\n// --- Surfaces (the view render bodies; CMS gains a focus lens) ---\n\n/**\n * Components section (EC-050): a registry-browser sidebar beside the active\n * screen — the index (no selection) or a component's detail. Forced states\n * (?state=) replace the whole surface.\n */\nfunction ComponentsScreen({\n projectId,\n componentId,\n state,\n}: {\n projectId: string\n componentId: string | null\n state?: SectionState\n}) {\n if (state && state !== 'working') {\n return <SectionStateView state={state} title={translate('nav.components.label')} />\n }\n return (\n <div className=\"flex h-full min-h-0\">\n <aside className=\"hidden h-full min-h-0 w-[300px] shrink-0 border-r border-border md:block\">\n <RegistryBrowser projectId={projectId} activeComponentId={componentId ?? undefined} />\n </aside>\n <div className=\"min-h-0 flex-1 overflow-auto\">\n {componentId === null ? (\n <ComponentsIndex projectId={projectId} />\n ) : (\n <ComponentDetail projectId={projectId} componentId={componentId} />\n )}\n </div>\n </div>\n )\n}\n\n/**\n * The CMS schema source for a project mount (bug fix, playground-collections):\n * a hydrated session wins (Convex), then a kit's imported schema (EC-119), then —\n * in playground mode only — the active project's config-declared collections\n * (AD-11, `resolveDeploymentCollections`). Without this last fallback, a non-kit\n * playground project has no hydrated session (`null`, by design — see\n * `project-session.tsx`) and no kit, so `CmsProvider` fell through to the core's\n * hardcoded `sampleFilterableCollections` fixture instead of the project's own\n * on-disk schema. Convex mode's first-run `null` is untouched — it still seeds\n * from the fixture there, as before. `playground` defaults to the live\n * {@link isPlaygroundMode} but is overridable so this stays unit testable\n * without stubbing the env (same seam as `resolveBackendConfig`).\n */\nexport function resolveCmsInitialCollections(\n cms: HydratedCms | null,\n kit: InstantiatedKit | undefined,\n playground: boolean = isPlaygroundMode(),\n): CollectionDef[] | undefined {\n return cms?.collections ?? kit?.collections ?? (playground ? resolveDeploymentCollections() : undefined)\n}\n\n/**\n * The CMS documents source for a project mount (EC-424) — the document-side\n * twin of {@link resolveCmsInitialCollections}, same ladder: a hydrated session\n * wins (Convex), then a kit's imported documents, then — in playground mode\n * only, and only when a config project actually resolved collections — the\n * engine's own `sampleDocuments` fixture RECONCILED against them.\n *\n * Without this, a config project whose OWN schema diverges from the shared\n * fixture's localization (e.g. this repo's demo declares `post.slug` localized,\n * EC-315, while the engine's `postCollection` fixture deliberately does not)\n * left `CmsProvider` falling straight through to the un-reconciled\n * `sampleDocuments` default — the CMS surfaces' `initialCollections` already\n * reflected the config schema (via the fallback above), but `initialDocuments`\n * did not, so every seeded post showed EC-390's shape-aware validation's\n * `plain-value` warning + `missing-localized-value` error, standing noise a\n * project never actually wrote.\n *\n * This is the STATIC-baseline twin of `reconcileSeedDocumentLocalization`\n * (`@elytracms/core/persistence`, wired into the seeded persistence adapter for\n * Convex-mode/delivery reads): no adapter round-trip, so the EC-138/EC-320\n * synchronous-first-render contract (`project-session.tsx`: \"CMS surfaces keep\n * their fixture baseline\") is untouched. `collections` defaults to the SAME\n * resolution `resolveCmsInitialCollections` returns, so the two stay in lockstep\n * by construction; only reconciles when that resolved to a non-empty config\n * schema (env-only playground, with nothing config-declared, is unaffected).\n */\nexport function resolveCmsInitialDocuments(\n cms: HydratedCms | null,\n kit: InstantiatedKit | undefined,\n collections: CollectionDef[] | undefined = resolveCmsInitialCollections(cms, kit),\n): CmsDocument[] | undefined {\n if (cms) return cms.documents\n if (kit) return kit.documents\n if (!collections || collections.length === 0) return undefined\n const collectionsById = new Map(collections.map((collection) => [collection.id, collection]))\n return sampleDocuments.map((doc) => {\n const collection = collectionsById.get(doc.collection)\n return collection ? reconcileSeedDocumentLocalization(doc, collection) : doc\n })\n}\n\n/**\n * The CMS locale config for a project mount (EC-357 finding #3). Locales are\n * structure and structure is code: when the active `elytra.config.ts` project\n * declares locales, they drive the ambient locale switcher in EVERY mode. The old\n * wiring only applied the project's locale settings on a HYDRATED session, so a\n * playground boot — or a connected FIRST RUN with nothing persisted yet — fell\n * back to a kit's locales or the core's sample `en/de` fixture and the switcher\n * showed `EN·/DE` against a `locales: ['de']` config. Without a config project\n * (env-only wiring) the previous ladder is unchanged: hydrated project settings,\n * then the kit's locales, then the sample fixture (via `CmsProvider`'s default).\n * `configLocales` is a parameter purely so the ladder is unit-testable (same seam\n * pattern as {@link resolveCmsInitialCollections}).\n */\nexport function resolveCmsInitialLocaleConfig(\n cms: HydratedCms | null,\n kit: InstantiatedKit | undefined,\n project: Project,\n configLocales: LocaleConfig | undefined = resolveDeploymentLocaleConfig(),\n): LocaleConfig | undefined {\n if (configLocales) return configLocales\n if (cms) return { default: project.settings.defaultLocale, locales: [...project.settings.locales] }\n return kit?.localeConfig\n}\n\n/**\n * CMS section: hydrates the persisted (or kit-seeded) collections + documents, then\n * renders the active screen full-width (index, a collection table, or a document\n * editor). EC-293: the collection list lives in the section rail (nested under the\n * CMS view), so there is no in-surface collections sidebar. Forced states (?state=)\n * replace the whole surface. `focusCollections` (EC-239) narrows the index to a\n * subset, so a focused view (\"Catalog\") reuses this surface with config.\n */\nfunction CmsScreen({\n project,\n collectionId,\n documentId,\n state,\n field,\n focusCollections,\n}: {\n project: Project\n collectionId: string | null\n documentId: string | null\n state?: SectionState\n field?: string\n focusCollections?: readonly string[]\n}) {\n const projectId = project.id\n if (state && state !== 'working') {\n return <SectionStateView state={state} title={translate('nav.cms.label')} />\n }\n\n // A project created from a starter kit (EC-119) opens the CMS on the kit's schemas + content.\n const kit = getInstantiatedKit(projectId)\n\n return (\n // EC-177 gap 1: in Convex mode every CMS mount loads the CURRENT persisted\n // schema + documents (fixtures only when nothing is persisted — first run).\n // Playground renders synchronously with `null` → fixture seeds, as before.\n <CmsSessionGate>\n {(cms) => {\n // EC-424: resolved once so `initialDocuments`' reconciliation (below)\n // stays in lockstep with the SAME collections `initialCollections` uses.\n const initialCollections = resolveCmsInitialCollections(cms, kit)\n return (\n <CmsProvider\n key={projectId}\n projectId={projectId}\n initialCollections={initialCollections}\n initialDocuments={resolveCmsInitialDocuments(cms, kit, initialCollections)}\n initialPublishedKeys={cms?.publishedKeys}\n initialTimestamps={cms?.timestamps}\n initialPublishTimestamps={cms?.publishTimestamps}\n initialChangedKeys={cms?.changedKeys}\n initialLocaleConfig={resolveCmsInitialLocaleConfig(cms, kit, project)}\n >\n <div className=\"flex h-full min-h-0 flex-col\">\n <CmsPersistErrorBanner />\n {/* EC-293: the collection list now lives in the section rail (nested\n under the CMS view), so the surface is full-width — the old 240px\n CollectionsSidebar aside is gone. EC-199: the AI assistant mounts as\n a right-hand sibling (both list + editor), so the content is a row. */}\n <div className=\"flex min-h-0 flex-1 overflow-hidden\">\n <div className=\"min-w-0 flex-1 overflow-hidden\">\n <CmsContent\n projectId={projectId}\n collectionId={collectionId}\n documentId={documentId}\n field={field}\n focusCollections={focusCollections}\n />\n </div>\n <AiCmsPanel />\n </div>\n </div>\n </CmsProvider>\n )\n }}\n </CmsSessionGate>\n )\n}\n\n/** Resolves the active CMS screen inside the provider (needs the live store). */\nfunction CmsContent({\n projectId,\n collectionId,\n documentId,\n field,\n focusCollections,\n}: {\n projectId: string\n collectionId: string | null\n documentId: string | null\n field?: string\n focusCollections?: readonly string[]\n}) {\n const cms = useCms()\n\n if (collectionId === null) return <CmsIndex projectId={projectId} focusCollections={focusCollections} />\n\n if (documentId === null) {\n const collection = cms.getCollection(collectionId)\n // EC-274: a singleton is a UI lens — at the collection URL it opens its one\n // document directly *while in its expected shape*, but degrades to the list\n // (with a \"for review\" lane) on drift. SingletonGate decides per the data.\n if (collection && isSingleton(collection)) {\n return <SingletonGate projectId={projectId} collectionId={collectionId} />\n }\n return <CollectionTable projectId={projectId} collectionId={collectionId} />\n }\n\n return (\n <DetailEditor\n projectId={projectId}\n collectionId={collectionId}\n documentId={documentId}\n focusPath={field}\n />\n )\n}\n\n/**\n * EC-274 — the singleton lens gate. At a singleton collection's URL, resolve its\n * lone document and open it directly; on drift (> 1 document) fall back to the\n * list with the \"for review\" lane so an editor can resolve the conflict.\n */\nfunction SingletonGate({ projectId, collectionId }: { projectId: string; collectionId: string }) {\n const cms = useCms()\n const collection = cms.getCollection(collectionId)\n const docs = cms.documentsOf(collectionId)\n const resolution = collection\n ? resolveSingletonDocument(collection, docs)\n : ({ kind: 'empty' } as const)\n\n // Materialize the lone document on first visit (an empty draft, persisted like\n // any create). Idempotent in the store; only fires while genuinely empty.\n useEffect(() => {\n if (resolution.kind === 'empty') cms.ensureSingletonDocument(collectionId)\n }, [resolution.kind, cms, collectionId])\n\n if (resolution.kind === 'ready') {\n return (\n <DetailEditor projectId={projectId} collectionId={collectionId} documentId={resolution.doc.id} />\n )\n }\n if (resolution.kind === 'drift') {\n return <CollectionTable projectId={projectId} collectionId={collectionId} />\n }\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Text size=\"sm\" tone=\"muted\">\n {translate('appViews.singleton.opening', {\n label: localizedLabel(collection?.form?.label, collectionId),\n })}\n </Text>\n </div>\n )\n}\n\n/** Failed/denied persisted CMS mutations are always visible (EC-141). */\nfunction CmsPersistErrorBanner() {\n const cms = useCms()\n if (!cms.persistError) return null\n return (\n <div className=\"shrink-0 border-b border-border p-2\">\n <OperationErrorBanner error={cms.persistError} onDismiss={cms.clearPersistError} />\n </div>\n )\n}\n"]}
|
|
@@ -82,9 +82,28 @@ export interface CmsStore {
|
|
|
82
82
|
* `$updatedAt` list column. Seeded from hydration (connected: the persisted
|
|
83
83
|
* `cmsDocuments.updatedAt`; playground: session-start for the seeded fixtures),
|
|
84
84
|
* then re-stamped on the common edit paths. `undefined` for a ref the session
|
|
85
|
-
* has no timestamp for.
|
|
85
|
+
* has no timestamp for. See {@link documentPublishedAt} for the publish-instant column.
|
|
86
86
|
*/
|
|
87
87
|
documentUpdatedAt(ref: DocumentRef): string | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* The record's FIRST-publish ISO instant (EC-447) — the data behind the
|
|
90
|
+
* `$publishedAt` list column. Seeded from hydration (the persisted
|
|
91
|
+
* `cmsDocuments.publishedAt`), then re-stamped/cleared authoritatively on
|
|
92
|
+
* publish/unpublish from the reconciled backend record (set-once on publish,
|
|
93
|
+
* cleared on unpublish). `undefined` for a draft / never-published /
|
|
94
|
+
* currently-unpublished ref (the column shows EmDash).
|
|
95
|
+
*/
|
|
96
|
+
documentPublishedAt(ref: DocumentRef): string | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Does this PUBLISHED document's working draft carry unpublished changes
|
|
99
|
+
* (EC-449)? The cheap per-row signal behind the amber "changed" `$state` dot —
|
|
100
|
+
* sourced from the materialized `cmsDocuments.hasUnpublishedChanges` (computed
|
|
101
|
+
* ONCE at the write chokepoint, never per render). Seeded from hydration, then
|
|
102
|
+
* re-stamped authoritatively from the returned record on save (the persister's
|
|
103
|
+
* saved-record hook / the inline-edit dispatch), publish (→ false) and
|
|
104
|
+
* unpublish (→ cleared). Always `false` for a draft / never-published ref.
|
|
105
|
+
*/
|
|
106
|
+
documentHasUnpublishedChanges(ref: DocumentRef): boolean;
|
|
88
107
|
/**
|
|
89
108
|
* Publish: snapshot the draft as a new version and pin it live (EC-224).
|
|
90
109
|
* Flips published-ness optimistically, then reconciles against the backend
|
|
@@ -199,7 +218,7 @@ export declare function hasDocumentFor(docs: readonly CmsDocument[], collectionI
|
|
|
199
218
|
* layer (`documents.*` / `schema.*` commands) through {@link createCmsPersister}.
|
|
200
219
|
* Failed/denied commands surface as {@link CmsStore.persistError}.
|
|
201
220
|
*/
|
|
202
|
-
export declare function CmsProvider({ children, projectId, initialCollections, initialDocuments, initialPublishedKeys, initialTimestamps, initialLocaleConfig, }: {
|
|
221
|
+
export declare function CmsProvider({ children, projectId, initialCollections, initialDocuments, initialPublishedKeys, initialTimestamps, initialPublishTimestamps, initialChangedKeys, initialLocaleConfig, }: {
|
|
203
222
|
children: ReactNode;
|
|
204
223
|
/** Enables persistence through the shared operations client. */
|
|
205
224
|
projectId?: string;
|
|
@@ -219,6 +238,22 @@ export declare function CmsProvider({ children, projectId, initialCollections, i
|
|
|
219
238
|
* timestamp so the column is never blank, then re-stamped on edits.
|
|
220
239
|
*/
|
|
221
240
|
initialTimestamps?: Record<string, string>;
|
|
241
|
+
/**
|
|
242
|
+
* Per-document FIRST-publish instants from hydration (EC-447), keyed by
|
|
243
|
+
* `documentKey` (`collection:id`) → ISO string. Backs the `$publishedAt` column.
|
|
244
|
+
* UNLIKE {@link initialTimestamps} there is NO session-start fallback — an absent
|
|
245
|
+
* entry is a real "no publish date" (EmDash); the store re-stamps/clears it
|
|
246
|
+
* authoritatively on publish/unpublish. Absent (playground / kit) → empty map.
|
|
247
|
+
*/
|
|
248
|
+
initialPublishTimestamps?: Record<string, string>;
|
|
249
|
+
/**
|
|
250
|
+
* Document keys whose draft carries unpublished changes, from hydration
|
|
251
|
+
* (EC-449, the persisted `cmsDocuments.hasUnpublishedChanges === true` set) —
|
|
252
|
+
* the amber "changed" `$state` dot. Mirrors {@link initialPublishedKeys}; the
|
|
253
|
+
* store re-stamps the set authoritatively on save/publish/unpublish from the
|
|
254
|
+
* returned records. Absent (playground / kit) → empty set.
|
|
255
|
+
*/
|
|
256
|
+
initialChangedKeys?: readonly string[];
|
|
222
257
|
/** Locale config for the seeded content; defaults to the sample config. */
|
|
223
258
|
initialLocaleConfig?: LocaleConfig;
|
|
224
259
|
}): import("react").JSX.Element;
|