@rebasepro/studio 0.9.1-canary.fd3754b → 0.10.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/dist/{ApiKeysView-Dbe5cXkS.js → ApiKeysView-vQKqt2wl.js} +44 -2
- package/dist/ApiKeysView-vQKqt2wl.js.map +1 -0
- package/dist/{JSEditor-Etgw9-Cw.js → JSEditor-BeHHuxhA.js} +3 -3
- package/dist/{JSEditor-Etgw9-Cw.js.map → JSEditor-BeHHuxhA.js.map} +1 -1
- package/dist/LogsExplorer-BCXoGREr.js +298 -0
- package/dist/LogsExplorer-BCXoGREr.js.map +1 -0
- package/dist/{RLSEditor-Cgv_kUVW.js → RLSEditor-rLlMcZPG.js} +42 -28
- package/dist/RLSEditor-rLlMcZPG.js.map +1 -0
- package/dist/{SQLEditor-ZZsy1Mk6.js → SQLEditor-_dCqVAN_.js} +3 -3
- package/dist/SQLEditor-_dCqVAN_.js.map +1 -0
- package/dist/{SchemaVisualizer-D01DzU-f.js → SchemaVisualizer-C_ErehfH.js} +2 -2
- package/dist/{SchemaVisualizer-D01DzU-f.js.map → SchemaVisualizer-C_ErehfH.js.map} +1 -1
- package/dist/components/RLSEditor/PolicyEditor.d.ts +1 -1
- package/dist/components/SQLEditor/SQLEditor.d.ts +2 -10
- package/dist/components/SQLEditor/SQLEditorSidebar.d.ts +1 -1
- package/dist/components/SQLEditor/SchemaBrowser.d.ts +1 -1
- package/dist/components/SQLEditor/sql_editor_types.d.ts +16 -0
- package/dist/index.es.js +58 -105
- package/dist/index.es.js.map +1 -1
- package/dist/utils/sql_utils.d.ts +1 -1
- package/package.json +13 -12
- package/src/components/ApiKeys/ApiKeysView.tsx +41 -1
- package/src/components/JSEditor/JSEditor.tsx +3 -3
- package/src/components/LogsExplorer/LogsExplorer.tsx +168 -46
- package/src/components/RLSEditor/PolicyEditor.tsx +15 -13
- package/src/components/RLSEditor/RLSEditor.tsx +48 -18
- package/src/components/SQLEditor/SQLEditor.tsx +3 -11
- package/src/components/SQLEditor/SQLEditorSidebar.tsx +1 -1
- package/src/components/SQLEditor/SchemaBrowser.tsx +3 -3
- package/src/components/SQLEditor/sql_editor_types.ts +18 -0
- package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +2 -1
- package/src/components/StudioHomePage.tsx +61 -86
- package/src/utils/sql_utils.test.ts +1 -1
- package/src/utils/sql_utils.ts +1 -1
- package/dist/ApiKeysView-Dbe5cXkS.js.map +0 -1
- package/dist/LogsExplorer-J4xfsuv3.js +0 -206
- package/dist/LogsExplorer-J4xfsuv3.js.map +0 -1
- package/dist/RLSEditor-Cgv_kUVW.js.map +0 -1
- package/dist/SQLEditor-ZZsy1Mk6.js.map +0 -1
- package/dist/index.umd.js +0 -11709
- package/dist/index.umd.js.map +0 -1
package/dist/index.es.js
CHANGED
|
@@ -1,107 +1,43 @@
|
|
|
1
|
-
import { IconForView, SchemaDriftBanner, StudioBridgeContext, StudioBridgeProvider, useRebaseContext, useRebaseRegistryDispatch, useRestoreScroll, useSlot, useStudioBreadcrumbs, useStudioBreadcrumbs as useStudioBreadcrumbs$1, useStudioCollectionRegistry, useStudioNavigationState, useStudioSidePanelController, useStudioUrlController } from "@rebasepro/app";
|
|
1
|
+
import { IconForView, SchemaDriftBanner, StudioBridgeContext, StudioBridgeProvider, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRestoreScroll, useSlot, useStudioBreadcrumbs, useStudioBreadcrumbs as useStudioBreadcrumbs$1, useStudioCollectionRegistry, useStudioNavigationState, useStudioSidePanelController, useStudioUrlController } from "@rebasepro/app";
|
|
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
5
|
import { useNavigate } from "react-router-dom";
|
|
6
6
|
//#region src/components/StudioHomePage.tsx
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
description: "Define and manage your data model and collection schemas",
|
|
15
|
-
icon: "LayoutList"
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
path: "/schema-visualizer",
|
|
19
|
-
name: "Schema Visualizer",
|
|
20
|
-
description: "Interactive ERD showing tables, columns, and relationships",
|
|
21
|
-
icon: "Network"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
path: "/sql",
|
|
25
|
-
name: "SQL Console",
|
|
26
|
-
description: "Execute raw SQL queries directly against your database",
|
|
27
|
-
icon: "terminal"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
path: "/branches",
|
|
31
|
-
name: "Branches",
|
|
32
|
-
description: "Create and manage isolated database copies for development",
|
|
33
|
-
icon: "GitBranch"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
path: "/rls",
|
|
37
|
-
name: "RLS Policies",
|
|
38
|
-
description: "Configure Row Level Security for fine-grained data access",
|
|
39
|
-
icon: "ShieldCheck"
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
path: "/logs",
|
|
43
|
-
name: "Logs Explorer",
|
|
44
|
-
description: "Real-time system, query, and authentication logs",
|
|
45
|
-
icon: "Activity"
|
|
46
|
-
}
|
|
47
|
-
]
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
label: "Compute",
|
|
51
|
-
tools: [{
|
|
52
|
-
path: "/js",
|
|
53
|
-
name: "JS Console",
|
|
54
|
-
description: "Run JavaScript with the Rebase SDK in a live sandbox",
|
|
55
|
-
icon: "code"
|
|
56
|
-
}, {
|
|
57
|
-
path: "/cron",
|
|
58
|
-
name: "Cron Jobs",
|
|
59
|
-
description: "Monitor and manage scheduled background tasks",
|
|
60
|
-
icon: "Clock"
|
|
61
|
-
}]
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
label: "API",
|
|
65
|
-
tools: [{
|
|
66
|
-
path: "/api",
|
|
67
|
-
name: "API Explorer",
|
|
68
|
-
description: "Interactive API documentation with live request testing",
|
|
69
|
-
icon: "BookOpen"
|
|
70
|
-
}]
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
label: "Storage",
|
|
74
|
-
tools: [{
|
|
75
|
-
path: "/storage",
|
|
76
|
-
name: "Storage",
|
|
77
|
-
description: "Browse, upload, and manage files in your storage bucket",
|
|
78
|
-
icon: "HardDrive"
|
|
79
|
-
}]
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
label: "Access Control",
|
|
83
|
-
tools: [
|
|
84
|
-
{
|
|
85
|
-
path: "/users",
|
|
86
|
-
name: "Users",
|
|
87
|
-
description: "Manage developers and assign roles in your workspace",
|
|
88
|
-
icon: "group"
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
path: "/roles",
|
|
92
|
-
name: "Roles",
|
|
93
|
-
description: "Create and configure fine-grained access permissions",
|
|
94
|
-
icon: "ShieldEllipsis"
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
path: "/api-keys",
|
|
98
|
-
name: "API Keys",
|
|
99
|
-
description: "Create and manage scoped keys for machine-to-machine access",
|
|
100
|
-
icon: "KeyRound"
|
|
101
|
-
}
|
|
102
|
-
]
|
|
103
|
-
}
|
|
7
|
+
/** Group order for the home page; unknown groups are appended in view order. */
|
|
8
|
+
var GROUP_ORDER = [
|
|
9
|
+
"Database",
|
|
10
|
+
"Compute",
|
|
11
|
+
"Storage",
|
|
12
|
+
"API",
|
|
13
|
+
"Access Control"
|
|
104
14
|
];
|
|
15
|
+
var UNGROUPED_LABEL = "Tools";
|
|
16
|
+
/**
|
|
17
|
+
* Build the home page sections from the Studio views that are actually
|
|
18
|
+
* registered, so a card exists if and only if its route does. A hand-written
|
|
19
|
+
* list drifts: it used to advertise Users and Roles pages that 404, while
|
|
20
|
+
* omitting the Backups view that does exist.
|
|
21
|
+
*/
|
|
22
|
+
function buildSections(views) {
|
|
23
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
24
|
+
for (const view of views) {
|
|
25
|
+
if (view.hideFromNavigation) continue;
|
|
26
|
+
const label = view.group ?? UNGROUPED_LABEL;
|
|
27
|
+
const tools = byGroup.get(label) ?? [];
|
|
28
|
+
tools.push({
|
|
29
|
+
path: `/${view.slug}`,
|
|
30
|
+
name: view.name,
|
|
31
|
+
description: view.description ?? "",
|
|
32
|
+
icon: view.icon
|
|
33
|
+
});
|
|
34
|
+
byGroup.set(label, tools);
|
|
35
|
+
}
|
|
36
|
+
return [...GROUP_ORDER.filter((g) => byGroup.has(g)), ...[...byGroup.keys()].filter((g) => !GROUP_ORDER.includes(g))].map((label) => ({
|
|
37
|
+
label,
|
|
38
|
+
tools: byGroup.get(label)
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
105
41
|
var COLLAPSED_STORAGE_KEY = "rebase-studio-home-collapsed";
|
|
106
42
|
function useStudioCollapsedGroups(groupNames) {
|
|
107
43
|
const [collapsed, setCollapsed] = useState(() => {
|
|
@@ -133,12 +69,29 @@ function StudioHomePage({ additionalActions, additionalChildrenStart, additional
|
|
|
133
69
|
const context = useRebaseContext();
|
|
134
70
|
const breadcrumbs = useStudioBreadcrumbs$1();
|
|
135
71
|
const navigate = useNavigate();
|
|
72
|
+
const registry = useRebaseRegistry();
|
|
136
73
|
useEffect(() => {
|
|
137
74
|
breadcrumbs.set({ breadcrumbs: [] });
|
|
138
75
|
}, [breadcrumbs.set]);
|
|
139
76
|
const { containerRef } = useRestoreScroll();
|
|
140
77
|
const pluginActions = useSlot("home.actions", { context });
|
|
141
|
-
const
|
|
78
|
+
const schemaEnabled = Boolean(registry.studioConfig && registry.cmsConfig?.collectionEditor);
|
|
79
|
+
const filteredSections = useMemo(() => {
|
|
80
|
+
const views = [];
|
|
81
|
+
if (schemaEnabled) views.push({
|
|
82
|
+
slug: "schema",
|
|
83
|
+
name: "Collections",
|
|
84
|
+
group: "Database",
|
|
85
|
+
icon: "LayoutList",
|
|
86
|
+
description: "Define and manage your data model and collection schemas"
|
|
87
|
+
});
|
|
88
|
+
views.push(...registry.studioConfig?.devViews ?? []);
|
|
89
|
+
return buildSections(views).filter((s) => !hiddenGroups?.includes(s.label));
|
|
90
|
+
}, [
|
|
91
|
+
registry.studioConfig?.devViews,
|
|
92
|
+
schemaEnabled,
|
|
93
|
+
hiddenGroups
|
|
94
|
+
]);
|
|
142
95
|
const { isGroupCollapsed, toggleGroupCollapsed } = useStudioCollapsedGroups(useMemo(() => filteredSections.map((s) => s.label), [filteredSections]));
|
|
143
96
|
return /* @__PURE__ */ jsx("div", {
|
|
144
97
|
ref: containerRef,
|
|
@@ -596,17 +549,17 @@ function SyntaxHighlightedSnippet() {
|
|
|
596
549
|
}
|
|
597
550
|
//#endregion
|
|
598
551
|
//#region src/components/RebaseStudio.tsx
|
|
599
|
-
var SQLEditor = lazy(() => import("./SQLEditor-
|
|
600
|
-
var JSEditor = lazy(() => import("./JSEditor-
|
|
601
|
-
var RLSEditor = lazy(() => import("./RLSEditor-
|
|
552
|
+
var SQLEditor = lazy(() => import("./SQLEditor-_dCqVAN_.js").then((m) => ({ default: m.SQLEditor })));
|
|
553
|
+
var JSEditor = lazy(() => import("./JSEditor-BeHHuxhA.js").then((m) => ({ default: m.JSEditor })));
|
|
554
|
+
var RLSEditor = lazy(() => import("./RLSEditor-rLlMcZPG.js").then((m) => ({ default: m.RLSEditor })));
|
|
602
555
|
var StorageView = lazy(() => import("./StorageView-UvyM2xdW.js").then((m) => ({ default: m.StorageView })));
|
|
603
556
|
var CronJobsView = lazy(() => import("./CronJobsView-B59i3Mis.js").then((m) => ({ default: m.CronJobsView })));
|
|
604
|
-
var SchemaVisualizer = lazy(() => import("./SchemaVisualizer-
|
|
557
|
+
var SchemaVisualizer = lazy(() => import("./SchemaVisualizer-C_ErehfH.js").then((m) => ({ default: m.SchemaVisualizer })));
|
|
605
558
|
var BranchesView = lazy(() => import("./BranchesView-DH0qnU9J.js").then((m) => ({ default: m.BranchesView })));
|
|
606
559
|
var BackupsView = lazy(() => import("./BackupsView-DNS6LdVg.js").then((m) => ({ default: m.BackupsView })));
|
|
607
560
|
var ApiExplorer = lazy(() => import("./ApiExplorer-B9M9-uy_.js").then((m) => ({ default: m.ApiExplorer })));
|
|
608
|
-
var LogsExplorer = lazy(() => import("./LogsExplorer-
|
|
609
|
-
var ApiKeysView = lazy(() => import("./ApiKeysView-
|
|
561
|
+
var LogsExplorer = lazy(() => import("./LogsExplorer-BCXoGREr.js").then((m) => ({ default: m.LogsExplorer })));
|
|
562
|
+
var ApiKeysView = lazy(() => import("./ApiKeysView-vQKqt2wl.js").then((m) => ({ default: m.ApiKeysView })));
|
|
610
563
|
/**
|
|
611
564
|
* Declarative component to configure the Studio in Rebase.
|
|
612
565
|
* Renders nothing — purely registers config into the RebaseRegistry.
|
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 { HomePageSection, PluginGenericProps } from \"@rebasepro/types\";\nimport React, { useEffect, useMemo, useState } from \"react\";\nimport { Card, cls, Container, ExpandablePanel, Typography } from \"@rebasepro/ui\";\nimport { IconForView, useRebaseContext, useRestoreScroll, useSlot } from \"@rebasepro/app\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useStudioBreadcrumbs, SchemaDriftBanner } from \"@rebasepro/app\";\n\n/* ═══════════════════════════════════════════════════════════════\n Static studio tool definitions\n ═══════════════════════════════════════════════════════════════ */\n\ninterface StudioTool {\n path: string;\n name: string;\n description: string;\n icon: string;\n}\n\ninterface StudioSection {\n label: string;\n tools: StudioTool[];\n}\n\nconst SECTIONS: StudioSection[] = [\n {\n label: \"Database\",\n tools: [\n { path: \"/schema\",\nname: \"Collections\",\ndescription: \"Define and manage your data model and collection schemas\",\nicon: \"LayoutList\" },\n { path: \"/schema-visualizer\",\nname: \"Schema Visualizer\",\ndescription: \"Interactive ERD showing tables, columns, and relationships\",\nicon: \"Network\" },\n { path: \"/sql\",\nname: \"SQL Console\",\ndescription: \"Execute raw SQL queries directly against your database\",\nicon: \"terminal\" },\n { path: \"/branches\",\nname: \"Branches\",\ndescription: \"Create and manage isolated database copies for development\",\nicon: \"GitBranch\" },\n { path: \"/rls\",\nname: \"RLS Policies\",\ndescription: \"Configure Row Level Security for fine-grained data access\",\nicon: \"ShieldCheck\" },\n { path: \"/logs\",\nname: \"Logs Explorer\",\ndescription: \"Real-time system, query, and authentication logs\",\nicon: \"Activity\" }\n ]\n },\n {\n label: \"Compute\",\n tools: [\n { path: \"/js\",\nname: \"JS Console\",\ndescription: \"Run JavaScript with the Rebase SDK in a live sandbox\",\nicon: \"code\" },\n { path: \"/cron\",\nname: \"Cron Jobs\",\ndescription: \"Monitor and manage scheduled background tasks\",\nicon: \"Clock\" }\n ]\n },\n {\n label: \"API\",\n tools: [\n { path: \"/api\",\nname: \"API Explorer\",\ndescription: \"Interactive API documentation with live request testing\",\nicon: \"BookOpen\" }\n ]\n },\n {\n label: \"Storage\",\n tools: [\n { path: \"/storage\",\nname: \"Storage\",\ndescription: \"Browse, upload, and manage files in your storage bucket\",\nicon: \"HardDrive\" }\n ]\n },\n {\n label: \"Access Control\",\n tools: [\n { path: \"/users\",\nname: \"Users\",\ndescription: \"Manage developers and assign roles in your workspace\",\nicon: \"group\" },\n { path: \"/roles\",\nname: \"Roles\",\ndescription: \"Create and configure fine-grained access permissions\",\nicon: \"ShieldEllipsis\" },\n { path: \"/api-keys\",\nname: \"API Keys\",\ndescription: \"Create and manage scoped keys for machine-to-machine access\",\nicon: \"KeyRound\" }\n ]\n }\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\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 const filteredSections = useMemo(\n () => SECTIONS.filter(s => !hiddenGroups?.includes(s.label)),\n [hiddenGroups]\n );\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/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 CMS 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/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 CMS 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/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 CMS 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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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/studio",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"main": "./dist/index.
|
|
4
|
+
"version": "0.10.0",
|
|
5
|
+
"main": "./dist/index.es.js",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"source": "src/index.ts",
|
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
"pgsql-ast-parser": "12.0.2",
|
|
16
16
|
"prism-react-renderer": "^2.4.1",
|
|
17
17
|
"react-dropzone": "^15.0.0",
|
|
18
|
-
"@rebasepro/client": "0.
|
|
19
|
-
"@rebasepro/
|
|
20
|
-
"@rebasepro/
|
|
21
|
-
"@rebasepro/
|
|
22
|
-
"@rebasepro/ui": "0.
|
|
23
|
-
"@rebasepro/
|
|
18
|
+
"@rebasepro/client": "0.10.0",
|
|
19
|
+
"@rebasepro/app": "0.10.0",
|
|
20
|
+
"@rebasepro/types": "0.10.0",
|
|
21
|
+
"@rebasepro/common": "0.10.0",
|
|
22
|
+
"@rebasepro/ui": "0.10.0",
|
|
23
|
+
"@rebasepro/utils": "0.10.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"react": ">=19.0.0",
|
|
27
27
|
"react-dom": ">=19.0.0",
|
|
28
28
|
"react-router": "^7.0.0",
|
|
29
29
|
"react-router-dom": "^7.0.0",
|
|
30
|
-
"@rebasepro/admin": "0.
|
|
30
|
+
"@rebasepro/admin": "0.10.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependenciesMeta": {
|
|
33
33
|
"@rebasepro/admin": {
|
|
@@ -38,8 +38,7 @@
|
|
|
38
38
|
".": {
|
|
39
39
|
"types": "./dist/index.d.ts",
|
|
40
40
|
"development": "./dist/index.es.js",
|
|
41
|
-
"import": "./dist/index.es.js"
|
|
42
|
-
"require": "./dist/index.umd.js"
|
|
41
|
+
"import": "./dist/index.es.js"
|
|
43
42
|
},
|
|
44
43
|
"./package.json": "./package.json"
|
|
45
44
|
},
|
|
@@ -57,6 +56,8 @@
|
|
|
57
56
|
},
|
|
58
57
|
"devDependencies": {
|
|
59
58
|
"@jest/globals": "^30.4.1",
|
|
59
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
60
|
+
"@testing-library/react": "^16.3.2",
|
|
60
61
|
"@types/jest": "^30.0.0",
|
|
61
62
|
"@types/node": "^25.9.3",
|
|
62
63
|
"@types/react": "^19.2.17",
|
|
@@ -86,7 +87,7 @@
|
|
|
86
87
|
"scripts": {
|
|
87
88
|
"dev": "vite",
|
|
88
89
|
"test": "jest --passWithNoTests",
|
|
89
|
-
"build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
|
|
90
|
+
"build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json && node ../../scripts/assert-build-output.mjs",
|
|
90
91
|
"clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
|
|
91
92
|
}
|
|
92
93
|
}
|
|
@@ -108,6 +108,7 @@ export function ApiKeysView() {
|
|
|
108
108
|
const [showCreate, setShowCreate] = useState(false);
|
|
109
109
|
const [showSecret, setShowSecret] = useState<ApiKeyWithSecret | null>(null);
|
|
110
110
|
const [revoking, setRevoking] = useState<string | null>(null);
|
|
111
|
+
const [confirmRevoke, setConfirmRevoke] = useState<ApiKeyMasked | null>(null);
|
|
111
112
|
|
|
112
113
|
const clientRef = useRef(client);
|
|
113
114
|
clientRef.current = client;
|
|
@@ -223,7 +224,7 @@ export function ApiKeysView() {
|
|
|
223
224
|
size="small"
|
|
224
225
|
color="error"
|
|
225
226
|
variant="outlined"
|
|
226
|
-
onClick={() =>
|
|
227
|
+
onClick={() => setConfirmRevoke(selectedKey)}
|
|
227
228
|
disabled={revoking === selectedKey.id}
|
|
228
229
|
startIcon={revoking === selectedKey.id ? <CircularProgress size="smallest"/> : <DeleteIcon size={iconSize.smallest}/>}
|
|
229
230
|
>
|
|
@@ -283,6 +284,45 @@ export function ApiKeysView() {
|
|
|
283
284
|
</div>
|
|
284
285
|
</div>
|
|
285
286
|
|
|
287
|
+
{/* Revoke Confirmation Dialog */}
|
|
288
|
+
<Dialog
|
|
289
|
+
open={confirmRevoke !== null}
|
|
290
|
+
onOpenChange={(open) => {
|
|
291
|
+
if (!open && !revoking) setConfirmRevoke(null);
|
|
292
|
+
}}
|
|
293
|
+
>
|
|
294
|
+
<DialogTitle hidden>Revoke Confirmation</DialogTitle>
|
|
295
|
+
<DialogContent>
|
|
296
|
+
<Typography variant="subtitle1" className="font-semibold mb-2">
|
|
297
|
+
Revoke "{confirmRevoke?.name}"?
|
|
298
|
+
</Typography>
|
|
299
|
+
<Typography variant="body2" color="secondary">
|
|
300
|
+
Requests authenticated with this key will stop working immediately. This action cannot be undone.
|
|
301
|
+
</Typography>
|
|
302
|
+
</DialogContent>
|
|
303
|
+
<DialogActions>
|
|
304
|
+
<Button
|
|
305
|
+
variant="text"
|
|
306
|
+
onClick={() => setConfirmRevoke(null)}
|
|
307
|
+
disabled={revoking !== null}
|
|
308
|
+
>
|
|
309
|
+
Cancel
|
|
310
|
+
</Button>
|
|
311
|
+
<Button
|
|
312
|
+
color="error"
|
|
313
|
+
disabled={revoking !== null}
|
|
314
|
+
startIcon={revoking !== null ? <CircularProgress size="smallest"/> : <DeleteIcon size={iconSize.smallest}/>}
|
|
315
|
+
onClick={async () => {
|
|
316
|
+
if (!confirmRevoke) return;
|
|
317
|
+
await handleRevoke(confirmRevoke.id);
|
|
318
|
+
setConfirmRevoke(null);
|
|
319
|
+
}}
|
|
320
|
+
>
|
|
321
|
+
Revoke
|
|
322
|
+
</Button>
|
|
323
|
+
</DialogActions>
|
|
324
|
+
</Dialog>
|
|
325
|
+
|
|
286
326
|
{/* Create Dialog */}
|
|
287
327
|
{showCreate && (
|
|
288
328
|
<CreateApiKeyDialog
|
|
@@ -670,7 +670,7 @@ message: t("studio_sql_markdown_copy_failed") });
|
|
|
670
670
|
</div>
|
|
671
671
|
</div>
|
|
672
672
|
<div className="flex shrink-0 items-center justify-end gap-1.5">
|
|
673
|
-
<Tooltip title="
|
|
673
|
+
<Tooltip title="Save as snippet">
|
|
674
674
|
<IconButton
|
|
675
675
|
size="small"
|
|
676
676
|
onClick={() => {
|
|
@@ -1009,9 +1009,9 @@ id: String(ra.entityId) })}
|
|
|
1009
1009
|
/>
|
|
1010
1010
|
</div>
|
|
1011
1011
|
|
|
1012
|
-
{/*
|
|
1012
|
+
{/* Save snippet dialog */}
|
|
1013
1013
|
<Dialog open={showSaveDialog} onOpenChange={setShowSaveDialog}>
|
|
1014
|
-
<DialogTitle>
|
|
1014
|
+
<DialogTitle>Save Snippet</DialogTitle>
|
|
1015
1015
|
<DialogContent>
|
|
1016
1016
|
<TextField
|
|
1017
1017
|
label="Snippet name"
|