@rebasepro/studio 0.0.1-canary.000dc36

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 (363) hide show
  1. package/LICENSE +114 -0
  2. package/README.md +159 -0
  3. package/dist/ApiExplorer-DHVmWYfK.js +1053 -0
  4. package/dist/ApiExplorer-DHVmWYfK.js.map +1 -0
  5. package/dist/AuthSimulationSelector-CM488Eei.js +106 -0
  6. package/dist/AuthSimulationSelector-CM488Eei.js.map +1 -0
  7. package/dist/BranchesView-DcHZtvXo.js +292 -0
  8. package/dist/BranchesView-DcHZtvXo.js.map +1 -0
  9. package/dist/CronJobsView-CijCToeK.js +456 -0
  10. package/dist/CronJobsView-CijCToeK.js.map +1 -0
  11. package/dist/JSEditor-CSHA0t_O.js +1308 -0
  12. package/dist/JSEditor-CSHA0t_O.js.map +1 -0
  13. package/dist/MonacoEditor-CMYEjiRf.js +161 -0
  14. package/dist/MonacoEditor-CMYEjiRf.js.map +1 -0
  15. package/dist/RLSEditor-BzDjqo6w.js +1872 -0
  16. package/dist/RLSEditor-BzDjqo6w.js.map +1 -0
  17. package/dist/SQLEditor-Cr9Kg_Qg.js +1780 -0
  18. package/dist/SQLEditor-Cr9Kg_Qg.js.map +1 -0
  19. package/dist/SchemaVisualizer-BGpmzyXT.js +1069 -0
  20. package/dist/SchemaVisualizer-BGpmzyXT.js.map +1 -0
  21. package/dist/StorageView-DG9tJZG1.js +811 -0
  22. package/dist/StorageView-DG9tJZG1.js.map +1 -0
  23. package/dist/common/src/collections/CollectionRegistry.d.ts +56 -0
  24. package/dist/common/src/collections/index.d.ts +1 -0
  25. package/dist/common/src/data/buildRebaseData.d.ts +14 -0
  26. package/dist/common/src/index.d.ts +3 -0
  27. package/dist/common/src/util/builders.d.ts +57 -0
  28. package/dist/common/src/util/callbacks.d.ts +6 -0
  29. package/dist/common/src/util/collections.d.ts +11 -0
  30. package/dist/common/src/util/common.d.ts +2 -0
  31. package/dist/common/src/util/conditions.d.ts +26 -0
  32. package/dist/common/src/util/entities.d.ts +58 -0
  33. package/dist/common/src/util/enums.d.ts +3 -0
  34. package/dist/common/src/util/index.d.ts +16 -0
  35. package/dist/common/src/util/navigation_from_path.d.ts +34 -0
  36. package/dist/common/src/util/navigation_utils.d.ts +20 -0
  37. package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
  38. package/dist/common/src/util/paths.d.ts +14 -0
  39. package/dist/common/src/util/permissions.d.ts +5 -0
  40. package/dist/common/src/util/references.d.ts +2 -0
  41. package/dist/common/src/util/relations.d.ts +22 -0
  42. package/dist/common/src/util/resolutions.d.ts +72 -0
  43. package/dist/common/src/util/storage.d.ts +24 -0
  44. package/dist/core/src/components/AIIcon.d.ts +16 -0
  45. package/dist/core/src/components/ConfirmationDialog.d.ts +9 -0
  46. package/dist/core/src/components/Debug/UIReferenceView.d.ts +1 -0
  47. package/dist/core/src/components/Debug/UIStyleGuide.d.ts +1 -0
  48. package/dist/core/src/components/ErrorTooltip.d.ts +2 -0
  49. package/dist/core/src/components/ErrorView.d.ts +21 -0
  50. package/dist/core/src/components/LanguageToggle.d.ts +1 -0
  51. package/dist/core/src/components/LoginView/LoginView.d.ts +68 -0
  52. package/dist/core/src/components/LoginView/index.d.ts +2 -0
  53. package/dist/core/src/components/NotFoundPage.d.ts +1 -0
  54. package/dist/core/src/components/RebaseAuth.d.ts +10 -0
  55. package/dist/core/src/components/RebaseLogo.d.ts +7 -0
  56. package/dist/core/src/components/UnsavedChangesDialog.d.ts +9 -0
  57. package/dist/core/src/components/UserDisplay.d.ts +7 -0
  58. package/dist/core/src/components/UserSelectPopover.d.ts +62 -0
  59. package/dist/core/src/components/UserSettingsView.d.ts +1 -0
  60. package/dist/core/src/components/common/index.d.ts +6 -0
  61. package/dist/core/src/components/common/table_height.d.ts +5 -0
  62. package/dist/core/src/components/common/types.d.ts +63 -0
  63. package/dist/core/src/components/common/useColumnsIds.d.ts +9 -0
  64. package/dist/core/src/components/common/useDataTableController.d.ts +45 -0
  65. package/dist/core/src/components/common/useDebouncedData.d.ts +9 -0
  66. package/dist/core/src/components/common/useScrollRestoration.d.ts +14 -0
  67. package/dist/core/src/components/index.d.ts +16 -0
  68. package/dist/core/src/contexts/AdminModeController.d.ts +4 -0
  69. package/dist/core/src/contexts/AnalyticsContext.d.ts +3 -0
  70. package/dist/core/src/contexts/AuthControllerContext.d.ts +3 -0
  71. package/dist/core/src/contexts/CustomizationControllerContext.d.ts +3 -0
  72. package/dist/core/src/contexts/DataDriverContext.d.ts +3 -0
  73. package/dist/core/src/contexts/DatabaseAdminContext.d.ts +3 -0
  74. package/dist/core/src/contexts/DialogsProvider.d.ts +4 -0
  75. package/dist/core/src/contexts/EffectiveRoleController.d.ts +4 -0
  76. package/dist/core/src/contexts/InternalUserManagementContext.d.ts +3 -0
  77. package/dist/core/src/contexts/ModeController.d.ts +4 -0
  78. package/dist/core/src/contexts/RebaseClientInstanceContext.d.ts +6 -0
  79. package/dist/core/src/contexts/RebaseDataContext.d.ts +3 -0
  80. package/dist/core/src/contexts/SnackbarProvider.d.ts +2 -0
  81. package/dist/core/src/contexts/StorageSourceContext.d.ts +3 -0
  82. package/dist/core/src/contexts/UserConfigurationPersistenceContext.d.ts +3 -0
  83. package/dist/core/src/contexts/index.d.ts +13 -0
  84. package/dist/core/src/core/PluginLifecycleManager.d.ts +17 -0
  85. package/dist/core/src/core/PluginProviderStack.d.ts +21 -0
  86. package/dist/core/src/core/Rebase.d.ts +14 -0
  87. package/dist/core/src/core/RebaseProps.d.ts +136 -0
  88. package/dist/core/src/core/RebaseRouter.d.ts +4 -0
  89. package/dist/core/src/core/RebaseRoutes.d.ts +17 -0
  90. package/dist/core/src/core/index.d.ts +4 -0
  91. package/dist/core/src/hooks/ApiConfigContext.d.ts +24 -0
  92. package/dist/core/src/hooks/data/delete.d.ts +31 -0
  93. package/dist/core/src/hooks/data/save.d.ts +34 -0
  94. package/dist/core/src/hooks/data/useCollectionFetch.d.ts +51 -0
  95. package/dist/core/src/hooks/data/useData.d.ts +13 -0
  96. package/dist/core/src/hooks/data/useDataOrder.d.ts +12 -0
  97. package/dist/core/src/hooks/data/useEntityFetch.d.ts +38 -0
  98. package/dist/core/src/hooks/data/useRelationSelector.d.ts +52 -0
  99. package/dist/core/src/hooks/data/useUserSelector.d.ts +31 -0
  100. package/dist/core/src/hooks/index.d.ts +37 -0
  101. package/dist/core/src/hooks/useAdminModeController.d.ts +19 -0
  102. package/dist/core/src/hooks/useAnalyticsController.d.ts +5 -0
  103. package/dist/core/src/hooks/useAuthController.d.ts +11 -0
  104. package/dist/core/src/hooks/useAuthSubscription.d.ts +2 -0
  105. package/dist/core/src/hooks/useBackendStorageSource.d.ts +30 -0
  106. package/dist/core/src/hooks/useBridgeRegistration.d.ts +18 -0
  107. package/dist/core/src/hooks/useBrowserTitleAndIcon.d.ts +6 -0
  108. package/dist/core/src/hooks/useBuildAdminModeController.d.ts +6 -0
  109. package/dist/core/src/hooks/useBuildEffectiveRoleController.d.ts +8 -0
  110. package/dist/core/src/hooks/useBuildLocalConfigurationPersistence.d.ts +2 -0
  111. package/dist/core/src/hooks/useBuildModeController.d.ts +6 -0
  112. package/dist/core/src/hooks/useClipboard.d.ts +57 -0
  113. package/dist/core/src/hooks/useCollapsedGroups.d.ts +12 -0
  114. package/dist/core/src/hooks/useCustomizationController.d.ts +11 -0
  115. package/dist/core/src/hooks/useDialogsController.d.ts +11 -0
  116. package/dist/core/src/hooks/useEffectiveRoleController.d.ts +7 -0
  117. package/dist/core/src/hooks/useInternalUserManagementController.d.ts +12 -0
  118. package/dist/core/src/hooks/useLargeLayout.d.ts +1 -0
  119. package/dist/core/src/hooks/useModeController.d.ts +19 -0
  120. package/dist/core/src/hooks/usePermissions.d.ts +12 -0
  121. package/dist/core/src/hooks/useRebaseClient.d.ts +5 -0
  122. package/dist/core/src/hooks/useRebaseContext.d.ts +11 -0
  123. package/dist/core/src/hooks/useRebaseRegistry.d.ts +34 -0
  124. package/dist/core/src/hooks/useSlot.d.ts +18 -0
  125. package/dist/core/src/hooks/useSnackbarController.d.ts +20 -0
  126. package/dist/core/src/hooks/useStorageSource.d.ts +7 -0
  127. package/dist/core/src/hooks/useStudioBridge.d.ts +91 -0
  128. package/dist/core/src/hooks/useTranslation.d.ts +17 -0
  129. package/dist/core/src/hooks/useUnsavedChangesDialog.d.ts +12 -0
  130. package/dist/core/src/hooks/useUserConfigurationPersistence.d.ts +8 -0
  131. package/dist/core/src/hooks/useValidateAuthenticator.d.ts +21 -0
  132. package/dist/core/src/i18n/RebaseI18nProvider.d.ts +33 -0
  133. package/dist/core/src/index.d.ts +15 -0
  134. package/dist/core/src/internal/common.d.ts +3 -0
  135. package/dist/core/src/internal/useRestoreScroll.d.ts +6 -0
  136. package/dist/core/src/locales/de.d.ts +2 -0
  137. package/dist/core/src/locales/en.d.ts +10 -0
  138. package/dist/core/src/locales/es.d.ts +10 -0
  139. package/dist/core/src/locales/fr.d.ts +2 -0
  140. package/dist/core/src/locales/hi.d.ts +2 -0
  141. package/dist/core/src/locales/it.d.ts +2 -0
  142. package/dist/core/src/locales/pt.d.ts +7 -0
  143. package/dist/core/src/util/constants.d.ts +1 -0
  144. package/dist/core/src/util/createFormexStub.d.ts +2 -0
  145. package/dist/core/src/util/entity_cache.d.ts +27 -0
  146. package/dist/core/src/util/enums.d.ts +5 -0
  147. package/dist/core/src/util/icon_list.d.ts +5 -0
  148. package/dist/core/src/util/icon_synonyms.d.ts +1 -0
  149. package/dist/core/src/util/icons.d.ts +20 -0
  150. package/dist/core/src/util/index.d.ts +10 -0
  151. package/dist/core/src/util/previews.d.ts +4 -0
  152. package/dist/core/src/util/useStorageUploadController.d.ts +38 -0
  153. package/dist/core/src/util/useTraceUpdate.d.ts +2 -0
  154. package/dist/formex/src/Field.d.ts +52 -0
  155. package/dist/formex/src/Formex.d.ts +7 -0
  156. package/dist/formex/src/index.d.ts +5 -0
  157. package/dist/formex/src/types.d.ts +40 -0
  158. package/dist/formex/src/useCreateFormex.d.ts +14 -0
  159. package/dist/formex/src/utils.d.ts +16 -0
  160. package/dist/index.es.js +726 -0
  161. package/dist/index.es.js.map +1 -0
  162. package/dist/index.umd.js +9567 -0
  163. package/dist/index.umd.js.map +1 -0
  164. package/dist/studio/src/components/ApiExplorer/ApiExplorer.d.ts +9 -0
  165. package/dist/studio/src/components/ApiExplorer/EndpointDetail.d.ts +9 -0
  166. package/dist/studio/src/components/ApiExplorer/TryItPanel.d.ts +15 -0
  167. package/dist/studio/src/components/ApiExplorer/parseSpec.d.ts +16 -0
  168. package/dist/studio/src/components/ApiExplorer/types.d.ts +90 -0
  169. package/dist/studio/src/components/AuthSimulationSelector.d.ts +11 -0
  170. package/dist/studio/src/components/Branches/BranchesView.d.ts +1 -0
  171. package/dist/studio/src/components/CronJobs/CronJobsView.d.ts +1 -0
  172. package/dist/studio/src/components/JSEditor/JSEditor.d.ts +1 -0
  173. package/dist/studio/src/components/JSEditor/JSEditorSidebar.d.ts +21 -0
  174. package/dist/studio/src/components/JSEditor/JSMonacoEditor.d.ts +18 -0
  175. package/dist/studio/src/components/RLSEditor/PolicyEditor.d.ts +9 -0
  176. package/dist/studio/src/components/RLSEditor/RLSEditor.d.ts +19 -0
  177. package/dist/studio/src/components/RLSEditor/index.d.ts +1 -0
  178. package/dist/studio/src/components/RebaseStudio.d.ts +2 -0
  179. package/dist/studio/src/components/SQLEditor/ExplainVisualizer.d.ts +24 -0
  180. package/dist/studio/src/components/SQLEditor/MonacoEditor.d.ts +17 -0
  181. package/dist/studio/src/components/SQLEditor/SQLEditor.d.ts +11 -0
  182. package/dist/studio/src/components/SQLEditor/SQLEditorSidebar.d.ts +21 -0
  183. package/dist/studio/src/components/SQLEditor/SchemaBrowser.d.ts +8 -0
  184. package/dist/studio/src/components/SchemaVisualizer/RelationEdge.d.ts +3 -0
  185. package/dist/studio/src/components/SchemaVisualizer/SchemaVisualizer.d.ts +2 -0
  186. package/dist/studio/src/components/SchemaVisualizer/TableNode.d.ts +3 -0
  187. package/dist/studio/src/components/SchemaVisualizer/index.d.ts +5 -0
  188. package/dist/studio/src/components/SchemaVisualizer/schema-visualizer.utils.d.ts +42 -0
  189. package/dist/studio/src/components/SchemaVisualizer/useSchemaGraph.d.ts +37 -0
  190. package/dist/studio/src/components/StorageView/StorageView.d.ts +1 -0
  191. package/dist/studio/src/components/StudioHomePage.d.ts +9 -0
  192. package/dist/studio/src/index.d.ts +4 -0
  193. package/dist/studio/src/utils/entities.d.ts +0 -0
  194. package/dist/studio/src/utils/pgColumnToProperty.d.ts +6 -0
  195. package/dist/studio/src/utils/sql_utils.d.ts +52 -0
  196. package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
  197. package/dist/types/src/controllers/auth.d.ts +119 -0
  198. package/dist/types/src/controllers/client.d.ts +170 -0
  199. package/dist/types/src/controllers/collection_registry.d.ts +46 -0
  200. package/dist/types/src/controllers/customization_controller.d.ts +60 -0
  201. package/dist/types/src/controllers/data.d.ts +168 -0
  202. package/dist/types/src/controllers/data_driver.d.ts +195 -0
  203. package/dist/types/src/controllers/database_admin.d.ts +11 -0
  204. package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
  205. package/dist/types/src/controllers/effective_role.d.ts +4 -0
  206. package/dist/types/src/controllers/email.d.ts +34 -0
  207. package/dist/types/src/controllers/index.d.ts +18 -0
  208. package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
  209. package/dist/types/src/controllers/navigation.d.ts +213 -0
  210. package/dist/types/src/controllers/registry.d.ts +54 -0
  211. package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
  212. package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
  213. package/dist/types/src/controllers/snackbar.d.ts +24 -0
  214. package/dist/types/src/controllers/storage.d.ts +171 -0
  215. package/dist/types/src/index.d.ts +4 -0
  216. package/dist/types/src/rebase_context.d.ts +105 -0
  217. package/dist/types/src/types/backend.d.ts +536 -0
  218. package/dist/types/src/types/backend_hooks.d.ts +187 -0
  219. package/dist/types/src/types/builders.d.ts +15 -0
  220. package/dist/types/src/types/chips.d.ts +5 -0
  221. package/dist/types/src/types/collections.d.ts +857 -0
  222. package/dist/types/src/types/cron.d.ts +102 -0
  223. package/dist/types/src/types/data_source.d.ts +64 -0
  224. package/dist/types/src/types/entities.d.ts +145 -0
  225. package/dist/types/src/types/entity_actions.d.ts +98 -0
  226. package/dist/types/src/types/entity_callbacks.d.ts +173 -0
  227. package/dist/types/src/types/entity_link_builder.d.ts +7 -0
  228. package/dist/types/src/types/entity_overrides.d.ts +10 -0
  229. package/dist/types/src/types/entity_views.d.ts +59 -0
  230. package/dist/types/src/types/export_import.d.ts +21 -0
  231. package/dist/types/src/types/formex.d.ts +40 -0
  232. package/dist/types/src/types/index.d.ts +25 -0
  233. package/dist/types/src/types/locales.d.ts +4 -0
  234. package/dist/types/src/types/modify_collections.d.ts +5 -0
  235. package/dist/types/src/types/plugins.d.ts +282 -0
  236. package/dist/types/src/types/properties.d.ts +1148 -0
  237. package/dist/types/src/types/property_config.d.ts +70 -0
  238. package/dist/types/src/types/relations.d.ts +336 -0
  239. package/dist/types/src/types/slots.d.ts +262 -0
  240. package/dist/types/src/types/translations.d.ts +874 -0
  241. package/dist/types/src/types/user_management_delegate.d.ts +121 -0
  242. package/dist/types/src/types/websockets.d.ts +78 -0
  243. package/dist/types/src/users/index.d.ts +2 -0
  244. package/dist/types/src/users/roles.d.ts +22 -0
  245. package/dist/types/src/users/user.d.ts +46 -0
  246. package/dist/ui/src/components/Alert.d.ts +12 -0
  247. package/dist/ui/src/components/Autocomplete.d.ts +21 -0
  248. package/dist/ui/src/components/Avatar.d.ts +11 -0
  249. package/dist/ui/src/components/Badge.d.ts +8 -0
  250. package/dist/ui/src/components/BooleanSwitch.d.ts +14 -0
  251. package/dist/ui/src/components/BooleanSwitchWithLabel.d.ts +17 -0
  252. package/dist/ui/src/components/Button.d.ts +14 -0
  253. package/dist/ui/src/components/Card.d.ts +9 -0
  254. package/dist/ui/src/components/CenteredView.d.ts +9 -0
  255. package/dist/ui/src/components/Checkbox.d.ts +13 -0
  256. package/dist/ui/src/components/Chip.d.ts +26 -0
  257. package/dist/ui/src/components/CircularProgress.d.ts +5 -0
  258. package/dist/ui/src/components/CircularProgressCenter.d.ts +11 -0
  259. package/dist/ui/src/components/Collapse.d.ts +9 -0
  260. package/dist/ui/src/components/ColorPicker.d.ts +30 -0
  261. package/dist/ui/src/components/Container.d.ts +8 -0
  262. package/dist/ui/src/components/DateTimeField.d.ts +24 -0
  263. package/dist/ui/src/components/DebouncedTextField.d.ts +2 -0
  264. package/dist/ui/src/components/Dialog.d.ts +39 -0
  265. package/dist/ui/src/components/DialogActions.d.ts +7 -0
  266. package/dist/ui/src/components/DialogContent.d.ts +7 -0
  267. package/dist/ui/src/components/DialogTitle.d.ts +10 -0
  268. package/dist/ui/src/components/ErrorBoundary.d.ts +11 -0
  269. package/dist/ui/src/components/ExpandablePanel.d.ts +12 -0
  270. package/dist/ui/src/components/FileUpload.d.ts +23 -0
  271. package/dist/ui/src/components/IconButton.d.ts +12 -0
  272. package/dist/ui/src/components/InfoLabel.d.ts +5 -0
  273. package/dist/ui/src/components/InputLabel.d.ts +11 -0
  274. package/dist/ui/src/components/Label.d.ts +7 -0
  275. package/dist/ui/src/components/LoadingButton.d.ts +7 -0
  276. package/dist/ui/src/components/Markdown.d.ts +10 -0
  277. package/dist/ui/src/components/Menu.d.ts +23 -0
  278. package/dist/ui/src/components/Menubar.d.ts +80 -0
  279. package/dist/ui/src/components/MultiSelect.d.ts +48 -0
  280. package/dist/ui/src/components/Paper.d.ts +6 -0
  281. package/dist/ui/src/components/Popover.d.ts +24 -0
  282. package/dist/ui/src/components/RadioGroup.d.ts +28 -0
  283. package/dist/ui/src/components/ResizablePanels.d.ts +18 -0
  284. package/dist/ui/src/components/SearchBar.d.ts +22 -0
  285. package/dist/ui/src/components/Select.d.ts +43 -0
  286. package/dist/ui/src/components/Separator.d.ts +5 -0
  287. package/dist/ui/src/components/Sheet.d.ts +22 -0
  288. package/dist/ui/src/components/Skeleton.d.ts +6 -0
  289. package/dist/ui/src/components/Slider.d.ts +21 -0
  290. package/dist/ui/src/components/Table.d.ts +34 -0
  291. package/dist/ui/src/components/Tabs.d.ts +19 -0
  292. package/dist/ui/src/components/TextField.d.ts +58 -0
  293. package/dist/ui/src/components/TextareaAutosize.d.ts +43 -0
  294. package/dist/ui/src/components/ToggleButtonGroup.d.ts +30 -0
  295. package/dist/ui/src/components/Tooltip.d.ts +19 -0
  296. package/dist/ui/src/components/Typography.d.ts +36 -0
  297. package/dist/ui/src/components/VirtualTable/VirtualTable.d.ts +11 -0
  298. package/dist/ui/src/components/VirtualTable/VirtualTableCell.d.ts +21 -0
  299. package/dist/ui/src/components/VirtualTable/VirtualTableHeader.d.ts +29 -0
  300. package/dist/ui/src/components/VirtualTable/VirtualTableHeaderRow.d.ts +2 -0
  301. package/dist/ui/src/components/VirtualTable/VirtualTableProps.d.ts +243 -0
  302. package/dist/ui/src/components/VirtualTable/VirtualTableRow.d.ts +3 -0
  303. package/dist/ui/src/components/VirtualTable/index.d.ts +3 -0
  304. package/dist/ui/src/components/VirtualTable/types.d.ts +38 -0
  305. package/dist/ui/src/components/common/SelectInputLabel.d.ts +5 -0
  306. package/dist/ui/src/components/index.d.ts +53 -0
  307. package/dist/ui/src/hooks/PortalContainerContext.d.ts +31 -0
  308. package/dist/ui/src/hooks/index.d.ts +6 -0
  309. package/dist/ui/src/hooks/useDebounceCallback.d.ts +1 -0
  310. package/dist/ui/src/hooks/useDebounceValue.d.ts +1 -0
  311. package/dist/ui/src/hooks/useDebouncedCallback.d.ts +1 -0
  312. package/dist/ui/src/hooks/useInjectStyles.d.ts +7 -0
  313. package/dist/ui/src/hooks/useOutsideAlerter.d.ts +5 -0
  314. package/dist/ui/src/icons/GitHubIcon.d.ts +2 -0
  315. package/dist/ui/src/icons/HandleIcon.d.ts +1 -0
  316. package/dist/ui/src/icons/Icon.d.ts +20 -0
  317. package/dist/ui/src/icons/cool_icon_keys.d.ts +1 -0
  318. package/dist/ui/src/icons/icon_keys.d.ts +1 -0
  319. package/dist/ui/src/icons/index.d.ts +6 -0
  320. package/dist/ui/src/index.d.ts +5 -0
  321. package/dist/ui/src/styles.d.ts +12 -0
  322. package/dist/ui/src/util/chip_colors.d.ts +4 -0
  323. package/dist/ui/src/util/cls.d.ts +2 -0
  324. package/dist/ui/src/util/debounce.d.ts +10 -0
  325. package/dist/ui/src/util/hash.d.ts +1 -0
  326. package/dist/ui/src/util/index.d.ts +4 -0
  327. package/dist/ui/src/util/key_to_icon_component.d.ts +1 -0
  328. package/package.json +84 -0
  329. package/src/components/ApiExplorer/ApiExplorer.tsx +292 -0
  330. package/src/components/ApiExplorer/EndpointDetail.tsx +271 -0
  331. package/src/components/ApiExplorer/TryItPanel.tsx +486 -0
  332. package/src/components/ApiExplorer/parseSpec.ts +104 -0
  333. package/src/components/ApiExplorer/types.ts +84 -0
  334. package/src/components/AuthSimulationSelector.tsx +73 -0
  335. package/src/components/Branches/BranchesView.tsx +370 -0
  336. package/src/components/CronJobs/CronJobsView.tsx +346 -0
  337. package/src/components/JSEditor/JSEditor.tsx +1033 -0
  338. package/src/components/JSEditor/JSEditorSidebar.tsx +340 -0
  339. package/src/components/JSEditor/JSMonacoEditor.tsx +390 -0
  340. package/src/components/RLSEditor/PolicyEditor.tsx +444 -0
  341. package/src/components/RLSEditor/RLSEditor.tsx +771 -0
  342. package/src/components/RLSEditor/index.ts +1 -0
  343. package/src/components/RebaseStudio.tsx +121 -0
  344. package/src/components/SQLEditor/ExplainVisualizer.tsx +128 -0
  345. package/src/components/SQLEditor/MonacoEditor.tsx +203 -0
  346. package/src/components/SQLEditor/SQLEditor.tsx +1417 -0
  347. package/src/components/SQLEditor/SQLEditorSidebar.tsx +174 -0
  348. package/src/components/SQLEditor/SchemaBrowser.tsx +156 -0
  349. package/src/components/SchemaVisualizer/RelationEdge.tsx +102 -0
  350. package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +663 -0
  351. package/src/components/SchemaVisualizer/TableNode.tsx +257 -0
  352. package/src/components/SchemaVisualizer/index.ts +5 -0
  353. package/src/components/SchemaVisualizer/schema-visualizer.utils.ts +140 -0
  354. package/src/components/SchemaVisualizer/useSchemaGraph.ts +397 -0
  355. package/src/components/StorageView/StorageView.tsx +938 -0
  356. package/src/components/StudioHomePage.tsx +357 -0
  357. package/src/index.ts +31 -0
  358. package/src/utils/entities.ts +2 -0
  359. package/src/utils/pgColumnToProperty.test.ts +401 -0
  360. package/src/utils/pgColumnToProperty.ts +275 -0
  361. package/src/utils/sql_utils.test.ts +265 -0
  362. package/src/utils/sql_utils.ts +291 -0
  363. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,938 @@
