@elytracms/studio 0.0.70 → 0.0.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/components/account/self-account-view-source.js +1 -0
  2. package/dist/components/account/self-account-view-source.js.map +1 -1
  3. package/dist/components/assets/assets-collection-view-source.js +1 -0
  4. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  5. package/dist/components/cms/collection-table.js +19 -2
  6. package/dist/components/cms/collection-table.js.map +1 -1
  7. package/dist/components/cms/detail-editor.js +73 -51
  8. package/dist/components/cms/detail-editor.js.map +1 -1
  9. package/dist/components/cms/duplicate-document.d.ts +45 -0
  10. package/dist/components/cms/duplicate-document.js +141 -0
  11. package/dist/components/cms/duplicate-document.js.map +1 -0
  12. package/dist/components/cms/field-anchors.d.ts +67 -0
  13. package/dist/components/cms/field-anchors.js +91 -0
  14. package/dist/components/cms/field-anchors.js.map +1 -0
  15. package/dist/components/cms/issue-copy.d.ts +45 -0
  16. package/dist/components/cms/issue-copy.js +89 -0
  17. package/dist/components/cms/issue-copy.js.map +1 -0
  18. package/dist/components/cms/issues-pane.d.ts +11 -2
  19. package/dist/components/cms/issues-pane.js +22 -4
  20. package/dist/components/cms/issues-pane.js.map +1 -1
  21. package/dist/components/cms/object-array-field.d.ts +2 -0
  22. package/dist/components/cms/object-array-field.js +75 -21
  23. package/dist/components/cms/object-array-field.js.map +1 -1
  24. package/dist/components/cms/row-actions.d.ts +10 -3
  25. package/dist/components/cms/row-actions.js +8 -0
  26. package/dist/components/cms/row-actions.js.map +1 -1
  27. package/dist/components/cms/use-field-focus.d.ts +25 -9
  28. package/dist/components/cms/use-field-focus.js +80 -15
  29. package/dist/components/cms/use-field-focus.js.map +1 -1
  30. package/dist/components/collection-view/cms-collection-view-source.js +1 -0
  31. package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
  32. package/dist/components/redirects/redirects-collection-view-source.js +1 -0
  33. package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
  34. package/dist/components/users/users-collection-view-source.js +1 -0
  35. package/dist/components/users/users-collection-view-source.js.map +1 -1
  36. package/dist/customization.d.ts +9 -0
  37. package/dist/customization.js +8 -0
  38. package/dist/customization.js.map +1 -1
  39. package/dist/lib/collection-view/collection-view-source.d.ts +8 -0
  40. package/dist/lib/collection-view/collection-view-source.js.map +1 -1
  41. package/dist/lib/i18n/messages.de.js +38 -1
  42. package/dist/lib/i18n/messages.de.js.map +1 -1
  43. package/dist/lib/i18n/messages.en.d.ts +34 -0
  44. package/dist/lib/i18n/messages.en.js +41 -1
  45. package/dist/lib/i18n/messages.en.js.map +1 -1
  46. package/dist/lib/persona/labels.d.ts +5 -1
  47. package/dist/lib/persona/labels.js +43 -30
  48. package/dist/lib/persona/labels.js.map +1 -1
  49. package/dist/previews/enumerate.d.ts +68 -0
  50. package/dist/previews/enumerate.js +78 -0
  51. package/dist/previews/enumerate.js.map +1 -0
  52. package/dist/previews/harness.d.ts +50 -0
  53. package/dist/previews/harness.js +123 -0
  54. package/dist/previews/harness.js.map +1 -0
  55. package/dist/previews/manifest-format.d.ts +57 -0
  56. package/dist/previews/manifest-format.js +59 -0
  57. package/dist/previews/manifest-format.js.map +1 -0
  58. package/dist/ui/field.d.ts +24 -1
  59. package/dist/ui/field.js +23 -2
  60. package/dist/ui/field.js.map +1 -1
  61. package/package.json +10 -6
