@elytracms/studio 0.0.32 → 0.0.33

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 (113) hide show
  1. package/dist/components/app-views/default-modules.js +8 -6
  2. package/dist/components/app-views/default-modules.js.map +1 -1
  3. package/dist/components/assets/asset-library.js +21 -13
  4. package/dist/components/assets/asset-library.js.map +1 -1
  5. package/dist/components/assets/asset-thumb.js +3 -2
  6. package/dist/components/assets/asset-thumb.js.map +1 -1
  7. package/dist/components/backend-gate.js +4 -3
  8. package/dist/components/backend-gate.js.map +1 -1
  9. package/dist/components/builder/blocks-panel.js +11 -2
  10. package/dist/components/builder/blocks-panel.js.map +1 -1
  11. package/dist/components/builder/canvas.js +6 -5
  12. package/dist/components/builder/canvas.js.map +1 -1
  13. package/dist/components/builder/clipboard-actions.js +12 -6
  14. package/dist/components/builder/clipboard-actions.js.map +1 -1
  15. package/dist/components/builder/composition-field-editor.js +7 -4
  16. package/dist/components/builder/composition-field-editor.js.map +1 -1
  17. package/dist/components/builder/fixture-thumbnail.js +2 -1
  18. package/dist/components/builder/fixture-thumbnail.js.map +1 -1
  19. package/dist/components/builder/layout-inspector.d.ts +5 -5
  20. package/dist/components/builder/layout-inspector.js +21 -20
  21. package/dist/components/builder/layout-inspector.js.map +1 -1
  22. package/dist/components/builder/page-tree.js +5 -4
  23. package/dist/components/builder/page-tree.js.map +1 -1
  24. package/dist/components/builder/property-inspector.js +10 -10
  25. package/dist/components/builder/property-inspector.js.map +1 -1
  26. package/dist/components/builder/reuse-context.js +9 -9
  27. package/dist/components/builder/reuse-context.js.map +1 -1
  28. package/dist/components/builder/rich-text-toolbar.js +4 -3
  29. package/dist/components/builder/rich-text-toolbar.js.map +1 -1
  30. package/dist/components/builder/spacing-handles.js +4 -3
  31. package/dist/components/builder/spacing-handles.js.map +1 -1
  32. package/dist/components/cms/asset-picker.js +8 -7
  33. package/dist/components/cms/asset-picker.js.map +1 -1
  34. package/dist/components/cms/bulk-bar.js +8 -5
  35. package/dist/components/cms/bulk-bar.js.map +1 -1
  36. package/dist/components/cms/cell.js +9 -5
  37. package/dist/components/cms/cell.js.map +1 -1
  38. package/dist/components/cms/cms-store.js +4 -3
  39. package/dist/components/cms/cms-store.js.map +1 -1
  40. package/dist/components/cms/collection-table.js +18 -7
  41. package/dist/components/cms/collection-table.js.map +1 -1
  42. package/dist/components/cms/detail-editor.js +38 -27
  43. package/dist/components/cms/detail-editor.js.map +1 -1
  44. package/dist/components/cms/editable-cell.js +4 -4
  45. package/dist/components/cms/editable-cell.js.map +1 -1
  46. package/dist/components/cms/field-control.js +29 -21
  47. package/dist/components/cms/field-control.js.map +1 -1
  48. package/dist/components/cms/filter-builder.js +9 -8
  49. package/dist/components/cms/filter-builder.js.map +1 -1
  50. package/dist/components/cms/reference-confirm.js +24 -10
  51. package/dist/components/cms/reference-confirm.js.map +1 -1
  52. package/dist/components/cms/relation-picker.js +5 -4
  53. package/dist/components/cms/relation-picker.js.map +1 -1
  54. package/dist/components/cms/scalar-value-editor.js +5 -5
  55. package/dist/components/cms/scalar-value-editor.js.map +1 -1
  56. package/dist/components/cms/version-compare.js +36 -21
  57. package/dist/components/cms/version-compare.js.map +1 -1
  58. package/dist/components/cms/versions-pane.js +8 -5
  59. package/dist/components/cms/versions-pane.js.map +1 -1
  60. package/dist/components/cms/view-bar.js +3 -2
  61. package/dist/components/cms/view-bar.js.map +1 -1
  62. package/dist/components/components/component-detail.js +3 -2
  63. package/dist/components/components/component-detail.js.map +1 -1
  64. package/dist/components/components/component-status.js +8 -13
  65. package/dist/components/components/component-status.js.map +1 -1
  66. package/dist/components/components/components-index.js +5 -1
  67. package/dist/components/components/components-index.js.map +1 -1
  68. package/dist/components/components/field-inspector.js +4 -4
  69. package/dist/components/components/field-inspector.js.map +1 -1
  70. package/dist/components/components/fixture-runner.js +4 -1
  71. package/dist/components/components/fixture-runner.js.map +1 -1
  72. package/dist/components/components/registry-browser.js +4 -3
  73. package/dist/components/components/registry-browser.js.map +1 -1
  74. package/dist/components/delivery-health-banner.js +2 -1
  75. package/dist/components/delivery-health-banner.js.map +1 -1
  76. package/dist/components/dev-login-badge.js +2 -1
  77. package/dist/components/dev-login-badge.js.map +1 -1
  78. package/dist/components/extensions/extensions-view.js +22 -21
  79. package/dist/components/extensions/extensions-view.js.map +1 -1
  80. package/dist/components/guards.js +3 -2
  81. package/dist/components/guards.js.map +1 -1
  82. package/dist/components/operation-error.js +3 -2
  83. package/dist/components/operation-error.js.map +1 -1
  84. package/dist/components/playground-badge.js +2 -1
  85. package/dist/components/playground-badge.js.map +1 -1
  86. package/dist/components/project-list.js +5 -4
  87. package/dist/components/project-list.js.map +1 -1
  88. package/dist/components/project-session.js +3 -2
  89. package/dist/components/project-session.js.map +1 -1
  90. package/dist/components/project-switcher.js +4 -3
  91. package/dist/components/project-switcher.js.map +1 -1
  92. package/dist/components/redirects/redirects-module.js +3 -2
  93. package/dist/components/redirects/redirects-module.js.map +1 -1
  94. package/dist/components/references/references-panel.js +7 -6
  95. package/dist/components/references/references-panel.js.map +1 -1
  96. package/dist/components/section-nav.js +8 -5
  97. package/dist/components/section-nav.js.map +1 -1
  98. package/dist/components/section-state.js +13 -32
  99. package/dist/components/section-state.js.map +1 -1
  100. package/dist/components/settings-form.js +2 -1
  101. package/dist/components/settings-form.js.map +1 -1
  102. package/dist/components/studio-project-screen.js +3 -2
  103. package/dist/components/studio-project-screen.js.map +1 -1
  104. package/dist/components/studio-shell.js +3 -2
  105. package/dist/components/studio-shell.js.map +1 -1
  106. package/dist/components/users/users-module.js +5 -4
  107. package/dist/components/users/users-module.js.map +1 -1
  108. package/dist/components/workspace-gate.js +2 -1
  109. package/dist/components/workspace-gate.js.map +1 -1
  110. package/dist/lib/i18n/messages.en.d.ts +11 -4
  111. package/dist/lib/i18n/messages.en.js +636 -6
  112. package/dist/lib/i18n/messages.en.js.map +1 -1
  113. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"users-module.js","sourceRoot":"","sources":["../../../src/components/users/users-module.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AlertTriangle, RotateCcw, ShieldAlert } from 'lucide-react'\nimport { Button, Heading, Row, Stack, Text } from '@elytracms/studio/ui'\nimport { useProjectRole } from '../auth-context'\nimport { UsersProvider, useUsers } from './users-store'\nimport { useUsersCollectionViewSource } from './users-collection-view-source'\nimport { CollectionViewProvider } from '../collection-view/collection-view-context'\nimport { LoadedCollectionTable } from '../cms/collection-table'\nimport { PortableDetailEditor } from '../cms/detail-editor'\n\n/**\n * The studio Users module (EC-240). An admin-only section for managing who has\n * access to this deployment and their role. AD-19: it is the **reuse proof** for\n * the portable interaction surfaces — it renders the SAME `LoadedCollectionTable`\n * the CMS uses for the roster, and the SAME `DetailEditor` body (via\n * {@link PortableDetailEditor}) for a member's detail, both over a users\n * {@link useUsersCollectionViewSource} instead of a CMS source. No bespoke roster,\n * no bespoke editor: filtering, columns, the count, and the edit form all come\n * from the shared surfaces. The users capability profile (admin-only, no publish/\n * versions/create/import) drives which regions appear.\n *\n * `userId === null` is the roster; a `userId` opens that member's detail (routed\n * via `studioPath.user`). Both render under one `UsersProvider` + one source.\n *\n * Gating is layered: the operations chokepoint AND the Convex handlers enforce\n * admin-only server-side (a non-admin `users.list` is denied). This UI gate is UX\n * politeness — a non-admin sees a clear \"restricted\" state instead of a raw\n * authorization error, and we skip firing a query we know will be denied.\n */\nexport function UsersModule({\n projectId,\n userId = null,\n}: {\n projectId: string\n userId?: string | null\n}) {\n const role = useProjectRole(projectId)\n\n // `useProjectRole` conflates \"loading\" and \"not a member\" as null; in the studio\n // a member always reaches here, so null resolves to the real role momentarily.\n if (role === null) {\n return (\n <Stack className=\"h-full place-items-center p-8\" align=\"center\" justify=\"center\">\n <Text size=\"sm\" tone=\"muted\">\n Checking access…\n </Text>\n </Stack>\n )\n }\n if (role !== 'admin') return <RestrictedState />\n\n return (\n <UsersProvider projectId={projectId}>\n <UsersSurface projectId={projectId} userId={userId} />\n </UsersProvider>\n )\n}\n\n/** The non-admin gate: honest about why the surface is empty. */\nfunction RestrictedState() {\n return (\n <Stack gap=\"2\" align=\"center\" justify=\"center\" className=\"h-full p-8 text-center\">\n <ShieldAlert className=\"size-6 text-muted-foreground\" />\n <Heading level={2} size=\"h3\">\n Admins only\n </Heading>\n <Text size=\"sm\" tone=\"muted\" className=\"max-w-sm\">\n Managing users and roles requires the admin role on this deployment. Ask an admin to grant\n you access.\n </Text>\n </Stack>\n )\n}\n\n/** The loaded surface: the portable list (or a member's detail) over the users source. */\nfunction UsersSurface({ projectId, userId }: { projectId: string; userId: string | null }) {\n const store = useUsers()\n const source = useUsersCollectionViewSource(projectId)\n\n if (store.status === 'error') {\n return (\n <Stack gap=\"2\" className=\"p-6\" data-testid=\"users-error\">\n <Row gap=\"2\">\n <AlertTriangle className=\"size-4 text-destructive\" />\n <Text weight=\"semibold\">Users failed to load</Text>\n </Row>\n <Text size=\"sm\" tone=\"muted\">\n {store.errorMessage ?? 'The persistence backend is unreachable.'}\n </Text>\n <Row>\n <Button variant=\"outline\" size=\"sm\" onClick={() => void store.refresh()}>\n <RotateCcw /> Retry\n </Button>\n </Row>\n </Stack>\n )\n }\n\n if (store.status === 'loading') {\n return (\n <Stack className=\"h-full place-items-center p-8\" align=\"center\" justify=\"center\">\n <Text size=\"sm\" tone=\"muted\">\n Loading users…\n </Text>\n </Stack>\n )\n }\n\n // A member's detail opens in the portable edit shell (AD-19); the list otherwise.\n // The shell mounts its own CollectionViewProvider, so only the list wraps here.\n if (userId !== null) {\n return <PortableDetailEditor source={source} projectId={projectId} documentId={userId} />\n }\n\n return (\n <CollectionViewProvider source={source}>\n <LoadedCollectionTable projectId={projectId} />\n </CollectionViewProvider>\n )\n}\n"]}
