@rebasepro/studio 0.17.3 → 0.18.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.
Files changed (81) hide show
  1. package/LICENSE +0 -1
  2. package/README.md +12 -4
  3. package/dist/{ApiKeysView-DiCurTEU.js → ApiKeysView-EIvegrUk.js} +54 -39
  4. package/dist/ApiKeysView-EIvegrUk.js.map +1 -0
  5. package/dist/{BackupsView-DNS6LdVg.js → BackupsView-DVkKzaPR.js} +29 -31
  6. package/dist/BackupsView-DVkKzaPR.js.map +1 -0
  7. package/dist/{BranchesView-CM3B-ER3.js → BranchesView-Df8hQ8wE.js} +85 -24
  8. package/dist/BranchesView-Df8hQ8wE.js.map +1 -0
  9. package/dist/{CronJobsView-BHtJZeJZ.js → CronJobsView-CLOGjb-L.js} +32 -27
  10. package/dist/CronJobsView-CLOGjb-L.js.map +1 -0
  11. package/dist/{RLSEditor-CBX4uD66.js → RLSEditor-Sa1D34ny.js} +724 -615
  12. package/dist/RLSEditor-Sa1D34ny.js.map +1 -0
  13. package/dist/{SQLEditor-ByecwX8B.js → SQLEditor-BfNA8xM3.js} +9 -4
  14. package/dist/{SQLEditor-ByecwX8B.js.map → SQLEditor-BfNA8xM3.js.map} +1 -1
  15. package/dist/{SchemaVisualizer-D6tMZgeC.js → SchemaVisualizer-DTDMYL6o.js} +84 -110
  16. package/dist/SchemaVisualizer-DTDMYL6o.js.map +1 -0
  17. package/dist/{StorageView-BGDeC5GI.js → StorageView-Ed9Q_VlB.js} +5 -32
  18. package/dist/StorageView-Ed9Q_VlB.js.map +1 -0
  19. package/dist/components/RLSEditor/saveRules.d.ts +38 -0
  20. package/dist/components/RebaseStudio.d.ts +1 -1
  21. package/dist/components/SchemaVisualizer/schema-visualizer.utils.d.ts +5 -2
  22. package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +0 -4
  23. package/dist/components/load-failure-view.d.ts +17 -0
  24. package/dist/components/load-failure.d.ts +47 -0
  25. package/dist/index.es.js +56 -36
  26. package/dist/index.es.js.map +1 -1
  27. package/dist/load-failure-C6OMJSGC.js +31 -0
  28. package/dist/load-failure-C6OMJSGC.js.map +1 -0
  29. package/dist/load-failure-view-CBTB_h-h.js +48 -0
  30. package/dist/load-failure-view-CBTB_h-h.js.map +1 -0
  31. package/package.json +36 -21
  32. package/dist/ApiKeysView-DiCurTEU.js.map +0 -1
  33. package/dist/BackupsView-DNS6LdVg.js.map +0 -1
  34. package/dist/BranchesView-CM3B-ER3.js.map +0 -1
  35. package/dist/CronJobsView-BHtJZeJZ.js.map +0 -1
  36. package/dist/RLSEditor-CBX4uD66.js.map +0 -1
  37. package/dist/SchemaVisualizer-D6tMZgeC.js.map +0 -1
  38. package/dist/StorageView-BGDeC5GI.js.map +0 -1
  39. package/dist/components/StorageView/storage-failure.d.ts +0 -34
  40. package/src/components/ApiExplorer/ApiExplorer.tsx +0 -299
  41. package/src/components/ApiExplorer/EndpointDetail.tsx +0 -279
  42. package/src/components/ApiExplorer/TryItPanel.tsx +0 -499
  43. package/src/components/ApiExplorer/parseSpec.ts +0 -104
  44. package/src/components/ApiExplorer/types.ts +0 -84
  45. package/src/components/ApiKeys/ApiKeysView.tsx +0 -479
  46. package/src/components/ApiKeys/CreateApiKeyDialog.tsx +0 -711
  47. package/src/components/ApiKeys/permissions.ts +0 -145
  48. package/src/components/AuthSimulationSelector.tsx +0 -72
  49. package/src/components/Backups/BackupsView.tsx +0 -185
  50. package/src/components/Branches/BranchesView.tsx +0 -386
  51. package/src/components/CronJobs/CronJobsView.tsx +0 -413
  52. package/src/components/JSEditor/JSEditor.tsx +0 -1100
  53. package/src/components/JSEditor/JSEditorSidebar.tsx +0 -350
  54. package/src/components/JSEditor/JSMonacoEditor.tsx +0 -410
  55. package/src/components/LogsExplorer/LogsExplorer.tsx +0 -273
  56. package/src/components/LogsExplorer/useLogTail.ts +0 -356
  57. package/src/components/RLSEditor/PolicyEditor.tsx +0 -399
  58. package/src/components/RLSEditor/RLSEditor.tsx +0 -1128
  59. package/src/components/RLSEditor/index.ts +0 -1
  60. package/src/components/RLSEditor/policy-presets.ts +0 -176
  61. package/src/components/RLSEditor/saveSecurityRules.ts +0 -48
  62. package/src/components/RebaseStudio.tsx +0 -174
  63. package/src/components/SQLEditor/ExplainVisualizer.tsx +0 -128
  64. package/src/components/SQLEditor/MonacoEditor.tsx +0 -203
  65. package/src/components/SQLEditor/SQLEditor.tsx +0 -1518
  66. package/src/components/SQLEditor/SQLEditorSidebar.tsx +0 -173
  67. package/src/components/SQLEditor/SchemaBrowser.tsx +0 -168
  68. package/src/components/SQLEditor/sql_editor_types.ts +0 -18
  69. package/src/components/SchemaVisualizer/RelationEdge.tsx +0 -102
  70. package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +0 -710
  71. package/src/components/SchemaVisualizer/TableNode.tsx +0 -257
  72. package/src/components/SchemaVisualizer/schema-visualizer.utils.ts +0 -140
  73. package/src/components/SchemaVisualizer/useLiveRls.ts +0 -86
  74. package/src/components/SchemaVisualizer/useSchemaGraph.ts +0 -426
  75. package/src/components/StorageView/StorageView.tsx +0 -1588
  76. package/src/components/StorageView/storage-failure.ts +0 -63
  77. package/src/components/StudioHomePage.tsx +0 -430
  78. package/src/index.ts +0 -31
  79. package/src/utils/parseSpec.test.ts +0 -309
  80. package/src/utils/sql_utils.test.ts +0 -265
  81. package/src/utils/sql_utils.ts +0 -291
