@elytracms/studio 0.0.85 → 0.0.87

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 (248) hide show
  1. package/convex/asset-revalidate.test.ts +186 -0
  2. package/convex/assets.ts +53 -19
  3. package/convex/cms.ts +22 -0
  4. package/convex/delivery.ts +54 -116
  5. package/convex/engine-version.test.ts +31 -0
  6. package/convex/functions.ts +3 -3
  7. package/convex/rail-write-parity.test.ts +180 -0
  8. package/convex/redirects.ts +33 -11
  9. package/convex/revalidate.ts +7 -5
  10. package/convex/triggers.ts +53 -3
  11. package/dist/components/ai/ai-chat-panel.js +13 -2
  12. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  13. package/dist/components/app-views/default-modules.d.ts +20 -20
  14. package/dist/components/app-views/default-modules.js +27 -48
  15. package/dist/components/app-views/default-modules.js.map +1 -1
  16. package/dist/components/app-views/studio-config.d.ts +5 -6
  17. package/dist/components/app-views/studio-config.js.map +1 -1
  18. package/dist/components/assets/asset-metadata.d.ts +28 -19
  19. package/dist/components/assets/asset-metadata.js +24 -31
  20. package/dist/components/assets/asset-metadata.js.map +1 -1
  21. package/dist/components/assets/asset-selection.d.ts +24 -5
  22. package/dist/components/assets/asset-selection.js +39 -9
  23. package/dist/components/assets/asset-selection.js.map +1 -1
  24. package/dist/components/assets/assets-collection-view-source.d.ts +17 -14
  25. package/dist/components/assets/assets-collection-view-source.js +25 -31
  26. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  27. package/dist/components/builder/blocks-model.d.ts +5 -4
  28. package/dist/components/builder/blocks-model.js +5 -4
  29. package/dist/components/builder/blocks-model.js.map +1 -1
  30. package/dist/components/builder/blocks-panel.js +3 -3
  31. package/dist/components/builder/blocks-panel.js.map +1 -1
  32. package/dist/components/builder/builder-store.d.ts +25 -1
  33. package/dist/components/builder/builder-store.js +3 -2
  34. package/dist/components/builder/builder-store.js.map +1 -1
  35. package/dist/components/builder/canvas-chrome-css.js +0 -17
  36. package/dist/components/builder/canvas-chrome-css.js.map +1 -1
  37. package/dist/components/builder/canvas-chrome.d.ts +10 -12
  38. package/dist/components/builder/canvas-chrome.js +12 -13
  39. package/dist/components/builder/canvas-chrome.js.map +1 -1
  40. package/dist/components/builder/canvas-dnd.d.ts +8 -0
  41. package/dist/components/builder/canvas-dnd.js +5 -3
  42. package/dist/components/builder/canvas-dnd.js.map +1 -1
  43. package/dist/components/builder/canvas-render-context.d.ts +12 -24
  44. package/dist/components/builder/canvas-render-context.js +15 -44
  45. package/dist/components/builder/canvas-render-context.js.map +1 -1
  46. package/dist/components/builder/canvas.js +5 -11
  47. package/dist/components/builder/canvas.js.map +1 -1
  48. package/dist/components/builder/clipboard-actions.d.ts +15 -1
  49. package/dist/components/builder/clipboard-actions.js +35 -12
  50. package/dist/components/builder/clipboard-actions.js.map +1 -1
  51. package/dist/components/builder/composition-field-editor.d.ts +32 -26
  52. package/dist/components/builder/composition-field-editor.js +47 -54
  53. package/dist/components/builder/composition-field-editor.js.map +1 -1
  54. package/dist/components/builder/insert-context.d.ts +3 -1
  55. package/dist/components/builder/insert-context.js +19 -12
  56. package/dist/components/builder/insert-context.js.map +1 -1
  57. package/dist/components/builder/page-tree.d.ts +29 -0
  58. package/dist/components/builder/page-tree.js +56 -58
  59. package/dist/components/builder/page-tree.js.map +1 -1
  60. package/dist/components/builder/reuse.js +5 -2
  61. package/dist/components/builder/reuse.js.map +1 -1
  62. package/dist/components/cms/asset-field.js +20 -32
  63. package/dist/components/cms/asset-field.js.map +1 -1
  64. package/dist/components/cms/cell-edit.d.ts +7 -0
  65. package/dist/components/cms/cell-edit.js +9 -1
  66. package/dist/components/cms/cell-edit.js.map +1 -1
  67. package/dist/components/cms/cms-composition-validators.js +11 -2
  68. package/dist/components/cms/cms-composition-validators.js.map +1 -1
  69. package/dist/components/cms/collection-table.js +11 -0
  70. package/dist/components/cms/collection-table.js.map +1 -1
  71. package/dist/components/cms/document-url-path.d.ts +7 -4
  72. package/dist/components/cms/document-url-path.js +15 -1
  73. package/dist/components/cms/document-url-path.js.map +1 -1
  74. package/dist/components/cms/editable-cell.js +5 -2
  75. package/dist/components/cms/editable-cell.js.map +1 -1
  76. package/dist/components/cms/field-control.js +4 -0
  77. package/dist/components/cms/field-control.js.map +1 -1
  78. package/dist/components/cms/inline-field-errors.d.ts +36 -0
  79. package/dist/components/cms/inline-field-errors.js +13 -5
  80. package/dist/components/cms/inline-field-errors.js.map +1 -1
  81. package/dist/components/cms/rich-text/insert.d.ts +6 -3
  82. package/dist/components/cms/rich-text/insert.js +9 -6
  83. package/dist/components/cms/rich-text/insert.js.map +1 -1
  84. package/dist/components/cms/use-document-versions.js +1 -0
  85. package/dist/components/cms/use-document-versions.js.map +1 -1
  86. package/dist/components/cms/use-live-url.d.ts +18 -6
  87. package/dist/components/cms/use-live-url.js +10 -0
  88. package/dist/components/cms/use-live-url.js.map +1 -1
  89. package/dist/components/cms/versions-model.d.ts +0 -12
  90. package/dist/components/cms/versions-model.js +3 -15
  91. package/dist/components/cms/versions-model.js.map +1 -1
  92. package/dist/components/components/component-detail.js +1 -1
  93. package/dist/components/components/component-detail.js.map +1 -1
  94. package/dist/components/components/transfer-dialog.js +2 -4
  95. package/dist/components/components/transfer-dialog.js.map +1 -1
  96. package/dist/components/components/transfer.d.ts +19 -15
  97. package/dist/components/components/transfer.js +28 -25
  98. package/dist/components/components/transfer.js.map +1 -1
  99. package/dist/components/deployment-setup-gate.js +9 -3
  100. package/dist/components/deployment-setup-gate.js.map +1 -1
  101. package/dist/components/project-session.d.ts +9 -0
  102. package/dist/components/project-session.js +1 -1
  103. package/dist/components/project-session.js.map +1 -1
  104. package/dist/components/redirects/redirects-collection-view-source.d.ts +17 -2
  105. package/dist/components/redirects/redirects-collection-view-source.js +20 -6
  106. package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
  107. package/dist/components/section-surface.js +3 -2
  108. package/dist/components/section-surface.js.map +1 -1
  109. package/dist/components/settings-form.js +1 -2
  110. package/dist/components/settings-form.js.map +1 -1
  111. package/dist/components/studio-context.js +1 -18
  112. package/dist/components/studio-context.js.map +1 -1
  113. package/dist/components/studio-project-screen.js +3 -5
  114. package/dist/components/studio-project-screen.js.map +1 -1
  115. package/dist/lib/ai/apply-content.js +73 -29
  116. package/dist/lib/ai/apply-content.js.map +1 -1
  117. package/dist/lib/ai/constraints.d.ts +35 -0
  118. package/dist/lib/ai/constraints.js +155 -72
  119. package/dist/lib/ai/constraints.js.map +1 -1
  120. package/dist/lib/ai/index.d.ts +1 -0
  121. package/dist/lib/ai/index.js +1 -0
  122. package/dist/lib/ai/index.js.map +1 -1
  123. package/dist/lib/ai/route-fetch.d.ts +58 -0
  124. package/dist/lib/ai/route-fetch.js +98 -0
  125. package/dist/lib/ai/route-fetch.js.map +1 -0
  126. package/dist/lib/ai/route-provider.d.ts +5 -22
  127. package/dist/lib/ai/route-provider.js +23 -1
  128. package/dist/lib/ai/route-provider.js.map +1 -1
  129. package/dist/lib/ai/route.js +4 -1
  130. package/dist/lib/ai/route.js.map +1 -1
  131. package/dist/lib/ai/shape.d.ts +3 -2
  132. package/dist/lib/ai/shape.js +16 -24
  133. package/dist/lib/ai/shape.js.map +1 -1
  134. package/dist/lib/ai/types.d.ts +8 -0
  135. package/dist/lib/ai/types.js.map +1 -1
  136. package/dist/lib/ai/update-fields.js +33 -12
  137. package/dist/lib/ai/update-fields.js.map +1 -1
  138. package/dist/lib/assets/asset-placements.d.ts +35 -0
  139. package/dist/lib/assets/asset-placements.js +74 -0
  140. package/dist/lib/assets/asset-placements.js.map +1 -0
  141. package/dist/lib/assets/backfill-thumbs.d.ts +9 -8
  142. package/dist/lib/assets/backfill-thumbs.js +13 -25
  143. package/dist/lib/assets/backfill-thumbs.js.map +1 -1
  144. package/dist/lib/clipboard/paste-issues.d.ts +17 -15
  145. package/dist/lib/clipboard/paste-issues.js +8 -45
  146. package/dist/lib/clipboard/paste-issues.js.map +1 -1
  147. package/dist/lib/clipboard/paste-target.d.ts +47 -1
  148. package/dist/lib/clipboard/paste-target.js +19 -11
  149. package/dist/lib/clipboard/paste-target.js.map +1 -1
  150. package/dist/lib/cms/document-gate.d.ts +19 -9
  151. package/dist/lib/cms/document-gate.js +25 -4
  152. package/dist/lib/cms/document-gate.js.map +1 -1
  153. package/dist/lib/cms/published-documents.js +1 -0
  154. package/dist/lib/cms/published-documents.js.map +1 -1
  155. package/dist/lib/cms-views/view-model.d.ts +3 -3
  156. package/dist/lib/data/index.d.ts +0 -3
  157. package/dist/lib/data/index.js +4 -6
  158. package/dist/lib/data/index.js.map +1 -1
  159. package/dist/lib/dnd/drop-rules.d.ts +45 -2
  160. package/dist/lib/dnd/drop-rules.js +64 -9
  161. package/dist/lib/dnd/drop-rules.js.map +1 -1
  162. package/dist/lib/engine-version.d.ts +16 -0
  163. package/dist/lib/engine-version.js +17 -0
  164. package/dist/lib/engine-version.js.map +1 -0
  165. package/dist/lib/i18n/messages.de.js +8 -56
  166. package/dist/lib/i18n/messages.de.js.map +1 -1
  167. package/dist/lib/i18n/messages.en.d.ts +8 -53
  168. package/dist/lib/i18n/messages.en.js +15 -57
  169. package/dist/lib/i18n/messages.en.js.map +1 -1
  170. package/dist/lib/locale/fallback.js +8 -2
  171. package/dist/lib/locale/fallback.js.map +1 -1
  172. package/dist/lib/locale/index.d.ts +0 -1
  173. package/dist/lib/locale/index.js +0 -1
  174. package/dist/lib/locale/index.js.map +1 -1
  175. package/dist/lib/locale/locale-store.d.ts +10 -3
  176. package/dist/lib/locale/locale-store.js +12 -5
  177. package/dist/lib/locale/locale-store.js.map +1 -1
  178. package/dist/lib/operations/session-hydration.d.ts +9 -5
  179. package/dist/lib/operations/session-hydration.js +10 -10
  180. package/dist/lib/operations/session-hydration.js.map +1 -1
  181. package/dist/lib/operations/studio-operations.js +4 -2
  182. package/dist/lib/operations/studio-operations.js.map +1 -1
  183. package/dist/lib/persistence/convex-adapter.js +63 -75
  184. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  185. package/dist/lib/persistence/deployment-readiness.d.ts +8 -1
  186. package/dist/lib/persistence/deployment-readiness.js +4 -0
  187. package/dist/lib/persistence/deployment-readiness.js.map +1 -1
  188. package/dist/lib/persona/labels.d.ts +0 -2
  189. package/dist/lib/persona/labels.js +1 -7
  190. package/dist/lib/persona/labels.js.map +1 -1
  191. package/dist/lib/registry/index.d.ts +4 -2
  192. package/dist/lib/registry/index.js +26 -21
  193. package/dist/lib/registry/index.js.map +1 -1
  194. package/dist/lib/registry/project-components.js +0 -3
  195. package/dist/lib/registry/project-components.js.map +1 -1
  196. package/dist/lib/routes/route-issues.js +4 -5
  197. package/dist/lib/routes/route-issues.js.map +1 -1
  198. package/dist/lib/theme.js +3 -2
  199. package/dist/lib/theme.js.map +1 -1
  200. package/dist/lib/webhooks/tags.js +11 -14
  201. package/dist/lib/webhooks/tags.js.map +1 -1
  202. package/dist/lib/workspace/index.d.ts +1 -1
  203. package/dist/lib/workspace/index.js +1 -1
  204. package/dist/lib/workspace/index.js.map +1 -1
  205. package/dist/previews/enumerate.d.ts +27 -9
  206. package/dist/previews/enumerate.js +27 -6
  207. package/dist/previews/enumerate.js.map +1 -1
  208. package/dist/previews/manifest-format.d.ts +16 -7
  209. package/dist/previews/manifest-format.js +0 -21
  210. package/dist/previews/manifest-format.js.map +1 -1
  211. package/dist/router/match.d.ts +0 -2
  212. package/dist/router/match.js +0 -2
  213. package/dist/router/match.js.map +1 -1
  214. package/dist/ui/badge.d.ts +1 -1
  215. package/package.json +2 -2
  216. package/src/styles/studio.css +5 -4
  217. package/src/styles/tokens.css +72 -55
  218. package/convex/content.ts +0 -291
  219. package/dist/components/collab/collab-context.d.ts +0 -27
  220. package/dist/components/collab/collab-context.js +0 -39
  221. package/dist/components/collab/collab-context.js.map +0 -1
  222. package/dist/components/collab/collab-seed.d.ts +0 -15
  223. package/dist/components/collab/collab-seed.js +0 -20
  224. package/dist/components/collab/collab-seed.js.map +0 -1
  225. package/dist/components/extensions/extensions-view.d.ts +0 -7
  226. package/dist/components/extensions/extensions-view.js +0 -127
  227. package/dist/components/extensions/extensions-view.js.map +0 -1
  228. package/dist/lib/bindings/section-bindings.d.ts +0 -24
  229. package/dist/lib/bindings/section-bindings.js +0 -9
  230. package/dist/lib/bindings/section-bindings.js.map +0 -1
  231. package/dist/lib/data/data-store.d.ts +0 -98
  232. package/dist/lib/data/data-store.js +0 -211
  233. package/dist/lib/data/data-store.js.map +0 -1
  234. package/dist/lib/data/providers.d.ts +0 -143
  235. package/dist/lib/data/providers.js +0 -255
  236. package/dist/lib/data/providers.js.map +0 -1
  237. package/dist/lib/data/sample-capture.d.ts +0 -30
  238. package/dist/lib/data/sample-capture.js +0 -70
  239. package/dist/lib/data/sample-capture.js.map +0 -1
  240. package/dist/lib/data/sources-store.d.ts +0 -42
  241. package/dist/lib/data/sources-store.js +0 -120
  242. package/dist/lib/data/sources-store.js.map +0 -1
  243. package/dist/lib/locale/binding-fallback.d.ts +0 -35
  244. package/dist/lib/locale/binding-fallback.js +0 -73
  245. package/dist/lib/locale/binding-fallback.js.map +0 -1
  246. package/dist/lib/starters/kit-session.d.ts +0 -5
  247. package/dist/lib/starters/kit-session.js +0 -17
  248. package/dist/lib/starters/kit-session.js.map +0 -1
