@object-ui/app-shell 6.2.3 → 7.1.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.
- package/CHANGELOG.md +1229 -0
- package/README.md +292 -0
- package/dist/assistant/assistantBus.d.ts +72 -0
- package/dist/assistant/assistantBus.js +133 -0
- package/dist/chrome/CommandPalette.d.ts +1 -1
- package/dist/chrome/CommandPalette.js +26 -22
- package/dist/chrome/ConditionalAuthWrapper.d.ts +1 -1
- package/dist/chrome/ConsoleToaster.d.ts +1 -1
- package/dist/chrome/ConsoleToaster.js +3 -1
- package/dist/chrome/ErrorBoundary.d.ts +1 -1
- package/dist/chrome/KeyboardShortcutsDialog.d.ts +1 -1
- package/dist/chrome/KeyboardShortcutsDialog.js +16 -5
- package/dist/chrome/LoadingScreen.d.ts +1 -1
- package/dist/chrome/LoadingScreen.js +22 -26
- package/dist/chrome/RouteFader.d.ts +1 -1
- package/dist/chrome/ThemeProvider.d.ts +1 -1
- package/dist/components/ManagedByBadge.d.ts +1 -1
- package/dist/console/AppContent.d.ts +1 -1
- package/dist/console/AppContent.js +184 -39
- package/dist/console/ConsoleShell.d.ts +7 -7
- package/dist/console/ConsoleShell.js +32 -3
- package/dist/console/ai/AiChatPage.d.ts +88 -1
- package/dist/console/ai/AiChatPage.js +747 -66
- package/dist/console/ai/ConversationsSidebar.d.ts +26 -1
- package/dist/console/ai/ConversationsSidebar.js +149 -34
- package/dist/console/ai/LiveCanvas.d.ts +28 -0
- package/dist/console/ai/LiveCanvas.js +80 -0
- package/dist/console/ai/reconcileTurn.d.ts +8 -0
- package/dist/console/ai/reconcileTurn.js +20 -0
- package/dist/console/auth/AuthPageLayout.d.ts +1 -1
- package/dist/console/auth/ForgotPasswordPage.d.ts +1 -1
- package/dist/console/auth/LoginPage.d.ts +1 -1
- package/dist/console/auth/RegisterPage.d.ts +1 -1
- package/dist/console/auth/RegisterPage.js +23 -3
- package/dist/console/cloud-connection/CloudConnectionPanel.d.ts +1 -0
- package/dist/console/cloud-connection/CloudConnectionPanel.js +169 -0
- package/dist/console/home/AppCard.d.ts +1 -1
- package/dist/console/home/AppCard.js +6 -12
- package/dist/console/home/HomeAppsStrip.d.ts +8 -0
- package/dist/console/home/HomeAppsStrip.js +61 -0
- package/dist/console/home/HomeLayout.d.ts +1 -1
- package/dist/console/home/HomeLayout.js +3 -1
- package/dist/console/home/HomePage.d.ts +1 -2
- package/dist/console/home/HomePage.js +149 -21
- package/dist/console/home/HomeRail.d.ts +22 -0
- package/dist/console/home/HomeRail.js +62 -0
- package/dist/console/home/QuickActions.d.ts +1 -1
- package/dist/console/home/QuickActions.js +3 -11
- package/dist/console/home/RecentApps.d.ts +1 -1
- package/dist/console/home/RecentApps.js +2 -2
- package/dist/console/home/StarredApps.d.ts +1 -1
- package/dist/console/home/StarredApps.js +2 -2
- package/dist/console/marketplace/InstalledListWidget.d.ts +1 -0
- package/dist/console/marketplace/InstalledListWidget.js +93 -0
- package/dist/console/marketplace/MarkdownText.d.ts +1 -1
- package/dist/console/marketplace/MarketplaceAccessDenied.d.ts +1 -1
- package/dist/console/marketplace/MarketplaceInstalledPage.d.ts +8 -14
- package/dist/console/marketplace/MarketplaceInstalledPage.js +14 -66
- package/dist/console/marketplace/MarketplacePackagePage.d.ts +1 -1
- package/dist/console/marketplace/MarketplacePackagePage.js +249 -8
- package/dist/console/marketplace/MarketplacePage.d.ts +1 -1
- package/dist/console/marketplace/MarketplacePage.js +60 -3
- package/dist/console/marketplace/PackageIcon.d.ts +1 -1
- package/dist/console/marketplace/PluginDisclosure.d.ts +14 -0
- package/dist/console/marketplace/PluginDisclosure.js +38 -0
- package/dist/console/marketplace/marketplaceApi.d.ts +123 -0
- package/dist/console/marketplace/marketplaceApi.js +254 -1
- package/dist/console/organizations/CreateWorkspaceDialog.d.ts +1 -1
- package/dist/console/organizations/OrganizationsLayout.d.ts +1 -1
- package/dist/console/organizations/OrganizationsPage.d.ts +1 -1
- package/dist/console/organizations/manage/AcceptInvitationPage.d.ts +1 -1
- package/dist/console/organizations/manage/InvitationsPage.d.ts +1 -1
- package/dist/console/organizations/manage/InviteMemberDialog.d.ts +1 -1
- package/dist/console/organizations/manage/MembersPage.d.ts +1 -1
- package/dist/console/organizations/manage/OrganizationLayout.d.ts +1 -1
- package/dist/console/organizations/manage/SettingsPage.d.ts +1 -1
- package/dist/context/CommandPaletteProvider.d.ts +44 -0
- package/dist/context/CommandPaletteProvider.js +71 -0
- package/dist/context/FavoritesProvider.d.ts +1 -1
- package/dist/context/NavigationContext.d.ts +1 -1
- package/dist/context/RecentItemsProvider.d.ts +2 -2
- package/dist/context/UserStateAdapters.d.ts +1 -1
- package/dist/context/index.d.ts +2 -0
- package/dist/context/index.js +1 -0
- package/dist/hooks/index.d.ts +5 -2
- package/dist/hooks/index.js +4 -1
- package/dist/hooks/useActionModal.d.ts +53 -0
- package/dist/hooks/useActionModal.js +111 -0
- package/dist/hooks/useChatConversation.d.ts +137 -4
- package/dist/hooks/useChatConversation.js +316 -25
- package/dist/hooks/useConsoleActionRuntime.d.ts +70 -0
- package/dist/hooks/useConsoleActionRuntime.js +564 -0
- package/dist/hooks/useConversationList.js +61 -3
- package/dist/hooks/useHomeInbox.d.ts +13 -0
- package/dist/hooks/useHomeInbox.js +142 -0
- package/dist/hooks/useNavPins.js +17 -23
- package/dist/hooks/useNavigationSync.d.ts +33 -0
- package/dist/hooks/useNavigationSync.js +98 -12
- package/dist/hooks/useReconcileOnError.d.ts +40 -0
- package/dist/hooks/useReconcileOnError.js +37 -0
- package/dist/hooks/useRecordApprovals.d.ts +18 -19
- package/dist/hooks/useRecordApprovals.js +24 -40
- package/dist/hooks/useResponsiveSidebar.js +14 -5
- package/dist/hooks/useSettleSignal.d.ts +19 -0
- package/dist/hooks/useSettleSignal.js +20 -0
- package/dist/hooks/useTrackRouteAsRecent.js +35 -0
- package/dist/hooks/useUrlOverlay.d.ts +62 -0
- package/dist/hooks/useUrlOverlay.js +88 -0
- package/dist/index.d.ts +18 -8
- package/dist/index.js +17 -5
- package/dist/layout/ActivityFeed.d.ts +1 -1
- package/dist/layout/AppHeader.d.ts +3 -2
- package/dist/layout/AppHeader.js +237 -72
- package/dist/layout/AppSidebar.d.ts +2 -1
- package/dist/layout/AppSidebar.js +26 -46
- package/dist/layout/AppSwitcher.d.ts +2 -1
- package/dist/layout/AppSwitcher.js +9 -5
- package/dist/layout/AuthPageLayout.d.ts +1 -1
- package/dist/layout/ConnectionStatus.d.ts +1 -1
- package/dist/layout/ConnectionStatus.js +9 -6
- package/dist/layout/ConsoleChatbotFab.d.ts +19 -1
- package/dist/layout/ConsoleChatbotFab.js +16 -2
- package/dist/layout/ConsoleFloatingChatbot.d.ts +34 -2
- package/dist/layout/ConsoleFloatingChatbot.js +391 -41
- package/dist/layout/ConsoleLayout.d.ts +1 -1
- package/dist/layout/ConsoleLayout.js +27 -11
- package/dist/layout/ContextSelectors.d.ts +44 -0
- package/dist/layout/ContextSelectors.js +242 -0
- package/dist/layout/InboxPopover.d.ts +6 -1
- package/dist/layout/InboxPopover.js +25 -6
- package/dist/layout/LocaleSwitcher.d.ts +1 -1
- package/dist/layout/LocalizedSidebarTrigger.d.ts +2 -0
- package/dist/layout/LocalizedSidebarTrigger.js +15 -0
- package/dist/layout/MobileViewSwitcherContext.d.ts +1 -1
- package/dist/layout/ModeToggle.d.ts +1 -1
- package/dist/layout/PageHeader.d.ts +1 -1
- package/dist/layout/UnifiedSidebar.d.ts +2 -1
- package/dist/layout/UnifiedSidebar.js +116 -15
- package/dist/layout/agentPicker.d.ts +56 -0
- package/dist/layout/agentPicker.js +40 -0
- package/dist/observability/index.d.ts +1 -0
- package/dist/observability/index.js +1 -0
- package/dist/observability/settleSignal.d.ts +64 -0
- package/dist/observability/settleSignal.js +131 -0
- package/dist/preview/CommitTimeline.d.ts +15 -0
- package/dist/preview/CommitTimeline.js +82 -0
- package/dist/preview/DraftChangesPanel.d.ts +19 -0
- package/dist/preview/DraftChangesPanel.js +114 -0
- package/dist/preview/DraftPreviewBar.d.ts +8 -0
- package/dist/preview/DraftPreviewBar.js +86 -0
- package/dist/preview/PreviewDraftEmptyState.d.ts +16 -0
- package/dist/preview/PreviewDraftEmptyState.js +47 -0
- package/dist/preview/PreviewModeContext.d.ts +57 -0
- package/dist/preview/PreviewModeContext.js +99 -0
- package/dist/preview/UnpublishedAppBar.d.ts +8 -0
- package/dist/preview/UnpublishedAppBar.js +83 -0
- package/dist/preview/commitHistory.d.ts +28 -0
- package/dist/preview/commitHistory.js +48 -0
- package/dist/preview/draftStatus.d.ts +20 -0
- package/dist/preview/draftStatus.js +27 -0
- package/dist/preview/usePublishAllDrafts.d.ts +18 -0
- package/dist/preview/usePublishAllDrafts.js +106 -0
- package/dist/providers/AdapterProvider.d.ts +1 -1
- package/dist/providers/AdapterProvider.js +6 -1
- package/dist/providers/ExpressionProvider.d.ts +1 -1
- package/dist/providers/MetadataProvider.d.ts +17 -2
- package/dist/providers/MetadataProvider.js +192 -12
- package/dist/runtime-config.d.ts +46 -2
- package/dist/runtime-config.js +39 -2
- package/dist/services/builtinComponents.js +68 -59
- package/dist/skeletons/SkeletonDashboard.d.ts +1 -1
- package/dist/skeletons/SkeletonDetail.d.ts +1 -1
- package/dist/skeletons/SkeletonGrid.d.ts +1 -1
- package/dist/utils/appRoute.d.ts +21 -0
- package/dist/utils/appRoute.js +25 -0
- package/dist/utils/deriveRelatedLists.d.ts +54 -0
- package/dist/utils/deriveRelatedLists.js +91 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/managedByEmptyState.d.ts +8 -1
- package/dist/utils/managedByEmptyState.js +13 -7
- package/dist/utils/preferLocal.d.ts +18 -0
- package/dist/utils/preferLocal.js +24 -0
- package/dist/views/ActionConfirmDialog.d.ts +1 -1
- package/dist/views/ActionConfirmDialog.js +3 -1
- package/dist/views/ActionParamDialog.d.ts +6 -1
- package/dist/views/ActionParamDialog.js +9 -3
- package/dist/views/ActionResultDialog.d.ts +13 -0
- package/dist/views/ActionResultDialog.js +134 -0
- package/dist/views/ComponentNavView.d.ts +14 -1
- package/dist/views/CreateViewDialog.d.ts +1 -1
- package/dist/views/DashboardConfigPanel.d.ts +28 -0
- package/dist/views/DashboardConfigPanel.js +81 -0
- package/dist/views/DashboardView.d.ts +4 -3
- package/dist/views/DashboardView.js +38 -239
- package/dist/views/FlowRunner.d.ts +31 -0
- package/dist/views/FlowRunner.js +121 -0
- package/dist/views/InterfaceListPage.d.ts +49 -0
- package/dist/views/InterfaceListPage.js +347 -0
- package/dist/views/MetadataInspector.d.ts +2 -2
- package/dist/views/ObjectView.d.ts +1 -1
- package/dist/views/ObjectView.js +209 -532
- package/dist/views/PageView.d.ts +8 -3
- package/dist/views/PageView.js +45 -32
- package/dist/views/RecordDetailView.d.ts +1 -1
- package/dist/views/RecordDetailView.js +363 -148
- package/dist/views/RecordFormPage.d.ts +1 -1
- package/dist/views/RecordFormPage.js +26 -1
- package/dist/views/ReportConfigPanel.d.ts +37 -0
- package/dist/views/ReportConfigPanel.js +85 -0
- package/dist/views/ReportView.d.ts +1 -1
- package/dist/views/ReportView.js +116 -7
- package/dist/views/RuntimeDraftBar.d.ts +30 -0
- package/dist/views/RuntimeDraftBar.js +112 -0
- package/dist/views/ScreenView.d.ts +70 -0
- package/dist/views/ScreenView.js +73 -0
- package/dist/views/SearchResultsPage.d.ts +1 -1
- package/dist/views/SearchResultsPage.js +8 -18
- package/dist/views/ViewConfigPanel.d.ts +24 -17
- package/dist/views/ViewConfigPanel.js +121 -77
- package/dist/views/index.d.ts +1 -1
- package/dist/views/index.js +1 -1
- package/dist/views/metadata-admin/AuditPanel.d.ts +28 -0
- package/dist/views/metadata-admin/AuditPanel.js +79 -0
- package/dist/views/metadata-admin/DiagnosticsPage.d.ts +20 -0
- package/dist/views/metadata-admin/DiagnosticsPage.js +69 -0
- package/dist/views/metadata-admin/DirectoryPage.d.ts +16 -1
- package/dist/views/metadata-admin/DirectoryPage.js +101 -24
- package/dist/views/metadata-admin/DraftReviewPanel.d.ts +33 -0
- package/dist/views/metadata-admin/DraftReviewPanel.js +77 -0
- package/dist/views/metadata-admin/EmbeddedItemEditor.d.ts +17 -1
- package/dist/views/metadata-admin/EmbeddedItemEditor.js +15 -8
- package/dist/views/metadata-admin/JsonSourceEditor.d.ts +39 -0
- package/dist/views/metadata-admin/JsonSourceEditor.js +196 -0
- package/dist/views/metadata-admin/LayeredDiff.d.ts +39 -1
- package/dist/views/metadata-admin/LayeredDiff.js +171 -5
- package/dist/views/metadata-admin/MetadataDetailDrawer.d.ts +15 -1
- package/dist/views/metadata-admin/MetadataTypeActions.d.ts +48 -0
- package/dist/views/metadata-admin/MetadataTypeActions.js +165 -0
- package/dist/views/metadata-admin/PackagesPage.d.ts +18 -0
- package/dist/views/metadata-admin/PackagesPage.js +403 -0
- package/dist/views/metadata-admin/PageShell.d.ts +1 -1
- package/dist/views/metadata-admin/PageShell.js +9 -4
- package/dist/views/metadata-admin/PermissionMatrixEditor.d.ts +35 -1
- package/dist/views/metadata-admin/QuickFind.d.ts +21 -1
- package/dist/views/metadata-admin/QuickFind.js +6 -3
- package/dist/views/metadata-admin/RelatedPanel.d.ts +24 -1
- package/dist/views/metadata-admin/RelatedPanel.js +20 -18
- package/dist/views/metadata-admin/ResourceEditPage.d.ts +40 -1
- package/dist/views/metadata-admin/ResourceEditPage.js +1250 -60
- package/dist/views/metadata-admin/ResourceHistoryPage.d.ts +39 -1
- package/dist/views/metadata-admin/ResourceHistoryPage.js +66 -16
- package/dist/views/metadata-admin/ResourceListPage.d.ts +13 -1
- package/dist/views/metadata-admin/ResourceListPage.js +258 -30
- package/dist/views/metadata-admin/ResourceRouter.d.ts +23 -1
- package/dist/views/metadata-admin/SchemaForm.d.ts +34 -1
- package/dist/views/metadata-admin/SchemaForm.js +559 -49
- package/dist/views/metadata-admin/StudioHomePage.d.ts +22 -0
- package/dist/views/metadata-admin/StudioHomePage.js +205 -0
- package/dist/views/metadata-admin/anchors.js +255 -24
- package/dist/views/metadata-admin/clientValidation.d.ts +50 -0
- package/dist/views/metadata-admin/clientValidation.js +169 -0
- package/dist/views/metadata-admin/color-variant-field.d.ts +30 -0
- package/dist/views/metadata-admin/color-variant-field.js +38 -0
- package/dist/views/metadata-admin/createDerive.d.ts +75 -0
- package/dist/views/metadata-admin/createDerive.js +179 -0
- package/dist/views/metadata-admin/dashboard-schema.d.ts +12 -0
- package/dist/views/metadata-admin/dashboard-schema.js +80 -0
- package/dist/views/metadata-admin/datasource/DatasourceResourcePage.d.ts +35 -0
- package/dist/views/metadata-admin/datasource/DatasourceResourcePage.js +327 -0
- package/dist/views/metadata-admin/datasource/register.d.ts +1 -0
- package/dist/views/metadata-admin/datasource/register.js +24 -0
- package/dist/views/metadata-admin/default-inspector-registry.d.ts +49 -0
- package/dist/views/metadata-admin/default-inspector-registry.js +8 -0
- package/dist/views/metadata-admin/default-schemas.js +115 -10
- package/dist/views/metadata-admin/external/ExternalDatasourcePanel.d.ts +27 -0
- package/dist/views/metadata-admin/external/ExternalDatasourcePanel.js +69 -0
- package/dist/views/metadata-admin/external/ImportObjectDialog.d.ts +27 -0
- package/dist/views/metadata-admin/external/ImportObjectDialog.js +77 -0
- package/dist/views/metadata-admin/external/SchemaBrowser.d.ts +16 -0
- package/dist/views/metadata-admin/external/SchemaBrowser.js +74 -0
- package/dist/views/metadata-admin/external/ValidationPanel.d.ts +16 -0
- package/dist/views/metadata-admin/external/ValidationPanel.js +68 -0
- package/dist/views/metadata-admin/external/api.d.ts +100 -0
- package/dist/views/metadata-admin/external/api.js +124 -0
- package/dist/views/metadata-admin/i18n.d.ts +1 -0
- package/dist/views/metadata-admin/i18n.js +1252 -2
- package/dist/views/metadata-admin/index.d.ts +8 -5
- package/dist/views/metadata-admin/index.js +12 -2
- package/dist/views/metadata-admin/inspector-registry.d.ts +51 -0
- package/dist/views/metadata-admin/inspector-registry.js +11 -0
- package/dist/views/metadata-admin/inspectors/ActionDefaultInspector.d.ts +30 -0
- package/dist/views/metadata-admin/inspectors/ActionDefaultInspector.js +180 -0
- package/dist/views/metadata-admin/inspectors/AppNavInspector.d.ts +16 -0
- package/dist/views/metadata-admin/inspectors/AppNavInspector.js +110 -0
- package/dist/views/metadata-admin/inspectors/ConditionBuilder.d.ts +29 -0
- package/dist/views/metadata-admin/inspectors/ConditionBuilder.js +154 -0
- package/dist/views/metadata-admin/inspectors/DashboardDefaultInspector.d.ts +28 -0
- package/dist/views/metadata-admin/inspectors/DashboardDefaultInspector.js +110 -0
- package/dist/views/metadata-admin/inspectors/DashboardWidgetInspector.d.ts +18 -0
- package/dist/views/metadata-admin/inspectors/DashboardWidgetInspector.js +139 -0
- package/dist/views/metadata-admin/inspectors/DatasetDefaultInspector.d.ts +21 -0
- package/dist/views/metadata-admin/inspectors/DatasetDefaultInspector.js +221 -0
- package/dist/views/metadata-admin/inspectors/FlowEdgeInspector.d.ts +16 -0
- package/dist/views/metadata-admin/inspectors/FlowEdgeInspector.js +126 -0
- package/dist/views/metadata-admin/inspectors/FlowInspector.d.ts +12 -0
- package/dist/views/metadata-admin/inspectors/FlowInspector.js +9 -0
- package/dist/views/metadata-admin/inspectors/FlowKeyValueField.d.ts +30 -0
- package/dist/views/metadata-admin/inspectors/FlowKeyValueField.js +125 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeConfigField.d.ts +18 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeConfigField.js +40 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeInspector.d.ts +14 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeInspector.js +205 -0
- package/dist/views/metadata-admin/inspectors/FlowObjectListField.d.ts +26 -0
- package/dist/views/metadata-admin/inspectors/FlowObjectListField.js +105 -0
- package/dist/views/metadata-admin/inspectors/FlowReferenceField.d.ts +83 -0
- package/dist/views/metadata-admin/inspectors/FlowReferenceField.js +181 -0
- package/dist/views/metadata-admin/inspectors/FlowStringListField.d.ts +21 -0
- package/dist/views/metadata-admin/inspectors/FlowStringListField.js +60 -0
- package/dist/views/metadata-admin/inspectors/InspectorComboField.d.ts +40 -0
- package/dist/views/metadata-admin/inspectors/InspectorComboField.js +61 -0
- package/dist/views/metadata-admin/inspectors/ObjectDefaultInspector.d.ts +21 -0
- package/dist/views/metadata-admin/inspectors/ObjectDefaultInspector.js +55 -0
- package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.d.ts +23 -0
- package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.js +365 -0
- package/dist/views/metadata-admin/inspectors/PageBlockInspector.d.ts +48 -0
- package/dist/views/metadata-admin/inspectors/PageBlockInspector.js +332 -0
- package/dist/views/metadata-admin/inspectors/ReportDefaultInspector.d.ts +58 -0
- package/dist/views/metadata-admin/inspectors/ReportDefaultInspector.js +218 -0
- package/dist/views/metadata-admin/inspectors/ViewColumnInspector.d.ts +19 -0
- package/dist/views/metadata-admin/inspectors/ViewColumnInspector.js +144 -0
- package/dist/views/metadata-admin/inspectors/ViewInspector.d.ts +19 -0
- package/dist/views/metadata-admin/inspectors/ViewInspector.js +21 -0
- package/dist/views/metadata-admin/inspectors/ViewVariantInspector.d.ts +54 -0
- package/dist/views/metadata-admin/inspectors/ViewVariantInspector.js +191 -0
- package/dist/views/metadata-admin/inspectors/_shared.d.ts +128 -0
- package/dist/views/metadata-admin/inspectors/_shared.js +113 -0
- package/dist/views/metadata-admin/inspectors/datasetFilterCondition.d.ts +24 -0
- package/dist/views/metadata-admin/inspectors/datasetFilterCondition.js +97 -0
- package/dist/views/metadata-admin/inspectors/expression-validate.d.ts +26 -0
- package/dist/views/metadata-admin/inspectors/expression-validate.js +66 -0
- package/dist/views/metadata-admin/inspectors/flow-node-config.d.ts +143 -0
- package/dist/views/metadata-admin/inspectors/flow-node-config.js +506 -0
- package/dist/views/metadata-admin/inspectors/index.d.ts +1 -0
- package/dist/views/metadata-admin/inspectors/index.js +45 -0
- package/dist/views/metadata-admin/inspectors/json-schema-to-fields.d.ts +40 -0
- package/dist/views/metadata-admin/inspectors/json-schema-to-fields.js +227 -0
- package/dist/views/metadata-admin/inspectors/useDatasetFields.d.ts +72 -0
- package/dist/views/metadata-admin/inspectors/useDatasetFields.js +0 -0
- package/dist/views/metadata-admin/issuePath.d.ts +22 -0
- package/dist/views/metadata-admin/issuePath.js +65 -0
- package/dist/views/metadata-admin/mergeServerFields.d.ts +65 -0
- package/dist/views/metadata-admin/mergeServerFields.js +56 -0
- package/dist/views/metadata-admin/package-scope.d.ts +26 -0
- package/dist/views/metadata-admin/package-scope.js +43 -0
- package/dist/views/metadata-admin/preview-registry.d.ts +55 -0
- package/dist/views/metadata-admin/previews/ActionPreview.d.ts +25 -0
- package/dist/views/metadata-admin/previews/ActionPreview.js +238 -0
- package/dist/views/metadata-admin/previews/AddWidgetPicker.d.ts +12 -0
- package/dist/views/metadata-admin/previews/AddWidgetPicker.js +56 -0
- package/dist/views/metadata-admin/previews/AgentPreview.d.ts +24 -0
- package/dist/views/metadata-admin/previews/AgentPreview.js +100 -0
- package/dist/views/metadata-admin/previews/AppNavCanvas.d.ts +31 -0
- package/dist/views/metadata-admin/previews/AppNavCanvas.js +260 -0
- package/dist/views/metadata-admin/previews/AppPreview.d.ts +16 -1
- package/dist/views/metadata-admin/previews/AppPreview.js +23 -14
- package/dist/views/metadata-admin/previews/BookPreview.d.ts +20 -0
- package/dist/views/metadata-admin/previews/BookPreview.js +132 -0
- package/dist/views/metadata-admin/previews/DashboardPreview.d.ts +16 -1
- package/dist/views/metadata-admin/previews/DashboardPreview.js +110 -8
- package/dist/views/metadata-admin/previews/DatasetPreview.d.ts +18 -0
- package/dist/views/metadata-admin/previews/DatasetPreview.js +105 -0
- package/dist/views/metadata-admin/previews/DatasourcePreview.d.ts +23 -0
- package/dist/views/metadata-admin/previews/DatasourcePreview.js +68 -0
- package/dist/views/metadata-admin/previews/EmailTemplatePreview.d.ts +14 -1
- package/dist/views/metadata-admin/previews/FieldStub.d.ts +30 -0
- package/dist/views/metadata-admin/previews/FieldStub.js +104 -0
- package/dist/views/metadata-admin/previews/FieldsListEditor.d.ts +50 -0
- package/dist/views/metadata-admin/previews/FieldsListEditor.js +97 -0
- package/dist/views/metadata-admin/previews/FlowCanvas.d.ts +49 -0
- package/dist/views/metadata-admin/previews/FlowCanvas.js +416 -0
- package/dist/views/metadata-admin/previews/FlowPreview.d.ts +20 -0
- package/dist/views/metadata-admin/previews/FlowPreview.js +120 -0
- package/dist/views/metadata-admin/previews/FlowRunsPanel.d.ts +46 -0
- package/dist/views/metadata-admin/previews/FlowRunsPanel.js +97 -0
- package/dist/views/metadata-admin/previews/FlowSimulatorPanel.d.ts +25 -0
- package/dist/views/metadata-admin/previews/FlowSimulatorPanel.js +204 -0
- package/dist/views/metadata-admin/previews/JobPreview.d.ts +28 -0
- package/dist/views/metadata-admin/previews/JobPreview.js +290 -0
- package/dist/views/metadata-admin/previews/ObjectFormCanvas.d.ts +30 -0
- package/dist/views/metadata-admin/previews/ObjectFormCanvas.js +547 -0
- package/dist/views/metadata-admin/previews/ObjectPreview.d.ts +14 -1
- package/dist/views/metadata-admin/previews/ObjectPreview.js +5 -30
- package/dist/views/metadata-admin/previews/OutlineStrip.d.ts +32 -0
- package/dist/views/metadata-admin/previews/OutlineStrip.js +8 -0
- package/dist/views/metadata-admin/previews/PageBlockCanvas.d.ts +49 -0
- package/dist/views/metadata-admin/previews/PageBlockCanvas.js +510 -0
- package/dist/views/metadata-admin/previews/PagePreview.d.ts +10 -1
- package/dist/views/metadata-admin/previews/PagePreview.js +200 -5
- package/dist/views/metadata-admin/previews/PermissionPreview.d.ts +27 -0
- package/dist/views/metadata-admin/previews/PermissionPreview.js +115 -0
- package/dist/views/metadata-admin/previews/PreviewShell.d.ts +29 -6
- package/dist/views/metadata-admin/previews/PreviewShell.js +16 -3
- package/dist/views/metadata-admin/previews/ReportPreview.d.ts +18 -1
- package/dist/views/metadata-admin/previews/ReportPreview.js +23 -15
- package/dist/views/metadata-admin/previews/RolePreview.d.ts +19 -0
- package/dist/views/metadata-admin/previews/RolePreview.js +14 -0
- package/dist/views/metadata-admin/previews/ScreenPreview.d.ts +38 -0
- package/dist/views/metadata-admin/previews/ScreenPreview.js +61 -0
- package/dist/views/metadata-admin/previews/SkillPreview.d.ts +22 -0
- package/dist/views/metadata-admin/previews/SkillPreview.js +34 -0
- package/dist/views/metadata-admin/previews/ToolPreview.d.ts +25 -0
- package/dist/views/metadata-admin/previews/ToolPreview.js +122 -0
- package/dist/views/metadata-admin/previews/TranslationPreview.d.ts +25 -0
- package/dist/views/metadata-admin/previews/TranslationPreview.js +52 -0
- package/dist/views/metadata-admin/previews/ValidationPreview.d.ts +27 -0
- package/dist/views/metadata-admin/previews/ValidationPreview.js +110 -0
- package/dist/views/metadata-admin/previews/ViewColumnPanes.d.ts +62 -0
- package/dist/views/metadata-admin/previews/ViewColumnPanes.js +140 -0
- package/dist/views/metadata-admin/previews/ViewPreview.d.ts +23 -1
- package/dist/views/metadata-admin/previews/ViewPreview.js +101 -73
- package/dist/views/metadata-admin/previews/block-config.d.ts +82 -0
- package/dist/views/metadata-admin/previews/block-config.js +324 -0
- package/dist/views/metadata-admin/previews/block-types.d.ts +40 -0
- package/dist/views/metadata-admin/previews/block-types.js +110 -0
- package/dist/views/metadata-admin/previews/field-types.d.ts +53 -0
- package/dist/views/metadata-admin/previews/field-types.js +97 -0
- package/dist/views/metadata-admin/previews/flow-canvas-layout.d.ts +102 -0
- package/dist/views/metadata-admin/previews/flow-canvas-layout.js +227 -0
- package/dist/views/metadata-admin/previews/flow-canvas-parts.d.ts +96 -0
- package/dist/views/metadata-admin/previews/flow-canvas-parts.js +373 -0
- package/dist/views/metadata-admin/previews/form-preview.d.ts +24 -0
- package/dist/views/metadata-admin/previews/form-preview.js +29 -0
- package/dist/views/metadata-admin/previews/index.js +43 -0
- package/dist/views/metadata-admin/previews/object-fields-bridge.d.ts +66 -0
- package/dist/views/metadata-admin/previews/object-fields-bridge.js +171 -0
- package/dist/views/metadata-admin/previews/object-fields-io.d.ts +130 -0
- package/dist/views/metadata-admin/previews/object-fields-io.js +243 -0
- package/dist/views/metadata-admin/previews/screen-spec.d.ts +43 -0
- package/dist/views/metadata-admin/previews/screen-spec.js +108 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-types.d.ts +102 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-types.js +2 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-validate.d.ts +15 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-validate.js +185 -0
- package/dist/views/metadata-admin/previews/simulator/flow-simulator.d.ts +73 -0
- package/dist/views/metadata-admin/previews/simulator/flow-simulator.js +426 -0
- package/dist/views/metadata-admin/previews/useDatasetCatalog.d.ts +47 -0
- package/dist/views/metadata-admin/previews/useDatasetCatalog.js +133 -0
- package/dist/views/metadata-admin/previews/useFlowNodePalette.d.ts +44 -0
- package/dist/views/metadata-admin/previews/useFlowNodePalette.js +124 -0
- package/dist/views/metadata-admin/previews/useMetaOptions.d.ts +8 -0
- package/dist/views/metadata-admin/previews/useMetaOptions.js +50 -0
- package/dist/views/metadata-admin/previews/useObjectFields.d.ts +23 -0
- package/dist/views/metadata-admin/previews/useObjectFields.js +79 -0
- package/dist/views/metadata-admin/previews/useObjectOptions.d.ts +8 -0
- package/dist/views/metadata-admin/previews/useObjectOptions.js +43 -0
- package/dist/views/metadata-admin/previews/view-column-io.d.ts +42 -0
- package/dist/views/metadata-admin/previews/view-column-io.js +73 -0
- package/dist/views/metadata-admin/previews/widget-types.d.ts +24 -0
- package/dist/views/metadata-admin/previews/widget-types.js +40 -0
- package/dist/views/metadata-admin/registry.d.ts +140 -19
- package/dist/views/metadata-admin/report-schema.d.ts +26 -0
- package/dist/views/metadata-admin/report-schema.js +121 -0
- package/dist/views/metadata-admin/useMetadata.d.ts +100 -2
- package/dist/views/metadata-admin/useMetadata.js +155 -4
- package/dist/views/metadata-admin/view-item-normalize.d.ts +20 -0
- package/dist/views/metadata-admin/view-item-normalize.js +68 -0
- package/dist/views/metadata-admin/view-schema.d.ts +16 -0
- package/dist/views/metadata-admin/view-schema.js +107 -0
- package/dist/views/metadata-admin/view-variant-model.d.ts +23 -0
- package/dist/views/metadata-admin/view-variant-model.js +64 -0
- package/dist/views/metadata-admin/widgets.d.ts +89 -1
- package/dist/views/metadata-admin/widgets.js +491 -17
- package/dist/views/runtime-metadata-persistence.d.ts +78 -0
- package/dist/views/runtime-metadata-persistence.js +89 -0
- package/dist/views/useOpenRecordList.d.ts +18 -0
- package/dist/views/useOpenRecordList.js +36 -0
- package/dist/views/userFilterUrlState.d.ts +15 -0
- package/dist/views/userFilterUrlState.js +53 -0
- package/dist/views/view-config-adapter.d.ts +38 -0
- package/dist/views/view-config-adapter.js +80 -0
- package/package.json +52 -34
- package/dist/views/DesignDrawer.d.ts +0 -28
- package/dist/views/DesignDrawer.js +0 -51
- package/dist/views/metadata-admin/DesignerEditorWrapper.d.ts +0 -68
- 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
|
-
|
|
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,
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
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 }),
|
|
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 =
|
|
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:
|
|
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:
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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 (
|
|
640
|
+
if (effectiveType === 'string') {
|
|
350
641
|
const maxLength = fieldSpec?.maxLength;
|
|
351
|
-
const long =
|
|
352
|
-
|
|
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 (
|
|
361
|
-
const itemsSchema =
|
|
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:
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
-
|
|
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
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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:
|
|
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":
|
|
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:
|
|
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":
|
|
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" }), "
|
|
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 ?? '
|
|
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
|
}
|