1
+ {"version":3,"file":"users-module.js","sourceRoot":"","sources":["../../../src/components/users/users-module.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AlertTriangle, RotateCcw, ShieldAlert } from 'lucide-react'\nimport { Button, Heading, Row, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { useProjectRole } from '../auth-context'\nimport { UsersProvider, useUsers } from './users-store'\nimport { useUsersCollectionViewSource } from './users-collection-view-source'\nimport { CollectionViewProvider } from '../collection-view/collection-view-context'\nimport { LoadedCollectionTable } from '../cms/collection-table'\nimport { PortableDetailEditor } from '../cms/detail-editor'\n\n/**\n * The studio Users module (EC-240). An admin-only section for managing who has\n * access to this deployment and their role. AD-19: it is the **reuse proof** for\n * the portable interaction surfaces — it renders the SAME `LoadedCollectionTable`\n * the CMS uses for the roster, and the SAME `DetailEditor` body (via\n * {@link PortableDetailEditor}) for a member's detail, both over a users\n * {@link useUsersCollectionViewSource} instead of a CMS source. No bespoke roster,\n * no bespoke editor: filtering, columns, the count, and the edit form all come\n * from the shared surfaces. The users capability profile (admin-only, no publish/\n * versions/create/import) drives which regions appear.\n *\n * `userId === null` is the roster; a `userId` opens that member's detail (routed\n * via `studioPath.user`). Both render under one `UsersProvider` + one source.\n *\n * Gating is layered: the operations chokepoint AND the Convex handlers enforce\n * admin-only server-side (a non-admin `users.list` is denied). This UI gate is UX\n * politeness — a non-admin sees a clear \"restricted\" state instead of a raw\n * authorization error, and we skip firing a query we know will be denied.\n */\nexport function UsersModule({\n projectId,\n userId = null,\n}: {\n projectId: string\n userId?: string | null\n}) {\n const role = useProjectRole(projectId)\n\n // `useProjectRole` conflates \"loading\" and \"not a member\" as null; in the studio\n // a member always reaches here, so null resolves to the real role momentarily.\n if (role === null) {\n return (\n <Stack className=\"h-full place-items-center p-8\" align=\"center\" justify=\"center\">\n <Text size=\"sm\" tone=\"muted\">\n {translate('users.checkingAccess')}\n </Text>\n </Stack>\n )\n }\n if (role !== 'admin') return <RestrictedState />\n\n return (\n <UsersProvider projectId={projectId}>\n <UsersSurface projectId={projectId} userId={userId} />\n </UsersProvider>\n )\n}\n\n/** The non-admin gate: honest about why the surface is empty. */\nfunction RestrictedState() {\n return (\n <Stack gap=\"2\" align=\"center\" justify=\"center\" className=\"h-full p-8 text-center\">\n <ShieldAlert className=\"size-6 text-muted-foreground\" />\n <Heading level={2} size=\"h3\">\n {translate('users.restrictedTitle')}\n </Heading>\n <Text size=\"sm\" tone=\"muted\" className=\"max-w-sm\">\n {translate('users.restrictedBody')}\n </Text>\n </Stack>\n )\n}\n\n/** The loaded surface: the portable list (or a member's detail) over the users source. */\nfunction UsersSurface({ projectId, userId }: { projectId: string; userId: string | null }) {\n const store = useUsers()\n const source = useUsersCollectionViewSource(projectId)\n\n if (store.status === 'error') {\n return (\n <Stack gap=\"2\" className=\"p-6\" data-testid=\"users-error\">\n <Row gap=\"2\">\n <AlertTriangle className=\"size-4 text-destructive\" />\n <Text weight=\"semibold\">{translate('users.loadFailed')}</Text>\n </Row>\n <Text size=\"sm\" tone=\"muted\">\n {store.errorMessage ?? translate('common.backendUnreachable')}\n </Text>\n <Row>\n <Button variant=\"outline\" size=\"sm\" onClick={() => void store.refresh()}>\n <RotateCcw /> {translate('common.retry')}\n </Button>\n </Row>\n </Stack>\n )\n }\n\n if (store.status === 'loading') {\n return (\n <Stack className=\"h-full place-items-center p-8\" align=\"center\" justify=\"center\">\n <Text size=\"sm\" tone=\"muted\">\n {translate('users.loading')}\n </Text>\n </Stack>\n )\n }\n\n // A member's detail opens in the portable edit shell (AD-19); the list otherwise.\n // The shell mounts its own CollectionViewProvider, so only the list wraps here.\n if (userId !== null) {\n return <PortableDetailEditor source={source} projectId={projectId} documentId={userId} />\n }\n\n return (\n <CollectionViewProvider source={source}>\n <LoadedCollectionTable projectId={projectId} />\n </CollectionViewProvider>\n )\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Card, CardContent, CardHeader, CardTitle, Code, Stack, Text } from '@elytracms/studio/ui';
3
3
  import { getWorkspaceState } from '@elytracms/studio/lib/workspace';
4
+ import { translate } from '@elytracms/studio/lib/i18n';
4
5
  /**
5
6
  * Workspace-config boot gate (EC-154) — sibling of the EC-153 `BackendGate`.
6
7
  * The workspace config is static boot input (bundled `elytra.config.ts` or
@@ -16,7 +17,7 @@ export function WorkspaceGate({ children }) {
16
17
  return _jsx(_Fragment, { children: children });
17
18
  }
18
19
  function WorkspaceConfigErrorScreen({ workspace, }) {
19
- return (_jsx("div", { className: "grid min-h-dvh place-items-center bg-background p-6", children: _jsxs(Card, { className: "max-w-2xl border-destructive/40", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: "Workspace config is invalid" }) }), _jsx(CardContent, { children: _jsxs(Stack, { gap: "3", children: [_jsxs(Text, { size: "sm", tone: "muted", children: ["The studio loaded a workspace config from ", _jsx(Code, { children: workspace.source }), ", but it failed validation. Fix the issues below and reload \u2014 the studio never falls back to unconfigured behavior while a config is present."] }), _jsx(Stack, { gap: "2", role: "list", "aria-label": "Workspace config issues", children: workspace.issues.map((issue, index) => (_jsx(WorkspaceIssueLine, { code: issue.code, message: issue.message, location: formatIssuePath(issue.path) }, `${issue.code}-${index}`))) }), _jsxs(Text, { size: "xs", tone: "muted", children: ["Validate locally with ", _jsx(Code, { children: "builder init --project <slug>" }), " \u2014 the CLI runs the same schema and reports the same issues."] })] }) })] }) }));
20
+ return (_jsx("div", { className: "grid min-h-dvh place-items-center bg-background p-6", children: _jsxs(Card, { className: "max-w-2xl border-destructive/40", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: translate('shell.workspaceGate.title') }) }), _jsx(CardContent, { children: _jsxs(Stack, { gap: "3", children: [_jsxs(Text, { size: "sm", tone: "muted", children: ["The studio loaded a workspace config from ", _jsx(Code, { children: workspace.source }), ", but it failed validation. Fix the issues below and reload \u2014 the studio never falls back to unconfigured behavior while a config is present."] }), _jsx(Stack, { gap: "2", role: "list", "aria-label": translate('shell.workspaceGate.issuesLabel'), children: workspace.issues.map((issue, index) => (_jsx(WorkspaceIssueLine, { code: issue.code, message: issue.message, location: formatIssuePath(issue.path) }, `${issue.code}-${index}`))) }), _jsxs(Text, { size: "xs", tone: "muted", children: ["Validate locally with ", _jsx(Code, { children: "builder init --project <slug>" }), " \u2014 the CLI runs the same schema and reports the same issues."] })] }) })] }) }));
20
21
  }
21
22
  function WorkspaceIssueLine({ code, message, location, }) {
22
23
  return (_jsxs("div", { role: "listitem", className: "rounded-md border border-destructive/30 bg-destructive/5 p-2", children: [_jsxs(Text, { size: "xs", mono: true, className: "text-destructive", children: ["[", code, "]", location ? ` at ${location}` : ''] }), _jsx(Text, { size: "sm", children: message })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-gate.js","sourceRoot":"","sources":["../../src/components/workspace-gate.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AAGnE;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,EAAE,QAAQ,EAA2B;IACjE,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAA;IACrC,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,KAAC,0BAA0B,IAAC,SAAS,EAAE,SAAS,GAAI,CAAA;IAC7F,OAAO,4BAAG,QAAQ,GAAI,CAAA;AACxB,CAAC;AAED,SAAS,0BAA0B,CAAC,EAClC,SAAS,GAGV;IACC,OAAO,CACL,cAAK,SAAS,EAAC,qDAAqD,YAClE,MAAC,IAAI,IAAC,SAAS,EAAC,iCAAiC,aAC/C,KAAC,UAAU,cACT,KAAC,SAAS,8CAAwC,GACvC,EACb,KAAC,WAAW,cACV,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,aACZ,MAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,2DACgB,KAAC,IAAI,cAAE,SAAS,CAAC,MAAM,GAAQ,0JAGpE,EACP,KAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,IAAI,EAAC,MAAM,gBAAY,yBAAyB,YAC5D,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CACtC,KAAC,kBAAkB,IAEjB,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,OAAO,EAAE,KAAK,CAAC,OAAO,EACtB,QAAQ,EAAE,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,IAHhC,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,EAAE,CAI7B,CACH,CAAC,GACI,EACR,MAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,uCACJ,KAAC,IAAI,gDAA2C,yEAEjE,IACD,GACI,IACT,GACH,CACP,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,EAC1B,IAAI,EACJ,OAAO,EACP,QAAQ,GAKT;IACC,OAAO,CACL,eAAK,IAAI,EAAC,UAAU,EAAC,SAAS,EAAC,8DAA8D,aAC3F,MAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,QAAC,SAAS,EAAC,kBAAkB,kBAC7C,IAAI,OAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,IACrC,EACP,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,YAAE,OAAO,GAAQ,IAC5B,CACP,CAAA;AACH,CAAC;AAED,SAAS,eAAe,CAAC,IAAoC;IAC3D,OAAO,IAAI,CAAC,MAAM,CAChB,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAC7F,EAAE,CACH,CAAA;AACH,CAAC","sourcesContent":["import type { ReactNode } from 'react'\nimport { Card, CardContent, CardHeader, CardTitle, Code, Stack, Text } from '@elytracms/studio/ui'\nimport { getWorkspaceState } from '@elytracms/studio/lib/workspace'\nimport type { WorkspaceState } from '@elytracms/studio/lib/workspace'\n\n/**\n * Workspace-config boot gate (EC-154) — sibling of the EC-153 `BackendGate`.\n * The workspace config is static boot input (bundled `elytra.config.ts` or\n * env-injected JSON), so an *invalid* config is a deterministic boot-error\n * state rendered before auth and the studio store: structured, file-located\n * issues — never a silent fall-back to unconfigured behavior. No config and a\n * valid config render children untouched.\n */\nexport function WorkspaceGate({ children }: { children: ReactNode }) {\n const workspace = getWorkspaceState()\n if (workspace.kind === 'invalid') return <WorkspaceConfigErrorScreen workspace={workspace} />\n return <>{children}</>\n}\n\nfunction WorkspaceConfigErrorScreen({\n workspace,\n}: {\n workspace: Extract<WorkspaceState, { kind: 'invalid' }>\n}) {\n return (\n <div className=\"grid min-h-dvh place-items-center bg-background p-6\">\n <Card className=\"max-w-2xl border-destructive/40\">\n <CardHeader>\n <CardTitle>Workspace config is invalid</CardTitle>\n </CardHeader>\n <CardContent>\n <Stack gap=\"3\">\n <Text size=\"sm\" tone=\"muted\">\n The studio loaded a workspace config from <Code>{workspace.source}</Code>, but it\n failed validation. Fix the issues below and reload — the studio never falls back to\n unconfigured behavior while a config is present.\n </Text>\n <Stack gap=\"2\" role=\"list\" aria-label=\"Workspace config issues\">\n {workspace.issues.map((issue, index) => (\n <WorkspaceIssueLine\n key={`${issue.code}-${index}`}\n code={issue.code}\n message={issue.message}\n location={formatIssuePath(issue.path)}\n />\n ))}\n </Stack>\n <Text size=\"xs\" tone=\"muted\">\n Validate locally with <Code>builder init --project &lt;slug&gt;</Code> — the CLI runs\n the same schema and reports the same issues.\n </Text>\n </Stack>\n </CardContent>\n </Card>\n </div>\n )\n}\n\nfunction WorkspaceIssueLine({\n code,\n message,\n location,\n}: {\n code: string\n message: string\n location: string\n}) {\n return (\n <div role=\"listitem\" className=\"rounded-md border border-destructive/30 bg-destructive/5 p-2\">\n <Text size=\"xs\" mono className=\"text-destructive\">\n [{code}]{location ? ` at ${location}` : ''}\n </Text>\n <Text size=\"sm\">{message}</Text>\n </div>\n )\n}\n\nfunction formatIssuePath(path: ReadonlyArray<string | number>): string {\n return path.reduce<string>(\n (acc, part) => (typeof part === 'number' ? `${acc}[${part}]` : acc ? `${acc}.${part}` : part),\n '',\n )\n}\n"]}
1
+ {"version":3,"file":"workspace-gate.js","sourceRoot":"","sources":["../../src/components/workspace-gate.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AAEnE,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAEtD;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,EAAE,QAAQ,EAA2B;IACjE,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAA;IACrC,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,KAAC,0BAA0B,IAAC,SAAS,EAAE,SAAS,GAAI,CAAA;IAC7F,OAAO,4BAAG,QAAQ,GAAI,CAAA;AACxB,CAAC;AAED,SAAS,0BAA0B,CAAC,EAClC,SAAS,GAGV;IACC,OAAO,CACL,cAAK,SAAS,EAAC,qDAAqD,YAClE,MAAC,IAAI,IAAC,SAAS,EAAC,iCAAiC,aAC/C,KAAC,UAAU,cACT,KAAC,SAAS,cAAE,SAAS,CAAC,2BAA2B,CAAC,GAAa,GACpD,EACb,KAAC,WAAW,cACV,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,aACZ,MAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,2DACgB,KAAC,IAAI,cAAE,SAAS,CAAC,MAAM,GAAQ,0JAGpE,EACP,KAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,IAAI,EAAC,MAAM,gBAAa,SAAS,CAAC,iCAAiC,CAAC,YAChF,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CACtC,KAAC,kBAAkB,IAEjB,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,OAAO,EAAE,KAAK,CAAC,OAAO,EACtB,QAAQ,EAAE,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,IAHhC,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,EAAE,CAI7B,CACH,CAAC,GACI,EACR,MAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,uCACJ,KAAC,IAAI,gDAA2C,yEAEjE,IACD,GACI,IACT,GACH,CACP,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,EAC1B,IAAI,EACJ,OAAO,EACP,QAAQ,GAKT;IACC,OAAO,CACL,eAAK,IAAI,EAAC,UAAU,EAAC,SAAS,EAAC,8DAA8D,aAC3F,MAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,QAAC,SAAS,EAAC,kBAAkB,kBAC7C,IAAI,OAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,IACrC,EACP,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,YAAE,OAAO,GAAQ,IAC5B,CACP,CAAA;AACH,CAAC;AAED,SAAS,eAAe,CAAC,IAAoC;IAC3D,OAAO,IAAI,CAAC,MAAM,CAChB,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAC7F,EAAE,CACH,CAAA;AACH,CAAC","sourcesContent":["import type { ReactNode } from 'react'\nimport { Card, CardContent, CardHeader, CardTitle, Code, Stack, Text } from '@elytracms/studio/ui'\nimport { getWorkspaceState } from '@elytracms/studio/lib/workspace'\nimport type { WorkspaceState } from '@elytracms/studio/lib/workspace'\nimport { translate } from '@elytracms/studio/lib/i18n'\n\n/**\n * Workspace-config boot gate (EC-154) — sibling of the EC-153 `BackendGate`.\n * The workspace config is static boot input (bundled `elytra.config.ts` or\n * env-injected JSON), so an *invalid* config is a deterministic boot-error\n * state rendered before auth and the studio store: structured, file-located\n * issues — never a silent fall-back to unconfigured behavior. No config and a\n * valid config render children untouched.\n */\nexport function WorkspaceGate({ children }: { children: ReactNode }) {\n const workspace = getWorkspaceState()\n if (workspace.kind === 'invalid') return <WorkspaceConfigErrorScreen workspace={workspace} />\n return <>{children}</>\n}\n\nfunction WorkspaceConfigErrorScreen({\n workspace,\n}: {\n workspace: Extract<WorkspaceState, { kind: 'invalid' }>\n}) {\n return (\n <div className=\"grid min-h-dvh place-items-center bg-background p-6\">\n <Card className=\"max-w-2xl border-destructive/40\">\n <CardHeader>\n <CardTitle>{translate('shell.workspaceGate.title')}</CardTitle>\n </CardHeader>\n <CardContent>\n <Stack gap=\"3\">\n <Text size=\"sm\" tone=\"muted\">\n The studio loaded a workspace config from <Code>{workspace.source}</Code>, but it\n failed validation. Fix the issues below and reload — the studio never falls back to\n unconfigured behavior while a config is present.\n </Text>\n <Stack gap=\"2\" role=\"list\" aria-label={translate('shell.workspaceGate.issuesLabel')}>\n {workspace.issues.map((issue, index) => (\n <WorkspaceIssueLine\n key={`${issue.code}-${index}`}\n code={issue.code}\n message={issue.message}\n location={formatIssuePath(issue.path)}\n />\n ))}\n </Stack>\n <Text size=\"xs\" tone=\"muted\">\n Validate locally with <Code>builder init --project &lt;slug&gt;</Code> — the CLI runs\n the same schema and reports the same issues.\n </Text>\n </Stack>\n </CardContent>\n </Card>\n </div>\n )\n}\n\nfunction WorkspaceIssueLine({\n code,\n message,\n location,\n}: {\n code: string\n message: string\n location: string\n}) {\n return (\n <div role=\"listitem\" className=\"rounded-md border border-destructive/30 bg-destructive/5 p-2\">\n <Text size=\"xs\" mono className=\"text-destructive\">\n [{code}]{location ? ` at ${location}` : ''}\n </Text>\n <Text size=\"sm\">{message}</Text>\n </div>\n )\n}\n\nfunction formatIssuePath(path: ReadonlyArray<string | number>): string {\n return path.reduce<string>(\n (acc, part) => (typeof part === 'number' ? `${acc}[${part}]` : acc ? `${acc}.${part}` : part),\n '',\n )\n}\n"]}
@@ -7,9 +7,16 @@ import type { SourceCatalog } from './types';
7
7
  * the key itself (a visible fallback + a one-time console signal), never a silent
8
8
  * blank.
9
9
  *
10
- * v1 seeds a representative, high-traffic slice — the section rail + the shell user
11
- * menu — to prove the pipeline end-to-end. Extracting every remaining studio string
12
- * into keys is an ongoing mechanical sweep (see EC-241 "scope honesty"), not a
13
- * silent omission.
10
+ * Organised by surface. `common.*` holds generic, reused affordances (buttons,
11
+ * status words); every other namespace maps to one studio surface. Values that come
12
+ * from the project's schema/config (field labels, collection names, component
13
+ * manifests) are NOT here — those are `LocalizedString`s resolved by `resolveLabel`
14
+ * (the separate schema-label axis).
15
+ *
16
+ * Scope honesty (EC-241): this covers the studio's **component chrome**. A handful
17
+ * of `lib/*` label helpers (`persona/labels`, `cms-views` operator labels,
18
+ * `auth-errors`, `clipboard` summaries, core `describeAccept`) and the JSX-chip
19
+ * developer boot hints (backend/workspace gates) are a documented follow-up — they
20
+ * need a message-with-elements splitter beyond the plain-string `translate()`.
14
21
  */
15
22
  export declare const EN_MESSAGES: SourceCatalog;