1
+
2
+ import React, { useState, useEffect, useCallback, useMemo } from "react";
3
+ import { Typography, cls, defaultBorderMixin, Button, IconButton, Tooltip, CircularProgress, ResizablePanels, Chip, Dialog, DialogContent, DialogActions, FileUpload , iconSize } from "@rebasepro/ui";
4
+ import { VideoIcon, Music2Icon, RefreshCwIcon, Trash2Icon, XIcon, PlusIcon, DownloadIcon, UploadCloudIcon, FolderIcon, FileTextIcon, ImageIcon, ArrowLeftIcon, FileIcon, HomeIcon, LayoutGridIcon, ListIcon } from "lucide-react";
5
+ import { useStorageSource, useSnackbarController, ErrorView } from "@rebasepro/core";
6
+ import type { StorageListResult } from "@rebasepro/types";
7
+ import { useSearchParams } from "react-router-dom";
8
+
9
+ // ──────────────────────────────────────────────
10
+ // Types
11
+ // ──────────────────────────────────────────────
12
+
13
+ interface StorageFile {
14
+ name: string;
15
+ fullPath: string;
16
+ isFolder: boolean;
17
+ /** Only populated when metadata is fetched */
18
+ size?: number;
19
+ contentType?: string;
20
+ downloadUrl?: string;
21
+ }
22
+
23
+ // ──────────────────────────────────────────────
24
+ // Helpers
25
+ // ──────────────────────────────────────────────
26
+
27
+ function formatFileSize(bytes: number): string {
28
+ if (bytes < 1024) return `${bytes} B`;
29
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
30
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
31
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
32
+ }
33
+
34
+ function getFileIcon(contentType?: string) {
35
+ if (!contentType) return FileTextIcon;
36
+ if (contentType.startsWith("image/")) return ImageIcon;
37
+ if (contentType.startsWith("video/")) return VideoIcon;
38
+ if (contentType.startsWith("audio/")) return Music2Icon;
39
+ return FileTextIcon;
40
+ }
41
+
42
+ function getExtension(name: string): string {
43
+ const parts = name.split(".");
44
+ return parts.length > 1 ? parts[parts.length - 1].toUpperCase() : "";
45
+ }
46
+
47
+ function breadcrumbSegments(path: string): { label: string; path: string }[] {
48
+ if (!path || path === "/") return [{ label: "Root",
49
+ path: "" }];
50
+ const parts = path.split("/").filter(Boolean);
51
+ const segments = [{ label: "Root",
52
+ path: "" }];
53
+ let accumulated = "";
54
+ for (const part of parts) {
55
+ accumulated = accumulated ? `${accumulated}/${part}` : part;
56
+ segments.push({ label: part,
57
+ path: accumulated });
58
+ }
59
+ return segments;
60
+ }
61
+
62
+ // ──────────────────────────────────────────────
63
+ // Upload Dialog
64
+ // ──────────────────────────────────────────────
65
+
66
+ function UploadDialog({
67
+ open,
68
+ currentPath,
69
+ onClose,
70
+ onUpload
71
+ }: {
72
+ open: boolean;
73
+ currentPath: string;
74
+ onClose: () => void;
75
+ onUpload: (files: File[]) => Promise<void>;
76
+ }) {
77
+ const [uploading, setUploading] = useState(false);
78
+ const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
79
+ const [error, setError] = useState<string | null>(null);
80
+
81
+ const handleFilesAdded = useCallback((files: File[]) => {
82
+ setSelectedFiles(prev => [...prev, ...files]);
83
+ }, []);
84
+
85
+ const handleRemoveFile = useCallback((index: number) => {
86
+ setSelectedFiles(prev => prev.filter((_, i) => i !== index));
87
+ }, []);
88
+
89
+ const handleUpload = useCallback(async () => {
90
+ if (selectedFiles.length === 0) return;
91
+ setUploading(true);
92
+ setError(null);
93
+ try {
94
+ await onUpload(selectedFiles);
95
+ setSelectedFiles([]);
96
+ onClose();
97
+ } catch (err) {
98
+ setError(err instanceof Error ? err.message : "Upload failed");
99
+ } finally {
100
+ setUploading(false);
101
+ }
102
+ }, [selectedFiles, onUpload, onClose]);
103
+
104
+ const handleClose = useCallback(() => {
105
+ if (!uploading) {
106
+ setSelectedFiles([]);
107
+ setError(null);
108
+ onClose();
109
+ }
110
+ }, [uploading, onClose]);
111
+
112
+ return (
113
+ <Dialog open={open} onOpenChange={(o) => !o && handleClose()} maxWidth="md">
114
+ <DialogContent className="p-0">
115
+ <div className="p-4 border-b border-surface-accent-200 dark:border-surface-accent-700">
116
+ <Typography variant="h6">Upload Files</Typography>
117
+ <Typography variant="caption" className="text-text-secondary dark:text-text-secondary-dark mt-1 block">
118
+ to <span className="font-mono text-primary">/{currentPath || "root"}</span>
119
+ </Typography>
120
+ </div>
121
+
122
+ <div className="p-4">
123
+ {/* Drop Zone */}
124
+ <FileUpload
125
+ accept={{} as Record<string, string[]>}
126
+ onFilesAdded={handleFilesAdded}
127
+ size="large"
128
+ uploadDescription={
129
+ <div className="flex flex-col items-center justify-center pointer-events-none mt-2">
130
+ <UploadCloudIcon className="text-surface-accent-400 mb-2 w-8 h-8"/>
131
+ <Typography variant="h6" className="font-bold">
132
+ Drop files here or click to browse
133
+ </Typography>
134
+ <Typography variant="caption" className="text-surface-accent-500 font-medium">
135
+ Any file type supported
136
+ </Typography>
137
+ </div>
138
+ }
139
+ />
140
+
141
+ {error && (
142
+ <Typography variant="caption" className="text-red-500 mt-2 block whitespace-pre-line">
143
+ {error}
144
+ </Typography>
145
+ )}
146
+
147
+ {selectedFiles.length > 0 && (
148
+ <div className="mt-4 space-y-2">
149
+ <Typography variant="caption" className="text-surface-accent-500">
150
+ Selected files ({selectedFiles.length})
151
+ </Typography>
152
+ <div className="max-h-40 overflow-auto space-y-1">
153
+ {selectedFiles.map((file, index) => (
154
+ <div
155
+ key={`${file.name}-${index}`}
156
+ className={cls(
157
+ "flex items-center justify-between p-2 rounded",
158
+ "bg-surface-accent-50 dark:bg-surface-accent-800"
159
+ )}
160
+ >
161
+ <div className="flex-1 min-w-0 mr-2">
162
+ <Typography variant="body2" className="truncate">
163
+ {file.name}
164
+ </Typography>
165
+ <Typography variant="caption" className="text-surface-accent-500">
166
+ {formatFileSize(file.size)}
167
+ </Typography>
168
+ </div>
169
+ <Button
170
+ variant="text"
171
+ size="small"
172
+ onClick={(e) => {
173
+ e.stopPropagation();
174
+ handleRemoveFile(index);
175
+ }}
176
+ disabled={uploading}
177
+ >
178
+ Remove
179
+ </Button>
180
+ </div>
181
+ ))}
182
+ </div>
183
+ </div>
184
+ )}
185
+ </div>
186
+ </DialogContent>
187
+
188
+ <DialogActions>
189
+ <Button variant="text" onClick={handleClose} disabled={uploading}>
190
+ Cancel
191
+ </Button>
192
+ <Button
193
+ variant="filled"
194
+ onClick={handleUpload}
195
+ disabled={selectedFiles.length === 0 || uploading}
196
+ >
197
+ {uploading ? (
198
+ <>
199
+ <CircularProgress size="smallest"/>
200
+ Uploading...
201
+ </>
202
+ ) : (
203
+ `Upload ${selectedFiles.length > 0 ? `(${selectedFiles.length})` : ""}`
204
+ )}
205
+ </Button>
206
+ </DialogActions>
207
+ </Dialog>
208
+ );
209
+ }
210
+
211
+ // ──────────────────────────────────────────────
212
+ // FileIcon preview panel
213
+ // ──────────────────────────────────────────────
214
+
215
+ function FilePreviewPanel({
216
+ file,
217
+ onClose,
218
+ onDelete,
219
+ downloadUrl
220
+ }: {
221
+ file: StorageFile;
222
+ onClose: () => void;
223
+ onDelete: () => void;
224
+ downloadUrl: string | null;
225
+ }) {
226
+ const isImage = file.contentType?.startsWith("image/");
227
+ const isVideo = file.contentType?.startsWith("video/");
228
+ const isAudio = file.contentType?.startsWith("audio/");
229
+ const FileIconComponent = getFileIcon(file.contentType);
230
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
231
+
232
+ return (
233
+ <>
234
+ <div className={cls(
235
+ "flex flex-col h-full border-l",
236
+ defaultBorderMixin,
237
+ "bg-white dark:bg-surface-950"
238
+ )}>
239
+ {/* Header */}
240
+ <div className={cls("flex items-center justify-between p-3 border-b shrink-0", defaultBorderMixin)}>
241
+ <Typography variant="body2" className="font-medium truncate flex-1 mr-2">
242
+ {file.name}
243
+ </Typography>
244
+ <div className="flex items-center gap-0.5">
245
+ {downloadUrl && (
246
+ <Tooltip title="Download">
247
+ <IconButton
248
+ size="small"
249
+ onClick={() => window.open(downloadUrl, "_blank")}
250
+ >
251
+ <DownloadIcon size={iconSize.smallest}/>
252
+ </IconButton>
253
+ </Tooltip>
254
+ )}
255
+ <Tooltip title="Delete">
256
+ <IconButton
257
+ size="small"
258
+ onClick={() => setDeleteDialogOpen(true)}
259
+ className="text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20"
260
+ >
261
+ <Trash2Icon size={iconSize.smallest}/>
262
+ </IconButton>
263
+ </Tooltip>
264
+ <IconButton size="small" onClick={onClose}>
265
+ <XIcon size={iconSize.smallest}/>
266
+ </IconButton>
267
+ </div>
268
+ </div>
269
+
270
+ {/* Preview */}
271
+ <div className="flex-1 overflow-auto">
272
+ <div className="flex flex-col items-center justify-center min-h-[200px] p-4 bg-surface-50 dark:bg-surface-900 border-b border-surface-accent-200 dark:border-surface-accent-700">
273
+ {(() => {
274
+ const ext = getExtension(file.name)?.toLowerCase() || "";
275
+ const isImage = file.contentType?.startsWith("image/") || ["jpg", "jpeg", "png", "gif", "webp", "svg"].includes(ext);
276
+ const isVideo = file.contentType?.startsWith("video/") || ["mp4", "webm", "ogg", "mov"].includes(ext);
277
+ const isAudio = file.contentType?.startsWith("audio/") || ["mp3", "wav", "ogg", "m4a"].includes(ext);
278
+ const downloadUrl = file.downloadUrl;
279
+
280
+ if (isImage && downloadUrl) {
281
+ return (
282
+ <img
283
+ src={downloadUrl}
284
+ alt={file.name}
285
+ className="max-w-full max-h-[400px] object-contain rounded-md shadow-sm"
286
+ />
287
+ );
288
+ } else if (isVideo && downloadUrl) {
289
+ return (
290
+ <video
291
+ src={downloadUrl}
292
+ className="max-w-full max-h-[400px] rounded-md"
293
+ controls
294
+ />
295
+ );
296
+ } else if (isAudio && downloadUrl) {
297
+ return (
298
+ <div className="flex flex-col items-center gap-4">
299
+ <Music2Icon className="text-surface-accent-400 w-10 h-10"/>
300
+ <audio src={downloadUrl} controls className="w-full max-w-xs"/>
301
+ </div>
302
+ );
303
+ } else {
304
+ return (
305
+ <div className="flex flex-col items-center gap-3 text-surface-accent-400">
306
+ <FileIconComponent className="w-10 h-10"/>
307
+ <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark">
308
+ No preview available
309
+ </Typography>
310
+ </div>
311
+ );
312
+ }
313
+ })()}
314
+ </div>
315
+ </div>
316
+
317
+ {/* Metadata */}
318
+ <div className="p-4 space-y-3">
319
+ <div>
320
+ <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark text-[10px] uppercase tracking-wider font-bold mb-1 block">
321
+ File Info
322
+ </Typography>
323
+ </div>
324
+ <div className="grid grid-cols-2 gap-3">
325
+ <div>
326
+ <Typography variant="caption" className="text-surface-accent-500 text-[11px]">
327
+ Name
328
+ </Typography>
329
+ <Typography variant="body2" className="text-[13px] break-all">
330
+ {file.name}
331
+ </Typography>
332
+ </div>
333
+ <div>
334
+ <Typography variant="caption" className="text-surface-accent-500 text-[11px]">
335
+ Type
336
+ </Typography>
337
+ <Typography variant="body2" className="text-[13px]">
338
+ {file.contentType || "Unknown"}
339
+ </Typography>
340
+ </div>
341
+ {file.size !== undefined && (
342
+ <div>
343
+ <Typography variant="caption" className="text-surface-accent-500 text-[11px]">
344
+ Size
345
+ </Typography>
346
+ <Typography variant="body2" className="text-[13px]">
347
+ {formatFileSize(file.size)}
348
+ </Typography>
349
+ </div>
350
+ )}
351
+ <div>
352
+ <Typography variant="caption" className="text-surface-accent-500 text-[11px]">
353
+ Extension
354
+ </Typography>
355
+ <Typography variant="body2" className="text-[13px] font-mono">
356
+ {getExtension(file.name) || "—"}
357
+ </Typography>
358
+ </div>
359
+ <div className="col-span-2">
360
+ <Typography variant="caption" className="text-surface-accent-500 text-[11px]">
361
+ Path
362
+ </Typography>
363
+ <Typography variant="body2" className="text-[13px] font-mono break-all">
364
+ {file.fullPath}
365
+ </Typography>
366
+ </div>
367
+ </div>
368
+
369
+ {downloadUrl && (
370
+ <div className="pt-2">
371
+ <Typography variant="caption" className="text-surface-accent-500 text-[11px] block mb-1">
372
+ URL
373
+ </Typography>
374
+ <div
375
+ className="p-2 rounded bg-surface-100 dark:bg-surface-950 cursor-pointer hover:bg-surface-200 dark:hover:bg-surface-700 transition-colors"
376
+ onClick={() => {
377
+ navigator.clipboard.writeText(downloadUrl);
378
+ }}
379
+ >
380
+ <Typography variant="caption" className="font-mono text-[11px] break-all text-primary">
381
+ {downloadUrl}
382
+ </Typography>
383
+ <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark text-[10px] block mt-1">
384
+ Click to copy
385
+ </Typography>
386
+ </div>
387
+ </div>
388
+ )}
389
+ </div>
390
+ </div>
391
+
392
+ {/* Delete Confirmation */}
393
+ <Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
394
+ <DialogContent>
395
+ <Typography variant="subtitle1" className="mb-2">
396
+ Delete File?
397
+ </Typography>
398
+ <Typography className="text-surface-accent-600 dark:text-surface-accent-400">
399
+ Are you sure you want to delete &quot;{file.name}&quot;?
400
+ This action cannot be undone.
401
+ </Typography>
402
+ </DialogContent>
403
+ <DialogActions>
404
+ <Button variant="text" onClick={() => setDeleteDialogOpen(false)}>
405
+ Cancel
406
+ </Button>
407
+ <Button
408
+ variant="filled"
409
+ color="error"
410
+ onClick={() => {
411
+ setDeleteDialogOpen(false);
412
+ onDelete();
413
+ }}
414
+ >
415
+ Delete
416
+ </Button>
417
+ </DialogActions>
418
+ </Dialog>
419
+ </>
420
+ );
421
+ }
422
+
423
+ // ──────────────────────────────────────────────
424
+ // Main StorageView Export
425
+ // ──────────────────────────────────────────────
426
+
427
+ export const StorageView = () => {
428
+ const storageSource = useStorageSource();
429
+ const snackbarController = useSnackbarController();
430
+
431
+ // Navigation
432
+ const [searchParams, setSearchParams] = useSearchParams();
433
+ const currentPath = searchParams.get("path") || "";
434
+ const [loading, setLoading] = useState(true);
435
+ const [error, setError] = useState<string | null>(null);
436
+
437
+ // Contents
438
+ const [folders, setFolders] = useState<StorageFile[]>([]);
439
+ const [files, setFiles] = useState<StorageFile[]>([]);
440
+
441
+ // Selection and preview
442
+ const [selectedFile, setSelectedFile] = useState<StorageFile | null>(null);
443
+ const [selectedDownloadUrl, setSelectedDownloadUrl] = useState<string | null>(null);
444
+
445
+ // Upload
446
+ const [uploadDialogOpen, setUploadDialogOpen] = useState(false);
447
+
448
+ // View mode
449
+ const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
450
+
451
+ const storageSourceRef = React.useRef(storageSource);
452
+ useEffect(() => {
453
+ storageSourceRef.current = storageSource;
454
+ }, [storageSource]);
455
+
456
+ // ── Fetch directory contents ──
457
+ const fetchContents = useCallback(async (path: string) => {
458
+ setLoading(true);
459
+ setError(null);
460
+ try {
461
+ const result: StorageListResult = await storageSourceRef.current.listObjects(path);
462
+
463
+ const folderItems: StorageFile[] = (result.prefixes ?? []).map(ref => ({
464
+ name: ref.name,
465
+ fullPath: ref.fullPath,
466
+ isFolder: true
467
+ }));
468
+
469
+ // Build file items and fetch metadata for each
470
+ const fileItems: StorageFile[] = await Promise.all(
471
+ (result.items ?? []).map(async (ref) => {
472
+ try {
473
+ const downloadConfig = await storageSourceRef.current.getSignedUrl(ref.fullPath);
474
+ return {
475
+ name: ref.name,
476
+ fullPath: ref.fullPath,
477
+ isFolder: false,
478
+ size: downloadConfig.metadata?.size,
479
+ contentType: downloadConfig.metadata?.contentType,
480
+ downloadUrl: downloadConfig.url ?? undefined
481
+ };
482
+ } catch {
483
+ return {
484
+ name: ref.name,
485
+ fullPath: ref.fullPath,
486
+ isFolder: false
487
+ };
488
+ }
489
+ })
490
+ );
491
+
492
+ setFolders(folderItems);
493
+ setFiles(fileItems);
494
+ } catch (e) {
495
+ console.error("Storage list error:", e);
496
+ setError(e instanceof Error ? e.message : String(e));
497
+ } finally {
498
+ setLoading(false);
499
+ }
500
+ }, []);
501
+
502
+ useEffect(() => {
503
+ fetchContents(currentPath);
504
+ }, [currentPath, fetchContents]);
505
+
506
+ // Navigate to path
507
+ const handleNavigate = useCallback((path: string) => {
508
+ if (!path) {
509
+ setSearchParams({});
510
+ } else {
511
+ setSearchParams({ path });
512
+ }
513
+ setSelectedFile(null);
514
+ setSelectedDownloadUrl(null);
515
+ }, [setSearchParams]);
516
+
517
+ // Navigate up one level
518
+ const handleNavigateUp = useCallback(() => {
519
+ const parts = currentPath.split("/").filter(Boolean);
520
+ parts.pop();
521
+ handleNavigate(parts.join("/"));
522
+ }, [currentPath, handleNavigate]);
523
+
524
+ // Select a file for preview
525
+ const handleSelectFile = useCallback(async (file: StorageFile) => {
526
+ setSelectedFile(file);
527
+ if (file.downloadUrl) {
528
+ setSelectedDownloadUrl(file.downloadUrl);
529
+ } else {
530
+ try {
531
+ const config = await storageSourceRef.current.getSignedUrl(file.fullPath);
532
+ setSelectedDownloadUrl(config.url);
533
+ } catch {
534
+ setSelectedDownloadUrl(null);
535
+ }
536
+ }
537
+ }, []);
538
+
539
+ // Upload files
540
+ const handleUpload = useCallback(async (uploadFiles: File[]) => {
541
+ for (const file of uploadFiles) {
542
+ const key = currentPath ? `${currentPath}/${file.name}` : file.name;
543
+ await storageSourceRef.current.putObject({
544
+ file,
545
+ key
546
+ });
547
+ }
548
+ snackbarController.open({
549
+ type: "success",
550
+ message: `${uploadFiles.length} file${uploadFiles.length > 1 ? "s" : ""} uploaded successfully`
551
+ });
552
+ fetchContents(currentPath);
553
+ }, [currentPath, snackbarController, fetchContents]);
554
+
555
+ // Delete a file
556
+ const handleDeleteFile = useCallback(async (file: StorageFile) => {
557
+ try {
558
+ await storageSourceRef.current.deleteObject(file.fullPath);
559
+ snackbarController.open({ type: "success",
560
+ message: `"${file.name}" deleted` });
561
+ setSelectedFile(null);
562
+ setSelectedDownloadUrl(null);
563
+ fetchContents(currentPath);
564
+ } catch (e) {
565
+ snackbarController.open({ type: "error",
566
+ message: e instanceof Error ? e.message : String(e) });
567
+ }
568
+ }, [currentPath, snackbarController, fetchContents]);
569
+
570
+ // Handle refresh
571
+ const handleRefresh = useCallback(() => {
572
+ fetchContents(currentPath);
573
+ }, [currentPath, fetchContents]);
574
+
575
+ const segments = breadcrumbSegments(currentPath);
576
+
577
+ // ── Render file grid/list ──
578
+ const renderContents = () => {
579
+ if (loading) {
580
+ return (
581
+ <div className="flex-grow flex items-center justify-center">
582
+ <div className="text-center">
583
+ <CircularProgress size="medium"/>
584
+ <Typography variant="body2" className="mt-4 text-text-secondary dark:text-text-secondary-dark font-mono tracking-tight animate-pulse">
585
+ Loading...
586
+ </Typography>
587
+ </div>
588
+ </div>
589
+ );
590
+ }
591
+
592
+ if (error) {
593
+ return (
594
+ <div className="flex-grow flex items-center justify-center p-6 overflow-auto">
595
+ <ErrorView title="Error loading storage" error={error} onRetry={handleRefresh}/>
596
+ </div>
597
+ );
598
+ }
599
+
600
+ const allItems = [...folders, ...files];
601
+
602
+ if (allItems.length === 0) {
603
+ return (
604
+ <div className="flex-grow flex items-center justify-center text-text-disabled dark:text-text-disabled-dark">
605
+ <div className="text-center">
606
+ <svg className="w-12 h-12 mx-auto mb-4 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
607
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1} d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"/>
608
+ </svg>
609
+ <Typography variant="body2">
610
+ This folder is empty
611
+ </Typography>
612
+ <Button className="mt-3" onClick={() => setUploadDialogOpen(true)}>
613
+ <PlusIcon size={iconSize.smallest}/>
614
+ Upload files
615
+ </Button>
616
+ </div>
617
+ </div>
618
+ );
619
+ }
620
+
621
+ if (viewMode === "list") {
622
+ return (
623
+ <div className="flex-grow overflow-auto">
624
+ <table className="w-full">
625
+ <thead>
626
+ <tr className={cls("border-b text-left text-[10px] uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark", defaultBorderMixin)}>
627
+ <th className="px-4 py-2 font-bold">Name</th>
628
+ <th className="px-4 py-2 font-bold w-24">Type</th>
629
+ <th className="px-4 py-2 font-bold w-24 text-right">Size</th>
630
+ </tr>
631
+ </thead>
632
+ <tbody>
633
+ {folders.map(folder => (
634
+ <tr
635
+ key={folder.fullPath}
636
+ className="hover:bg-surface-100 dark:hover:bg-surface-950 cursor-pointer transition-colors border-b border-surface-100 dark:border-surface-950/50"
637
+ onClick={() => handleNavigate(folder.fullPath)}
638
+ >
639
+ <td className="px-4 py-2.5">
640
+ <div className="flex items-center gap-2">
641
+ <FolderIcon size={iconSize.smallest} className="text-amber-500 dark:text-amber-400 shrink-0"/>
642
+ <Typography variant="body2" className="text-[13px] font-medium truncate">
643
+ {folder.name}
644
+ </Typography>
645
+ </div>
646
+ </td>
647
+ <td className="px-4 py-2.5">
648
+ <Typography variant="caption" className="text-text-secondary dark:text-text-secondary-dark">
649
+ Folder
650
+ </Typography>
651
+ </td>
652
+ <td className="px-4 py-2.5 text-right">
653
+ <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark">
654
+
655
+ </Typography>
656
+ </td>
657
+ </tr>
658
+ ))}
659
+ {files.map(file => {
660
+ const FileIconComp = getFileIcon(file.contentType);
661
+ const isSelected = selectedFile?.fullPath === file.fullPath;
662
+ return (
663
+ <tr
664
+ key={file.fullPath}
665
+ className={cls(
666
+ "cursor-pointer transition-colors border-b border-surface-100 dark:border-surface-950/50",
667
+ isSelected
668
+ ? "bg-primary/5 dark:bg-primary/10"
669
+ : "hover:bg-surface-100 dark:hover:bg-surface-950"
670
+ )}
671
+ onClick={() => handleSelectFile(file)}
672
+ >
673
+ <td className="px-4 py-2.5">
674
+ <div className="flex items-center gap-2">
675
+ <FileIconComp size={iconSize.smallest} className="text-surface-accent-400 shrink-0"/>
676
+ <Typography variant="body2" className="text-[13px] truncate">
677
+ {file.name}
678
+ </Typography>
679
+ </div>
680
+ </td>
681
+ <td className="px-4 py-2.5">
682
+ <Typography variant="caption" className="text-text-secondary dark:text-text-secondary-dark">
683
+ {getExtension(file.name) || file.contentType?.split("/")[1]?.toUpperCase() || "—"}
684
+ </Typography>
685
+ </td>
686
+ <td className="px-4 py-2.5 text-right">
687
+ <Typography variant="caption" className="text-text-secondary dark:text-text-secondary-dark font-mono text-[11px]">
688
+ {file.size !== undefined ? formatFileSize(file.size) : "—"}
689
+ </Typography>
690
+ </td>
691
+ </tr>
692
+ );
693
+ })}
694
+ </tbody>
695
+ </table>
696
+ </div>
697
+ );
698
+ }
699
+
700
+ // Grid view
701
+ return (
702
+ <div className="flex-grow overflow-auto p-4">
703
+ {/* Folder cards */}
704
+ {folders.length > 0 && (
705
+ <div className="mb-4">
706
+ <Typography variant="caption" className="text-[10px] uppercase tracking-wider font-bold text-text-disabled dark:text-text-disabled-dark mb-2 block">
707
+ Folders
708
+ </Typography>
709
+ <div className="grid gap-3 grid-cols-[repeat(auto-fill,minmax(140px,1fr))]">
710
+ {folders.map(folder => (
711
+ <div
712
+ key={folder.fullPath}
713
+ className={cls(
714
+ "rounded-lg p-3 cursor-pointer transition-all duration-150 border",
715
+ defaultBorderMixin,
716
+ "hover:bg-surface-100 dark:hover:bg-surface-950 hover:shadow-sm",
717
+ "flex items-center gap-2"
718
+ )}
719
+ onClick={() => handleNavigate(folder.fullPath)}
720
+ >
721
+ <FolderIcon size={iconSize.smallest} className="text-amber-500 dark:text-amber-400 shrink-0"/>
722
+ <Typography variant="body2" className="text-[13px] font-medium truncate">
723
+ {folder.name}
724
+ </Typography>
725
+ </div>
726
+ ))}
727
+ </div>
728
+ </div>
729
+ )}
730
+
731
+ {/* FileIcon cards */}
732
+ {files.length > 0 && (
733
+ <div>
734
+ <Typography variant="caption" className="text-[10px] uppercase tracking-wider font-bold text-text-disabled dark:text-text-disabled-dark mb-2 block">
735
+ Files ({files.length})
736
+ </Typography>
737
+ <div className="grid gap-3 grid-cols-[repeat(auto-fill,minmax(140px,1fr))]">
738
+ {files.map(file => {
739
+ const FileIconComp = getFileIcon(file.contentType);
740
+ const ext = getExtension(file.name)?.toLowerCase() || "";
741
+ const isImage = file.contentType?.startsWith("image/") || ["jpg", "jpeg", "png", "gif", "webp", "svg"].includes(ext);
742
+ const isSelected = selectedFile?.fullPath === file.fullPath;
743
+
744
+ return (
745
+ <div
746
+ key={file.fullPath}
747
+ className={cls(
748
+ "rounded-lg overflow-hidden cursor-pointer transition-all duration-150 border group",
749
+ defaultBorderMixin,
750
+ "hover:shadow-md hover:-translate-y-0.5",
751
+ isSelected && "ring-2 ring-primary"
752
+ )}
753
+ onClick={() => handleSelectFile(file)}
754
+ >
755
+ {/* Thumbnail or icon */}
756
+ <div className="aspect-square relative overflow-hidden bg-surface-100 dark:bg-surface-950 flex items-center justify-center">
757
+ {isImage && file.downloadUrl ? (
758
+ <img
759
+ src={file.downloadUrl}
760
+ alt={file.name}
761
+ className="w-full h-full object-cover"
762
+ />
763
+ ) : (
764
+ <FileIconComp className="text-surface-accent-400 dark:text-surface-accent-500 w-8 h-8"/>
765
+ )}
766
+
767
+ {/* Extension badge */}
768
+ {getExtension(file.name) && (
769
+ <div className="absolute bottom-1.5 right-1.5 px-1.5 py-0.5 rounded text-[9px] font-bold uppercase bg-black/50 text-white backdrop-blur-sm">
770
+ {getExtension(file.name)}
771
+ </div>
772
+ )}
773
+
774
+ {/* Hover overlay */}
775
+ <div className={cls(
776
+ "absolute inset-0 bg-black/0 group-hover:bg-black/10",
777
+ "transition-colors duration-200 pointer-events-none"
778
+ )}/>
779
+ </div>
780
+
781
+ {/* Name & size */}
782
+ <div className="p-2.5">
783
+ <Typography variant="body2" className="text-[12px] font-medium truncate text-surface-900 dark:text-white">
784
+ {file.name}
785
+ </Typography>
786
+ <Typography variant="caption" color="secondary" className="truncate block mt-0.5 text-[11px]">
787
+ {file.size !== undefined ? formatFileSize(file.size) : "—"}
788
+ </Typography>
789
+ </div>
790
+ </div>
791
+ );
792
+ })}
793
+ </div>
794
+ </div>
795
+ )}
796
+ </div>
797
+ );
798
+ };
799
+
800
+ return (
801
+ <div className="flex h-full w-full bg-white dark:bg-surface-950 overflow-hidden text-text-primary dark:text-text-primary-dark">
802
+ <div className="flex h-full w-full">
803
+ {/* Main content */}
804
+ <div className="flex-grow flex flex-col min-w-0 h-full">
805
+ {/* Toolbar */}
806
+ <div className={cls("flex items-center justify-between pr-2 border-b bg-white dark:bg-surface-950 shrink-0", defaultBorderMixin)}>
807
+ <div className="flex items-center gap-1.5 flex-grow overflow-hidden px-3 py-2">
808
+ {/* Breadcrumbs */}
809
+ {currentPath && (
810
+ <Tooltip title="Go up">
811
+ <IconButton size="small" onClick={handleNavigateUp}>
812
+ <ArrowLeftIcon size={iconSize.smallest}/>
813
+ </IconButton>
814
+ </Tooltip>
815
+ )}
816
+ <div className="flex items-center gap-0.5 overflow-x-auto no-scrollbar">
817
+ {segments.map((seg, i) => (
818
+ <React.Fragment key={seg.path}>
819
+ {i > 0 && (
820
+ <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark mx-0.5">/</Typography>
821
+ )}
822
+ <Button
823
+ variant="text"
824
+ size="small"
825
+ className={cls(
826
+ "px-1.5 py-0.5 min-h-0 min-w-0 h-6 text-xs whitespace-nowrap normal-case font-normal",
827
+ i === segments.length - 1
828
+ ? "text-text-primary dark:text-text-primary-dark font-medium"
829
+ : "text-text-secondary dark:text-text-secondary-dark"
830
+ )}
831
+ onClick={() => handleNavigate(seg.path)}
832
+ >
833
+ {seg.label}
834
+ </Button>
835
+ </React.Fragment>
836
+ ))}
837
+ </div>
838
+
839
+ <div className="flex-1"/>
840
+
841
+ {/* FileIcon count */}
842
+ {!loading && (
843
+ <Chip size="small" className="shrink-0 text-[10px]">
844
+ {files.length} file{files.length !== 1 ? "s" : ""}
845
+ {folders.length > 0 ? `, ${folders.length} folder${folders.length !== 1 ? "s" : ""}` : ""}
846
+ </Chip>
847
+ )}
848
+ </div>
849
+
850
+ <div className="flex shrink-0 items-center justify-end gap-1.5 pr-1">
851
+
852
+ <Tooltip title="Grid view">
853
+ <IconButton
854
+ size="small"
855
+ onClick={() => setViewMode("grid")}
856
+ className={cls(viewMode === "grid" && "bg-surface-100 dark:bg-surface-950")}
857
+ >
858
+ <LayoutGridIcon size={iconSize.smallest}/>
859
+ </IconButton>
860
+ </Tooltip>
861
+ <Tooltip title="List view">
862
+ <IconButton
863
+ size="small"
864
+ onClick={() => setViewMode("list")}
865
+ className={cls(viewMode === "list" && "bg-surface-100 dark:bg-surface-950")}
866
+ >
867
+ <ListIcon size={iconSize.smallest}/>
868
+ </IconButton>
869
+ </Tooltip>
870
+
871
+ <div className="h-4 w-px bg-surface-200 dark:bg-surface-950 mx-0.5"/>
872
+
873
+ <Tooltip title="Refresh">
874
+ <IconButton size="small" onClick={handleRefresh} disabled={loading}>
875
+ <RefreshCwIcon size={iconSize.smallest}/>
876
+ </IconButton>
877
+ </Tooltip>
878
+
879
+ <Button
880
+ size="small"
881
+ color="primary"
882
+ onClick={() => setUploadDialogOpen(true)}
883
+ >
884
+ <UploadCloudIcon size={iconSize.smallest} className="mr-1"/>
885
+ Upload
886
+ </Button>
887
+ </div>
888
+ </div>
889
+
890
+ {/* FileIcon grid / list */}
891
+ <div className="flex-grow flex flex-col overflow-hidden min-h-0">
892
+ {renderContents()}
893
+ </div>
894
+
895
+ {/* Status bar */}
896
+ <div className={cls("px-4 py-1.5 border-t bg-surface-50 dark:bg-surface-900 flex items-center justify-between shrink-0", defaultBorderMixin)}>
897
+ <div className="flex items-center gap-4 text-[11px]">
898
+ <span className="text-text-disabled dark:text-text-disabled-dark font-bold uppercase tracking-tighter">
899
+ Path
900
+ </span>
901
+ <span className="font-mono text-text-secondary dark:text-text-secondary-dark">
902
+ /{currentPath || ""}
903
+ </span>
904
+ </div>
905
+ {selectedFile && (
906
+ <div className="text-[11px] text-text-secondary dark:text-text-secondary-dark">
907
+ Selected: <span className="font-mono">{selectedFile.name}</span>
908
+ </div>
909
+ )}
910
+ </div>
911
+ </div>
912
+
913
+ {/* Preview panel */}
914
+ {selectedFile && (
915
+ <div className="w-80 lg:w-96 shrink-0">
916
+ <FilePreviewPanel
917
+ file={selectedFile}
918
+ downloadUrl={selectedDownloadUrl}
919
+ onClose={() => {
920
+ setSelectedFile(null);
921
+ setSelectedDownloadUrl(null);
922
+ }}
923
+ onDelete={() => handleDeleteFile(selectedFile)}
924
+ />
925
+ </div>
926
+ )}
927
+ </div>
928
+
929
+ {/* Upload Dialog */}
930
+ <UploadDialog
931
+ open={uploadDialogOpen}
932
+ currentPath={currentPath}
933
+ onClose={() => setUploadDialogOpen(false)}
934
+ onUpload={handleUpload}
935
+ />
936
+ </div>
937
+ );
938
+ };