@@ -1,173 +0,0 @@
1
-
2
- import React, { useState } from "react";
3
- import { cls, defaultBorderMixin, IconButton, iconSize, Tab, Tabs, Trash2Icon, Typography } from "@rebasepro/ui";
4
- import { useTranslation } from "@rebasepro/app";
5
- import { SchemaBrowser } from "./SchemaBrowser";
6
- import type { TableInfo } from "./sql_editor_types";
7
-
8
- export interface Snippet {
9
- id: string;
10
- name: string;
11
- sql: string;
12
- createdAt: number;
13
- isFavorite?: boolean;
14
- }
15
-
16
- interface SQLEditorSidebarProps {
17
- onSelectSnippet: (sql: string) => void;
18
- onTableClick: (tableName: string) => void;
19
- snippets: Snippet[];
20
- history: string[];
21
- onDeleteSnippet: (id: string) => void;
22
- schemas: Record<string, TableInfo[]>;
23
- isSchemaLoading: boolean;
24
- schemaError: string | null;
25
- onRetrySchema: () => void;
26
- }
27
-
28
- export const SQLEditorSidebar = ({
29
- onSelectSnippet,
30
- onTableClick,
31
- snippets,
32
- history,
33
- onDeleteSnippet,
34
- schemas,
35
- isSchemaLoading,
36
- schemaError,
37
- onRetrySchema
38
- }: SQLEditorSidebarProps) => {
39
- const [activeTab, setActiveTab] = useState<"schema" | "snippets" | "history">("schema");
40
- const { t } = useTranslation();
41
-
42
- return (
43
- <div className={cls("flex flex-col h-full w-full bg-white dark:bg-surface-950 border-r", defaultBorderMixin)}>
44
- <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as "schema" | "snippets" | "history")} variant="boxy" className="border-b border-surface-200 dark:border-surface-950">
45
- <Tab value="schema">{t("studio_sql_sidebar_schema")}</Tab>
46
- <Tab value="snippets">{t("studio_sql_sidebar_snippets")}</Tab>
47
- <Tab value="history">{t("studio_sql_sidebar_history")}</Tab>
48
- </Tabs>
49
-
50
- <div className="flex-grow overflow-hidden relative">
51
- {activeTab === "schema" && (
52
- <SchemaBrowser
53
- onTableClick={onTableClick}
54
- schemas={schemas}
55
- isSchemaLoading={isSchemaLoading}
56
- schemaError={schemaError}
57
- onRetrySchema={onRetrySchema}
58
- />
59
- )}
60
-
61
- {activeTab === "snippets" && (() => {
62
- const favorites = snippets.filter(s => s.isFavorite);
63
- const others = snippets.filter(s => !s.isFavorite);
64
-
65
- return (
66
- <div className="flex flex-col h-full">
67
- <div className={cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin)}>
68
- <Typography variant="caption" className="font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark">{t("studio_sql_sidebar_snippets")}</Typography>
69
- </div>
70
- <div className="flex-grow overflow-y-auto p-2 space-y-2 no-scrollbar">
71
- {snippets.length === 0 ? (
72
- <div className="p-4 text-center">
73
- <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark">{t("studio_sql_sidebar_no_snippets")}</Typography>
74
- </div>
75
- ) : (
76
- <>
77
- {favorites.length > 0 && (
78
- <div className="mb-4">
79
- <Typography variant="caption" className="text-[10px] font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark mb-2 px-1 flex items-center">
80
- <svg className="w-3 h-3 mr-1 text-red-500" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clipRule="evenodd"/></svg>
81
- Favorites
82
- </Typography>
83
- <div className="space-y-2">
84
- {favorites.map(snippet => (
85
- <div
86
- key={snippet.id}
87
- className={cls("group p-2 rounded border hover:border-surface-400 dark:hover:border-surface-600 bg-white dark:bg-surface-900 transition-all cursor-pointer relative", defaultBorderMixin)}
88
- onClick={() => onSelectSnippet(snippet.sql)}
89
- >
90
- <Typography variant="body2" className="text-text-primary dark:text-text-primary-dark font-medium text-[13px] truncate pr-6">{snippet.name}</Typography>
91
- <Typography variant="caption" className="text-text-secondary dark:text-text-secondary-dark text-[10px] block mt-1 truncate">{snippet.sql}</Typography>
92
- <IconButton
93
- size="smallest"
94
- className="absolute right-1 top-1 opacity-0 group-hover:opacity-100 text-text-disabled hover:text-red-500 transition-opacity"
95
- onClick={(e) => {
96
- e.stopPropagation();
97
- onDeleteSnippet(snippet.id);
98
- }}
99
- >
100
- <Trash2Icon size={iconSize.smallest}/>
101
- </IconButton>
102
- </div>
103
- ))}
104
- </div>
105
- </div>
106
- )}
107
- {others.length > 0 && (
108
- <div>
109
- {favorites.length > 0 && (
110
- <Typography variant="caption" className="text-[10px] font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark mb-2 px-1 mt-4">
111
- Others
112
- </Typography>
113
- )}
114
- <div className="space-y-2">
115
- {others.map(snippet => (
116
- <div
117
- key={snippet.id}
118
- className={cls("group p-2 rounded border hover:border-surface-400 dark:hover:border-surface-600 bg-white dark:bg-surface-900 transition-all cursor-pointer relative", defaultBorderMixin)}
119
- onClick={() => onSelectSnippet(snippet.sql)}
120
- >
121
- <Typography variant="body2" className="text-text-primary dark:text-text-primary-dark font-medium text-[13px] truncate pr-6">{snippet.name}</Typography>
122
- <Typography variant="caption" className="text-text-secondary dark:text-text-secondary-dark text-[10px] block mt-1 truncate">{snippet.sql}</Typography>
123
- <IconButton
124
- size="smallest"
125
- className="absolute right-1 top-1 opacity-0 group-hover:opacity-100 text-text-disabled hover:text-red-500 transition-opacity"
126
- onClick={(e) => {
127
- e.stopPropagation();
128
- onDeleteSnippet(snippet.id);
129
- }}
130
- >
131
- <Trash2Icon size={iconSize.smallest}/>
132
- </IconButton>
133
- </div>
134
- ))}
135
- </div>
136
- </div>
137
- )}
138
- </>
139
- )}
140
- </div>
141
- </div>
142
- );
143
- })()}
144
-
145
- {activeTab === "history" && (
146
- <div className="flex flex-col h-full">
147
- <div className={cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin)}>
148
- <Typography variant="caption" className="font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark">{t("studio_sql_sidebar_history")}</Typography>
149
- </div>
150
- <div className="flex-grow overflow-y-auto p-1 space-y-1 no-scrollbar">
151
- {history.length === 0 ? (
152
- <div className="p-4 text-center">
153
- <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark">{t("studio_sql_sidebar_no_history")}</Typography>
154
- </div>
155
- ) : (
156
- [...history].reverse().map((sql, i) => (
157
- <div
158
- key={i}
159
- className="p-2 py-1.5 rounded hover:bg-surface-100 dark:hover:bg-surface-950 cursor-pointer group transition-colors flex items-start"
160
- onClick={() => onSelectSnippet(sql)}
161
- >
162
- <svg className="w-3 h-3 mt-1 mr-2 text-text-disabled dark:text-text-disabled-dark group-hover:text-primary transition-colors flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
163
- <Typography variant="caption" className="text-text-secondary dark:text-text-secondary-dark group-hover:text-text-primary dark:group-hover:text-text-primary-dark text-[11px] line-clamp-2 leading-tight flex-grow">{sql}</Typography>
164
- </div>
165
- ))
166
- )}
167
- </div>
168
- </div>
169
- )}
170
- </div>
171
- </div>
172
- );
173
- };
@@ -1,168 +0,0 @@
1
-
2
- import React, { useState } from "react";
3
- import {
4
- Button,
5
- CircularProgress,
6
- cls,
7
- CopyIcon,
8
- defaultBorderMixin,
9
- IconButton,
10
- iconSize,
11
- Menu,
12
- MenuItem,
13
- MoreVerticalIcon,
14
- RefreshCwIcon,
15
- Typography
16
- } from "@rebasepro/ui";
17
- import type { TableInfo } from "./sql_editor_types";
18
- import { ErrorView, useTranslation } from "@rebasepro/app";
19
-
20
- export const SchemaBrowser = ({
21
- onTableClick,
22
- schemas,
23
- isSchemaLoading,
24
- schemaError,
25
- onRetrySchema
26
- }: {
27
- onTableClick?: (tableName: string) => void,
28
- schemas: Record<string, TableInfo[]>,
29
- isSchemaLoading: boolean,
30
- schemaError: string | null,
31
- onRetrySchema: () => void
32
- }) => {
33
- const [expandedSchemas, setExpandedSchemas] = useState<Record<string, boolean>>({ public: true });
34
- const [expandedTables, setExpandedTables] = useState<Record<string, boolean>>({});
35
- const { t } = useTranslation();
36
-
37
- if (isSchemaLoading) return <div className="p-4 flex justify-center"><CircularProgress size="small"/></div>;
38
- if (schemaError) return (
39
- <div className="p-2">
40
- <ErrorView error={schemaError} onRetry={onRetrySchema}/>
41
- </div>
42
- );
43
-
44
- return (
45
- <div className="flex flex-col h-full overflow-hidden">
46
- <div className={cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin)}>
47
- <Typography variant="caption" className="font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark">{t("studio_schema_tables")}</Typography>
48
- <IconButton size="small" onClick={onRetrySchema} title="Refresh schema">
49
- <RefreshCwIcon size={iconSize.smallest}/>
50
- </IconButton>
51
- </div>
52
-
53
- <div className="flex-grow overflow-y-auto no-scrollbar p-1">
54
- {Object.keys(schemas).length === 0 ? (
55
- <div className="p-4 text-center">
56
- <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark italic">{t("studio_schema_no_tables")}</Typography>
57
- </div>
58
- ) : Object.entries(schemas).map(([schemaName, tables]) => (
59
- <div key={schemaName} className="mb-2">
60
- <div
61
- className="flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors"
62
- onClick={() => setExpandedSchemas(prev => ({ ...prev,
63
- [schemaName]: !prev[schemaName] }))}
64
- >
65
- <svg className={cls("w-3 h-3 mr-1 transition-transform", expandedSchemas[schemaName] ? "rotate-90" : "")} fill="currentColor" viewBox="0 0 20 20"><path d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"/></svg>
66
- <Typography variant="body2" className="text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow">{schemaName}</Typography>
67
- </div>
68
-
69
- {expandedSchemas[schemaName] && (
70
- <div className="ml-3 mt-1 space-y-1">
71
- {tables.map(table => (
72
- <div key={table.tableName}>
73
- <div
74
- className="flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors group relative"
75
- onClick={() => setExpandedTables(prev => ({ ...prev,
76
- [`${schemaName}.${table.tableName}`]: !prev[`${schemaName}.${table.tableName}`] }))}
77
- >
78
- <svg className={cls("w-3 h-3 mr-1 transition-transform shrink-0", expandedTables[`${schemaName}.${table.tableName}`] ? "rotate-90" : "")} fill="currentColor" viewBox="0 0 20 20"><path d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"/></svg>
79
- <svg className="w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
80
- <Typography variant="body2" className="text-text-secondary dark:text-text-secondary-dark text-xs truncate flex-1 min-w-0">{table.tableName}</Typography>
81
-
82
- <div className="flex opacity-0 group-hover:opacity-100 focus-within:opacity-100 absolute right-1 items-center bg-surface-100 dark:bg-surface-800 px-1 gap-1 rounded transition-opacity">
83
- <IconButton
84
- size="small"
85
- onClick={(e) => {
86
- e.stopPropagation();
87
- navigator.clipboard.writeText(table.tableName);
88
- }}
89
- title="Copy table name"
90
- >
91
- <CopyIcon size={iconSize.small}/>
92
- </IconButton>
93
-
94
- <Menu
95
- trigger={
96
- <IconButton
97
- size={"small"}
98
- onClick={(e) => e.stopPropagation()}
99
- title="Generate SQL templates"
100
- >
101
- <MoreVerticalIcon size={iconSize.small}/>
102
- </IconButton>
103
- }
104
- >
105
- <MenuItem dense className="text-xs" onClick={(e: React.MouseEvent) => {
106
- e.stopPropagation();
107
- onTableClick?.(`SELECT * FROM ${schemaName !== "public" ? `${schemaName}.` : ""}${table.tableName} LIMIT 100;`);
108
- }}>SELECT (Top 100)</MenuItem>
109
- <MenuItem dense className="text-xs" onClick={(e: React.MouseEvent) => {
110
- e.stopPropagation();
111
- onTableClick?.(`SELECT \n ${table.columns.map(c => c.name).join(",\n ")}\nFROM ${schemaName !== "public" ? `${schemaName}.` : ""}${table.tableName};`);
112
- }}>SELECT (All columns)</MenuItem>
113
- <MenuItem dense className="text-xs" onClick={(e: React.MouseEvent) => {
114
- e.stopPropagation();
115
- onTableClick?.(`INSERT INTO ${schemaName !== "public" ? `${schemaName}.` : ""}${table.tableName} (\n ${table.columns.map(c => c.name).join(",\n ")}\n) VALUES (\n ${table.columns.map(() => "?").join(",\n ")}\n);`);
116
- }}>INSERT statement</MenuItem>
117
- <MenuItem dense className="text-xs" onClick={(e: React.MouseEvent) => {
118
- e.stopPropagation();
119
- onTableClick?.(`UPDATE ${schemaName !== "public" ? `${schemaName}.` : ""}${table.tableName} \nSET \n ${table.columns.map(c => `${c.name} = ?`).join(",\n ")}\nWHERE id = ?;`);
120
- }}>UPDATE statement</MenuItem>
121
- </Menu>
122
-
123
- <Button
124
- variant="text"
125
- size="small"
126
- className="text-[10px] text-primary uppercase min-h-0 py-0.5 px-1 font-medium ml-1 flex-shrink-0 leading-none h-[22px]"
127
- onClick={(e) => {
128
- e.stopPropagation();
129
- onTableClick?.(`SELECT * FROM ${schemaName !== "public" ? `${schemaName}.` : ""}${table.tableName} LIMIT 100;`);
130
- }}
131
- >
132
- SELECT
133
- </Button>
134
- </div>
135
- </div>
136
-
137
- {expandedTables[`${schemaName}.${table.tableName}`] && (
138
- <div className={cls("ml-5 mt-1 space-y-0.5 border-l", defaultBorderMixin)}>
139
- {table.columns.map(col => (
140
- <div key={col.name} className="flex items-center p-1 group pl-2 hover:bg-surface-50 dark:hover:bg-surface-900 rounded-r relative min-h-[28px]">
141
- <svg className="w-3 h-3 mr-1.5 text-text-disabled dark:text-text-disabled-dark shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 4.5v15m6-15v15m-10.5-1.5h15c.621 0 1.125-.504 1.125-1.125V5.625c0-.621-.504-1.125-1.125-1.125h-15c-.621 0-1.125.504-1.125 1.125v12.75c0 .621.504 1.125 1.125 1.125Z"/></svg>
142
- <Typography variant="caption" className="text-text-primary dark:text-text-primary-dark text-[11px] truncate flex-grow mr-2">{col.name}</Typography>
143
- <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark text-[9px] truncate mr-1 uppercase shrink-0" title={col.dataType}>{col.dataType}</Typography>
144
- <IconButton
145
- size="smallest"
146
- className="opacity-0 group-hover:opacity-100 absolute right-1 bg-surface-50 dark:bg-surface-800 transition-colors pointer-events-auto"
147
- onClick={(e) => {
148
- e.stopPropagation();
149
- navigator.clipboard.writeText(col.name);
150
- }}
151
- title="Copy column name"
152
- >
153
- <CopyIcon size={iconSize.smallest}/>
154
- </IconButton>
155
- </div>
156
- ))}
157
- </div>
158
- )}
159
- </div>
160
- ))}
161
- </div>
162
- )}
163
- </div>
164
- ))}
165
- </div>
166
- </div>
167
- );
168
- };
@@ -1,18 +0,0 @@
1
- /**
2
- * Shapes describing the introspected schema the SQL editor works against.
3
- * They live here rather than in SQLEditor.tsx because the sidebar, the schema
4
- * browser and utils/sql_utils all need them, and importing them from the
5
- * component would close a cycle back through it.
6
- */
7
-
8
- export interface SQLEditorColumnInfo {
9
- name: string;
10
- dataType: string;
11
- isPrimaryKey: boolean;
12
- }
13
-
14
- export interface TableInfo {
15
- schemaName: string;
16
- tableName: string;
17
- columns: SQLEditorColumnInfo[];
18
- }
@@ -1,102 +0,0 @@
1
- import React, { memo } from "react";
2
- import { BaseEdge, getSmoothStepPath, EdgeLabelRenderer } from "@xyflow/react";
3
- import type { EdgeProps } from "@xyflow/react";
4
- import { cls } from "@rebasepro/ui";
5
- import type { RelationEdgeData } from "./schema-visualizer.utils";
6
-
7
- /**
8
- * Custom React Flow edge that renders relations with style variations
9
- * based on cardinality and direction.
10
- *
11
- * - Owning one-to-one: solid, primary color
12
- * - Many-to-many (junction): solid, violet
13
- * - Inverse: dashed, muted
14
- * - Join path: dotted, muted
15
- */
16
- const RelationEdgeInner = ({
17
- id,
18
- sourceX,
19
- sourceY,
20
- targetX,
21
- targetY,
22
- sourcePosition,
23
- targetPosition,
24
- data,
25
- selected
26
- }: EdgeProps) => {
27
- const edgeData = data as RelationEdgeData | undefined;
28
-
29
- const [edgePath, labelX, labelY] = getSmoothStepPath({
30
- sourceX,
31
- sourceY,
32
- sourcePosition,
33
- targetX,
34
- targetY,
35
- targetPosition,
36
- borderRadius: 8
37
- });
38
-
39
- const isInverse = edgeData?.direction === "inverse";
40
- const isJoinPath = edgeData?.hasJoinPath;
41
- const isJunction = edgeData?.hasJunction;
42
-
43
- // Determine stroke style
44
- let strokeColor = "var(--rf-edge-stroke, #94a3b8)";
45
- let strokeDasharray: string | undefined;
46
- let strokeWidth = 1.5;
47
-
48
- if (selected) {
49
- strokeColor = "var(--rf-edge-stroke-selected, #6366f1)";
50
- strokeWidth = 2.5;
51
- } else if (isJunction) {
52
- strokeColor = "#8b5cf6"; // violet
53
- } else if (isInverse) {
54
- strokeDasharray = "6 3";
55
- strokeColor = "#94a3b8";
56
- } else if (isJoinPath) {
57
- strokeDasharray = "3 3";
58
- strokeColor = "#94a3b8";
59
- } else {
60
- strokeColor = "#6366f1"; // primary/indigo
61
- }
62
-
63
- return (
64
- <>
65
- <BaseEdge
66
- id={id}
67
- path={edgePath}
68
- style={{
69
- stroke: strokeColor,
70
- strokeWidth,
71
- strokeDasharray
72
- }}
73
- />
74
- {edgeData?.label && (
75
- <EdgeLabelRenderer>
76
- <div
77
- style={{
78
- position: "absolute",
79
- transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
80
- pointerEvents: "all"
81
- }}
82
- className={cls(
83
- "px-1.5 py-0.5 rounded text-[9px] font-mono font-semibold leading-none",
84
- "bg-white dark:bg-surface-900 border",
85
- selected
86
- ? "border-primary text-primary"
87
- : isJunction
88
- ? "border-violet-200 dark:border-violet-800 text-violet-600 dark:text-violet-400"
89
- : isInverse || isJoinPath
90
- ? "border-surface-200 dark:border-surface-700 text-text-disabled dark:text-text-disabled-dark"
91
- : "border-primary/30 dark:border-primary/30 text-primary"
92
- )}
93
- >
94
- {edgeData.label}
95
- </div>
96
- </EdgeLabelRenderer>
97
- )}
98
- </>
99
- );
100
- };
101
-
102
- export const RelationEdge = memo(RelationEdgeInner);