@object-ui/app-shell 6.2.3 → 7.0.0

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 (468) hide show
  1. package/CHANGELOG.md +948 -0
  2. package/README.md +292 -0
  3. package/dist/assistant/assistantBus.d.ts +72 -0
  4. package/dist/assistant/assistantBus.js +133 -0
  5. package/dist/chrome/CommandPalette.d.ts +1 -1
  6. package/dist/chrome/CommandPalette.js +26 -22
  7. package/dist/chrome/ConditionalAuthWrapper.d.ts +1 -1
  8. package/dist/chrome/ConsoleToaster.d.ts +1 -1
  9. package/dist/chrome/ConsoleToaster.js +3 -1
  10. package/dist/chrome/ErrorBoundary.d.ts +1 -1
  11. package/dist/chrome/KeyboardShortcutsDialog.d.ts +1 -1
  12. package/dist/chrome/KeyboardShortcutsDialog.js +16 -5
  13. package/dist/chrome/LoadingScreen.d.ts +1 -1
  14. package/dist/chrome/LoadingScreen.js +22 -26
  15. package/dist/chrome/RouteFader.d.ts +1 -1
  16. package/dist/chrome/ThemeProvider.d.ts +1 -1
  17. package/dist/components/ManagedByBadge.d.ts +1 -1
  18. package/dist/console/AppContent.d.ts +1 -1
  19. package/dist/console/AppContent.js +170 -37
  20. package/dist/console/ConsoleShell.d.ts +7 -7
  21. package/dist/console/ConsoleShell.js +32 -3
  22. package/dist/console/ai/AiChatPage.d.ts +88 -1
  23. package/dist/console/ai/AiChatPage.js +743 -66
  24. package/dist/console/ai/ConversationsSidebar.d.ts +26 -1
  25. package/dist/console/ai/ConversationsSidebar.js +149 -34
  26. package/dist/console/ai/LiveCanvas.d.ts +22 -0
  27. package/dist/console/ai/LiveCanvas.js +78 -0
  28. package/dist/console/ai/reconcileTurn.d.ts +8 -0
  29. package/dist/console/ai/reconcileTurn.js +20 -0
  30. package/dist/console/auth/AuthPageLayout.d.ts +1 -1
  31. package/dist/console/auth/ForgotPasswordPage.d.ts +1 -1
  32. package/dist/console/auth/LoginPage.d.ts +1 -1
  33. package/dist/console/auth/RegisterPage.d.ts +1 -1
  34. package/dist/console/auth/RegisterPage.js +23 -3
  35. package/dist/console/cloud-connection/CloudConnectionPanel.d.ts +1 -0
  36. package/dist/console/cloud-connection/CloudConnectionPanel.js +169 -0
  37. package/dist/console/home/AppCard.d.ts +1 -1
  38. package/dist/console/home/AppCard.js +6 -12
  39. package/dist/console/home/HomeAppsStrip.d.ts +8 -0
  40. package/dist/console/home/HomeAppsStrip.js +61 -0
  41. package/dist/console/home/HomeLayout.d.ts +1 -1
  42. package/dist/console/home/HomeLayout.js +3 -1
  43. package/dist/console/home/HomePage.d.ts +1 -2
  44. package/dist/console/home/HomePage.js +149 -21
  45. package/dist/console/home/HomeRail.d.ts +22 -0
  46. package/dist/console/home/HomeRail.js +62 -0
  47. package/dist/console/home/QuickActions.d.ts +1 -1
  48. package/dist/console/home/QuickActions.js +3 -11
  49. package/dist/console/home/RecentApps.d.ts +1 -1
  50. package/dist/console/home/RecentApps.js +2 -2
  51. package/dist/console/home/StarredApps.d.ts +1 -1
  52. package/dist/console/home/StarredApps.js +2 -2
  53. package/dist/console/marketplace/InstalledListWidget.d.ts +1 -0
  54. package/dist/console/marketplace/InstalledListWidget.js +93 -0
  55. package/dist/console/marketplace/MarkdownText.d.ts +1 -1
  56. package/dist/console/marketplace/MarketplaceAccessDenied.d.ts +1 -1
  57. package/dist/console/marketplace/MarketplaceInstalledPage.d.ts +8 -14
  58. package/dist/console/marketplace/MarketplaceInstalledPage.js +14 -66
  59. package/dist/console/marketplace/MarketplacePackagePage.d.ts +1 -1
  60. package/dist/console/marketplace/MarketplacePackagePage.js +249 -8
  61. package/dist/console/marketplace/MarketplacePage.d.ts +1 -1
  62. package/dist/console/marketplace/MarketplacePage.js +60 -3
  63. package/dist/console/marketplace/PackageIcon.d.ts +1 -1
  64. package/dist/console/marketplace/PluginDisclosure.d.ts +14 -0
  65. package/dist/console/marketplace/PluginDisclosure.js +38 -0
  66. package/dist/console/marketplace/marketplaceApi.d.ts +123 -0
  67. package/dist/console/marketplace/marketplaceApi.js +254 -1
  68. package/dist/console/organizations/CreateWorkspaceDialog.d.ts +1 -1
  69. package/dist/console/organizations/OrganizationsLayout.d.ts +1 -1
  70. package/dist/console/organizations/OrganizationsPage.d.ts +1 -1
  71. package/dist/console/organizations/manage/AcceptInvitationPage.d.ts +1 -1
  72. package/dist/console/organizations/manage/InvitationsPage.d.ts +1 -1
  73. package/dist/console/organizations/manage/InviteMemberDialog.d.ts +1 -1
  74. package/dist/console/organizations/manage/MembersPage.d.ts +1 -1
  75. package/dist/console/organizations/manage/OrganizationLayout.d.ts +1 -1
  76. package/dist/console/organizations/manage/SettingsPage.d.ts +1 -1
  77. package/dist/context/CommandPaletteProvider.d.ts +44 -0
  78. package/dist/context/CommandPaletteProvider.js +71 -0
  79. package/dist/context/FavoritesProvider.d.ts +1 -1
  80. package/dist/context/NavigationContext.d.ts +1 -1
  81. package/dist/context/RecentItemsProvider.d.ts +2 -2
  82. package/dist/context/UserStateAdapters.d.ts +1 -1
  83. package/dist/context/index.d.ts +2 -0
  84. package/dist/context/index.js +1 -0
  85. package/dist/hooks/index.d.ts +5 -2
  86. package/dist/hooks/index.js +4 -1
  87. package/dist/hooks/useActionModal.d.ts +53 -0
  88. package/dist/hooks/useActionModal.js +111 -0
  89. package/dist/hooks/useChatConversation.d.ts +107 -4
  90. package/dist/hooks/useChatConversation.js +253 -25
  91. package/dist/hooks/useConsoleActionRuntime.d.ts +70 -0
  92. package/dist/hooks/useConsoleActionRuntime.js +560 -0
  93. package/dist/hooks/useConversationList.js +61 -3
  94. package/dist/hooks/useHomeInbox.d.ts +13 -0
  95. package/dist/hooks/useHomeInbox.js +142 -0
  96. package/dist/hooks/useNavPins.js +17 -23
  97. package/dist/hooks/useNavigationSync.d.ts +33 -0
  98. package/dist/hooks/useNavigationSync.js +98 -12
  99. package/dist/hooks/useReconcileOnError.d.ts +40 -0
  100. package/dist/hooks/useReconcileOnError.js +37 -0
  101. package/dist/hooks/useRecordApprovals.d.ts +18 -19
  102. package/dist/hooks/useRecordApprovals.js +24 -40
  103. package/dist/hooks/useResponsiveSidebar.js +14 -5
  104. package/dist/hooks/useSettleSignal.d.ts +19 -0
  105. package/dist/hooks/useSettleSignal.js +20 -0
  106. package/dist/hooks/useTrackRouteAsRecent.js +35 -0
  107. package/dist/hooks/useUrlOverlay.d.ts +62 -0
  108. package/dist/hooks/useUrlOverlay.js +88 -0
  109. package/dist/index.d.ts +16 -7
  110. package/dist/index.js +12 -4
  111. package/dist/layout/ActivityFeed.d.ts +1 -1
  112. package/dist/layout/AppHeader.d.ts +3 -2
  113. package/dist/layout/AppHeader.js +237 -72
  114. package/dist/layout/AppSidebar.d.ts +2 -1
  115. package/dist/layout/AppSidebar.js +26 -46
  116. package/dist/layout/AppSwitcher.d.ts +2 -1
  117. package/dist/layout/AppSwitcher.js +9 -5
  118. package/dist/layout/AuthPageLayout.d.ts +1 -1
  119. package/dist/layout/ConnectionStatus.d.ts +1 -1
  120. package/dist/layout/ConnectionStatus.js +9 -6
  121. package/dist/layout/ConsoleChatbotFab.d.ts +19 -1
  122. package/dist/layout/ConsoleChatbotFab.js +16 -2
  123. package/dist/layout/ConsoleFloatingChatbot.d.ts +32 -2
  124. package/dist/layout/ConsoleFloatingChatbot.js +374 -41
  125. package/dist/layout/ConsoleLayout.d.ts +1 -1
  126. package/dist/layout/ConsoleLayout.js +27 -11
  127. package/dist/layout/ContextSelectors.d.ts +44 -0
  128. package/dist/layout/ContextSelectors.js +218 -0
  129. package/dist/layout/InboxPopover.d.ts +6 -1
  130. package/dist/layout/InboxPopover.js +25 -6
  131. package/dist/layout/LocaleSwitcher.d.ts +1 -1
  132. package/dist/layout/LocalizedSidebarTrigger.d.ts +2 -0
  133. package/dist/layout/LocalizedSidebarTrigger.js +15 -0
  134. package/dist/layout/MobileViewSwitcherContext.d.ts +1 -1
  135. package/dist/layout/ModeToggle.d.ts +1 -1
  136. package/dist/layout/PageHeader.d.ts +1 -1
  137. package/dist/layout/UnifiedSidebar.d.ts +2 -1
  138. package/dist/layout/UnifiedSidebar.js +116 -15
  139. package/dist/observability/index.d.ts +1 -0
  140. package/dist/observability/index.js +1 -0
  141. package/dist/observability/settleSignal.d.ts +64 -0
  142. package/dist/observability/settleSignal.js +131 -0
  143. package/dist/preview/DraftChangesPanel.d.ts +19 -0
  144. package/dist/preview/DraftChangesPanel.js +114 -0
  145. package/dist/preview/DraftPreviewBar.d.ts +8 -0
  146. package/dist/preview/DraftPreviewBar.js +86 -0
  147. package/dist/preview/PreviewDraftEmptyState.d.ts +16 -0
  148. package/dist/preview/PreviewDraftEmptyState.js +47 -0
  149. package/dist/preview/PreviewModeContext.d.ts +57 -0
  150. package/dist/preview/PreviewModeContext.js +99 -0
  151. package/dist/preview/UnpublishedAppBar.d.ts +8 -0
  152. package/dist/preview/UnpublishedAppBar.js +79 -0
  153. package/dist/preview/draftStatus.d.ts +20 -0
  154. package/dist/preview/draftStatus.js +27 -0
  155. package/dist/preview/usePublishAllDrafts.d.ts +18 -0
  156. package/dist/preview/usePublishAllDrafts.js +106 -0
  157. package/dist/providers/AdapterProvider.d.ts +1 -1
  158. package/dist/providers/AdapterProvider.js +6 -1
  159. package/dist/providers/ExpressionProvider.d.ts +1 -1
  160. package/dist/providers/MetadataProvider.d.ts +17 -2
  161. package/dist/providers/MetadataProvider.js +183 -12
  162. package/dist/runtime-config.d.ts +46 -2
  163. package/dist/runtime-config.js +39 -2
  164. package/dist/services/builtinComponents.js +68 -59
  165. package/dist/skeletons/SkeletonDashboard.d.ts +1 -1
  166. package/dist/skeletons/SkeletonDetail.d.ts +1 -1
  167. package/dist/skeletons/SkeletonGrid.d.ts +1 -1
  168. package/dist/utils/appRoute.d.ts +21 -0
  169. package/dist/utils/appRoute.js +25 -0
  170. package/dist/utils/deriveRelatedLists.d.ts +54 -0
  171. package/dist/utils/deriveRelatedLists.js +91 -0
  172. package/dist/utils/index.d.ts +4 -0
  173. package/dist/utils/index.js +3 -0
  174. package/dist/utils/managedByEmptyState.d.ts +8 -1
  175. package/dist/utils/managedByEmptyState.js +13 -7
  176. package/dist/utils/preferLocal.d.ts +18 -0
  177. package/dist/utils/preferLocal.js +24 -0
  178. package/dist/views/ActionConfirmDialog.d.ts +1 -1
  179. package/dist/views/ActionConfirmDialog.js +3 -1
  180. package/dist/views/ActionParamDialog.d.ts +6 -1
  181. package/dist/views/ActionParamDialog.js +9 -3
  182. package/dist/views/ActionResultDialog.d.ts +13 -0
  183. package/dist/views/ActionResultDialog.js +134 -0
  184. package/dist/views/ComponentNavView.d.ts +14 -1
  185. package/dist/views/CreateViewDialog.d.ts +1 -1
  186. package/dist/views/DashboardConfigPanel.d.ts +28 -0
  187. package/dist/views/DashboardConfigPanel.js +81 -0
  188. package/dist/views/DashboardView.d.ts +4 -3
  189. package/dist/views/DashboardView.js +38 -239
  190. package/dist/views/FlowRunner.d.ts +59 -0
  191. package/dist/views/FlowRunner.js +153 -0
  192. package/dist/views/InterfaceListPage.d.ts +49 -0
  193. package/dist/views/InterfaceListPage.js +347 -0
  194. package/dist/views/MetadataInspector.d.ts +2 -2
  195. package/dist/views/ObjectView.d.ts +1 -1
  196. package/dist/views/ObjectView.js +209 -532
  197. package/dist/views/PageView.d.ts +8 -3
  198. package/dist/views/PageView.js +45 -32
  199. package/dist/views/RecordDetailView.d.ts +1 -1
  200. package/dist/views/RecordDetailView.js +363 -148
  201. package/dist/views/RecordFormPage.d.ts +1 -1
  202. package/dist/views/RecordFormPage.js +26 -1
  203. package/dist/views/ReportConfigPanel.d.ts +37 -0
  204. package/dist/views/ReportConfigPanel.js +85 -0
  205. package/dist/views/ReportView.d.ts +1 -1
  206. package/dist/views/ReportView.js +116 -7
  207. package/dist/views/RuntimeDraftBar.d.ts +30 -0
  208. package/dist/views/RuntimeDraftBar.js +112 -0
  209. package/dist/views/SearchResultsPage.d.ts +1 -1
  210. package/dist/views/SearchResultsPage.js +8 -18
  211. package/dist/views/ViewConfigPanel.d.ts +24 -17
  212. package/dist/views/ViewConfigPanel.js +121 -77
  213. package/dist/views/index.d.ts +1 -1
  214. package/dist/views/index.js +1 -1
  215. package/dist/views/metadata-admin/AuditPanel.d.ts +28 -0
  216. package/dist/views/metadata-admin/AuditPanel.js +79 -0
  217. package/dist/views/metadata-admin/DiagnosticsPage.d.ts +20 -0
  218. package/dist/views/metadata-admin/DiagnosticsPage.js +69 -0
  219. package/dist/views/metadata-admin/DirectoryPage.d.ts +16 -1
  220. package/dist/views/metadata-admin/DirectoryPage.js +113 -24
  221. package/dist/views/metadata-admin/DraftReviewPanel.d.ts +33 -0
  222. package/dist/views/metadata-admin/DraftReviewPanel.js +77 -0
  223. package/dist/views/metadata-admin/EmbeddedItemEditor.d.ts +17 -1
  224. package/dist/views/metadata-admin/EmbeddedItemEditor.js +15 -8
  225. package/dist/views/metadata-admin/JsonSourceEditor.d.ts +37 -0
  226. package/dist/views/metadata-admin/JsonSourceEditor.js +178 -0
  227. package/dist/views/metadata-admin/LayeredDiff.d.ts +39 -1
  228. package/dist/views/metadata-admin/LayeredDiff.js +171 -5
  229. package/dist/views/metadata-admin/MetadataDetailDrawer.d.ts +15 -1
  230. package/dist/views/metadata-admin/MetadataTypeActions.d.ts +48 -0
  231. package/dist/views/metadata-admin/MetadataTypeActions.js +165 -0
  232. package/dist/views/metadata-admin/PackagesPage.d.ts +18 -0
  233. package/dist/views/metadata-admin/PackagesPage.js +395 -0
  234. package/dist/views/metadata-admin/PageShell.d.ts +1 -1
  235. package/dist/views/metadata-admin/PageShell.js +9 -4
  236. package/dist/views/metadata-admin/PermissionMatrixEditor.d.ts +35 -1
  237. package/dist/views/metadata-admin/QuickFind.d.ts +21 -1
  238. package/dist/views/metadata-admin/QuickFind.js +6 -3
  239. package/dist/views/metadata-admin/RelatedPanel.d.ts +24 -1
  240. package/dist/views/metadata-admin/RelatedPanel.js +20 -18
  241. package/dist/views/metadata-admin/ResourceEditPage.d.ts +40 -1
  242. package/dist/views/metadata-admin/ResourceEditPage.js +1223 -60
  243. package/dist/views/metadata-admin/ResourceHistoryPage.d.ts +39 -1
  244. package/dist/views/metadata-admin/ResourceHistoryPage.js +66 -16
  245. package/dist/views/metadata-admin/ResourceListPage.d.ts +13 -1
  246. package/dist/views/metadata-admin/ResourceListPage.js +266 -30
  247. package/dist/views/metadata-admin/ResourceRouter.d.ts +23 -1
  248. package/dist/views/metadata-admin/SchemaForm.d.ts +34 -1
  249. package/dist/views/metadata-admin/SchemaForm.js +559 -49
  250. package/dist/views/metadata-admin/StudioHomePage.d.ts +22 -0
  251. package/dist/views/metadata-admin/StudioHomePage.js +213 -0
  252. package/dist/views/metadata-admin/anchors.js +237 -24
  253. package/dist/views/metadata-admin/clientValidation.d.ts +50 -0
  254. package/dist/views/metadata-admin/clientValidation.js +169 -0
  255. package/dist/views/metadata-admin/color-variant-field.d.ts +30 -0
  256. package/dist/views/metadata-admin/color-variant-field.js +38 -0
  257. package/dist/views/metadata-admin/createDerive.d.ts +75 -0
  258. package/dist/views/metadata-admin/createDerive.js +179 -0
  259. package/dist/views/metadata-admin/dashboard-schema.d.ts +12 -0
  260. package/dist/views/metadata-admin/dashboard-schema.js +80 -0
  261. package/dist/views/metadata-admin/datasource/DatasourceResourcePage.d.ts +35 -0
  262. package/dist/views/metadata-admin/datasource/DatasourceResourcePage.js +327 -0
  263. package/dist/views/metadata-admin/datasource/register.d.ts +1 -0
  264. package/dist/views/metadata-admin/datasource/register.js +24 -0
  265. package/dist/views/metadata-admin/default-inspector-registry.d.ts +49 -0
  266. package/dist/views/metadata-admin/default-inspector-registry.js +8 -0
  267. package/dist/views/metadata-admin/default-schemas.js +115 -10
  268. package/dist/views/metadata-admin/external/ExternalDatasourcePanel.d.ts +27 -0
  269. package/dist/views/metadata-admin/external/ExternalDatasourcePanel.js +69 -0
  270. package/dist/views/metadata-admin/external/ImportObjectDialog.d.ts +27 -0
  271. package/dist/views/metadata-admin/external/ImportObjectDialog.js +77 -0
  272. package/dist/views/metadata-admin/external/SchemaBrowser.d.ts +16 -0
  273. package/dist/views/metadata-admin/external/SchemaBrowser.js +74 -0
  274. package/dist/views/metadata-admin/external/ValidationPanel.d.ts +16 -0
  275. package/dist/views/metadata-admin/external/ValidationPanel.js +68 -0
  276. package/dist/views/metadata-admin/external/api.d.ts +100 -0
  277. package/dist/views/metadata-admin/external/api.js +124 -0
  278. package/dist/views/metadata-admin/i18n.d.ts +1 -0
  279. package/dist/views/metadata-admin/i18n.js +1166 -2
  280. package/dist/views/metadata-admin/index.d.ts +8 -5
  281. package/dist/views/metadata-admin/index.js +12 -2
  282. package/dist/views/metadata-admin/inspector-registry.d.ts +51 -0
  283. package/dist/views/metadata-admin/inspector-registry.js +11 -0
  284. package/dist/views/metadata-admin/inspectors/ActionDefaultInspector.d.ts +30 -0
  285. package/dist/views/metadata-admin/inspectors/ActionDefaultInspector.js +180 -0
  286. package/dist/views/metadata-admin/inspectors/AppNavInspector.d.ts +16 -0
  287. package/dist/views/metadata-admin/inspectors/AppNavInspector.js +110 -0
  288. package/dist/views/metadata-admin/inspectors/ConditionBuilder.d.ts +29 -0
  289. package/dist/views/metadata-admin/inspectors/ConditionBuilder.js +154 -0
  290. package/dist/views/metadata-admin/inspectors/DashboardDefaultInspector.d.ts +28 -0
  291. package/dist/views/metadata-admin/inspectors/DashboardDefaultInspector.js +110 -0
  292. package/dist/views/metadata-admin/inspectors/DashboardWidgetInspector.d.ts +18 -0
  293. package/dist/views/metadata-admin/inspectors/DashboardWidgetInspector.js +139 -0
  294. package/dist/views/metadata-admin/inspectors/DatasetDefaultInspector.d.ts +21 -0
  295. package/dist/views/metadata-admin/inspectors/DatasetDefaultInspector.js +107 -0
  296. package/dist/views/metadata-admin/inspectors/FlowEdgeInspector.d.ts +16 -0
  297. package/dist/views/metadata-admin/inspectors/FlowEdgeInspector.js +45 -0
  298. package/dist/views/metadata-admin/inspectors/FlowInspector.d.ts +12 -0
  299. package/dist/views/metadata-admin/inspectors/FlowInspector.js +9 -0
  300. package/dist/views/metadata-admin/inspectors/FlowKeyValueField.d.ts +30 -0
  301. package/dist/views/metadata-admin/inspectors/FlowKeyValueField.js +125 -0
  302. package/dist/views/metadata-admin/inspectors/FlowNodeConfigField.d.ts +18 -0
  303. package/dist/views/metadata-admin/inspectors/FlowNodeConfigField.js +40 -0
  304. package/dist/views/metadata-admin/inspectors/FlowNodeInspector.d.ts +14 -0
  305. package/dist/views/metadata-admin/inspectors/FlowNodeInspector.js +140 -0
  306. package/dist/views/metadata-admin/inspectors/FlowObjectListField.d.ts +26 -0
  307. package/dist/views/metadata-admin/inspectors/FlowObjectListField.js +105 -0
  308. package/dist/views/metadata-admin/inspectors/FlowReferenceField.d.ts +83 -0
  309. package/dist/views/metadata-admin/inspectors/FlowReferenceField.js +181 -0
  310. package/dist/views/metadata-admin/inspectors/FlowStringListField.d.ts +21 -0
  311. package/dist/views/metadata-admin/inspectors/FlowStringListField.js +60 -0
  312. package/dist/views/metadata-admin/inspectors/InspectorComboField.d.ts +40 -0
  313. package/dist/views/metadata-admin/inspectors/InspectorComboField.js +61 -0
  314. package/dist/views/metadata-admin/inspectors/ObjectDefaultInspector.d.ts +21 -0
  315. package/dist/views/metadata-admin/inspectors/ObjectDefaultInspector.js +54 -0
  316. package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.d.ts +23 -0
  317. package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.js +330 -0
  318. package/dist/views/metadata-admin/inspectors/PageBlockInspector.d.ts +48 -0
  319. package/dist/views/metadata-admin/inspectors/PageBlockInspector.js +332 -0
  320. package/dist/views/metadata-admin/inspectors/ReportDefaultInspector.d.ts +58 -0
  321. package/dist/views/metadata-admin/inspectors/ReportDefaultInspector.js +160 -0
  322. package/dist/views/metadata-admin/inspectors/ViewColumnInspector.d.ts +19 -0
  323. package/dist/views/metadata-admin/inspectors/ViewColumnInspector.js +144 -0
  324. package/dist/views/metadata-admin/inspectors/ViewInspector.d.ts +19 -0
  325. package/dist/views/metadata-admin/inspectors/ViewInspector.js +21 -0
  326. package/dist/views/metadata-admin/inspectors/ViewVariantInspector.d.ts +54 -0
  327. package/dist/views/metadata-admin/inspectors/ViewVariantInspector.js +191 -0
  328. package/dist/views/metadata-admin/inspectors/_shared.d.ts +124 -0
  329. package/dist/views/metadata-admin/inspectors/_shared.js +113 -0
  330. package/dist/views/metadata-admin/inspectors/expression-validate.d.ts +26 -0
  331. package/dist/views/metadata-admin/inspectors/expression-validate.js +66 -0
  332. package/dist/views/metadata-admin/inspectors/flow-node-config.d.ts +143 -0
  333. package/dist/views/metadata-admin/inspectors/flow-node-config.js +461 -0
  334. package/dist/views/metadata-admin/inspectors/index.d.ts +1 -0
  335. package/dist/views/metadata-admin/inspectors/index.js +45 -0
  336. package/dist/views/metadata-admin/inspectors/json-schema-to-fields.d.ts +40 -0
  337. package/dist/views/metadata-admin/inspectors/json-schema-to-fields.js +227 -0
  338. package/dist/views/metadata-admin/inspectors/useDatasetFields.d.ts +72 -0
  339. package/dist/views/metadata-admin/inspectors/useDatasetFields.js +0 -0
  340. package/dist/views/metadata-admin/mergeServerFields.d.ts +65 -0
  341. package/dist/views/metadata-admin/mergeServerFields.js +56 -0
  342. package/dist/views/metadata-admin/preview-registry.d.ts +55 -0
  343. package/dist/views/metadata-admin/previews/ActionPreview.d.ts +25 -0
  344. package/dist/views/metadata-admin/previews/ActionPreview.js +238 -0
  345. package/dist/views/metadata-admin/previews/AddWidgetPicker.d.ts +12 -0
  346. package/dist/views/metadata-admin/previews/AddWidgetPicker.js +56 -0
  347. package/dist/views/metadata-admin/previews/AgentPreview.d.ts +24 -0
  348. package/dist/views/metadata-admin/previews/AgentPreview.js +100 -0
  349. package/dist/views/metadata-admin/previews/AppNavCanvas.d.ts +31 -0
  350. package/dist/views/metadata-admin/previews/AppNavCanvas.js +260 -0
  351. package/dist/views/metadata-admin/previews/AppPreview.d.ts +16 -1
  352. package/dist/views/metadata-admin/previews/AppPreview.js +23 -14
  353. package/dist/views/metadata-admin/previews/BookPreview.d.ts +20 -0
  354. package/dist/views/metadata-admin/previews/BookPreview.js +132 -0
  355. package/dist/views/metadata-admin/previews/DashboardPreview.d.ts +16 -1
  356. package/dist/views/metadata-admin/previews/DashboardPreview.js +110 -8
  357. package/dist/views/metadata-admin/previews/DatasetPreview.d.ts +18 -0
  358. package/dist/views/metadata-admin/previews/DatasetPreview.js +89 -0
  359. package/dist/views/metadata-admin/previews/DatasourcePreview.d.ts +23 -0
  360. package/dist/views/metadata-admin/previews/DatasourcePreview.js +68 -0
  361. package/dist/views/metadata-admin/previews/EmailTemplatePreview.d.ts +14 -1
  362. package/dist/views/metadata-admin/previews/FieldStub.d.ts +30 -0
  363. package/dist/views/metadata-admin/previews/FieldStub.js +104 -0
  364. package/dist/views/metadata-admin/previews/FieldsListEditor.d.ts +50 -0
  365. package/dist/views/metadata-admin/previews/FieldsListEditor.js +97 -0
  366. package/dist/views/metadata-admin/previews/FlowCanvas.d.ts +43 -0
  367. package/dist/views/metadata-admin/previews/FlowCanvas.js +328 -0
  368. package/dist/views/metadata-admin/previews/FlowPreview.d.ts +20 -0
  369. package/dist/views/metadata-admin/previews/FlowPreview.js +92 -0
  370. package/dist/views/metadata-admin/previews/FlowRunsPanel.d.ts +46 -0
  371. package/dist/views/metadata-admin/previews/FlowRunsPanel.js +97 -0
  372. package/dist/views/metadata-admin/previews/FlowSimulatorPanel.d.ts +25 -0
  373. package/dist/views/metadata-admin/previews/FlowSimulatorPanel.js +170 -0
  374. package/dist/views/metadata-admin/previews/JobPreview.d.ts +28 -0
  375. package/dist/views/metadata-admin/previews/JobPreview.js +290 -0
  376. package/dist/views/metadata-admin/previews/ObjectFormCanvas.d.ts +30 -0
  377. package/dist/views/metadata-admin/previews/ObjectFormCanvas.js +547 -0
  378. package/dist/views/metadata-admin/previews/ObjectPreview.d.ts +14 -1
  379. package/dist/views/metadata-admin/previews/ObjectPreview.js +5 -30
  380. package/dist/views/metadata-admin/previews/OutlineStrip.d.ts +32 -0
  381. package/dist/views/metadata-admin/previews/OutlineStrip.js +8 -0
  382. package/dist/views/metadata-admin/previews/PageBlockCanvas.d.ts +49 -0
  383. package/dist/views/metadata-admin/previews/PageBlockCanvas.js +510 -0
  384. package/dist/views/metadata-admin/previews/PagePreview.d.ts +10 -1
  385. package/dist/views/metadata-admin/previews/PagePreview.js +90 -4
  386. package/dist/views/metadata-admin/previews/PermissionPreview.d.ts +27 -0
  387. package/dist/views/metadata-admin/previews/PermissionPreview.js +115 -0
  388. package/dist/views/metadata-admin/previews/PreviewShell.d.ts +29 -6
  389. package/dist/views/metadata-admin/previews/PreviewShell.js +16 -3
  390. package/dist/views/metadata-admin/previews/ReportPreview.d.ts +18 -1
  391. package/dist/views/metadata-admin/previews/ReportPreview.js +23 -15
  392. package/dist/views/metadata-admin/previews/RolePreview.d.ts +19 -0
  393. package/dist/views/metadata-admin/previews/RolePreview.js +14 -0
  394. package/dist/views/metadata-admin/previews/SkillPreview.d.ts +22 -0
  395. package/dist/views/metadata-admin/previews/SkillPreview.js +34 -0
  396. package/dist/views/metadata-admin/previews/ToolPreview.d.ts +25 -0
  397. package/dist/views/metadata-admin/previews/ToolPreview.js +122 -0
  398. package/dist/views/metadata-admin/previews/TranslationPreview.d.ts +25 -0
  399. package/dist/views/metadata-admin/previews/TranslationPreview.js +52 -0
  400. package/dist/views/metadata-admin/previews/ValidationPreview.d.ts +27 -0
  401. package/dist/views/metadata-admin/previews/ValidationPreview.js +110 -0
  402. package/dist/views/metadata-admin/previews/ViewColumnPanes.d.ts +62 -0
  403. package/dist/views/metadata-admin/previews/ViewColumnPanes.js +140 -0
  404. package/dist/views/metadata-admin/previews/ViewPreview.d.ts +23 -1
  405. package/dist/views/metadata-admin/previews/ViewPreview.js +101 -73
  406. package/dist/views/metadata-admin/previews/block-config.d.ts +82 -0
  407. package/dist/views/metadata-admin/previews/block-config.js +324 -0
  408. package/dist/views/metadata-admin/previews/block-types.d.ts +40 -0
  409. package/dist/views/metadata-admin/previews/block-types.js +110 -0
  410. package/dist/views/metadata-admin/previews/field-types.d.ts +53 -0
  411. package/dist/views/metadata-admin/previews/field-types.js +97 -0
  412. package/dist/views/metadata-admin/previews/flow-canvas-layout.d.ts +88 -0
  413. package/dist/views/metadata-admin/previews/flow-canvas-layout.js +190 -0
  414. package/dist/views/metadata-admin/previews/flow-canvas-parts.d.ts +88 -0
  415. package/dist/views/metadata-admin/previews/flow-canvas-parts.js +358 -0
  416. package/dist/views/metadata-admin/previews/form-preview.d.ts +24 -0
  417. package/dist/views/metadata-admin/previews/form-preview.js +29 -0
  418. package/dist/views/metadata-admin/previews/index.js +43 -0
  419. package/dist/views/metadata-admin/previews/object-fields-bridge.d.ts +66 -0
  420. package/dist/views/metadata-admin/previews/object-fields-bridge.js +171 -0
  421. package/dist/views/metadata-admin/previews/object-fields-io.d.ts +109 -0
  422. package/dist/views/metadata-admin/previews/object-fields-io.js +208 -0
  423. package/dist/views/metadata-admin/previews/simulator/flow-sim-types.d.ts +91 -0
  424. package/dist/views/metadata-admin/previews/simulator/flow-sim-types.js +2 -0
  425. package/dist/views/metadata-admin/previews/simulator/flow-sim-validate.d.ts +8 -0
  426. package/dist/views/metadata-admin/previews/simulator/flow-sim-validate.js +113 -0
  427. package/dist/views/metadata-admin/previews/simulator/flow-simulator.d.ts +44 -0
  428. package/dist/views/metadata-admin/previews/simulator/flow-simulator.js +316 -0
  429. package/dist/views/metadata-admin/previews/useDatasetCatalog.d.ts +47 -0
  430. package/dist/views/metadata-admin/previews/useDatasetCatalog.js +133 -0
  431. package/dist/views/metadata-admin/previews/useFlowNodePalette.d.ts +44 -0
  432. package/dist/views/metadata-admin/previews/useFlowNodePalette.js +124 -0
  433. package/dist/views/metadata-admin/previews/useMetaOptions.d.ts +8 -0
  434. package/dist/views/metadata-admin/previews/useMetaOptions.js +50 -0
  435. package/dist/views/metadata-admin/previews/useObjectFields.d.ts +23 -0
  436. package/dist/views/metadata-admin/previews/useObjectFields.js +79 -0
  437. package/dist/views/metadata-admin/previews/useObjectOptions.d.ts +8 -0
  438. package/dist/views/metadata-admin/previews/useObjectOptions.js +43 -0
  439. package/dist/views/metadata-admin/previews/view-column-io.d.ts +42 -0
  440. package/dist/views/metadata-admin/previews/view-column-io.js +73 -0
  441. package/dist/views/metadata-admin/previews/widget-types.d.ts +24 -0
  442. package/dist/views/metadata-admin/previews/widget-types.js +40 -0
  443. package/dist/views/metadata-admin/registry.d.ts +140 -19
  444. package/dist/views/metadata-admin/report-schema.d.ts +26 -0
  445. package/dist/views/metadata-admin/report-schema.js +121 -0
  446. package/dist/views/metadata-admin/useMetadata.d.ts +100 -2
  447. package/dist/views/metadata-admin/useMetadata.js +155 -4
  448. package/dist/views/metadata-admin/view-item-normalize.d.ts +20 -0
  449. package/dist/views/metadata-admin/view-item-normalize.js +68 -0
  450. package/dist/views/metadata-admin/view-schema.d.ts +16 -0
  451. package/dist/views/metadata-admin/view-schema.js +107 -0
  452. package/dist/views/metadata-admin/view-variant-model.d.ts +23 -0
  453. package/dist/views/metadata-admin/view-variant-model.js +64 -0
  454. package/dist/views/metadata-admin/widgets.d.ts +89 -1
  455. package/dist/views/metadata-admin/widgets.js +491 -17
  456. package/dist/views/runtime-metadata-persistence.d.ts +78 -0
  457. package/dist/views/runtime-metadata-persistence.js +89 -0
  458. package/dist/views/useOpenRecordList.d.ts +18 -0
  459. package/dist/views/useOpenRecordList.js +36 -0
  460. package/dist/views/userFilterUrlState.d.ts +15 -0
  461. package/dist/views/userFilterUrlState.js +53 -0
  462. package/dist/views/view-config-adapter.d.ts +38 -0
  463. package/dist/views/view-config-adapter.js +80 -0
  464. package/package.json +52 -34
  465. package/dist/views/DesignDrawer.d.ts +0 -28
  466. package/dist/views/DesignDrawer.js +0 -51
  467. package/dist/views/metadata-admin/DesignerEditorWrapper.d.ts +0 -68
  468. package/dist/views/metadata-admin/DesignerEditorWrapper.js +0 -158
