@rebasepro/studio 0.5.0 → 0.6.1

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