@@ -19,6 +19,7 @@ export const ACCOUNT_CAPABILITIES = {
19
19
  locales: false,
20
20
  inlineEdit: false,
21
21
  create: false,
22
+ duplicate: false,
22
23
  remove: false,
23
24
  bulk: false,
24
25
  liveUrl: false,
@@ -1 +1 @@
1
- {"version":3,"file":"self-account-view-source.js","sourceRoot":"","sources":["../../../src/components/account/self-account-view-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback } from 'react'\nimport type { CmsDocument, CmsValidationIssue, DocumentRef, Locale, LocaleConfig } from '@elytracms/core/cms-core'\nimport type { UserRecord } from '@elytracms/core/persistence'\nimport { studioPath } from '@elytracms/studio/router'\nimport type {\n CollectionViewCapabilities,\n CollectionViewSource,\n} from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport type { OperationError } from '@elytracms/core/operations'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { updateProfileErrorMessageKey } from '@elytracms/studio/lib/auth-errors'\nimport { useAuth } from '../auth-context'\nimport { selfAccountCollectionDef } from './self-account-schema'\nimport { normalizeOkResult, useRailViewSource } from '../rail-view-source'\n\n/** The account surface is not localized — one implicit locale keeps the lens honest. */\nconst ACCOUNT_LOCALE: Locale = 'en'\nconst ACCOUNT_LOCALE_CONFIG: LocaleConfig = { default: ACCOUNT_LOCALE, locales: [ACCOUNT_LOCALE] }\n\n/**\n * The capability profile of the account surface (EC-240 H-tail): a single self\n * record edited in place — no publish/versions/create/remove/import. The record\n * is the caller's own; there is nothing to delete or list.\n */\nexport const ACCOUNT_CAPABILITIES: CollectionViewCapabilities = {\n publish: false,\n versions: false,\n locales: false,\n inlineEdit: false,\n create: false,\n remove: false,\n bulk: false,\n liveUrl: false,\n hierarchy: false,\n importExport: false,\n}\n\n/** Everything in `values` that is NOT an editable identity field is display-only. */\nconst EDITABLE_IDENTITY = new Set(['name', 'avatar'])\n\n/** Project the self record into the account lens's `values`. */\nexport function selfToDocument(record: UserRecord): CmsDocument {\n return {\n collection: selfAccountCollectionDef.id,\n id: record.id,\n values: {\n name: record.name ?? '',\n avatar: record.avatar ?? '',\n email: record.email,\n role: record.roles[0] ?? 'viewer',\n },\n }\n}\n\n/**\n * Compute the identity diff (name / avatar) an edited account document carries —\n * the exact seam handed to `AuthAdapter.updateProfile`. Pure so the split-write is\n * unit-testable: only the fields the person changed are sent (an empty diff is a\n * no-op the caller can short-circuit). Never carries `email`/`role` — those are\n * read-only on this surface and are not auth-adapter-writable here.\n */\nexport function accountIdentityDiff(\n doc: CmsDocument,\n previous: UserRecord,\n): { name?: string; avatar?: string } {\n const values = doc.values ?? {}\n const diff: { name?: string; avatar?: string } = {}\n const nextName = typeof values.name === 'string' ? values.name : ''\n const nextAvatar = typeof values.avatar === 'string' ? values.avatar : ''\n if (nextName !== (previous.name ?? '')) diff.name = nextName\n if (nextAvatar !== (previous.avatar ?? '')) diff.avatar = nextAvatar\n return diff\n}\n\n/**\n * Assemble a {@link CollectionViewSource} for the **account-settings** surface\n * (EC-240 H-tail). Reuses the portable edit shell over the caller's OWN record —\n * name/avatar editable, email/role read-only (per the schema). The `save` routes\n * the identity diff to `AuthAdapter.updateProfile` (auth-owned) — NOT the\n * members-rail `users.save` — then refreshes the loaded record. `EDITABLE_IDENTITY`\n * marks which fields the save may write; everything else is display-only.\n */\nexport function useSelfAccountViewSource(input: {\n projectId: string\n self: UserRecord\n refresh: () => Promise<void>\n}): CollectionViewSource {\n const { projectId, self, refresh } = input\n const auth = useAuth()\n\n const documents = useCallback(() => [selfToDocument(self)], [self])\n const get = useCallback(\n (ref: DocumentRef): CmsDocument | undefined =>\n ref.id === self.id ? selfToDocument(self) : undefined,\n [self],\n )\n const titleOf = useCallback(() => self.name || self.email || self.id, [self])\n\n const save = useCallback(\n async (doc: CmsDocument): Promise<{ ok: true } | { ok: false; message: string }> => {\n const diff = accountIdentityDiff(doc, self)\n // No identity change (e.g. a read-only-only edit) → nothing to write.\n if (diff.name === undefined && diff.avatar === undefined) return { ok: true }\n const result = await auth.updateProfile(diff)\n if (!result.ok) {\n // EC-416: engine-classified failures localize through the message\n // catalog; an unrecognized shape renders the adapter's raw English\n // message — honest, never a stale mapping (the EC-397/EC-412 pattern).\n const errorKey = updateProfileErrorMessageKey(result)\n return { ok: false, message: errorKey ? translate(errorKey) : result.message }\n }\n // Reflect the new identity in the loaded record (session already refreshed).\n await refresh()\n return { ok: true }\n },\n [auth, self, refresh],\n )\n\n const noRemove = useCallback(\n async (): Promise<{ ok: true }> => ({ ok: true }),\n [],\n )\n const noValidation = useCallback((): CmsValidationIssue[] => [], [])\n const createUnsupported = useCallback((): DocumentRef => {\n throw new Error('the account surface has no create')\n }, [])\n\n return useRailViewSource({\n projectId,\n schema: selfAccountCollectionDef,\n capabilities: ACCOUNT_CAPABILITIES,\n localeConfig: ACCOUNT_LOCALE_CONFIG,\n documents,\n get,\n titleOf,\n // Editable-identity fields drive writability; the rest are read-only per schema.\n isFieldReadOnly: (field) => !EDITABLE_IDENTITY.has(field.name) || (field.form?.readOnly ?? false),\n detailPath: (id: string) => studioPath.user(id),\n listPath: studioPath.users(),\n validate: noValidation,\n create: createUnsupported,\n save,\n remove: noRemove,\n normalizeSaveResult: normalizeOkResult,\n normalizeRemoveResult: (): OperationError | null => null,\n })\n}\n"]}
1
+ {"version":3,"file":"self-account-view-source.js","sourceRoot":"","sources":["../../../src/components/account/self-account-view-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback } from 'react'\nimport type { CmsDocument, CmsValidationIssue, DocumentRef, Locale, LocaleConfig } from '@elytracms/core/cms-core'\nimport type { UserRecord } from '@elytracms/core/persistence'\nimport { studioPath } from '@elytracms/studio/router'\nimport type {\n CollectionViewCapabilities,\n CollectionViewSource,\n} from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport type { OperationError } from '@elytracms/core/operations'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { updateProfileErrorMessageKey } from '@elytracms/studio/lib/auth-errors'\nimport { useAuth } from '../auth-context'\nimport { selfAccountCollectionDef } from './self-account-schema'\nimport { normalizeOkResult, useRailViewSource } from '../rail-view-source'\n\n/** The account surface is not localized — one implicit locale keeps the lens honest. */\nconst ACCOUNT_LOCALE: Locale = 'en'\nconst ACCOUNT_LOCALE_CONFIG: LocaleConfig = { default: ACCOUNT_LOCALE, locales: [ACCOUNT_LOCALE] }\n\n/**\n * The capability profile of the account surface (EC-240 H-tail): a single self\n * record edited in place — no publish/versions/create/remove/import. The record\n * is the caller's own; there is nothing to delete or list.\n */\nexport const ACCOUNT_CAPABILITIES: CollectionViewCapabilities = {\n publish: false,\n versions: false,\n locales: false,\n inlineEdit: false,\n create: false,\n duplicate: false,\n remove: false,\n bulk: false,\n liveUrl: false,\n hierarchy: false,\n importExport: false,\n}\n\n/** Everything in `values` that is NOT an editable identity field is display-only. */\nconst EDITABLE_IDENTITY = new Set(['name', 'avatar'])\n\n/** Project the self record into the account lens's `values`. */\nexport function selfToDocument(record: UserRecord): CmsDocument {\n return {\n collection: selfAccountCollectionDef.id,\n id: record.id,\n values: {\n name: record.name ?? '',\n avatar: record.avatar ?? '',\n email: record.email,\n role: record.roles[0] ?? 'viewer',\n },\n }\n}\n\n/**\n * Compute the identity diff (name / avatar) an edited account document carries —\n * the exact seam handed to `AuthAdapter.updateProfile`. Pure so the split-write is\n * unit-testable: only the fields the person changed are sent (an empty diff is a\n * no-op the caller can short-circuit). Never carries `email`/`role` — those are\n * read-only on this surface and are not auth-adapter-writable here.\n */\nexport function accountIdentityDiff(\n doc: CmsDocument,\n previous: UserRecord,\n): { name?: string; avatar?: string } {\n const values = doc.values ?? {}\n const diff: { name?: string; avatar?: string } = {}\n const nextName = typeof values.name === 'string' ? values.name : ''\n const nextAvatar = typeof values.avatar === 'string' ? values.avatar : ''\n if (nextName !== (previous.name ?? '')) diff.name = nextName\n if (nextAvatar !== (previous.avatar ?? '')) diff.avatar = nextAvatar\n return diff\n}\n\n/**\n * Assemble a {@link CollectionViewSource} for the **account-settings** surface\n * (EC-240 H-tail). Reuses the portable edit shell over the caller's OWN record —\n * name/avatar editable, email/role read-only (per the schema). The `save` routes\n * the identity diff to `AuthAdapter.updateProfile` (auth-owned) — NOT the\n * members-rail `users.save` — then refreshes the loaded record. `EDITABLE_IDENTITY`\n * marks which fields the save may write; everything else is display-only.\n */\nexport function useSelfAccountViewSource(input: {\n projectId: string\n self: UserRecord\n refresh: () => Promise<void>\n}): CollectionViewSource {\n const { projectId, self, refresh } = input\n const auth = useAuth()\n\n const documents = useCallback(() => [selfToDocument(self)], [self])\n const get = useCallback(\n (ref: DocumentRef): CmsDocument | undefined =>\n ref.id === self.id ? selfToDocument(self) : undefined,\n [self],\n )\n const titleOf = useCallback(() => self.name || self.email || self.id, [self])\n\n const save = useCallback(\n async (doc: CmsDocument): Promise<{ ok: true } | { ok: false; message: string }> => {\n const diff = accountIdentityDiff(doc, self)\n // No identity change (e.g. a read-only-only edit) → nothing to write.\n if (diff.name === undefined && diff.avatar === undefined) return { ok: true }\n const result = await auth.updateProfile(diff)\n if (!result.ok) {\n // EC-416: engine-classified failures localize through the message\n // catalog; an unrecognized shape renders the adapter's raw English\n // message — honest, never a stale mapping (the EC-397/EC-412 pattern).\n const errorKey = updateProfileErrorMessageKey(result)\n return { ok: false, message: errorKey ? translate(errorKey) : result.message }\n }\n // Reflect the new identity in the loaded record (session already refreshed).\n await refresh()\n return { ok: true }\n },\n [auth, self, refresh],\n )\n\n const noRemove = useCallback(\n async (): Promise<{ ok: true }> => ({ ok: true }),\n [],\n )\n const noValidation = useCallback((): CmsValidationIssue[] => [], [])\n const createUnsupported = useCallback((): DocumentRef => {\n throw new Error('the account surface has no create')\n }, [])\n\n return useRailViewSource({\n projectId,\n schema: selfAccountCollectionDef,\n capabilities: ACCOUNT_CAPABILITIES,\n localeConfig: ACCOUNT_LOCALE_CONFIG,\n documents,\n get,\n titleOf,\n // Editable-identity fields drive writability; the rest are read-only per schema.\n isFieldReadOnly: (field) => !EDITABLE_IDENTITY.has(field.name) || (field.form?.readOnly ?? false),\n detailPath: (id: string) => studioPath.user(id),\n listPath: studioPath.users(),\n validate: noValidation,\n create: createUnsupported,\n save,\n remove: noRemove,\n normalizeSaveResult: normalizeOkResult,\n normalizeRemoveResult: (): OperationError | null => null,\n })\n}\n"]}
@@ -34,6 +34,7 @@ export const ASSET_COLLECTION_CAPABILITIES = {
34
34
  locales: false,
35
35
  inlineEdit: false,
36
36
  create: false,
37
+ duplicate: false, // EC-442: blob duplication is a different feature, out of scope
37
38
  remove: true,
38
39
  bulk: true,
39
40
  liveUrl: false,
@@ -1 +1 @@
1
- {"version":3,"file":"assets-collection-view-source.js","sourceRoot":"","sources":["../../../src/components/assets/assets-collection-view-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo } from 'react'\nimport type { CmsDocument, CollectionDef, DocumentRef, Locale, LocaleConfig } from '@elytracms/core/cms-core'\nimport { createCollectionRegistry, validateDocument } from '@elytracms/core/cms-core'\nimport type { AssetRecord, ReferenceTarget } from '@elytracms/core/persistence'\nimport type { OperationError } from '@elytracms/core/operations'\nimport { studioPath } from '@elytracms/studio/router'\nimport type { DeleteAssetResult } from '@elytracms/studio/lib/assets'\nimport type { CollectionViewSource, CollectionViewCapabilities } from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport { useAssets } from './assets-store'\nimport { SYSTEM_ASSET_FIELDS } from './asset-metadata'\nimport { normalizeOkResult, useRailViewSource } from '../rail-view-source'\n\n/** The assets rail is not localized in v1 (EC-301: asset metadata is non-localized). */\nconst ASSETS_LOCALE: Locale = 'en'\nconst ASSETS_LOCALE_CONFIG: LocaleConfig = {\n default: ASSETS_LOCALE,\n locales: [ASSETS_LOCALE],\n}\n\n/**\n * The capability profile of the assets rail (EC-319, EC-433). Hoisted to a\n * module-level constant (no prop/state dependency) so it's both the value the\n * hook returns AND independently unit-testable.\n *\n * `create` is off — a new asset comes from an upload, never a blank draft.\n * `remove` + `bulk` are ON since EC-433: the list's row/bulk delete used to be\n * off deliberately because the generic `ReferenceConfirmDialog` enumerated\n * inbound references as `{ type: 'document' }` targets — the wrong index shape\n * for an asset — which would have shown \"not referenced\" for an asset that IS\n * referenced. That pre-flight now queries the shape THIS source declares via\n * {@link assetReferenceTarget} (`{ type: 'asset' }`, the same lookup the\n * detail surface runs), and the confirmed delete dispatches the SAME checked\n * store delete the detail surface uses (`deleteAsset`: original + EC-400 thumb\n * blobs, then the record — EC-420 lqips ride on the record). Nothing else\n * applies to assets: no publish/versions/locales/inline-edit/live-URL/\n * hierarchy/import-export.\n */\nexport const ASSET_COLLECTION_CAPABILITIES: CollectionViewCapabilities = {\n publish: false,\n versions: false,\n locales: false,\n inlineEdit: false,\n create: false,\n remove: true,\n bulk: true,\n liveUrl: false,\n hierarchy: false,\n importExport: false,\n adminOnly: false,\n}\n\n/** Read one of the record's fixed, upload-managed columns by field name. */\nfunction systemFieldValue(record: AssetRecord, name: string): unknown {\n switch (name) {\n case 'filename':\n return record.filename\n case 'url':\n return record.url\n case 'contentType':\n return record.contentType\n case 'byteSize':\n return record.byteSize\n case 'width':\n return record.width\n case 'height':\n return record.height\n case 'storageKey':\n return record.storageKey\n case 'title':\n return record.title\n default:\n return undefined\n }\n}\n\n/**\n * Project a stored asset record into the lens's `values` shape (EC-319), reading\n * only the schema's DECLARED fields — the same \"schema is a lens\" projection the\n * redirects/users rails use. A system column (`filename`, `url`, dimensions, …)\n * reads straight off the record; everything else — the built-in `alt`, a\n * project's own `copyright`/`credit`, or an EC-311 additive field — round-trips\n * through the record's open `meta` bag (EC-301). `alt` is schema-driven but ALSO\n * has a legacy record column: the meta value wins, falling back to the column\n * (matches delivery's `meta.alt ?? record.alt` read and the image field's\n * options dialog).\n */\nexport function assetToDocument(collection: CollectionDef, record: AssetRecord): CmsDocument {\n const values: Record<string, unknown> = {}\n for (const field of collection.fields) {\n if (field.name === 'alt') {\n const metaAlt = typeof record.meta?.alt === 'string' ? record.meta.alt : undefined\n values.alt = metaAlt ?? record.alt\n continue\n }\n values[field.name] = SYSTEM_ASSET_FIELDS.has(field.name)\n ? systemFieldValue(record, field.name)\n : record.meta?.[field.name]\n }\n return { collection: collection.id, id: record.id, values }\n}\n\n/**\n * The inverse of {@link assetToDocument}: the schema-driven meta bag a save\n * writes back — every declared field OUTSIDE the system set (EC-301/EC-311),\n * keyed exactly as the record stores it. System columns are upload-managed and\n * never written here.\n */\nexport function documentToAssetMetaPatch(\n collection: CollectionDef,\n doc: CmsDocument,\n): Record<string, unknown> {\n const values = doc.values ?? {}\n const meta: Record<string, unknown> = {}\n for (const field of collection.fields) {\n if (SYSTEM_ASSET_FIELDS.has(field.name)) continue\n if (field.name in values) meta[field.name] = values[field.name]\n }\n return meta\n}\n\n/** `create` when `capabilities.create === false` — never reached from the UI\n * (list/detail never call it); an explicit throw rather than a silent no-op. */\nfunction createUnsupported(): DocumentRef {\n throw new Error('assets are created by upload, not the assets rail')\n}\n\n/** Assets' `remove()` returns an `outcome`-discriminated {@link DeleteAssetResult}\n * (not the `{ ok }` shape users/redirects use, EC-327). `'error'` surfaces as-is;\n * `'blocked'` (the unconfirmed usage gate fired — nothing was removed) surfaces\n * as an explicit failure too (EC-433): a delete that left the asset in place\n * must NEVER look like success. It is unreachable from this rail's UI — every\n * delete path confirms through the reference dialog and dispatches with\n * `confirmInUse` — but stays honest for any direct caller. `'not-found'` maps\n * to success (idempotent delete: the desired end state — gone — holds). */\nexport function normalizeAssetRemoveResult(result: DeleteAssetResult): OperationError | null {\n if (result.outcome === 'error') return { code: 'unknown', message: result.message }\n if (result.outcome === 'blocked') {\n return {\n code: 'unknown',\n message: `Asset is referenced in ${result.usageCount} ${result.usageCount === 1 ? 'place' : 'places'} and was not deleted.`,\n }\n }\n return null\n}\n\n/**\n * How an asset row is addressed in the EC-155 reference index (EC-433):\n * `{ type: 'asset' }`, never the document shape — so the delete confirmation's\n * inbound pre-flight counts the SAME references `loadInboundAssetReferenceViews`\n * (the detail surface's check) does.\n */\nexport function assetReferenceTarget(ref: DocumentRef): ReferenceTarget {\n return { type: 'asset', id: ref.id }\n}\n\n/**\n * Assemble a {@link CollectionViewSource} for the **assets rail** (EC-319) — the\n * portable list surface over the existing asset library store instead of a\n * bespoke listing. `schema` is the DEPLOYMENT-resolved `kind:'asset'` collection\n * (EC-311: a project's own declared fields, or a mount's `nav.assets({ fields })`\n * override merged over it — see `resolveAssetsSchema` in `app-views/default-modules.tsx`),\n * never a hardcoded engine default — so a project's `copyright`/`credit`/… fields\n * (and any EC-311 override) drive the list's columns/filters exactly as they\n * drive the image field's options dialog.\n *\n * Must be mounted under an `<AssetsProvider>` (for {@link useAssets}) — already\n * ambient at the project level (EC-195), so callers rarely mount their own.\n *\n * The shared scaffold (`useRailViewSource`, EC-327) owns `viewState`/`lastError`\n * plumbing and the assembled source; this hook supplies only the asset-store\n * reads/writes, the capability profile, and the projections above.\n */\nexport function useAssetsCollectionViewSource(\n projectId: string,\n schema: CollectionDef,\n): CollectionViewSource {\n const store = useAssets()\n const registry = useMemo(() => createCollectionRegistry([schema]), [schema])\n\n const documents = useCallback(\n () => store.assets.map((record) => assetToDocument(schema, record)),\n [store.assets, schema],\n )\n const get = useCallback(\n (ref: DocumentRef): CmsDocument | undefined => {\n const record = store.assets.find((a) => a.id === ref.id)\n return record ? assetToDocument(schema, record) : undefined\n },\n [store.assets, schema],\n )\n const titleOf = useCallback(\n (ref: DocumentRef): string => {\n const record = store.assets.find((a) => a.id === ref.id)\n return record?.title ?? record?.filename ?? ref.id\n },\n [store.assets],\n )\n const validate = useCallback(\n (doc: CmsDocument) => validateDocument(registry, doc, { localeConfig: ASSETS_LOCALE_CONFIG }),\n [registry],\n )\n const save = useCallback(\n (doc: CmsDocument) => store.updateMetadata(doc.id, { meta: documentToAssetMetaPatch(schema, doc) }),\n [schema, store],\n )\n // The unconfirmed delete (the store's usage gate still applies — a referenced\n // asset comes back `blocked`, surfaced via `lastError`). The UI's delete paths\n // never land here: row/bulk deletes confirm through the reference dialog and\n // dispatch `bulkRemove` below.\n const remove = useCallback((ref: DocumentRef) => store.remove(ref.id), [store])\n // The CONFIRMED delete the bulk/row path dispatches (EC-433): the reference\n // dialog has already enumerated inbound usages (asset-shaped, see\n // `assetReferenceTarget`), so this passes `confirmInUse` exactly like the\n // detail surface's confirmed delete — same blob + thumb + cache handling.\n const bulkRemove = useCallback(\n (ref: DocumentRef) => store.remove(ref.id, { confirmInUse: true }),\n [store],\n )\n\n return useRailViewSource({\n projectId,\n schema,\n capabilities: ASSET_COLLECTION_CAPABILITIES,\n localeConfig: ASSETS_LOCALE_CONFIG,\n documents,\n get,\n titleOf,\n // No `referenceCandidates`/`collections`/`isFieldReadOnly` override: assets carry\n // no relation/asset fields of their own (v1) — nothing cross-collection to\n // resolve — and read-only follows the factory's default `form.readOnly` check.\n detailPath: (id: string) => studioPath.asset(id),\n listPath: studioPath.assets(),\n // EC-433: the delete confirmation must enumerate ASSET-shaped inbound\n // references — the document-shaped default would report \"not referenced\"\n // for a referenced asset.\n referenceTarget: assetReferenceTarget,\n validate,\n create: createUnsupported,\n save,\n remove,\n bulkRemove,\n normalizeSaveResult: normalizeOkResult,\n normalizeRemoveResult: normalizeAssetRemoveResult,\n })\n}\n"]}
1
+ {"version":3,"file":"assets-collection-view-source.js","sourceRoot":"","sources":["../../../src/components/assets/assets-collection-view-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo } from 'react'\nimport type { CmsDocument, CollectionDef, DocumentRef, Locale, LocaleConfig } from '@elytracms/core/cms-core'\nimport { createCollectionRegistry, validateDocument } from '@elytracms/core/cms-core'\nimport type { AssetRecord, ReferenceTarget } from '@elytracms/core/persistence'\nimport type { OperationError } from '@elytracms/core/operations'\nimport { studioPath } from '@elytracms/studio/router'\nimport type { DeleteAssetResult } from '@elytracms/studio/lib/assets'\nimport type { CollectionViewSource, CollectionViewCapabilities } from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport { useAssets } from './assets-store'\nimport { SYSTEM_ASSET_FIELDS } from './asset-metadata'\nimport { normalizeOkResult, useRailViewSource } from '../rail-view-source'\n\n/** The assets rail is not localized in v1 (EC-301: asset metadata is non-localized). */\nconst ASSETS_LOCALE: Locale = 'en'\nconst ASSETS_LOCALE_CONFIG: LocaleConfig = {\n default: ASSETS_LOCALE,\n locales: [ASSETS_LOCALE],\n}\n\n/**\n * The capability profile of the assets rail (EC-319, EC-433). Hoisted to a\n * module-level constant (no prop/state dependency) so it's both the value the\n * hook returns AND independently unit-testable.\n *\n * `create` is off — a new asset comes from an upload, never a blank draft.\n * `remove` + `bulk` are ON since EC-433: the list's row/bulk delete used to be\n * off deliberately because the generic `ReferenceConfirmDialog` enumerated\n * inbound references as `{ type: 'document' }` targets — the wrong index shape\n * for an asset — which would have shown \"not referenced\" for an asset that IS\n * referenced. That pre-flight now queries the shape THIS source declares via\n * {@link assetReferenceTarget} (`{ type: 'asset' }`, the same lookup the\n * detail surface runs), and the confirmed delete dispatches the SAME checked\n * store delete the detail surface uses (`deleteAsset`: original + EC-400 thumb\n * blobs, then the record — EC-420 lqips ride on the record). Nothing else\n * applies to assets: no publish/versions/locales/inline-edit/live-URL/\n * hierarchy/import-export.\n */\nexport const ASSET_COLLECTION_CAPABILITIES: CollectionViewCapabilities = {\n publish: false,\n versions: false,\n locales: false,\n inlineEdit: false,\n create: false,\n duplicate: false, // EC-442: blob duplication is a different feature, out of scope\n remove: true,\n bulk: true,\n liveUrl: false,\n hierarchy: false,\n importExport: false,\n adminOnly: false,\n}\n\n/** Read one of the record's fixed, upload-managed columns by field name. */\nfunction systemFieldValue(record: AssetRecord, name: string): unknown {\n switch (name) {\n case 'filename':\n return record.filename\n case 'url':\n return record.url\n case 'contentType':\n return record.contentType\n case 'byteSize':\n return record.byteSize\n case 'width':\n return record.width\n case 'height':\n return record.height\n case 'storageKey':\n return record.storageKey\n case 'title':\n return record.title\n default:\n return undefined\n }\n}\n\n/**\n * Project a stored asset record into the lens's `values` shape (EC-319), reading\n * only the schema's DECLARED fields — the same \"schema is a lens\" projection the\n * redirects/users rails use. A system column (`filename`, `url`, dimensions, …)\n * reads straight off the record; everything else — the built-in `alt`, a\n * project's own `copyright`/`credit`, or an EC-311 additive field — round-trips\n * through the record's open `meta` bag (EC-301). `alt` is schema-driven but ALSO\n * has a legacy record column: the meta value wins, falling back to the column\n * (matches delivery's `meta.alt ?? record.alt` read and the image field's\n * options dialog).\n */\nexport function assetToDocument(collection: CollectionDef, record: AssetRecord): CmsDocument {\n const values: Record<string, unknown> = {}\n for (const field of collection.fields) {\n if (field.name === 'alt') {\n const metaAlt = typeof record.meta?.alt === 'string' ? record.meta.alt : undefined\n values.alt = metaAlt ?? record.alt\n continue\n }\n values[field.name] = SYSTEM_ASSET_FIELDS.has(field.name)\n ? systemFieldValue(record, field.name)\n : record.meta?.[field.name]\n }\n return { collection: collection.id, id: record.id, values }\n}\n\n/**\n * The inverse of {@link assetToDocument}: the schema-driven meta bag a save\n * writes back — every declared field OUTSIDE the system set (EC-301/EC-311),\n * keyed exactly as the record stores it. System columns are upload-managed and\n * never written here.\n */\nexport function documentToAssetMetaPatch(\n collection: CollectionDef,\n doc: CmsDocument,\n): Record<string, unknown> {\n const values = doc.values ?? {}\n const meta: Record<string, unknown> = {}\n for (const field of collection.fields) {\n if (SYSTEM_ASSET_FIELDS.has(field.name)) continue\n if (field.name in values) meta[field.name] = values[field.name]\n }\n return meta\n}\n\n/** `create` when `capabilities.create === false` — never reached from the UI\n * (list/detail never call it); an explicit throw rather than a silent no-op. */\nfunction createUnsupported(): DocumentRef {\n throw new Error('assets are created by upload, not the assets rail')\n}\n\n/** Assets' `remove()` returns an `outcome`-discriminated {@link DeleteAssetResult}\n * (not the `{ ok }` shape users/redirects use, EC-327). `'error'` surfaces as-is;\n * `'blocked'` (the unconfirmed usage gate fired — nothing was removed) surfaces\n * as an explicit failure too (EC-433): a delete that left the asset in place\n * must NEVER look like success. It is unreachable from this rail's UI — every\n * delete path confirms through the reference dialog and dispatches with\n * `confirmInUse` — but stays honest for any direct caller. `'not-found'` maps\n * to success (idempotent delete: the desired end state — gone — holds). */\nexport function normalizeAssetRemoveResult(result: DeleteAssetResult): OperationError | null {\n if (result.outcome === 'error') return { code: 'unknown', message: result.message }\n if (result.outcome === 'blocked') {\n return {\n code: 'unknown',\n message: `Asset is referenced in ${result.usageCount} ${result.usageCount === 1 ? 'place' : 'places'} and was not deleted.`,\n }\n }\n return null\n}\n\n/**\n * How an asset row is addressed in the EC-155 reference index (EC-433):\n * `{ type: 'asset' }`, never the document shape — so the delete confirmation's\n * inbound pre-flight counts the SAME references `loadInboundAssetReferenceViews`\n * (the detail surface's check) does.\n */\nexport function assetReferenceTarget(ref: DocumentRef): ReferenceTarget {\n return { type: 'asset', id: ref.id }\n}\n\n/**\n * Assemble a {@link CollectionViewSource} for the **assets rail** (EC-319) — the\n * portable list surface over the existing asset library store instead of a\n * bespoke listing. `schema` is the DEPLOYMENT-resolved `kind:'asset'` collection\n * (EC-311: a project's own declared fields, or a mount's `nav.assets({ fields })`\n * override merged over it — see `resolveAssetsSchema` in `app-views/default-modules.tsx`),\n * never a hardcoded engine default — so a project's `copyright`/`credit`/… fields\n * (and any EC-311 override) drive the list's columns/filters exactly as they\n * drive the image field's options dialog.\n *\n * Must be mounted under an `<AssetsProvider>` (for {@link useAssets}) — already\n * ambient at the project level (EC-195), so callers rarely mount their own.\n *\n * The shared scaffold (`useRailViewSource`, EC-327) owns `viewState`/`lastError`\n * plumbing and the assembled source; this hook supplies only the asset-store\n * reads/writes, the capability profile, and the projections above.\n */\nexport function useAssetsCollectionViewSource(\n projectId: string,\n schema: CollectionDef,\n): CollectionViewSource {\n const store = useAssets()\n const registry = useMemo(() => createCollectionRegistry([schema]), [schema])\n\n const documents = useCallback(\n () => store.assets.map((record) => assetToDocument(schema, record)),\n [store.assets, schema],\n )\n const get = useCallback(\n (ref: DocumentRef): CmsDocument | undefined => {\n const record = store.assets.find((a) => a.id === ref.id)\n return record ? assetToDocument(schema, record) : undefined\n },\n [store.assets, schema],\n )\n const titleOf = useCallback(\n (ref: DocumentRef): string => {\n const record = store.assets.find((a) => a.id === ref.id)\n return record?.title ?? record?.filename ?? ref.id\n },\n [store.assets],\n )\n const validate = useCallback(\n (doc: CmsDocument) => validateDocument(registry, doc, { localeConfig: ASSETS_LOCALE_CONFIG }),\n [registry],\n )\n const save = useCallback(\n (doc: CmsDocument) => store.updateMetadata(doc.id, { meta: documentToAssetMetaPatch(schema, doc) }),\n [schema, store],\n )\n // The unconfirmed delete (the store's usage gate still applies — a referenced\n // asset comes back `blocked`, surfaced via `lastError`). The UI's delete paths\n // never land here: row/bulk deletes confirm through the reference dialog and\n // dispatch `bulkRemove` below.\n const remove = useCallback((ref: DocumentRef) => store.remove(ref.id), [store])\n // The CONFIRMED delete the bulk/row path dispatches (EC-433): the reference\n // dialog has already enumerated inbound usages (asset-shaped, see\n // `assetReferenceTarget`), so this passes `confirmInUse` exactly like the\n // detail surface's confirmed delete — same blob + thumb + cache handling.\n const bulkRemove = useCallback(\n (ref: DocumentRef) => store.remove(ref.id, { confirmInUse: true }),\n [store],\n )\n\n return useRailViewSource({\n projectId,\n schema,\n capabilities: ASSET_COLLECTION_CAPABILITIES,\n localeConfig: ASSETS_LOCALE_CONFIG,\n documents,\n get,\n titleOf,\n // No `referenceCandidates`/`collections`/`isFieldReadOnly` override: assets carry\n // no relation/asset fields of their own (v1) — nothing cross-collection to\n // resolve — and read-only follows the factory's default `form.readOnly` check.\n detailPath: (id: string) => studioPath.asset(id),\n listPath: studioPath.assets(),\n // EC-433: the delete confirmation must enumerate ASSET-shaped inbound\n // references — the document-shaped default would report \"not referenced\"\n // for a referenced asset.\n referenceTarget: assetReferenceTarget,\n validate,\n create: createUnsupported,\n save,\n remove,\n bulkRemove,\n normalizeSaveResult: normalizeOkResult,\n normalizeRemoveResult: normalizeAssetRemoveResult,\n })\n}\n"]}
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Link, STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router';
3
- import { ArrowDown, ArrowUp, CornerDownRight, Download, Eye, EyeOff, ExternalLink, MoreHorizontal, Pencil, Plus, Trash2, Upload, } from 'lucide-react';
3
+ import { ArrowDown, ArrowUp, Copy, CornerDownRight, Download, Eye, EyeOff, ExternalLink, MoreHorizontal, Pencil, Plus, Trash2, Upload, } from 'lucide-react';
4
4
  import { useMemo, useState } from 'react';
5
5
  import { orderHierarchically, resolveDocumentForLocale, resolveDocumentTitle, validateCollectionShape, } from '@elytracms/core/cms-core';
6
- import { Button, Code, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Row, Separator, Spacer, Stack, Text, } from '@elytracms/studio/ui';
6
+ import { Button, Code, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Row, Separator, Spacer, Stack, Text, toast, } from '@elytracms/studio/ui';
7
7
  import { cn } from '@elytracms/studio/lib/utils';
8
8
  import { localizedLabel, translate } from '@elytracms/studio/lib/i18n';
9
9
  import { LocaleSwitcher, resolveDisplayedValue } from '@elytracms/studio/lib/locale';
@@ -19,6 +19,7 @@ import { cmsIssueLabel } from '@elytracms/studio/lib/persona/labels';
19
19
  import { TriangleAlert } from 'lucide-react';
20
20
  import { BulkBar, BulkReportPanel } from './bulk-bar.js';
21
21
  import { rowActionsFor } from './row-actions.js';
22
+ import { duplicateFromSource } from './duplicate-document.js';
22
23
  import { CHECKBOX_COLUMN_WIDTH_REM, PRIMARY_COLUMN_WIDTH_REM, ROW_ACTIONS_COLUMN_WIDTH_REM, STATE_COLUMN_WIDTH_REM, URL_COLUMN_WIDTH_REM, columnWidthRem, } from './column-width.js';
23
24
  import { ReferenceConfirmDialog } from './reference-confirm.js';
24
25
  import { FilterControl } from './filter-builder.js';
@@ -167,6 +168,20 @@ export function LoadedCollectionTable({ projectId, toolbarExtra, }) {
167
168
  navigate({ to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: splat } });
168
169
  return;
169
170
  }
171
+ // EC-442: duplicate the row's draft into a fresh DRAFT document (never
172
+ // auto-published) through the seam's existing create+save write paths,
173
+ // then land on the new draft's editor — the same navigation `+ New` takes.
174
+ // The action is offered iff `capabilities.create` + a detail surface
175
+ // (`rowActionsFor`), the New button's exact gate.
176
+ if (action === 'duplicate') {
177
+ const newRef = duplicateFromSource(source, doc);
178
+ toast.success(translate('cms.editor.duplicateCreated'));
179
+ const splat = source.detailPath?.(newRef.id);
180
+ if (splat === undefined)
181
+ return;
182
+ navigate({ to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: splat } });
183
+ return;
184
+ }
170
185
  if (action === 'publish') {
171
186
  void handleBulk(action, [doc.id]);
172
187
  return;
@@ -290,6 +305,8 @@ function RowActionItem({ action, onSelect }) {
290
305
  switch (action) {
291
306
  case 'open':
292
307
  return (_jsxs(DropdownMenuItem, { onClick: onSelect, "data-testid": "row-action-open", children: [_jsx(Pencil, {}), " ", translate('cms.list.rowAction.open')] }));
308
+ case 'duplicate':
309
+ return (_jsxs(DropdownMenuItem, { onClick: onSelect, "data-testid": "row-action-duplicate", children: [_jsx(Copy, {}), " ", translate('common.duplicate')] }));
293
310
  case 'publish':
294
311
  return (_jsxs(DropdownMenuItem, { onClick: onSelect, "data-testid": "row-action-publish", children: [_jsx(Eye, {}), " ", translate('cms.list.bulk.publish')] }));
295
312
  case 'unpublish':
@@ -1 +1 @@
1
- {"version":3,"file":"collection-table.js","sourceRoot":"","sources":["../../../src/components/cms/collection-table.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport {\n ArrowDown,\n ArrowUp,\n CornerDownRight,\n Download,\n Eye,\n EyeOff,\n ExternalLink,\n MoreHorizontal,\n Pencil,\n Plus,\n Trash2,\n Upload,\n} from 'lucide-react'\nimport { useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { CmsDocument, CollectionDef, DocumentUrl, FieldDef, Locale, LocaleConfig } from '@elytracms/core/cms-core'\nimport {\n orderHierarchically,\n resolveDocumentForLocale,\n resolveDocumentTitle,\n validateCollectionShape,\n} from '@elytracms/core/cms-core'\nimport {\n Button,\n Code,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Row,\n Separator,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { LocaleSwitcher, resolveDisplayedValue } from '@elytracms/studio/lib/locale'\nimport {\n EMPTY_SELECTION,\n STATE_FIELD,\n applyViewState,\n clearSelection,\n primaryColumn,\n selectAll,\n toggleRow,\n visibleColumns,\n visibleSelection,\n} from '@elytracms/studio/lib/cms-views'\nimport type { SelectionState, ViewSort } from '@elytracms/studio/lib/cms-views'\nimport { cellEditKind, cellKey } from './cell-edit'\nimport { FallbackChip } from './cell'\nimport { EditableCell } from './editable-cell'\nimport { useCellEdit } from './use-cell-edit'\nimport { ImportDialog } from './import-export'\nimport { AssetThumb, useAssetRecord } from '../assets/asset-thumb'\nimport { fieldLabel } from './cms-labels'\nimport { cmsIssueLabel } from '@elytracms/studio/lib/persona/labels'\nimport { TriangleAlert } from 'lucide-react'\nimport type { BulkAction, BulkReport, BulkSetField } from './bulk-actions'\nimport { BulkBar, BulkReportPanel } from './bulk-bar'\nimport { rowActionsFor } from './row-actions'\nimport type { RowAction } from './row-actions'\nimport {\n CHECKBOX_COLUMN_WIDTH_REM,\n PRIMARY_COLUMN_WIDTH_REM,\n ROW_ACTIONS_COLUMN_WIDTH_REM,\n STATE_COLUMN_WIDTH_REM,\n URL_COLUMN_WIDTH_REM,\n columnWidthRem,\n} from './column-width'\nimport { ReferenceConfirmDialog } from './reference-confirm'\nimport { FilterControl } from './filter-builder'\nimport { DisplayMenu, ViewTabs } from './view-bar'\nimport { DocStateBadge } from './doc-state'\nimport { useSetBreadcrumb } from '../breadcrumb-context'\nimport { useCmsCollectionViewSource } from '../collection-view/cms-collection-view-source'\nimport { CollectionViewProvider, useCollectionView } from '../collection-view/collection-view-context'\n\nexport function CollectionTable({ projectId, collectionId }: { projectId: string; collectionId: string }) {\n // AD-19: render over the portable CollectionViewSource, not `useCms()` directly,\n // so the SAME list surface serves the users rail (EC-240), redirects (EC-303), …\n const source = useCmsCollectionViewSource(collectionId)\n\n if (!source) {\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Text tone=\"muted\">\n Unknown collection <Code tone=\"muted\">{collectionId}</Code>.\n </Text>\n </div>\n )\n }\n\n // Keyed per collection so selection/report state never leaks across collections.\n return (\n <CollectionViewProvider source={source}>\n <LoadedCollectionTable key={collectionId} projectId={projectId} />\n </CollectionViewProvider>\n )\n}\n\n/**\n * The portable list surface (AD-19). Renders entirely over the ambient\n * {@link useCollectionView} source — no `useCms`, no `CmsProvider` required — so\n * any rail (the users rail, redirects, …) reuses it by wrapping it in a\n * {@link CollectionViewProvider} over its own source. `CollectionTable` is just\n * the CMS entry point that builds a CMS source and mounts this.\n */\nexport function LoadedCollectionTable({\n projectId,\n toolbarExtra,\n}: {\n projectId: string\n /**\n * A rail-owned action rendered at the tail of the header toolbar (EC-319) —\n * e.g. the assets rail's Upload button + grid/list toggle. Absent for every\n * other rail today (users, redirects, CMS), so their toolbar is unchanged.\n */\n toolbarExtra?: ReactNode\n}) {\n const source = useCollectionView()\n const collection = source.schema\n const navigate = useNavigate()\n const collectionId = source.collectionId\n // EC-214: the collection is the active record — the global header breadcrumb\n // names it, replacing the per-view title heading.\n useSetBreadcrumb([{ label: localizedLabel(collection.form?.label, collectionId) }])\n const [importOpen, setImportOpen] = useState(false)\n const [selection, setSelection] = useState<SelectionState>(EMPTY_SELECTION)\n const [bulkBusy, setBulkBusy] = useState(false)\n const [bulkReport, setBulkReport] = useState<BulkReport | null>(null)\n // EC-173: bulk unpublish/delete go through the reference-aware confirmation\n // with a snapshot of the selection taken when the action was raised.\n const [pendingBulk, setPendingBulk] = useState<{\n action: 'unpublish' | 'delete'\n targets: { collection: string; id: string }[]\n } | null>(null)\n\n // The working view state (EC-169): filters + sort + columns, possibly dirty\n // against the active saved view. The table renders exactly this state.\n const viewState = source.viewState()\n // EC-213: the title/link column is the first visible column; the rest follow\n // in the view's column order. No separate primary-column selector.\n const primary = primaryColumn(collection, viewState)\n const columns = visibleColumns(collection, viewState).filter((name) => name !== primary)\n const primaryFieldDef = collection.fields.find((f) => f.name === primary)\n\n const docs = source.documents()\n\n // EC-274/EC-284: collection-shape drift (a singleton with > 1 document; later\n // duplicate/unknown/unfilled aliases). When the data has drifted from the lens's\n // expected shape the studio shows this list instead of skipping — surfacing the\n // conflicts here as a \"for review\" banner so an editor resolves them without a\n // developer (keep/delete the outliers), after which the lens applies again.\n const shapeIssues = useMemo(() => validateCollectionShape(collection, docs), [collection, docs])\n\n // EC-216 + EC-205 (\"live = published\"): each routable document's LIVE URL,\n // derived from the PUBLISHED pool (never draft rows), memoised inside the\n // source. `null` when the collection is not routable → the column is hidden.\n const urlByDocId = source.publishing?.liveUrls?.() ?? null\n\n const visibleDocs = useMemo(\n // EC-224: the `$state` filter/sort sources published-ness from the version\n // POINTER (`source.publishing.isPublished`), not a flat `state` field.\n () =>\n applyViewState(collection, docs, source.localeConfig, viewState, (doc) =>\n source.publishing?.isPublished({ collection: doc.collection, id: doc.id }) ?? false,\n ).docs,\n [collection, docs, source.localeConfig, viewState, source.publishing],\n )\n\n // EC-218: a collection with a `parent` self-relation can render as a nested\n // tree (parents above their children, indented). Tree mode reorders the\n // already-filtered rows depth-first — filters/sort still order siblings.\n const hierarchical = source.capabilities.hierarchy\n const [treeMode, setTreeMode] = useState(hierarchical)\n const treeRows = useMemo(\n () => (hierarchical && treeMode ? orderHierarchically(visibleDocs) : null),\n [hierarchical, treeMode, visibleDocs],\n )\n const orderedDocs = treeRows ? treeRows.map((row) => row.doc) : visibleDocs\n const depthById = useMemo(\n () => (treeRows ? new Map(treeRows.map((row) => [row.doc.id, row.depth])) : undefined),\n [treeRows],\n )\n\n // EC-246a header derivations. The count reads with the collection's own noun\n // (pluralized via the config labels), not a bare number or the generic kind.\n const pluralLabel = localizedLabel(collection.form?.label, `${collectionId}s`)\n const singularLabel = localizedLabel(collection.form?.labelSingular, collectionId)\n const countLabel =\n visibleDocs.length === docs.length\n ? translate('cms.list.count', {\n count: docs.length,\n noun: docs.length === 1 ? singularLabel : pluralLabel,\n })\n : translate('cms.list.countFiltered', {\n visible: visibleDocs.length,\n total: docs.length,\n noun: pluralLabel,\n })\n\n // EC-246d: New creates an empty draft and opens its editor directly — no\n // interstitial modal, for pages or any other collection. Id is auto-generated;\n // the slug/title (and, for pages, the parent) are typed in the form, and a\n // missing/duplicate slug surfaces as a Publish-gating issue, never blocks the\n // draft. Routes are config-sourced (EC-207), so nothing else is written here.\n const isPageCollection = collectionId === 'page'\n // Creation is a capability (a credential rail like users mints identities via\n // sign-up, not here), and the detail route is rail-owned (never the CMS route).\n const canCreate = source.capabilities.create && source.detailPath !== undefined\n const handleNew = () => {\n const ref = source.create()\n const splat = source.detailPath?.(ref.id)\n if (splat === undefined) return\n navigate({ to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: splat } })\n }\n const visibleIds = orderedDocs.map((doc) => doc.id)\n // Selection is always read against the current view — rows the filters hid\n // are never silently included in a bulk action.\n const selectedIds = visibleSelection(selection, visibleIds)\n // EC-316: the right-edge row menu is a capability-gated column — a rail with\n // no detail surface AND no publish/remove capability offers nothing (users\n // rail today has remove, so this is rarely empty; kept honest for AD-19).\n const hasRowActions = source.detailPath !== undefined || source.capabilities.publish || source.capabilities.remove\n\n function toggleSort(field: string) {\n source.setViewState((prev) => {\n const sort: ViewSort | null =\n prev.sort?.field !== field\n ? { field, dir: 'asc' }\n : prev.sort.dir === 'asc'\n ? { field, dir: 'desc' }\n : null\n return { ...prev, sort }\n })\n }\n\n function raiseBulk(action: BulkAction) {\n if (action === 'publish') {\n void handleBulk(action, [...selectedIds])\n return\n }\n // Set-field is composed + raised through the bulk bar's own panel\n // (onSetField), never this state-action path.\n if (action === 'set-field') return\n // Unpublish/delete first enumerate inbound references (EC-173): the\n // dialog shows the aggregate \"N documents, M references will break\".\n setPendingBulk({\n action,\n targets: selectedIds.map((id) => ({ collection: collectionId, id })),\n })\n }\n\n // EC-316: the row `⋮` menu dispatches through the SAME machinery as the\n // toolbar's bulk actions — one document is just a selection of one — so\n // there is exactly one write path for publish/unpublish/delete, never a\n // parallel one. `open` is the row menu's own affordance (the primary-column\n // link already offers it; this is the keyboard/menu-driven equivalent).\n function handleRowAction(action: RowAction, doc: CmsDocument) {\n if (action === 'open') {\n const splat = source.detailPath?.(doc.id)\n if (splat === undefined) return\n navigate({ to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: splat } })\n return\n }\n if (action === 'publish') {\n void handleBulk(action, [doc.id])\n return\n }\n // unpublish/delete: the same reference-aware confirmation the bulk bar\n // raises, scoped to this one document.\n setPendingBulk({ action, targets: [{ collection: collectionId, id: doc.id }] })\n }\n\n async function handleBulk(action: BulkAction, acted: readonly string[], setField?: BulkSetField) {\n // Defensive: with the capability gates below (`source.bulk` presence gates\n // the bar, per-action capabilities gate its buttons) this is unreachable —\n // but a confirmed action must NEVER silently no-op (EC-433: the assets\n // rail's bulk delete used to die exactly here), so a missing runner still\n // reports one honest failure per item.\n if (!source.bulk) {\n setBulkReport({\n action,\n ...(setField ? { setField } : {}),\n results: acted.map((id) => ({\n ref: { collection: collectionId, id },\n title: source.titleOf({ collection: collectionId, id }),\n ok: false,\n reason: 'Bulk actions are not available in this view.',\n error: { code: 'unknown', message: 'Bulk actions are not available in this view.' },\n })),\n })\n return\n }\n setBulkBusy(true)\n try {\n const report = await source.bulk([...acted], action, setField)\n setBulkReport(report)\n // Succeeded items deselect; failed items stay selected for a retry.\n const actedSet = new Set(acted)\n const failed = new Set(report.results.filter((r) => !r.ok).map((r) => r.ref.id))\n setSelection((prev) => ({\n ids: prev.ids.filter((id) => !actedSet.has(id) || failed.has(id)),\n anchorId: null,\n }))\n } finally {\n setBulkBusy(false)\n }\n }\n\n function exportJson() {\n const data = JSON.stringify(source.documents(), null, 2)\n const blob = new Blob([data], { type: 'application/json' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = `${collectionId}.json`\n a.click()\n URL.revokeObjectURL(url)\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n {/* Header (EC-246a redesign). One toolbar: identity + refine on the left,\n lens + actions on the right; a transient filter bar drops below only\n while refining. The header breadcrumb (EC-214) names the collection —\n no title here. Locale is a lens; column/layout config lives behind the\n far-right Display control; shared views are config-as-code (EC-246b). */}\n <div className=\"shrink-0 border-b border-border px-4 py-3 sm:px-6\">\n <Row gap=\"2\" wrap>\n {/* Left — which view (only when >1), then the transient refine. */}\n <ViewTabs />\n {/* Transient ad-hoc refine — a chip beside the view tabs, editor in a\n popover (EC-246b); the chip's ✕ clears it, so no separate Reset. */}\n <FilterControl collection={collection} />\n {/* The working-set count is the RESULT of the view + filter above, so it\n reads last, set off by a divider and sized like its neighbours. */}\n <Separator orientation=\"vertical\" className=\"h-5 self-center\" />\n <Text\n as=\"span\"\n size=\"xs\"\n tone=\"muted\"\n className=\"whitespace-nowrap tabular-nums\"\n data-testid=\"collection-count\"\n >\n {countLabel}\n </Text>\n\n <Spacer />\n\n {/* Right — the ambient locale lens (EC-176; hidden for single-locale AND\n for a non-localized rail like users), then display config + write actions. */}\n {source.capabilities.locales ? <LocaleSwitcher /> : null}\n <DisplayMenu\n collection={collection}\n hierarchical={hierarchical}\n treeMode={treeMode}\n onToggleTree={setTreeMode}\n />\n {canCreate ? (\n <Button size=\"sm\" onClick={handleNew} data-testid=\"new-document\">\n <Plus /> {isPageCollection ? translate('cms.list.newPage') : translate('cms.list.new')}\n </Button>\n ) : null}\n {/* EC-213: import/export fold into one action menu, off the header.\n AD-19: a capability — absent on credential rails (users), so identity\n records are never dumped to / loaded from raw JSON. */}\n {source.capabilities.importExport ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon-sm\"\n aria-label={translate('cms.list.moreActions')}\n data-testid=\"collection-actions\"\n >\n <MoreHorizontal />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={() => setImportOpen(true)} data-testid=\"action-import\">\n <Upload /> {translate('cms.list.import')}\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={exportJson}\n disabled={docs.length === 0}\n data-testid=\"action-export\"\n >\n <Download /> {translate('cms.list.export')}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n {toolbarExtra}\n </Row>\n </div>\n\n {/* Bulk actions over the selection (EC-169). Mounted only when the source\n actually carries a bulk runner (present iff `capabilities.bulk`,\n EC-433) and each action button is capability-gated inside — a bar\n whose confirmed actions can't dispatch is never offered. */}\n {source.bulk ? (\n <BulkBar\n collection={collection}\n capabilities={source.capabilities}\n count={selectedIds.length}\n busy={bulkBusy}\n onAction={raiseBulk}\n onSetField={(setField) => void handleBulk('set-field', [...selectedIds], setField)}\n onClear={() => setSelection(clearSelection())}\n />\n ) : null}\n {bulkReport ? (\n <BulkReportPanel report={bulkReport} onDismiss={() => setBulkReport(null)} />\n ) : null}\n\n {/* Reference-aware bulk unpublish/delete confirmation (EC-173). */}\n {pendingBulk !== null ? (\n <ReferenceConfirmDialog\n projectId={projectId}\n action={pendingBulk.action}\n targets={pendingBulk.targets}\n open\n busy={bulkBusy}\n onOpenChange={(open) => {\n if (!open) setPendingBulk(null)\n }}\n onConfirm={() => {\n const { action, targets } = pendingBulk\n setPendingBulk(null)\n void handleBulk(\n action,\n targets.map((target) => target.id),\n )\n }}\n />\n ) : null}\n\n {/* EC-274/EC-284: \"for review\" lane — the collection has drifted from its\n lens's expected shape; surface each conflict so an editor can resolve it. */}\n {shapeIssues.length > 0 ? (\n <div\n className=\"shrink-0 border-b border-warning/30 bg-warning/10 px-4 py-2 sm:px-6\"\n data-testid=\"shape-review-banner\"\n >\n <Stack gap=\"1\">\n {shapeIssues.map((issue, i) => (\n <Row key={`${issue.code}:${i}`} gap=\"2\" align=\"start\">\n <TriangleAlert className=\"mt-0.5 size-3.5 shrink-0 text-warning\" aria-hidden />\n <Stack gap=\"none\" className=\"min-w-0\">\n <Text size=\"xs\" weight=\"semibold\" tone=\"default\" title={issue.code}>\n {translate('cms.list.forReview')} · {cmsIssueLabel(issue.code)}\n </Text>\n <Text size=\"xs\" tone=\"muted\">\n {issue.message}\n </Text>\n </Stack>\n </Row>\n ))}\n </Stack>\n </div>\n ) : null}\n\n {/* Table */}\n <div className=\"min-h-0 flex-1 overflow-auto\">\n {docs.length === 0 ? (\n <div className=\"grid h-full place-items-center p-8\">\n <Stack gap=\"2\" align=\"center\" className=\"text-center\">\n <Text tone=\"muted\">{translate('cms.list.emptyNone', { noun: pluralLabel.toLowerCase() })}</Text>\n {canCreate ? (\n <Button size=\"sm\" onClick={handleNew}>\n <Plus />{' '}\n {isPageCollection\n ? translate('cms.list.newPage')\n : translate('cms.list.newNamed', {\n noun: localizedLabel(collection.form?.labelSingular, 'document'),\n })}\n </Button>\n ) : null}\n </Stack>\n </div>\n ) : visibleDocs.length === 0 ? (\n <div className=\"grid h-full place-items-center p-8\">\n <Text tone=\"muted\" data-testid=\"empty-filtered\">\n {translate('cms.list.emptyFiltered')}\n </Text>\n </div>\n ) : (\n <DocumentsGrid\n projectId={projectId}\n collection={collection}\n primary={primary}\n primaryFieldDef={primaryFieldDef}\n columns={columns}\n sortedDocs={orderedDocs}\n depthById={depthById}\n urlByDocId={urlByDocId}\n sort={viewState.sort}\n onSort={toggleSort}\n selectedIds={selectedIds}\n onToggleRow={(id, shift) =>\n setSelection((prev) => toggleRow(prev, visibleIds, id, shift))\n }\n onToggleAll={() =>\n setSelection(\n selectedIds.length === visibleIds.length && visibleIds.length > 0\n ? clearSelection()\n : selectAll(visibleIds),\n )\n }\n hasRowActions={hasRowActions}\n onRowAction={handleRowAction}\n />\n )}\n </div>\n\n {source.capabilities.importExport ? (\n <ImportDialog collectionId={collectionId} open={importOpen} onOpenChange={setImportOpen} />\n ) : null}\n </div>\n )\n}\n\n/**\n * The documents table with inline cell editing (EC-168). Cells edit in place\n * (text/number/boolean/select/date) or open pickers (relation/asset); each\n * commit is one operations command via the cell-edit controller, with\n * rollback + inline cell errors on rejection. Row checkboxes (EC-169) live in\n * their own column, so selection clicks never collide with cell editing.\n */\nfunction DocumentsGrid({\n projectId,\n collection,\n primary,\n primaryFieldDef,\n columns,\n sortedDocs,\n depthById,\n urlByDocId,\n sort,\n onSort,\n selectedIds,\n onToggleRow,\n onToggleAll,\n hasRowActions,\n onRowAction,\n}: {\n projectId: string\n collection: CollectionDef\n primary: string\n primaryFieldDef: FieldDef | undefined\n columns: string[]\n sortedDocs: CmsDocument[]\n /** EC-218: nesting depth per doc id when rendering the hierarchy (tree mode). */\n depthById?: Map<string, number>\n /** EC-216: each routable doc's derived URL; `null` hides the URL column. */\n urlByDocId?: Map<string, DocumentUrl> | null\n sort: ViewSort | null\n onSort: (field: string) => void\n selectedIds: readonly string[]\n onToggleRow: (id: string, shift: boolean) => void\n onToggleAll: () => void\n /** EC-316: whether the right-edge row `⋮` menu has anything to offer at all. */\n hasRowActions: boolean\n onRowAction: (action: RowAction, doc: CmsDocument) => void\n}) {\n const source = useCollectionView()\n const collectionId = collection.id\n // EC-346: a rail with no publish lifecycle (assets/redirects/users) has no\n // state to show — the column is omitted entirely (header + cells), never a\n // badge defaulted to \"draft\" via `?? false`. `source.publishing` presence IS\n // the gate (mirrors the capability contract: the group is present iff the\n // flag is set), narrowed once here so every render site below is type-safe.\n const publishing = source.publishing\n const fieldByName = (name: string) => collection.fields.find((f) => f.name === name)\n // Inline editing is a capability: a read-only rail yields no editable cells.\n const editableFields = source.capabilities.inlineEdit\n ? columns.filter((name) => {\n const field = fieldByName(name)\n return field ? cellEditKind(field, source.isFieldReadOnly(field)) !== 'none' : false\n })\n : []\n const cellEdit = useCellEdit({ collection, docs: sortedDocs, editableFields })\n const selected = new Set(selectedIds)\n const allSelected = sortedDocs.length > 0 && selectedIds.length === sortedDocs.length\n\n return (\n <div ref={cellEdit.containerRef}>\n {/* EC-316: `table-fixed` + an explicit `<colgroup>` fix every column's\n width from the SCHEMA alone, before any row renders. Under the\n browser's default `table-layout: auto` a cell's INTRINSIC content\n width feeds back into its column — so swapping a cell's rendered\n value for its inline editor (a wider `<input>`) reflowed the whole\n grid. Fixed layout makes a column's width immune to its cells'\n content; `columnWidthRem` only sets relative proportions, so the\n grid still scales naturally with the container (never with a cell). */}\n <table className=\"w-full table-fixed border-collapse text-sm\">\n <colgroup>\n <col style={{ width: `${CHECKBOX_COLUMN_WIDTH_REM}rem` }} />\n <col style={{ width: `${PRIMARY_COLUMN_WIDTH_REM}rem` }} />\n {columns.map((name) => (\n <col key={name} style={{ width: `${columnWidthRem(fieldByName(name))}rem` }} />\n ))}\n {publishing ? <col style={{ width: `${STATE_COLUMN_WIDTH_REM}rem` }} /> : null}\n {urlByDocId ? <col style={{ width: `${URL_COLUMN_WIDTH_REM}rem` }} /> : null}\n {hasRowActions ? <col style={{ width: `${ROW_ACTIONS_COLUMN_WIDTH_REM}rem` }} /> : null}\n </colgroup>\n <thead className=\"sticky top-0 z-10 bg-background\">\n <tr className=\"border-b border-border\">\n <th className=\"px-3 py-2 text-left\">\n <SelectionCheckbox\n checked={allSelected}\n indeterminate={selectedIds.length > 0 && !allSelected}\n label={translate('cms.list.selectAll')}\n testId=\"select-all\"\n onToggle={() => onToggleAll()}\n />\n </th>\n <HeaderCell\n field={primary}\n label={primaryFieldDef ? fieldLabel(primaryFieldDef) : translate('cms.list.column.title')}\n sort={sort}\n onSort={onSort}\n />\n {columns.map((name) => {\n const field = fieldByName(name)\n return field ? (\n <HeaderCell key={name} field={name} label={fieldLabel(field)} sort={sort} onSort={onSort} />\n ) : null\n })}\n {publishing ? (\n <HeaderCell field={STATE_FIELD} label={translate('cms.list.column.state')} sort={sort} onSort={onSort} />\n ) : null}\n {/* EC-216: derived URL — not a field, so not sortable. */}\n {urlByDocId ? (\n <th className=\"px-3 py-2 text-left text-xs font-semibold text-muted-foreground\">{translate('cms.list.column.url')}</th>\n ) : null}\n {hasRowActions ? (\n <th className=\"px-2 py-2 text-right\">\n <span className=\"sr-only\">{translate('cms.list.rowActionsColumnLabel')}</span>\n </th>\n ) : null}\n </tr>\n </thead>\n <tbody>\n {sortedDocs.map((doc) => (\n <tr\n key={doc.id}\n className={cn(\n 'group border-b border-border/60 hover:bg-muted/40',\n selected.has(doc.id) && 'bg-primary/5',\n )}\n >\n <td className=\"px-3 py-2 align-middle\">\n <SelectionCheckbox\n checked={selected.has(doc.id)}\n label={translate('cms.list.selectRow', { id: doc.id })}\n testId={`select-row-${doc.id}`}\n onToggle={(shift) => onToggleRow(doc.id, shift)}\n />\n </td>\n <td className=\"px-3 py-2 align-middle\">\n <PrimaryCell\n projectId={projectId}\n collection={collection}\n doc={doc}\n primary={primary}\n depth={depthById?.get(doc.id) ?? 0}\n locale={source.activeLocale}\n config={source.localeConfig}\n detailPath={source.detailPath}\n />\n </td>\n {columns.map((name) => {\n const field = fieldByName(name)\n const coord = { docId: doc.id, field: name }\n const key = cellKey(coord)\n return (\n <td key={name} className=\"px-3 py-2 align-middle\">\n {field ? (\n <EditableCell\n collection={collection}\n doc={doc}\n field={field}\n editing={cellEdit.activeKey === key}\n busy={cellEdit.busyKey === key}\n errorMessage={cellEdit.errorOf(key)}\n onStartEdit={() => cellEdit.start(coord)}\n onCancel={() => cellEdit.cancel(coord)}\n onCommit={(value, advance) => cellEdit.commit(coord, value, advance)}\n />\n ) : null}\n </td>\n )\n })}\n {publishing ? (\n <td className=\"px-3 py-2 align-middle\">\n <DocStateBadge published={publishing.isPublished({ collection: collectionId, id: doc.id })} />\n </td>\n ) : null}\n {urlByDocId ? (\n <td className=\"px-3 py-2 align-middle\">\n <UrlCell url={urlByDocId.get(doc.id)} />\n </td>\n ) : null}\n {hasRowActions ? (\n <td className=\"px-2 py-2 text-right align-middle\">\n <RowActionsMenu\n doc={doc}\n published={source.publishing?.isPublished({ collection: collectionId, id: doc.id }) ?? false}\n hasDetailPath={source.detailPath !== undefined}\n onAction={(action) => onRowAction(action, doc)}\n />\n </td>\n ) : null}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\n/**\n * The right-edge row `⋮` menu (EC-316): the SAME action vocabulary as the\n * toolbar's bulk actions (`rowActionsFor`), gated by the SAME capabilities —\n * so it degrades exactly like the rest of the list for a rail without\n * publish/remove (AD-19). Renders nothing when the gate leaves zero actions.\n */\nfunction RowActionsMenu({\n doc,\n published,\n hasDetailPath,\n onAction,\n}: {\n doc: CmsDocument\n published: boolean\n hasDetailPath: boolean\n onAction: (action: RowAction) => void\n}) {\n const source = useCollectionView()\n const actions = rowActionsFor({ capabilities: source.capabilities, hasDetailPath, published })\n if (actions.length === 0) return null\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={translate('cms.list.rowActionsLabel', { id: doc.id })}\n data-testid={`row-actions-${doc.id}`}\n >\n <MoreHorizontal />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {actions.map((action) => (\n <RowActionItem key={action} action={action} onSelect={() => onAction(action)} />\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\nfunction RowActionItem({ action, onSelect }: { action: RowAction; onSelect: () => void }) {\n switch (action) {\n case 'open':\n return (\n <DropdownMenuItem onClick={onSelect} data-testid=\"row-action-open\">\n <Pencil /> {translate('cms.list.rowAction.open')}\n </DropdownMenuItem>\n )\n case 'publish':\n return (\n <DropdownMenuItem onClick={onSelect} data-testid=\"row-action-publish\">\n <Eye /> {translate('cms.list.bulk.publish')}\n </DropdownMenuItem>\n )\n case 'unpublish':\n return (\n <DropdownMenuItem onClick={onSelect} data-testid=\"row-action-unpublish\">\n <EyeOff /> {translate('cms.list.bulk.unpublish')}\n </DropdownMenuItem>\n )\n case 'delete':\n return (\n <DropdownMenuItem\n variant=\"destructive\"\n onClick={onSelect}\n data-testid=\"row-action-delete\"\n >\n <Trash2 /> {translate('common.delete')}\n </DropdownMenuItem>\n )\n }\n}\n\n/**\n * A selection checkbox (row or select-all). Its own column and focus target —\n * it never participates in the cell-edit keyboard flow. Shift-click selects\n * the anchor→row range (the mousedown guard stops text selection).\n */\nfunction SelectionCheckbox({\n checked,\n indeterminate = false,\n label,\n testId,\n onToggle,\n}: {\n checked: boolean\n indeterminate?: boolean\n label: string\n testId: string\n onToggle: (shift: boolean) => void\n}) {\n return (\n <input\n ref={(el) => {\n if (el) el.indeterminate = indeterminate && !checked\n }}\n type=\"checkbox\"\n className=\"size-4 cursor-pointer accent-primary align-middle\"\n checked={checked}\n readOnly\n aria-label={label}\n data-testid={testId}\n onClick={(event) => onToggle(event.shiftKey)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n onToggle(event.shiftKey)\n }\n }}\n onMouseDown={(event) => {\n if (event.shiftKey) event.preventDefault()\n }}\n />\n )\n}\n\nfunction HeaderCell({\n field,\n label,\n sort,\n onSort,\n}: {\n field: string\n label: string\n sort: ViewSort | null\n onSort: (field: string) => void\n}) {\n const active = sort?.field === field\n return (\n <th className=\"px-3 py-2 text-left text-xs font-semibold text-muted-foreground\">\n <button type=\"button\" onClick={() => onSort(field)} className=\"inline-flex items-center gap-1 hover:text-foreground\">\n {label}\n {active ? (\n sort!.dir === 'asc' ? (\n <ArrowUp className=\"size-3\" />\n ) : (\n <ArrowDown className=\"size-3\" />\n )\n ) : null}\n </button>\n </th>\n )\n}\n\n/**\n * The primary (title) column cell: the document link, resolved in the\n * ambient active locale with a visible fallback chip when the shown title is\n * untranslated (EC-176).\n */\nfunction PrimaryCell({\n projectId,\n collection,\n doc,\n primary,\n depth = 0,\n locale,\n config,\n detailPath,\n}: {\n projectId: string\n collection: CollectionDef\n doc: CmsDocument\n primary: string\n /** EC-218: nesting depth in the hierarchy (tree mode) — indents the title. */\n depth?: number\n locale: Locale\n config: LocaleConfig\n /** Rail-owned detail route splat (AD-19); absent ⇒ the rail has no detail surface. */\n detailPath?: (id: string) => string\n}) {\n // EC-242: the title cell renders the document identity (resolveDocumentTitle) —\n // a string `titleField` or a function pattern — over the fallback-resolved\n // values, NOT the raw `primary` column value. The untranslated-title chip applies\n // only to a field-backed title (a function pattern has no single source field).\n const title = resolveDocumentTitle(\n collection,\n resolveDocumentForLocale(collection, doc, locale, config),\n doc.id,\n )\n const fallback =\n typeof collection.titleField === 'function'\n ? null\n : resolveDisplayedValue(collection, doc, primary, locale, config).fallback\n const splat = detailPath?.(doc.id)\n return (\n <Row gap=\"2\" align=\"center\" className=\"min-w-0\" style={{ paddingLeft: depth * 20 }}>\n {depth > 0 ? (\n <CornerDownRight className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n ) : null}\n {/* 0.0.39: assets get a leading, fixed-size preview in the filename column —\n an image thumbnail (object-cover) for image assets, the file-type placeholder\n otherwise. Fixed box, so it never reflows the EC-316 fixed column widths. */}\n {collection.kind === 'asset' ? <AssetThumbCell assetId={doc.id} alt={title} /> : null}\n {splat !== undefined ? (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: splat }}\n className=\"truncate font-medium text-foreground hover:text-primary hover:underline\"\n >\n {title}\n </Link>\n ) : (\n <Text as=\"span\" className=\"truncate font-medium text-foreground\">\n {title}\n </Text>\n )}\n <FallbackChip fallback={fallback} testId={`cell-fallback-${doc.id}-${primary}`} />\n </Row>\n )\n}\n\n/**\n * The leading asset preview in the filename column (0.0.39). Resolves the asset\n * record from the ambient library and renders {@link AssetThumb} — an image preview\n * for image assets, the file-type placeholder for the rest. Mounted only for\n * `kind:'asset'` collections (which are always under an `<AssetsProvider>`), so the\n * `useAssetRecord` hook is never called on a rail without a library.\n */\nfunction AssetThumbCell({ assetId, alt }: { assetId: string; alt: string }) {\n const record = useAssetRecord(assetId)\n return <AssetThumb record={record} alt={alt} className=\"size-8 shrink-0\" />\n}\n\n/**\n * The read-only URL column cell (EC-216): the document's LIVE (published) path,\n * linking to the absolute live URL when a `frontendUrl` is configured. Only\n * published documents have a `url` here (the URL is derived from the published\n * pool, EC-205), so an unpublished document — having no live page — shows a dash.\n * The URL is always derived, never stored.\n */\nfunction UrlCell({ url }: { url?: DocumentUrl }) {\n if (!url) {\n return (\n <Text size=\"xs\" tone=\"subtle\">\n —\n </Text>\n )\n }\n if (url.href) {\n return (\n <a\n href={url.href}\n target=\"_blank\"\n rel=\"noreferrer\"\n title={url.href}\n className=\"inline-flex max-w-[18rem] items-center gap-1 text-xs text-muted-foreground transition-colors hover:text-foreground\"\n >\n <span className=\"truncate\">{url.path}</span>\n <ExternalLink className=\"size-3 shrink-0\" aria-hidden />\n </a>\n )\n }\n return (\n <Text size=\"xs\" tone=\"muted\" className=\"max-w-[18rem] truncate\" title={url.path}>\n {url.path}\n </Text>\n )\n}\n\nexport type { FieldDef }\n"]}
1
+ {"version":3,"file":"collection-table.js","sourceRoot":"","sources":["../../../src/components/cms/collection-table.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport {\n ArrowDown,\n ArrowUp,\n Copy,\n CornerDownRight,\n Download,\n Eye,\n EyeOff,\n ExternalLink,\n MoreHorizontal,\n Pencil,\n Plus,\n Trash2,\n Upload,\n} from 'lucide-react'\nimport { useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { CmsDocument, CollectionDef, DocumentUrl, FieldDef, Locale, LocaleConfig } from '@elytracms/core/cms-core'\nimport {\n orderHierarchically,\n resolveDocumentForLocale,\n resolveDocumentTitle,\n validateCollectionShape,\n} from '@elytracms/core/cms-core'\nimport {\n Button,\n Code,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Row,\n Separator,\n Spacer,\n Stack,\n Text,\n toast,\n} from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { LocaleSwitcher, resolveDisplayedValue } from '@elytracms/studio/lib/locale'\nimport {\n EMPTY_SELECTION,\n STATE_FIELD,\n applyViewState,\n clearSelection,\n primaryColumn,\n selectAll,\n toggleRow,\n visibleColumns,\n visibleSelection,\n} from '@elytracms/studio/lib/cms-views'\nimport type { SelectionState, ViewSort } from '@elytracms/studio/lib/cms-views'\nimport { cellEditKind, cellKey } from './cell-edit'\nimport { FallbackChip } from './cell'\nimport { EditableCell } from './editable-cell'\nimport { useCellEdit } from './use-cell-edit'\nimport { ImportDialog } from './import-export'\nimport { AssetThumb, useAssetRecord } from '../assets/asset-thumb'\nimport { fieldLabel } from './cms-labels'\nimport { cmsIssueLabel } from '@elytracms/studio/lib/persona/labels'\nimport { TriangleAlert } from 'lucide-react'\nimport type { BulkAction, BulkReport, BulkSetField } from './bulk-actions'\nimport { BulkBar, BulkReportPanel } from './bulk-bar'\nimport { rowActionsFor } from './row-actions'\nimport type { RowAction } from './row-actions'\nimport { duplicateFromSource } from './duplicate-document'\nimport {\n CHECKBOX_COLUMN_WIDTH_REM,\n PRIMARY_COLUMN_WIDTH_REM,\n ROW_ACTIONS_COLUMN_WIDTH_REM,\n STATE_COLUMN_WIDTH_REM,\n URL_COLUMN_WIDTH_REM,\n columnWidthRem,\n} from './column-width'\nimport { ReferenceConfirmDialog } from './reference-confirm'\nimport { FilterControl } from './filter-builder'\nimport { DisplayMenu, ViewTabs } from './view-bar'\nimport { DocStateBadge } from './doc-state'\nimport { useSetBreadcrumb } from '../breadcrumb-context'\nimport { useCmsCollectionViewSource } from '../collection-view/cms-collection-view-source'\nimport { CollectionViewProvider, useCollectionView } from '../collection-view/collection-view-context'\n\nexport function CollectionTable({ projectId, collectionId }: { projectId: string; collectionId: string }) {\n // AD-19: render over the portable CollectionViewSource, not `useCms()` directly,\n // so the SAME list surface serves the users rail (EC-240), redirects (EC-303), …\n const source = useCmsCollectionViewSource(collectionId)\n\n if (!source) {\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Text tone=\"muted\">\n Unknown collection <Code tone=\"muted\">{collectionId}</Code>.\n </Text>\n </div>\n )\n }\n\n // Keyed per collection so selection/report state never leaks across collections.\n return (\n <CollectionViewProvider source={source}>\n <LoadedCollectionTable key={collectionId} projectId={projectId} />\n </CollectionViewProvider>\n )\n}\n\n/**\n * The portable list surface (AD-19). Renders entirely over the ambient\n * {@link useCollectionView} source — no `useCms`, no `CmsProvider` required — so\n * any rail (the users rail, redirects, …) reuses it by wrapping it in a\n * {@link CollectionViewProvider} over its own source. `CollectionTable` is just\n * the CMS entry point that builds a CMS source and mounts this.\n */\nexport function LoadedCollectionTable({\n projectId,\n toolbarExtra,\n}: {\n projectId: string\n /**\n * A rail-owned action rendered at the tail of the header toolbar (EC-319) —\n * e.g. the assets rail's Upload button + grid/list toggle. Absent for every\n * other rail today (users, redirects, CMS), so their toolbar is unchanged.\n */\n toolbarExtra?: ReactNode\n}) {\n const source = useCollectionView()\n const collection = source.schema\n const navigate = useNavigate()\n const collectionId = source.collectionId\n // EC-214: the collection is the active record — the global header breadcrumb\n // names it, replacing the per-view title heading.\n useSetBreadcrumb([{ label: localizedLabel(collection.form?.label, collectionId) }])\n const [importOpen, setImportOpen] = useState(false)\n const [selection, setSelection] = useState<SelectionState>(EMPTY_SELECTION)\n const [bulkBusy, setBulkBusy] = useState(false)\n const [bulkReport, setBulkReport] = useState<BulkReport | null>(null)\n // EC-173: bulk unpublish/delete go through the reference-aware confirmation\n // with a snapshot of the selection taken when the action was raised.\n const [pendingBulk, setPendingBulk] = useState<{\n action: 'unpublish' | 'delete'\n targets: { collection: string; id: string }[]\n } | null>(null)\n\n // The working view state (EC-169): filters + sort + columns, possibly dirty\n // against the active saved view. The table renders exactly this state.\n const viewState = source.viewState()\n // EC-213: the title/link column is the first visible column; the rest follow\n // in the view's column order. No separate primary-column selector.\n const primary = primaryColumn(collection, viewState)\n const columns = visibleColumns(collection, viewState).filter((name) => name !== primary)\n const primaryFieldDef = collection.fields.find((f) => f.name === primary)\n\n const docs = source.documents()\n\n // EC-274/EC-284: collection-shape drift (a singleton with > 1 document; later\n // duplicate/unknown/unfilled aliases). When the data has drifted from the lens's\n // expected shape the studio shows this list instead of skipping — surfacing the\n // conflicts here as a \"for review\" banner so an editor resolves them without a\n // developer (keep/delete the outliers), after which the lens applies again.\n const shapeIssues = useMemo(() => validateCollectionShape(collection, docs), [collection, docs])\n\n // EC-216 + EC-205 (\"live = published\"): each routable document's LIVE URL,\n // derived from the PUBLISHED pool (never draft rows), memoised inside the\n // source. `null` when the collection is not routable → the column is hidden.\n const urlByDocId = source.publishing?.liveUrls?.() ?? null\n\n const visibleDocs = useMemo(\n // EC-224: the `$state` filter/sort sources published-ness from the version\n // POINTER (`source.publishing.isPublished`), not a flat `state` field.\n () =>\n applyViewState(collection, docs, source.localeConfig, viewState, (doc) =>\n source.publishing?.isPublished({ collection: doc.collection, id: doc.id }) ?? false,\n ).docs,\n [collection, docs, source.localeConfig, viewState, source.publishing],\n )\n\n // EC-218: a collection with a `parent` self-relation can render as a nested\n // tree (parents above their children, indented). Tree mode reorders the\n // already-filtered rows depth-first — filters/sort still order siblings.\n const hierarchical = source.capabilities.hierarchy\n const [treeMode, setTreeMode] = useState(hierarchical)\n const treeRows = useMemo(\n () => (hierarchical && treeMode ? orderHierarchically(visibleDocs) : null),\n [hierarchical, treeMode, visibleDocs],\n )\n const orderedDocs = treeRows ? treeRows.map((row) => row.doc) : visibleDocs\n const depthById = useMemo(\n () => (treeRows ? new Map(treeRows.map((row) => [row.doc.id, row.depth])) : undefined),\n [treeRows],\n )\n\n // EC-246a header derivations. The count reads with the collection's own noun\n // (pluralized via the config labels), not a bare number or the generic kind.\n const pluralLabel = localizedLabel(collection.form?.label, `${collectionId}s`)\n const singularLabel = localizedLabel(collection.form?.labelSingular, collectionId)\n const countLabel =\n visibleDocs.length === docs.length\n ? translate('cms.list.count', {\n count: docs.length,\n noun: docs.length === 1 ? singularLabel : pluralLabel,\n })\n : translate('cms.list.countFiltered', {\n visible: visibleDocs.length,\n total: docs.length,\n noun: pluralLabel,\n })\n\n // EC-246d: New creates an empty draft and opens its editor directly — no\n // interstitial modal, for pages or any other collection. Id is auto-generated;\n // the slug/title (and, for pages, the parent) are typed in the form, and a\n // missing/duplicate slug surfaces as a Publish-gating issue, never blocks the\n // draft. Routes are config-sourced (EC-207), so nothing else is written here.\n const isPageCollection = collectionId === 'page'\n // Creation is a capability (a credential rail like users mints identities via\n // sign-up, not here), and the detail route is rail-owned (never the CMS route).\n const canCreate = source.capabilities.create && source.detailPath !== undefined\n const handleNew = () => {\n const ref = source.create()\n const splat = source.detailPath?.(ref.id)\n if (splat === undefined) return\n navigate({ to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: splat } })\n }\n const visibleIds = orderedDocs.map((doc) => doc.id)\n // Selection is always read against the current view — rows the filters hid\n // are never silently included in a bulk action.\n const selectedIds = visibleSelection(selection, visibleIds)\n // EC-316: the right-edge row menu is a capability-gated column — a rail with\n // no detail surface AND no publish/remove capability offers nothing (users\n // rail today has remove, so this is rarely empty; kept honest for AD-19).\n const hasRowActions = source.detailPath !== undefined || source.capabilities.publish || source.capabilities.remove\n\n function toggleSort(field: string) {\n source.setViewState((prev) => {\n const sort: ViewSort | null =\n prev.sort?.field !== field\n ? { field, dir: 'asc' }\n : prev.sort.dir === 'asc'\n ? { field, dir: 'desc' }\n : null\n return { ...prev, sort }\n })\n }\n\n function raiseBulk(action: BulkAction) {\n if (action === 'publish') {\n void handleBulk(action, [...selectedIds])\n return\n }\n // Set-field is composed + raised through the bulk bar's own panel\n // (onSetField), never this state-action path.\n if (action === 'set-field') return\n // Unpublish/delete first enumerate inbound references (EC-173): the\n // dialog shows the aggregate \"N documents, M references will break\".\n setPendingBulk({\n action,\n targets: selectedIds.map((id) => ({ collection: collectionId, id })),\n })\n }\n\n // EC-316: the row `⋮` menu dispatches through the SAME machinery as the\n // toolbar's bulk actions — one document is just a selection of one — so\n // there is exactly one write path for publish/unpublish/delete, never a\n // parallel one. `open` is the row menu's own affordance (the primary-column\n // link already offers it; this is the keyboard/menu-driven equivalent).\n function handleRowAction(action: RowAction, doc: CmsDocument) {\n if (action === 'open') {\n const splat = source.detailPath?.(doc.id)\n if (splat === undefined) return\n navigate({ to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: splat } })\n return\n }\n // EC-442: duplicate the row's draft into a fresh DRAFT document (never\n // auto-published) through the seam's existing create+save write paths,\n // then land on the new draft's editor — the same navigation `+ New` takes.\n // The action is offered iff `capabilities.create` + a detail surface\n // (`rowActionsFor`), the New button's exact gate.\n if (action === 'duplicate') {\n const newRef = duplicateFromSource(source, doc)\n toast.success(translate('cms.editor.duplicateCreated'))\n const splat = source.detailPath?.(newRef.id)\n if (splat === undefined) return\n navigate({ to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: splat } })\n return\n }\n if (action === 'publish') {\n void handleBulk(action, [doc.id])\n return\n }\n // unpublish/delete: the same reference-aware confirmation the bulk bar\n // raises, scoped to this one document.\n setPendingBulk({ action, targets: [{ collection: collectionId, id: doc.id }] })\n }\n\n async function handleBulk(action: BulkAction, acted: readonly string[], setField?: BulkSetField) {\n // Defensive: with the capability gates below (`source.bulk` presence gates\n // the bar, per-action capabilities gate its buttons) this is unreachable —\n // but a confirmed action must NEVER silently no-op (EC-433: the assets\n // rail's bulk delete used to die exactly here), so a missing runner still\n // reports one honest failure per item.\n if (!source.bulk) {\n setBulkReport({\n action,\n ...(setField ? { setField } : {}),\n results: acted.map((id) => ({\n ref: { collection: collectionId, id },\n title: source.titleOf({ collection: collectionId, id }),\n ok: false,\n reason: 'Bulk actions are not available in this view.',\n error: { code: 'unknown', message: 'Bulk actions are not available in this view.' },\n })),\n })\n return\n }\n setBulkBusy(true)\n try {\n const report = await source.bulk([...acted], action, setField)\n setBulkReport(report)\n // Succeeded items deselect; failed items stay selected for a retry.\n const actedSet = new Set(acted)\n const failed = new Set(report.results.filter((r) => !r.ok).map((r) => r.ref.id))\n setSelection((prev) => ({\n ids: prev.ids.filter((id) => !actedSet.has(id) || failed.has(id)),\n anchorId: null,\n }))\n } finally {\n setBulkBusy(false)\n }\n }\n\n function exportJson() {\n const data = JSON.stringify(source.documents(), null, 2)\n const blob = new Blob([data], { type: 'application/json' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = `${collectionId}.json`\n a.click()\n URL.revokeObjectURL(url)\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n {/* Header (EC-246a redesign). One toolbar: identity + refine on the left,\n lens + actions on the right; a transient filter bar drops below only\n while refining. The header breadcrumb (EC-214) names the collection —\n no title here. Locale is a lens; column/layout config lives behind the\n far-right Display control; shared views are config-as-code (EC-246b). */}\n <div className=\"shrink-0 border-b border-border px-4 py-3 sm:px-6\">\n <Row gap=\"2\" wrap>\n {/* Left — which view (only when >1), then the transient refine. */}\n <ViewTabs />\n {/* Transient ad-hoc refine — a chip beside the view tabs, editor in a\n popover (EC-246b); the chip's ✕ clears it, so no separate Reset. */}\n <FilterControl collection={collection} />\n {/* The working-set count is the RESULT of the view + filter above, so it\n reads last, set off by a divider and sized like its neighbours. */}\n <Separator orientation=\"vertical\" className=\"h-5 self-center\" />\n <Text\n as=\"span\"\n size=\"xs\"\n tone=\"muted\"\n className=\"whitespace-nowrap tabular-nums\"\n data-testid=\"collection-count\"\n >\n {countLabel}\n </Text>\n\n <Spacer />\n\n {/* Right — the ambient locale lens (EC-176; hidden for single-locale AND\n for a non-localized rail like users), then display config + write actions. */}\n {source.capabilities.locales ? <LocaleSwitcher /> : null}\n <DisplayMenu\n collection={collection}\n hierarchical={hierarchical}\n treeMode={treeMode}\n onToggleTree={setTreeMode}\n />\n {canCreate ? (\n <Button size=\"sm\" onClick={handleNew} data-testid=\"new-document\">\n <Plus /> {isPageCollection ? translate('cms.list.newPage') : translate('cms.list.new')}\n </Button>\n ) : null}\n {/* EC-213: import/export fold into one action menu, off the header.\n AD-19: a capability — absent on credential rails (users), so identity\n records are never dumped to / loaded from raw JSON. */}\n {source.capabilities.importExport ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon-sm\"\n aria-label={translate('cms.list.moreActions')}\n data-testid=\"collection-actions\"\n >\n <MoreHorizontal />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={() => setImportOpen(true)} data-testid=\"action-import\">\n <Upload /> {translate('cms.list.import')}\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={exportJson}\n disabled={docs.length === 0}\n data-testid=\"action-export\"\n >\n <Download /> {translate('cms.list.export')}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n {toolbarExtra}\n </Row>\n </div>\n\n {/* Bulk actions over the selection (EC-169). Mounted only when the source\n actually carries a bulk runner (present iff `capabilities.bulk`,\n EC-433) and each action button is capability-gated inside — a bar\n whose confirmed actions can't dispatch is never offered. */}\n {source.bulk ? (\n <BulkBar\n collection={collection}\n capabilities={source.capabilities}\n count={selectedIds.length}\n busy={bulkBusy}\n onAction={raiseBulk}\n onSetField={(setField) => void handleBulk('set-field', [...selectedIds], setField)}\n onClear={() => setSelection(clearSelection())}\n />\n ) : null}\n {bulkReport ? (\n <BulkReportPanel report={bulkReport} onDismiss={() => setBulkReport(null)} />\n ) : null}\n\n {/* Reference-aware bulk unpublish/delete confirmation (EC-173). */}\n {pendingBulk !== null ? (\n <ReferenceConfirmDialog\n projectId={projectId}\n action={pendingBulk.action}\n targets={pendingBulk.targets}\n open\n busy={bulkBusy}\n onOpenChange={(open) => {\n if (!open) setPendingBulk(null)\n }}\n onConfirm={() => {\n const { action, targets } = pendingBulk\n setPendingBulk(null)\n void handleBulk(\n action,\n targets.map((target) => target.id),\n )\n }}\n />\n ) : null}\n\n {/* EC-274/EC-284: \"for review\" lane — the collection has drifted from its\n lens's expected shape; surface each conflict so an editor can resolve it. */}\n {shapeIssues.length > 0 ? (\n <div\n className=\"shrink-0 border-b border-warning/30 bg-warning/10 px-4 py-2 sm:px-6\"\n data-testid=\"shape-review-banner\"\n >\n <Stack gap=\"1\">\n {shapeIssues.map((issue, i) => (\n <Row key={`${issue.code}:${i}`} gap=\"2\" align=\"start\">\n <TriangleAlert className=\"mt-0.5 size-3.5 shrink-0 text-warning\" aria-hidden />\n <Stack gap=\"none\" className=\"min-w-0\">\n <Text size=\"xs\" weight=\"semibold\" tone=\"default\" title={issue.code}>\n {translate('cms.list.forReview')} · {cmsIssueLabel(issue.code)}\n </Text>\n <Text size=\"xs\" tone=\"muted\">\n {issue.message}\n </Text>\n </Stack>\n </Row>\n ))}\n </Stack>\n </div>\n ) : null}\n\n {/* Table */}\n <div className=\"min-h-0 flex-1 overflow-auto\">\n {docs.length === 0 ? (\n <div className=\"grid h-full place-items-center p-8\">\n <Stack gap=\"2\" align=\"center\" className=\"text-center\">\n <Text tone=\"muted\">{translate('cms.list.emptyNone', { noun: pluralLabel.toLowerCase() })}</Text>\n {canCreate ? (\n <Button size=\"sm\" onClick={handleNew}>\n <Plus />{' '}\n {isPageCollection\n ? translate('cms.list.newPage')\n : translate('cms.list.newNamed', {\n noun: localizedLabel(collection.form?.labelSingular, 'document'),\n })}\n </Button>\n ) : null}\n </Stack>\n </div>\n ) : visibleDocs.length === 0 ? (\n <div className=\"grid h-full place-items-center p-8\">\n <Text tone=\"muted\" data-testid=\"empty-filtered\">\n {translate('cms.list.emptyFiltered')}\n </Text>\n </div>\n ) : (\n <DocumentsGrid\n projectId={projectId}\n collection={collection}\n primary={primary}\n primaryFieldDef={primaryFieldDef}\n columns={columns}\n sortedDocs={orderedDocs}\n depthById={depthById}\n urlByDocId={urlByDocId}\n sort={viewState.sort}\n onSort={toggleSort}\n selectedIds={selectedIds}\n onToggleRow={(id, shift) =>\n setSelection((prev) => toggleRow(prev, visibleIds, id, shift))\n }\n onToggleAll={() =>\n setSelection(\n selectedIds.length === visibleIds.length && visibleIds.length > 0\n ? clearSelection()\n : selectAll(visibleIds),\n )\n }\n hasRowActions={hasRowActions}\n onRowAction={handleRowAction}\n />\n )}\n </div>\n\n {source.capabilities.importExport ? (\n <ImportDialog collectionId={collectionId} open={importOpen} onOpenChange={setImportOpen} />\n ) : null}\n </div>\n )\n}\n\n/**\n * The documents table with inline cell editing (EC-168). Cells edit in place\n * (text/number/boolean/select/date) or open pickers (relation/asset); each\n * commit is one operations command via the cell-edit controller, with\n * rollback + inline cell errors on rejection. Row checkboxes (EC-169) live in\n * their own column, so selection clicks never collide with cell editing.\n */\nfunction DocumentsGrid({\n projectId,\n collection,\n primary,\n primaryFieldDef,\n columns,\n sortedDocs,\n depthById,\n urlByDocId,\n sort,\n onSort,\n selectedIds,\n onToggleRow,\n onToggleAll,\n hasRowActions,\n onRowAction,\n}: {\n projectId: string\n collection: CollectionDef\n primary: string\n primaryFieldDef: FieldDef | undefined\n columns: string[]\n sortedDocs: CmsDocument[]\n /** EC-218: nesting depth per doc id when rendering the hierarchy (tree mode). */\n depthById?: Map<string, number>\n /** EC-216: each routable doc's derived URL; `null` hides the URL column. */\n urlByDocId?: Map<string, DocumentUrl> | null\n sort: ViewSort | null\n onSort: (field: string) => void\n selectedIds: readonly string[]\n onToggleRow: (id: string, shift: boolean) => void\n onToggleAll: () => void\n /** EC-316: whether the right-edge row `⋮` menu has anything to offer at all. */\n hasRowActions: boolean\n onRowAction: (action: RowAction, doc: CmsDocument) => void\n}) {\n const source = useCollectionView()\n const collectionId = collection.id\n // EC-346: a rail with no publish lifecycle (assets/redirects/users) has no\n // state to show — the column is omitted entirely (header + cells), never a\n // badge defaulted to \"draft\" via `?? false`. `source.publishing` presence IS\n // the gate (mirrors the capability contract: the group is present iff the\n // flag is set), narrowed once here so every render site below is type-safe.\n const publishing = source.publishing\n const fieldByName = (name: string) => collection.fields.find((f) => f.name === name)\n // Inline editing is a capability: a read-only rail yields no editable cells.\n const editableFields = source.capabilities.inlineEdit\n ? columns.filter((name) => {\n const field = fieldByName(name)\n return field ? cellEditKind(field, source.isFieldReadOnly(field)) !== 'none' : false\n })\n : []\n const cellEdit = useCellEdit({ collection, docs: sortedDocs, editableFields })\n const selected = new Set(selectedIds)\n const allSelected = sortedDocs.length > 0 && selectedIds.length === sortedDocs.length\n\n return (\n <div ref={cellEdit.containerRef}>\n {/* EC-316: `table-fixed` + an explicit `<colgroup>` fix every column's\n width from the SCHEMA alone, before any row renders. Under the\n browser's default `table-layout: auto` a cell's INTRINSIC content\n width feeds back into its column — so swapping a cell's rendered\n value for its inline editor (a wider `<input>`) reflowed the whole\n grid. Fixed layout makes a column's width immune to its cells'\n content; `columnWidthRem` only sets relative proportions, so the\n grid still scales naturally with the container (never with a cell). */}\n <table className=\"w-full table-fixed border-collapse text-sm\">\n <colgroup>\n <col style={{ width: `${CHECKBOX_COLUMN_WIDTH_REM}rem` }} />\n <col style={{ width: `${PRIMARY_COLUMN_WIDTH_REM}rem` }} />\n {columns.map((name) => (\n <col key={name} style={{ width: `${columnWidthRem(fieldByName(name))}rem` }} />\n ))}\n {publishing ? <col style={{ width: `${STATE_COLUMN_WIDTH_REM}rem` }} /> : null}\n {urlByDocId ? <col style={{ width: `${URL_COLUMN_WIDTH_REM}rem` }} /> : null}\n {hasRowActions ? <col style={{ width: `${ROW_ACTIONS_COLUMN_WIDTH_REM}rem` }} /> : null}\n </colgroup>\n <thead className=\"sticky top-0 z-10 bg-background\">\n <tr className=\"border-b border-border\">\n <th className=\"px-3 py-2 text-left\">\n <SelectionCheckbox\n checked={allSelected}\n indeterminate={selectedIds.length > 0 && !allSelected}\n label={translate('cms.list.selectAll')}\n testId=\"select-all\"\n onToggle={() => onToggleAll()}\n />\n </th>\n <HeaderCell\n field={primary}\n label={primaryFieldDef ? fieldLabel(primaryFieldDef) : translate('cms.list.column.title')}\n sort={sort}\n onSort={onSort}\n />\n {columns.map((name) => {\n const field = fieldByName(name)\n return field ? (\n <HeaderCell key={name} field={name} label={fieldLabel(field)} sort={sort} onSort={onSort} />\n ) : null\n })}\n {publishing ? (\n <HeaderCell field={STATE_FIELD} label={translate('cms.list.column.state')} sort={sort} onSort={onSort} />\n ) : null}\n {/* EC-216: derived URL — not a field, so not sortable. */}\n {urlByDocId ? (\n <th className=\"px-3 py-2 text-left text-xs font-semibold text-muted-foreground\">{translate('cms.list.column.url')}</th>\n ) : null}\n {hasRowActions ? (\n <th className=\"px-2 py-2 text-right\">\n <span className=\"sr-only\">{translate('cms.list.rowActionsColumnLabel')}</span>\n </th>\n ) : null}\n </tr>\n </thead>\n <tbody>\n {sortedDocs.map((doc) => (\n <tr\n key={doc.id}\n className={cn(\n 'group border-b border-border/60 hover:bg-muted/40',\n selected.has(doc.id) && 'bg-primary/5',\n )}\n >\n <td className=\"px-3 py-2 align-middle\">\n <SelectionCheckbox\n checked={selected.has(doc.id)}\n label={translate('cms.list.selectRow', { id: doc.id })}\n testId={`select-row-${doc.id}`}\n onToggle={(shift) => onToggleRow(doc.id, shift)}\n />\n </td>\n <td className=\"px-3 py-2 align-middle\">\n <PrimaryCell\n projectId={projectId}\n collection={collection}\n doc={doc}\n primary={primary}\n depth={depthById?.get(doc.id) ?? 0}\n locale={source.activeLocale}\n config={source.localeConfig}\n detailPath={source.detailPath}\n />\n </td>\n {columns.map((name) => {\n const field = fieldByName(name)\n const coord = { docId: doc.id, field: name }\n const key = cellKey(coord)\n return (\n <td key={name} className=\"px-3 py-2 align-middle\">\n {field ? (\n <EditableCell\n collection={collection}\n doc={doc}\n field={field}\n editing={cellEdit.activeKey === key}\n busy={cellEdit.busyKey === key}\n errorMessage={cellEdit.errorOf(key)}\n onStartEdit={() => cellEdit.start(coord)}\n onCancel={() => cellEdit.cancel(coord)}\n onCommit={(value, advance) => cellEdit.commit(coord, value, advance)}\n />\n ) : null}\n </td>\n )\n })}\n {publishing ? (\n <td className=\"px-3 py-2 align-middle\">\n <DocStateBadge published={publishing.isPublished({ collection: collectionId, id: doc.id })} />\n </td>\n ) : null}\n {urlByDocId ? (\n <td className=\"px-3 py-2 align-middle\">\n <UrlCell url={urlByDocId.get(doc.id)} />\n </td>\n ) : null}\n {hasRowActions ? (\n <td className=\"px-2 py-2 text-right align-middle\">\n <RowActionsMenu\n doc={doc}\n published={source.publishing?.isPublished({ collection: collectionId, id: doc.id }) ?? false}\n hasDetailPath={source.detailPath !== undefined}\n onAction={(action) => onRowAction(action, doc)}\n />\n </td>\n ) : null}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\n/**\n * The right-edge row `⋮` menu (EC-316): the SAME action vocabulary as the\n * toolbar's bulk actions (`rowActionsFor`), gated by the SAME capabilities —\n * so it degrades exactly like the rest of the list for a rail without\n * publish/remove (AD-19). Renders nothing when the gate leaves zero actions.\n */\nfunction RowActionsMenu({\n doc,\n published,\n hasDetailPath,\n onAction,\n}: {\n doc: CmsDocument\n published: boolean\n hasDetailPath: boolean\n onAction: (action: RowAction) => void\n}) {\n const source = useCollectionView()\n const actions = rowActionsFor({ capabilities: source.capabilities, hasDetailPath, published })\n if (actions.length === 0) return null\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={translate('cms.list.rowActionsLabel', { id: doc.id })}\n data-testid={`row-actions-${doc.id}`}\n >\n <MoreHorizontal />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {actions.map((action) => (\n <RowActionItem key={action} action={action} onSelect={() => onAction(action)} />\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\nfunction RowActionItem({ action, onSelect }: { action: RowAction; onSelect: () => void }) {\n switch (action) {\n case 'open':\n return (\n <DropdownMenuItem onClick={onSelect} data-testid=\"row-action-open\">\n <Pencil /> {translate('cms.list.rowAction.open')}\n </DropdownMenuItem>\n )\n case 'duplicate':\n return (\n <DropdownMenuItem onClick={onSelect} data-testid=\"row-action-duplicate\">\n <Copy /> {translate('common.duplicate')}\n </DropdownMenuItem>\n )\n case 'publish':\n return (\n <DropdownMenuItem onClick={onSelect} data-testid=\"row-action-publish\">\n <Eye /> {translate('cms.list.bulk.publish')}\n </DropdownMenuItem>\n )\n case 'unpublish':\n return (\n <DropdownMenuItem onClick={onSelect} data-testid=\"row-action-unpublish\">\n <EyeOff /> {translate('cms.list.bulk.unpublish')}\n </DropdownMenuItem>\n )\n case 'delete':\n return (\n <DropdownMenuItem\n variant=\"destructive\"\n onClick={onSelect}\n data-testid=\"row-action-delete\"\n >\n <Trash2 /> {translate('common.delete')}\n </DropdownMenuItem>\n )\n }\n}\n\n/**\n * A selection checkbox (row or select-all). Its own column and focus target —\n * it never participates in the cell-edit keyboard flow. Shift-click selects\n * the anchor→row range (the mousedown guard stops text selection).\n */\nfunction SelectionCheckbox({\n checked,\n indeterminate = false,\n label,\n testId,\n onToggle,\n}: {\n checked: boolean\n indeterminate?: boolean\n label: string\n testId: string\n onToggle: (shift: boolean) => void\n}) {\n return (\n <input\n ref={(el) => {\n if (el) el.indeterminate = indeterminate && !checked\n }}\n type=\"checkbox\"\n className=\"size-4 cursor-pointer accent-primary align-middle\"\n checked={checked}\n readOnly\n aria-label={label}\n data-testid={testId}\n onClick={(event) => onToggle(event.shiftKey)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n onToggle(event.shiftKey)\n }\n }}\n onMouseDown={(event) => {\n if (event.shiftKey) event.preventDefault()\n }}\n />\n )\n}\n\nfunction HeaderCell({\n field,\n label,\n sort,\n onSort,\n}: {\n field: string\n label: string\n sort: ViewSort | null\n onSort: (field: string) => void\n}) {\n const active = sort?.field === field\n return (\n <th className=\"px-3 py-2 text-left text-xs font-semibold text-muted-foreground\">\n <button type=\"button\" onClick={() => onSort(field)} className=\"inline-flex items-center gap-1 hover:text-foreground\">\n {label}\n {active ? (\n sort!.dir === 'asc' ? (\n <ArrowUp className=\"size-3\" />\n ) : (\n <ArrowDown className=\"size-3\" />\n )\n ) : null}\n </button>\n </th>\n )\n}\n\n/**\n * The primary (title) column cell: the document link, resolved in the\n * ambient active locale with a visible fallback chip when the shown title is\n * untranslated (EC-176).\n */\nfunction PrimaryCell({\n projectId,\n collection,\n doc,\n primary,\n depth = 0,\n locale,\n config,\n detailPath,\n}: {\n projectId: string\n collection: CollectionDef\n doc: CmsDocument\n primary: string\n /** EC-218: nesting depth in the hierarchy (tree mode) — indents the title. */\n depth?: number\n locale: Locale\n config: LocaleConfig\n /** Rail-owned detail route splat (AD-19); absent ⇒ the rail has no detail surface. */\n detailPath?: (id: string) => string\n}) {\n // EC-242: the title cell renders the document identity (resolveDocumentTitle) —\n // a string `titleField` or a function pattern — over the fallback-resolved\n // values, NOT the raw `primary` column value. The untranslated-title chip applies\n // only to a field-backed title (a function pattern has no single source field).\n const title = resolveDocumentTitle(\n collection,\n resolveDocumentForLocale(collection, doc, locale, config),\n doc.id,\n )\n const fallback =\n typeof collection.titleField === 'function'\n ? null\n : resolveDisplayedValue(collection, doc, primary, locale, config).fallback\n const splat = detailPath?.(doc.id)\n return (\n <Row gap=\"2\" align=\"center\" className=\"min-w-0\" style={{ paddingLeft: depth * 20 }}>\n {depth > 0 ? (\n <CornerDownRight className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n ) : null}\n {/* 0.0.39: assets get a leading, fixed-size preview in the filename column —\n an image thumbnail (object-cover) for image assets, the file-type placeholder\n otherwise. Fixed box, so it never reflows the EC-316 fixed column widths. */}\n {collection.kind === 'asset' ? <AssetThumbCell assetId={doc.id} alt={title} /> : null}\n {splat !== undefined ? (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: splat }}\n className=\"truncate font-medium text-foreground hover:text-primary hover:underline\"\n >\n {title}\n </Link>\n ) : (\n <Text as=\"span\" className=\"truncate font-medium text-foreground\">\n {title}\n </Text>\n )}\n <FallbackChip fallback={fallback} testId={`cell-fallback-${doc.id}-${primary}`} />\n </Row>\n )\n}\n\n/**\n * The leading asset preview in the filename column (0.0.39). Resolves the asset\n * record from the ambient library and renders {@link AssetThumb} — an image preview\n * for image assets, the file-type placeholder for the rest. Mounted only for\n * `kind:'asset'` collections (which are always under an `<AssetsProvider>`), so the\n * `useAssetRecord` hook is never called on a rail without a library.\n */\nfunction AssetThumbCell({ assetId, alt }: { assetId: string; alt: string }) {\n const record = useAssetRecord(assetId)\n return <AssetThumb record={record} alt={alt} className=\"size-8 shrink-0\" />\n}\n\n/**\n * The read-only URL column cell (EC-216): the document's LIVE (published) path,\n * linking to the absolute live URL when a `frontendUrl` is configured. Only\n * published documents have a `url` here (the URL is derived from the published\n * pool, EC-205), so an unpublished document — having no live page — shows a dash.\n * The URL is always derived, never stored.\n */\nfunction UrlCell({ url }: { url?: DocumentUrl }) {\n if (!url) {\n return (\n <Text size=\"xs\" tone=\"subtle\">\n —\n </Text>\n )\n }\n if (url.href) {\n return (\n <a\n href={url.href}\n target=\"_blank\"\n rel=\"noreferrer\"\n title={url.href}\n className=\"inline-flex max-w-[18rem] items-center gap-1 text-xs text-muted-foreground transition-colors hover:text-foreground\"\n >\n <span className=\"truncate\">{url.path}</span>\n <ExternalLink className=\"size-3 shrink-0\" aria-hidden />\n </a>\n )\n }\n return (\n <Text size=\"xs\" tone=\"muted\" className=\"max-w-[18rem] truncate\" title={url.path}>\n {url.path}\n </Text>\n )\n}\n\nexport type { FieldDef }\n"]}