@@ -35,10 +35,13 @@ import { Label } from '@object-ui/components';
35
35
  import { Switch } from '@object-ui/components';
36
36
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@object-ui/components';
37
37
  import { Button } from '@object-ui/components';
38
- import { Plus, Trash2, ChevronDown, ChevronRight } from 'lucide-react';
38
+ import { Plus, Trash2, ChevronDown, ChevronRight, GripVertical, Settings2 } from 'lucide-react';
39
+ import { Popover, PopoverTrigger, PopoverContent } from '@object-ui/components';
39
40
  import { Tabs, TabsList, TabsTrigger, TabsContent, } from '@object-ui/components';
41
+ import { Collapsible, CollapsibleTrigger, CollapsibleContent, } from '@object-ui/components';
40
42
  import { evaluatePredicate } from './predicate';
41
43
  import { WIDGETS } from './widgets';
44
+ import { detectLocale, t, tFormat, translateValidationMessage } from './i18n';
42
45
  /** Widgets that don't need a custom renderer — they overlay on the
43
46
  * existing default control (textarea/input/etc) and just act as a hint. */
44
47
  const KNOWN_PASSTHROUGH_WIDGETS = new Set([
@@ -49,6 +52,72 @@ const KNOWN_PASSTHROUGH_WIDGETS = new Set([
49
52
  'select',
50
53
  'json',
51
54
  ]);
55
+ /**
56
+ * Pick the best-matching branch of a JSON Schema `oneOf` / `anyOf`
57
+ * union for the given value. Scores each branch by how many of its
58
+ * `required` keys are present in the value (and same `type`); falls
59
+ * back to the first branch when nothing matches (so create-mode forms
60
+ * with empty values still render *something* structured).
61
+ *
62
+ * Returns the original schema unchanged when there's no union to
63
+ * resolve. Used by the recursive renderer so View `data` (provider
64
+ * discriminator), `columns`, `sort`, etc. produce real labelled
65
+ * inputs instead of a raw JSON blob.
66
+ */
67
+ function resolveUnionBranch(schema, value) {
68
+ if (!schema)
69
+ return schema;
70
+ const branches = (schema.oneOf ?? schema.anyOf);
71
+ if (!Array.isArray(branches) || branches.length === 0)
72
+ return schema;
73
+ const isPlainObj = value != null && typeof value === 'object' && !Array.isArray(value);
74
+ const isArray = Array.isArray(value);
75
+ const valKeys = isPlainObj ? new Set(Object.keys(value)) : null;
76
+ let best = null;
77
+ const firstItem = isArray && value.length ? value[0] : undefined;
78
+ const firstItemIsObj = firstItem != null && typeof firstItem === 'object' && !Array.isArray(firstItem);
79
+ for (const b of branches) {
80
+ let score = 0;
81
+ if (b.type === 'array' && isArray) {
82
+ score += 5;
83
+ // Tiebreaker for `anyOf [array<string>, array<object>]` etc — match the
84
+ // branch's items.type against the actual element type.
85
+ const itemType = b.items?.type;
86
+ if (itemType === 'object' && firstItemIsObj)
87
+ score += 3;
88
+ else if ((itemType === 'string' && typeof firstItem === 'string') ||
89
+ (itemType === 'number' && typeof firstItem === 'number') ||
90
+ (itemType === 'integer' && typeof firstItem === 'number'))
91
+ score += 3;
92
+ }
93
+ if (b.type === 'object' && isPlainObj)
94
+ score += 5;
95
+ if (b.type === 'string' && typeof value === 'string')
96
+ score += 5;
97
+ if (b.type === 'number' && typeof value === 'number')
98
+ score += 5;
99
+ if (b.type === 'boolean' && typeof value === 'boolean')
100
+ score += 5;
101
+ if (valKeys && Array.isArray(b.required)) {
102
+ for (const r of b.required) {
103
+ if (valKeys.has(r))
104
+ score += 1;
105
+ }
106
+ }
107
+ if (!best || score > best.score)
108
+ best = { branch: b, score };
109
+ }
110
+ // Merge the branch's shape on top of any parent metadata (title /
111
+ // description) so the recursive renderer still sees the field's
112
+ // documentation.
113
+ const picked = best?.branch ?? branches[0];
114
+ return {
115
+ ...schema,
116
+ ...picked,
117
+ oneOf: undefined,
118
+ anyOf: undefined,
119
+ };
120
+ }
52
121
  /**
53
122
  * Infer widget name from FormFieldSpec.type (Data.FieldType) and schema.
54
123
  * Priority: explicit widget > type-based inference > schema-based inference > default.
@@ -86,6 +155,8 @@ function inferWidget(fieldSpec, schema) {
86
155
  return 'composite';
87
156
  if (t === 'repeater')
88
157
  return 'repeater';
158
+ if (t === 'record')
159
+ return 'record';
89
160
  // Relational
90
161
  if (t === 'lookup' || t === 'master_detail')
91
162
  return 'ref-object';
@@ -124,6 +195,11 @@ function inferWidget(fieldSpec, schema) {
124
195
  // 3. Infer from JSON Schema
125
196
  if (schema) {
126
197
  const type = schema.type;
198
+ // Array of enum → multi-select of the allowed values (picker, not free
199
+ // text). Checked before the generic string-array case because a Zod
200
+ // `z.enum` serialises as `items: { type: 'string', enum: [...] }`.
201
+ if (type === 'array' && Array.isArray(schema.items?.enum))
202
+ return 'multiselect';
127
203
  // Array of strings → string-tags
128
204
  if (type === 'array' && schema.items?.type === 'string')
129
205
  return 'string-tags';
@@ -155,6 +231,111 @@ function inferWidget(fieldSpec, schema) {
155
231
  // 4. Default fallback
156
232
  return undefined;
157
233
  }
234
+ /**
235
+ * Detect a field-reference widget by NAME CONVENTION, gated on having an
236
+ * object field catalog in `widgetContext`. This is what makes every view
237
+ * type's field-reference config (titleField, groupByField, startDateField,
238
+ * xAxisField, visibleFields, yAxisFields, …) render as an object-field
239
+ * picker instead of free text — without hardcoding per-type knowledge.
240
+ *
241
+ * Convention (spec props carry no `format:'field'` marker, so name + shape
242
+ * is the pragmatic signal):
243
+ * • SINGLE (`field-ref`): a string prop named `*Field` (or bare `field`),
244
+ * excluding enum props (those are real selects).
245
+ * • MULTI (`field-multi`): an array-of-strings prop named `*Fields`
246
+ * (or `columns` / `fieldOrder`).
247
+ */
248
+ function detectFieldRefWidget(name, schema, widgetContext) {
249
+ if (!widgetContext?.objectFields)
250
+ return undefined;
251
+ if (Array.isArray(schema?.enum))
252
+ return undefined;
253
+ const isStringArray = schema?.type === 'array' &&
254
+ schema.items?.type === 'string';
255
+ if (isStringArray && (/Fields$/.test(name) || name === 'columns' || name === 'fieldOrder')) {
256
+ return 'field-multi';
257
+ }
258
+ const isString = schema?.type === 'string' ||
259
+ (Array.isArray(schema?.anyOf) &&
260
+ schema.anyOf.some((b) => b?.type === 'string'));
261
+ if (isString && (/.Field$/.test(name) || name === 'field')) {
262
+ return 'field-ref';
263
+ }
264
+ return undefined;
265
+ }
266
+ /**
267
+ * Detect the icon picker by NAME CONVENTION: a string prop named `icon` (or
268
+ * `*Icon`) with no enum. Mirrors {@link detectFieldRefWidget} so every metadata
269
+ * type's icon field renders the searchable Lucide picker instead of a free-text
270
+ * input, without each spec `*.form.ts` having to pin `widget: 'icon'`.
271
+ */
272
+ function detectIconWidget(name, schema) {
273
+ if (Array.isArray(schema?.enum))
274
+ return undefined;
275
+ const isString = schema?.type === 'string' ||
276
+ (Array.isArray(schema?.anyOf) && schema.anyOf.some((b) => b?.type === 'string'));
277
+ if (!isString)
278
+ return undefined;
279
+ if (name === 'icon' || /Icon$/.test(name))
280
+ return 'icon';
281
+ return undefined;
282
+ }
283
+ /**
284
+ * Detect the color swatch picker by NAME CONVENTION: a string field named
285
+ * `color`, `colorVariant`, or `*Color`. An enum gets the semantic swatch row;
286
+ * a free string gets the native hex picker. Mirrors the icon/field-ref
287
+ * conventions so color fields are consistent across every metadata type.
288
+ */
289
+ function detectColorWidget(name, schema) {
290
+ const isString = schema?.type === 'string' ||
291
+ Array.isArray(schema?.enum) ||
292
+ (Array.isArray(schema?.anyOf) && schema.anyOf.some((b) => b?.type === 'string'));
293
+ if (!isString)
294
+ return undefined;
295
+ if (name === 'color' || name === 'colorVariant' || /Color$/.test(name))
296
+ return 'color-picker';
297
+ return undefined;
298
+ }
299
+ const CONDITION_FIELD_NAMES = new Set(['visible', 'hidden', 'disabled', 'visibleOn', 'condition', 'predicate']);
300
+ /**
301
+ * Detect a CEL predicate field by NAME CONVENTION (`visible` / `hidden` /
302
+ * `disabled` / `visibleOn` / `condition` / `*When`) so it renders the no-code
303
+ * condition builder instead of a raw expression text box. String-only, no enum.
304
+ */
305
+ function detectConditionWidget(name, schema) {
306
+ if (Array.isArray(schema?.enum))
307
+ return undefined;
308
+ const isString = schema?.type === 'string' ||
309
+ (Array.isArray(schema?.anyOf) && schema.anyOf.some((b) => b?.type === 'string'));
310
+ if (!isString)
311
+ return undefined;
312
+ if (CONDITION_FIELD_NAMES.has(name) || /When$/.test(name))
313
+ return 'condition';
314
+ return undefined;
315
+ }
316
+ const SECRET_FIELD_NAME_RE = /(^|_)(secret|token|api[_-]?key|access[_-]?key|client[_-]?secret|credential|private[_-]?key|passphrase)$/i;
317
+ /**
318
+ * Detect a write-only credential field → the masked `secret` widget. The
319
+ * reliable signals are `format: 'password'` (driver configSchemas use this) and
320
+ * JSON-Schema `writeOnly: true`; a conservative NAME CONVENTION (secret / token
321
+ * / apiKey / accessKey / clientSecret / credential / privateKey / passphrase)
322
+ * is the secondary cue. So credential fields render masked + write-only on every
323
+ * metadata type, without each spec pinning `widget: 'secret'`. Bare `password`
324
+ * is intentionally NOT matched here (auth owns that field, one-way hashed).
325
+ */
326
+ function detectSecretWidget(name, schema) {
327
+ if (schema?.format === 'password' || schema?.writeOnly === true)
328
+ return 'secret';
329
+ if (Array.isArray(schema?.enum))
330
+ return undefined;
331
+ const isString = schema?.type === 'string' ||
332
+ (Array.isArray(schema?.anyOf) && schema.anyOf.some((b) => b?.type === 'string'));
333
+ if (!isString)
334
+ return undefined;
335
+ if (SECRET_FIELD_NAME_RE.test(name))
336
+ return 'secret';
337
+ return undefined;
338
+ }
158
339
  export function SchemaForm({ schema, form, value, onChange, issues = [], hiddenFields = [], fieldOrder = [], readOnly = false, createMode = false, widgetContext, }) {
159
340
  // No schema → synthesize one from the value's top-level keys so the
160
341
  // form renderer can still produce a structured, labelled view (with
@@ -181,8 +362,9 @@ export function SchemaForm({ schema, form, value, onChange, issues = [], hiddenF
181
362
  const issuesByPath = React.useMemo(() => {
182
363
  var _a;
183
364
  const map = {};
365
+ const locale = detectLocale();
184
366
  for (const i of issues) {
185
- (map[_a = i.path] ?? (map[_a] = [])).push(i.message);
367
+ (map[_a = i.path] ?? (map[_a] = [])).push(translateValidationMessage(i.message, locale));
186
368
  }
187
369
  return map;
188
370
  }, [issues]);
@@ -196,10 +378,36 @@ export function SchemaForm({ schema, form, value, onChange, issues = [], hiddenF
196
378
  }
197
379
  // If the framework provided a FormView layout, render sections (tabbed
198
380
  // or simple). Otherwise fall through to the flat property list.
381
+ //
382
+ // Guard: when none of the fields declared by the layout actually exist
383
+ // in the JSON schema (typically because the schema was reshaped under
384
+ // a nested wrapper, e.g. `view` now bundles its props under
385
+ // `list/form/listViews/formViews`), the layout would render a wall of
386
+ // amber "missing from schema" warnings and nothing else. Detect that
387
+ // total mismatch and fall through to the flat schema-driven
388
+ // rendering so the user still gets a usable form.
199
389
  if (form?.sections?.length) {
200
- return (_jsx(SectionedSchemaForm, { form: form, props: props, required: required, hiddenFields: hiddenFields, issuesByPath: issuesByPath, value: v, readOnly: readOnly, createMode: createMode, widgetContext: widgetContext, onChange: setField }));
390
+ const declaredFields = [];
391
+ for (const s of form.sections) {
392
+ for (const f of s.fields) {
393
+ declaredFields.push(typeof f === 'string' ? f : f.field);
394
+ }
395
+ }
396
+ const matched = declaredFields.filter((f) => props[f]).length;
397
+ const usable = declaredFields.length === 0 || matched > 0;
398
+ if (usable) {
399
+ return (_jsx(SectionedSchemaForm, { form: form, props: props, required: required, hiddenFields: hiddenFields, issuesByPath: issuesByPath, value: v, readOnly: readOnly, createMode: createMode, widgetContext: widgetContext, onChange: setField }));
400
+ }
401
+ if (typeof console !== 'undefined') {
402
+ console.warn('[SchemaForm] form layout declares no fields that exist in the schema; ' +
403
+ 'falling back to flat schema-driven rendering. Declared:', declaredFields, 'Available:', Object.keys(props));
404
+ }
201
405
  }
202
- return (_jsx("div", { className: "space-y-4", children: keys.map((key) => (_jsx(FieldRow, { name: key, schema: props[key], value: v[key], required: required.includes(key), issues: issuesByPath[key], readOnly: readOnly, widgetContext: widgetContext, formData: v, onChange: (val) => setField(key, val) }, key))) }));
406
+ return (_jsx("div", { className: "space-y-4", children: keys.map((key) => (_jsx(FieldRow, { name: key, schema: props[key], value: v[key], required: required.includes(key), issues: issuesByPath[key], readOnly: readOnly, widgetContext: widgetContext,
407
+ // Honor an explicit `widget` declared on a raw JSON-schema property
408
+ // (e.g. createSchema's `object: { widget: 'ref:object' }`) so the
409
+ // flat auto-form can use rich pickers, not just type inference.
410
+ fieldSpec: props[key]?.widget ? { field: key, widget: props[key].widget } : undefined, formData: v, onChange: (val) => setField(key, val) }, key))) }));
203
411
  }
204
412
  /* ----- sectioned layout (FormView spec) ---------------------------------- */
205
413
  function normaliseField(f) {
@@ -225,20 +433,29 @@ function SectionedSchemaForm({ form, props, required, hiddenFields, issuesByPath
225
433
  if (fields.length === 0)
226
434
  return null;
227
435
  const cols = s.columns ?? 1;
228
- return (_jsxs("section", { className: "space-y-3 rounded-md border border-border/40 bg-card/30 p-4", children: [s.label && (_jsxs("header", { children: [_jsx("h3", { className: "text-sm font-semibold text-foreground/90", children: s.label }), s.description && (_jsx("p", { className: "text-xs text-muted-foreground", children: s.description }))] })), _jsx("div", { className: "grid gap-4", style: {
229
- gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
230
- }, children: fields.map((f) => {
231
- const propSchema = props[f.field];
232
- if (!propSchema) {
233
- return (_jsxs("div", { className: "rounded border border-dashed border-amber-500/40 bg-amber-500/5 p-2 text-xs text-amber-700 dark:text-amber-300", style: { gridColumn: `span ${f.colSpan ?? 1}` }, children: ["\u26A0\uFE0F Field ", _jsx("code", { children: f.field }), " declared in form layout but missing from schema. Skipping."] }, f.field));
234
- }
235
- return (_jsx("div", { style: { gridColumn: `span ${f.colSpan ?? 1}` }, children: _jsx(FieldRow, { name: f.field, schema: {
236
- ...propSchema,
237
- ...(f.label ? { title: f.label } : {}),
238
- ...(f.helpText ? { description: f.helpText } : {}),
239
- ...(f.placeholder ? { placeholder: f.placeholder } : {}),
240
- }, value: value[f.field], required: f.required ?? required.includes(f.field), issues: issuesByPath[f.field], readOnly: readOnly || f.readonly || (f.immutable && !createMode), fieldSpec: f, widgetContext: widgetContext, formData: value, onChange: (val) => onChange(f.field, val) }) }, f.field));
241
- }) })] }, idx));
436
+ const fieldsGrid = (_jsx("div", { className: "grid gap-4", style: {
437
+ gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
438
+ }, children: fields.map((f) => {
439
+ const propSchema = props[f.field];
440
+ if (!propSchema) {
441
+ return (_jsx("div", { className: "rounded border border-dashed border-amber-500/40 bg-amber-500/5 p-2 text-xs text-amber-700 dark:text-amber-300", style: { gridColumn: `span ${f.colSpan ?? 1}` }, children: tFormat('engine.form.missingField', detectLocale(), { field: f.field }) }, f.field));
442
+ }
443
+ return (_jsx("div", { style: { gridColumn: `span ${f.colSpan ?? 1}` }, children: _jsx(FieldRow, { name: f.field, schema: {
444
+ ...propSchema,
445
+ ...(f.label ? { title: f.label } : {}),
446
+ ...(f.helpText ? { description: f.helpText } : {}),
447
+ ...(f.placeholder ? { placeholder: f.placeholder } : {}),
448
+ }, value: value[f.field], required: f.required ?? required.includes(f.field), issues: issuesByPath[f.field], readOnly: readOnly || f.readonly || (f.immutable && !createMode), fieldSpec: f, widgetContext: widgetContext, formData: value, onChange: (val) => onChange(f.field, val) }) }, f.field));
449
+ }) }));
450
+ // Collapsible section (FormSectionSpec.collapsible) — the spec marks
451
+ // rarely-used groups (Advanced, type-specific options) collapsible and
452
+ // often `collapsed: true`. Honour both so the panel opens lean and the
453
+ // author expands only what they need. Non-collapsible sections render
454
+ // as a plain bordered block (unchanged).
455
+ if (s.collapsible && s.label) {
456
+ return (_jsxs(Collapsible, { defaultOpen: !s.collapsed, className: "rounded-md border border-border/40 bg-card/30", children: [_jsxs(CollapsibleTrigger, { className: "group flex w-full items-center justify-between gap-2 p-4 text-left", children: [_jsxs("span", { children: [_jsx("span", { className: "block text-sm font-semibold text-foreground/90", children: s.label }), s.description && (_jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: s.description }))] }), _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 text-muted-foreground transition-transform group-data-[state=closed]:-rotate-90" })] }), _jsx(CollapsibleContent, { className: "space-y-3 px-4 pb-4", children: fieldsGrid })] }, idx));
457
+ }
458
+ return (_jsxs("section", { className: "space-y-3 rounded-md border border-border/40 bg-card/30 p-4", children: [s.label && (_jsxs("header", { children: [_jsx("h3", { className: "text-sm font-semibold text-foreground/90", children: s.label }), s.description && (_jsx("p", { className: "text-xs text-muted-foreground", children: s.description }))] })), fieldsGrid] }, idx));
242
459
  };
243
460
  if (isTabbed) {
244
461
  const tabSections = sections.filter((s) => s.fields
@@ -256,11 +473,38 @@ function SectionedSchemaForm({ form, props, required, hiddenFields, issuesByPath
256
473
  }
257
474
  /* ----- inner field row ---------------------------------------------------- */
258
475
  function FieldRow({ name, schema, value, required, issues, readOnly, fieldSpec, widgetContext, formData, onChange, }) {
259
- const label = schema?.title || prettify(name);
260
- const description = schema?.description;
476
+ const label = fieldSpec?.label || schema?.title || prettify(name);
477
+ const description = fieldSpec?.helpText || schema?.description;
261
478
  const id = `mdf-${name}`;
262
479
  // Auto-infer widget from fieldSpec.type or schema
263
- const widget = inferWidget(fieldSpec, schema);
480
+ let widget = inferWidget(fieldSpec, schema);
481
+ // Field-reference props become object-field pickers when a field catalog
482
+ // is available and the spec didn't pin an explicit widget.
483
+ if (!fieldSpec?.widget) {
484
+ const refWidget = detectFieldRefWidget(name, schema, widgetContext);
485
+ if (refWidget)
486
+ widget = refWidget;
487
+ else {
488
+ const secretWidget = detectSecretWidget(name, schema);
489
+ if (secretWidget)
490
+ widget = secretWidget;
491
+ else {
492
+ const iconWidget = detectIconWidget(name, schema);
493
+ if (iconWidget)
494
+ widget = iconWidget;
495
+ else {
496
+ const colorWidget = detectColorWidget(name, schema);
497
+ if (colorWidget)
498
+ widget = colorWidget;
499
+ else {
500
+ const condWidget = detectConditionWidget(name, schema);
501
+ if (condWidget)
502
+ widget = condWidget;
503
+ }
504
+ }
505
+ }
506
+ }
507
+ }
264
508
  // Booleans with a schema default are never *missing* — don't show the
265
509
  // required asterisk (which would otherwise lie about user obligation).
266
510
  const isBoolean = schema?.type === 'boolean' || widget === 'switch';
@@ -278,6 +522,7 @@ function FieldRow({ name, schema, value, required, issues, readOnly, fieldSpec,
278
522
  return (_jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "flex items-center justify-between gap-2", children: _jsxs(Label, { htmlFor: id, className: "text-sm font-medium", children: [label, showRequiredStar && _jsx("span", { className: "text-destructive ml-0.5", children: "*" }), !labelMatchesName && (_jsx("code", { className: "ml-2 text-[10px] font-mono text-muted-foreground/70", title: "Machine name", children: name }))] }) }), _jsx(FieldControl, { id: id, schema: schema, value: value, onChange: onChange, readOnly: readOnly, widget: widget, fieldSpec: fieldSpec, widgetContext: widgetContext, formData: formData }), description && (_jsx("div", { className: "text-xs text-muted-foreground", children: description })), issues?.map((m, i) => (_jsx("div", { className: "text-xs text-destructive", children: m }, i)))] }));
279
523
  }
280
524
  function FieldControl({ id, schema, value, onChange, readOnly, widget, fieldSpec, widgetContext, formData, }) {
525
+ const locale = detectLocale();
281
526
  // Composite/repeater are first-class structured types — render natively
282
527
  // with recursive FieldRow calls so all UI features (widgets, options,
283
528
  // visibility, readonly) work uniformly at every nesting level.
@@ -288,7 +533,7 @@ function FieldControl({ id, schema, value, onChange, readOnly, widget, fieldSpec
288
533
  const fields = fieldSpec.fields?.length
289
534
  ? fieldSpec.fields
290
535
  : derivePropertyNames(schema);
291
- return (_jsx(CompositeField, { value: value, fields: fields, schema: schema, readOnly: readOnly, widgetContext: widgetContext, onChange: onChange }));
536
+ return (_jsx(CompositeField, { value: value, fields: fields, schema: schema, readOnly: readOnly, widgetContext: widgetContext, fieldSpec: fieldSpec, onChange: onChange }));
292
537
  }
293
538
  if (fieldSpec?.type === 'repeater') {
294
539
  const itemSchema = schema?.items ?? {};
@@ -297,6 +542,15 @@ function FieldControl({ id, schema, value, onChange, readOnly, widget, fieldSpec
297
542
  : derivePropertyNames(itemSchema);
298
543
  return (_jsx(RepeaterField, { value: value, fields: fields, schema: schema, readOnly: readOnly, widgetContext: widgetContext, widget: fieldSpec.widget, onChange: onChange }));
299
544
  }
545
+ if (fieldSpec?.type === 'record') {
546
+ // Record<string, item> — name-keyed map. Insertion order is display
547
+ // order. JSON Schema shape: { type:'object', additionalProperties: itemSchema }.
548
+ const itemSchema = schema?.additionalProperties ?? {};
549
+ const fields = fieldSpec.fields?.length
550
+ ? fieldSpec.fields
551
+ : derivePropertyNames(itemSchema);
552
+ return (_jsx(RecordField, { value: value, fields: fields, schema: schema, readOnly: readOnly, widgetContext: widgetContext, widget: fieldSpec.widget, keyField: fieldSpec.keyField, formData: formData, onChange: onChange }));
553
+ }
300
554
  // Widget hint takes precedence: try the registry first, then the
301
555
  // passthrough hint list, then fall back to JSON with an inline hint.
302
556
  if (widget) {
@@ -304,20 +558,57 @@ function FieldControl({ id, schema, value, onChange, readOnly, widget, fieldSpec
304
558
  if (Renderer) {
305
559
  return (_jsx(Renderer, { id: id, schema: schema, value: value, onChange: onChange, readOnly: readOnly, context: widgetContext, fieldSpec: fieldSpec, formData: formData }));
306
560
  }
561
+ // Resolve discriminated unions (oneOf / anyOf) against the current
562
+ // value so the recursive renderer below works on a concrete branch.
563
+ // Without this, every union field (View `data`, `columns`, `sort`,
564
+ // ...) falls through to a raw JSON editor.
565
+ const effective = resolveUnionBranch(schema, value);
566
+ // Nested object schema with a `properties` map: recurse into a
567
+ // nested SchemaForm so the user gets real labelled inputs instead
568
+ // of raw JSON. Covers the auto-inferred `object-fields` widget that
569
+ // SchemaForm picks for every `type: 'object'` schema, and any custom
570
+ // widget name that wasn't registered but still describes structured
571
+ // data.
572
+ if (effective?.type === 'object' &&
573
+ effective.properties &&
574
+ typeof effective.properties === 'object') {
575
+ return (_jsx("div", { className: "rounded-md border border-border/40 bg-card/30 p-3", children: _jsx(SchemaForm, { schema: effective, value: value ?? {}, onChange: (v) => onChange(v), readOnly: readOnly, widgetContext: widgetContext }) }));
576
+ }
577
+ // Array-of-object schemas: route through RepeaterField so the user
578
+ // gets per-row inputs rather than a JSON blob. Derive sub-field
579
+ // names from items.properties (after union resolution).
580
+ if (effective?.type === 'array' &&
581
+ effective.items &&
582
+ typeof effective.items === 'object') {
583
+ const itemSchema = resolveUnionBranch(effective.items, Array.isArray(value) && value.length ? value[0] : undefined);
584
+ if (itemSchema?.type === 'object' &&
585
+ itemSchema.properties &&
586
+ typeof itemSchema.properties === 'object') {
587
+ return (_jsx(RepeaterField, { value: value, fields: derivePropertyNames(itemSchema), schema: { ...effective, items: itemSchema }, readOnly: readOnly, widgetContext: widgetContext, widget: fieldSpec?.widget, onChange: onChange }));
588
+ }
589
+ }
307
590
  if (!KNOWN_PASSTHROUGH_WIDGETS.has(widget)) {
308
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(RawJsonEditor, { value: value, onChange: (v) => onChange(v), readOnly: readOnly }), _jsxs("div", { className: "text-[10px] text-muted-foreground", children: ["widget ", _jsx("code", { className: "font-mono", children: widget }), " \u2014 falling back to JSON until a custom renderer is registered."] })] }));
591
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(RawJsonEditor, { value: value, onChange: (v) => onChange(v), readOnly: readOnly }), _jsx("div", { className: "text-[10px] text-muted-foreground", children: tFormat('engine.form.fallbackJson', locale, { widget }) })] }));
309
592
  }
310
593
  }
594
+ // For schemas authored as `anyOf` / `oneOf` (e.g. `width: anyOf[string,
595
+ // number]`, `sort: anyOf[string, array<obj>]`), the outer schema's
596
+ // `type` / `enum` are undefined and every primitive branch below would
597
+ // miss, dropping us straight to RawJsonEditor. Resolve the union against
598
+ // the current value once and use the resolved branch for type/enum
599
+ // checks so primitive unions render as real inputs.
600
+ const effective = resolveUnionBranch(schema, value) ?? schema;
601
+ const effectiveType = effective?.type;
311
602
  // Enum / Select — fieldSpec.options takes precedence over schema.enum.
312
603
  const options = fieldSpec?.options;
313
- const enumValues = schema?.enum ?? undefined;
604
+ const enumValues = effective?.enum ?? undefined;
314
605
  if (Array.isArray(options) && options.length > 0) {
315
606
  // Render from fieldSpec.options (Data.SelectOption[])
316
- return (_jsxs(Select, { value: value == null ? '' : String(value), onValueChange: (v) => onChange(v), disabled: readOnly, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: "Select\u2026" }) }), _jsx(SelectContent, { children: options.map((opt) => (_jsxs(SelectItem, { value: opt.value, children: [opt.label, opt.color && (_jsx("span", { className: "ml-2 inline-block h-3 w-3 rounded", style: { backgroundColor: opt.color } }))] }, opt.value))) })] }));
607
+ return (_jsxs(Select, { value: value == null ? '' : String(value), onValueChange: (v) => onChange(v), disabled: readOnly, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: t('engine.form.selectEllipsis', locale) }) }), _jsx(SelectContent, { children: options.map((opt) => (_jsxs(SelectItem, { value: opt.value, children: [opt.label, opt.color && (_jsx("span", { className: "ml-2 inline-block h-3 w-3 rounded", style: { backgroundColor: opt.color } }))] }, opt.value))) })] }));
317
608
  }
