@rebasepro/studio 0.12.1-canary.gf5f1d39 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/{ApiExplorer-VRKWqCVI.js → ApiExplorer-BIN9hE7r.js} +5 -3
- package/dist/{ApiExplorer-VRKWqCVI.js.map → ApiExplorer-BIN9hE7r.js.map} +1 -1
- package/dist/{ApiKeysView-vQKqt2wl.js → ApiKeysView-dA-OvlcH.js} +3 -2
- package/dist/{ApiKeysView-vQKqt2wl.js.map → ApiKeysView-dA-OvlcH.js.map} +1 -1
- package/dist/BackupsView-DNS6LdVg.js.map +1 -1
- package/dist/{BranchesView-DH0qnU9J.js → BranchesView-Bgmp_1bm.js} +3 -2
- package/dist/{BranchesView-DH0qnU9J.js.map → BranchesView-Bgmp_1bm.js.map} +1 -1
- package/dist/{CronJobsView-B59i3Mis.js → CronJobsView-CL9q-DX7.js} +5 -3
- package/dist/{CronJobsView-B59i3Mis.js.map → CronJobsView-CL9q-DX7.js.map} +1 -1
- package/dist/{JSEditor-CIkng40W.js → JSEditor-BSTU-NXF.js} +45 -8
- package/dist/JSEditor-BSTU-NXF.js.map +1 -0
- package/dist/{LogsExplorer-BCXoGREr.js → LogsExplorer-BsHaHIMK.js} +6 -4
- package/dist/LogsExplorer-BsHaHIMK.js.map +1 -0
- package/dist/MonacoEditor-D0_QljcX.js.map +1 -1
- package/dist/{RLSEditor-rLlMcZPG.js → RLSEditor-DuzofJ8t.js} +16 -12
- package/dist/RLSEditor-DuzofJ8t.js.map +1 -0
- package/dist/{SQLEditor-DFYk9r1u.js → SQLEditor-CL6DcGRM.js} +29 -17
- package/dist/{SQLEditor-DFYk9r1u.js.map → SQLEditor-CL6DcGRM.js.map} +1 -1
- package/dist/{SchemaVisualizer-C-CaHaRK.js → SchemaVisualizer-vxKSG80C.js} +158 -24
- package/dist/SchemaVisualizer-vxKSG80C.js.map +1 -0
- package/dist/{StorageView-bRwRfwku.js → StorageView-CwDI4sBG.js} +7 -5
- package/dist/StorageView-CwDI4sBG.js.map +1 -0
- package/dist/components/SchemaVisualizer/useLiveRls.d.ts +3 -0
- package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +6 -1
- package/dist/index.es.js +16 -18
- package/dist/index.es.js.map +1 -1
- package/package.json +21 -23
- package/src/components/ApiExplorer/ApiExplorer.tsx +4 -3
- package/src/components/JSEditor/JSEditor.tsx +42 -5
- package/src/components/LogsExplorer/LogsExplorer.tsx +4 -3
- package/src/components/RLSEditor/RLSEditor.tsx +59 -12
- package/src/components/RebaseStudio.tsx +26 -3
- package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +72 -26
- package/src/components/SchemaVisualizer/useLiveRls.ts +86 -0
- package/src/components/SchemaVisualizer/useSchemaGraph.ts +30 -6
- package/src/components/StorageView/StorageView.tsx +5 -4
- package/src/components/StudioHomePage.tsx +1 -1
- package/src/utils/parseSpec.test.ts +9 -3
- package/dist/JSEditor-CIkng40W.js.map +0 -1
- package/dist/LogsExplorer-BCXoGREr.js.map +0 -1
- package/dist/RLSEditor-rLlMcZPG.js.map +0 -1
- package/dist/SchemaVisualizer-C-CaHaRK.js.map +0 -1
- package/dist/StorageView-bRwRfwku.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StorageView-CwDI4sBG.js","names":[],"sources":["../src/components/StorageView/StorageView.tsx"],"sourcesContent":["\nimport React, { useState, useEffect, useCallback, useMemo, useRef } from \"react\";\nimport {\n ArrowLeftIcon,\n Button,\n Checkbox,\n CheckIcon,\n Chip,\n CircularProgress,\n cls,\n CopyIcon,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n DownloadIcon,\n FileIcon,\n FileTextIcon,\n FileUpload,\n FolderIcon,\n FolderPlusIcon,\n HomeIcon,\n IconButton,\n iconSize,\n ImageIcon,\n LayoutGridIcon,\n ListIcon,\n LoadingButton,\n Music2Icon,\n PlusIcon,\n RefreshCwIcon,\n ResizablePanels,\n Select,\n SelectItem,\n TextField,\n Tooltip,\n Trash2Icon,\n Typography,\n UploadCloudIcon,\n VideoIcon,\n XIcon\n} from \"@rebasepro/ui\";\nimport { useStorageSource, useStorageSources, useSnackbarController, ErrorView, useApiBase, useApiConfig } from \"@rebasepro/app\";\nimport { DEFAULT_STORAGE_SOURCE_KEY, type StorageListResult } from \"@rebasepro/types\";\nimport { useSearchParams } from \"react-router\";\nimport { useDropzone } from \"react-dropzone\";\n\n// ──────────────────────────────────────────────\n// Types\n// ──────────────────────────────────────────────\n\ninterface StorageFile {\n name: string;\n fullPath: string;\n isFolder: boolean;\n /** Only populated when metadata is fetched */\n size?: number;\n contentType?: string;\n downloadUrl?: string;\n}\n\n// ──────────────────────────────────────────────\n// Helpers\n// ──────────────────────────────────────────────\n\n/**\n * The query parameter holding the folder being browsed.\n *\n * Namespaced because Studio does not always own the URL it renders under: the\n * SaaS console embeds this view in a page that keeps its own state in `?tab=`\n * and `?sub=`, and a bare `path` is a name any host app might already be using.\n */\nconst STORAGE_PATH_PARAM = \"storagePath\";\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;\n}\n\nfunction getFileIcon(contentType?: string) {\n if (!contentType) return FileTextIcon;\n if (contentType.startsWith(\"image/\")) return ImageIcon;\n if (contentType.startsWith(\"video/\")) return VideoIcon;\n if (contentType.startsWith(\"audio/\")) return Music2Icon;\n return FileTextIcon;\n}\n\nfunction getExtension(name: string): string {\n const parts = name.split(\".\");\n return parts.length > 1 ? parts[parts.length - 1].toUpperCase() : \"\";\n}\n\nfunction breadcrumbSegments(path: string): { label: string; path: string }[] {\n if (!path || path === \"/\") return [{ label: \"Root\",\npath: \"\" }];\n const parts = path.split(\"/\").filter(Boolean);\n const segments = [{ label: \"Root\",\npath: \"\" }];\n let accumulated = \"\";\n for (const part of parts) {\n accumulated = accumulated ? `${accumulated}/${part}` : part;\n segments.push({ label: part,\npath: accumulated });\n }\n return segments;\n}\n\n// ──────────────────────────────────────────────\n// Upload Dialog\n// ──────────────────────────────────────────────\n\nfunction UploadDialog({\n open,\n currentPath,\n onClose,\n onUpload\n}: {\n open: boolean;\n currentPath: string;\n onClose: () => void;\n onUpload: (files: File[]) => Promise<void>;\n}) {\n const [uploading, setUploading] = useState(false);\n const [selectedFiles, setSelectedFiles] = useState<File[]>([]);\n const [error, setError] = useState<string | null>(null);\n\n const handleFilesAdded = useCallback((files: File[]) => {\n setSelectedFiles(prev => [...prev, ...files]);\n }, []);\n\n const handleRemoveFile = useCallback((index: number) => {\n setSelectedFiles(prev => prev.filter((_, i) => i !== index));\n }, []);\n\n const handleUpload = useCallback(async () => {\n if (selectedFiles.length === 0) return;\n setUploading(true);\n setError(null);\n try {\n await onUpload(selectedFiles);\n setSelectedFiles([]);\n onClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Upload failed\");\n } finally {\n setUploading(false);\n }\n }, [selectedFiles, onUpload, onClose]);\n\n const handleClose = useCallback(() => {\n if (!uploading) {\n setSelectedFiles([]);\n setError(null);\n onClose();\n }\n }, [uploading, onClose]);\n\n return (\n <Dialog open={open} onOpenChange={(o) => !o && handleClose()} maxWidth=\"md\">\n <DialogTitle>\n Upload Files\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark mt-0.5 block\">\n to <span className=\"font-mono text-primary\">/{currentPath || \"root\"}</span>\n </Typography>\n </DialogTitle>\n <DialogContent className=\"space-y-4\">\n <FileUpload\n onFilesAdded={handleFilesAdded}\n size=\"large\"\n uploadDescription={\n <div className=\"flex flex-col items-center justify-center pointer-events-none\">\n <UploadCloudIcon className=\"text-surface-accent-400 mb-2 w-8 h-8\"/>\n <Typography variant=\"label\">\n Drop files here or click to browse\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n Any file type supported\n </Typography>\n </div>\n }\n />\n\n {error && (\n <Typography variant=\"caption\" className=\"text-red-500 block whitespace-pre-line\">\n {error}\n </Typography>\n )}\n\n {selectedFiles.length > 0 && (\n <div className=\"space-y-2\">\n <Typography variant=\"caption\" color=\"secondary\">\n Selected files ({selectedFiles.length})\n </Typography>\n <div className=\"max-h-40 overflow-auto space-y-1\">\n {selectedFiles.map((file, index) => (\n <div\n key={`${file.name}-${index}`}\n className=\"flex items-center justify-between p-2 rounded bg-surface-100 dark:bg-surface-800\"\n >\n <div className=\"flex-1 min-w-0 mr-2\">\n <Typography variant=\"body2\" className=\"truncate\">\n {file.name}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n {formatFileSize(file.size)}\n </Typography>\n </div>\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n handleRemoveFile(index);\n }}\n disabled={uploading}\n >\n <XIcon size={14}/>\n </IconButton>\n </div>\n ))}\n </div>\n </div>\n )}\n </DialogContent>\n\n <DialogActions>\n <Button variant=\"text\" onClick={handleClose} disabled={uploading}>\n Cancel\n </Button>\n <Button\n variant=\"filled\"\n onClick={handleUpload}\n disabled={selectedFiles.length === 0 || uploading}\n startIcon={uploading ? <CircularProgress size=\"smallest\"/> : <UploadCloudIcon size={14}/>}\n >\n {uploading ? \"Uploading...\" : `Upload${selectedFiles.length > 0 ? ` (${selectedFiles.length})` : \"\"}`}\n </Button>\n </DialogActions>\n </Dialog>\n );\n}\n\n// ──────────────────────────────────────────────\n// FileIcon preview panel\n// ──────────────────────────────────────────────\n\nfunction FilePreviewPanel({\n file,\n onClose,\n onDelete,\n downloadUrl\n}: {\n file: StorageFile;\n onClose: () => void;\n onDelete: () => void;\n downloadUrl: string | null;\n}) {\n const isImage = file.contentType?.startsWith(\"image/\");\n const isVideo = file.contentType?.startsWith(\"video/\");\n const isAudio = file.contentType?.startsWith(\"audio/\");\n const FileIconComponent = getFileIcon(file.contentType);\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n const [urlCopied, setUrlCopied] = useState(false);\n\n return (\n <>\n <div className={cls(\n \"flex flex-col h-full border-l\",\n defaultBorderMixin,\n \"bg-white dark:bg-surface-800\"\n )}>\n {/* Header */}\n <div className={cls(\"flex items-center justify-between p-3 border-b shrink-0\", defaultBorderMixin)}>\n <Typography variant=\"body2\" className=\"font-medium truncate flex-1 mr-2\">\n {file.name}\n </Typography>\n <div className=\"flex items-center gap-0.5\">\n {downloadUrl && (\n <Tooltip title=\"Download\">\n <IconButton\n size=\"small\"\n onClick={() => window.open(downloadUrl, \"_blank\")}\n >\n <DownloadIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n )}\n <Tooltip title=\"Delete\">\n <IconButton\n size=\"small\"\n onClick={() => setDeleteDialogOpen(true)}\n className=\"text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20\"\n >\n <Trash2Icon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n <IconButton size=\"small\" onClick={onClose}>\n <XIcon size={iconSize.smallest}/>\n </IconButton>\n </div>\n </div>\n\n {/* Preview */}\n <div className=\"flex-1 overflow-auto\">\n <div className={cls(\"flex flex-col items-center justify-center min-h-[200px] p-4 bg-surface-50 dark:bg-surface-800 border-b\", defaultBorderMixin)}>\n {(() => {\n const ext = getExtension(file.name)?.toLowerCase() || \"\";\n const isImage = file.contentType?.startsWith(\"image/\") || [\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\"].includes(ext);\n const isVideo = file.contentType?.startsWith(\"video/\") || [\"mp4\", \"webm\", \"ogg\", \"mov\"].includes(ext);\n const isAudio = file.contentType?.startsWith(\"audio/\") || [\"mp3\", \"wav\", \"ogg\", \"m4a\"].includes(ext);\n const downloadUrl = file.downloadUrl;\n\n if (isImage && downloadUrl) {\n return (\n <img\n src={downloadUrl}\n alt={file.name}\n className=\"max-w-full max-h-[400px] object-contain rounded-md shadow-sm\"\n />\n );\n } else if (isVideo && downloadUrl) {\n return (\n <video\n src={downloadUrl}\n className=\"max-w-full max-h-[400px] rounded-md\"\n controls\n />\n );\n } else if (isAudio && downloadUrl) {\n return (\n <div className=\"flex flex-col items-center gap-4\">\n <Music2Icon className=\"text-surface-accent-400 w-10 h-10\"/>\n <audio src={downloadUrl} controls className=\"w-full max-w-xs\"/>\n </div>\n );\n } else {\n return (\n <div className=\"flex flex-col items-center gap-3 text-surface-accent-400\">\n <FileIconComponent className=\"w-10 h-10\"/>\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">\n No preview available\n </Typography>\n </div>\n );\n }\n })()}\n </div>\n </div>\n\n {/* Metadata */}\n <div className=\"p-4 space-y-3\">\n <div>\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark text-[10px] uppercase tracking-wider font-bold mb-1 block\">\n File Info\n </Typography>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n Name\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px] break-all\">\n {file.name}\n </Typography>\n </div>\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n Type\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px]\">\n {file.contentType || \"Unknown\"}\n </Typography>\n </div>\n {file.size !== undefined && (\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n Size\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px]\">\n {formatFileSize(file.size)}\n </Typography>\n </div>\n )}\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n Extension\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px] font-mono\">\n {getExtension(file.name) || \"—\"}\n </Typography>\n </div>\n <div className=\"col-span-2\">\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n Path\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px] font-mono break-all\">\n {file.fullPath}\n </Typography>\n </div>\n </div>\n\n {downloadUrl && (\n <div className=\"pt-2\">\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px] block mb-1\">\n URL\n </Typography>\n <div\n className={cls(\n \"flex items-center gap-2 p-2 rounded cursor-pointer transition-colors\",\n \"bg-surface-100 dark:bg-surface-800 hover:bg-surface-200 dark:hover:bg-surface-700\"\n )}\n onClick={() => {\n const fullUrl = downloadUrl.startsWith(\"http\")\n ? downloadUrl\n : `${window.location.origin}${downloadUrl.startsWith(\"/\") ? \"\" : \"/\"}${downloadUrl}`;\n navigator.clipboard.writeText(fullUrl).then(() => {\n setUrlCopied(true);\n setTimeout(() => setUrlCopied(false), 2000);\n });\n }}\n >\n <Typography variant=\"caption\" className=\"font-mono text-[11px] truncate flex-1 min-w-0 text-primary\">\n {(() => {\n const fullUrl = downloadUrl.startsWith(\"http\")\n ? downloadUrl\n : `${window.location.origin}${downloadUrl.startsWith(\"/\") ? \"\" : \"/\"}${downloadUrl}`;\n return fullUrl;\n })()}\n </Typography>\n <Tooltip title={urlCopied ? \"Copied!\" : \"Copy URL\"}>\n <div className=\"shrink-0\">\n {urlCopied\n ? <CheckIcon size={14} className=\"text-green-500\"/>\n : <CopyIcon size={14} className=\"text-surface-accent-400\"/>\n }\n </div>\n </Tooltip>\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Delete Confirmation */}\n <Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>\n <DialogTitle hidden>Delete File</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"mb-2\">\n Delete File?\n </Typography>\n <Typography className=\"text-surface-accent-600 dark:text-surface-accent-400\">\n Are you sure you want to delete "{file.name}"?\n This action cannot be undone.\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={() => setDeleteDialogOpen(false)}>\n Cancel\n </Button>\n <Button\n variant=\"filled\"\n color=\"error\"\n onClick={() => {\n setDeleteDialogOpen(false);\n onDelete();\n }}\n >\n Delete\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n}\n\n// ──────────────────────────────────────────────\n// Main StorageView Export\n// ──────────────────────────────────────────────\n\nexport const StorageView = () => {\n const defaultStorageSource = useStorageSource();\n const storageSources = useStorageSources();\n const snackbarController = useSnackbarController();\n\n // Available backends to browse. Always includes the default; named\n // sources come from `<Rebase storageSources={...}>`.\n const sourceKeys = useMemo(() => {\n const keys = Object.keys(storageSources.sources);\n if (!keys.includes(DEFAULT_STORAGE_SOURCE_KEY)) keys.unshift(DEFAULT_STORAGE_SOURCE_KEY);\n return keys;\n }, [storageSources.sources]);\n\n const [selectedSourceKey, setSelectedSourceKey] = useState<string>(DEFAULT_STORAGE_SOURCE_KEY);\n\n const storageSource = storageSources.sources[selectedSourceKey] ?? defaultStorageSource;\n\n // Navigation\n const [searchParams, setSearchParams] = useSearchParams();\n // Accepts the historical unqualified `path` so links already shared keep\n // working; only the namespaced one is ever written.\n const currentPath = searchParams.get(STORAGE_PATH_PARAM) || searchParams.get(\"path\") || \"\";\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n // Contents\n const [folders, setFolders] = useState<StorageFile[]>([]);\n const [files, setFiles] = useState<StorageFile[]>([]);\n\n // Selection and preview\n const [selectedFile, setSelectedFile] = useState<StorageFile | null>(null);\n const [selectedDownloadUrl, setSelectedDownloadUrl] = useState<string | null>(null);\n\n // Upload\n const [uploadDialogOpen, setUploadDialogOpen] = useState(false);\n\n // View mode\n const [viewMode, setViewMode] = useState<\"grid\" | \"list\">(\"grid\");\n\n // Multi-selection\n const [selectedPaths, setSelectedPaths] = useState<Set<string>>(new Set());\n const lastClickedRef = useRef<string | null>(null);\n\n // Bulk / folder delete\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n const [deleteDialogTarget, setDeleteDialogTarget] = useState<\"selection\" | StorageFile | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n // New folder\n const [newFolderDialogOpen, setNewFolderDialogOpen] = useState(false);\n const [newFolderName, setNewFolderName] = useState(\"\");\n const [creatingFolder, setCreatingFolder] = useState(false);\n const apiConfig = useApiConfig();\n const apiBase = useApiBase();\n\n const storageSourceRef = React.useRef(storageSource);\n useEffect(() => {\n storageSourceRef.current = storageSource;\n }, [storageSource]);\n\n // ── Fetch directory contents ──\n const fetchContents = useCallback(async (path: string) => {\n setLoading(true);\n setError(null);\n try {\n const result: StorageListResult = await storageSourceRef.current.listObjects(path);\n\n const folderItems: StorageFile[] = (result.prefixes ?? []).map(ref => ({\n name: ref.name,\n fullPath: ref.fullPath,\n isFolder: true\n }));\n\n // Build file items and fetch metadata for each\n const fileItems: StorageFile[] = await Promise.all(\n (result.items ?? []).map(async (ref) => {\n try {\n const downloadConfig = await storageSourceRef.current.getSignedUrl(ref.fullPath);\n return {\n name: ref.name,\n fullPath: ref.fullPath,\n isFolder: false,\n size: downloadConfig.metadata?.size,\n contentType: downloadConfig.metadata?.contentType,\n downloadUrl: downloadConfig.url ?? undefined\n };\n } catch {\n return {\n name: ref.name,\n fullPath: ref.fullPath,\n isFolder: false\n };\n }\n })\n );\n\n setFolders(folderItems);\n setFiles(fileItems);\n } catch (e) {\n console.error(\"Storage list error:\", e);\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n setLoading(false);\n }\n }, []);\n\n useEffect(() => {\n // `selectedSourceKey` is a dep so switching backend re-lists; the\n // ref-sync effect above runs first, so `storageSourceRef` is current.\n fetchContents(currentPath);\n }, [currentPath, fetchContents, selectedSourceKey]);\n\n // Navigate to path\n //\n // Updates only this view's own parameter and leaves the rest of the query\n // string alone. It used to assign the whole thing (`setSearchParams({})`),\n // which is fine when Studio owns the URL but destructive when it is\n // embedded: the SaaS console keeps the open tab in `?tab=`, so opening a\n // folder erased it and bounced the user out of Storage entirely. The key is\n // namespaced for the same reason — a bare `path` is a name a host app can\n // easily be using for something else.\n const handleNavigate = useCallback((path: string) => {\n setSearchParams(prev => {\n const next = new URLSearchParams(prev);\n if (path) next.set(STORAGE_PATH_PARAM, path);\n else next.delete(STORAGE_PATH_PARAM);\n return next;\n }, { replace: true });\n setSelectedFile(null);\n setSelectedDownloadUrl(null);\n setSelectedPaths(new Set());\n lastClickedRef.current = null;\n }, [setSearchParams]);\n\n // Navigate up one level\n const handleNavigateUp = useCallback(() => {\n const parts = currentPath.split(\"/\").filter(Boolean);\n parts.pop();\n handleNavigate(parts.join(\"/\"));\n }, [currentPath, handleNavigate]);\n\n // All items (folders + files) in display order, for shift-range select\n const allItems = useMemo(() => [...folders, ...files], [folders, files]);\n\n // ── Multi-select click handler ──\n const handleItemClick = useCallback((item: StorageFile, e: React.MouseEvent) => {\n const path = item.fullPath;\n if (e.metaKey || e.ctrlKey) {\n // Toggle individual item\n setSelectedPaths(prev => {\n const next = new Set(prev);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n return next;\n });\n lastClickedRef.current = path;\n } else if (e.shiftKey && lastClickedRef.current) {\n // Range select\n const allPaths = allItems.map(i => i.fullPath);\n const anchorIdx = allPaths.indexOf(lastClickedRef.current);\n const currentIdx = allPaths.indexOf(path);\n if (anchorIdx >= 0 && currentIdx >= 0) {\n const [start, end] = anchorIdx < currentIdx ? [anchorIdx, currentIdx] : [currentIdx, anchorIdx];\n setSelectedPaths(prev => {\n const next = new Set(prev);\n for (let i = start; i <= end; i++) next.add(allPaths[i]);\n return next;\n });\n }\n } else {\n // Exclusive select\n setSelectedPaths(new Set([path]));\n lastClickedRef.current = path;\n // Also open preview if it's a file\n if (!item.isFolder) {\n setSelectedFile(item);\n if (item.downloadUrl) {\n setSelectedDownloadUrl(item.downloadUrl);\n } else {\n storageSourceRef.current.getSignedUrl(item.fullPath)\n .then(config => setSelectedDownloadUrl(config.url))\n .catch(() => setSelectedDownloadUrl(null));\n }\n } else {\n setSelectedFile(null);\n setSelectedDownloadUrl(null);\n }\n }\n }, [allItems]);\n\n // Double-click: open folder or preview file\n const handleItemDoubleClick = useCallback((item: StorageFile) => {\n if (item.isFolder) {\n handleNavigate(item.fullPath);\n } else {\n setSelectedFile(item);\n if (item.downloadUrl) {\n setSelectedDownloadUrl(item.downloadUrl);\n } else {\n storageSourceRef.current.getSignedUrl(item.fullPath)\n .then(config => setSelectedDownloadUrl(config.url))\n .catch(() => setSelectedDownloadUrl(null));\n }\n }\n }, [handleNavigate]);\n\n // Upload files\n const handleUpload = useCallback(async (uploadFiles: File[]) => {\n for (const file of uploadFiles) {\n const key = currentPath ? `${currentPath}/${file.name}` : file.name;\n await storageSourceRef.current.putObject({\n file,\n key\n });\n }\n snackbarController.open({\n type: \"success\",\n message: `${uploadFiles.length} file${uploadFiles.length > 1 ? \"s\" : \"\"} uploaded successfully`\n });\n await fetchContents(currentPath);\n }, [currentPath, snackbarController, fetchContents]);\n\n // Create new folder\n const handleCreateFolder = useCallback(async () => {\n if (!newFolderName.trim() || !apiConfig?.apiUrl) return;\n\n // Validate folder name\n const name = newFolderName.trim();\n if (name.includes(\"/\") || name.includes(\"\\\\\")) {\n snackbarController.open({ type: \"error\",\nmessage: \"Folder name cannot contain slashes\" });\n return;\n }\n\n // Check if folder already exists\n const existingFolder = folders.find(f => f.name === name);\n if (existingFolder) {\n snackbarController.open({ type: \"error\",\nmessage: `Folder \"${name}\" already exists` });\n return;\n }\n\n setCreatingFolder(true);\n try {\n const folderPath = currentPath ? `default/${currentPath}/${name}` : `default/${name}`;\n const token = apiConfig.getAuthToken ? await apiConfig.getAuthToken() : null;\n const response = await fetch(`${apiBase}/storage/folder`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(token ? { \"Authorization\": `Bearer ${token}` } : {})\n },\n body: JSON.stringify({ path: folderPath })\n });\n\n if (!response.ok) {\n const err = await response.json().catch(() => ({ error: \"Failed to create folder\" }));\n throw new Error(err.error || \"Failed to create folder\");\n }\n\n snackbarController.open({ type: \"success\",\nmessage: `Folder \"${name}\" created` });\n setNewFolderDialogOpen(false);\n setNewFolderName(\"\");\n await fetchContents(currentPath);\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n } finally {\n setCreatingFolder(false);\n }\n }, [newFolderName, currentPath, apiConfig, apiBase, snackbarController, fetchContents, folders]);\n\n // Drag-and-drop on main view\n const handleDropFiles = useCallback(async (droppedFiles: File[]) => {\n if (droppedFiles.length === 0) return;\n try {\n for (const file of droppedFiles) {\n const key = currentPath ? `${currentPath}/${file.name}` : file.name;\n await storageSourceRef.current.putObject({ file,\nkey });\n }\n snackbarController.open({\n type: \"success\",\n message: `${droppedFiles.length} file${droppedFiles.length > 1 ? \"s\" : \"\"} uploaded successfully`\n });\n await fetchContents(currentPath);\n } catch (e) {\n snackbarController.open({\n type: \"error\",\n message: e instanceof Error ? e.message : String(e)\n });\n }\n }, [currentPath, snackbarController, fetchContents]);\n\n const {\n getRootProps: getDropRootProps,\n getInputProps: getDropInputProps,\n isDragActive\n } = useDropzone({\n onDrop: handleDropFiles,\n noClick: true,\n noKeyboard: true,\n noDragEventsBubbling: true\n });\n\n // ── Recursive folder delete helper ──\n const deleteFolderRecursive = useCallback(async (prefix: string) => {\n const result = await storageSourceRef.current.listObjects(prefix);\n // Delete all files in this level\n for (const item of result.items ?? []) {\n await storageSourceRef.current.deleteObject(item.fullPath);\n }\n // Recurse into sub-folders\n for (const sub of result.prefixes ?? []) {\n await deleteFolderRecursive(sub.fullPath);\n }\n // Delete the folder entry itself (needed for local filesystem)\n try {\n await storageSourceRef.current.deleteObject(prefix);\n } catch {\n // Ignore — S3 folders are virtual and may not exist as objects\n }\n }, []);\n\n // Delete a single file\n const handleDeleteFile = useCallback(async (file: StorageFile) => {\n try {\n if (file.isFolder) {\n await deleteFolderRecursive(file.fullPath);\n } else {\n await storageSourceRef.current.deleteObject(file.fullPath);\n }\n snackbarController.open({ type: \"success\",\nmessage: `\"${file.name}\" deleted` });\n setSelectedFile(null);\n setSelectedDownloadUrl(null);\n setSelectedPaths(prev => {\n const next = new Set(prev);\n next.delete(file.fullPath);\n return next;\n });\n fetchContents(currentPath);\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n }\n }, [currentPath, snackbarController, fetchContents, deleteFolderRecursive]);\n\n // Bulk delete (selected items)\n const handleBulkDelete = useCallback(async () => {\n setDeleting(true);\n try {\n const items = allItems.filter(i => selectedPaths.has(i.fullPath));\n for (const item of items) {\n if (item.isFolder) {\n await deleteFolderRecursive(item.fullPath);\n } else {\n await storageSourceRef.current.deleteObject(item.fullPath);\n }\n }\n snackbarController.open({ type: \"success\",\nmessage: `${items.length} item${items.length !== 1 ? \"s\" : \"\"} deleted` });\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedDownloadUrl(null);\n await fetchContents(currentPath);\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n } finally {\n setDeleting(false);\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }\n }, [allItems, selectedPaths, currentPath, snackbarController, fetchContents, deleteFolderRecursive]);\n\n // Confirm delete for a single folder\n const handleConfirmDeleteFolder = useCallback(async () => {\n if (!deleteDialogTarget || deleteDialogTarget === \"selection\") return;\n setDeleting(true);\n try {\n await deleteFolderRecursive(deleteDialogTarget.fullPath);\n snackbarController.open({ type: \"success\",\nmessage: `Folder \"${deleteDialogTarget.name}\" deleted` });\n setSelectedPaths(prev => {\n const next = new Set(prev);\n next.delete(deleteDialogTarget.fullPath);\n return next;\n });\n await fetchContents(currentPath);\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n } finally {\n setDeleting(false);\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }\n }, [deleteDialogTarget, currentPath, snackbarController, fetchContents, deleteFolderRecursive]);\n\n // Select all / deselect\n const handleSelectAll = useCallback(() => {\n if (selectedPaths.size === allItems.length) {\n setSelectedPaths(new Set());\n } else {\n setSelectedPaths(new Set(allItems.map(i => i.fullPath)));\n }\n }, [allItems, selectedPaths]);\n\n // ── Keyboard shortcuts ──\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n // Don't handle shortcuts when a dialog is open\n if (deleteDialogOpen || uploadDialogOpen || newFolderDialogOpen) return;\n // Cmd/Ctrl+A: select all\n if ((e.metaKey || e.ctrlKey) && e.key === \"a\") {\n e.preventDefault();\n handleSelectAll();\n }\n // Escape: deselect\n if (e.key === \"Escape\") {\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedDownloadUrl(null);\n }\n // Delete / Backspace: delete selected\n if ((e.key === \"Delete\" || e.key === \"Backspace\") && selectedPaths.size > 0 && !e.metaKey && !e.ctrlKey) {\n // Don't trigger if user is typing in an input\n if ((e.target as HTMLElement)?.tagName === \"INPUT\" || (e.target as HTMLElement)?.tagName === \"TEXTAREA\") return;\n e.preventDefault();\n setDeleteDialogTarget(\"selection\");\n setDeleteDialogOpen(true);\n }\n };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n }, [handleSelectAll, selectedPaths, deleteDialogOpen, uploadDialogOpen, newFolderDialogOpen]);\n\n // Handle refresh\n const handleRefresh = useCallback(() => {\n fetchContents(currentPath);\n }, [currentPath, fetchContents]);\n\n const segments = breadcrumbSegments(currentPath);\n\n\n // ── Render file grid/list ──\n const renderContents = () => {\n if (loading) {\n return (\n <div className=\"flex-grow flex items-center justify-center\">\n <div className=\"text-center\">\n <CircularProgress size=\"medium\"/>\n <Typography variant=\"body2\" className=\"mt-4 text-text-secondary dark:text-text-secondary-dark font-mono tracking-tight animate-pulse\">\n Loading...\n </Typography>\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex-grow flex items-center justify-center p-6 overflow-auto\">\n <ErrorView title=\"Error loading storage\" error={error} onRetry={handleRefresh}/>\n </div>\n );\n }\n\n\n if (allItems.length === 0) {\n return (\n <div className=\"flex-grow flex items-center justify-center text-text-disabled dark:text-text-disabled-dark\">\n <div className=\"text-center\">\n <svg className=\"w-12 h-12 mx-auto mb-4 opacity-50\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\"/>\n </svg>\n <Typography variant=\"body2\">\n This folder is empty\n </Typography>\n <div className=\"flex items-center gap-2 mt-3\">\n <Button variant=\"text\" onClick={() => {\n setNewFolderName(\"\");\n setNewFolderDialogOpen(true);\n }}>\n <FolderPlusIcon size={iconSize.smallest}/>\n New folder\n </Button>\n <Button onClick={() => setUploadDialogOpen(true)}>\n <PlusIcon size={iconSize.smallest}/>\n Upload files\n </Button>\n </div>\n </div>\n </div>\n );\n }\n\n if (viewMode === \"list\") {\n return (\n <div className=\"flex-grow overflow-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className={cls(\"border-b text-left text-[10px] uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\", defaultBorderMixin)}>\n <th className=\"pl-3 pr-0 py-2 w-8\">\n <Checkbox\n size=\"small\"\n checked={allItems.length > 0 && selectedPaths.size === allItems.length}\n indeterminate={selectedPaths.size > 0 && selectedPaths.size < allItems.length}\n onCheckedChange={handleSelectAll}\n />\n </th>\n <th className=\"px-2 py-2 font-bold\">Name</th>\n <th className=\"px-4 py-2 font-bold w-24\">Type</th>\n <th className=\"px-4 py-2 font-bold w-24 text-right\">Size</th>\n <th className=\"px-2 py-2 w-10\"/>\n </tr>\n </thead>\n <tbody>\n {folders.map(folder => {\n const isChecked = selectedPaths.has(folder.fullPath);\n return (\n <tr\n key={folder.fullPath}\n data-storage-item\n className={cls(\n \"cursor-pointer transition-colors border-b group\",\n defaultBorderMixin,\n isChecked\n ? \"bg-primary/5 dark:bg-primary/10\"\n : \"hover:bg-surface-100 dark:hover:bg-surface-800\"\n )}\n onClick={(e) => handleItemClick(folder, e)}\n onDoubleClick={() => handleItemDoubleClick(folder)}\n >\n <td className=\"pl-3 pr-0 py-2.5\" onClick={(e) => e.stopPropagation()}>\n <Checkbox\n size=\"small\"\n checked={isChecked}\n onCheckedChange={() => {\n setSelectedPaths(prev => {\n const next = new Set(prev);\n if (next.has(folder.fullPath)) next.delete(folder.fullPath);\n else next.add(folder.fullPath);\n return next;\n });\n }}\n />\n </td>\n <td className=\"px-2 py-2.5\">\n <div className=\"flex items-center gap-2\">\n <FolderIcon size={iconSize.smallest} className=\"text-amber-500 dark:text-amber-400 shrink-0\"/>\n <Typography variant=\"body2\" className=\"text-[13px] font-medium truncate\">\n {folder.name}\n </Typography>\n </div>\n </td>\n <td className=\"px-4 py-2.5\">\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n Folder\n </Typography>\n </td>\n <td className=\"px-4 py-2.5 text-right\">\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">\n —\n </Typography>\n </td>\n <td className=\"px-2 py-2.5\"/>\n </tr>\n );\n })}\n {files.map(file => {\n const FileIconComp = getFileIcon(file.contentType);\n const isChecked = selectedPaths.has(file.fullPath);\n return (\n <tr\n key={file.fullPath}\n data-storage-item\n className={cls(\n \"cursor-pointer transition-colors border-b group\",\n defaultBorderMixin,\n isChecked\n ? \"bg-primary/5 dark:bg-primary/10\"\n : \"hover:bg-surface-100 dark:hover:bg-surface-800\"\n )}\n onClick={(e) => handleItemClick(file, e)}\n onDoubleClick={() => handleItemDoubleClick(file)}\n >\n <td className=\"pl-3 pr-0 py-2.5\" onClick={(e) => e.stopPropagation()}>\n <Checkbox\n size=\"small\"\n checked={isChecked}\n onCheckedChange={() => {\n setSelectedPaths(prev => {\n const next = new Set(prev);\n if (next.has(file.fullPath)) next.delete(file.fullPath);\n else next.add(file.fullPath);\n return next;\n });\n }}\n />\n </td>\n <td className=\"px-2 py-2.5\">\n <div className=\"flex items-center gap-2\">\n <FileIconComp size={iconSize.smallest} className=\"text-surface-accent-400 shrink-0\"/>\n <Typography variant=\"body2\" className=\"text-[13px] truncate\">\n {file.name}\n </Typography>\n </div>\n </td>\n <td className=\"px-4 py-2.5\">\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n {getExtension(file.name) || file.contentType?.split(\"/\")[1]?.toUpperCase() || \"—\"}\n </Typography>\n </td>\n <td className=\"px-4 py-2.5 text-right\">\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark font-mono text-[11px]\">\n {file.size !== undefined ? formatFileSize(file.size) : \"—\"}\n </Typography>\n </td>\n <td className=\"px-2 py-2.5\" onClick={(e) => e.stopPropagation()}>\n <IconButton\n size=\"smallest\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity\"\n onClick={() => handleDeleteFile(file)}\n >\n <Trash2Icon size={14}/>\n </IconButton>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n }\n\n // Grid view\n return (\n <div className=\"flex-grow overflow-auto p-4\">\n {/* Folder cards */}\n {folders.length > 0 && (\n <div className=\"mb-4\">\n <Typography variant=\"caption\" className=\"text-[10px] uppercase tracking-wider font-bold text-text-disabled dark:text-text-disabled-dark mb-2 block\">\n Folders\n </Typography>\n <div className=\"grid gap-3 grid-cols-[repeat(auto-fill,minmax(140px,1fr))]\">\n {folders.map(folder => {\n const isChecked = selectedPaths.has(folder.fullPath);\n return (\n <div\n key={folder.fullPath}\n data-storage-item\n className={cls(\n \"rounded-lg p-3 cursor-pointer border\",\n \"transition-colors duration-150\",\n defaultBorderMixin,\n \"hover:bg-surface-100 dark:hover:bg-surface-800 hover:shadow-sm\",\n \"flex items-center gap-2\",\n isChecked && \"ring-2 ring-primary bg-primary/5 dark:bg-primary/10\"\n )}\n onClick={(e) => handleItemClick(folder, e)}\n onDoubleClick={() => handleItemDoubleClick(folder)}\n >\n <FolderIcon size={iconSize.smallest} className=\"text-amber-500 dark:text-amber-400 shrink-0\"/>\n <Typography variant=\"body2\" className=\"text-[13px] font-medium truncate\">\n {folder.name}\n </Typography>\n </div>\n );\n })}\n </div>\n </div>\n )}\n\n {/* FileIcon cards */}\n {files.length > 0 && (\n <div>\n <Typography variant=\"caption\" className=\"text-[10px] uppercase tracking-wider font-bold text-text-disabled dark:text-text-disabled-dark mb-2 block\">\n Files ({files.length})\n </Typography>\n <div className=\"grid gap-3 grid-cols-[repeat(auto-fill,minmax(140px,1fr))]\">\n {files.map(file => {\n const FileIconComp = getFileIcon(file.contentType);\n const ext = getExtension(file.name)?.toLowerCase() || \"\";\n const isImage = file.contentType?.startsWith(\"image/\") || [\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\"].includes(ext);\n const isChecked = selectedPaths.has(file.fullPath);\n\n return (\n <div\n key={file.fullPath}\n data-storage-item\n className={cls(\n \"rounded-lg overflow-hidden cursor-pointer border\",\n \"transition-shadow duration-150\",\n defaultBorderMixin,\n \"hover:shadow-md\",\n isChecked && \"ring-2 ring-primary\"\n )}\n onClick={(e) => handleItemClick(file, e)}\n onDoubleClick={() => handleItemDoubleClick(file)}\n >\n {/* Thumbnail or icon */}\n <div className=\"aspect-square relative overflow-hidden bg-surface-100 dark:bg-surface-800 flex items-center justify-center\">\n {isImage && file.downloadUrl ? (\n <img\n src={file.downloadUrl}\n alt={file.name}\n className=\"w-full h-full object-cover\"\n loading=\"lazy\"\n />\n ) : (\n <FileIconComp className=\"text-surface-accent-400 dark:text-surface-accent-500 w-8 h-8\"/>\n )}\n\n {/* Extension badge */}\n {getExtension(file.name) && (\n <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\">\n {getExtension(file.name)}\n </div>\n )}\n </div>\n\n {/* Name & size */}\n <div className=\"p-2.5\">\n <Typography variant=\"body2\" className=\"text-[12px] font-medium truncate text-surface-900 dark:text-white\">\n {file.name}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate block mt-0.5 text-[11px]\">\n {file.size !== undefined ? formatFileSize(file.size) : \"—\"}\n </Typography>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div className=\"flex h-full w-full bg-white dark:bg-surface-800 overflow-hidden text-text-primary dark:text-text-primary-dark\">\n <div className=\"flex h-full w-full\">\n {/* Main content */}\n <div className=\"flex-grow flex flex-col min-w-0 h-full\">\n {/* Toolbar */}\n <div className={cls(\"flex items-center justify-between pr-2 border-b bg-white dark:bg-surface-800 shrink-0 h-10\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-1.5 flex-grow overflow-hidden px-3 py-2\">\n {/* Breadcrumbs — always visible */}\n {currentPath && (\n <Tooltip title=\"Go up\">\n <IconButton size=\"small\" onClick={handleNavigateUp}>\n <ArrowLeftIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n )}\n <div className=\"flex items-center gap-0.5 overflow-x-auto no-scrollbar\">\n {segments.map((seg, i) => (\n <React.Fragment key={seg.path}>\n {i > 0 && (\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark mx-0.5\">/</Typography>\n )}\n <Button\n variant=\"text\"\n size=\"small\"\n className={cls(\n \"px-1.5 py-0.5 min-h-0 min-w-0 h-6 text-xs whitespace-nowrap normal-case font-normal\",\n i === segments.length - 1\n ? \"text-text-primary dark:text-text-primary-dark font-medium\"\n : \"text-text-secondary dark:text-text-secondary-dark\"\n )}\n onClick={() => handleNavigate(seg.path)}\n >\n {seg.label}\n </Button>\n </React.Fragment>\n ))}\n </div>\n\n <div className=\"flex-1\"/>\n\n {/* Selection actions or file count */}\n {selectedPaths.size > 0 ? (\n <div className=\"flex items-center gap-1.5 shrink-0\">\n <Typography variant=\"body2\" className=\"text-[13px] font-medium whitespace-nowrap\">\n {selectedPaths.size} selected\n </Typography>\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={() => {\n setDeleteDialogTarget(\"selection\");\n setDeleteDialogOpen(true);\n }}\n >\n <Trash2Icon size={14} className=\"mr-1\"/>\n Delete\n </Button>\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={() => {\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedDownloadUrl(null);\n }}\n >\n <XIcon size={14} className=\"mr-1\"/>\n Deselect\n </Button>\n </div>\n ) : !loading ? (\n <Chip size=\"small\" className=\"shrink-0 text-[10px]\">\n {files.length} file{files.length !== 1 ? \"s\" : \"\"}\n {folders.length > 0 ? `, ${folders.length} folder${folders.length !== 1 ? \"s\" : \"\"}` : \"\"}\n </Chip>\n ) : null}\n </div>\n\n <div className=\"flex shrink-0 items-center justify-end gap-1.5 pr-1\">\n\n {/* Backend picker — only shown when more than one storage source is available */}\n {sourceKeys.length > 1 && (\n <Select\n size=\"small\"\n position=\"item-aligned\"\n value={selectedSourceKey}\n onValueChange={(value) => {\n if (value) setSelectedSourceKey(value);\n }}\n renderValue={(key) => {\n const label = storageSources.registry[key]?.label;\n return label ?? (key === DEFAULT_STORAGE_SOURCE_KEY ? \"Default\" : key);\n }}>\n {sourceKeys.map((key) => (\n <SelectItem key={key} value={key}>\n {storageSources.registry[key]?.label\n ?? (key === DEFAULT_STORAGE_SOURCE_KEY ? \"Default\" : key)}\n </SelectItem>\n ))}\n </Select>\n )}\n\n <Tooltip title=\"Grid view\">\n <IconButton\n size=\"small\"\n onClick={() => setViewMode(\"grid\")}\n className={cls(viewMode === \"grid\" && \"bg-surface-100 dark:bg-surface-800\")}\n >\n <LayoutGridIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n <Tooltip title=\"List view\">\n <IconButton\n size=\"small\"\n onClick={() => setViewMode(\"list\")}\n className={cls(viewMode === \"list\" && \"bg-surface-100 dark:bg-surface-800\")}\n >\n <ListIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n\n <div className={cls(\"h-4 w-px mx-0.5\", defaultBorderMixin, \"bg-surface-200 dark:bg-surface-700\")}/>\n\n <Tooltip title=\"Refresh\">\n <IconButton size=\"small\" onClick={handleRefresh} disabled={loading}>\n <RefreshCwIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n\n <Tooltip title=\"New folder\">\n <IconButton\n size=\"small\"\n onClick={() => {\n setNewFolderName(\"\");\n setNewFolderDialogOpen(true);\n }}\n >\n <FolderPlusIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n <Button\n size=\"small\"\n color=\"primary\"\n onClick={() => setUploadDialogOpen(true)}\n >\n <UploadCloudIcon size={iconSize.smallest} className=\"mr-1\"/>\n Upload\n </Button>\n </div>\n </div>\n\n {/* File grid / list — drop zone */}\n <div {...getDropRootProps()}\n className=\"flex-grow flex flex-col overflow-hidden min-h-0 relative\"\n onClick={(e) => {\n const target = e.target as HTMLElement;\n if (!target.closest(\"[data-storage-item]\") && selectedPaths.size > 0) {\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedDownloadUrl(null);\n }\n }}\n >\n <input {...getDropInputProps()} />\n {renderContents()}\n {/* Drag overlay */}\n {isDragActive && (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-primary/5 dark:bg-primary/10 backdrop-blur-[2px]\">\n <div className=\"flex flex-col items-center gap-2 p-6 rounded-xl border-2 border-dashed border-primary bg-white/80 dark:bg-surface-900/80\">\n <UploadCloudIcon className=\"w-10 h-10 text-primary\"/>\n <Typography variant=\"subtitle2\" className=\"text-primary font-semibold\">\n Drop files to upload\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n to /{currentPath || \"root\"}\n </Typography>\n </div>\n </div>\n )}\n </div>\n\n {/* Status bar */}\n <div className={cls(\"px-4 py-1.5 border-t bg-surface-50 dark:bg-surface-800 flex items-center justify-between shrink-0\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-4 text-[11px]\">\n <span className=\"text-text-disabled dark:text-text-disabled-dark font-bold uppercase tracking-tighter\">\n Path\n </span>\n <span className=\"font-mono text-text-secondary dark:text-text-secondary-dark\">\n /{currentPath || \"\"}\n </span>\n </div>\n {selectedPaths.size > 0 ? (\n <div className=\"text-[11px] text-text-secondary dark:text-text-secondary-dark\">\n {selectedPaths.size} item{selectedPaths.size !== 1 ? \"s\" : \"\"} selected\n </div>\n ) : selectedFile ? (\n <div className=\"text-[11px] text-text-secondary dark:text-text-secondary-dark\">\n Selected: <span className=\"font-mono\">{selectedFile.name}</span>\n </div>\n ) : null}\n </div>\n </div>\n\n {/* Preview panel */}\n {selectedFile && (\n <div className=\"w-80 lg:w-96 shrink-0\">\n <FilePreviewPanel\n file={selectedFile}\n downloadUrl={selectedDownloadUrl}\n onClose={() => {\n setSelectedFile(null);\n setSelectedDownloadUrl(null);\n }}\n onDelete={() => handleDeleteFile(selectedFile)}\n />\n </div>\n )}\n </div>\n\n {/* Upload Dialog */}\n <UploadDialog\n open={uploadDialogOpen}\n currentPath={currentPath}\n onClose={() => setUploadDialogOpen(false)}\n onUpload={handleUpload}\n />\n\n {/* Delete confirmation dialog */}\n <Dialog\n open={deleteDialogOpen}\n onOpenChange={(open) => {\n if (!open && !deleting) {\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }\n }}\n >\n <DialogTitle hidden>Delete Confirmation</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"font-semibold mb-2\">\n {deleteDialogTarget === \"selection\"\n ? `Delete ${selectedPaths.size} item${selectedPaths.size !== 1 ? \"s\" : \"\"}?`\n : deleteDialogTarget\n ? `Delete folder \"${deleteDialogTarget.name}\"?`\n : \"Delete?\"}\n </Typography>\n <Typography variant=\"body2\" color=\"secondary\">\n {deleteDialogTarget === \"selection\"\n ? \"This will permanently delete all selected files and folders, including their contents. This action cannot be undone.\"\n : \"This will permanently delete the folder and all of its contents. This action cannot be undone.\"}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={() => {\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }}\n disabled={deleting}\n >\n Cancel\n </Button>\n <LoadingButton\n color=\"error\"\n loading={deleting}\n onClick={deleteDialogTarget === \"selection\" ? handleBulkDelete : handleConfirmDeleteFolder}\n >\n <Trash2Icon size={14} className=\"mr-1\"/>\n Delete\n </LoadingButton>\n </DialogActions>\n </Dialog>\n\n {/* New Folder Dialog */}\n <Dialog\n open={newFolderDialogOpen}\n onOpenChange={(open) => {\n if (!open && !creatingFolder) {\n setNewFolderDialogOpen(false);\n setNewFolderName(\"\");\n }\n }}\n >\n <DialogTitle hidden>New Folder</DialogTitle>\n <DialogContent>\n <TextField\n autoFocus\n size=\"small\"\n label=\"Folder name\"\n value={newFolderName}\n onChange={(e) => setNewFolderName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && newFolderName.trim()) {\n e.preventDefault();\n handleCreateFolder();\n }\n }}\n disabled={creatingFolder}\n placeholder=\"Enter folder name\"\n />\n {currentPath && (\n <Typography variant=\"caption\" color=\"secondary\" className=\"mt-2\">\n Will be created in <span className=\"font-mono\">/{currentPath}/</span>\n </Typography>\n )}\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={() => {\n setNewFolderDialogOpen(false);\n setNewFolderName(\"\");\n }}\n disabled={creatingFolder}\n >\n Cancel\n </Button>\n <LoadingButton\n color=\"primary\"\n loading={creatingFolder}\n disabled={!newFolderName.trim()}\n onClick={handleCreateFolder}\n >\n <FolderPlusIcon size={14} className=\"mr-1\"/>\n Create\n </LoadingButton>\n </DialogActions>\n </Dialog>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -34,4 +34,9 @@ export interface UseSchemaGraphResult {
|
|
|
34
34
|
tableCount: number;
|
|
35
35
|
relationCount: number;
|
|
36
36
|
}
|
|
37
|
-
export declare const useSchemaGraph: (collections: AdminCollection[] | undefined
|
|
37
|
+
export declare const useSchemaGraph: (collections: AdminCollection[] | undefined,
|
|
38
|
+
/**
|
|
39
|
+
* `schema.table` for every table the database reports RLS on, or `null`
|
|
40
|
+
* when there is no SQL capability to ask. See {@link useLiveRlsTables}.
|
|
41
|
+
*/
|
|
42
|
+
liveRls?: Set<string> | null) => UseSchemaGraphResult;
|
package/dist/index.es.js
CHANGED
|
@@ -2,7 +2,7 @@ import { IconForView, SchemaDriftBanner, StudioBridgeContext, StudioBridgeProvid
|
|
|
2
2
|
import React, { Suspense, lazy, useEffect, useLayoutEffect, useMemo, useState } from "react";
|
|
3
3
|
import { Card, CircularProgressCenter, Container, ExpandablePanel, Typography, cls } from "@rebasepro/ui";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
-
import { useNavigate } from "react-router
|
|
5
|
+
import { useNavigate } from "react-router";
|
|
6
6
|
//#region src/components/StudioHomePage.tsx
|
|
7
7
|
/** Group order for the home page; unknown groups are appended in view order. */
|
|
8
8
|
var GROUP_ORDER = [
|
|
@@ -549,17 +549,17 @@ function SyntaxHighlightedSnippet() {
|
|
|
549
549
|
}
|
|
550
550
|
//#endregion
|
|
551
551
|
//#region src/components/RebaseStudio.tsx
|
|
552
|
-
var SQLEditor = lazy(() => import("./SQLEditor-
|
|
553
|
-
var JSEditor = lazy(() => import("./JSEditor-
|
|
554
|
-
var RLSEditor = lazy(() => import("./RLSEditor-
|
|
555
|
-
var StorageView = lazy(() => import("./StorageView-
|
|
556
|
-
var CronJobsView = lazy(() => import("./CronJobsView-
|
|
557
|
-
var SchemaVisualizer = lazy(() => import("./SchemaVisualizer-
|
|
558
|
-
var BranchesView = lazy(() => import("./BranchesView-
|
|
552
|
+
var SQLEditor = lazy(() => import("./SQLEditor-CL6DcGRM.js").then((m) => ({ default: m.SQLEditor })));
|
|
553
|
+
var JSEditor = lazy(() => import("./JSEditor-BSTU-NXF.js").then((m) => ({ default: m.JSEditor })));
|
|
554
|
+
var RLSEditor = lazy(() => import("./RLSEditor-DuzofJ8t.js").then((m) => ({ default: m.RLSEditor })));
|
|
555
|
+
var StorageView = lazy(() => import("./StorageView-CwDI4sBG.js").then((m) => ({ default: m.StorageView })));
|
|
556
|
+
var CronJobsView = lazy(() => import("./CronJobsView-CL9q-DX7.js").then((m) => ({ default: m.CronJobsView })));
|
|
557
|
+
var SchemaVisualizer = lazy(() => import("./SchemaVisualizer-vxKSG80C.js").then((m) => ({ default: m.SchemaVisualizer })));
|
|
558
|
+
var BranchesView = lazy(() => import("./BranchesView-Bgmp_1bm.js").then((m) => ({ default: m.BranchesView })));
|
|
559
559
|
var BackupsView = lazy(() => import("./BackupsView-DNS6LdVg.js").then((m) => ({ default: m.BackupsView })));
|
|
560
|
-
var ApiExplorer = lazy(() => import("./ApiExplorer-
|
|
561
|
-
var LogsExplorer = lazy(() => import("./LogsExplorer-
|
|
562
|
-
var ApiKeysView = lazy(() => import("./ApiKeysView-
|
|
560
|
+
var ApiExplorer = lazy(() => import("./ApiExplorer-BIN9hE7r.js").then((m) => ({ default: m.ApiExplorer })));
|
|
561
|
+
var LogsExplorer = lazy(() => import("./LogsExplorer-BsHaHIMK.js").then((m) => ({ default: m.LogsExplorer })));
|
|
562
|
+
var ApiKeysView = lazy(() => import("./ApiKeysView-dA-OvlcH.js").then((m) => ({ default: m.ApiKeysView })));
|
|
563
563
|
/**
|
|
564
564
|
* Declarative component to configure the Studio in Rebase.
|
|
565
565
|
* Renders nothing — purely registers config into the RebaseRegistry.
|
|
@@ -680,21 +680,19 @@ function RebaseStudio({ tools, homePage }) {
|
|
|
680
680
|
view: suspense(/* @__PURE__ */ jsx(ApiKeysView, {}))
|
|
681
681
|
});
|
|
682
682
|
return views;
|
|
683
|
-
}, [tools]);
|
|
683
|
+
}, [tools ? tools.join(",") : ""]);
|
|
684
684
|
const homePageRef = React.useRef(resolvedHomePage);
|
|
685
685
|
homePageRef.current = resolvedHomePage;
|
|
686
|
+
const toolsRef = React.useRef(tools);
|
|
687
|
+
toolsRef.current = tools;
|
|
686
688
|
useLayoutEffect(() => {
|
|
687
689
|
dispatch.registerStudio({
|
|
688
|
-
tools,
|
|
690
|
+
tools: toolsRef.current,
|
|
689
691
|
homePage: homePageRef.current,
|
|
690
692
|
devViews
|
|
691
693
|
});
|
|
692
694
|
return () => dispatch.unregisterStudio();
|
|
693
|
-
}, [
|
|
694
|
-
dispatch,
|
|
695
|
-
tools,
|
|
696
|
-
devViews
|
|
697
|
-
]);
|
|
695
|
+
}, [dispatch, devViews]);
|
|
698
696
|
return null;
|
|
699
697
|
}
|
|
700
698
|
//#endregion
|
package/dist/index.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","names":[],"sources":["../src/components/StudioHomePage.tsx","../src/components/RebaseStudio.tsx"],"sourcesContent":["import type { AppView, HomePageSection, PluginGenericProps } from \"@rebasepro/admin-types\";\nimport React, { useEffect, useMemo, useState } from \"react\";\nimport { Card, cls, Container, ExpandablePanel, Typography } from \"@rebasepro/ui\";\nimport { IconForView, useRebaseContext, useRebaseRegistry, useRestoreScroll, useSlot } from \"@rebasepro/app\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useStudioBreadcrumbs, SchemaDriftBanner } from \"@rebasepro/app\";\n\n/* ═══════════════════════════════════════════════════════════════\n Studio tool sections, derived from the registered Studio views\n ═══════════════════════════════════════════════════════════════ */\n\ninterface StudioTool {\n path: string;\n name: string;\n description: string;\n icon?: string | React.ReactNode;\n}\n\ninterface StudioSection {\n label: string;\n tools: StudioTool[];\n}\n\n/** The view metadata the cards need — not the rendered `view` itself. */\ntype StudioViewMeta = Pick<AppView, \"slug\" | \"name\" | \"group\" | \"description\" | \"icon\" | \"hideFromNavigation\">;\n\n/** Group order for the home page; unknown groups are appended in view order. */\nconst GROUP_ORDER = [\"Database\", \"Compute\", \"Storage\", \"API\", \"Access Control\"];\n\nconst UNGROUPED_LABEL = \"Tools\";\n\n/**\n * Build the home page sections from the Studio views that are actually\n * registered, so a card exists if and only if its route does. A hand-written\n * list drifts: it used to advertise Users and Roles pages that 404, while\n * omitting the Backups view that does exist.\n */\nfunction buildSections(views: StudioViewMeta[]): StudioSection[] {\n const byGroup = new Map<string, StudioTool[]>();\n\n for (const view of views) {\n if (view.hideFromNavigation) continue;\n const label = view.group ?? UNGROUPED_LABEL;\n const tools = byGroup.get(label) ?? [];\n tools.push({\n path: `/${view.slug}`,\n name: view.name,\n description: view.description ?? \"\",\n icon: view.icon\n });\n byGroup.set(label, tools);\n }\n\n const ordered = [\n ...GROUP_ORDER.filter(g => byGroup.has(g)),\n ...[...byGroup.keys()].filter(g => !GROUP_ORDER.includes(g))\n ];\n\n return ordered.map(label => ({ label,\ntools: byGroup.get(label)! }));\n}\n\n/* ═══════════════════════════════════════════════════════════════ */\n\nconst COLLAPSED_STORAGE_KEY = \"rebase-studio-home-collapsed\";\n\nfunction useStudioCollapsedGroups(groupNames: string[]) {\n const [collapsed, setCollapsed] = useState<Set<string>>(() => {\n try {\n const stored = localStorage.getItem(COLLAPSED_STORAGE_KEY);\n return stored ? new Set(JSON.parse(stored)) : new Set<string>();\n } catch {\n return new Set<string>();\n }\n });\n\n const isGroupCollapsed = (name: string) => collapsed.has(name);\n\n const toggleGroupCollapsed = (name: string) => {\n setCollapsed(prev => {\n const next = new Set(prev);\n if (next.has(name)) {\n next.delete(name);\n } else {\n next.add(name);\n }\n try {\n localStorage.setItem(COLLAPSED_STORAGE_KEY, JSON.stringify([...next]));\n } catch { /* noop */ }\n return next;\n });\n };\n\n return { isGroupCollapsed, toggleGroupCollapsed };\n}\n\nexport function StudioHomePage({\n additionalActions,\n additionalChildrenStart,\n additionalChildrenEnd,\n sections,\n hiddenGroups\n}: {\n additionalActions?: React.ReactNode;\n additionalChildrenStart?: React.ReactNode;\n additionalChildrenEnd?: React.ReactNode;\n sections?: HomePageSection[];\n hiddenGroups?: string[];\n}) {\n const context = useRebaseContext();\n const breadcrumbs = useStudioBreadcrumbs();\n const navigate = useNavigate();\n const registry = useRebaseRegistry();\n\n useEffect(() => {\n breadcrumbs.set({ breadcrumbs: [] });\n }, [breadcrumbs.set]);\n\n const { containerRef } = useRestoreScroll();\n\n const sectionProps: PluginGenericProps = { context };\n\n const pluginActions = useSlot(\"home.actions\", sectionProps);\n\n // The collection editor (\"schema\") is not part of `devViews` — RebaseNavigation\n // injects it when the admin enables a collection editor. Mirror that condition so\n // the card tracks the route.\n const schemaEnabled = Boolean(registry.studioConfig && registry.cmsConfig?.collectionEditor);\n\n const filteredSections = useMemo(() => {\n const views: StudioViewMeta[] = [];\n if (schemaEnabled) {\n views.push({\n slug: \"schema\",\n name: \"Collections\",\n group: \"Database\",\n icon: \"LayoutList\",\n description: \"Define and manage your data model and collection schemas\"\n });\n }\n views.push(...(registry.studioConfig?.devViews ?? []));\n return buildSections(views).filter(s => !hiddenGroups?.includes(s.label));\n }, [registry.studioConfig?.devViews, schemaEnabled, hiddenGroups]);\n\n const groupNames = useMemo(\n () => filteredSections.map(s => s.label),\n [filteredSections]\n );\n\n const { isGroupCollapsed, toggleGroupCollapsed } = useStudioCollapsedGroups(groupNames);\n\n return (\n <div ref={containerRef} className=\"py-2 overflow-auto h-full w-full bg-surface-50 dark:bg-surface-800\">\n <Container maxWidth=\"6xl\">\n <div className=\"mb-4 flex flex-col gap-2\">\n <SchemaDriftBanner />\n </div>\n\n {(additionalActions || pluginActions) && (\n <div className=\"w-full sticky py-4 transition-all duration-400 ease-in-out top-0 z-10 flex flex-row gap-4 justify-end\">\n {additionalActions}\n {pluginActions}\n </div>\n )}\n\n {additionalChildrenStart}\n\n {/* ── Tool sections ── */}\n {filteredSections.map((section) => {\n const sectionCollapsed = isGroupCollapsed(section.label);\n\n return (\n <div key={section.label} className=\"my-10\">\n <ExpandablePanel\n invisible\n expanded={!sectionCollapsed}\n onExpandedChange={(open) => {\n if (open !== !sectionCollapsed) {\n toggleGroupCollapsed(section.label);\n }\n }}\n className=\"mt-6\"\n titleClassName={cls(\n \"min-h-0 p-0 border-none\",\n \"rounded flex items-center justify-between w-full\",\n \"hover:bg-transparent\",\n \"cursor-pointer select-none\",\n sectionCollapsed && \"bg-surface-100 dark:bg-surface-900/50\"\n )}\n innerClassName=\"mt-4 pt-0\"\n title={\n <Typography\n variant=\"caption\"\n component=\"h2\"\n color=\"secondary\"\n className={cls(\n \"px-4 py-1 rounded\",\n \"font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70\"\n )}\n >\n {section.label}\n </Typography>\n }\n >\n <div className=\"mt-4 pt-0\">\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {section.tools.map((tool) => (\n <Card\n key={tool.path}\n onClick={() => {\n navigate(tool.path);\n context.analyticsController?.onAnalyticsEvent?.(\n \"home_navigate_to_view\",\n { path: tool.path }\n );\n }}\n className={cls(\n \"group h-full p-4 cursor-pointer transition-colors duration-150 ease-in-out\",\n \"hover:bg-primary/5 dark:hover:bg-primary/5\"\n )}\n >\n <div className=\"flex flex-col h-full\">\n {/* Header: icon + title */}\n <div className=\"flex items-center w-full justify-between mb-1\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center justify-center w-5 h-5 text-surface-400 dark:text-surface-500 transition-colors duration-150 group-hover:text-primary dark:group-hover:text-primary\">\n <IconForView\n collectionOrView={{ slug: tool.path, name: tool.name, icon: tool.icon }}\n size=\"small\"\n />\n </div>\n <Typography variant=\"subtitle1\" component=\"h2\">\n {tool.name}\n </Typography>\n </div>\n </div>\n\n {/* Description indented to align with title */}\n <div className=\"pl-8\">\n {tool.description && (\n <Typography variant=\"caption\" color=\"secondary\" component=\"div\">\n {tool.description}\n </Typography>\n )}\n </div>\n\n {/* Spacer */}\n <div className=\"grow\"/>\n </div>\n </Card>\n ))}\n </div>\n </div>\n </ExpandablePanel>\n </div>\n );\n })}\n\n {/* ── SDK Quick Start ── */}\n <div className=\"mt-10 mb-6\">\n <div className=\"flex items-center mb-1\">\n <Typography\n variant=\"caption\"\n component=\"h2\"\n color=\"secondary\"\n className={cls(\n \"px-4 py-1 rounded\",\n \"font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70\"\n )}\n >\n Quick Start\n </Typography>\n </div>\n\n <Typography variant=\"body2\" color=\"secondary\" className=\"mb-4 max-w-2xl\">\n Generate a fully-typed SDK from your collections with{\" \"}\n <code className=\"text-emerald-400 font-mono text-xs bg-emerald-400/10 px-1.5 py-0.5 rounded\">\n npx rebase generate-sdk\n </code>\n {\" \"}and start querying your data with full TypeScript autocompletion.\n </Typography>\n\n <div className=\"rounded-lg border border-surface-200/40 dark:border-surface-700/40 bg-white dark:bg-surface-950 overflow-hidden\">\n {/* Title bar */}\n <div className=\"flex items-center justify-between px-4 py-2.5 border-b border-surface-200/40 dark:border-surface-700/40 bg-surface-50 dark:bg-surface-900/80\">\n <div className=\"flex items-center gap-2.5\">\n <div className=\"flex gap-1.5\">\n <span className=\"w-2.5 h-2.5 rounded-full bg-red-400/60\"/>\n <span className=\"w-2.5 h-2.5 rounded-full bg-amber-400/60\"/>\n <span className=\"w-2.5 h-2.5 rounded-full bg-emerald-400/60\"/>\n </div>\n <span className=\"text-xs font-mono text-surface-400 dark:text-surface-500 ml-1\">\n app.ts\n </span>\n </div>\n <span className=\"text-xs font-mono text-surface-400 dark:text-surface-500\">\n TypeScript\n </span>\n </div>\n\n {/* Syntax-highlighted code */}\n <div className=\"px-5 py-4 overflow-x-auto text-[13px] leading-6 font-mono\">\n <SyntaxHighlightedSnippet/>\n </div>\n </div>\n </div>\n\n {/* ── Extra sections from props ── */}\n {sections?.map((s) => (\n <div key={s.key} className=\"my-10\">\n <Typography\n variant=\"caption\"\n component=\"h2\"\n color=\"secondary\"\n className={cls(\n \"px-4 py-1 rounded\",\n \"font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70\"\n )}\n >\n {s.title}\n </Typography>\n <div className=\"mt-4\">{s.children}</div>\n </div>\n ))}\n\n {additionalChildrenEnd}\n </Container>\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Hand-crafted syntax-highlighted code snippet.\n Uses inline spans with Tailwind color classes to avoid\n pulling in a syntax highlighting library.\n ═══════════════════════════════════════════════════════════════ */\n\nfunction SyntaxHighlightedSnippet() {\n const kw = \"text-violet-600 dark:text-violet-400\"; // keywords\n const str = \"text-emerald-600 dark:text-emerald-400\"; // strings\n const typ = \"text-amber-600 dark:text-amber-300\"; // types\n const fn = \"text-blue-600 dark:text-blue-400\"; // functions\n const cm = \"text-surface-500 dark:text-surface-400 italic\"; // comments\n const op = \"text-surface-500 dark:text-surface-400\"; // operators / punctuation\n const tx = \"text-surface-950 dark:text-surface-200\"; // plain text\n\n return (\n <pre className=\"m-0 whitespace-pre\">\n <span className={kw}>import</span>\n <span className={tx}>{\" { \"}</span>\n <span className={fn}>createRebaseClient</span>\n <span className={tx}>{\" } \"}</span>\n <span className={kw}>from</span>\n <span className={tx}> </span>\n <span className={str}>'@rebasepro/client'</span>\n <span className={op}>;</span>\n {\"\\n\"}\n\n <span className={kw}>import</span>\n <span className={tx}> </span>\n <span className={kw}>type</span>\n <span className={tx}>{\" { \"}</span>\n <span className={typ}>Database</span>\n <span className={tx}>{\" } \"}</span>\n <span className={kw}>from</span>\n <span className={tx}> </span>\n <span className={str}>'./database.types'</span>\n <span className={op}>;</span>\n {\"\\n\\n\"}\n\n <span className={kw}>const</span>\n <span className={tx}> rebase </span>\n <span className={op}>= </span>\n <span className={fn}>createRebaseClient</span>\n <span className={op}>{\"<\"}</span>\n <span className={typ}>Database</span>\n <span className={op}>{\">(\"}</span>\n <span className={tx}>{\"{\"}</span>\n {\"\\n\"}\n <span className={tx}>{\" baseUrl\"}</span>\n <span className={op}>: </span>\n <span className={str}>'http://localhost:3001'</span>\n <span className={op}>,</span>\n {\"\\n\"}\n <span className={tx}>{\"}\"}</span>\n <span className={op}>);</span>\n {\"\\n\\n\"}\n\n <span className={cm}>{\"// Fully typed — autocompletion for tables and columns\"}</span>\n {\"\\n\"}\n <span className={kw}>const</span>\n <span className={tx}>{\" { \"}</span>\n <span className={tx}>data</span>\n <span className={op}>: </span>\n <span className={tx}>users</span>\n <span className={tx}>{\" } \"}</span>\n <span className={op}>= </span>\n <span className={kw}>await</span>\n <span className={tx}> rebase</span>\n <span className={op}>.</span>\n <span className={tx}>data</span>\n <span className={op}>.</span>\n <span className={tx}>users</span>\n <span className={op}>.</span>\n <span className={fn}>find</span>\n <span className={op}>();</span>\n {\"\\n\"}\n\n <span className={kw}>const</span>\n <span className={tx}>{\" { \"}</span>\n <span className={tx}>data</span>\n <span className={op}>: </span>\n <span className={tx}>posts</span>\n <span className={tx}>{\" } \"}</span>\n <span className={op}>= </span>\n <span className={kw}>await</span>\n <span className={tx}> rebase</span>\n <span className={op}>.</span>\n <span className={tx}>data</span>\n <span className={op}>.</span>\n <span className={fn}>collection</span>\n <span className={op}>(</span>\n <span className={str}>'posts'</span>\n <span className={op}>)</span>\n <span className={op}>.</span>\n <span className={fn}>find</span>\n <span className={op}>();</span>\n </pre>\n );\n}\n","import React, { lazy, Suspense, useLayoutEffect, useMemo } from \"react\";\nimport { useRebaseRegistryDispatch } from \"@rebasepro/app\";\nimport type { RebaseStudioConfig, AppView } from \"@rebasepro/admin-types\";\nimport { CircularProgressCenter } from \"@rebasepro/ui\";\n\n// Lazy-loaded studio tools — each fetched only when its route is visited.\n// This keeps Monaco, @xyflow/react, dagre, pgsql-ast-parser etc. out of the initial bundle.\nconst SQLEditor = lazy(() => import(\"./SQLEditor/SQLEditor\").then(m => ({ default: m.SQLEditor })));\nconst JSEditor = lazy(() => import(\"./JSEditor/JSEditor\").then(m => ({ default: m.JSEditor })));\nconst RLSEditor = lazy(() => import(\"./RLSEditor/RLSEditor\").then(m => ({ default: m.RLSEditor })));\nconst StorageView = lazy(() => import(\"./StorageView/StorageView\").then(m => ({ default: m.StorageView })));\nconst CronJobsView = lazy(() => import(\"./CronJobs/CronJobsView\").then(m => ({ default: m.CronJobsView })));\nconst SchemaVisualizer = lazy(() => import(\"./SchemaVisualizer/SchemaVisualizer\").then(m => ({ default: m.SchemaVisualizer })));\nconst BranchesView = lazy(() => import(\"./Branches/BranchesView\").then(m => ({ default: m.BranchesView })));\nconst BackupsView = lazy(() => import(\"./Backups/BackupsView\").then(m => ({ default: m.BackupsView })));\nconst ApiExplorer = lazy(() => import(\"./ApiExplorer/ApiExplorer\").then(m => ({ default: m.ApiExplorer })));\nconst LogsExplorer = lazy(() => import(\"./LogsExplorer/LogsExplorer\").then(m => ({ default: m.LogsExplorer })));\nconst ApiKeysView = lazy(() => import(\"./ApiKeys/ApiKeysView\").then(m => ({ default: m.ApiKeysView })));\n\nimport { StudioHomePage } from \"./StudioHomePage\";\n\n/**\n * Declarative component to configure the Studio in Rebase.\n * Renders nothing — purely registers config into the RebaseRegistry.\n *\n * The \"schema\" tool (collection editor view) is now a built-in admin feature.\n * When `<RebaseAdmin collectionEditor={...}>` is used, the schema view is\n * automatically injected into Studio — no manual wiring needed.\n */\nconst DEFAULT_HOME_PAGE = <StudioHomePage/>;\n\nexport function RebaseStudio({ tools, homePage }: RebaseStudioConfig) {\n const dispatch = useRebaseRegistryDispatch();\n\n const resolvedHomePage = homePage ?? DEFAULT_HOME_PAGE;\n\n const devViews: AppView[] = useMemo(() => {\n const views: AppView[] = [];\n const activeTools = tools ?? [\"sql\", \"js\", \"rls\", \"storage\", \"cron\", \"schema-visualizer\", \"branches\", \"backups\", \"api\", \"logs\", \"api-keys\"];\n const suspense = (el: React.ReactNode) => <Suspense fallback={<CircularProgressCenter/>}>{el}</Suspense>;\n\n if (activeTools.includes(\"sql\")) {\n views.push({ slug: \"sql\",\nname: \"SQL Console\",\ngroup: \"Database\",\nicon: \"terminal\",\ndescription: \"Execute SQL queries\",\nview: suspense(<SQLEditor/>) });\n }\n if (activeTools.includes(\"js\")) {\n views.push({ slug: \"js\",\nname: \"JS Console\",\ngroup: \"Compute\",\nicon: \"code\",\ndescription: \"Execute JavaScript\",\nview: suspense(<JSEditor/>) });\n }\n if (activeTools.includes(\"rls\")) {\n views.push({ slug: \"rls\",\nname: \"RLS Policies\",\ngroup: \"Database\",\nicon: \"ShieldCheck\",\ndescription: \"Row Level Security\",\nview: suspense(<RLSEditor/>) });\n }\n if (activeTools.includes(\"storage\")) {\n views.push({ slug: \"storage\",\nname: \"Storage\",\ngroup: \"Storage\",\nicon: \"HardDrive\",\ndescription: \"Manage storage files\",\nview: suspense(<StorageView/>) });\n }\n if (activeTools.includes(\"cron\")) {\n views.push({ slug: \"cron\",\nname: \"Cron Jobs\",\ngroup: \"Compute\",\nicon: \"Clock\",\ndescription: \"Manage scheduled tasks\",\nview: suspense(<CronJobsView/>) });\n }\n if (activeTools.includes(\"schema-visualizer\")) {\n views.push({ slug: \"schema-visualizer\",\nname: \"Schema Visualizer\",\ngroup: \"Database\",\nicon: \"Network\",\ndescription: \"Interactive database ERD\",\nview: suspense(<SchemaVisualizer/>) });\n }\n if (activeTools.includes(\"branches\")) {\n views.push({ slug: \"branches\",\nname: \"Branches\",\ngroup: \"Database\",\nicon: \"GitBranch\",\ndescription: \"Create and manage database branches\",\nview: suspense(<BranchesView/>) });\n }\n if (activeTools.includes(\"backups\")) {\n views.push({ slug: \"backups\",\nname: \"Backups\",\ngroup: \"Database\",\nicon: \"Database\",\ndescription: \"Download database backups\",\nview: suspense(<BackupsView/>) });\n }\n if (activeTools.includes(\"api\")) {\n views.push({ slug: \"api\",\nname: \"API Explorer\",\ngroup: \"API\",\nicon: \"BookOpen\",\ndescription: \"Interactive API documentation and testing\",\nview: suspense(<ApiExplorer/>) });\n }\n if (activeTools.includes(\"logs\")) {\n views.push({ slug: \"logs\",\nname: \"Logs Explorer\",\ngroup: \"Database\",\nicon: \"Activity\",\ndescription: \"Real-time system and query logs\",\nview: suspense(<LogsExplorer/>) });\n }\n if (activeTools.includes(\"api-keys\")) {\n views.push({ slug: \"api-keys\",\nname: \"API Keys\",\ngroup: \"Access Control\",\nicon: \"KeyRound\",\ndescription: \"Create and manage scoped API keys\",\nview: suspense(<ApiKeysView/>) });\n }\n // Note: \"schema\" tool is auto-injected by RebaseShell when collectionEditor is enabled.\n // It is NOT registered here anymore.\n return views;\n }, [tools]);\n\n // Use a ref for homePage so it never destabilizes the effect.\n // homePage is a React element — its identity doesn't matter for registration.\n const homePageRef = React.useRef(resolvedHomePage);\n homePageRef.current = resolvedHomePage;\n\n useLayoutEffect(() => {\n dispatch.registerStudio({ tools,\nhomePage: homePageRef.current,\ndevViews });\n return () => dispatch.unregisterStudio();\n }, [dispatch, tools, devViews]);\n\n return null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.es.js","names":[],"sources":["../src/components/StudioHomePage.tsx","../src/components/RebaseStudio.tsx"],"sourcesContent":["import type { AppView, HomePageSection, PluginGenericProps } from \"@rebasepro/admin-types\";\nimport React, { useEffect, useMemo, useState } from \"react\";\nimport { Card, cls, Container, ExpandablePanel, Typography } from \"@rebasepro/ui\";\nimport { IconForView, useRebaseContext, useRebaseRegistry, useRestoreScroll, useSlot } from \"@rebasepro/app\";\nimport { useNavigate } from \"react-router\";\nimport { useStudioBreadcrumbs, SchemaDriftBanner } from \"@rebasepro/app\";\n\n/* ═══════════════════════════════════════════════════════════════\n Studio tool sections, derived from the registered Studio views\n ═══════════════════════════════════════════════════════════════ */\n\ninterface StudioTool {\n path: string;\n name: string;\n description: string;\n icon?: string | React.ReactNode;\n}\n\ninterface StudioSection {\n label: string;\n tools: StudioTool[];\n}\n\n/** The view metadata the cards need — not the rendered `view` itself. */\ntype StudioViewMeta = Pick<AppView, \"slug\" | \"name\" | \"group\" | \"description\" | \"icon\" | \"hideFromNavigation\">;\n\n/** Group order for the home page; unknown groups are appended in view order. */\nconst GROUP_ORDER = [\"Database\", \"Compute\", \"Storage\", \"API\", \"Access Control\"];\n\nconst UNGROUPED_LABEL = \"Tools\";\n\n/**\n * Build the home page sections from the Studio views that are actually\n * registered, so a card exists if and only if its route does. A hand-written\n * list drifts: it used to advertise Users and Roles pages that 404, while\n * omitting the Backups view that does exist.\n */\nfunction buildSections(views: StudioViewMeta[]): StudioSection[] {\n const byGroup = new Map<string, StudioTool[]>();\n\n for (const view of views) {\n if (view.hideFromNavigation) continue;\n const label = view.group ?? UNGROUPED_LABEL;\n const tools = byGroup.get(label) ?? [];\n tools.push({\n path: `/${view.slug}`,\n name: view.name,\n description: view.description ?? \"\",\n icon: view.icon\n });\n byGroup.set(label, tools);\n }\n\n const ordered = [\n ...GROUP_ORDER.filter(g => byGroup.has(g)),\n ...[...byGroup.keys()].filter(g => !GROUP_ORDER.includes(g))\n ];\n\n return ordered.map(label => ({ label,\ntools: byGroup.get(label)! }));\n}\n\n/* ═══════════════════════════════════════════════════════════════ */\n\nconst COLLAPSED_STORAGE_KEY = \"rebase-studio-home-collapsed\";\n\nfunction useStudioCollapsedGroups(groupNames: string[]) {\n const [collapsed, setCollapsed] = useState<Set<string>>(() => {\n try {\n const stored = localStorage.getItem(COLLAPSED_STORAGE_KEY);\n return stored ? new Set(JSON.parse(stored)) : new Set<string>();\n } catch {\n return new Set<string>();\n }\n });\n\n const isGroupCollapsed = (name: string) => collapsed.has(name);\n\n const toggleGroupCollapsed = (name: string) => {\n setCollapsed(prev => {\n const next = new Set(prev);\n if (next.has(name)) {\n next.delete(name);\n } else {\n next.add(name);\n }\n try {\n localStorage.setItem(COLLAPSED_STORAGE_KEY, JSON.stringify([...next]));\n } catch { /* noop */ }\n return next;\n });\n };\n\n return { isGroupCollapsed, toggleGroupCollapsed };\n}\n\nexport function StudioHomePage({\n additionalActions,\n additionalChildrenStart,\n additionalChildrenEnd,\n sections,\n hiddenGroups\n}: {\n additionalActions?: React.ReactNode;\n additionalChildrenStart?: React.ReactNode;\n additionalChildrenEnd?: React.ReactNode;\n sections?: HomePageSection[];\n hiddenGroups?: string[];\n}) {\n const context = useRebaseContext();\n const breadcrumbs = useStudioBreadcrumbs();\n const navigate = useNavigate();\n const registry = useRebaseRegistry();\n\n useEffect(() => {\n breadcrumbs.set({ breadcrumbs: [] });\n }, [breadcrumbs.set]);\n\n const { containerRef } = useRestoreScroll();\n\n const sectionProps: PluginGenericProps = { context };\n\n const pluginActions = useSlot(\"home.actions\", sectionProps);\n\n // The collection editor (\"schema\") is not part of `devViews` — RebaseNavigation\n // injects it when the admin enables a collection editor. Mirror that condition so\n // the card tracks the route.\n const schemaEnabled = Boolean(registry.studioConfig && registry.cmsConfig?.collectionEditor);\n\n const filteredSections = useMemo(() => {\n const views: StudioViewMeta[] = [];\n if (schemaEnabled) {\n views.push({\n slug: \"schema\",\n name: \"Collections\",\n group: \"Database\",\n icon: \"LayoutList\",\n description: \"Define and manage your data model and collection schemas\"\n });\n }\n views.push(...(registry.studioConfig?.devViews ?? []));\n return buildSections(views).filter(s => !hiddenGroups?.includes(s.label));\n }, [registry.studioConfig?.devViews, schemaEnabled, hiddenGroups]);\n\n const groupNames = useMemo(\n () => filteredSections.map(s => s.label),\n [filteredSections]\n );\n\n const { isGroupCollapsed, toggleGroupCollapsed } = useStudioCollapsedGroups(groupNames);\n\n return (\n <div ref={containerRef} className=\"py-2 overflow-auto h-full w-full bg-surface-50 dark:bg-surface-800\">\n <Container maxWidth=\"6xl\">\n <div className=\"mb-4 flex flex-col gap-2\">\n <SchemaDriftBanner />\n </div>\n\n {(additionalActions || pluginActions) && (\n <div className=\"w-full sticky py-4 transition-all duration-400 ease-in-out top-0 z-10 flex flex-row gap-4 justify-end\">\n {additionalActions}\n {pluginActions}\n </div>\n )}\n\n {additionalChildrenStart}\n\n {/* ── Tool sections ── */}\n {filteredSections.map((section) => {\n const sectionCollapsed = isGroupCollapsed(section.label);\n\n return (\n <div key={section.label} className=\"my-10\">\n <ExpandablePanel\n invisible\n expanded={!sectionCollapsed}\n onExpandedChange={(open) => {\n if (open !== !sectionCollapsed) {\n toggleGroupCollapsed(section.label);\n }\n }}\n className=\"mt-6\"\n titleClassName={cls(\n \"min-h-0 p-0 border-none\",\n \"rounded flex items-center justify-between w-full\",\n \"hover:bg-transparent\",\n \"cursor-pointer select-none\",\n sectionCollapsed && \"bg-surface-100 dark:bg-surface-900/50\"\n )}\n innerClassName=\"mt-4 pt-0\"\n title={\n <Typography\n variant=\"caption\"\n component=\"h2\"\n color=\"secondary\"\n className={cls(\n \"px-4 py-1 rounded\",\n \"font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70\"\n )}\n >\n {section.label}\n </Typography>\n }\n >\n <div className=\"mt-4 pt-0\">\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {section.tools.map((tool) => (\n <Card\n key={tool.path}\n onClick={() => {\n navigate(tool.path);\n context.analyticsController?.onAnalyticsEvent?.(\n \"home_navigate_to_view\",\n { path: tool.path }\n );\n }}\n className={cls(\n \"group h-full p-4 cursor-pointer transition-colors duration-150 ease-in-out\",\n \"hover:bg-primary/5 dark:hover:bg-primary/5\"\n )}\n >\n <div className=\"flex flex-col h-full\">\n {/* Header: icon + title */}\n <div className=\"flex items-center w-full justify-between mb-1\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center justify-center w-5 h-5 text-surface-400 dark:text-surface-500 transition-colors duration-150 group-hover:text-primary dark:group-hover:text-primary\">\n <IconForView\n collectionOrView={{ slug: tool.path, name: tool.name, icon: tool.icon }}\n size=\"small\"\n />\n </div>\n <Typography variant=\"subtitle1\" component=\"h2\">\n {tool.name}\n </Typography>\n </div>\n </div>\n\n {/* Description indented to align with title */}\n <div className=\"pl-8\">\n {tool.description && (\n <Typography variant=\"caption\" color=\"secondary\" component=\"div\">\n {tool.description}\n </Typography>\n )}\n </div>\n\n {/* Spacer */}\n <div className=\"grow\"/>\n </div>\n </Card>\n ))}\n </div>\n </div>\n </ExpandablePanel>\n </div>\n );\n })}\n\n {/* ── SDK Quick Start ── */}\n <div className=\"mt-10 mb-6\">\n <div className=\"flex items-center mb-1\">\n <Typography\n variant=\"caption\"\n component=\"h2\"\n color=\"secondary\"\n className={cls(\n \"px-4 py-1 rounded\",\n \"font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70\"\n )}\n >\n Quick Start\n </Typography>\n </div>\n\n <Typography variant=\"body2\" color=\"secondary\" className=\"mb-4 max-w-2xl\">\n Generate a fully-typed SDK from your collections with{\" \"}\n <code className=\"text-emerald-400 font-mono text-xs bg-emerald-400/10 px-1.5 py-0.5 rounded\">\n npx rebase generate-sdk\n </code>\n {\" \"}and start querying your data with full TypeScript autocompletion.\n </Typography>\n\n <div className=\"rounded-lg border border-surface-200/40 dark:border-surface-700/40 bg-white dark:bg-surface-950 overflow-hidden\">\n {/* Title bar */}\n <div className=\"flex items-center justify-between px-4 py-2.5 border-b border-surface-200/40 dark:border-surface-700/40 bg-surface-50 dark:bg-surface-900/80\">\n <div className=\"flex items-center gap-2.5\">\n <div className=\"flex gap-1.5\">\n <span className=\"w-2.5 h-2.5 rounded-full bg-red-400/60\"/>\n <span className=\"w-2.5 h-2.5 rounded-full bg-amber-400/60\"/>\n <span className=\"w-2.5 h-2.5 rounded-full bg-emerald-400/60\"/>\n </div>\n <span className=\"text-xs font-mono text-surface-400 dark:text-surface-500 ml-1\">\n app.ts\n </span>\n </div>\n <span className=\"text-xs font-mono text-surface-400 dark:text-surface-500\">\n TypeScript\n </span>\n </div>\n\n {/* Syntax-highlighted code */}\n <div className=\"px-5 py-4 overflow-x-auto text-[13px] leading-6 font-mono\">\n <SyntaxHighlightedSnippet/>\n </div>\n </div>\n </div>\n\n {/* ── Extra sections from props ── */}\n {sections?.map((s) => (\n <div key={s.key} className=\"my-10\">\n <Typography\n variant=\"caption\"\n component=\"h2\"\n color=\"secondary\"\n className={cls(\n \"px-4 py-1 rounded\",\n \"font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70\"\n )}\n >\n {s.title}\n </Typography>\n <div className=\"mt-4\">{s.children}</div>\n </div>\n ))}\n\n {additionalChildrenEnd}\n </Container>\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Hand-crafted syntax-highlighted code snippet.\n Uses inline spans with Tailwind color classes to avoid\n pulling in a syntax highlighting library.\n ═══════════════════════════════════════════════════════════════ */\n\nfunction SyntaxHighlightedSnippet() {\n const kw = \"text-violet-600 dark:text-violet-400\"; // keywords\n const str = \"text-emerald-600 dark:text-emerald-400\"; // strings\n const typ = \"text-amber-600 dark:text-amber-300\"; // types\n const fn = \"text-blue-600 dark:text-blue-400\"; // functions\n const cm = \"text-surface-500 dark:text-surface-400 italic\"; // comments\n const op = \"text-surface-500 dark:text-surface-400\"; // operators / punctuation\n const tx = \"text-surface-950 dark:text-surface-200\"; // plain text\n\n return (\n <pre className=\"m-0 whitespace-pre\">\n <span className={kw}>import</span>\n <span className={tx}>{\" { \"}</span>\n <span className={fn}>createRebaseClient</span>\n <span className={tx}>{\" } \"}</span>\n <span className={kw}>from</span>\n <span className={tx}> </span>\n <span className={str}>'@rebasepro/client'</span>\n <span className={op}>;</span>\n {\"\\n\"}\n\n <span className={kw}>import</span>\n <span className={tx}> </span>\n <span className={kw}>type</span>\n <span className={tx}>{\" { \"}</span>\n <span className={typ}>Database</span>\n <span className={tx}>{\" } \"}</span>\n <span className={kw}>from</span>\n <span className={tx}> </span>\n <span className={str}>'./database.types'</span>\n <span className={op}>;</span>\n {\"\\n\\n\"}\n\n <span className={kw}>const</span>\n <span className={tx}> rebase </span>\n <span className={op}>= </span>\n <span className={fn}>createRebaseClient</span>\n <span className={op}>{\"<\"}</span>\n <span className={typ}>Database</span>\n <span className={op}>{\">(\"}</span>\n <span className={tx}>{\"{\"}</span>\n {\"\\n\"}\n <span className={tx}>{\" baseUrl\"}</span>\n <span className={op}>: </span>\n <span className={str}>'http://localhost:3001'</span>\n <span className={op}>,</span>\n {\"\\n\"}\n <span className={tx}>{\"}\"}</span>\n <span className={op}>);</span>\n {\"\\n\\n\"}\n\n <span className={cm}>{\"// Fully typed — autocompletion for tables and columns\"}</span>\n {\"\\n\"}\n <span className={kw}>const</span>\n <span className={tx}>{\" { \"}</span>\n <span className={tx}>data</span>\n <span className={op}>: </span>\n <span className={tx}>users</span>\n <span className={tx}>{\" } \"}</span>\n <span className={op}>= </span>\n <span className={kw}>await</span>\n <span className={tx}> rebase</span>\n <span className={op}>.</span>\n <span className={tx}>data</span>\n <span className={op}>.</span>\n <span className={tx}>users</span>\n <span className={op}>.</span>\n <span className={fn}>find</span>\n <span className={op}>();</span>\n {\"\\n\"}\n\n <span className={kw}>const</span>\n <span className={tx}>{\" { \"}</span>\n <span className={tx}>data</span>\n <span className={op}>: </span>\n <span className={tx}>posts</span>\n <span className={tx}>{\" } \"}</span>\n <span className={op}>= </span>\n <span className={kw}>await</span>\n <span className={tx}> rebase</span>\n <span className={op}>.</span>\n <span className={tx}>data</span>\n <span className={op}>.</span>\n <span className={fn}>collection</span>\n <span className={op}>(</span>\n <span className={str}>'posts'</span>\n <span className={op}>)</span>\n <span className={op}>.</span>\n <span className={fn}>find</span>\n <span className={op}>();</span>\n </pre>\n );\n}\n","import React, { lazy, Suspense, useLayoutEffect, useMemo } from \"react\";\nimport { useRebaseRegistryDispatch } from \"@rebasepro/app\";\nimport type { RebaseStudioConfig, AppView } from \"@rebasepro/admin-types\";\nimport { CircularProgressCenter } from \"@rebasepro/ui\";\n\n// Lazy-loaded studio tools — each fetched only when its route is visited.\n// This keeps Monaco, @xyflow/react, dagre, pgsql-ast-parser etc. out of the initial bundle.\nconst SQLEditor = lazy(() => import(\"./SQLEditor/SQLEditor\").then(m => ({ default: m.SQLEditor })));\nconst JSEditor = lazy(() => import(\"./JSEditor/JSEditor\").then(m => ({ default: m.JSEditor })));\nconst RLSEditor = lazy(() => import(\"./RLSEditor/RLSEditor\").then(m => ({ default: m.RLSEditor })));\nconst StorageView = lazy(() => import(\"./StorageView/StorageView\").then(m => ({ default: m.StorageView })));\nconst CronJobsView = lazy(() => import(\"./CronJobs/CronJobsView\").then(m => ({ default: m.CronJobsView })));\nconst SchemaVisualizer = lazy(() => import(\"./SchemaVisualizer/SchemaVisualizer\").then(m => ({ default: m.SchemaVisualizer })));\nconst BranchesView = lazy(() => import(\"./Branches/BranchesView\").then(m => ({ default: m.BranchesView })));\nconst BackupsView = lazy(() => import(\"./Backups/BackupsView\").then(m => ({ default: m.BackupsView })));\nconst ApiExplorer = lazy(() => import(\"./ApiExplorer/ApiExplorer\").then(m => ({ default: m.ApiExplorer })));\nconst LogsExplorer = lazy(() => import(\"./LogsExplorer/LogsExplorer\").then(m => ({ default: m.LogsExplorer })));\nconst ApiKeysView = lazy(() => import(\"./ApiKeys/ApiKeysView\").then(m => ({ default: m.ApiKeysView })));\n\nimport { StudioHomePage } from \"./StudioHomePage\";\n\n/**\n * Declarative component to configure the Studio in Rebase.\n * Renders nothing — purely registers config into the RebaseRegistry.\n *\n * The \"schema\" tool (collection editor view) is now a built-in admin feature.\n * When `<RebaseAdmin collectionEditor={...}>` is used, the schema view is\n * automatically injected into Studio — no manual wiring needed.\n */\nconst DEFAULT_HOME_PAGE = <StudioHomePage/>;\n\nexport function RebaseStudio({ tools, homePage }: RebaseStudioConfig) {\n const dispatch = useRebaseRegistryDispatch();\n\n const resolvedHomePage = homePage ?? DEFAULT_HOME_PAGE;\n\n /**\n * The tool list reduced to a value, so that re-rendering the host does not\n * republish the registry.\n *\n * `tools` is an array, and callers write it inline — `tools={[...TOOLS]}`\n * in the hosted console. A fresh array every render meant a fresh\n * `devViews` every render, which meant the effect below unregistered and\n * re-registered on every render. Because each registration builds *new*\n * `<Suspense><SQLEditor/></Suspense>` elements, whatever view was on screen\n * was torn down and remounted each time: a half-typed SQL query, a storage\n * folder you had navigated into, an open policy editor — all discarded, and\n * every mount reissued the view's initial requests.\n */\n const toolsKey = tools ? tools.join(\",\") : \"\";\n\n const devViews: AppView[] = useMemo(() => {\n const views: AppView[] = [];\n const activeTools = tools ?? [\"sql\", \"js\", \"rls\", \"storage\", \"cron\", \"schema-visualizer\", \"branches\", \"backups\", \"api\", \"logs\", \"api-keys\"];\n const suspense = (el: React.ReactNode) => <Suspense fallback={<CircularProgressCenter/>}>{el}</Suspense>;\n\n if (activeTools.includes(\"sql\")) {\n views.push({ slug: \"sql\",\nname: \"SQL Console\",\ngroup: \"Database\",\nicon: \"terminal\",\ndescription: \"Execute SQL queries\",\nview: suspense(<SQLEditor/>) });\n }\n if (activeTools.includes(\"js\")) {\n views.push({ slug: \"js\",\nname: \"JS Console\",\ngroup: \"Compute\",\nicon: \"code\",\ndescription: \"Execute JavaScript\",\nview: suspense(<JSEditor/>) });\n }\n if (activeTools.includes(\"rls\")) {\n views.push({ slug: \"rls\",\nname: \"RLS Policies\",\ngroup: \"Database\",\nicon: \"ShieldCheck\",\ndescription: \"Row Level Security\",\nview: suspense(<RLSEditor/>) });\n }\n if (activeTools.includes(\"storage\")) {\n views.push({ slug: \"storage\",\nname: \"Storage\",\ngroup: \"Storage\",\nicon: \"HardDrive\",\ndescription: \"Manage storage files\",\nview: suspense(<StorageView/>) });\n }\n if (activeTools.includes(\"cron\")) {\n views.push({ slug: \"cron\",\nname: \"Cron Jobs\",\ngroup: \"Compute\",\nicon: \"Clock\",\ndescription: \"Manage scheduled tasks\",\nview: suspense(<CronJobsView/>) });\n }\n if (activeTools.includes(\"schema-visualizer\")) {\n views.push({ slug: \"schema-visualizer\",\nname: \"Schema Visualizer\",\ngroup: \"Database\",\nicon: \"Network\",\ndescription: \"Interactive database ERD\",\nview: suspense(<SchemaVisualizer/>) });\n }\n if (activeTools.includes(\"branches\")) {\n views.push({ slug: \"branches\",\nname: \"Branches\",\ngroup: \"Database\",\nicon: \"GitBranch\",\ndescription: \"Create and manage database branches\",\nview: suspense(<BranchesView/>) });\n }\n if (activeTools.includes(\"backups\")) {\n views.push({ slug: \"backups\",\nname: \"Backups\",\ngroup: \"Database\",\nicon: \"Database\",\ndescription: \"Download database backups\",\nview: suspense(<BackupsView/>) });\n }\n if (activeTools.includes(\"api\")) {\n views.push({ slug: \"api\",\nname: \"API Explorer\",\ngroup: \"API\",\nicon: \"BookOpen\",\ndescription: \"Interactive API documentation and testing\",\nview: suspense(<ApiExplorer/>) });\n }\n if (activeTools.includes(\"logs\")) {\n views.push({ slug: \"logs\",\nname: \"Logs Explorer\",\ngroup: \"Database\",\nicon: \"Activity\",\ndescription: \"Real-time system and query logs\",\nview: suspense(<LogsExplorer/>) });\n }\n if (activeTools.includes(\"api-keys\")) {\n views.push({ slug: \"api-keys\",\nname: \"API Keys\",\ngroup: \"Access Control\",\nicon: \"KeyRound\",\ndescription: \"Create and manage scoped API keys\",\nview: suspense(<ApiKeysView/>) });\n }\n // Note: \"schema\" tool is auto-injected by RebaseShell when collectionEditor is enabled.\n // It is NOT registered here anymore.\n return views;\n // Keyed by the tool list's *contents*; see `toolsKey`. `tools` itself is\n // read inside, and is exactly determined by the key.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [toolsKey]);\n\n // Use a ref for homePage so it never destabilizes the effect.\n // homePage is a React element — its identity doesn't matter for registration.\n const homePageRef = React.useRef(resolvedHomePage);\n homePageRef.current = resolvedHomePage;\n\n // Same reasoning for `tools`: it is registered as config, but a new array\n // with the same contents must not count as a change.\n const toolsRef = React.useRef(tools);\n toolsRef.current = tools;\n\n useLayoutEffect(() => {\n dispatch.registerStudio({ tools: toolsRef.current,\nhomePage: homePageRef.current,\ndevViews });\n return () => dispatch.unregisterStudio();\n }, [dispatch, devViews]);\n\n return null;\n}\n"],"mappings":"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"}
|