@rebasepro/studio 0.0.1-canary.09e5ec5
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/LICENSE +114 -0
- package/README.md +159 -0
- package/dist/ApiExplorer-gMJt5JrS.js +1053 -0
- package/dist/ApiExplorer-gMJt5JrS.js.map +1 -0
- package/dist/AuthSimulationSelector-BF4rkRGp.js +118 -0
- package/dist/AuthSimulationSelector-BF4rkRGp.js.map +1 -0
- package/dist/BranchesView-DcHZtvXo.js +292 -0
- package/dist/BranchesView-DcHZtvXo.js.map +1 -0
- package/dist/CronJobsView-CijCToeK.js +456 -0
- package/dist/CronJobsView-CijCToeK.js.map +1 -0
- package/dist/JSEditor-D8nVp3Lp.js +1308 -0
- package/dist/JSEditor-D8nVp3Lp.js.map +1 -0
- package/dist/MonacoEditor-CMYEjiRf.js +161 -0
- package/dist/MonacoEditor-CMYEjiRf.js.map +1 -0
- package/dist/RLSEditor-DBH09u9v.js +1831 -0
- package/dist/RLSEditor-DBH09u9v.js.map +1 -0
- package/dist/SQLEditor-CkVx9vgr.js +1792 -0
- package/dist/SQLEditor-CkVx9vgr.js.map +1 -0
- package/dist/SchemaVisualizer-BgD5Zb77.js +1069 -0
- package/dist/SchemaVisualizer-BgD5Zb77.js.map +1 -0
- package/dist/StorageView-CTqGFhY9.js +907 -0
- package/dist/StorageView-CTqGFhY9.js.map +1 -0
- package/dist/common/src/collections/CollectionRegistry.d.ts +56 -0
- package/dist/common/src/collections/index.d.ts +1 -0
- package/dist/common/src/data/buildRebaseData.d.ts +14 -0
- package/dist/common/src/index.d.ts +3 -0
- package/dist/common/src/util/builders.d.ts +57 -0
- package/dist/common/src/util/callbacks.d.ts +6 -0
- package/dist/common/src/util/collections.d.ts +11 -0
- package/dist/common/src/util/common.d.ts +2 -0
- package/dist/common/src/util/conditions.d.ts +26 -0
- package/dist/common/src/util/entities.d.ts +58 -0
- package/dist/common/src/util/enums.d.ts +3 -0
- package/dist/common/src/util/index.d.ts +16 -0
- package/dist/common/src/util/navigation_from_path.d.ts +34 -0
- package/dist/common/src/util/navigation_utils.d.ts +20 -0
- package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
- package/dist/common/src/util/paths.d.ts +14 -0
- package/dist/common/src/util/permissions.d.ts +5 -0
- package/dist/common/src/util/references.d.ts +2 -0
- package/dist/common/src/util/relations.d.ts +22 -0
- package/dist/common/src/util/resolutions.d.ts +72 -0
- package/dist/common/src/util/storage.d.ts +24 -0
- package/dist/core/src/components/AIIcon.d.ts +16 -0
- package/dist/core/src/components/ConfirmationDialog.d.ts +9 -0
- package/dist/core/src/components/Debug/UIReferenceView.d.ts +1 -0
- package/dist/core/src/components/Debug/UIStyleGuide.d.ts +1 -0
- package/dist/core/src/components/ErrorTooltip.d.ts +2 -0
- package/dist/core/src/components/ErrorView.d.ts +21 -0
- package/dist/core/src/components/LanguageToggle.d.ts +1 -0
- package/dist/core/src/components/LoginView/LoginView.d.ts +68 -0
- package/dist/core/src/components/LoginView/index.d.ts +2 -0
- package/dist/core/src/components/NotFoundPage.d.ts +1 -0
- package/dist/core/src/components/RebaseAuth.d.ts +10 -0
- package/dist/core/src/components/RebaseLogo.d.ts +7 -0
- package/dist/core/src/components/UnsavedChangesDialog.d.ts +9 -0
- package/dist/core/src/components/UserDisplay.d.ts +7 -0
- package/dist/core/src/components/UserSelectPopover.d.ts +62 -0
- package/dist/core/src/components/UserSettingsView.d.ts +1 -0
- package/dist/core/src/components/common/index.d.ts +6 -0
- package/dist/core/src/components/common/table_height.d.ts +5 -0
- package/dist/core/src/components/common/types.d.ts +63 -0
- package/dist/core/src/components/common/useColumnsIds.d.ts +9 -0
- package/dist/core/src/components/common/useDataTableController.d.ts +45 -0
- package/dist/core/src/components/common/useDebouncedData.d.ts +9 -0
- package/dist/core/src/components/common/useScrollRestoration.d.ts +14 -0
- package/dist/core/src/components/index.d.ts +16 -0
- package/dist/core/src/contexts/AdminModeController.d.ts +4 -0
- package/dist/core/src/contexts/AnalyticsContext.d.ts +3 -0
- package/dist/core/src/contexts/AuthControllerContext.d.ts +3 -0
- package/dist/core/src/contexts/CustomizationControllerContext.d.ts +3 -0
- package/dist/core/src/contexts/DataDriverContext.d.ts +3 -0
- package/dist/core/src/contexts/DatabaseAdminContext.d.ts +3 -0
- package/dist/core/src/contexts/DialogsProvider.d.ts +4 -0
- package/dist/core/src/contexts/EffectiveRoleController.d.ts +4 -0
- package/dist/core/src/contexts/InternalUserManagementContext.d.ts +3 -0
- package/dist/core/src/contexts/ModeController.d.ts +4 -0
- package/dist/core/src/contexts/RebaseClientInstanceContext.d.ts +6 -0
- package/dist/core/src/contexts/RebaseDataContext.d.ts +3 -0
- package/dist/core/src/contexts/SnackbarProvider.d.ts +2 -0
- package/dist/core/src/contexts/StorageSourceContext.d.ts +3 -0
- package/dist/core/src/contexts/UserConfigurationPersistenceContext.d.ts +3 -0
- package/dist/core/src/contexts/index.d.ts +13 -0
- package/dist/core/src/core/PluginLifecycleManager.d.ts +17 -0
- package/dist/core/src/core/PluginProviderStack.d.ts +21 -0
- package/dist/core/src/core/Rebase.d.ts +14 -0
- package/dist/core/src/core/RebaseProps.d.ts +136 -0
- package/dist/core/src/core/RebaseRouter.d.ts +4 -0
- package/dist/core/src/core/RebaseRoutes.d.ts +17 -0
- package/dist/core/src/core/index.d.ts +4 -0
- package/dist/core/src/hooks/ApiConfigContext.d.ts +24 -0
- package/dist/core/src/hooks/data/delete.d.ts +31 -0
- package/dist/core/src/hooks/data/save.d.ts +34 -0
- package/dist/core/src/hooks/data/useCollectionFetch.d.ts +51 -0
- package/dist/core/src/hooks/data/useData.d.ts +13 -0
- package/dist/core/src/hooks/data/useDataOrder.d.ts +12 -0
- package/dist/core/src/hooks/data/useEntityFetch.d.ts +38 -0
- package/dist/core/src/hooks/data/useRelationSelector.d.ts +52 -0
- package/dist/core/src/hooks/data/useUserSelector.d.ts +31 -0
- package/dist/core/src/hooks/index.d.ts +37 -0
- package/dist/core/src/hooks/useAdminModeController.d.ts +19 -0
- package/dist/core/src/hooks/useAnalyticsController.d.ts +5 -0
- package/dist/core/src/hooks/useAuthController.d.ts +11 -0
- package/dist/core/src/hooks/useAuthSubscription.d.ts +2 -0
- package/dist/core/src/hooks/useBackendStorageSource.d.ts +30 -0
- package/dist/core/src/hooks/useBridgeRegistration.d.ts +18 -0
- package/dist/core/src/hooks/useBrowserTitleAndIcon.d.ts +6 -0
- package/dist/core/src/hooks/useBuildAdminModeController.d.ts +6 -0
- package/dist/core/src/hooks/useBuildEffectiveRoleController.d.ts +8 -0
- package/dist/core/src/hooks/useBuildLocalConfigurationPersistence.d.ts +2 -0
- package/dist/core/src/hooks/useBuildModeController.d.ts +6 -0
- package/dist/core/src/hooks/useClipboard.d.ts +57 -0
- package/dist/core/src/hooks/useCollapsedGroups.d.ts +12 -0
- package/dist/core/src/hooks/useCustomizationController.d.ts +11 -0
- package/dist/core/src/hooks/useDialogsController.d.ts +11 -0
- package/dist/core/src/hooks/useEffectiveRoleController.d.ts +7 -0
- package/dist/core/src/hooks/useInternalUserManagementController.d.ts +12 -0
- package/dist/core/src/hooks/useLargeLayout.d.ts +1 -0
- package/dist/core/src/hooks/useModeController.d.ts +19 -0
- package/dist/core/src/hooks/usePermissions.d.ts +12 -0
- package/dist/core/src/hooks/useRebaseClient.d.ts +5 -0
- package/dist/core/src/hooks/useRebaseContext.d.ts +11 -0
- package/dist/core/src/hooks/useRebaseRegistry.d.ts +34 -0
- package/dist/core/src/hooks/useSlot.d.ts +18 -0
- package/dist/core/src/hooks/useSnackbarController.d.ts +20 -0
- package/dist/core/src/hooks/useStorageSource.d.ts +7 -0
- package/dist/core/src/hooks/useStudioBridge.d.ts +91 -0
- package/dist/core/src/hooks/useTranslation.d.ts +17 -0
- package/dist/core/src/hooks/useUnsavedChangesDialog.d.ts +12 -0
- package/dist/core/src/hooks/useUserConfigurationPersistence.d.ts +8 -0
- package/dist/core/src/hooks/useValidateAuthenticator.d.ts +21 -0
- package/dist/core/src/i18n/RebaseI18nProvider.d.ts +33 -0
- package/dist/core/src/index.d.ts +15 -0
- package/dist/core/src/internal/common.d.ts +3 -0
- package/dist/core/src/internal/useRestoreScroll.d.ts +6 -0
- package/dist/core/src/locales/de.d.ts +2 -0
- package/dist/core/src/locales/en.d.ts +10 -0
- package/dist/core/src/locales/es.d.ts +10 -0
- package/dist/core/src/locales/fr.d.ts +2 -0
- package/dist/core/src/locales/hi.d.ts +2 -0
- package/dist/core/src/locales/it.d.ts +2 -0
- package/dist/core/src/locales/pt.d.ts +7 -0
- package/dist/core/src/util/constants.d.ts +1 -0
- package/dist/core/src/util/createFormexStub.d.ts +2 -0
- package/dist/core/src/util/entity_cache.d.ts +27 -0
- package/dist/core/src/util/enums.d.ts +5 -0
- package/dist/core/src/util/icon_list.d.ts +5 -0
- package/dist/core/src/util/icon_synonyms.d.ts +1 -0
- package/dist/core/src/util/icons.d.ts +20 -0
- package/dist/core/src/util/index.d.ts +10 -0
- package/dist/core/src/util/previews.d.ts +4 -0
- package/dist/core/src/util/useStorageUploadController.d.ts +38 -0
- package/dist/core/src/util/useTraceUpdate.d.ts +2 -0
- package/dist/formex/src/Field.d.ts +52 -0
- package/dist/formex/src/Formex.d.ts +7 -0
- package/dist/formex/src/index.d.ts +5 -0
- package/dist/formex/src/types.d.ts +40 -0
- package/dist/formex/src/useCreateFormex.d.ts +14 -0
- package/dist/formex/src/utils.d.ts +16 -0
- package/dist/index.es.js +726 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +9647 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/studio/src/components/ApiExplorer/ApiExplorer.d.ts +9 -0
- package/dist/studio/src/components/ApiExplorer/EndpointDetail.d.ts +9 -0
- package/dist/studio/src/components/ApiExplorer/TryItPanel.d.ts +15 -0
- package/dist/studio/src/components/ApiExplorer/parseSpec.d.ts +16 -0
- package/dist/studio/src/components/ApiExplorer/types.d.ts +90 -0
- package/dist/studio/src/components/AuthSimulationSelector.d.ts +11 -0
- package/dist/studio/src/components/Branches/BranchesView.d.ts +1 -0
- package/dist/studio/src/components/CronJobs/CronJobsView.d.ts +1 -0
- package/dist/studio/src/components/JSEditor/JSEditor.d.ts +1 -0
- package/dist/studio/src/components/JSEditor/JSEditorSidebar.d.ts +21 -0
- package/dist/studio/src/components/JSEditor/JSMonacoEditor.d.ts +18 -0
- package/dist/studio/src/components/RLSEditor/PolicyEditor.d.ts +9 -0
- package/dist/studio/src/components/RLSEditor/RLSEditor.d.ts +19 -0
- package/dist/studio/src/components/RLSEditor/index.d.ts +1 -0
- package/dist/studio/src/components/RebaseStudio.d.ts +2 -0
- package/dist/studio/src/components/SQLEditor/ExplainVisualizer.d.ts +24 -0
- package/dist/studio/src/components/SQLEditor/MonacoEditor.d.ts +17 -0
- package/dist/studio/src/components/SQLEditor/SQLEditor.d.ts +11 -0
- package/dist/studio/src/components/SQLEditor/SQLEditorSidebar.d.ts +21 -0
- package/dist/studio/src/components/SQLEditor/SchemaBrowser.d.ts +8 -0
- package/dist/studio/src/components/SchemaVisualizer/RelationEdge.d.ts +3 -0
- package/dist/studio/src/components/SchemaVisualizer/SchemaVisualizer.d.ts +2 -0
- package/dist/studio/src/components/SchemaVisualizer/TableNode.d.ts +3 -0
- package/dist/studio/src/components/SchemaVisualizer/index.d.ts +5 -0
- package/dist/studio/src/components/SchemaVisualizer/schema-visualizer.utils.d.ts +42 -0
- package/dist/studio/src/components/SchemaVisualizer/useSchemaGraph.d.ts +37 -0
- package/dist/studio/src/components/StorageView/StorageView.d.ts +1 -0
- package/dist/studio/src/components/StudioHomePage.d.ts +9 -0
- package/dist/studio/src/index.d.ts +4 -0
- package/dist/studio/src/utils/entities.d.ts +0 -0
- package/dist/studio/src/utils/pgColumnToProperty.d.ts +6 -0
- package/dist/studio/src/utils/sql_utils.d.ts +52 -0
- package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
- package/dist/types/src/controllers/auth.d.ts +119 -0
- package/dist/types/src/controllers/client.d.ts +170 -0
- package/dist/types/src/controllers/collection_registry.d.ts +45 -0
- package/dist/types/src/controllers/customization_controller.d.ts +60 -0
- package/dist/types/src/controllers/data.d.ts +168 -0
- package/dist/types/src/controllers/data_driver.d.ts +160 -0
- package/dist/types/src/controllers/database_admin.d.ts +11 -0
- package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
- package/dist/types/src/controllers/effective_role.d.ts +4 -0
- package/dist/types/src/controllers/email.d.ts +34 -0
- package/dist/types/src/controllers/index.d.ts +18 -0
- package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
- package/dist/types/src/controllers/navigation.d.ts +213 -0
- package/dist/types/src/controllers/registry.d.ts +54 -0
- package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
- package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
- package/dist/types/src/controllers/snackbar.d.ts +24 -0
- package/dist/types/src/controllers/storage.d.ts +171 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/rebase_context.d.ts +105 -0
- package/dist/types/src/types/backend.d.ts +536 -0
- package/dist/types/src/types/builders.d.ts +15 -0
- package/dist/types/src/types/chips.d.ts +5 -0
- package/dist/types/src/types/collections.d.ts +856 -0
- package/dist/types/src/types/cron.d.ts +102 -0
- package/dist/types/src/types/data_source.d.ts +64 -0
- package/dist/types/src/types/entities.d.ts +145 -0
- package/dist/types/src/types/entity_actions.d.ts +98 -0
- package/dist/types/src/types/entity_callbacks.d.ts +173 -0
- package/dist/types/src/types/entity_link_builder.d.ts +7 -0
- package/dist/types/src/types/entity_overrides.d.ts +10 -0
- package/dist/types/src/types/entity_views.d.ts +61 -0
- package/dist/types/src/types/export_import.d.ts +21 -0
- package/dist/types/src/types/index.d.ts +23 -0
- package/dist/types/src/types/locales.d.ts +4 -0
- package/dist/types/src/types/modify_collections.d.ts +5 -0
- package/dist/types/src/types/plugins.d.ts +279 -0
- package/dist/types/src/types/properties.d.ts +1176 -0
- package/dist/types/src/types/property_config.d.ts +70 -0
- package/dist/types/src/types/relations.d.ts +336 -0
- package/dist/types/src/types/slots.d.ts +252 -0
- package/dist/types/src/types/translations.d.ts +870 -0
- package/dist/types/src/types/user_management_delegate.d.ts +121 -0
- package/dist/types/src/types/websockets.d.ts +78 -0
- package/dist/types/src/users/index.d.ts +2 -0
- package/dist/types/src/users/roles.d.ts +22 -0
- package/dist/types/src/users/user.d.ts +46 -0
- package/dist/ui/src/components/Alert.d.ts +12 -0
- package/dist/ui/src/components/Autocomplete.d.ts +21 -0
- package/dist/ui/src/components/Avatar.d.ts +11 -0
- package/dist/ui/src/components/Badge.d.ts +8 -0
- package/dist/ui/src/components/BooleanSwitch.d.ts +14 -0
- package/dist/ui/src/components/BooleanSwitchWithLabel.d.ts +17 -0
- package/dist/ui/src/components/Button.d.ts +14 -0
- package/dist/ui/src/components/Card.d.ts +9 -0
- package/dist/ui/src/components/CenteredView.d.ts +9 -0
- package/dist/ui/src/components/Checkbox.d.ts +13 -0
- package/dist/ui/src/components/Chip.d.ts +26 -0
- package/dist/ui/src/components/CircularProgress.d.ts +5 -0
- package/dist/ui/src/components/CircularProgressCenter.d.ts +11 -0
- package/dist/ui/src/components/Collapse.d.ts +9 -0
- package/dist/ui/src/components/ColorPicker.d.ts +30 -0
- package/dist/ui/src/components/Container.d.ts +8 -0
- package/dist/ui/src/components/DateTimeField.d.ts +24 -0
- package/dist/ui/src/components/DebouncedTextField.d.ts +2 -0
- package/dist/ui/src/components/Dialog.d.ts +39 -0
- package/dist/ui/src/components/DialogActions.d.ts +7 -0
- package/dist/ui/src/components/DialogContent.d.ts +7 -0
- package/dist/ui/src/components/DialogTitle.d.ts +10 -0
- package/dist/ui/src/components/ErrorBoundary.d.ts +11 -0
- package/dist/ui/src/components/ExpandablePanel.d.ts +12 -0
- package/dist/ui/src/components/FileUpload.d.ts +23 -0
- package/dist/ui/src/components/IconButton.d.ts +12 -0
- package/dist/ui/src/components/InfoLabel.d.ts +5 -0
- package/dist/ui/src/components/InputLabel.d.ts +11 -0
- package/dist/ui/src/components/Label.d.ts +7 -0
- package/dist/ui/src/components/LoadingButton.d.ts +7 -0
- package/dist/ui/src/components/Markdown.d.ts +10 -0
- package/dist/ui/src/components/Menu.d.ts +23 -0
- package/dist/ui/src/components/Menubar.d.ts +80 -0
- package/dist/ui/src/components/MultiSelect.d.ts +48 -0
- package/dist/ui/src/components/Paper.d.ts +6 -0
- package/dist/ui/src/components/Popover.d.ts +24 -0
- package/dist/ui/src/components/RadioGroup.d.ts +28 -0
- package/dist/ui/src/components/ResizablePanels.d.ts +18 -0
- package/dist/ui/src/components/SearchBar.d.ts +22 -0
- package/dist/ui/src/components/Select.d.ts +43 -0
- package/dist/ui/src/components/Separator.d.ts +5 -0
- package/dist/ui/src/components/Sheet.d.ts +22 -0
- package/dist/ui/src/components/Skeleton.d.ts +6 -0
- package/dist/ui/src/components/Slider.d.ts +21 -0
- package/dist/ui/src/components/Table.d.ts +34 -0
- package/dist/ui/src/components/Tabs.d.ts +19 -0
- package/dist/ui/src/components/TextField.d.ts +58 -0
- package/dist/ui/src/components/TextareaAutosize.d.ts +43 -0
- package/dist/ui/src/components/ToggleButtonGroup.d.ts +30 -0
- package/dist/ui/src/components/Tooltip.d.ts +19 -0
- package/dist/ui/src/components/Typography.d.ts +36 -0
- package/dist/ui/src/components/VirtualTable/VirtualTable.d.ts +11 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableCell.d.ts +21 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableHeader.d.ts +29 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableHeaderRow.d.ts +2 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableProps.d.ts +243 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableRow.d.ts +3 -0
- package/dist/ui/src/components/VirtualTable/index.d.ts +3 -0
- package/dist/ui/src/components/VirtualTable/types.d.ts +38 -0
- package/dist/ui/src/components/common/SelectInputLabel.d.ts +5 -0
- package/dist/ui/src/components/index.d.ts +53 -0
- package/dist/ui/src/hooks/PortalContainerContext.d.ts +31 -0
- package/dist/ui/src/hooks/index.d.ts +6 -0
- package/dist/ui/src/hooks/useDebounceCallback.d.ts +1 -0
- package/dist/ui/src/hooks/useDebounceValue.d.ts +1 -0
- package/dist/ui/src/hooks/useDebouncedCallback.d.ts +1 -0
- package/dist/ui/src/hooks/useInjectStyles.d.ts +7 -0
- package/dist/ui/src/hooks/useOutsideAlerter.d.ts +5 -0
- package/dist/ui/src/icons/GitHubIcon.d.ts +2 -0
- package/dist/ui/src/icons/HandleIcon.d.ts +1 -0
- package/dist/ui/src/icons/Icon.d.ts +20 -0
- package/dist/ui/src/icons/cool_icon_keys.d.ts +1 -0
- package/dist/ui/src/icons/icon_keys.d.ts +1 -0
- package/dist/ui/src/icons/index.d.ts +6 -0
- package/dist/ui/src/index.d.ts +5 -0
- package/dist/ui/src/styles.d.ts +12 -0
- package/dist/ui/src/util/chip_colors.d.ts +4 -0
- package/dist/ui/src/util/cls.d.ts +2 -0
- package/dist/ui/src/util/debounce.d.ts +10 -0
- package/dist/ui/src/util/hash.d.ts +1 -0
- package/dist/ui/src/util/index.d.ts +4 -0
- package/dist/ui/src/util/key_to_icon_component.d.ts +1 -0
- package/package.json +84 -0
- package/src/components/ApiExplorer/ApiExplorer.tsx +290 -0
- package/src/components/ApiExplorer/EndpointDetail.tsx +271 -0
- package/src/components/ApiExplorer/TryItPanel.tsx +510 -0
- package/src/components/ApiExplorer/parseSpec.ts +104 -0
- package/src/components/ApiExplorer/types.ts +84 -0
- package/src/components/AuthSimulationSelector.tsx +77 -0
- package/src/components/Branches/BranchesView.tsx +370 -0
- package/src/components/CronJobs/CronJobsView.tsx +346 -0
- package/src/components/JSEditor/JSEditor.tsx +1033 -0
- package/src/components/JSEditor/JSEditorSidebar.tsx +340 -0
- package/src/components/JSEditor/JSMonacoEditor.tsx +390 -0
- package/src/components/RLSEditor/PolicyEditor.tsx +444 -0
- package/src/components/RLSEditor/RLSEditor.tsx +692 -0
- package/src/components/RLSEditor/index.ts +1 -0
- package/src/components/RebaseStudio.tsx +121 -0
- package/src/components/SQLEditor/ExplainVisualizer.tsx +128 -0
- package/src/components/SQLEditor/MonacoEditor.tsx +203 -0
- package/src/components/SQLEditor/SQLEditor.tsx +1419 -0
- package/src/components/SQLEditor/SQLEditorSidebar.tsx +174 -0
- package/src/components/SQLEditor/SchemaBrowser.tsx +158 -0
- package/src/components/SchemaVisualizer/RelationEdge.tsx +102 -0
- package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +665 -0
- package/src/components/SchemaVisualizer/TableNode.tsx +257 -0
- package/src/components/SchemaVisualizer/index.ts +5 -0
- package/src/components/SchemaVisualizer/schema-visualizer.utils.ts +140 -0
- package/src/components/SchemaVisualizer/useSchemaGraph.ts +397 -0
- package/src/components/StorageView/StorageView.tsx +1035 -0
- package/src/components/StudioHomePage.tsx +357 -0
- package/src/index.ts +31 -0
- package/src/utils/entities.ts +2 -0
- package/src/utils/pgColumnToProperty.test.ts +401 -0
- package/src/utils/pgColumnToProperty.ts +275 -0
- package/src/utils/sql_utils.test.ts +265 -0
- package/src/utils/sql_utils.ts +291 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,1792 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { c } from "react-compiler-runtime";
|
|
3
|
+
import { useTranslation, ErrorView, useRebaseContext, useStudioSideEntityController, useSnackbarController, useStudioCollectionRegistry, ConfirmationDialog, IconForView } from "@rebasepro/core";
|
|
4
|
+
import { useState, useRef, useCallback, useEffect } from "react";
|
|
5
|
+
import { createPortal } from "react-dom";
|
|
6
|
+
import { CircularProgress, Typography, IconButton, iconSize, cls, defaultBorderMixin, Menu, MenuItem, Button, Tabs, Tab, Collapse, ResizablePanels, Tooltip, Dialog, DialogTitle, DialogContent, TextField, DialogActions, VirtualTable } from "@rebasepro/ui";
|
|
7
|
+
import { RefreshCwIcon, CopyIcon, MoreVerticalIcon, Trash2Icon, TerminalIcon, XIcon, PlusIcon, MenuIcon, DatabaseIcon, PlayIcon, PencilIcon } from "lucide-react";
|
|
8
|
+
import { M as MonacoEditor } from "./MonacoEditor-CMYEjiRf.js";
|
|
9
|
+
import { parseFirst } from "pgsql-ast-parser";
|
|
10
|
+
import { toSnakeCase } from "@rebasepro/utils";
|
|
11
|
+
const SchemaBrowser = ({
|
|
12
|
+
onTableClick,
|
|
13
|
+
schemas,
|
|
14
|
+
isSchemaLoading,
|
|
15
|
+
schemaError,
|
|
16
|
+
onRetrySchema
|
|
17
|
+
}) => {
|
|
18
|
+
const [expandedSchemas, setExpandedSchemas] = useState({
|
|
19
|
+
public: true
|
|
20
|
+
});
|
|
21
|
+
const [expandedTables, setExpandedTables] = useState({});
|
|
22
|
+
const {
|
|
23
|
+
t
|
|
24
|
+
} = useTranslation();
|
|
25
|
+
if (isSchemaLoading) return /* @__PURE__ */ jsx("div", { className: "p-4 flex justify-center", children: /* @__PURE__ */ jsx(CircularProgress, { size: "small" }) });
|
|
26
|
+
if (schemaError) return /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(ErrorView, { error: schemaError, onRetry: onRetrySchema }) });
|
|
27
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full overflow-hidden", children: [
|
|
28
|
+
/* @__PURE__ */ jsxs("div", { className: cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin), children: [
|
|
29
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-bold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark", children: t("studio_schema_tables") }),
|
|
30
|
+
/* @__PURE__ */ jsx(IconButton, { size: "small", onClick: onRetrySchema, title: "Refresh schema", children: /* @__PURE__ */ jsx(RefreshCwIcon, { size: iconSize.smallest }) })
|
|
31
|
+
] }),
|
|
32
|
+
/* @__PURE__ */ jsx("div", { className: "flex-grow overflow-y-auto no-scrollbar p-1", children: Object.keys(schemas).length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-disabled dark:text-text-disabled-dark italic", children: t("studio_schema_no_tables") }) }) : Object.entries(schemas).map(([schemaName, tables]) => /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
|
|
33
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-950 rounded transition-colors", onClick: () => setExpandedSchemas((prev) => ({
|
|
34
|
+
...prev,
|
|
35
|
+
[schemaName]: !prev[schemaName]
|
|
36
|
+
})), children: [
|
|
37
|
+
/* @__PURE__ */ jsx("svg", { className: cls("w-3 h-3 mr-1 transition-transform", expandedSchemas[schemaName] ? "rotate-90" : ""), fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" }) }),
|
|
38
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow", children: schemaName })
|
|
39
|
+
] }),
|
|
40
|
+
expandedSchemas[schemaName] && /* @__PURE__ */ jsx("div", { className: "ml-3 mt-1 space-y-1", children: tables.map((table) => /* @__PURE__ */ jsxs("div", { children: [
|
|
41
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-950 rounded transition-colors group relative", onClick: () => setExpandedTables((prev_0) => ({
|
|
42
|
+
...prev_0,
|
|
43
|
+
[`${schemaName}.${table.tableName}`]: !prev_0[`${schemaName}.${table.tableName}`]
|
|
44
|
+
})), children: [
|
|
45
|
+
/* @__PURE__ */ jsx("svg", { className: cls("w-3 h-3 mr-1 transition-transform shrink-0", expandedTables[`${schemaName}.${table.tableName}`] ? "rotate-90" : ""), fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" }) }),
|
|
46
|
+
/* @__PURE__ */ jsx("svg", { className: "w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" }) }),
|
|
47
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-text-secondary dark:text-text-secondary-dark text-xs truncate flex-1 min-w-0", children: table.tableName }),
|
|
48
|
+
/* @__PURE__ */ jsxs("div", { className: "flex opacity-0 group-hover:opacity-100 focus-within:opacity-100 absolute right-1 items-center bg-surface-100 dark:bg-surface-950 px-1 gap-1 rounded transition-opacity", children: [
|
|
49
|
+
/* @__PURE__ */ jsx(IconButton, { size: "smallest", className: "transition-colors text-text-secondary hover:text-text-primary pointer-events-auto", onClick: (e) => {
|
|
50
|
+
e.stopPropagation();
|
|
51
|
+
navigator.clipboard.writeText(table.tableName);
|
|
52
|
+
}, title: "CopyIcon table name", children: /* @__PURE__ */ jsx(CopyIcon, { size: iconSize.smallest }) }),
|
|
53
|
+
/* @__PURE__ */ jsxs(Menu, { trigger: /* @__PURE__ */ jsx(IconButton, { size: "smallest", className: "transition-colors text-text-secondary hover:text-text-primary pointer-events-auto", onClick: (e_0) => e_0.stopPropagation(), title: "Generate SQL templates", children: /* @__PURE__ */ jsx(MoreVerticalIcon, { size: iconSize.smallest }) }), children: [
|
|
54
|
+
/* @__PURE__ */ jsx(MenuItem, { dense: true, className: "text-xs", onClick: (e_1) => {
|
|
55
|
+
e_1.stopPropagation();
|
|
56
|
+
onTableClick?.(`SELECT * FROM ${schemaName !== "public" ? `${schemaName}.` : ""}${table.tableName} LIMIT 100;`);
|
|
57
|
+
}, children: "SELECT (Top 100)" }),
|
|
58
|
+
/* @__PURE__ */ jsx(MenuItem, { dense: true, className: "text-xs", onClick: (e_2) => {
|
|
59
|
+
e_2.stopPropagation();
|
|
60
|
+
onTableClick?.(`SELECT
|
|
61
|
+
${table.columns.map((c2) => c2.name).join(",\n ")}
|
|
62
|
+
FROM ${schemaName !== "public" ? `${schemaName}.` : ""}${table.tableName};`);
|
|
63
|
+
}, children: "SELECT (All columns)" }),
|
|
64
|
+
/* @__PURE__ */ jsx(MenuItem, { dense: true, className: "text-xs", onClick: (e_3) => {
|
|
65
|
+
e_3.stopPropagation();
|
|
66
|
+
onTableClick?.(`INSERT INTO ${schemaName !== "public" ? `${schemaName}.` : ""}${table.tableName} (
|
|
67
|
+
${table.columns.map((c_0) => c_0.name).join(",\n ")}
|
|
68
|
+
) VALUES (
|
|
69
|
+
${table.columns.map(() => "?").join(",\n ")}
|
|
70
|
+
);`);
|
|
71
|
+
}, children: "INSERT statement" }),
|
|
72
|
+
/* @__PURE__ */ jsx(MenuItem, { dense: true, className: "text-xs", onClick: (e_4) => {
|
|
73
|
+
e_4.stopPropagation();
|
|
74
|
+
onTableClick?.(`UPDATE ${schemaName !== "public" ? `${schemaName}.` : ""}${table.tableName}
|
|
75
|
+
SET
|
|
76
|
+
${table.columns.map((c_1) => `${c_1.name} = ?`).join(",\n ")}
|
|
77
|
+
WHERE id = ?;`);
|
|
78
|
+
}, children: "UPDATE statement" })
|
|
79
|
+
] }),
|
|
80
|
+
/* @__PURE__ */ jsx(Button, { variant: "text", size: "small", className: "text-[10px] text-primary uppercase min-h-0 py-0.5 px-1 font-medium ml-1 flex-shrink-0 leading-none h-[22px]", onClick: (e_5) => {
|
|
81
|
+
e_5.stopPropagation();
|
|
82
|
+
onTableClick?.(`SELECT * FROM ${schemaName !== "public" ? `${schemaName}.` : ""}${table.tableName} LIMIT 100;`);
|
|
83
|
+
}, children: "SELECT" })
|
|
84
|
+
] })
|
|
85
|
+
] }),
|
|
86
|
+
expandedTables[`${schemaName}.${table.tableName}`] && /* @__PURE__ */ jsx("div", { className: cls("ml-5 mt-1 space-y-0.5 border-l", defaultBorderMixin), children: table.columns.map((col) => /* @__PURE__ */ jsxs("div", { className: "flex items-center p-1 group pl-2 hover:bg-surface-50 dark:hover:bg-surface-950 rounded-r relative min-h-[28px]", children: [
|
|
87
|
+
/* @__PURE__ */ jsx("svg", { className: "w-3 h-3 mr-1.5 text-text-disabled dark:text-text-disabled-dark shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M9 4.5v15m6-15v15m-10.5-1.5h15c.621 0 1.125-.504 1.125-1.125V5.625c0-.621-.504-1.125-1.125-1.125h-15c-.621 0-1.125.504-1.125 1.125v12.75c0 .621.504 1.125 1.125 1.125Z" }) }),
|
|
88
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-primary dark:text-text-primary-dark text-[11px] truncate flex-grow mr-2", children: col.name }),
|
|
89
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-disabled dark:text-text-disabled-dark text-[9px] truncate mr-1 uppercase shrink-0", title: col.dataType, children: col.dataType }),
|
|
90
|
+
/* @__PURE__ */ jsx(IconButton, { size: "smallest", className: "opacity-0 group-hover:opacity-100 absolute right-1 bg-surface-50 dark:bg-surface-950 transition-colors pointer-events-auto", onClick: (e_6) => {
|
|
91
|
+
e_6.stopPropagation();
|
|
92
|
+
navigator.clipboard.writeText(col.name);
|
|
93
|
+
}, title: "CopyIcon column name", children: /* @__PURE__ */ jsx(CopyIcon, { size: iconSize.smallest }) })
|
|
94
|
+
] }, col.name)) })
|
|
95
|
+
] }, table.tableName)) })
|
|
96
|
+
] }, schemaName)) })
|
|
97
|
+
] });
|
|
98
|
+
};
|
|
99
|
+
const SQLEditorSidebar = ({
|
|
100
|
+
onSelectSnippet,
|
|
101
|
+
onTableClick,
|
|
102
|
+
snippets,
|
|
103
|
+
history,
|
|
104
|
+
onDeleteSnippet,
|
|
105
|
+
schemas,
|
|
106
|
+
isSchemaLoading,
|
|
107
|
+
schemaError,
|
|
108
|
+
onRetrySchema
|
|
109
|
+
}) => {
|
|
110
|
+
const [activeTab, setActiveTab] = useState("schema");
|
|
111
|
+
const {
|
|
112
|
+
t
|
|
113
|
+
} = useTranslation();
|
|
114
|
+
return /* @__PURE__ */ jsxs("div", { className: cls("flex flex-col h-full w-full bg-white dark:bg-surface-950 border-r", defaultBorderMixin), children: [
|
|
115
|
+
/* @__PURE__ */ jsxs(Tabs, { value: activeTab, onValueChange: (v) => setActiveTab(v), variant: "boxy", className: "border-b border-surface-200 dark:border-surface-950", children: [
|
|
116
|
+
/* @__PURE__ */ jsx(Tab, { value: "schema", children: t("studio_sql_sidebar_schema") }),
|
|
117
|
+
/* @__PURE__ */ jsx(Tab, { value: "snippets", children: t("studio_sql_sidebar_snippets") }),
|
|
118
|
+
/* @__PURE__ */ jsx(Tab, { value: "history", children: t("studio_sql_sidebar_history") })
|
|
119
|
+
] }),
|
|
120
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-grow overflow-hidden relative", children: [
|
|
121
|
+
activeTab === "schema" && /* @__PURE__ */ jsx(SchemaBrowser, { onTableClick, schemas, isSchemaLoading, schemaError, onRetrySchema }),
|
|
122
|
+
activeTab === "snippets" && (() => {
|
|
123
|
+
const favorites = snippets.filter((s) => s.isFavorite);
|
|
124
|
+
const others = snippets.filter((s_0) => !s_0.isFavorite);
|
|
125
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
126
|
+
/* @__PURE__ */ jsx("div", { className: cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin), children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-bold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark", children: t("studio_sql_sidebar_snippets") }) }),
|
|
127
|
+
/* @__PURE__ */ jsx("div", { className: "flex-grow overflow-y-auto p-2 space-y-2 no-scrollbar", children: snippets.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-disabled dark:text-text-disabled-dark", children: t("studio_sql_sidebar_no_snippets") }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
128
|
+
favorites.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
|
|
129
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-[10px] font-bold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark mb-2 px-1 flex items-center", children: [
|
|
130
|
+
/* @__PURE__ */ jsx("svg", { className: "w-3 h-3 mr-1 text-red-500", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z", clipRule: "evenodd" }) }),
|
|
131
|
+
"Favorites"
|
|
132
|
+
] }),
|
|
133
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: favorites.map((snippet) => /* @__PURE__ */ jsxs("div", { className: cls("group p-2 rounded border hover:border-surface-400 dark:hover:border-surface-600 bg-white dark:bg-surface-900 transition-all cursor-pointer relative", defaultBorderMixin), onClick: () => onSelectSnippet(snippet.sql), children: [
|
|
134
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-text-primary dark:text-text-primary-dark font-medium text-[13px] truncate pr-6", children: snippet.name }),
|
|
135
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-secondary dark:text-text-secondary-dark text-[10px] block mt-1 truncate", children: snippet.sql }),
|
|
136
|
+
/* @__PURE__ */ jsx(IconButton, { size: "smallest", className: "absolute right-1 top-1 opacity-0 group-hover:opacity-100 text-text-disabled hover:text-red-500 transition-opacity", onClick: (e) => {
|
|
137
|
+
e.stopPropagation();
|
|
138
|
+
onDeleteSnippet(snippet.id);
|
|
139
|
+
}, children: /* @__PURE__ */ jsx(Trash2Icon, { size: iconSize.smallest }) })
|
|
140
|
+
] }, snippet.id)) })
|
|
141
|
+
] }),
|
|
142
|
+
others.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
143
|
+
favorites.length > 0 && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[10px] font-bold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark mb-2 px-1 mt-4", children: "Others" }),
|
|
144
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: others.map((snippet_0) => /* @__PURE__ */ jsxs("div", { className: cls("group p-2 rounded border hover:border-surface-400 dark:hover:border-surface-600 bg-white dark:bg-surface-900 transition-all cursor-pointer relative", defaultBorderMixin), onClick: () => onSelectSnippet(snippet_0.sql), children: [
|
|
145
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-text-primary dark:text-text-primary-dark font-medium text-[13px] truncate pr-6", children: snippet_0.name }),
|
|
146
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-secondary dark:text-text-secondary-dark text-[10px] block mt-1 truncate", children: snippet_0.sql }),
|
|
147
|
+
/* @__PURE__ */ jsx(IconButton, { size: "smallest", className: "absolute right-1 top-1 opacity-0 group-hover:opacity-100 text-text-disabled hover:text-red-500 transition-opacity", onClick: (e_0) => {
|
|
148
|
+
e_0.stopPropagation();
|
|
149
|
+
onDeleteSnippet(snippet_0.id);
|
|
150
|
+
}, children: /* @__PURE__ */ jsx(Trash2Icon, { size: iconSize.smallest }) })
|
|
151
|
+
] }, snippet_0.id)) })
|
|
152
|
+
] })
|
|
153
|
+
] }) })
|
|
154
|
+
] });
|
|
155
|
+
})(),
|
|
156
|
+
activeTab === "history" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
157
|
+
/* @__PURE__ */ jsx("div", { className: cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin), children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-bold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark", children: t("studio_sql_sidebar_history") }) }),
|
|
158
|
+
/* @__PURE__ */ jsx("div", { className: "flex-grow overflow-y-auto p-1 space-y-1 no-scrollbar", children: history.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-disabled dark:text-text-disabled-dark", children: t("studio_sql_sidebar_no_history") }) }) : [...history].reverse().map((sql, i) => /* @__PURE__ */ jsxs("div", { className: "p-2 py-1.5 rounded hover:bg-surface-100 dark:hover:bg-surface-950 cursor-pointer group transition-colors flex items-start", onClick: () => onSelectSnippet(sql), children: [
|
|
159
|
+
/* @__PURE__ */ jsx("svg", { className: "w-3 h-3 mt-1 mr-2 text-text-disabled dark:text-text-disabled-dark group-hover:text-primary transition-colors flex-shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
160
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-secondary dark:text-text-secondary-dark group-hover:text-text-primary dark:group-hover:text-text-primary-dark text-[11px] line-clamp-2 leading-tight flex-grow", children: sql })
|
|
161
|
+
] }, i)) })
|
|
162
|
+
] })
|
|
163
|
+
] })
|
|
164
|
+
] });
|
|
165
|
+
};
|
|
166
|
+
function extractTablesFromQuery(sqlString) {
|
|
167
|
+
try {
|
|
168
|
+
const ast = parseFirst(sqlString);
|
|
169
|
+
if (ast.type !== "select") return [];
|
|
170
|
+
const tables = [];
|
|
171
|
+
const processFrom = (fromItems) => {
|
|
172
|
+
for (const item of fromItems) {
|
|
173
|
+
if (item.type === "table" && item.name) {
|
|
174
|
+
tables.push({
|
|
175
|
+
name: item.name.name,
|
|
176
|
+
alias: item.name.alias
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (item.type === "join") {
|
|
180
|
+
if (item.left) processFrom([item.left]);
|
|
181
|
+
if (item.right) processFrom([item.right]);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
if (ast.from) {
|
|
186
|
+
processFrom(ast.from);
|
|
187
|
+
}
|
|
188
|
+
return tables;
|
|
189
|
+
} catch {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function resolveQueryCollections(sqlString, schemas, collections, resultColumns) {
|
|
194
|
+
const tables = extractTablesFromQuery(sqlString);
|
|
195
|
+
if (tables.length === 0) return [];
|
|
196
|
+
const selectColumns = [];
|
|
197
|
+
try {
|
|
198
|
+
const ast = parseFirst(sqlString);
|
|
199
|
+
if (ast.type === "select" && ast.columns) {
|
|
200
|
+
for (const col of ast.columns) {
|
|
201
|
+
if (col.expr?.type === "ref") {
|
|
202
|
+
selectColumns.push({
|
|
203
|
+
table: col.expr.table?.name,
|
|
204
|
+
column: col.expr.name,
|
|
205
|
+
alias: col.alias?.name
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
}
|
|
212
|
+
const results = [];
|
|
213
|
+
for (const table of tables) {
|
|
214
|
+
const matched = collections.find((c2) => {
|
|
215
|
+
const tableName = c2.table || toSnakeCase(c2.slug);
|
|
216
|
+
return tableName === table.name;
|
|
217
|
+
});
|
|
218
|
+
if (!matched) continue;
|
|
219
|
+
const tableColumns = [];
|
|
220
|
+
for (const schemaEntries of Object.values(schemas)) {
|
|
221
|
+
const tableInfo = schemaEntries.find((t) => t.tableName === table.name);
|
|
222
|
+
if (tableInfo) {
|
|
223
|
+
tableColumns.push(...tableInfo.columns.map((c2) => c2.name));
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
let pkColumn;
|
|
228
|
+
const tableRef = table.alias || table.name;
|
|
229
|
+
const idSelectCol = selectColumns.find((sc) => sc.column === "id" && (!sc.table || sc.table === tableRef || sc.table === table.name));
|
|
230
|
+
if (idSelectCol) {
|
|
231
|
+
pkColumn = idSelectCol.alias || idSelectCol.column;
|
|
232
|
+
}
|
|
233
|
+
if (!pkColumn && resultColumns) {
|
|
234
|
+
if (resultColumns.includes("id")) {
|
|
235
|
+
pkColumn = "id";
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (!pkColumn && tableColumns.includes("id")) {
|
|
239
|
+
pkColumn = "id";
|
|
240
|
+
}
|
|
241
|
+
results.push({
|
|
242
|
+
tableName: table.name,
|
|
243
|
+
tableAlias: table.alias,
|
|
244
|
+
collection: matched,
|
|
245
|
+
columns: tableColumns,
|
|
246
|
+
pkColumn
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return results;
|
|
250
|
+
}
|
|
251
|
+
function determineTableAndPK(sqlString, columnKey, schemas) {
|
|
252
|
+
try {
|
|
253
|
+
const tables = extractTablesFromQuery(sqlString);
|
|
254
|
+
const ast = parseFirst(sqlString);
|
|
255
|
+
if (ast.type !== "select") {
|
|
256
|
+
return {
|
|
257
|
+
error: "Inline editing is only supported for SELECT queries."
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
if (tables.length === 0) {
|
|
261
|
+
return {
|
|
262
|
+
error: "Could not find any tables in the query."
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
const selectColumns = [];
|
|
266
|
+
if (ast.columns) {
|
|
267
|
+
for (const col of ast.columns) {
|
|
268
|
+
if (col.expr?.type === "ref") {
|
|
269
|
+
selectColumns.push({
|
|
270
|
+
table: col.expr.table?.name,
|
|
271
|
+
column: col.expr.name,
|
|
272
|
+
alias: col.alias?.name
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const resolvedColumn = selectColumns.find((sc) => sc.alias === columnKey || !sc.alias && sc.column === columnKey);
|
|
278
|
+
const actualDbColumnName = resolvedColumn?.column ?? columnKey;
|
|
279
|
+
const columnTableRef = resolvedColumn?.table;
|
|
280
|
+
let resolvedTableName = null;
|
|
281
|
+
if (tables.length === 1) {
|
|
282
|
+
resolvedTableName = tables[0].name;
|
|
283
|
+
} else {
|
|
284
|
+
if (columnTableRef) {
|
|
285
|
+
const matchedTable = tables.find((t) => t.alias === columnTableRef || t.name === columnTableRef);
|
|
286
|
+
if (matchedTable) {
|
|
287
|
+
resolvedTableName = matchedTable.name;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (!resolvedTableName) {
|
|
291
|
+
const matchedTables = tables.filter((t) => {
|
|
292
|
+
for (const schema of Object.values(schemas)) {
|
|
293
|
+
const tableInfo = schema.find((ti) => ti.tableName === t.name);
|
|
294
|
+
if (tableInfo && tableInfo.columns.some((c2) => c2.name === actualDbColumnName)) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return false;
|
|
299
|
+
});
|
|
300
|
+
if (matchedTables.length === 1) {
|
|
301
|
+
resolvedTableName = matchedTables[0].name;
|
|
302
|
+
} else if (matchedTables.length > 1) {
|
|
303
|
+
return {
|
|
304
|
+
error: `Ambiguous column "${columnKey}": Found in multiple queried tables.`
|
|
305
|
+
};
|
|
306
|
+
} else {
|
|
307
|
+
return {
|
|
308
|
+
error: `Could not find column "${columnKey}" in the queried tables.`
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (!resolvedTableName) {
|
|
314
|
+
return {
|
|
315
|
+
error: "Could not resolve the target table."
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
let pkDbColumns = [];
|
|
319
|
+
for (const schema of Object.values(schemas)) {
|
|
320
|
+
const tableInfo = schema.find((t) => t.tableName === resolvedTableName);
|
|
321
|
+
if (tableInfo) {
|
|
322
|
+
pkDbColumns = tableInfo.columns.filter((c2) => c2.isPrimaryKey).map((c2) => c2.name);
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (pkDbColumns.length === 0) {
|
|
327
|
+
return {
|
|
328
|
+
error: `Table "${resolvedTableName}" has no primary key defined.`
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
const tableEntry = tables.find((t) => t.name === resolvedTableName);
|
|
332
|
+
const tableAlias = tableEntry?.alias;
|
|
333
|
+
const primaryKeys = pkDbColumns.map((dbCol) => {
|
|
334
|
+
const selectCol = selectColumns.find((sc) => sc.column === dbCol && (!sc.table || sc.table === (tableAlias || resolvedTableName)));
|
|
335
|
+
return {
|
|
336
|
+
dbColumn: dbCol,
|
|
337
|
+
resultColumn: selectCol?.alias || selectCol?.column || dbCol
|
|
338
|
+
};
|
|
339
|
+
});
|
|
340
|
+
return {
|
|
341
|
+
tableName: resolvedTableName,
|
|
342
|
+
primaryKeys
|
|
343
|
+
};
|
|
344
|
+
} catch (e) {
|
|
345
|
+
console.warn("Failed to parse SQL AST:", e);
|
|
346
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
347
|
+
return {
|
|
348
|
+
error: `Could not safely parse query for inline editing: ${message}`
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const ExplainVisualizer = (t0) => {
|
|
353
|
+
const $ = c(77);
|
|
354
|
+
const {
|
|
355
|
+
plan,
|
|
356
|
+
isRoot: t1
|
|
357
|
+
} = t0;
|
|
358
|
+
const isRoot = t1 === void 0 ? true : t1;
|
|
359
|
+
const [expanded, setExpanded] = useState(true);
|
|
360
|
+
const hasChildren = plan.Plans && plan.Plans.length > 0;
|
|
361
|
+
const cost = plan["Total Cost"];
|
|
362
|
+
let costColor = "text-green-500 dark:text-green-400";
|
|
363
|
+
if (cost > 1e3) {
|
|
364
|
+
costColor = "text-red-500 dark:text-red-400";
|
|
365
|
+
} else {
|
|
366
|
+
if (cost > 100) {
|
|
367
|
+
costColor = "text-orange-500 dark:text-orange-400";
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const t2 = isRoot ? "p-4" : "pl-6 mt-2 relative";
|
|
371
|
+
let t3;
|
|
372
|
+
if ($[0] !== t2) {
|
|
373
|
+
t3 = cls("flex flex-col", t2);
|
|
374
|
+
$[0] = t2;
|
|
375
|
+
$[1] = t3;
|
|
376
|
+
} else {
|
|
377
|
+
t3 = $[1];
|
|
378
|
+
}
|
|
379
|
+
let t4;
|
|
380
|
+
if ($[2] !== isRoot) {
|
|
381
|
+
t4 = !isRoot && /* @__PURE__ */ jsx("div", { className: "absolute left-2.5 top-0 bottom-0 w-px bg-surface-200 dark:bg-surface-950 -z-10" });
|
|
382
|
+
$[2] = isRoot;
|
|
383
|
+
$[3] = t4;
|
|
384
|
+
} else {
|
|
385
|
+
t4 = $[3];
|
|
386
|
+
}
|
|
387
|
+
let t5;
|
|
388
|
+
if ($[4] !== isRoot) {
|
|
389
|
+
t5 = !isRoot && /* @__PURE__ */ jsx("div", { className: "absolute left-2.5 top-5 w-3 h-px bg-surface-200 dark:bg-surface-950 -z-10" });
|
|
390
|
+
$[4] = isRoot;
|
|
391
|
+
$[5] = t5;
|
|
392
|
+
} else {
|
|
393
|
+
t5 = $[5];
|
|
394
|
+
}
|
|
395
|
+
let t6;
|
|
396
|
+
let t7;
|
|
397
|
+
if ($[6] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
398
|
+
t6 = cls("border rounded-md bg-white dark:bg-surface-900 text-text-primary dark:text-text-primary-dark shadow-xs relative z-10 w-[420px] max-w-full", defaultBorderMixin);
|
|
399
|
+
t7 = cls("px-4 py-3 flex justify-between items-center cursor-pointer select-none");
|
|
400
|
+
$[6] = t6;
|
|
401
|
+
$[7] = t7;
|
|
402
|
+
} else {
|
|
403
|
+
t6 = $[6];
|
|
404
|
+
t7 = $[7];
|
|
405
|
+
}
|
|
406
|
+
let t8;
|
|
407
|
+
if ($[8] !== expanded) {
|
|
408
|
+
t8 = () => setExpanded(!expanded);
|
|
409
|
+
$[8] = expanded;
|
|
410
|
+
$[9] = t8;
|
|
411
|
+
} else {
|
|
412
|
+
t8 = $[9];
|
|
413
|
+
}
|
|
414
|
+
let t9;
|
|
415
|
+
if ($[10] !== expanded || $[11] !== hasChildren) {
|
|
416
|
+
t9 = hasChildren ? /* @__PURE__ */ jsx("svg", { className: cls("w-4 h-4 text-text-secondary transition-transform", !expanded ? "-rotate-90" : ""), fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) }) : /* @__PURE__ */ jsx("div", { className: "w-4 h-4" });
|
|
417
|
+
$[10] = expanded;
|
|
418
|
+
$[11] = hasChildren;
|
|
419
|
+
$[12] = t9;
|
|
420
|
+
} else {
|
|
421
|
+
t9 = $[12];
|
|
422
|
+
}
|
|
423
|
+
const t10 = plan["Node Type"];
|
|
424
|
+
let t11;
|
|
425
|
+
if ($[13] !== t10) {
|
|
426
|
+
t11 = /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "flex items-center", children: t10 });
|
|
427
|
+
$[13] = t10;
|
|
428
|
+
$[14] = t11;
|
|
429
|
+
} else {
|
|
430
|
+
t11 = $[14];
|
|
431
|
+
}
|
|
432
|
+
let t12;
|
|
433
|
+
if ($[15] !== plan) {
|
|
434
|
+
t12 = plan["Relation Name"] && /* @__PURE__ */ jsxs("span", { className: "font-mono text-[11px] text-text-secondary dark:text-text-secondary-dark px-2 py-0.5 rounded bg-surface-200 dark:bg-surface-950 ml-2", children: [
|
|
435
|
+
"on ",
|
|
436
|
+
plan["Relation Name"],
|
|
437
|
+
" ",
|
|
438
|
+
plan.Alias && plan.Alias !== plan["Relation Name"] ? `(${plan.Alias})` : ""
|
|
439
|
+
] });
|
|
440
|
+
$[15] = plan;
|
|
441
|
+
$[16] = t12;
|
|
442
|
+
} else {
|
|
443
|
+
t12 = $[16];
|
|
444
|
+
}
|
|
445
|
+
let t13;
|
|
446
|
+
if ($[17] !== t11 || $[18] !== t12 || $[19] !== t9) {
|
|
447
|
+
t13 = /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
448
|
+
t9,
|
|
449
|
+
t11,
|
|
450
|
+
t12
|
|
451
|
+
] });
|
|
452
|
+
$[17] = t11;
|
|
453
|
+
$[18] = t12;
|
|
454
|
+
$[19] = t9;
|
|
455
|
+
$[20] = t13;
|
|
456
|
+
} else {
|
|
457
|
+
t13 = $[20];
|
|
458
|
+
}
|
|
459
|
+
let t14;
|
|
460
|
+
if ($[21] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
461
|
+
t14 = /* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase text-text-disabled dark:text-text-disabled-dark font-semibold tracking-wide leading-tight mb-0.5", children: "Cost" });
|
|
462
|
+
$[21] = t14;
|
|
463
|
+
} else {
|
|
464
|
+
t14 = $[21];
|
|
465
|
+
}
|
|
466
|
+
let t15;
|
|
467
|
+
if ($[22] !== costColor) {
|
|
468
|
+
t15 = cls("font-mono font-medium text-[13px] leading-none", costColor);
|
|
469
|
+
$[22] = costColor;
|
|
470
|
+
$[23] = t15;
|
|
471
|
+
} else {
|
|
472
|
+
t15 = $[23];
|
|
473
|
+
}
|
|
474
|
+
let t16;
|
|
475
|
+
if ($[24] !== cost) {
|
|
476
|
+
t16 = cost.toFixed(2);
|
|
477
|
+
$[24] = cost;
|
|
478
|
+
$[25] = t16;
|
|
479
|
+
} else {
|
|
480
|
+
t16 = $[25];
|
|
481
|
+
}
|
|
482
|
+
let t17;
|
|
483
|
+
if ($[26] !== t15 || $[27] !== t16) {
|
|
484
|
+
t17 = /* @__PURE__ */ jsxs("div", { className: "flex flex-col text-right", children: [
|
|
485
|
+
t14,
|
|
486
|
+
/* @__PURE__ */ jsx("span", { className: t15, children: t16 })
|
|
487
|
+
] });
|
|
488
|
+
$[26] = t15;
|
|
489
|
+
$[27] = t16;
|
|
490
|
+
$[28] = t17;
|
|
491
|
+
} else {
|
|
492
|
+
t17 = $[28];
|
|
493
|
+
}
|
|
494
|
+
let t18;
|
|
495
|
+
if ($[29] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
496
|
+
t18 = /* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase text-text-disabled dark:text-text-disabled-dark font-semibold tracking-wide leading-tight mb-0.5", children: "Rows" });
|
|
497
|
+
$[29] = t18;
|
|
498
|
+
} else {
|
|
499
|
+
t18 = $[29];
|
|
500
|
+
}
|
|
501
|
+
const t19 = plan["Plan Rows"];
|
|
502
|
+
let t20;
|
|
503
|
+
if ($[30] !== t19) {
|
|
504
|
+
t20 = /* @__PURE__ */ jsxs("div", { className: "flex flex-col text-right", children: [
|
|
505
|
+
t18,
|
|
506
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[13px] text-text-secondary dark:text-text-secondary-dark leading-none", children: t19 })
|
|
507
|
+
] });
|
|
508
|
+
$[30] = t19;
|
|
509
|
+
$[31] = t20;
|
|
510
|
+
} else {
|
|
511
|
+
t20 = $[31];
|
|
512
|
+
}
|
|
513
|
+
let t21;
|
|
514
|
+
if ($[32] !== t17 || $[33] !== t20) {
|
|
515
|
+
t21 = /* @__PURE__ */ jsxs("div", { className: "flex space-x-6 items-center", children: [
|
|
516
|
+
t17,
|
|
517
|
+
t20
|
|
518
|
+
] });
|
|
519
|
+
$[32] = t17;
|
|
520
|
+
$[33] = t20;
|
|
521
|
+
$[34] = t21;
|
|
522
|
+
} else {
|
|
523
|
+
t21 = $[34];
|
|
524
|
+
}
|
|
525
|
+
let t22;
|
|
526
|
+
if ($[35] !== t13 || $[36] !== t21 || $[37] !== t8) {
|
|
527
|
+
t22 = /* @__PURE__ */ jsxs("div", { className: t7, onClick: t8, children: [
|
|
528
|
+
t13,
|
|
529
|
+
t21
|
|
530
|
+
] });
|
|
531
|
+
$[35] = t13;
|
|
532
|
+
$[36] = t21;
|
|
533
|
+
$[37] = t8;
|
|
534
|
+
$[38] = t22;
|
|
535
|
+
} else {
|
|
536
|
+
t22 = $[38];
|
|
537
|
+
}
|
|
538
|
+
let t23;
|
|
539
|
+
if ($[39] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
540
|
+
t23 = /* @__PURE__ */ jsx("span", { className: "text-text-disabled dark:text-text-disabled-dark", children: "Startup Cost:" });
|
|
541
|
+
$[39] = t23;
|
|
542
|
+
} else {
|
|
543
|
+
t23 = $[39];
|
|
544
|
+
}
|
|
545
|
+
const t24 = plan["Startup Cost"];
|
|
546
|
+
let t25;
|
|
547
|
+
if ($[40] !== t24) {
|
|
548
|
+
t25 = t24.toFixed(2);
|
|
549
|
+
$[40] = t24;
|
|
550
|
+
$[41] = t25;
|
|
551
|
+
} else {
|
|
552
|
+
t25 = $[41];
|
|
553
|
+
}
|
|
554
|
+
let t26;
|
|
555
|
+
if ($[42] !== t25) {
|
|
556
|
+
t26 = /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
557
|
+
t23,
|
|
558
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: t25 })
|
|
559
|
+
] });
|
|
560
|
+
$[42] = t25;
|
|
561
|
+
$[43] = t26;
|
|
562
|
+
} else {
|
|
563
|
+
t26 = $[43];
|
|
564
|
+
}
|
|
565
|
+
let t27;
|
|
566
|
+
if ($[44] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
567
|
+
t27 = /* @__PURE__ */ jsx("span", { className: "text-text-disabled dark:text-text-disabled-dark", children: "Width:" });
|
|
568
|
+
$[44] = t27;
|
|
569
|
+
} else {
|
|
570
|
+
t27 = $[44];
|
|
571
|
+
}
|
|
572
|
+
const t28 = plan["Plan Width"];
|
|
573
|
+
let t29;
|
|
574
|
+
if ($[45] !== t28) {
|
|
575
|
+
t29 = /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
576
|
+
t27,
|
|
577
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono font-medium", children: [
|
|
578
|
+
t28,
|
|
579
|
+
" bytes"
|
|
580
|
+
] })
|
|
581
|
+
] });
|
|
582
|
+
$[45] = t28;
|
|
583
|
+
$[46] = t29;
|
|
584
|
+
} else {
|
|
585
|
+
t29 = $[46];
|
|
586
|
+
}
|
|
587
|
+
let t30;
|
|
588
|
+
if ($[47] !== t26 || $[48] !== t29) {
|
|
589
|
+
t30 = /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-6", children: [
|
|
590
|
+
t26,
|
|
591
|
+
t29
|
|
592
|
+
] });
|
|
593
|
+
$[47] = t26;
|
|
594
|
+
$[48] = t29;
|
|
595
|
+
$[49] = t30;
|
|
596
|
+
} else {
|
|
597
|
+
t30 = $[49];
|
|
598
|
+
}
|
|
599
|
+
let t31;
|
|
600
|
+
if ($[50] !== plan.Filter) {
|
|
601
|
+
t31 = plan.Filter && /* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
|
|
602
|
+
/* @__PURE__ */ jsx("span", { className: "text-text-disabled dark:text-text-disabled-dark block mb-1", children: "Filter:" }),
|
|
603
|
+
/* @__PURE__ */ jsx("code", { className: "block w-full p-2 bg-surface-50 dark:bg-surface-950 border dark:border-surface-950 rounded font-mono text-[12px] truncate", children: plan.Filter })
|
|
604
|
+
] });
|
|
605
|
+
$[50] = plan.Filter;
|
|
606
|
+
$[51] = t31;
|
|
607
|
+
} else {
|
|
608
|
+
t31 = $[51];
|
|
609
|
+
}
|
|
610
|
+
let t32;
|
|
611
|
+
if ($[52] !== plan) {
|
|
612
|
+
t32 = plan["Index Cond"] && /* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
|
|
613
|
+
/* @__PURE__ */ jsx("span", { className: "text-text-disabled dark:text-text-disabled-dark block mb-1", children: "Index Cond:" }),
|
|
614
|
+
/* @__PURE__ */ jsx("code", { className: "block w-full p-2 bg-surface-50 dark:bg-surface-950 border dark:border-surface-950 rounded font-mono text-[12px] truncate", children: plan["Index Cond"] })
|
|
615
|
+
] });
|
|
616
|
+
$[52] = plan;
|
|
617
|
+
$[53] = t32;
|
|
618
|
+
} else {
|
|
619
|
+
t32 = $[53];
|
|
620
|
+
}
|
|
621
|
+
let t33;
|
|
622
|
+
if ($[54] !== plan) {
|
|
623
|
+
t33 = plan["Hash Cond"] && /* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
|
|
624
|
+
/* @__PURE__ */ jsx("span", { className: "text-text-disabled dark:text-text-disabled-dark block mb-1", children: "Hash Cond:" }),
|
|
625
|
+
/* @__PURE__ */ jsx("code", { className: "block w-full p-2 bg-surface-50 dark:bg-surface-950 border dark:border-surface-950 rounded font-mono text-[12px] truncate", children: plan["Hash Cond"] })
|
|
626
|
+
] });
|
|
627
|
+
$[54] = plan;
|
|
628
|
+
$[55] = t33;
|
|
629
|
+
} else {
|
|
630
|
+
t33 = $[55];
|
|
631
|
+
}
|
|
632
|
+
let t34;
|
|
633
|
+
if ($[56] !== t30 || $[57] !== t31 || $[58] !== t32 || $[59] !== t33) {
|
|
634
|
+
t34 = /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 border-t border-surface-200 dark:border-surface-950 text-[13px] flex flex-col gap-2", children: [
|
|
635
|
+
t30,
|
|
636
|
+
t31,
|
|
637
|
+
t32,
|
|
638
|
+
t33
|
|
639
|
+
] });
|
|
640
|
+
$[56] = t30;
|
|
641
|
+
$[57] = t31;
|
|
642
|
+
$[58] = t32;
|
|
643
|
+
$[59] = t33;
|
|
644
|
+
$[60] = t34;
|
|
645
|
+
} else {
|
|
646
|
+
t34 = $[60];
|
|
647
|
+
}
|
|
648
|
+
let t35;
|
|
649
|
+
if ($[61] !== expanded || $[62] !== t34) {
|
|
650
|
+
t35 = /* @__PURE__ */ jsx(Collapse, { in: expanded, children: t34 });
|
|
651
|
+
$[61] = expanded;
|
|
652
|
+
$[62] = t34;
|
|
653
|
+
$[63] = t35;
|
|
654
|
+
} else {
|
|
655
|
+
t35 = $[63];
|
|
656
|
+
}
|
|
657
|
+
let t36;
|
|
658
|
+
if ($[64] !== t22 || $[65] !== t35) {
|
|
659
|
+
t36 = /* @__PURE__ */ jsxs("div", { className: t6, children: [
|
|
660
|
+
t22,
|
|
661
|
+
t35
|
|
662
|
+
] });
|
|
663
|
+
$[64] = t22;
|
|
664
|
+
$[65] = t35;
|
|
665
|
+
$[66] = t36;
|
|
666
|
+
} else {
|
|
667
|
+
t36 = $[66];
|
|
668
|
+
}
|
|
669
|
+
let t37;
|
|
670
|
+
if ($[67] !== expanded || $[68] !== hasChildren || $[69] !== plan.Plans) {
|
|
671
|
+
t37 = expanded && hasChildren && /* @__PURE__ */ jsx("div", { className: "flex flex-col space-y-2 mt-[-4px]", children: plan.Plans.map(_temp) });
|
|
672
|
+
$[67] = expanded;
|
|
673
|
+
$[68] = hasChildren;
|
|
674
|
+
$[69] = plan.Plans;
|
|
675
|
+
$[70] = t37;
|
|
676
|
+
} else {
|
|
677
|
+
t37 = $[70];
|
|
678
|
+
}
|
|
679
|
+
let t38;
|
|
680
|
+
if ($[71] !== t3 || $[72] !== t36 || $[73] !== t37 || $[74] !== t4 || $[75] !== t5) {
|
|
681
|
+
t38 = /* @__PURE__ */ jsxs("div", { className: t3, children: [
|
|
682
|
+
t4,
|
|
683
|
+
t5,
|
|
684
|
+
t36,
|
|
685
|
+
t37
|
|
686
|
+
] });
|
|
687
|
+
$[71] = t3;
|
|
688
|
+
$[72] = t36;
|
|
689
|
+
$[73] = t37;
|
|
690
|
+
$[74] = t4;
|
|
691
|
+
$[75] = t5;
|
|
692
|
+
$[76] = t38;
|
|
693
|
+
} else {
|
|
694
|
+
t38 = $[76];
|
|
695
|
+
}
|
|
696
|
+
return t38;
|
|
697
|
+
};
|
|
698
|
+
function _temp(childPlan, idx) {
|
|
699
|
+
return /* @__PURE__ */ jsx(ExplainVisualizer, { plan: childPlan, isRoot: false }, idx);
|
|
700
|
+
}
|
|
701
|
+
let VirtualTableInput = null;
|
|
702
|
+
try {
|
|
703
|
+
const cms = require("@rebasepro/admin");
|
|
704
|
+
VirtualTableInput = cms.VirtualTableInput;
|
|
705
|
+
} catch {
|
|
706
|
+
}
|
|
707
|
+
const STORAGE_KEY_TABS = "rebase_sql_tabs";
|
|
708
|
+
const STORAGE_KEY_ACTIVE_TAB = "rebase_sql_active_tab";
|
|
709
|
+
const FixedEditorOverlay = (t0) => {
|
|
710
|
+
const $ = c(29);
|
|
711
|
+
const {
|
|
712
|
+
displayValue,
|
|
713
|
+
onSave,
|
|
714
|
+
onCancel
|
|
715
|
+
} = t0;
|
|
716
|
+
const [rect, setRect] = useState(null);
|
|
717
|
+
let t1;
|
|
718
|
+
if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
719
|
+
t1 = {
|
|
720
|
+
width: 1e3,
|
|
721
|
+
height: 1e3
|
|
722
|
+
};
|
|
723
|
+
$[0] = t1;
|
|
724
|
+
} else {
|
|
725
|
+
t1 = $[0];
|
|
726
|
+
}
|
|
727
|
+
const [windowSize, setWindowSize] = useState(t1);
|
|
728
|
+
const anchorRef = useRef(null);
|
|
729
|
+
let t2;
|
|
730
|
+
let t3;
|
|
731
|
+
if ($[1] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
732
|
+
t2 = () => {
|
|
733
|
+
if (anchorRef.current && anchorRef.current.parentElement) {
|
|
734
|
+
setRect(anchorRef.current.parentElement.getBoundingClientRect());
|
|
735
|
+
}
|
|
736
|
+
if (typeof window !== "undefined") {
|
|
737
|
+
setWindowSize({
|
|
738
|
+
width: window.innerWidth,
|
|
739
|
+
height: window.innerHeight
|
|
740
|
+
});
|
|
741
|
+
const handleResize = () => setWindowSize({
|
|
742
|
+
width: window.innerWidth,
|
|
743
|
+
height: window.innerHeight
|
|
744
|
+
});
|
|
745
|
+
window.addEventListener("resize", handleResize);
|
|
746
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
t3 = [];
|
|
750
|
+
$[1] = t2;
|
|
751
|
+
$[2] = t3;
|
|
752
|
+
} else {
|
|
753
|
+
t2 = $[1];
|
|
754
|
+
t3 = $[2];
|
|
755
|
+
}
|
|
756
|
+
useEffect(t2, t3);
|
|
757
|
+
if (!rect) {
|
|
758
|
+
let t42;
|
|
759
|
+
if ($[3] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
760
|
+
t42 = /* @__PURE__ */ jsx("div", { ref: anchorRef, className: "w-full h-full min-h-[20px]" });
|
|
761
|
+
$[3] = t42;
|
|
762
|
+
} else {
|
|
763
|
+
t42 = $[3];
|
|
764
|
+
}
|
|
765
|
+
return t42;
|
|
766
|
+
}
|
|
767
|
+
let top = rect.top - 2;
|
|
768
|
+
let left = rect.left - 2;
|
|
769
|
+
const minWidth = Math.max(rect.width + 4, 250);
|
|
770
|
+
const minHeight = rect.height + 4;
|
|
771
|
+
if (left + minWidth > windowSize.width) {
|
|
772
|
+
left = Math.max(10, windowSize.width - minWidth - 10);
|
|
773
|
+
}
|
|
774
|
+
const maxAvailableHeight = Math.max(50, windowSize.height - top - 10);
|
|
775
|
+
const resolvedMaxHeight = Math.min(300, maxAvailableHeight);
|
|
776
|
+
if (top + minHeight > windowSize.height) {
|
|
777
|
+
top = Math.max(10, windowSize.height - minHeight - 10);
|
|
778
|
+
}
|
|
779
|
+
const t4 = top;
|
|
780
|
+
const t5 = left;
|
|
781
|
+
const t6 = Math.min(400, windowSize.width - left - 10);
|
|
782
|
+
let t7;
|
|
783
|
+
if ($[4] !== left || $[5] !== minHeight || $[6] !== minWidth || $[7] !== t6 || $[8] !== top) {
|
|
784
|
+
t7 = {
|
|
785
|
+
top: t4,
|
|
786
|
+
left: t5,
|
|
787
|
+
minWidth,
|
|
788
|
+
minHeight,
|
|
789
|
+
maxWidth: t6
|
|
790
|
+
};
|
|
791
|
+
$[4] = left;
|
|
792
|
+
$[5] = minHeight;
|
|
793
|
+
$[6] = minWidth;
|
|
794
|
+
$[7] = t6;
|
|
795
|
+
$[8] = top;
|
|
796
|
+
$[9] = t7;
|
|
797
|
+
} else {
|
|
798
|
+
t7 = $[9];
|
|
799
|
+
}
|
|
800
|
+
let t8;
|
|
801
|
+
if ($[10] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
802
|
+
t8 = {
|
|
803
|
+
minHeight: "32px"
|
|
804
|
+
};
|
|
805
|
+
$[10] = t8;
|
|
806
|
+
} else {
|
|
807
|
+
t8 = $[10];
|
|
808
|
+
}
|
|
809
|
+
let t10;
|
|
810
|
+
let t9;
|
|
811
|
+
if ($[11] !== resolvedMaxHeight) {
|
|
812
|
+
t9 = (e) => {
|
|
813
|
+
const val = e.target.value;
|
|
814
|
+
e.target.value = "";
|
|
815
|
+
e.target.value = val;
|
|
816
|
+
e.target.style.height = "auto";
|
|
817
|
+
e.target.style.height = `${Math.min(e.target.scrollHeight, resolvedMaxHeight)}px`;
|
|
818
|
+
};
|
|
819
|
+
t10 = (e_0) => {
|
|
820
|
+
e_0.target.style.height = "auto";
|
|
821
|
+
e_0.target.style.height = `${Math.min(e_0.target.scrollHeight, resolvedMaxHeight)}px`;
|
|
822
|
+
};
|
|
823
|
+
$[11] = resolvedMaxHeight;
|
|
824
|
+
$[12] = t10;
|
|
825
|
+
$[13] = t9;
|
|
826
|
+
} else {
|
|
827
|
+
t10 = $[12];
|
|
828
|
+
t9 = $[13];
|
|
829
|
+
}
|
|
830
|
+
let t11;
|
|
831
|
+
let t12;
|
|
832
|
+
if ($[14] !== onCancel || $[15] !== onSave) {
|
|
833
|
+
t11 = (e_1) => {
|
|
834
|
+
onSave(e_1.target.value || null);
|
|
835
|
+
onCancel();
|
|
836
|
+
};
|
|
837
|
+
t12 = (e_2) => {
|
|
838
|
+
if (e_2.key === "Enter" && !e_2.shiftKey) {
|
|
839
|
+
e_2.preventDefault();
|
|
840
|
+
onSave(e_2.currentTarget.value || null);
|
|
841
|
+
onCancel();
|
|
842
|
+
}
|
|
843
|
+
if (e_2.key === "Escape") {
|
|
844
|
+
onCancel();
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
$[14] = onCancel;
|
|
848
|
+
$[15] = onSave;
|
|
849
|
+
$[16] = t11;
|
|
850
|
+
$[17] = t12;
|
|
851
|
+
} else {
|
|
852
|
+
t11 = $[16];
|
|
853
|
+
t12 = $[17];
|
|
854
|
+
}
|
|
855
|
+
let t13;
|
|
856
|
+
if ($[18] !== displayValue || $[19] !== t10 || $[20] !== t11 || $[21] !== t12 || $[22] !== t9) {
|
|
857
|
+
t13 = /* @__PURE__ */ jsx("textarea", { className: "w-full h-full bg-transparent outline-none border-none ring-0 font-mono text-[13px] text-text-primary dark:text-text-primary-dark px-4 py-1.5 resize-none overflow-y-auto", defaultValue: displayValue, autoFocus: true, style: t8, onFocus: t9, onChange: t10, onBlur: t11, onKeyDown: t12 });
|
|
858
|
+
$[18] = displayValue;
|
|
859
|
+
$[19] = t10;
|
|
860
|
+
$[20] = t11;
|
|
861
|
+
$[21] = t12;
|
|
862
|
+
$[22] = t9;
|
|
863
|
+
$[23] = t13;
|
|
864
|
+
} else {
|
|
865
|
+
t13 = $[23];
|
|
866
|
+
}
|
|
867
|
+
let t14;
|
|
868
|
+
if ($[24] !== t13 || $[25] !== t7) {
|
|
869
|
+
t14 = createPortal(/* @__PURE__ */ jsx("div", { className: "fixed z-[9999] bg-surface-50 dark:bg-surface-900 border-2 border-primary dark:border-primary-dark shadow-xl flex flex-col", style: t7, children: t13 }), document.body);
|
|
870
|
+
$[24] = t13;
|
|
871
|
+
$[25] = t7;
|
|
872
|
+
$[26] = t14;
|
|
873
|
+
} else {
|
|
874
|
+
t14 = $[26];
|
|
875
|
+
}
|
|
876
|
+
let t15;
|
|
877
|
+
if ($[27] !== t14) {
|
|
878
|
+
t15 = /* @__PURE__ */ jsx("div", { ref: anchorRef, className: "w-full h-full min-h-[20px]", children: t14 });
|
|
879
|
+
$[27] = t14;
|
|
880
|
+
$[28] = t15;
|
|
881
|
+
} else {
|
|
882
|
+
t15 = $[28];
|
|
883
|
+
}
|
|
884
|
+
return t15;
|
|
885
|
+
};
|
|
886
|
+
const SQLEditor = () => {
|
|
887
|
+
const {
|
|
888
|
+
databaseAdmin
|
|
889
|
+
} = useRebaseContext();
|
|
890
|
+
const sideEntityController = useStudioSideEntityController();
|
|
891
|
+
const snackbarController = useSnackbarController();
|
|
892
|
+
const collectionRegistry = useStudioCollectionRegistry();
|
|
893
|
+
const {
|
|
894
|
+
t
|
|
895
|
+
} = useTranslation();
|
|
896
|
+
const [schemas, setSchemas] = useState({});
|
|
897
|
+
const [isSchemaLoading, setIsSchemaLoading] = useState(true);
|
|
898
|
+
const schemaFetchedRef = useRef(false);
|
|
899
|
+
const [schemaError, setSchemaError] = useState(null);
|
|
900
|
+
const [selectedDatabase, setSelectedDatabase] = useState(() => {
|
|
901
|
+
return localStorage.getItem("rebase_sql_selected_db") || void 0;
|
|
902
|
+
});
|
|
903
|
+
const [selectedRole, setSelectedRole] = useState(() => {
|
|
904
|
+
return localStorage.getItem("rebase_sql_selected_role") || void 0;
|
|
905
|
+
});
|
|
906
|
+
const [availableDatabases, setAvailableDatabases] = useState([]);
|
|
907
|
+
const [availableRoles, setAvailableRoles] = useState([]);
|
|
908
|
+
const [isLoadingConfig, setIsLoadingConfig] = useState(true);
|
|
909
|
+
const [connectionConfigError, setConnectionConfigError] = useState(null);
|
|
910
|
+
const [tabs, setTabs] = useState(() => {
|
|
911
|
+
const saved = localStorage.getItem(STORAGE_KEY_TABS);
|
|
912
|
+
if (saved) {
|
|
913
|
+
const parsed = JSON.parse(saved);
|
|
914
|
+
return parsed.map((t_0) => ({
|
|
915
|
+
...t_0,
|
|
916
|
+
results: null,
|
|
917
|
+
loading: false,
|
|
918
|
+
error: null,
|
|
919
|
+
execTime: null,
|
|
920
|
+
lastExecutedSql: null
|
|
921
|
+
}));
|
|
922
|
+
}
|
|
923
|
+
return [{
|
|
924
|
+
id: "1",
|
|
925
|
+
name: "Query 1",
|
|
926
|
+
sql: "SELECT * FROM ",
|
|
927
|
+
database: localStorage.getItem("rebase_sql_selected_db") || void 0,
|
|
928
|
+
role: localStorage.getItem("rebase_sql_selected_role") || void 0,
|
|
929
|
+
results: null,
|
|
930
|
+
loading: false,
|
|
931
|
+
error: null,
|
|
932
|
+
execTime: null,
|
|
933
|
+
lastExecutedSql: null
|
|
934
|
+
}];
|
|
935
|
+
});
|
|
936
|
+
const [activeTabId, setActiveTabId] = useState(() => {
|
|
937
|
+
return localStorage.getItem(STORAGE_KEY_ACTIVE_TAB) || "1";
|
|
938
|
+
});
|
|
939
|
+
const activeTab = tabs.find((t_1) => t_1.id === activeTabId) || tabs[0];
|
|
940
|
+
const updateActiveTab = useCallback((update) => {
|
|
941
|
+
setTabs((prev) => prev.map((t_2) => t_2.id === activeTabId ? {
|
|
942
|
+
...t_2,
|
|
943
|
+
...update
|
|
944
|
+
} : t_2));
|
|
945
|
+
}, [activeTabId]);
|
|
946
|
+
const sql = activeTab.sql;
|
|
947
|
+
const results = activeTab.results;
|
|
948
|
+
const loading = activeTab.loading;
|
|
949
|
+
const error = activeTab.error;
|
|
950
|
+
const execTime = activeTab.execTime;
|
|
951
|
+
const setSql = (newSql) => updateActiveTab({
|
|
952
|
+
sql: newSql
|
|
953
|
+
});
|
|
954
|
+
useEffect(() => {
|
|
955
|
+
let mounted = true;
|
|
956
|
+
const fetchConnectionConfig = async () => {
|
|
957
|
+
if (!databaseAdmin?.fetchAvailableDatabases || !databaseAdmin?.fetchAvailableRoles) {
|
|
958
|
+
setConnectionConfigError(t("studio_sql_sql_not_supported"));
|
|
959
|
+
setIsLoadingConfig(false);
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
try {
|
|
963
|
+
const [dbs, roles, currentDbFromApi] = await Promise.all([databaseAdmin.fetchAvailableDatabases(), databaseAdmin.fetchAvailableRoles(), typeof databaseAdmin?.fetchCurrentDatabase === "function" ? databaseAdmin.fetchCurrentDatabase() : Promise.resolve(void 0)]);
|
|
964
|
+
if (mounted) {
|
|
965
|
+
setAvailableDatabases(dbs);
|
|
966
|
+
setAvailableRoles(roles);
|
|
967
|
+
const loadedDb = localStorage.getItem("rebase_sql_selected_db") || void 0;
|
|
968
|
+
const loadedRole = localStorage.getItem("rebase_sql_selected_role") || void 0;
|
|
969
|
+
const initialActiveTabId = localStorage.getItem(STORAGE_KEY_ACTIVE_TAB) || "1";
|
|
970
|
+
let initialTabs = [];
|
|
971
|
+
try {
|
|
972
|
+
const savedTabs = localStorage.getItem(STORAGE_KEY_TABS);
|
|
973
|
+
if (savedTabs) initialTabs = JSON.parse(savedTabs);
|
|
974
|
+
} catch (e) {
|
|
975
|
+
}
|
|
976
|
+
const currentActiveTab = initialTabs.find((t_3) => t_3.id === initialActiveTabId);
|
|
977
|
+
let actualDb = currentActiveTab?.database || loadedDb;
|
|
978
|
+
if (actualDb && !dbs.includes(actualDb)) actualDb = void 0;
|
|
979
|
+
if (!actualDb && dbs.length > 0) {
|
|
980
|
+
actualDb = currentDbFromApi && dbs.includes(currentDbFromApi) ? currentDbFromApi : dbs[0];
|
|
981
|
+
}
|
|
982
|
+
if (actualDb) {
|
|
983
|
+
setSelectedDatabase(actualDb);
|
|
984
|
+
localStorage.setItem("rebase_sql_selected_db", actualDb);
|
|
985
|
+
setTabs((prev_0) => prev_0.map((t_4) => t_4.id === initialActiveTabId && !t_4.database ? {
|
|
986
|
+
...t_4,
|
|
987
|
+
database: actualDb
|
|
988
|
+
} : t_4));
|
|
989
|
+
}
|
|
990
|
+
let actualRole = currentActiveTab?.role || loadedRole;
|
|
991
|
+
if (actualRole && !roles.includes(actualRole)) actualRole = void 0;
|
|
992
|
+
if (!actualRole && roles.length > 0) {
|
|
993
|
+
actualRole = roles.includes("postgres") ? "postgres" : roles[0];
|
|
994
|
+
}
|
|
995
|
+
if (actualRole) {
|
|
996
|
+
setSelectedRole(actualRole);
|
|
997
|
+
localStorage.setItem("rebase_sql_selected_role", actualRole);
|
|
998
|
+
setTabs((prev_1) => prev_1.map((t_5) => t_5.id === initialActiveTabId && !t_5.role ? {
|
|
999
|
+
...t_5,
|
|
1000
|
+
role: actualRole
|
|
1001
|
+
} : t_5));
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
} catch (err) {
|
|
1005
|
+
console.error("Failed to fetch databases or roles:", err);
|
|
1006
|
+
if (mounted) {
|
|
1007
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1008
|
+
setConnectionConfigError(t("studio_sql_fetch_error", {
|
|
1009
|
+
message
|
|
1010
|
+
}));
|
|
1011
|
+
}
|
|
1012
|
+
} finally {
|
|
1013
|
+
if (mounted) {
|
|
1014
|
+
setIsLoadingConfig(false);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
fetchConnectionConfig();
|
|
1019
|
+
return () => {
|
|
1020
|
+
mounted = false;
|
|
1021
|
+
};
|
|
1022
|
+
}, [databaseAdmin]);
|
|
1023
|
+
const handleDatabaseChange = (db, tabId) => {
|
|
1024
|
+
setSelectedDatabase(db);
|
|
1025
|
+
localStorage.setItem("rebase_sql_selected_db", db);
|
|
1026
|
+
setTabs((prev_2) => prev_2.map((t_6) => t_6.id === activeTabId ? {
|
|
1027
|
+
...t_6,
|
|
1028
|
+
database: db
|
|
1029
|
+
} : t_6));
|
|
1030
|
+
schemaFetchedRef.current = false;
|
|
1031
|
+
};
|
|
1032
|
+
const handleRoleChange = (role, tabId_0) => {
|
|
1033
|
+
setSelectedRole(role);
|
|
1034
|
+
localStorage.setItem("rebase_sql_selected_role", role);
|
|
1035
|
+
setTabs((prev_3) => prev_3.map((t_7) => t_7.id === activeTabId ? {
|
|
1036
|
+
...t_7,
|
|
1037
|
+
role
|
|
1038
|
+
} : t_7));
|
|
1039
|
+
};
|
|
1040
|
+
const handleTabChange = useCallback((newTabId) => {
|
|
1041
|
+
setActiveTabId(newTabId);
|
|
1042
|
+
const newTab = tabs.find((t_8) => t_8.id === newTabId);
|
|
1043
|
+
if (newTab) {
|
|
1044
|
+
if (newTab.database && newTab.database !== selectedDatabase) {
|
|
1045
|
+
setSelectedDatabase(newTab.database);
|
|
1046
|
+
localStorage.setItem("rebase_sql_selected_db", newTab.database);
|
|
1047
|
+
schemaFetchedRef.current = false;
|
|
1048
|
+
} else if (!newTab.database && selectedDatabase) {
|
|
1049
|
+
setTabs((prev_4) => prev_4.map((t_9) => t_9.id === newTabId ? {
|
|
1050
|
+
...t_9,
|
|
1051
|
+
database: selectedDatabase
|
|
1052
|
+
} : t_9));
|
|
1053
|
+
}
|
|
1054
|
+
if (newTab.role && newTab.role !== selectedRole) {
|
|
1055
|
+
setSelectedRole(newTab.role);
|
|
1056
|
+
localStorage.setItem("rebase_sql_selected_role", newTab.role);
|
|
1057
|
+
} else if (!newTab.role && selectedRole) {
|
|
1058
|
+
setTabs((prev_5) => prev_5.map((t_10) => t_10.id === newTabId ? {
|
|
1059
|
+
...t_10,
|
|
1060
|
+
role: selectedRole
|
|
1061
|
+
} : t_10));
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}, [tabs, selectedDatabase, selectedRole]);
|
|
1065
|
+
const fetchSchema = useCallback(async () => {
|
|
1066
|
+
if (!databaseAdmin?.executeSql) {
|
|
1067
|
+
setSchemaError(t("studio_sql_sql_not_supported"));
|
|
1068
|
+
setIsSchemaLoading(false);
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
setIsSchemaLoading(true);
|
|
1072
|
+
setSchemaError(null);
|
|
1073
|
+
try {
|
|
1074
|
+
const sql_0 = `
|
|
1075
|
+
SELECT
|
|
1076
|
+
c.table_schema as schema,
|
|
1077
|
+
c.table_name as "table",
|
|
1078
|
+
c.column_name as "column",
|
|
1079
|
+
c.data_type as "data_type",
|
|
1080
|
+
CASE WHEN kcu.column_name IS NOT NULL THEN true ELSE false END as "is_pk"
|
|
1081
|
+
FROM
|
|
1082
|
+
information_schema.columns c
|
|
1083
|
+
LEFT JOIN information_schema.table_constraints tc
|
|
1084
|
+
ON tc.table_schema = c.table_schema
|
|
1085
|
+
AND tc.table_name = c.table_name
|
|
1086
|
+
AND tc.constraint_type = 'PRIMARY KEY'
|
|
1087
|
+
LEFT JOIN information_schema.key_column_usage kcu
|
|
1088
|
+
ON kcu.constraint_name = tc.constraint_name
|
|
1089
|
+
AND kcu.table_schema = tc.table_schema
|
|
1090
|
+
AND kcu.table_name = tc.table_name
|
|
1091
|
+
AND kcu.column_name = c.column_name
|
|
1092
|
+
WHERE
|
|
1093
|
+
c.table_schema NOT IN ('information_schema', 'pg_catalog')
|
|
1094
|
+
ORDER BY
|
|
1095
|
+
c.table_schema, c.table_name, c.ordinal_position;
|
|
1096
|
+
`;
|
|
1097
|
+
const result = await databaseAdmin.executeSql(sql_0, {
|
|
1098
|
+
database: selectedDatabase
|
|
1099
|
+
});
|
|
1100
|
+
const processGrouped = (data) => {
|
|
1101
|
+
const grouped = data.reduce((acc, curr) => {
|
|
1102
|
+
const schema = curr.schema || curr.SCHEMA || curr.table_schema || "public";
|
|
1103
|
+
const table = curr.table || curr.TABLE || curr.table_name;
|
|
1104
|
+
const column = curr.column || curr.COLUMN || curr.column_name;
|
|
1105
|
+
const dataType = curr.data_type || curr.DATA_TYPE || "";
|
|
1106
|
+
const isPrimaryKey = curr.is_pk === true || curr.is_pk === "true";
|
|
1107
|
+
if (!acc[schema]) acc[schema] = [];
|
|
1108
|
+
let tableInfo = acc[schema].find((t_11) => t_11.tableName === table);
|
|
1109
|
+
if (!tableInfo) {
|
|
1110
|
+
tableInfo = {
|
|
1111
|
+
schemaName: schema,
|
|
1112
|
+
tableName: table,
|
|
1113
|
+
columns: []
|
|
1114
|
+
};
|
|
1115
|
+
acc[schema].push(tableInfo);
|
|
1116
|
+
}
|
|
1117
|
+
tableInfo.columns.push({
|
|
1118
|
+
name: column,
|
|
1119
|
+
dataType,
|
|
1120
|
+
isPrimaryKey
|
|
1121
|
+
});
|
|
1122
|
+
return acc;
|
|
1123
|
+
}, {});
|
|
1124
|
+
setSchemas(grouped);
|
|
1125
|
+
};
|
|
1126
|
+
if (!result || !Array.isArray(result)) {
|
|
1127
|
+
if (result && typeof result === "object" && "rows" in result && Array.isArray(result.rows)) {
|
|
1128
|
+
processGrouped(result.rows);
|
|
1129
|
+
} else {
|
|
1130
|
+
setSchemaError(t("studio_sql_unexpected_format", {
|
|
1131
|
+
type: typeof result
|
|
1132
|
+
}));
|
|
1133
|
+
setSchemas({});
|
|
1134
|
+
}
|
|
1135
|
+
} else if (result.length === 0) {
|
|
1136
|
+
setSchemas({});
|
|
1137
|
+
setSchemaError(t("studio_sql_no_tables"));
|
|
1138
|
+
} else {
|
|
1139
|
+
processGrouped(result);
|
|
1140
|
+
}
|
|
1141
|
+
schemaFetchedRef.current = true;
|
|
1142
|
+
} catch (e_0) {
|
|
1143
|
+
console.error("Schema fetch error:", e_0);
|
|
1144
|
+
const message_0 = e_0 instanceof Error ? e_0.message : String(e_0);
|
|
1145
|
+
setSchemaError(t("studio_sql_schema_fetch_error", {
|
|
1146
|
+
message: message_0
|
|
1147
|
+
}));
|
|
1148
|
+
} finally {
|
|
1149
|
+
setIsSchemaLoading(false);
|
|
1150
|
+
}
|
|
1151
|
+
}, [databaseAdmin, selectedDatabase]);
|
|
1152
|
+
useEffect(() => {
|
|
1153
|
+
if (!isLoadingConfig && !schemaFetchedRef.current) {
|
|
1154
|
+
fetchSchema();
|
|
1155
|
+
}
|
|
1156
|
+
}, [fetchSchema, isLoadingConfig, selectedDatabase]);
|
|
1157
|
+
const [autoLimit, setAutoLimit] = useState(true);
|
|
1158
|
+
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false);
|
|
1159
|
+
const [pendingAction, setPendingAction] = useState(null);
|
|
1160
|
+
const [editingCell, setEditingCell] = useState(null);
|
|
1161
|
+
const handleDoubleClick = useCallback((rowIndex, columnKey, initialValue, rowData) => {
|
|
1162
|
+
if (!activeTab.lastExecutedSql) {
|
|
1163
|
+
snackbarController.open({
|
|
1164
|
+
type: "error",
|
|
1165
|
+
message: t("studio_sql_cannot_edit_missing_query")
|
|
1166
|
+
});
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
const resolution = determineTableAndPK(activeTab.lastExecutedSql, columnKey, schemas);
|
|
1170
|
+
if (resolution.error || !resolution.primaryKeys || resolution.primaryKeys.length === 0) {
|
|
1171
|
+
snackbarController.open({
|
|
1172
|
+
type: "error",
|
|
1173
|
+
message: resolution.error || t("studio_sql_cannot_resolve_table")
|
|
1174
|
+
});
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
const missingPKs = resolution.primaryKeys.filter((pk) => rowData[pk.resultColumn] === void 0 || rowData[pk.resultColumn] === null);
|
|
1178
|
+
if (missingPKs.length > 0) {
|
|
1179
|
+
snackbarController.open({
|
|
1180
|
+
type: "error",
|
|
1181
|
+
message: t("studio_sql_missing_pk", {
|
|
1182
|
+
columns: missingPKs.map((pk_0) => `"${pk_0.resultColumn}"`).join(", ")
|
|
1183
|
+
})
|
|
1184
|
+
});
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
setEditingCell({
|
|
1188
|
+
rowIndex,
|
|
1189
|
+
columnKey,
|
|
1190
|
+
initialValue
|
|
1191
|
+
});
|
|
1192
|
+
}, [activeTab.lastExecutedSql, schemas, snackbarController]);
|
|
1193
|
+
const handleCellSave = useCallback(async (newValue, rowData_0, columnKey_0, rowIndex_0) => {
|
|
1194
|
+
if (!editingCell || !activeTab.lastExecutedSql) return;
|
|
1195
|
+
setEditingCell(null);
|
|
1196
|
+
if (newValue === editingCell.initialValue) return;
|
|
1197
|
+
const resolution_0 = determineTableAndPK(activeTab.lastExecutedSql, columnKey_0, schemas);
|
|
1198
|
+
if (resolution_0.error || !resolution_0.tableName || !resolution_0.primaryKeys || resolution_0.primaryKeys.length === 0) {
|
|
1199
|
+
snackbarController.open({
|
|
1200
|
+
type: "error",
|
|
1201
|
+
message: resolution_0.error || "Resolution failed."
|
|
1202
|
+
});
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
const tableName = resolution_0.tableName;
|
|
1206
|
+
const formatValue = (val) => {
|
|
1207
|
+
if (val === null || val === void 0) return "NULL";
|
|
1208
|
+
if (typeof val === "number") return val;
|
|
1209
|
+
if (typeof val === "boolean") return val ? "TRUE" : "FALSE";
|
|
1210
|
+
return `'${String(val).replace(/'/g, "''")}'`;
|
|
1211
|
+
};
|
|
1212
|
+
const resolveDbColumnName = (resultColKey) => {
|
|
1213
|
+
try {
|
|
1214
|
+
const ast = parseFirst(activeTab.lastExecutedSql);
|
|
1215
|
+
if (ast.type === "select" && ast.columns) {
|
|
1216
|
+
for (const col of ast.columns) {
|
|
1217
|
+
if (col.expr?.type === "ref") {
|
|
1218
|
+
const alias = col.alias?.name;
|
|
1219
|
+
const colName = col.expr.name;
|
|
1220
|
+
if (alias === resultColKey || !alias && colName === resultColKey) {
|
|
1221
|
+
return colName;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
} catch {
|
|
1227
|
+
}
|
|
1228
|
+
return resultColKey;
|
|
1229
|
+
};
|
|
1230
|
+
const dbColumnName = resolveDbColumnName(columnKey_0);
|
|
1231
|
+
const whereConditions = resolution_0.primaryKeys.map((pk_1) => `"${pk_1.dbColumn}" = ${formatValue(rowData_0[pk_1.resultColumn])}`).join(" AND ");
|
|
1232
|
+
const updateSql = `UPDATE "${tableName}" SET "${dbColumnName}" = ${formatValue(newValue)} WHERE ${whereConditions};`;
|
|
1233
|
+
try {
|
|
1234
|
+
if (databaseAdmin?.executeSql) {
|
|
1235
|
+
await databaseAdmin.executeSql(updateSql, {
|
|
1236
|
+
database: selectedDatabase,
|
|
1237
|
+
role: selectedRole
|
|
1238
|
+
});
|
|
1239
|
+
const newResults_0 = [...activeTab.results || []];
|
|
1240
|
+
if (newResults_0[rowIndex_0]) {
|
|
1241
|
+
newResults_0[rowIndex_0] = {
|
|
1242
|
+
...newResults_0[rowIndex_0],
|
|
1243
|
+
[columnKey_0]: newValue
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
updateActiveTab({
|
|
1247
|
+
results: newResults_0
|
|
1248
|
+
});
|
|
1249
|
+
snackbarController.open({
|
|
1250
|
+
type: "success",
|
|
1251
|
+
message: t("studio_sql_row_updated")
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
} catch (e_1) {
|
|
1255
|
+
snackbarController.open({
|
|
1256
|
+
type: "error",
|
|
1257
|
+
message: t("studio_sql_update_failed", {
|
|
1258
|
+
message: e_1 instanceof Error ? e_1.message : String(e_1)
|
|
1259
|
+
})
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
}, [editingCell, schemas, activeTab.lastExecutedSql, activeTab.results, databaseAdmin, updateActiveTab, snackbarController, selectedDatabase, selectedRole]);
|
|
1263
|
+
const [columnWidths, setColumnWidths] = useState(() => {
|
|
1264
|
+
const saved_0 = localStorage.getItem("rebase_sql_column_widths");
|
|
1265
|
+
return saved_0 ? JSON.parse(saved_0) : {};
|
|
1266
|
+
});
|
|
1267
|
+
const [snippets, setSnippets] = useState([]);
|
|
1268
|
+
const [history, setHistory] = useState([]);
|
|
1269
|
+
const [isSaveDialogOpen, setIsSaveDialogOpen] = useState(false);
|
|
1270
|
+
const [newSnippetName, setNewSnippetName] = useState("");
|
|
1271
|
+
useEffect(() => {
|
|
1272
|
+
const savedSnippets = localStorage.getItem("rebase_sql_snippets");
|
|
1273
|
+
if (savedSnippets) setSnippets(JSON.parse(savedSnippets));
|
|
1274
|
+
const savedHistory = localStorage.getItem("rebase_sql_history");
|
|
1275
|
+
if (savedHistory) setHistory(JSON.parse(savedHistory));
|
|
1276
|
+
}, []);
|
|
1277
|
+
useEffect(() => {
|
|
1278
|
+
const sanitizedTabs = tabs.map((t_12) => ({
|
|
1279
|
+
id: t_12.id,
|
|
1280
|
+
name: t_12.name,
|
|
1281
|
+
sql: t_12.sql,
|
|
1282
|
+
database: t_12.database,
|
|
1283
|
+
role: t_12.role
|
|
1284
|
+
}));
|
|
1285
|
+
localStorage.setItem(STORAGE_KEY_TABS, JSON.stringify(sanitizedTabs));
|
|
1286
|
+
}, [tabs]);
|
|
1287
|
+
useEffect(() => {
|
|
1288
|
+
localStorage.setItem(STORAGE_KEY_ACTIVE_TAB, activeTabId);
|
|
1289
|
+
}, [activeTabId]);
|
|
1290
|
+
const saveSnippets = (newSnippets) => {
|
|
1291
|
+
setSnippets(newSnippets);
|
|
1292
|
+
localStorage.setItem("rebase_sql_snippets", JSON.stringify(newSnippets));
|
|
1293
|
+
};
|
|
1294
|
+
const saveHistory = (newHistory) => {
|
|
1295
|
+
setHistory(newHistory);
|
|
1296
|
+
localStorage.setItem("rebase_sql_history", JSON.stringify(newHistory.slice(-50)));
|
|
1297
|
+
};
|
|
1298
|
+
const handleDeleteSnippet = (id) => {
|
|
1299
|
+
saveSnippets(snippets.filter((s) => s.id !== id));
|
|
1300
|
+
};
|
|
1301
|
+
const handleAddTab = () => {
|
|
1302
|
+
const newId = Math.random().toString(36).substring(2, 9);
|
|
1303
|
+
let maxNumber = 0;
|
|
1304
|
+
tabs.forEach((tab) => {
|
|
1305
|
+
const match = tab.name.match(/^Query (\d+)$/);
|
|
1306
|
+
if (match) {
|
|
1307
|
+
const num = parseInt(match[1], 10);
|
|
1308
|
+
if (num > maxNumber) maxNumber = num;
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1311
|
+
const name = `Query ${maxNumber + 1}`;
|
|
1312
|
+
setTabs((prev_6) => [...prev_6, {
|
|
1313
|
+
id: newId,
|
|
1314
|
+
name,
|
|
1315
|
+
sql: "SELECT * FROM ",
|
|
1316
|
+
database: selectedDatabase,
|
|
1317
|
+
role: selectedRole,
|
|
1318
|
+
results: null,
|
|
1319
|
+
loading: false,
|
|
1320
|
+
error: null,
|
|
1321
|
+
execTime: null,
|
|
1322
|
+
lastExecutedSql: null
|
|
1323
|
+
}]);
|
|
1324
|
+
setActiveTabId(newId);
|
|
1325
|
+
};
|
|
1326
|
+
const handleCloseTab = (id_0, e_2) => {
|
|
1327
|
+
e_2.stopPropagation();
|
|
1328
|
+
if (tabs.length === 1) return;
|
|
1329
|
+
const tabIndex = tabs.findIndex((t_13) => t_13.id === id_0);
|
|
1330
|
+
const newTabs = tabs.filter((t_14) => t_14.id !== id_0);
|
|
1331
|
+
setTabs(newTabs);
|
|
1332
|
+
if (activeTabId === id_0) {
|
|
1333
|
+
const nextIndex = Math.min(tabIndex, newTabs.length - 1);
|
|
1334
|
+
if (newTabs[nextIndex]) {
|
|
1335
|
+
setActiveTabId(newTabs[nextIndex].id);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
const handleColumnResize = useCallback(({
|
|
1340
|
+
key,
|
|
1341
|
+
width
|
|
1342
|
+
}) => {
|
|
1343
|
+
setColumnWidths((prev_7) => {
|
|
1344
|
+
const newWidths = {
|
|
1345
|
+
...prev_7,
|
|
1346
|
+
[activeTab.sql]: {
|
|
1347
|
+
...prev_7[activeTab.sql] || {},
|
|
1348
|
+
[key]: width
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
localStorage.setItem("rebase_sql_column_widths", JSON.stringify(newWidths));
|
|
1352
|
+
return newWidths;
|
|
1353
|
+
});
|
|
1354
|
+
}, [activeTab.sql]);
|
|
1355
|
+
const handlePrettify = () => {
|
|
1356
|
+
const formatted = activeTab.sql.replace(/\s+/g, " ").replace(/\s?,\s?/g, ", ").replace(/\s?=\s?/g, " = ").trim();
|
|
1357
|
+
setSql(formatted);
|
|
1358
|
+
};
|
|
1359
|
+
const handleExplain = async () => {
|
|
1360
|
+
const explainSql = `EXPLAIN (FORMAT JSON, ANALYZE) ${activeTab.sql}`;
|
|
1361
|
+
updateActiveTab({
|
|
1362
|
+
loading: true,
|
|
1363
|
+
error: null,
|
|
1364
|
+
results: null
|
|
1365
|
+
});
|
|
1366
|
+
const start = performance.now();
|
|
1367
|
+
try {
|
|
1368
|
+
if (databaseAdmin?.executeSql) {
|
|
1369
|
+
const result_0 = await databaseAdmin.executeSql(explainSql, {
|
|
1370
|
+
database: selectedDatabase,
|
|
1371
|
+
role: selectedRole
|
|
1372
|
+
});
|
|
1373
|
+
updateActiveTab({
|
|
1374
|
+
results: result_0,
|
|
1375
|
+
execTime: Math.round(performance.now() - start)
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
} catch (e_3) {
|
|
1379
|
+
const message_1 = e_3 instanceof Error ? e_3.message : String(e_3);
|
|
1380
|
+
updateActiveTab({
|
|
1381
|
+
error: message_1 || t("studio_sql_error_explaining")
|
|
1382
|
+
});
|
|
1383
|
+
} finally {
|
|
1384
|
+
updateActiveTab({
|
|
1385
|
+
loading: false
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
const executeRun = useCallback(async (sqlOverride) => {
|
|
1390
|
+
let sqlToRun = sqlOverride || activeTab.sql;
|
|
1391
|
+
const upperSql = sqlToRun.toUpperCase();
|
|
1392
|
+
const isAggregate = /\b(COUNT|SUM|AVG|MIN|MAX)\s*\(/i.test(sqlToRun);
|
|
1393
|
+
const isExplain = /\bEXPLAIN\b/i.test(sqlToRun);
|
|
1394
|
+
if (autoLimit && upperSql.includes("SELECT") && !upperSql.includes("LIMIT") && !isAggregate && !isExplain) {
|
|
1395
|
+
sqlToRun = sqlToRun.trim().replace(/;$/, "");
|
|
1396
|
+
sqlToRun = `${sqlToRun} LIMIT 1000;`;
|
|
1397
|
+
}
|
|
1398
|
+
updateActiveTab({
|
|
1399
|
+
loading: true,
|
|
1400
|
+
error: null,
|
|
1401
|
+
results: null
|
|
1402
|
+
});
|
|
1403
|
+
const start_0 = performance.now();
|
|
1404
|
+
try {
|
|
1405
|
+
if (databaseAdmin?.executeSql) {
|
|
1406
|
+
const result_1 = await databaseAdmin.executeSql(sqlToRun, {
|
|
1407
|
+
database: selectedDatabase,
|
|
1408
|
+
role: selectedRole
|
|
1409
|
+
});
|
|
1410
|
+
updateActiveTab({
|
|
1411
|
+
results: result_1,
|
|
1412
|
+
execTime: Math.round(performance.now() - start_0),
|
|
1413
|
+
lastExecutedSql: sqlToRun
|
|
1414
|
+
});
|
|
1415
|
+
if (history[history.length - 1] !== activeTab.sql) {
|
|
1416
|
+
saveHistory([...history, activeTab.sql]);
|
|
1417
|
+
}
|
|
1418
|
+
} else {
|
|
1419
|
+
updateActiveTab({
|
|
1420
|
+
error: t("studio_sql_execution_not_supported")
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
} catch (e_4) {
|
|
1424
|
+
const message_2 = e_4 instanceof Error ? e_4.message : String(e_4);
|
|
1425
|
+
updateActiveTab({
|
|
1426
|
+
error: message_2 || t("studio_sql_error_executing")
|
|
1427
|
+
});
|
|
1428
|
+
} finally {
|
|
1429
|
+
updateActiveTab({
|
|
1430
|
+
loading: false
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
}, [activeTab.sql, autoLimit, databaseAdmin, history, updateActiveTab]);
|
|
1434
|
+
const handleRun = useCallback(async (selectedText) => {
|
|
1435
|
+
const sqlTarget = selectedText || activeTab.sql;
|
|
1436
|
+
if (!sqlTarget.trim()) return;
|
|
1437
|
+
const destructiveKeywords = ["DELETE", "DROP", "TRUNCATE", "UPDATE"];
|
|
1438
|
+
const hasDestructive = destructiveKeywords.some((kw) => sqlTarget.toUpperCase().includes(kw));
|
|
1439
|
+
const hasWhere = sqlTarget.toUpperCase().includes("WHERE");
|
|
1440
|
+
if (hasDestructive && (!hasWhere || sqlTarget.toUpperCase().includes("DROP") || sqlTarget.toUpperCase().includes("TRUNCATE"))) {
|
|
1441
|
+
setPendingAction(() => () => executeRun(selectedText));
|
|
1442
|
+
setIsConfirmDialogOpen(true);
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
executeRun(selectedText);
|
|
1446
|
+
}, [activeTab.sql, executeRun]);
|
|
1447
|
+
useEffect(() => {
|
|
1448
|
+
const handleKeyDown = (e_5) => {
|
|
1449
|
+
if ((e_5.metaKey || e_5.ctrlKey) && e_5.key === "Enter") {
|
|
1450
|
+
const activeElement = document.activeElement;
|
|
1451
|
+
const isInput = activeElement?.tagName === "INPUT" || activeElement?.tagName === "TEXTAREA";
|
|
1452
|
+
const isMonaco = activeElement?.className?.includes("monaco-mouse-cursor-text");
|
|
1453
|
+
if (!isMonaco && !isInput) {
|
|
1454
|
+
e_5.preventDefault();
|
|
1455
|
+
handleRun();
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
1460
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1461
|
+
}, [handleRun]);
|
|
1462
|
+
const handleSaveSnippet = () => {
|
|
1463
|
+
if (!newSnippetName.trim() || !sql.trim()) return;
|
|
1464
|
+
const newSnippet = {
|
|
1465
|
+
id: Math.random().toString(36).substring(2, 9),
|
|
1466
|
+
name: newSnippetName,
|
|
1467
|
+
sql,
|
|
1468
|
+
createdAt: Date.now()
|
|
1469
|
+
};
|
|
1470
|
+
saveSnippets([...snippets, newSnippet]);
|
|
1471
|
+
setNewSnippetName("");
|
|
1472
|
+
setIsSaveDialogOpen(false);
|
|
1473
|
+
snackbarController.open({
|
|
1474
|
+
type: "success",
|
|
1475
|
+
message: t("studio_sql_snippet_saved", {
|
|
1476
|
+
name: newSnippetName
|
|
1477
|
+
})
|
|
1478
|
+
});
|
|
1479
|
+
};
|
|
1480
|
+
const handleExportCSV = () => {
|
|
1481
|
+
if (!results || results.length === 0) return;
|
|
1482
|
+
const headers = Object.keys(results[0]).join(",");
|
|
1483
|
+
const rows = results.map((row) => Object.values(row).map((val_0) => {
|
|
1484
|
+
const str = String(val_0);
|
|
1485
|
+
return str.includes(",") ? `"${str}"` : str;
|
|
1486
|
+
}).join(","));
|
|
1487
|
+
const csv = [headers, ...rows].join("\n");
|
|
1488
|
+
const blob = new Blob([csv], {
|
|
1489
|
+
type: "text/csv"
|
|
1490
|
+
});
|
|
1491
|
+
const url = window.URL.createObjectURL(blob);
|
|
1492
|
+
const a = document.createElement("a");
|
|
1493
|
+
a.href = url;
|
|
1494
|
+
a.download = `query_results_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19)}.csv`;
|
|
1495
|
+
a.click();
|
|
1496
|
+
window.URL.revokeObjectURL(url);
|
|
1497
|
+
};
|
|
1498
|
+
const handleExportJSON = () => {
|
|
1499
|
+
if (!results || results.length === 0) return;
|
|
1500
|
+
const json = JSON.stringify(results, null, 2);
|
|
1501
|
+
const blob_0 = new Blob([json], {
|
|
1502
|
+
type: "application/json"
|
|
1503
|
+
});
|
|
1504
|
+
const url_0 = window.URL.createObjectURL(blob_0);
|
|
1505
|
+
const a_0 = document.createElement("a");
|
|
1506
|
+
a_0.href = url_0;
|
|
1507
|
+
a_0.download = `query_results_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19)}.json`;
|
|
1508
|
+
a_0.click();
|
|
1509
|
+
window.URL.revokeObjectURL(url_0);
|
|
1510
|
+
};
|
|
1511
|
+
const handleExportMarkdown = () => {
|
|
1512
|
+
if (!results || results.length === 0) return;
|
|
1513
|
+
const headers_0 = Object.keys(results[0]);
|
|
1514
|
+
const headerRow = `| ${headers_0.join(" | ")} |`;
|
|
1515
|
+
const dividerRow = `| ${headers_0.map(() => "---").join(" | ")} |`;
|
|
1516
|
+
const dataRows = results.map((row_0) => `| ${headers_0.map((header) => {
|
|
1517
|
+
const val_1 = row_0[header];
|
|
1518
|
+
if (val_1 === null) return "null";
|
|
1519
|
+
if (val_1 === void 0) return "";
|
|
1520
|
+
return String(val_1).replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
1521
|
+
}).join(" | ")} |`);
|
|
1522
|
+
const markdown = [headerRow, dividerRow, ...dataRows].join("\n");
|
|
1523
|
+
navigator.clipboard.writeText(markdown).then(() => {
|
|
1524
|
+
snackbarController.open({
|
|
1525
|
+
type: "success",
|
|
1526
|
+
message: t("studio_sql_markdown_copied")
|
|
1527
|
+
});
|
|
1528
|
+
}).catch(() => {
|
|
1529
|
+
snackbarController.open({
|
|
1530
|
+
type: "error",
|
|
1531
|
+
message: t("studio_sql_markdown_copy_failed")
|
|
1532
|
+
});
|
|
1533
|
+
});
|
|
1534
|
+
};
|
|
1535
|
+
const renderResults = () => {
|
|
1536
|
+
if (loading) {
|
|
1537
|
+
return /* @__PURE__ */ jsx("div", { className: "flex-grow flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
1538
|
+
/* @__PURE__ */ jsx(CircularProgress, { size: "medium" }),
|
|
1539
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "mt-4 text-text-secondary dark:text-text-secondary-dark font-mono tracking-tight animate-pulse", children: t("studio_sql_executing_query") })
|
|
1540
|
+
] }) });
|
|
1541
|
+
}
|
|
1542
|
+
if (error) {
|
|
1543
|
+
return /* @__PURE__ */ jsx("div", { className: "flex-grow flex items-center justify-center p-6 overflow-auto", children: /* @__PURE__ */ jsx(ErrorView, { title: t("studio_sql_query_error"), error }) });
|
|
1544
|
+
}
|
|
1545
|
+
if (!results) {
|
|
1546
|
+
return /* @__PURE__ */ jsx("div", { className: "flex-grow flex items-center justify-center text-text-disabled dark:text-text-disabled-dark", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
1547
|
+
/* @__PURE__ */ jsx("svg", { className: "w-12 h-12 mx-auto mb-4 opacity-50", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1, d: "M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" }) }),
|
|
1548
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", children: t("studio_sql_run_query_placeholder") })
|
|
1549
|
+
] }) });
|
|
1550
|
+
}
|
|
1551
|
+
if (results.length === 1 && results[0]["QUERY PLAN"] && Array.isArray(results[0]["QUERY PLAN"])) {
|
|
1552
|
+
try {
|
|
1553
|
+
const plan = results[0]["QUERY PLAN"][0].Plan;
|
|
1554
|
+
if (plan) {
|
|
1555
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex-grow overflow-auto p-4 bg-surface-50 dark:bg-surface-900 flex flex-col items-start", children: [
|
|
1556
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-bold text-text-secondary mb-4 tracking-wider uppercase", children: t("studio_sql_visual_execution_plan") }),
|
|
1557
|
+
/* @__PURE__ */ jsx("div", { className: "pb-12", children: /* @__PURE__ */ jsx(ExplainVisualizer, { plan }) })
|
|
1558
|
+
] });
|
|
1559
|
+
}
|
|
1560
|
+
} catch (e_6) {
|
|
1561
|
+
console.warn("Failed to parse EXPLAIN JSON output:", e_6);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
if (results.length === 0) {
|
|
1565
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-6 flex flex-col items-center justify-center", children: [
|
|
1566
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-text-secondary dark:text-text-secondary-dark font-mono border-b border-surface-200 dark:border-surface-950 pb-2 mb-2", children: t("studio_sql_success") }),
|
|
1567
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-disabled dark:text-text-disabled-dark", children: t("studio_sql_no_results") })
|
|
1568
|
+
] });
|
|
1569
|
+
}
|
|
1570
|
+
const savedWidths = columnWidths[activeTab.sql] || {};
|
|
1571
|
+
const resultColumnKeys = Object.keys(results[0]);
|
|
1572
|
+
const matchedCollections = (() => {
|
|
1573
|
+
if (!activeTab.lastExecutedSql || !collectionRegistry.collections) return [];
|
|
1574
|
+
try {
|
|
1575
|
+
return resolveQueryCollections(activeTab.lastExecutedSql, schemas, collectionRegistry.collections, resultColumnKeys);
|
|
1576
|
+
} catch {
|
|
1577
|
+
return [];
|
|
1578
|
+
}
|
|
1579
|
+
})();
|
|
1580
|
+
const actionableCollections = matchedCollections.filter((mc) => mc.pkColumn && resultColumnKeys.includes(mc.pkColumn));
|
|
1581
|
+
const getRowEntityActions = (rowData_1) => {
|
|
1582
|
+
if (!rowData_1) return [];
|
|
1583
|
+
return actionableCollections.filter((mc_0) => rowData_1[mc_0.pkColumn] != null).map((mc_1) => ({
|
|
1584
|
+
collection: mc_1,
|
|
1585
|
+
entityId: rowData_1[mc_1.pkColumn]
|
|
1586
|
+
}));
|
|
1587
|
+
};
|
|
1588
|
+
const dataColumns = resultColumnKeys.map((key_0) => ({
|
|
1589
|
+
key: key_0,
|
|
1590
|
+
title: key_0,
|
|
1591
|
+
width: savedWidths[key_0] ?? 150,
|
|
1592
|
+
sortable: false,
|
|
1593
|
+
resizable: true
|
|
1594
|
+
}));
|
|
1595
|
+
const columns = actionableCollections.length > 0 ? [{
|
|
1596
|
+
key: "__cms_action__",
|
|
1597
|
+
title: "",
|
|
1598
|
+
width: 36,
|
|
1599
|
+
sortable: false,
|
|
1600
|
+
resizable: false
|
|
1601
|
+
}, ...dataColumns] : dataColumns;
|
|
1602
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex-grow flex flex-col overflow-hidden min-h-0", children: [
|
|
1603
|
+
actionableCollections.length > 0 && /* @__PURE__ */ jsxs("div", { className: cls("px-4 py-1.5 border-b flex items-center gap-2 shrink-0 bg-surface-50 dark:bg-surface-900", defaultBorderMixin), children: [
|
|
1604
|
+
/* @__PURE__ */ jsx(Tooltip, { title: t("studio_sql_cms_collections_tooltip"), children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[10px] font-bold uppercase tracking-widest text-text-disabled dark:text-text-disabled-dark mr-1 shrink-0 cursor-help", children: t("studio_sql_cms") }) }),
|
|
1605
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5 overflow-x-auto no-scrollbar", children: actionableCollections.map((mc_2) => /* @__PURE__ */ jsx(Tooltip, { title: `Table "${mc_2.tableName}" → ${mc_2.collection.name} (PK: ${mc_2.pkColumn})`, children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-medium bg-primary/10 dark:bg-primary-dark/15 text-primary dark:text-primary-dark whitespace-nowrap border border-primary/20 dark:border-primary-dark/20", children: [
|
|
1606
|
+
typeof mc_2.collection.icon === "string" && /* @__PURE__ */ jsx(IconForView, { collectionOrView: mc_2.collection, className: "text-[12px]" }),
|
|
1607
|
+
mc_2.collection.name
|
|
1608
|
+
] }) }, mc_2.tableName)) })
|
|
1609
|
+
] }),
|
|
1610
|
+
/* @__PURE__ */ jsx("div", { className: "flex-grow relative h-full min-h-0 min-w-0", children: /* @__PURE__ */ jsx(VirtualTable, { data: results, columns, rowHeight: 32, headerHeight: 32, extraData: editingCell, onColumnResizeEnd: handleColumnResize, cellRenderer: ({
|
|
1611
|
+
rowData: rowData_2,
|
|
1612
|
+
column: column_0,
|
|
1613
|
+
rowIndex: rowIndex_1
|
|
1614
|
+
}) => {
|
|
1615
|
+
if (column_0.key === "__cms_action__") {
|
|
1616
|
+
const rowActions = getRowEntityActions(rowData_2);
|
|
1617
|
+
if (rowActions.length === 0) {
|
|
1618
|
+
return /* @__PURE__ */ jsx("div", { className: "h-full w-full" });
|
|
1619
|
+
}
|
|
1620
|
+
if (rowActions.length === 1) {
|
|
1621
|
+
const ra = rowActions[0];
|
|
1622
|
+
return /* @__PURE__ */ jsx("div", { className: "h-full flex items-center justify-center", children: /* @__PURE__ */ jsx(Tooltip, { title: t("studio_sql_edit_entity", {
|
|
1623
|
+
name: ra.collection.collection.name,
|
|
1624
|
+
id: String(ra.entityId)
|
|
1625
|
+
}), children: /* @__PURE__ */ jsx(IconButton, { size: "small", className: "text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300 transition-colors", onClick: (e_7) => {
|
|
1626
|
+
e_7.stopPropagation();
|
|
1627
|
+
sideEntityController?.open({
|
|
1628
|
+
path: ra.collection.collection.slug,
|
|
1629
|
+
entityId: ra.entityId,
|
|
1630
|
+
collection: ra.collection.collection,
|
|
1631
|
+
updateUrl: false
|
|
1632
|
+
});
|
|
1633
|
+
}, children: /* @__PURE__ */ jsx(PencilIcon, { size: iconSize.smallest }) }) }) });
|
|
1634
|
+
}
|
|
1635
|
+
return /* @__PURE__ */ jsx("div", { className: "h-full flex items-center justify-center", children: /* @__PURE__ */ jsx(Menu, { trigger: /* @__PURE__ */ jsx(IconButton, { size: "small", className: "text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300 transition-colors", onClick: (e_8) => e_8.stopPropagation(), children: /* @__PURE__ */ jsx(MoreVerticalIcon, { size: iconSize.smallest }) }), children: rowActions.map((ra_0) => /* @__PURE__ */ jsx(MenuItem, { dense: true, onClick: () => {
|
|
1636
|
+
sideEntityController?.open({
|
|
1637
|
+
path: ra_0.collection.collection.slug,
|
|
1638
|
+
entityId: ra_0.entityId,
|
|
1639
|
+
collection: ra_0.collection.collection,
|
|
1640
|
+
updateUrl: false
|
|
1641
|
+
});
|
|
1642
|
+
}, children: t("studio_sql_edit_entity", {
|
|
1643
|
+
name: ra_0.collection.collection.name,
|
|
1644
|
+
id: String(ra_0.entityId)
|
|
1645
|
+
}) }, ra_0.collection.tableName)) }) });
|
|
1646
|
+
}
|
|
1647
|
+
const isEditing = editingCell?.rowIndex === rowIndex_1 && editingCell?.columnKey === column_0.key;
|
|
1648
|
+
const value = rowData_2 ? rowData_2[column_0.key] : null;
|
|
1649
|
+
const displayValue = typeof value === "object" && value !== null ? JSON.stringify(value) : String(value ?? "");
|
|
1650
|
+
if (isEditing) {
|
|
1651
|
+
return /* @__PURE__ */ jsx(FixedEditorOverlay, { displayValue, onSave: (val_2) => handleCellSave(val_2, rowData_2, column_0.key, rowIndex_1), onCancel: () => setEditingCell(null) });
|
|
1652
|
+
}
|
|
1653
|
+
return /* @__PURE__ */ jsx("div", { className: "px-4 py-1.5 h-full flex items-center whitespace-nowrap text-[13px] text-text-primary dark:text-text-primary-dark font-mono cursor-text group/cell", onDoubleClick: () => handleDoubleClick(rowIndex_1, column_0.key, displayValue, rowData_2), children: /* @__PURE__ */ jsx("div", { className: "truncate flex-grow", title: displayValue, children: displayValue === "" ? /* @__PURE__ */ jsx("span", { className: "text-text-disabled dark:text-text-disabled-dark italic text-[11px]", children: "NULL" }) : displayValue }) });
|
|
1654
|
+
} }) }),
|
|
1655
|
+
/* @__PURE__ */ jsxs("div", { className: cls("p-2 px-4 border-t bg-surface-50 dark:bg-surface-900 flex justify-between items-center shrink-0", defaultBorderMixin), children: [
|
|
1656
|
+
/* @__PURE__ */ jsxs("div", { className: "flex space-x-4", children: [
|
|
1657
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center text-[11px]", children: [
|
|
1658
|
+
/* @__PURE__ */ jsx("span", { className: "font-bold text-text-disabled dark:text-text-disabled-dark mr-2 uppercase tracking-tighter", children: t("studio_sql_rows") }),
|
|
1659
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-text-secondary dark:text-text-secondary-dark", children: results.length })
|
|
1660
|
+
] }),
|
|
1661
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center text-[11px]", children: [
|
|
1662
|
+
/* @__PURE__ */ jsx("span", { className: "font-bold text-text-disabled dark:text-text-disabled-dark mr-2 uppercase tracking-tighter", children: t("studio_sql_time") }),
|
|
1663
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-text-secondary dark:text-text-secondary-dark", children: [
|
|
1664
|
+
execTime,
|
|
1665
|
+
"ms"
|
|
1666
|
+
] })
|
|
1667
|
+
] })
|
|
1668
|
+
] }),
|
|
1669
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 overflow-x-auto no-scrollbar items-center px-2", children: [
|
|
1670
|
+
/* @__PURE__ */ jsx(Button, { size: "small", variant: "text", className: "text-[10px] uppercase font-bold text-text-secondary dark:text-text-secondary-dark whitespace-nowrap", onClick: handleExportMarkdown, children: t("studio_sql_copy_markdown") }),
|
|
1671
|
+
/* @__PURE__ */ jsx(Button, { size: "small", variant: "text", className: "text-[10px] uppercase font-bold text-text-secondary dark:text-text-secondary-dark whitespace-nowrap", onClick: handleExportJSON, children: t("studio_sql_export_json") }),
|
|
1672
|
+
/* @__PURE__ */ jsx(Button, { size: "small", variant: "text", className: "text-[10px] uppercase font-bold text-text-secondary dark:text-text-secondary-dark whitespace-nowrap", onClick: handleExportCSV, children: t("studio_sql_export_csv") })
|
|
1673
|
+
] })
|
|
1674
|
+
] })
|
|
1675
|
+
] });
|
|
1676
|
+
};
|
|
1677
|
+
const [sidebarSize, setSidebarSize] = useState(() => {
|
|
1678
|
+
try {
|
|
1679
|
+
const saved_1 = localStorage.getItem("rebase_sql_editor_sidebar_size");
|
|
1680
|
+
return saved_1 !== null ? parseFloat(saved_1) : 20;
|
|
1681
|
+
} catch (e_9) {
|
|
1682
|
+
return 20;
|
|
1683
|
+
}
|
|
1684
|
+
});
|
|
1685
|
+
const [editorHeight, setEditorHeight] = useState(() => {
|
|
1686
|
+
try {
|
|
1687
|
+
const saved_2 = localStorage.getItem("rebase_sql_editor_height");
|
|
1688
|
+
return saved_2 !== null ? parseFloat(saved_2) : 50;
|
|
1689
|
+
} catch (e_10) {
|
|
1690
|
+
return 50;
|
|
1691
|
+
}
|
|
1692
|
+
});
|
|
1693
|
+
useEffect(() => {
|
|
1694
|
+
try {
|
|
1695
|
+
localStorage.setItem("rebase_sql_editor_sidebar_size", sidebarSize.toString());
|
|
1696
|
+
} catch (e_11) {
|
|
1697
|
+
}
|
|
1698
|
+
}, [sidebarSize]);
|
|
1699
|
+
useEffect(() => {
|
|
1700
|
+
try {
|
|
1701
|
+
localStorage.setItem("rebase_sql_editor_height", editorHeight.toString());
|
|
1702
|
+
} catch (e_12) {
|
|
1703
|
+
}
|
|
1704
|
+
}, [editorHeight]);
|
|
1705
|
+
const activeSnippet = snippets.find((s_0) => s_0.sql === activeTab.sql);
|
|
1706
|
+
const isFavorite = activeSnippet?.isFavorite || false;
|
|
1707
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full bg-white dark:bg-surface-950 overflow-hidden text-text-primary dark:text-text-primary-dark", children: [
|
|
1708
|
+
/* @__PURE__ */ jsx(ResizablePanels, { orientation: "horizontal", panelSizePercent: sidebarSize, onPanelSizeChange: setSidebarSize, minPanelSizePx: 220, firstPanel: /* @__PURE__ */ jsx(SQLEditorSidebar, { snippets, history, onSelectSnippet: setSql, onTableClick: setSql, onDeleteSnippet: handleDeleteSnippet, schemas, isSchemaLoading, schemaError, onRetrySchema: fetchSchema }), secondPanel: /* @__PURE__ */ jsxs("div", { className: "flex-grow flex flex-col min-w-0 h-full w-full", children: [
|
|
1709
|
+
/* @__PURE__ */ jsxs("div", { className: cls("flex items-center justify-between pr-2 border-b bg-white dark:bg-surface-950", defaultBorderMixin), children: [
|
|
1710
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center flex-grow overflow-hidden mr-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center no-scrollbar overflow-x-auto min-w-0", children: [
|
|
1711
|
+
/* @__PURE__ */ jsx(Tabs, { value: activeTabId, onValueChange: handleTabChange, variant: "boxy", className: "w-[unset] flex-shrink-0", innerClassName: "bg-white dark:bg-surface-950", children: tabs.map((tab_0) => /* @__PURE__ */ jsxs(Tab, { value: tab_0.id, className: "flex items-center justify-between group max-w-[200px]", children: [
|
|
1712
|
+
/* @__PURE__ */ jsx(TerminalIcon, { size: iconSize.smallest, className: "text-blue-500 mr-1.5 flex-shrink-0" }),
|
|
1713
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: tab_0.name }),
|
|
1714
|
+
tabs.length > 1 && /* @__PURE__ */ jsx(IconButton, { size: "smallest", onClick: (e_13) => handleCloseTab(tab_0.id, e_13), className: "ml-1 !p-0.5 opacity-0 group-hover:opacity-100 hover:text-red-500 transition-opacity", children: /* @__PURE__ */ jsx(XIcon, { size: iconSize.smallest }) })
|
|
1715
|
+
] }, tab_0.id)) }),
|
|
1716
|
+
/* @__PURE__ */ jsx(IconButton, { size: "small", onClick: handleAddTab, className: "ml-2 flex-shrink-0", children: /* @__PURE__ */ jsx(PlusIcon, { size: iconSize.smallest }) })
|
|
1717
|
+
] }) }),
|
|
1718
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-end pr-2 gap-1.5", children: [
|
|
1719
|
+
/* @__PURE__ */ jsx(Tooltip, { title: t("studio_sql_format_sql"), children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: handlePrettify, className: "text-text-secondary hover:text-text-primary transition-colors", children: /* @__PURE__ */ jsx(MenuIcon, { size: iconSize.smallest }) }) }),
|
|
1720
|
+
/* @__PURE__ */ jsx(Button, { variant: "text", size: "small", onClick: handleExplain, disabled: loading, children: t("studio_sql_explain") }),
|
|
1721
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-surface-200 dark:bg-surface-950 mx-1" }),
|
|
1722
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2 px-2 cursor-pointer", onClick: () => setAutoLimit(!autoLimit), children: [
|
|
1723
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[11px] text-text-secondary cursor-pointer select-none", children: t("studio_sql_limit_1000") }),
|
|
1724
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", checked: autoLimit, onChange: (e_14) => setAutoLimit(e_14.target.checked), onClick: (e_15) => e_15.stopPropagation(), className: "w-3.5 h-3.5 rounded border-surface-300 text-primary focus:ring-primary cursor-pointer" })
|
|
1725
|
+
] }),
|
|
1726
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-surface-200 dark:bg-surface-950 mx-1" }),
|
|
1727
|
+
/* @__PURE__ */ jsx(Tooltip, { title: isFavorite ? t("studio_sql_remove_from_favorites") : t("studio_sql_add_to_favorites"), children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => {
|
|
1728
|
+
if (!activeSnippet) {
|
|
1729
|
+
snackbarController.open({
|
|
1730
|
+
type: "info",
|
|
1731
|
+
message: t("studio_sql_save_first_to_favorite")
|
|
1732
|
+
});
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
saveSnippets(snippets.map((s_1) => s_1.id === activeSnippet.id ? {
|
|
1736
|
+
...s_1,
|
|
1737
|
+
isFavorite: !s_1.isFavorite
|
|
1738
|
+
} : s_1));
|
|
1739
|
+
}, children: /* @__PURE__ */ jsx("svg", { className: `w-4 h-4 ${isFavorite ? "text-red-500 fill-current" : "text-text-disabled dark:text-text-disabled-dark hover:text-text-primary"}`, fill: isFavorite ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" }) }) }) }),
|
|
1740
|
+
/* @__PURE__ */ jsx(Button, { variant: "text", size: "small", onClick: () => setIsSaveDialogOpen(true), children: t("studio_sql_save") }),
|
|
1741
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-surface-200 dark:bg-surface-950 mx-1" }),
|
|
1742
|
+
/* @__PURE__ */ jsx(Menu, { trigger: /* @__PURE__ */ jsxs(Button, { size: "small", variant: "outlined", className: "text-text-secondary dark:text-text-secondary-dark font-medium mr-2", children: [
|
|
1743
|
+
/* @__PURE__ */ jsx(DatabaseIcon, { size: iconSize.small, className: "mr-1.5 text-text-disabled dark:text-text-disabled-dark" }),
|
|
1744
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-[80px] truncate", children: isLoadingConfig ? "..." : selectedDatabase || t("studio_sql_select_db") })
|
|
1745
|
+
] }), children: /* @__PURE__ */ jsxs("div", { className: "max-h-64 overflow-y-auto", children: [
|
|
1746
|
+
/* @__PURE__ */ jsx("div", { className: "px-3 py-1.5 border-b border-surface-200 dark:border-surface-950 mb-1", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-bold uppercase tracking-wider text-[9px] text-text-disabled dark:text-text-disabled-dark", children: t("studio_sql_database") }) }),
|
|
1747
|
+
isLoadingConfig ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-4", children: /* @__PURE__ */ jsx(CircularProgress, { size: "small" }) }) : connectionConfigError ? /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-red-500 dark:text-red-400 max-w-[200px] break-words", children: connectionConfigError }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1748
|
+
availableDatabases.map((db_0) => /* @__PURE__ */ jsx(MenuItem, { dense: true, onClick: () => handleDatabaseChange(db_0), className: cls("text-xs", selectedDatabase === db_0 && "text-primary dark:text-primary-dark"), children: db_0 }, db_0)),
|
|
1749
|
+
/* @__PURE__ */ jsx("div", { className: "px-3 py-1.5 border-y border-surface-200 dark:border-surface-950 mb-1 mt-1", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-bold uppercase tracking-wider text-[9px] text-text-disabled dark:text-text-disabled-dark", children: t("studio_sql_role") }) }),
|
|
1750
|
+
availableRoles.map((role_0) => /* @__PURE__ */ jsxs(MenuItem, { dense: true, onClick: () => handleRoleChange(role_0), className: cls("text-xs", selectedRole === role_0 && "text-primary dark:text-primary-dark"), children: [
|
|
1751
|
+
role_0,
|
|
1752
|
+
role_0 === "postgres" ? " " + t("studio_sql_admin") : ""
|
|
1753
|
+
] }, role_0))
|
|
1754
|
+
] })
|
|
1755
|
+
] }) }),
|
|
1756
|
+
/* @__PURE__ */ jsxs(Button, { onClick: () => handleRun(), disabled: loading, size: "small", color: "primary", children: [
|
|
1757
|
+
loading ? /* @__PURE__ */ jsx(CircularProgress, { size: "smallest", className: "mr-2" }) : /* @__PURE__ */ jsx(PlayIcon, { size: iconSize.smallest, className: "mr-2" }),
|
|
1758
|
+
t("studio_sql_run")
|
|
1759
|
+
] })
|
|
1760
|
+
] })
|
|
1761
|
+
] }),
|
|
1762
|
+
/* @__PURE__ */ jsx(ResizablePanels, { orientation: "vertical", panelSizePercent: editorHeight, onPanelSizeChange: setEditorHeight, minPanelSizePx: 100, firstPanel: /* @__PURE__ */ jsx("div", { className: "h-full w-full relative flex flex-col min-h-0", children: /* @__PURE__ */ jsx(MonacoEditor, { value: sql, onChange: (v) => setSql(v || ""), onRun: handleRun, schemas }) }), secondPanel: /* @__PURE__ */ jsxs("div", { className: "h-full w-full flex flex-col bg-surface-50 dark:bg-surface-950 overflow-hidden min-h-0", children: [
|
|
1763
|
+
/* @__PURE__ */ jsx("div", { className: cls("p-2 px-4 bg-surface-100 dark:bg-surface-900 border-b shrink-0 flex items-center", defaultBorderMixin), children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-bold text-text-disabled dark:text-text-disabled-dark uppercase tracking-widest text-[10px]", children: t("studio_sql_query_results") }) }),
|
|
1764
|
+
/* @__PURE__ */ jsx("div", { className: "flex-grow flex flex-col min-h-0 overflow-hidden", children: renderResults() })
|
|
1765
|
+
] }) })
|
|
1766
|
+
] }) }),
|
|
1767
|
+
/* @__PURE__ */ jsxs(Dialog, { open: isSaveDialogOpen, onOpenChange: setIsSaveDialogOpen, children: [
|
|
1768
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: t("studio_sql_save_snippet") }),
|
|
1769
|
+
/* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs("div", { className: "py-4 flex flex-col gap-4", children: [
|
|
1770
|
+
/* @__PURE__ */ jsx(TextField, { label: t("studio_sql_snippet_name"), autoFocus: true, placeholder: t("studio_sql_snippet_name_placeholder"), value: newSnippetName, onChange: (e_16) => setNewSnippetName(e_16.target.value), onKeyDown: (e_17) => {
|
|
1771
|
+
if (e_17.key === "Enter") {
|
|
1772
|
+
e_17.preventDefault();
|
|
1773
|
+
handleSaveSnippet();
|
|
1774
|
+
}
|
|
1775
|
+
} }),
|
|
1776
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-disabled dark:text-text-disabled-dark block", children: t("studio_sql_snippet_saved_local") })
|
|
1777
|
+
] }) }),
|
|
1778
|
+
/* @__PURE__ */ jsxs(DialogActions, { children: [
|
|
1779
|
+
/* @__PURE__ */ jsx(Button, { variant: "text", onClick: () => setIsSaveDialogOpen(false), children: t("studio_sql_cancel") }),
|
|
1780
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleSaveSnippet, color: "primary", disabled: !newSnippetName.trim(), children: t("studio_sql_save") })
|
|
1781
|
+
] })
|
|
1782
|
+
] }),
|
|
1783
|
+
/* @__PURE__ */ jsx(ConfirmationDialog, { open: isConfirmDialogOpen, onCancel: () => setIsConfirmDialogOpen(false), title: t("studio_sql_dangerous_operation"), body: t("studio_sql_dangerous_operation_body"), onAccept: () => {
|
|
1784
|
+
if (pendingAction) pendingAction();
|
|
1785
|
+
setIsConfirmDialogOpen(false);
|
|
1786
|
+
} })
|
|
1787
|
+
] });
|
|
1788
|
+
};
|
|
1789
|
+
export {
|
|
1790
|
+
SQLEditor
|
|
1791
|
+
};
|
|
1792
|
+
//# sourceMappingURL=SQLEditor-CkVx9vgr.js.map
|