@@ -1,127 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo, useReducer, useState } from 'react';
3
- import { Boxes, Check, Database, Download, HardDrive, Lock, Package, Puzzle, Search, Share2, LayoutTemplate, Type, Webhook, } from 'lucide-react';
4
- import { BUILTIN_FIELD_TYPES, BUILTIN_SOURCE_KINDS, HOOK_POINTS, MarketplaceClient, PluginHost, privateMarketplace, publicMarketplace, } from '@elytracms/core/plugins';
5
- import { Badge, Button, Code, Heading, Input, Row, Spacer, Stack, Text } from '@elytracms/studio/ui';
6
- import { translate } from '@elytracms/studio/lib/i18n';
7
- import { cn } from '@elytracms/studio/lib/utils';
8
- /**
9
- * The Extensions surface (Phase 11, EC-130–137). A live, agent-inspectable view over the
10
- * extensibility layer: browse a public + private marketplace, install plugins into a host, and
11
- * inspect what each surface contributes (components, field types, data sources, asset storage,
12
- * export targets, hooks). All state is deterministic — driven by `@elytracms/core/plugins` fixtures.
13
- */
14
- export function ExtensionsView() {
15
- // One host for the session; PluginHost mutates in place, so a bump reducer drives re-render.
16
- const [host] = useState(() => new PluginHost());
17
- const [, bump] = useReducer((n) => n + 1, 0);
18
- const [tab, setTab] = useState('marketplace');
19
- const [token, setToken] = useState('');
20
- const [query, setQuery] = useState('');
21
- const [results, setResults] = useState({});
22
- const client = useMemo(() => new MarketplaceClient([publicMarketplace, privateMarketplace], token.trim() ? { 'acme-private': token.trim() } : {}), [token]);
23
- const hits = client.search(query.trim() ? { query: query.trim() } : {});
24
- const privateUnlocked = client.accessibleRegistries().some((r) => r.id === 'acme-private');
25
- const installedCount = host.list().length;
26
- function install(listing) {
27
- const result = client.install(host, listing.id);
28
- setResults((prev) => ({ ...prev, [listing.id]: result }));
29
- bump();
30
- }
31
- return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx(ExtensionsHeader, { tab: tab, onTab: setTab, installedCount: installedCount, marketCount: hits.length }), _jsx("div", { className: "min-h-0 flex-1 overflow-auto px-4 py-5 sm:px-6", children: _jsx("div", { className: "mx-auto w-full max-w-5xl", children: tab === 'marketplace' ? (_jsx(MarketplaceTab, { hits: hits, query: query, onQuery: setQuery, token: token, onToken: setToken, privateUnlocked: privateUnlocked, isInstalled: (l) => host.isInstalled(l.plugin.id), onInstall: install, results: results })) : tab === 'installed' ? (_jsx(InstalledTab, { installed: host.list() })) : (_jsx(RegisteredTab, { host: host })) }) })] }));
32
- }
33
- // --- Header + tabs ----------------------------------------------------------
34
- function ExtensionsHeader({ tab, onTab, installedCount, marketCount, }) {
35
- const tabs = [
36
- { id: 'marketplace', label: translate('extensions.tab.marketplace'), count: marketCount },
37
- { id: 'installed', label: translate('extensions.tab.installed'), count: installedCount },
38
- { id: 'registered', label: translate('extensions.tab.registered') },
39
- ];
40
- return (_jsx("div", { className: "shrink-0 border-b border-border bg-gradient-to-b from-primary/[0.04] to-transparent px-4 py-3 sm:px-6", children: _jsxs(Row, { gap: "3", children: [_jsx("span", { className: "grid size-8 shrink-0 place-items-center rounded-lg bg-primary/15 text-primary", children: _jsx(Puzzle, { className: "size-4" }) }), _jsxs(Stack, { gap: "none", className: "min-w-0", children: [_jsx(Text, { size: "sm", weight: "semibold", children: translate('extensions.header.title') }), _jsx(Text, { size: "xs", tone: "subtle", children: translate('extensions.header.subtitle') })] }), _jsx(Spacer, {}), _jsx(Row, { gap: "1", className: "rounded-md bg-muted/50 p-0.5", children: tabs.map((t) => (_jsxs("button", { type: "button", "aria-pressed": tab === t.id, onClick: () => onTab(t.id), className: cn('flex items-center gap-1.5 rounded-[5px] px-3 py-1 text-sm font-medium transition-colors', tab === t.id ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'), children: [t.label, t.count !== undefined ? (_jsx("span", { className: "rounded-full bg-muted px-1.5 text-[0.7rem] font-semibold tabular-nums text-muted-foreground", children: t.count })) : null] }, t.id))) })] }) }));
41
- }
42
- // --- Marketplace ------------------------------------------------------------
43
- function MarketplaceTab({ hits, query, onQuery, token, onToken, privateUnlocked, isInstalled, onInstall, results, }) {
44
- return (_jsxs(Stack, { gap: "6", children: [_jsxs(Row, { gap: "3", wrap: true, children: [_jsxs("div", { className: "relative min-w-0 flex-1", children: [_jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: query, onChange: (e) => onQuery(e.target.value), placeholder: translate('extensions.search.placeholder'), className: "pl-8", "aria-label": translate('extensions.search.aria') })] }), _jsxs(Row, { gap: "2", className: "shrink-0", children: [_jsx(Lock, { className: cn('size-3.5', privateUnlocked ? 'text-success' : 'text-muted-foreground') }), _jsx(Input, { value: token, onChange: (e) => onToken(e.target.value), placeholder: translate('extensions.token.placeholder'), "aria-label": translate('extensions.token.placeholder'), className: "w-52" }), privateUnlocked ? _jsx(Badge, { variant: "success", children: translate('extensions.token.unlocked') }) : null] })] }), !privateUnlocked ? (_jsxs(Text, { size: "xs", tone: "subtle", children: ["A private registry is available. Enter its access token (hint: ", _jsx(Code, { tone: "muted", children: "acme-internal-token" }), ") to reveal private listings."] })) : null, hits.length === 0 ? (_jsx(EmptyHint, { icon: _jsx(Search, { className: "size-5" }), title: translate('extensions.empty.noMatch.title'), body: translate('extensions.empty.noMatch.body') })) : (_jsx("div", { className: "grid gap-3 sm:grid-cols-2", children: hits.map(({ registryId, listing }) => (_jsx(ListingCard, { listing: listing, registryId: registryId, installed: isInstalled(listing), result: results[listing.id], onInstall: () => onInstall(listing) }, listing.id))) }))] }));
45
- }
46
- function ListingCard({ listing, registryId, installed, result, onInstall, }) {
47
- const failed = result && !result.ok;
48
- return (_jsxs("div", { className: "flex flex-col rounded-xl border border-border bg-card/60 p-4 transition-colors hover:border-primary/40", children: [_jsxs(Row, { gap: "2", children: [_jsx("span", { className: "grid size-9 shrink-0 place-items-center rounded-lg bg-muted text-foreground/80", children: _jsx(KindIcon, { kind: listing.kind }) }), _jsxs(Stack, { gap: "none", className: "min-w-0", children: [_jsx(Text, { size: "sm", weight: "semibold", className: "truncate", children: listing.name }), _jsxs(Row, { gap: "1", className: "text-muted-foreground", children: [_jsx(KindBadge, { kind: listing.kind }), _jsxs(Code, { tone: "muted", children: ["v", listing.version] })] })] }), _jsx(Spacer, {}), registryId === 'acme-private' ? (_jsxs(Badge, { variant: "outline", children: [_jsx(Lock, { className: "size-3" }), " private"] })) : null] }), _jsx(Text, { size: "xs", tone: "muted", className: "mt-3 line-clamp-3 min-h-0 flex-1", children: listing.description ?? translate('extensions.listing.noDescription') }), listing.tags && listing.tags.length > 0 ? (_jsx(Row, { gap: "1", wrap: true, className: "mt-3", children: listing.tags.map((tag) => (_jsx("span", { className: "rounded-full bg-muted/70 px-2 py-0.5 text-[0.7rem] text-muted-foreground", children: tag }, tag))) })) : null, _jsxs(Row, { gap: "2", className: "mt-4", children: [_jsx(Text, { size: "xs", tone: "subtle", className: "truncate", children: listing.author ?? translate('extensions.listing.unknownAuthor') }), _jsx(Spacer, {}), installed ? (_jsxs(Button, { size: "sm", variant: "ghost", disabled: true, children: [_jsx(Check, {}), " ", translate('extensions.action.installed')] })) : (_jsxs(Button, { size: "sm", variant: failed ? 'destructive' : 'default', onClick: onInstall, children: [_jsx(Download, {}), " ", failed ? translate('common.retry') : translate('extensions.action.install')] }))] }), failed ? (_jsx(Text, { size: "xs", className: "mt-2 text-destructive", children: result?.blocked === 'access-denied'
49
- ? translate('extensions.error.accessDenied')
50
- : result?.issues[0]?.message ?? translate('extensions.error.installFailed') })) : null] }));
51
- }
52
- // --- Installed --------------------------------------------------------------
53
- function InstalledTab({ installed }) {
54
- if (installed.length === 0) {
55
- return (_jsx(EmptyHint, { icon: _jsx(Boxes, { className: "size-5" }), title: translate('extensions.empty.installed.title'), body: translate('extensions.empty.installed.body') }));
56
- }
57
- return (_jsx(Stack, { gap: "3", children: installed.map((plugin) => (_jsxs("div", { className: "rounded-xl border border-border bg-card/60 p-4", children: [_jsxs(Row, { gap: "2", children: [_jsx(Package, { className: "size-4 text-primary" }), _jsx(Text, { size: "sm", weight: "semibold", children: plugin.name }), _jsxs(Code, { tone: "muted", children: ["v", plugin.version] }), _jsx(Spacer, {}), _jsx(Text, { size: "xs", tone: "subtle", children: plugin.author ?? '' })] }), plugin.description ? (_jsx(Text, { size: "xs", tone: "muted", className: "mt-2", children: plugin.description })) : null, _jsxs(Row, { gap: "2", wrap: true, className: "mt-3", children: [_jsx(ContributionChip, { icon: _jsx(Boxes, {}), label: translate('extensions.contrib.components'), count: plugin.contributes.components }), _jsx(ContributionChip, { icon: _jsx(Type, {}), label: translate('extensions.contrib.fieldTypes'), count: plugin.contributes.fieldTypes }), _jsx(ContributionChip, { icon: _jsx(Database, {}), label: translate('extensions.contrib.dataSources'), count: plugin.contributes.dataSources }), _jsx(ContributionChip, { icon: _jsx(HardDrive, {}), label: translate('extensions.contrib.assetStorage'), count: plugin.contributes.assetStorage }), _jsx(ContributionChip, { icon: _jsx(Share2, {}), label: translate('extensions.contrib.exportTargets'), count: plugin.contributes.exportTargets }), _jsx(ContributionChip, { icon: _jsx(LayoutTemplate, {}), label: translate('extensions.contrib.templates'), count: plugin.contributes.templates }), _jsx(ContributionChip, { icon: _jsx(Webhook, {}), label: translate('extensions.contrib.hooks'), count: plugin.contributes.hooks })] })] }, plugin.id))) }));
58
- }
59
- function ContributionChip({ icon, label, count }) {
60
- if (count === 0)
61
- return null;
62
- return (_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-md border border-border/60 bg-muted/40 px-2 py-1 text-xs text-muted-foreground", children: [_jsx("span", { className: "grid size-3.5 place-items-center text-primary [&_svg]:size-3.5", children: icon }), _jsx("span", { className: "font-semibold tabular-nums text-foreground", children: count }), label] }));
63
- }
64
- // --- Registered (inspect) ---------------------------------------------------
65
- function RegisteredTab({ host }) {
66
- const hookPoints = host.hooks.points();
67
- return (_jsxs(Stack, { gap: "6", children: [_jsx(Text, { size: "xs", tone: "subtle", children: translate('extensions.registered.intro') }), _jsx(InspectGroup, { icon: _jsx(Boxes, {}), title: translate('extensions.registered.components.title'), sub: "base.* + project + vendor packages", children: _jsx(ChipRow, { items: host.components.projectComponents().map((m) => m.id), empty: translate('extensions.registered.components.empty') }) }), _jsx(InspectGroup, { icon: _jsx(Type, {}), title: translate('extensions.registered.fieldTypes.title'), children: _jsx(ChipRow, { items: [...BUILTIN_FIELD_TYPES], tone: "builtin", suffix: host.fieldTypes.list().map((f) => f.kind) }) }), _jsx(InspectGroup, { icon: _jsx(Database, {}), title: translate('extensions.registered.dataSources.title'), children: _jsx(ChipRow, { items: [...BUILTIN_SOURCE_KINDS], tone: "builtin", suffix: host.dataSources.list().map((a) => a.kind) }) }), _jsx(InspectGroup, { icon: _jsx(HardDrive, {}), title: translate('extensions.registered.assetStorage.title'), children: _jsx(ChipRow, { items: host.assetStorage.list().map((a) => `${a.kind} · ${a.title}`), empty: translate('extensions.registered.assetStorage.empty') }) }), _jsx(InspectGroup, { icon: _jsx(Share2, {}), title: translate('extensions.registered.exportTargets.title'), children: _jsx(ChipRow, { items: ['next-app-router · reference'], tone: "builtin", suffix: host.exportTargets.list().map((t) => `${t.id} · ${t.title}`) }) }), _jsx(InspectGroup, { icon: _jsx(Webhook, {}), title: translate('extensions.registered.hookPoints.title'), children: _jsx(ChipRow, { items: [...HOOK_POINTS], tone: "builtin", suffix: hookPoints.flatMap((p) => host.hooks.list(p).map((t) => `${p}${t.plugin ? ` ← ${t.plugin}` : ''}`)) }) })] }));
68
- }
69
- function InspectGroup({ icon, title, sub, children, }) {
70
- return (_jsxs(Stack, { gap: "2", children: [_jsxs(Row, { gap: "2", children: [_jsx("span", { className: "grid size-5 place-items-center text-primary [&_svg]:size-4", children: icon }), _jsx(Text, { size: "sm", weight: "semibold", children: title }), sub ? (_jsx(Text, { size: "xs", tone: "subtle", children: sub })) : null] }), children] }));
71
- }
72
- function ChipRow({ items, suffix, tone, empty, }) {
73
- const all = [...items.map((v) => ({ v, builtin: tone === 'builtin' })), ...(suffix ?? []).map((v) => ({ v, builtin: false }))];
74
- if (all.length === 0) {
75
- return (_jsx(Text, { size: "xs", tone: "subtle", children: empty ?? translate('common.none') }));
76
- }
77
- return (_jsx(Row, { gap: "1", wrap: true, children: all.map(({ v, builtin }) => (_jsx("span", { className: cn('rounded-md border px-2 py-0.5 font-mono text-xs', builtin
78
- ? 'border-border/60 bg-muted/40 text-muted-foreground'
79
- : 'border-primary/30 bg-primary/10 text-primary'), children: v }, v))) }));
80
- }
81
- // --- Shared bits -------------------------------------------------------------
82
- function EmptyHint({ icon, title, body }) {
83
- return (_jsxs(Stack, { gap: "2", align: "center", className: "rounded-xl border border-dashed border-border/70 px-6 py-16 text-center", children: [_jsx("span", { className: "grid size-10 place-items-center rounded-full bg-muted text-muted-foreground", children: icon }), _jsx(Heading, { level: 3, size: "h4", children: title }), _jsx(Text, { size: "sm", tone: "muted", className: "max-w-sm", children: body })] }));
84
- }
85
- const KIND_LABEL_KEY = {
86
- plugin: 'extensions.kind.plugin',
87
- 'component-package': 'extensions.kind.componentPackage',
88
- 'field-type': 'extensions.kind.fieldType',
89
- 'data-source': 'extensions.kind.dataSource',
90
- 'asset-storage': 'extensions.kind.assetStorage',
91
- 'export-target': 'extensions.kind.exportTarget',
92
- template: 'extensions.kind.template',
93
- };
94
- function KindBadge({ kind }) {
95
- const variant = kind === 'plugin'
96
- ? 'primary'
97
- : kind === 'field-type'
98
- ? 'success'
99
- : kind === 'data-source'
100
- ? 'warning'
101
- : kind === 'template'
102
- ? 'info'
103
- : 'outline';
104
- return _jsx(Badge, { variant: variant, children: translate(KIND_LABEL_KEY[kind]) });
105
- }
106
- function KindIcon({ kind }) {
107
- const className = 'size-4';
108
- switch (kind) {
109
- case 'plugin':
110
- return _jsx(Puzzle, { className: className });
111
- case 'component-package':
112
- return _jsx(Boxes, { className: className });
113
- case 'field-type':
114
- return _jsx(Type, { className: className });
115
- case 'data-source':
116
- return _jsx(Database, { className: className });
117
- case 'asset-storage':
118
- return _jsx(HardDrive, { className: className });
119
- case 'export-target':
120
- return _jsx(Share2, { className: className });
121
- case 'template':
122
- return _jsx(LayoutTemplate, { className: className });
123
- default:
124
- return _jsx(Package, { className: className });
125
- }
126
- }
127
- //# sourceMappingURL=extensions-view.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extensions-view.js","sourceRoot":"","sources":["../../../src/components/extensions/extensions-view.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useReducer, useState } from 'react'\nimport {\n Boxes,\n Check,\n Database,\n Download,\n HardDrive,\n Lock,\n Package,\n Puzzle,\n Search,\n Share2,\n LayoutTemplate,\n Type,\n Webhook,\n} from 'lucide-react'\nimport {\n BUILTIN_FIELD_TYPES,\n BUILTIN_SOURCE_KINDS,\n HOOK_POINTS,\n MarketplaceClient,\n PluginHost,\n privateMarketplace,\n publicMarketplace,\n} from '@elytracms/core/plugins'\nimport type { ExtensionKind, ExtensionListing, InstalledPlugin, MarketplaceInstallResult } from '@elytracms/core/plugins'\nimport { Badge, Button, Code, Heading, Input, Row, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { cn } from '@elytracms/studio/lib/utils'\n\ntype Tab = 'marketplace' | 'installed' | 'registered'\n\n/**\n * The Extensions surface (Phase 11, EC-130–137). A live, agent-inspectable view over the\n * extensibility layer: browse a public + private marketplace, install plugins into a host, and\n * inspect what each surface contributes (components, field types, data sources, asset storage,\n * export targets, hooks). All state is deterministic — driven by `@elytracms/core/plugins` fixtures.\n */\nexport function ExtensionsView() {\n // One host for the session; PluginHost mutates in place, so a bump reducer drives re-render.\n const [host] = useState(() => new PluginHost())\n const [, bump] = useReducer((n: number) => n + 1, 0)\n const [tab, setTab] = useState<Tab>('marketplace')\n const [token, setToken] = useState('')\n const [query, setQuery] = useState('')\n const [results, setResults] = useState<Record<string, MarketplaceInstallResult>>({})\n\n const client = useMemo(\n () => new MarketplaceClient([publicMarketplace, privateMarketplace], token.trim() ? { 'acme-private': token.trim() } : {}),\n [token],\n )\n\n const hits = client.search(query.trim() ? { query: query.trim() } : {})\n const privateUnlocked = client.accessibleRegistries().some((r) => r.id === 'acme-private')\n const installedCount = host.list().length\n\n function install(listing: ExtensionListing) {\n const result = client.install(host, listing.id)\n setResults((prev) => ({ ...prev, [listing.id]: result }))\n bump()\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <ExtensionsHeader\n tab={tab}\n onTab={setTab}\n installedCount={installedCount}\n marketCount={hits.length}\n />\n\n <div className=\"min-h-0 flex-1 overflow-auto px-4 py-5 sm:px-6\">\n <div className=\"mx-auto w-full max-w-5xl\">\n {tab === 'marketplace' ? (\n <MarketplaceTab\n hits={hits}\n query={query}\n onQuery={setQuery}\n token={token}\n onToken={setToken}\n privateUnlocked={privateUnlocked}\n isInstalled={(l) => host.isInstalled(l.plugin.id)}\n onInstall={install}\n results={results}\n />\n ) : tab === 'installed' ? (\n <InstalledTab installed={host.list()} />\n ) : (\n <RegisteredTab host={host} />\n )}\n </div>\n </div>\n </div>\n )\n}\n\n// --- Header + tabs ----------------------------------------------------------\n\nfunction ExtensionsHeader({\n tab,\n onTab,\n installedCount,\n marketCount,\n}: {\n tab: Tab\n onTab: (t: Tab) => void\n installedCount: number\n marketCount: number\n}) {\n const tabs: { id: Tab; label: string; count?: number }[] = [\n { id: 'marketplace', label: translate('extensions.tab.marketplace'), count: marketCount },\n { id: 'installed', label: translate('extensions.tab.installed'), count: installedCount },\n { id: 'registered', label: translate('extensions.tab.registered') },\n ]\n return (\n <div className=\"shrink-0 border-b border-border bg-gradient-to-b from-primary/[0.04] to-transparent px-4 py-3 sm:px-6\">\n <Row gap=\"3\">\n <span className=\"grid size-8 shrink-0 place-items-center rounded-lg bg-primary/15 text-primary\">\n <Puzzle className=\"size-4\" />\n </span>\n <Stack gap=\"none\" className=\"min-w-0\">\n <Text size=\"sm\" weight=\"semibold\">\n {translate('extensions.header.title')}\n </Text>\n <Text size=\"xs\" tone=\"subtle\">\n {translate('extensions.header.subtitle')}\n </Text>\n </Stack>\n <Spacer />\n <Row gap=\"1\" className=\"rounded-md bg-muted/50 p-0.5\">\n {tabs.map((t) => (\n <button\n key={t.id}\n type=\"button\"\n aria-pressed={tab === t.id}\n onClick={() => onTab(t.id)}\n className={cn(\n 'flex items-center gap-1.5 rounded-[5px] px-3 py-1 text-sm font-medium transition-colors',\n tab === t.id ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground',\n )}\n >\n {t.label}\n {t.count !== undefined ? (\n <span className=\"rounded-full bg-muted px-1.5 text-[0.7rem] font-semibold tabular-nums text-muted-foreground\">\n {t.count}\n </span>\n ) : null}\n </button>\n ))}\n </Row>\n </Row>\n </div>\n )\n}\n\n// --- Marketplace ------------------------------------------------------------\n\nfunction MarketplaceTab({\n hits,\n query,\n onQuery,\n token,\n onToken,\n privateUnlocked,\n isInstalled,\n onInstall,\n results,\n}: {\n hits: { registryId: string; listing: ExtensionListing }[]\n query: string\n onQuery: (q: string) => void\n token: string\n onToken: (t: string) => void\n privateUnlocked: boolean\n isInstalled: (l: ExtensionListing) => boolean\n onInstall: (l: ExtensionListing) => void\n results: Record<string, MarketplaceInstallResult>\n}) {\n return (\n <Stack gap=\"6\">\n <Row gap=\"3\" wrap>\n <div className=\"relative min-w-0 flex-1\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={query}\n onChange={(e) => onQuery(e.target.value)}\n placeholder={translate('extensions.search.placeholder')}\n className=\"pl-8\"\n aria-label={translate('extensions.search.aria')}\n />\n </div>\n <Row gap=\"2\" className=\"shrink-0\">\n <Lock className={cn('size-3.5', privateUnlocked ? 'text-success' : 'text-muted-foreground')} />\n <Input\n value={token}\n onChange={(e) => onToken(e.target.value)}\n placeholder={translate('extensions.token.placeholder')}\n aria-label={translate('extensions.token.placeholder')}\n className=\"w-52\"\n />\n {privateUnlocked ? <Badge variant=\"success\">{translate('extensions.token.unlocked')}</Badge> : null}\n </Row>\n </Row>\n\n {!privateUnlocked ? (\n <Text size=\"xs\" tone=\"subtle\">\n A private registry is available. Enter its access token (hint: <Code tone=\"muted\">acme-internal-token</Code>) to\n reveal private listings.\n </Text>\n ) : null}\n\n {hits.length === 0 ? (\n <EmptyHint\n icon={<Search className=\"size-5\" />}\n title={translate('extensions.empty.noMatch.title')}\n body={translate('extensions.empty.noMatch.body')}\n />\n ) : (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {hits.map(({ registryId, listing }) => (\n <ListingCard\n key={listing.id}\n listing={listing}\n registryId={registryId}\n installed={isInstalled(listing)}\n result={results[listing.id]}\n onInstall={() => onInstall(listing)}\n />\n ))}\n </div>\n )}\n </Stack>\n )\n}\n\nfunction ListingCard({\n listing,\n registryId,\n installed,\n result,\n onInstall,\n}: {\n listing: ExtensionListing\n registryId: string\n installed: boolean\n result: MarketplaceInstallResult | undefined\n onInstall: () => void\n}) {\n const failed = result && !result.ok\n return (\n <div className=\"flex flex-col rounded-xl border border-border bg-card/60 p-4 transition-colors hover:border-primary/40\">\n <Row gap=\"2\">\n <span className=\"grid size-9 shrink-0 place-items-center rounded-lg bg-muted text-foreground/80\">\n <KindIcon kind={listing.kind} />\n </span>\n <Stack gap=\"none\" className=\"min-w-0\">\n <Text size=\"sm\" weight=\"semibold\" className=\"truncate\">\n {listing.name}\n </Text>\n <Row gap=\"1\" className=\"text-muted-foreground\">\n <KindBadge kind={listing.kind} />\n <Code tone=\"muted\">v{listing.version}</Code>\n </Row>\n </Stack>\n <Spacer />\n {registryId === 'acme-private' ? (\n <Badge variant=\"outline\">\n <Lock className=\"size-3\" /> private\n </Badge>\n ) : null}\n </Row>\n\n <Text size=\"xs\" tone=\"muted\" className=\"mt-3 line-clamp-3 min-h-0 flex-1\">\n {listing.description ?? translate('extensions.listing.noDescription')}\n </Text>\n\n {listing.tags && listing.tags.length > 0 ? (\n <Row gap=\"1\" wrap className=\"mt-3\">\n {listing.tags.map((tag) => (\n <span key={tag} className=\"rounded-full bg-muted/70 px-2 py-0.5 text-[0.7rem] text-muted-foreground\">\n {tag}\n </span>\n ))}\n </Row>\n ) : null}\n\n <Row gap=\"2\" className=\"mt-4\">\n <Text size=\"xs\" tone=\"subtle\" className=\"truncate\">\n {listing.author ?? translate('extensions.listing.unknownAuthor')}\n </Text>\n <Spacer />\n {installed ? (\n <Button size=\"sm\" variant=\"ghost\" disabled>\n <Check /> {translate('extensions.action.installed')}\n </Button>\n ) : (\n <Button size=\"sm\" variant={failed ? 'destructive' : 'default'} onClick={onInstall}>\n <Download /> {failed ? translate('common.retry') : translate('extensions.action.install')}\n </Button>\n )}\n </Row>\n\n {failed ? (\n <Text size=\"xs\" className=\"mt-2 text-destructive\">\n {result?.blocked === 'access-denied'\n ? translate('extensions.error.accessDenied')\n : result?.issues[0]?.message ?? translate('extensions.error.installFailed')}\n </Text>\n ) : null}\n </div>\n )\n}\n\n// --- Installed --------------------------------------------------------------\n\nfunction InstalledTab({ installed }: { installed: InstalledPlugin[] }) {\n if (installed.length === 0) {\n return (\n <EmptyHint\n icon={<Boxes className=\"size-5\" />}\n title={translate('extensions.empty.installed.title')}\n body={translate('extensions.empty.installed.body')}\n />\n )\n }\n return (\n <Stack gap=\"3\">\n {installed.map((plugin) => (\n <div key={plugin.id} className=\"rounded-xl border border-border bg-card/60 p-4\">\n <Row gap=\"2\">\n <Package className=\"size-4 text-primary\" />\n <Text size=\"sm\" weight=\"semibold\">\n {plugin.name}\n </Text>\n <Code tone=\"muted\">v{plugin.version}</Code>\n <Spacer />\n <Text size=\"xs\" tone=\"subtle\">\n {plugin.author ?? ''}\n </Text>\n </Row>\n {plugin.description ? (\n <Text size=\"xs\" tone=\"muted\" className=\"mt-2\">\n {plugin.description}\n </Text>\n ) : null}\n <Row gap=\"2\" wrap className=\"mt-3\">\n <ContributionChip icon={<Boxes />} label={translate('extensions.contrib.components')} count={plugin.contributes.components} />\n <ContributionChip icon={<Type />} label={translate('extensions.contrib.fieldTypes')} count={plugin.contributes.fieldTypes} />\n <ContributionChip icon={<Database />} label={translate('extensions.contrib.dataSources')} count={plugin.contributes.dataSources} />\n <ContributionChip icon={<HardDrive />} label={translate('extensions.contrib.assetStorage')} count={plugin.contributes.assetStorage} />\n <ContributionChip icon={<Share2 />} label={translate('extensions.contrib.exportTargets')} count={plugin.contributes.exportTargets} />\n <ContributionChip icon={<LayoutTemplate />} label={translate('extensions.contrib.templates')} count={plugin.contributes.templates} />\n <ContributionChip icon={<Webhook />} label={translate('extensions.contrib.hooks')} count={plugin.contributes.hooks} />\n </Row>\n </div>\n ))}\n </Stack>\n )\n}\n\nfunction ContributionChip({ icon, label, count }: { icon: React.ReactNode; label: string; count: number }) {\n if (count === 0) return null\n return (\n <span className=\"inline-flex items-center gap-1.5 rounded-md border border-border/60 bg-muted/40 px-2 py-1 text-xs text-muted-foreground\">\n <span className=\"grid size-3.5 place-items-center text-primary [&_svg]:size-3.5\">{icon}</span>\n <span className=\"font-semibold tabular-nums text-foreground\">{count}</span>\n {label}\n </span>\n )\n}\n\n// --- Registered (inspect) ---------------------------------------------------\n\nfunction RegisteredTab({ host }: { host: PluginHost }) {\n const hookPoints = host.hooks.points()\n return (\n <Stack gap=\"6\">\n <Text size=\"xs\" tone=\"subtle\">\n {translate('extensions.registered.intro')}\n </Text>\n\n <InspectGroup icon={<Boxes />} title={translate('extensions.registered.components.title')} sub=\"base.* + project + vendor packages\">\n <ChipRow items={host.components.projectComponents().map((m) => m.id)} empty={translate('extensions.registered.components.empty')} />\n </InspectGroup>\n\n <InspectGroup icon={<Type />} title={translate('extensions.registered.fieldTypes.title')}>\n <ChipRow\n items={[...BUILTIN_FIELD_TYPES]}\n tone=\"builtin\"\n suffix={host.fieldTypes.list().map((f) => f.kind)}\n />\n </InspectGroup>\n\n <InspectGroup icon={<Database />} title={translate('extensions.registered.dataSources.title')}>\n <ChipRow items={[...BUILTIN_SOURCE_KINDS]} tone=\"builtin\" suffix={host.dataSources.list().map((a) => a.kind)} />\n </InspectGroup>\n\n <InspectGroup icon={<HardDrive />} title={translate('extensions.registered.assetStorage.title')}>\n <ChipRow items={host.assetStorage.list().map((a) => `${a.kind} · ${a.title}`)} empty={translate('extensions.registered.assetStorage.empty')} />\n </InspectGroup>\n\n <InspectGroup icon={<Share2 />} title={translate('extensions.registered.exportTargets.title')}>\n <ChipRow\n items={['next-app-router · reference']}\n tone=\"builtin\"\n suffix={host.exportTargets.list().map((t) => `${t.id} · ${t.title}`)}\n />\n </InspectGroup>\n\n <InspectGroup icon={<Webhook />} title={translate('extensions.registered.hookPoints.title')}>\n <ChipRow\n items={[...HOOK_POINTS]}\n tone=\"builtin\"\n suffix={hookPoints.flatMap((p) => host.hooks.list(p).map((t) => `${p}${t.plugin ? ` ← ${t.plugin}` : ''}`))}\n />\n </InspectGroup>\n </Stack>\n )\n}\n\nfunction InspectGroup({\n icon,\n title,\n sub,\n children,\n}: {\n icon: React.ReactNode\n title: string\n sub?: string\n children: React.ReactNode\n}) {\n return (\n <Stack gap=\"2\">\n <Row gap=\"2\">\n <span className=\"grid size-5 place-items-center text-primary [&_svg]:size-4\">{icon}</span>\n <Text size=\"sm\" weight=\"semibold\">\n {title}\n </Text>\n {sub ? (\n <Text size=\"xs\" tone=\"subtle\">\n {sub}\n </Text>\n ) : null}\n </Row>\n {children}\n </Stack>\n )\n}\n\nfunction ChipRow({\n items,\n suffix,\n tone,\n empty,\n}: {\n items: string[]\n suffix?: string[]\n tone?: 'builtin'\n empty?: string\n}) {\n const all = [...items.map((v) => ({ v, builtin: tone === 'builtin' })), ...(suffix ?? []).map((v) => ({ v, builtin: false }))]\n if (all.length === 0) {\n return (\n <Text size=\"xs\" tone=\"subtle\">\n {empty ?? translate('common.none')}\n </Text>\n )\n }\n return (\n <Row gap=\"1\" wrap>\n {all.map(({ v, builtin }) => (\n <span\n key={v}\n className={cn(\n 'rounded-md border px-2 py-0.5 font-mono text-xs',\n builtin\n ? 'border-border/60 bg-muted/40 text-muted-foreground'\n : 'border-primary/30 bg-primary/10 text-primary',\n )}\n >\n {v}\n </span>\n ))}\n </Row>\n )\n}\n\n// --- Shared bits -------------------------------------------------------------\n\nfunction EmptyHint({ icon, title, body }: { icon: React.ReactNode; title: string; body: string }) {\n return (\n <Stack gap=\"2\" align=\"center\" className=\"rounded-xl border border-dashed border-border/70 px-6 py-16 text-center\">\n <span className=\"grid size-10 place-items-center rounded-full bg-muted text-muted-foreground\">{icon}</span>\n <Heading level={3} size=\"h4\">\n {title}\n </Heading>\n <Text size=\"sm\" tone=\"muted\" className=\"max-w-sm\">\n {body}\n </Text>\n </Stack>\n )\n}\n\nconst KIND_LABEL_KEY: Record<ExtensionKind, string> = {\n plugin: 'extensions.kind.plugin',\n 'component-package': 'extensions.kind.componentPackage',\n 'field-type': 'extensions.kind.fieldType',\n 'data-source': 'extensions.kind.dataSource',\n 'asset-storage': 'extensions.kind.assetStorage',\n 'export-target': 'extensions.kind.exportTarget',\n template: 'extensions.kind.template',\n}\n\nfunction KindBadge({ kind }: { kind: ExtensionKind }) {\n const variant =\n kind === 'plugin'\n ? 'primary'\n : kind === 'field-type'\n ? 'success'\n : kind === 'data-source'\n ? 'warning'\n : kind === 'template'\n ? 'info'\n : 'outline'\n return <Badge variant={variant}>{translate(KIND_LABEL_KEY[kind])}</Badge>\n}\n\nfunction KindIcon({ kind }: { kind: ExtensionKind }) {\n const className = 'size-4'\n switch (kind) {\n case 'plugin':\n return <Puzzle className={className} />\n case 'component-package':\n return <Boxes className={className} />\n case 'field-type':\n return <Type className={className} />\n case 'data-source':\n return <Database className={className} />\n case 'asset-storage':\n return <HardDrive className={className} />\n case 'export-target':\n return <Share2 className={className} />\n case 'template':\n return <LayoutTemplate className={className} />\n default:\n return <Package className={className} />\n }\n}\n"]}
@@ -1,24 +0,0 @@
1
- import type { CmsSource } from '@elytracms/core/data-binding';
2
- import type { ListDocumentsQuery } from '@elytracms/core/content';
3
- /**
4
- * Stored repeating-section / template-page query config (EC-166), reduced to
5
- * what survives the EC-190 v1 realignment.
6
- *
7
- * The editor surfaces that AUTHORED these queries — the repeating-section and
8
- * template-page binding panels — are gone (the v1 canvas edits static props
9
- * only). What remains is the read-only side the canvas preview and the
10
- * locale-fallback marker still need: replaying a STORED query config as the
11
- * exact delivery `listDocuments` query, so a parked section/template source
12
- * previews through the same `@elytracms/core/content` resolution delivery runs. The
13
- * query-building, picker-vocabulary, and plain-language description helpers were
14
- * removed with the panels.
15
- */
16
- /**
17
- * The CMS source config of a section/template source — exactly the canonical
18
- * `@elytracms/core/data-binding` cms config: the AD-3 `where` shape is valid JSON and
19
- * stores verbatim in `filter`; `sort` and `limit` are first-class
20
- * `cmsConfigSchema` fields, so the full query round-trips Zod validation.
21
- */
22
- export type QueryCmsConfig = CmsSource['config'];
23
- /** The `listDocuments` query a stored source config replays (delivery parity). */
24
- export declare function listQueryOfConfig(config: QueryCmsConfig): ListDocumentsQuery;
@@ -1,9 +0,0 @@
1
- /** The `listDocuments` query a stored source config replays (delivery parity). */
2
- export function listQueryOfConfig(config) {
3
- return {
4
- ...(Object.keys(config.filter).length > 0 ? { where: config.filter } : {}),
5
- ...(config.sort !== undefined ? { sort: config.sort } : {}),
6
- ...(config.limit !== undefined ? { limit: config.limit } : {}),
7
- };
8
- }
9
- //# sourceMappingURL=section-bindings.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"section-bindings.js","sourceRoot":"","sources":["../../../src/lib/bindings/section-bindings.ts"],"names":[],"mappings":"AAyBA,kFAAkF;AAClF,MAAM,UAAU,iBAAiB,CAAC,MAAsB;IACtD,OAAO;QACL,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAoB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxF,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/D,CAAA;AACH,CAAC","sourcesContent":["import type { CmsSource } from '@elytracms/core/data-binding'\nimport type { ListDocumentsQuery, WhereInput } from '@elytracms/core/content'\n\n/**\n * Stored repeating-section / template-page query config (EC-166), reduced to\n * what survives the EC-190 v1 realignment.\n *\n * The editor surfaces that AUTHORED these queries — the repeating-section and\n * template-page binding panels — are gone (the v1 canvas edits static props\n * only). What remains is the read-only side the canvas preview and the\n * locale-fallback marker still need: replaying a STORED query config as the\n * exact delivery `listDocuments` query, so a parked section/template source\n * previews through the same `@elytracms/core/content` resolution delivery runs. The\n * query-building, picker-vocabulary, and plain-language description helpers were\n * removed with the panels.\n */\n\n/**\n * The CMS source config of a section/template source — exactly the canonical\n * `@elytracms/core/data-binding` cms config: the AD-3 `where` shape is valid JSON and\n * stores verbatim in `filter`; `sort` and `limit` are first-class\n * `cmsConfigSchema` fields, so the full query round-trips Zod validation.\n */\nexport type QueryCmsConfig = CmsSource['config']\n\n/** The `listDocuments` query a stored source config replays (delivery parity). */\nexport function listQueryOfConfig(config: QueryCmsConfig): ListDocumentsQuery {\n return {\n ...(Object.keys(config.filter).length > 0 ? { where: config.filter as WhereInput } : {}),\n ...(config.sort !== undefined ? { sort: config.sort } : {}),\n ...(config.limit !== undefined ? { limit: config.limit } : {}),\n }\n}\n"]}
@@ -1,98 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- import { SourceRegistry } from '@elytracms/core/data-binding';
3
- import type { BindingResolution, ConditionResolution, DataSource, InferredSchema, PayloadStore, RepeaterItemContext } from '@elytracms/core/data-binding';
4
- import type { BindingReference, Condition, JsonValue } from '@elytracms/core/project-graph';
5
- /**
6
- * The project's live data layer (brief §4.6, §7.5, EC-167). Holds the source
7
- * registry the Builder's Content tab binds through, plus the captured payloads
8
- * that drive binding resolution. One uniform model across CMS queries, data
9
- * providers, and the built-in REST/Convex kinds — resolution, sampling,
10
- * inference and tokenization treat them all identically.
11
- *
12
- * There is no source-authoring UI (EC-167): sources exist because code declared
13
- * them (registered data providers), the schema implied them (per-collection CMS
14
- * queries), or an editor flow provisioned them (repeating sections, template
15
- * pages).
16
- *
17
- * Persistence: when mounted with a `projectId`, the store hydrates the
18
- * project's persisted source records (the `cms-data-sources` system collection,
19
- * see `sources-store.ts`) over the deterministic playground seeds — persisted
20
- * wins by id, but never over a source provisioned/removed in THIS session —
21
- * and every provision/remove writes through `@elytracms/core/operations`. Without a
22
- * `projectId` (tests, fixtures) the store is pure session state, exactly as
23
- * before.
24
- */
25
- export interface DataStore {
26
- sources: DataSource[];
27
- registry: SourceRegistry;
28
- payloads: PayloadStore;
29
- knownSourceIds: ReadonlySet<string>;
30
- getSource: (id: string) => DataSource | undefined;
31
- removeSource: (id: string) => void;
32
- /**
33
- * Create or replace a source with a known id + config and capture its sample
34
- * immediately (EC-166/EC-167): repeating sections and template pages
35
- * provision their per-scope CMS query / provider sources through this.
36
- */
37
- provisionSource: (source: DataSource) => void;
38
- /**
39
- * The latest persistence failure in plain language, or `null`. Persistence
40
- * is best-effort on top of the live session (the canvas keeps working on
41
- * session state), but a failure is recorded here — visible, never silent.
42
- */
43
- persistError: string | null;
44
- /** The stable path tokens a source exposes from its inferred schema (EC-085). */
45
- tokensOf: (id: string) => string[];
46
- schemaOf: (id: string) => InferredSchema | undefined;
47
- sampleOf: (id: string) => JsonValue | undefined;
48
- /** Resolve a binding to a renderer-ready resolution (value/props/condition/issue). */
49
- resolve: (ref: BindingReference, item?: RepeaterItemContext) => BindingResolution;
50
- /** Resolve a binding to the concrete value the renderer injects (EC-086–089). */
51
- resolveValue: (ref: BindingReference, item?: RepeaterItemContext) => JsonValue | undefined;
52
- /** Evaluate a condition against the captured payloads (EC-088). */
53
- resolveCondition: (condition: Condition, item?: RepeaterItemContext) => ConditionResolution;
54
- }
55
- export interface DataState {
56
- sources: DataSource[];
57
- /**
58
- * Ids provisioned or removed in THIS session. Hydration (which resolves
59
- * asynchronously) never overrides them — the editor's latest action wins
60
- * over whatever was persisted before it.
61
- */
62
- sessionIds: ReadonlySet<string>;
63
- }
64
- export type DataAction = {
65
- type: 'remove';
66
- id: string;
67
- } | {
68
- type: 'provision';
69
- source: DataSource;
70
- }
71
- /** Merge the project's persisted source records in (persisted wins over seeds). */
72
- | {
73
- type: 'hydrate';
74
- sources: DataSource[];
75
- };
76
- /**
77
- * The store's reducer, exported because hydration/session-precedence semantics
78
- * live here and the app's test harness has no DOM to drive effects through.
79
- */
80
- export declare function dataStateReducer(state: DataState, action: DataAction): DataState;
81
- declare const useData: () => DataStore;
82
- /**
83
- * The session's starting state: EMPTY. The studio seeds no sample sources (Oli,
84
- * 2026-06-19) — real sources arrive from the project's config collections (the
85
- * hydrate effect, Convex mode) or are added by the author. Nothing is faked.
86
- */
87
- export declare function initialDataState(): DataState;
88
- export interface DataProviderProps {
89
- children: ReactNode;
90
- /**
91
- * When set, the store hydrates from and persists to this project's
92
- * `cms-data-sources` system collection (through `@elytracms/core/operations`).
93
- * Without it the store is pure session state (tests, fixtures).
94
- */
95
- projectId?: string;
96
- }
97
- export declare function DataProvider({ children, projectId }: DataProviderProps): import("react").JSX.Element;
98
- export { useData };
@@ -1,211 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
3
- import { SourceRegistry, captureSample, inferSchema, resolveBinding, resolveCondition, tokensForSchema, } from '@elytracms/core/data-binding';
4
- import { getStudioOperations } from '../operations/index.js';
5
- import { useOperationCache, useOperationQuery } from '@elytracms/studio/lib/query';
6
- import { createRequiredContext } from '@elytracms/studio/lib/required-context';
7
- import { STUDIO_SYSTEM_COLLECTION_IDS } from '../operations/session-hydration.js';
8
- import { resolveDeploymentCollections } from '../workspace/index.js';
9
- import { hasContentSession } from './collection-content.js';
10
- import { captureSourceSample, cmsSourceForCollection } from './sample-capture.js';
11
- import { deleteDataSource, loadDataSources, saveDataSource } from './sources-store.js';
12
- function upsert(sources, source) {
13
- const exists = sources.some((s) => s.id === source.id);
14
- return exists ? sources.map((s) => (s.id === source.id ? source : s)) : [...sources, source];
15
- }
16
- /**
17
- * A persisted record hydrates as-is when it carries its captured sample;
18
- * otherwise recapture. Exception (EC-177 gap 1): with a hydrated content
19
- * session configured (Convex mode), CMS sources ALWAYS recapture by replaying
20
- * their stored query over the live documents — a sample captured by an earlier
21
- * session may carry stale (or fixture-derived) entries.
22
- */
23
- function withSample(source) {
24
- if (source.kind === 'cms' && hasContentSession()) {
25
- const sample = captureSourceSample(source);
26
- return sample !== undefined ? captureSample(source, sample) : source;
27
- }
28
- if (source.sample !== undefined)
29
- return source;
30
- // No persisted sample: try an honest capture (a provider runs; anything the
31
- // studio can't fetch returns undefined) — never fabricate.
32
- const sample = captureSourceSample(source);
33
- return sample !== undefined ? captureSample(source, sample) : source;
34
- }
35
- /**
36
- * The store's reducer, exported because hydration/session-precedence semantics
37
- * live here and the app's test harness has no DOM to drive effects through.
38
- */
39
- export function dataStateReducer(state, action) {
40
- switch (action.type) {
41
- case 'remove':
42
- return {
43
- ...state,
44
- sources: state.sources.filter((s) => s.id !== action.id),
45
- sessionIds: new Set([...state.sessionIds, action.id]),
46
- };
47
- case 'provision': {
48
- // Upsert by id; attach a sample only if one can be captured honestly (a
49
- // provider run, or live CMS content) — otherwise the source carries none.
50
- const sample = captureSourceSample(action.source);
51
- const provisioned = sample !== undefined ? captureSample(action.source, sample) : action.source;
52
- return {
53
- ...state,
54
- sources: upsert(state.sources, provisioned),
55
- sessionIds: new Set([...state.sessionIds, provisioned.id]),
56
- };
57
- }
58
- case 'hydrate': {
59
- let sources = state.sources;
60
- for (const source of action.sources) {
61
- if (state.sessionIds.has(source.id))
62
- continue; // this session already decided
63
- sources = upsert(sources, withSample(source));
64
- }
65
- return sources === state.sources ? state : { ...state, sources };
66
- }
67
- default:
68
- return state;
69
- }
70
- }
71
- const { Context: DataContext, useRequired: useData } = createRequiredContext('Data');
72
- /**
73
- * The session's starting state: EMPTY. The studio seeds no sample sources (Oli,
74
- * 2026-06-19) — real sources arrive from the project's config collections (the
75
- * hydrate effect, Convex mode) or are added by the author. Nothing is faked.
76
- */
77
- export function initialDataState() {
78
- return { sources: [], sessionIds: new Set() };
79
- }
80
- export function DataProvider({ children, projectId }) {
81
- const [state, dispatch] = useReducer(dataStateReducer, undefined, initialDataState);
82
- const [persistError, setPersistError] = useState(null);
83
- // EC-192: surface every config-driven content collection as a pickable CMS
84
- // source, so the binding/condition pickers list what actually exists — the
85
- // same collection set the CMS uses (`resolveDeploymentCollections`) — rather
86
- // than only the demo fixture sources. Merged additively (the `hydrate` upsert
87
- // skips session-decided ids and recaptures live samples in Convex mode); studio
88
- // SYSTEM collections (routes/redirects/saved views/data sources) are excluded.
89
- useEffect(() => {
90
- if (projectId === undefined)
91
- return;
92
- const collections = resolveDeploymentCollections().filter((collection) => !STUDIO_SYSTEM_COLLECTION_IDS.has(collection.id));
93
- if (collections.length === 0)
94
- return;
95
- dispatch({ type: 'hydrate', sources: collections.map(cmsSourceForCollection) });
96
- }, [projectId]);
97
- // Hydrate the project's persisted sources through the one operations-backed
98
- // query cache (EC-308/AD-17). Sources provisioned by other sessions on the
99
- // same project show up here; the playground seeds stay for everything not
100
- // persisted. The reducer's `hydrate` keeps session precedence — a source
101
- // provisioned/removed in THIS session always wins over the hydrated list.
102
- const cache = useOperationCache();
103
- const sourcesQuery = useOperationQuery({
104
- queryKey: ['dataSources', projectId ?? '∅'],
105
- loader: async (operations) => {
106
- const result = await loadDataSources(operations, projectId);
107
- if (!result.ok)
108
- throw new Error(result.error.message);
109
- return result.value;
110
- },
111
- enabled: projectId !== undefined,
112
- });
113
- const hydratedSources = sourcesQuery.data;
114
- useEffect(() => {
115
- if (hydratedSources)
116
- dispatch({ type: 'hydrate', sources: hydratedSources });
117
- }, [hydratedSources]);
118
- const sourcesError = sourcesQuery.isError ? sourcesQuery.error : null;
119
- useEffect(() => {
120
- if (sourcesError)
121
- setPersistError(sourcesError instanceof Error ? sourcesError.message : 'Loading data sources failed.');
122
- }, [sourcesError]);
123
- // Best-effort persistence on top of the live session: the canvas keeps
124
- // working on session state; a failed write surfaces via `persistError`. On a
125
- // confirmed write, invalidate the hydration query so a later remount
126
- // re-hydrates the provisioned/removed source rather than a stale list.
127
- const persist = useCallback((write) => {
128
- if (projectId === undefined)
129
- return;
130
- void getStudioOperations()
131
- .then(({ operations }) => write(operations, projectId))
132
- .then((result) => {
133
- if (!result.ok) {
134
- setPersistError(result.error.message);
135
- return;
136
- }
137
- void cache.invalidate(['dataSources', projectId]);
138
- })
139
- .catch((error) => {
140
- setPersistError(error instanceof Error ? error.message : 'Saving the data source failed.');
141
- });
142
- }, [projectId, cache]);
143
- const store = useMemo(() => {
144
- const registry = SourceRegistry.from(state.sources);
145
- const payloads = {};
146
- for (const source of state.sources) {
147
- if (source.sample !== undefined)
148
- payloads[source.id] = source.sample;
149
- }
150
- const knownSourceIds = registry.knownSourceIds();
151
- const ctx = { payloads, registry };
152
- const getSource = (id) => registry.get(id);
153
- const resolve = (ref, item) => resolveBinding(ref, ctx, item);
154
- return {
155
- sources: state.sources,
156
- registry,
157
- payloads,
158
- knownSourceIds,
159
- getSource,
160
- removeSource: (id) => {
161
- dispatch({ type: 'remove', id });
162
- persist((operations, pid) => deleteDataSource(operations, pid, id));
163
- },
164
- provisionSource: (source) => {
165
- dispatch({ type: 'provision', source });
166
- // Persist exactly what the session resolves: the source plus any sample
167
- // that can be captured honestly (a provider run / live CMS content) —
168
- // never a fabricated one. Matches the `provision` reducer.
169
- const sample = captureSourceSample(source);
170
- const provisioned = sample !== undefined ? captureSample(source, sample) : source;
171
- persist((operations, pid) => saveDataSource(operations, pid, provisioned));
172
- },
173
- persistError,
174
- tokensOf: (id) => {
175
- const schema = getSource(id)?.schema;
176
- return schema ? tokensForSchema(schema) : [];
177
- },
178
- schemaOf: (id) => {
179
- const source = getSource(id);
180
- if (source?.schema)
181
- return source.schema;
182
- if (source?.sample !== undefined)
183
- return inferSchema(source.sample);
184
- return undefined;
185
- },
186
- sampleOf: (id) => getSource(id)?.sample,
187
- resolve,
188
- resolveValue: (ref, item) => {
189
- const r = resolve(ref, item);
190
- switch (r.kind) {
191
- case 'value':
192
- return r.value;
193
- case 'props':
194
- return r.props;
195
- case 'condition':
196
- return r.pass;
197
- case 'issue':
198
- return undefined;
199
- default: {
200
- const never = r;
201
- return never;
202
- }
203
- }
204
- },
205
- resolveCondition: (condition, item) => resolveCondition(condition, ctx, item),
206
- };
207
- }, [state, persist, persistError]);
208
- return _jsx(DataContext.Provider, { value: store, children: children });
209
- }
210
- export { useData };
211
- //# sourceMappingURL=data-store.js.map