318
609
  if (Array.isArray(enumValues) && enumValues.length > 0) {
319
610
  // Fallback to schema.enum
320
- return (_jsxs(Select, { value: value == null ? '' : String(value), onValueChange: (v) => onChange(v), disabled: readOnly, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: "Select\u2026" }) }), _jsx(SelectContent, { children: enumValues.map((opt) => (_jsx(SelectItem, { value: String(opt), children: String(opt) }, String(opt)))) })] }));
611
+ return (_jsxs(Select, { value: value == null ? '' : String(value), onValueChange: (v) => onChange(v), disabled: readOnly, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: t('engine.form.selectEllipsis', locale) }) }), _jsx(SelectContent, { children: enumValues.map((opt) => (_jsx(SelectItem, { value: String(opt), children: String(opt) }, String(opt)))) })] }));
321
612
  }
322
613
  // Boolean → Switch (no redundant "true/false" text; the toggle state
323
614
  // already conveys the value).
@@ -330,26 +621,26 @@ function FieldControl({ id, schema, value, onChange, readOnly, widget, fieldSpec
330
621
  // the sub-field as boolean. Without this, capability toggles inside the
331
622
  // Object editor's "Capabilities" section fell through to RawJsonEditor
332
623
  // and rendered as empty textareas.
333
- if (schema?.type === 'boolean' || widget === 'switch' || fieldSpec?.type === 'boolean' || fieldSpec?.type === 'toggle') {
624
+ if (effectiveType === 'boolean' || widget === 'switch' || fieldSpec?.type === 'boolean' || fieldSpec?.type === 'toggle') {
334
625
  return (_jsx(Switch, { id: id, checked: !!value, onCheckedChange: (c) => onChange(c), disabled: readOnly }));
335
626
  }
336
627
  // Number / integer → numeric input with min/max from fieldSpec.
337
- if (schema?.type === 'number' || schema?.type === 'integer') {
628
+ if (effectiveType === 'number' || effectiveType === 'integer') {
338
629
  const min = fieldSpec?.min;
339
630
  const max = fieldSpec?.max;
340
631
  return (_jsx(Input, { id: id, type: "number", value: value == null ? '' : String(value), min: min, max: max, onChange: (e) => {
341
632
  const raw = e.target.value;
342
633
  if (raw === '')
343
634
  return onChange(undefined);
344
- const n = schema.type === 'integer' ? parseInt(raw, 10) : Number(raw);
635
+ const n = effectiveType === 'integer' ? parseInt(raw, 10) : Number(raw);
345
636
  onChange(Number.isFinite(n) ? n : undefined);
346
637
  }, readOnly: readOnly }));
347
638
  }
348
639
  // String → Input (or Textarea if it looks long), with maxLength from fieldSpec.
349
- if (schema?.type === 'string') {
640
+ if (effectiveType === 'string') {
350
641
  const maxLength = fieldSpec?.maxLength;
351
- const long = schema?.format === 'multiline' ||
352
- schema?.contentMediaType === 'text/markdown' ||
642
+ const long = effective?.format === 'multiline' ||
643
+ effective?.contentMediaType === 'text/markdown' ||
353
644
  (typeof value === 'string' && value.length > 80);
354
645
  if (long) {
355
646
  return (_jsx(Textarea, { id: id, rows: 4, value: value ?? '', maxLength: maxLength, onChange: (e) => onChange(e.target.value || undefined), readOnly: readOnly }));
@@ -357,14 +648,14 @@ function FieldControl({ id, schema, value, onChange, readOnly, widget, fieldSpec
357
648
  return (_jsx(Input, { id: id, value: value ?? '', maxLength: maxLength, onChange: (e) => onChange(e.target.value || undefined), readOnly: readOnly }));
358
649
  }
359
650
  // Array of primitives → comma-separated tag editor (MVP).
360
- if (schema?.type === 'array') {
361
- const itemsSchema = schema?.items ?? {};
651
+ if (effectiveType === 'array') {
652
+ const itemsSchema = effective?.items ?? {};
362
653
  const isPrimitive = itemsSchema.type === 'string' ||
363
654
  itemsSchema.type === 'number' ||
364
655
  itemsSchema.type === 'integer';
365
656
  if (isPrimitive) {
366
657
  const arr = Array.isArray(value) ? value : [];
367
- return (_jsx(Input, { id: id, value: arr.map(String).join(', '), placeholder: "comma, separated, values", onChange: (e) => {
658
+ return (_jsx(Input, { id: id, value: arr.map(String).join(', '), placeholder: t('engine.form.arrayPlaceholder', locale), onChange: (e) => {
368
659
  const raw = e.target.value;
369
660
  const parts = raw
370
661
  .split(',')
@@ -379,6 +670,30 @@ function FieldControl({ id, schema, value, onChange, readOnly, widget, fieldSpec
379
670
  }, readOnly: readOnly }));
380
671
  }
381
672
  }
673
+ // Structured fallback — try to recurse into a real nested form before
674
+ // dropping to a raw JSON editor. Order matters:
675
+ // 1. Resolve oneOf / anyOf against the value (discriminated unions
676
+ // like View `data.provider` or anyOf [array<string>, array<obj>]).
677
+ // 2. type:'object' with properties → nested SchemaForm.
678
+ // 3. type:'array' of objects → RepeaterField with per-row inputs.
679
+ // 4. Last resort → JSON editor.
680
+ {
681
+ if (effective?.type === 'object' &&
682
+ effective.properties &&
683
+ typeof effective.properties === 'object') {
684
+ return (_jsx("div", { className: "rounded-md border border-border/40 bg-card/30 p-3", children: _jsx(SchemaForm, { schema: effective, value: value ?? {}, onChange: (v) => onChange(v), readOnly: readOnly, widgetContext: widgetContext }) }));
685
+ }
686
+ if (effective?.type === 'array' &&
687
+ effective.items &&
688
+ typeof effective.items === 'object') {
689
+ const itemSchema = resolveUnionBranch(effective.items, Array.isArray(value) && value.length ? value[0] : undefined);
690
+ if (itemSchema?.type === 'object' &&
691
+ itemSchema.properties &&
692
+ typeof itemSchema.properties === 'object') {
693
+ return (_jsx(RepeaterField, { value: value, fields: derivePropertyNames(itemSchema), schema: { ...effective, items: itemSchema }, readOnly: readOnly, widgetContext: widgetContext, widget: fieldSpec?.widget, onChange: onChange }));
694
+ }
695
+ }
696
+ }
382
697
  // Object / complex → JSON fallback so admins can still edit.
383
698
  return _jsx(RawJsonEditor, { value: value, onChange: onChange, readOnly: readOnly, small: true });
384
699
  }
@@ -391,20 +706,57 @@ function FieldControl({ id, schema, value, onChange, readOnly, widget, fieldSpec
391
706
  function pickSubSchema(parent, kind, subName) {
392
707
  if (!parent)
393
708
  return {};
394
- const props = kind === 'composite'
395
- ? parent.properties
396
- : parent.items?.properties;
709
+ let props;
710
+ if (kind === 'composite') {
711
+ props = parent.properties;
712
+ }
713
+ else if (kind === 'repeater') {
714
+ props = parent.items?.properties;
715
+ }
716
+ else {
717
+ // record: items live under additionalProperties.properties
718
+ props = parent.additionalProperties?.properties;
719
+ }
397
720
  return props?.[subName] ?? {};
398
721
  }
399
- function CompositeField({ value, fields, schema, readOnly, widgetContext, onChange, }) {
722
+ // Compact, human-readable summary of a composite value for the popover
723
+ // disclosure trigger — booleans show their field label when true, arrays show
724
+ // their entries, scalars show the value. Keeps the collapsed row informative.
725
+ function summariseComposite(obj, specs) {
726
+ const parts = [];
727
+ for (const spec of specs) {
728
+ const v = obj[spec.field];
729
+ if (v == null || v === '' || v === false)
730
+ continue;
731
+ const label = spec.label || spec.field;
732
+ if (v === true)
733
+ parts.push(label);
734
+ else if (Array.isArray(v)) {
735
+ if (v.length)
736
+ parts.push(v.map((x) => (typeof x === 'object' && x ? (x.field ?? x.name ?? '') : String(x))).filter(Boolean).join(', '));
737
+ }
738
+ else if (typeof v === 'object')
739
+ parts.push(label);
740
+ else
741
+ parts.push(String(v));
742
+ }
743
+ return parts.join(' · ');
744
+ }
745
+ function CompositeField({ value, fields, schema, readOnly, widgetContext, fieldSpec, onChange, }) {
400
746
  const obj = (value && typeof value === 'object' && !Array.isArray(value))
401
747
  ? value
402
748
  : {};
403
749
  const specs = fields.map(normaliseField);
404
- return (_jsx("div", { className: "rounded-md border border-border/50 bg-muted/20 p-3 space-y-3", children: specs.map((spec) => {
405
- const subSchema = pickSubSchema(schema, 'composite', spec.field);
406
- return (_jsx(FieldRow, { name: spec.field, schema: subSchema, value: obj[spec.field], required: Boolean(spec.required), readOnly: readOnly || spec.readonly, fieldSpec: spec, widgetContext: widgetContext, formData: obj, onChange: (v) => onChange({ ...obj, [spec.field]: v }) }, spec.field));
407
- }) }));
750
+ const rows = specs.map((spec) => {
751
+ const subSchema = pickSubSchema(schema, 'composite', spec.field);
752
+ return (_jsx(FieldRow, { name: spec.field, schema: subSchema, value: obj[spec.field], required: Boolean(spec.required), readOnly: readOnly || spec.readonly, fieldSpec: spec, widgetContext: widgetContext, formData: obj, onChange: (v) => onChange({ ...obj, [spec.field]: v }) }, spec.field));
753
+ });
754
+ // Progressive disclosure (Airtable parity): summary line + gear → popover.
755
+ if (fieldSpec?.disclosure === 'popover') {
756
+ const summary = summariseComposite(obj, specs);
757
+ return (_jsxs("div", { className: "flex items-center justify-between gap-2 rounded-md border border-border/50 bg-muted/20 px-3 py-1.5", children: [_jsx("span", { className: "text-sm text-muted-foreground truncate", title: summary, children: summary || _jsx("span", { className: "italic opacity-70", children: "Not configured" }) }), _jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "sm", className: "h-7 w-7 p-0 shrink-0", "aria-label": "Configure", "data-testid": `composite-popover-${fieldSpec.field}`, children: _jsx(Settings2, { className: "h-4 w-4" }) }) }), _jsx(PopoverContent, { align: "end", className: "w-80 space-y-3", children: rows })] })] }));
758
+ }
759
+ return (_jsx("div", { className: "rounded-md border border-border/50 bg-muted/20 p-3 space-y-3", children: rows }));
408
760
  }
409
761
  function RepeaterField({ value, fields, schema, readOnly, widgetContext, widget, onChange, }) {
410
762
  const rows = Array.isArray(value) ? value : [];
@@ -425,25 +777,183 @@ function RepeaterField({ value, fields, schema, readOnly, widgetContext, widget,
425
777
  setOpenIdx(rows.length);
426
778
  };
427
779
  if (useGrid) {
428
- return (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "overflow-x-auto rounded-md border border-border/50", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/40", children: _jsxs("tr", { children: [specs.map((s) => (_jsxs("th", { className: "px-2 py-1.5 text-left text-xs font-medium", children: [s.label || prettify(s.field), s.required && _jsx("span", { className: "text-destructive ml-0.5", children: "*" })] }, s.field))), !readOnly && _jsx("th", { className: "w-8" })] }) }), _jsxs("tbody", { children: [rows.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: specs.length + 1, className: "px-2 py-3 text-center text-xs text-muted-foreground", children: "No items. Click + to add." }) })), rows.map((row, idx) => (_jsxs("tr", { className: "border-t border-border/30 align-top", children: [specs.map((s) => {
780
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "overflow-x-auto rounded-md border border-border/50", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/40", children: _jsxs("tr", { children: [specs.map((s) => (_jsxs("th", { className: "px-2 py-1.5 text-left text-xs font-medium", children: [s.label || prettify(s.field), s.required && _jsx("span", { className: "text-destructive ml-0.5", children: "*" })] }, s.field))), !readOnly && _jsx("th", { className: "w-8" })] }) }), _jsxs("tbody", { children: [rows.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: specs.length + 1, className: "px-2 py-3 text-center text-xs text-muted-foreground", children: t('engine.repeater.empty', detectLocale()) }) })), rows.map((row, idx) => (_jsxs("tr", { className: "border-t border-border/30 align-top", children: [specs.map((s) => {
429
781
  const sub = pickSubSchema(schema, 'repeater', s.field);
430
782
  return (_jsx("td", { className: "p-1.5", children: _jsx(FieldControl, { id: `rep-${idx}-${s.field}`, schema: sub, value: row?.[s.field], readOnly: readOnly || s.readonly, widget: inferWidget(s, sub), fieldSpec: s, widgetContext: widgetContext, formData: row, onChange: (v) => update(idx, { [s.field]: v }) }) }, s.field));
431
- }), !readOnly && (_jsx("td", { className: "p-1.5 text-right", children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(idx), className: "h-7 w-7 p-0", "aria-label": "Remove", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }) }))] }, idx)))] })] }) }), !readOnly && (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: add, children: [_jsx(Plus, { className: "h-3.5 w-3.5 mr-1" }), " Add"] }))] }));
783
+ }), !readOnly && (_jsx("td", { className: "p-1.5 text-right", children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(idx), className: "h-7 w-7 p-0", "aria-label": t('engine.form.remove', detectLocale()), children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }) }))] }, idx)))] })] }) }), !readOnly && (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: add, children: [_jsx(Plus, { className: "h-3.5 w-3.5 mr-1" }), " ", t('engine.form.add', detectLocale())] }))] }));
432
784
  }
433
785
  // Card layout — one collapsible fieldset per row.
434
- return (_jsxs("div", { className: "space-y-2", children: [rows.length === 0 && (_jsx("div", { className: "rounded-md border border-dashed border-border/50 px-3 py-4 text-center text-xs text-muted-foreground", children: "No items yet." })), rows.map((row, idx) => {
786
+ return (_jsxs("div", { className: "space-y-2", children: [rows.length === 0 && (_jsx("div", { className: "rounded-md border border-dashed border-border/50 px-3 py-4 text-center text-xs text-muted-foreground", children: t('engine.list.empty', detectLocale()) })), rows.map((row, idx) => {
435
787
  const isOpen = openIdx === idx;
436
788
  const summary = specs
437
789
  .map((s) => row?.[s.field])
438
790
  .find((v) => v != null && v !== '');
439
- return (_jsxs("div", { className: "rounded-md border border-border/50 bg-muted/10", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-2 py-1.5 border-b border-border/30", children: [_jsxs("button", { type: "button", onClick: () => setOpenIdx(isOpen ? null : idx), className: "flex items-center gap-1.5 text-sm font-medium text-left flex-1 min-w-0", children: [isOpen ? _jsx(ChevronDown, { className: "h-3.5 w-3.5" }) : _jsx(ChevronRight, { className: "h-3.5 w-3.5" }), _jsxs("span", { className: "truncate", children: ["#", idx + 1, summary != null ? ` — ${String(summary)}` : ''] })] }), !readOnly && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(idx), className: "h-7 w-7 p-0", "aria-label": "Remove", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }), isOpen && (_jsx("div", { className: "p-3 space-y-3", children: specs.map((s) => {
791
+ return (_jsxs("div", { className: "rounded-md border border-border/50 bg-muted/10", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-2 py-1.5 border-b border-border/30", children: [_jsxs("button", { type: "button", onClick: () => setOpenIdx(isOpen ? null : idx), className: "flex items-center gap-1.5 text-sm font-medium text-left flex-1 min-w-0", children: [isOpen ? _jsx(ChevronDown, { className: "h-3.5 w-3.5" }) : _jsx(ChevronRight, { className: "h-3.5 w-3.5" }), _jsxs("span", { className: "truncate", children: ["#", idx + 1, summary != null ? ` — ${String(summary)}` : ''] })] }), !readOnly && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(idx), className: "h-7 w-7 p-0", "aria-label": t('engine.form.remove', detectLocale()), children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }), isOpen && (_jsx("div", { className: "p-3 space-y-3", children: specs.map((s) => {
440
792
  const sub = pickSubSchema(schema, 'repeater', s.field);
441
793
  return (_jsx(FieldRow, { name: s.field, schema: sub, value: row?.[s.field], required: Boolean(s.required), readOnly: readOnly || s.readonly, fieldSpec: s, widgetContext: widgetContext, formData: row, onChange: (v) => update(idx, { [s.field]: v }) }, s.field));
442
794
  }) }))] }, idx));
443
- }), !readOnly && (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: add, children: [_jsx(Plus, { className: "h-3.5 w-3.5 mr-1" }), " Add item"] }))] }));
795
+ }), !readOnly && (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: add, children: [_jsx(Plus, { className: "h-3.5 w-3.5 mr-1" }), " ", t('engine.form.addItem', detectLocale())] }))] }));
796
+ }
797
+ /* ----- RecordField — Record<string, item> editor -------------------------- */
798
+ /**
799
+ * Editor for `type: 'record'` form fields. The value is a name-keyed map
800
+ * (`Record<string, item>`) where insertion order is display order.
801
+ *
802
+ * Layout:
803
+ * - If `widget` matches a renderer in WIDGETS, delegate to it (e.g.
804
+ * `widget: 'airtable'` → AirtableTableWidget). The widget receives the
805
+ * Record value directly and is responsible for emitting a Record back.
806
+ * - Otherwise, fall back to an inline card list with a key column +
807
+ * per-row sub-fields, similar to RepeaterField's card layout.
808
+ *
809
+ * The key is mirrored into the item as a property (default name: 'name')
810
+ * so downstream consumers can treat each item as self-describing.
811
+ *
812
+ * See ADR-0007.
813
+ */
814
+ function RecordField({ value, fields, schema, readOnly, widgetContext, widget, keyField, formData, onChange, }) {
815
+ const locale = detectLocale();
816
+ // Delegate to a registered widget if the form spec asked for one
817
+ // explicitly (e.g. `widget: 'airtable'`). The widget owns the entire UI.
818
+ if (widget) {
819
+ const Renderer = WIDGETS[widget];
820
+ if (Renderer) {
821
+ return (_jsx(Renderer, { schema: schema, value: value, onChange: onChange, readOnly: readOnly, context: widgetContext, fieldSpec: { field: '', type: 'record', fields, widget }, formData: formData }));
822
+ }
823
+ }
824
+ // Inline fallback — card list with a key column + sub-fields.
825
+ const keyProp = keyField?.field ?? 'name';
826
+ const keyLabel = keyField?.label ?? prettify(keyProp);
827
+ const keyRegex = keyField?.regex ? new RegExp(keyField.regex) : null;
828
+ const keyImmutable = keyField?.immutable !== false; // default true
829
+ const record = value && typeof value === 'object' && !Array.isArray(value)
830
+ ? value
831
+ : {};
832
+ const entries = Object.entries(record);
833
+ const specs = fields.map(normaliseField).filter((s) => s.field !== keyProp);
834
+ const [openKey, setOpenKey] = React.useState(null);
835
+ const [pendingKey, setPendingKey] = React.useState('');
836
+ const [keyError, setKeyError] = React.useState(null);
837
+ const emit = (next) => onChange(next);
838
+ const updateItem = (key, patch) => {
839
+ const next = {};
840
+ for (const [k, v] of entries) {
841
+ next[k] = k === key ? { ...v, ...patch } : v;
842
+ }
843
+ emit(next);
844
+ };
845
+ const removeItem = (key) => {
846
+ const next = {};
847
+ for (const [k, v] of entries) {
848
+ if (k !== key)
849
+ next[k] = v;
850
+ }
851
+ emit(next);
852
+ };
853
+ const renameItem = (oldKey, newKey) => {
854
+ if (newKey === oldKey)
855
+ return;
856
+ if (record[newKey]) {
857
+ setKeyError(tFormat('engine.form.keyExists', locale, { key: newKey }));
858
+ return;
859
+ }
860
+ if (keyRegex && !keyRegex.test(newKey)) {
861
+ setKeyError(tFormat('engine.form.keyPattern', locale, { pattern: String(keyRegex) }));
862
+ return;
863
+ }
864
+ const next = {};
865
+ for (const [k, v] of entries) {
866
+ if (k === oldKey) {
867
+ next[newKey] = { ...v, [keyProp]: newKey };
868
+ }
869
+ else {
870
+ next[k] = v;
871
+ }
872
+ }
873
+ setKeyError(null);
874
+ emit(next);
875
+ };
876
+ const addItem = () => {
877
+ const trimmed = pendingKey.trim();
878
+ if (!trimmed) {
879
+ setKeyError(t('engine.form.keyRequired', locale));
880
+ return;
881
+ }
882
+ if (record[trimmed]) {
883
+ setKeyError(tFormat('engine.form.keyExists', locale, { key: trimmed }));
884
+ return;
885
+ }
886
+ if (keyRegex && !keyRegex.test(trimmed)) {
887
+ setKeyError(tFormat('engine.form.keyPattern', locale, { pattern: String(keyRegex) }));
888
+ return;
889
+ }
890
+ const blank = { [keyProp]: trimmed };
891
+ specs.forEach((s) => { blank[s.field] = undefined; });
892
+ emit({ ...record, [trimmed]: blank });
893
+ setPendingKey('');
894
+ setKeyError(null);
895
+ setOpenKey(trimmed);
896
+ };
897
+ // Drag-to-reorder. We rebuild the Record with the new key order, since
898
+ // insertion order = display order for `type: 'record'`.
899
+ const [dragKey, setDragKey] = React.useState(null);
900
+ const [dropTarget, setDropTarget] = React.useState(null);
901
+ const reorder = (sourceKey, targetKey) => {
902
+ if (sourceKey === targetKey)
903
+ return;
904
+ const keys = entries.map(([k]) => k);
905
+ const from = keys.indexOf(sourceKey);
906
+ const to = keys.indexOf(targetKey);
907
+ if (from < 0 || to < 0)
908
+ return;
909
+ keys.splice(from, 1);
910
+ keys.splice(to, 0, sourceKey);
911
+ const next = {};
912
+ for (const k of keys)
913
+ next[k] = record[k];
914
+ emit(next);
915
+ };
916
+ return (_jsxs("div", { className: "space-y-2", children: [entries.length === 0 && (_jsx("div", { className: "rounded-md border border-dashed border-border/50 px-3 py-4 text-center text-xs text-muted-foreground", children: t('engine.list.empty', locale) })), entries.map(([key, row]) => {
917
+ const isOpen = openKey === key;
918
+ const summary = specs
919
+ .map((s) => row?.[s.field])
920
+ .find((v) => v != null && v !== '');
921
+ const isDropTarget = dropTarget === key && dragKey && dragKey !== key;
922
+ return (_jsxs("div", { className: `rounded-md border bg-muted/10 ${isDropTarget ? 'border-primary border-2' : 'border-border/50'}`, onDragOver: (e) => {
923
+ if (!dragKey || readOnly)
924
+ return;
925
+ e.preventDefault();
926
+ if (dropTarget !== key)
927
+ setDropTarget(key);
928
+ }, onDragLeave: () => {
929
+ if (dropTarget === key)
930
+ setDropTarget(null);
931
+ }, onDrop: (e) => {
932
+ if (!dragKey || readOnly)
933
+ return;
934
+ e.preventDefault();
935
+ reorder(dragKey, key);
936
+ setDragKey(null);
937
+ setDropTarget(null);
938
+ }, children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-2 py-1.5 border-b border-border/30", children: [!readOnly && (_jsx("span", { draggable: true, onDragStart: (e) => {
939
+ setDragKey(key);
940
+ e.dataTransfer.effectAllowed = 'move';
941
+ e.dataTransfer.setData('text/plain', key);
942
+ }, onDragEnd: () => { setDragKey(null); setDropTarget(null); }, className: "cursor-grab active:cursor-grabbing text-muted-foreground hover:text-foreground", "aria-label": t('engine.form.dragToReorder', locale), title: t('engine.form.dragToReorder', locale), children: _jsx(GripVertical, { className: "h-3.5 w-3.5" }) })), _jsxs("button", { type: "button", onClick: () => setOpenKey(isOpen ? null : key), className: "flex items-center gap-1.5 text-sm font-medium text-left flex-1 min-w-0", children: [isOpen ? _jsx(ChevronDown, { className: "h-3.5 w-3.5" }) : _jsx(ChevronRight, { className: "h-3.5 w-3.5" }), _jsx("span", { className: "font-mono text-xs px-1.5 py-0.5 rounded bg-muted/60", children: key }), summary != null && _jsxs("span", { className: "truncate text-muted-foreground", children: ["\u2014 ", String(summary)] })] }), !readOnly && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeItem(key), className: "h-7 w-7 p-0", "aria-label": t('engine.form.remove', locale), children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }), isOpen && (_jsxs("div", { className: "p-3 space-y-3", children: [_jsx(FieldRow, { name: keyProp, schema: { type: 'string' }, value: key, required: true, readOnly: readOnly || keyImmutable, fieldSpec: { field: keyProp, type: 'text', label: keyLabel, helpText: keyField?.helpText }, widgetContext: widgetContext, formData: row, onChange: (v) => renameItem(key, String(v ?? '').trim()) }), specs.map((s) => {
943
+ if (s.visibleOn && !evaluatePredicate(s.visibleOn, { data: row }))
944
+ return null;
945
+ const sub = pickSubSchema(schema, 'record', s.field);
946
+ return (_jsx(FieldRow, { name: s.field, schema: sub, value: row?.[s.field], required: Boolean(s.required), readOnly: readOnly || s.readonly, fieldSpec: s, widgetContext: widgetContext, formData: row, onChange: (v) => updateItem(key, { [s.field]: v }) }, s.field));
947
+ })] }))] }, key));
948
+ }), !readOnly && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: pendingKey, onChange: (e) => { setPendingKey(e.target.value); if (keyError)
949
+ setKeyError(null); }, placeholder: keyField?.placeholder ?? keyLabel, className: "h-8 text-xs font-mono max-w-[220px]", onKeyDown: (e) => { if (e.key === 'Enter') {
950
+ e.preventDefault();
951
+ addItem();
952
+ } } }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addItem, children: [_jsx(Plus, { className: "h-3.5 w-3.5 mr-1" }), " ", t('engine.form.add', locale)] }), keyError && _jsx("span", { className: "text-xs text-destructive", children: keyError })] }))] }));
444
953
  }
445
954
  /* ----- raw JSON fallback -------------------------------------------------- */
446
955
  function RawJsonEditor({ value, onChange, readOnly, small, }) {
956
+ const locale = detectLocale();
447
957
  const [text, setText] = React.useState(() => safeStringify(value));
448
958
  const [error, setError] = React.useState(null);
449
959
  // Re-sync when external value changes (e.g. Reset Overlay).
@@ -465,7 +975,7 @@ function RawJsonEditor({ value, onChange, readOnly, small, }) {
465
975
  onChange(parsed);
466
976
  }
467
977
  catch (err) {
468
- setError(err?.message ?? 'Invalid JSON');
978
+ setError(err?.message ?? t('engine.form.invalidJson', locale));
469
979
  }
470
980
  } }), error && _jsx("div", { className: "text-xs text-destructive", children: error })] }));
471
981
  }