@rebasepro/studio 0.23.0 → 0.24.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/{ApiExplorer-pYMZflL3.js → ApiExplorer-CCtczj3F.js} +15 -28
- package/dist/ApiExplorer-CCtczj3F.js.map +1 -0
- package/dist/ApiKeysView-B66TdzhH.js +1738 -0
- package/dist/ApiKeysView-B66TdzhH.js.map +1 -0
- package/dist/BackupsView-DmSMzAbT.js +368 -0
- package/dist/BackupsView-DmSMzAbT.js.map +1 -0
- package/dist/{BranchesView-D6Sz1a5f.js → BranchesView-DP1a87E-.js} +12 -8
- package/dist/BranchesView-DP1a87E-.js.map +1 -0
- package/dist/{CronJobsView-7hjgSYId.js → CronJobsView-TDErHTuK.js} +126 -60
- package/dist/CronJobsView-TDErHTuK.js.map +1 -0
- package/dist/{JSEditor-qiuwmcvk.js → JSEditor-B59pwg4c.js} +120 -74
- package/dist/JSEditor-B59pwg4c.js.map +1 -0
- package/dist/{LogsExplorer-Xq8K4oo6.js → LogsExplorer-BKM4yfP8.js} +48 -22
- package/dist/LogsExplorer-BKM4yfP8.js.map +1 -0
- package/dist/{RLSEditor-Djh43lBE.js → RLSEditor-BlLLV6xs.js} +237 -77
- package/dist/RLSEditor-BlLLV6xs.js.map +1 -0
- package/dist/{SQLEditor-TBw_K6CW.js → SQLEditor-1An7w0j6.js} +369 -294
- package/dist/SQLEditor-1An7w0j6.js.map +1 -0
- package/dist/{SchemaVisualizer-DtDzIUNy.js → SchemaVisualizer-DCLVfA5t.js} +20 -24
- package/dist/SchemaVisualizer-DCLVfA5t.js.map +1 -0
- package/dist/{StorageView-CMG1DOVX.js → StorageView-CbsHuCBf.js} +355 -225
- package/dist/StorageView-CbsHuCBf.js.map +1 -0
- package/dist/components/ApiExplorer/TryItPanel.d.ts +6 -1
- package/dist/components/ApiKeys/CreateApiKeyDialog.d.ts +21 -2
- package/dist/components/ApiKeys/scope-words.d.ts +37 -0
- package/dist/components/ApiKeys/scopes.d.ts +113 -0
- package/dist/components/AuthSimulationSelector.d.ts +5 -1
- package/dist/components/LogsExplorer/useLogTail.d.ts +9 -0
- package/dist/components/RLSEditor/policyListing.d.ts +44 -0
- package/dist/components/SchemaVisualizer/schema-visualizer.utils.d.ts +0 -1
- package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +6 -0
- package/dist/components/load-failure.d.ts +1 -1
- package/dist/components/useRunAsUsers.d.ts +25 -0
- package/dist/index.es.js +11 -11
- package/dist/{load-failure-C6OMJSGC.js → load-failure-Bd8lFIO7.js} +2 -2
- package/dist/load-failure-Bd8lFIO7.js.map +1 -0
- package/dist/useRunAsUsers-CLaAtXt6.js +122 -0
- package/dist/useRunAsUsers-CLaAtXt6.js.map +1 -0
- package/dist/utils/sql_utils.d.ts +90 -49
- package/package.json +9 -9
- package/dist/ApiExplorer-pYMZflL3.js.map +0 -1
- package/dist/ApiKeysView-qLSs47tw.js +0 -1239
- package/dist/ApiKeysView-qLSs47tw.js.map +0 -1
- package/dist/AuthSimulationSelector-BewmhMBw.js +0 -56
- package/dist/AuthSimulationSelector-BewmhMBw.js.map +0 -1
- package/dist/BackupsView-DYTAD6aW.js +0 -253
- package/dist/BackupsView-DYTAD6aW.js.map +0 -1
- package/dist/BranchesView-D6Sz1a5f.js.map +0 -1
- package/dist/CronJobsView-7hjgSYId.js.map +0 -1
- package/dist/JSEditor-qiuwmcvk.js.map +0 -1
- package/dist/LogsExplorer-Xq8K4oo6.js.map +0 -1
- package/dist/RLSEditor-Djh43lBE.js.map +0 -1
- package/dist/SQLEditor-TBw_K6CW.js.map +0 -1
- package/dist/SchemaVisualizer-DtDzIUNy.js.map +0 -1
- package/dist/StorageView-CMG1DOVX.js.map +0 -1
- package/dist/components/ApiKeys/permissions.d.ts +0 -66
- package/dist/load-failure-C6OMJSGC.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SQLEditor-1An7w0j6.js","names":[],"sources":["../src/components/SQLEditor/SchemaBrowser.tsx","../src/components/SQLEditor/SQLEditorSidebar.tsx","../src/utils/sql_utils.ts","../src/components/SQLEditor/ExplainVisualizer.tsx","../src/components/SQLEditor/SQLEditor.tsx"],"sourcesContent":["\nimport React, { useState } from \"react\";\nimport {\n Button,\n CircularProgress,\n cls,\n CopyIcon,\n defaultBorderMixin,\n IconButton,\n iconSize,\n Menu,\n MenuItem,\n MoreVerticalIcon,\n RefreshCwIcon,\n Typography\n} from \"@rebasepro/ui\";\nimport type { TableInfo } from \"./sql_editor_types\";\nimport { ErrorView, useTranslation } from \"@rebasepro/app\";\n\nexport const SchemaBrowser = ({\n onTableClick,\n schemas,\n isSchemaLoading,\n schemaError,\n onRetrySchema\n}: {\n onTableClick?: (tableName: string) => void,\n schemas: Record<string, TableInfo[]>,\n isSchemaLoading: boolean,\n schemaError: string | null,\n onRetrySchema: () => void\n}) => {\n const [expandedSchemas, setExpandedSchemas] = useState<Record<string, boolean>>({ public: true });\n const [expandedTables, setExpandedTables] = useState<Record<string, boolean>>({});\n const { t } = useTranslation();\n\n if (isSchemaLoading) return <div className=\"p-4 flex justify-center\"><CircularProgress size=\"small\"/></div>;\n if (schemaError) return (\n <div className=\"p-2\">\n <ErrorView error={schemaError} onRetry={onRetrySchema}/>\n </div>\n );\n\n return (\n <div className=\"flex flex-col h-full overflow-hidden\">\n <div className={cls(\"flex items-center justify-between px-3 py-2 border-b bg-surface-sheet min-h-[48px]\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\">{t(\"studio_schema_tables\")}</Typography>\n <IconButton size=\"small\" onClick={onRetrySchema} title=\"Refresh schema\">\n <RefreshCwIcon size={iconSize.smallest}/>\n </IconButton>\n </div>\n\n <div className=\"flex-grow overflow-y-auto no-scrollbar p-1\">\n {Object.keys(schemas).length === 0 ? (\n <div className=\"p-4 text-center\">\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark italic\">{t(\"studio_schema_no_tables\")}</Typography>\n </div>\n ) : Object.entries(schemas).map(([schemaName, tables]) => (\n <div key={schemaName} className=\"mb-2\">\n <div\n className=\"flex items-center p-1 cursor-pointer hover:bg-surface-hover rounded transition-colors\"\n onClick={() => setExpandedSchemas(prev => ({ ...prev,\n[schemaName]: !prev[schemaName] }))}\n >\n <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>\n <Typography variant=\"body2\" className=\"text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow\">{schemaName}</Typography>\n </div>\n\n {expandedSchemas[schemaName] && (\n <div className=\"ml-3 mt-1 space-y-1\">\n {tables.map(table => (\n <div key={table.tableName}>\n <div\n className=\"flex items-center p-1 cursor-pointer hover:bg-surface-hover rounded transition-colors group relative\"\n onClick={() => setExpandedTables(prev => ({ ...prev,\n[`${schemaName}.${table.tableName}`]: !prev[`${schemaName}.${table.tableName}`] }))}\n >\n <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>\n <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>\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark text-xs truncate flex-1 min-w-0\">{table.tableName}</Typography>\n\n <div className=\"flex opacity-0 group-hover:opacity-100 focus-within:opacity-100 absolute right-1 items-center bg-surface-raised px-1 gap-1 rounded transition-opacity\">\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n navigator.clipboard.writeText(table.tableName);\n }}\n title=\"Copy table name\"\n >\n <CopyIcon size={iconSize.small}/>\n </IconButton>\n\n <Menu\n trigger={\n <IconButton\n size={\"small\"}\n onClick={(e) => e.stopPropagation()}\n title=\"Generate SQL templates\"\n >\n <MoreVerticalIcon size={iconSize.small}/>\n </IconButton>\n }\n >\n <MenuItem dense className=\"text-xs\" onClick={(e: React.MouseEvent) => {\n e.stopPropagation();\n onTableClick?.(`SELECT * FROM ${schemaName !== \"public\" ? `${schemaName}.` : \"\"}${table.tableName} LIMIT 100;`);\n }}>SELECT (Top 100)</MenuItem>\n <MenuItem dense className=\"text-xs\" onClick={(e: React.MouseEvent) => {\n e.stopPropagation();\n onTableClick?.(`SELECT \\n ${table.columns.map(c => c.name).join(\",\\n \")}\\nFROM ${schemaName !== \"public\" ? `${schemaName}.` : \"\"}${table.tableName};`);\n }}>SELECT (All columns)</MenuItem>\n <MenuItem dense className=\"text-xs\" onClick={(e: React.MouseEvent) => {\n e.stopPropagation();\n 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);`);\n }}>INSERT statement</MenuItem>\n <MenuItem dense className=\"text-xs\" onClick={(e: React.MouseEvent) => {\n e.stopPropagation();\n onTableClick?.(`UPDATE ${schemaName !== \"public\" ? `${schemaName}.` : \"\"}${table.tableName} \\nSET \\n ${table.columns.map(c => `${c.name} = ?`).join(\",\\n \")}\\nWHERE id = ?;`);\n }}>UPDATE statement</MenuItem>\n </Menu>\n\n <Button\n variant=\"text\"\n size=\"small\"\n 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]\"\n onClick={(e) => {\n e.stopPropagation();\n onTableClick?.(`SELECT * FROM ${schemaName !== \"public\" ? `${schemaName}.` : \"\"}${table.tableName} LIMIT 100;`);\n }}\n >\n SELECT\n </Button>\n </div>\n </div>\n\n {expandedTables[`${schemaName}.${table.tableName}`] && (\n <div className={cls(\"ml-5 mt-1 space-y-0.5 border-l\", defaultBorderMixin)}>\n {table.columns.map(col => (\n <div key={col.name} className=\"flex items-center p-1 group pl-2 hover:bg-surface-hover rounded-r relative min-h-[28px]\">\n <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>\n <Typography variant=\"caption\" className=\"text-text-primary dark:text-text-primary-dark text-[11px] truncate flex-grow mr-2\">{col.name}</Typography>\n <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>\n <IconButton\n size=\"smallest\"\n className=\"opacity-0 group-hover:opacity-100 absolute right-1 bg-surface-sheet transition-colors pointer-events-auto\"\n onClick={(e) => {\n e.stopPropagation();\n navigator.clipboard.writeText(col.name);\n }}\n title=\"Copy column name\"\n >\n <CopyIcon size={iconSize.smallest}/>\n </IconButton>\n </div>\n ))}\n </div>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n );\n};\n","\nimport React, { useState } from \"react\";\nimport { cls, defaultBorderMixin, IconButton, iconSize, Tab, Tabs, Trash2Icon, Typography } from \"@rebasepro/ui\";\nimport { useTranslation } from \"@rebasepro/app\";\nimport { SchemaBrowser } from \"./SchemaBrowser\";\nimport type { TableInfo } from \"./sql_editor_types\";\n\nexport interface Snippet {\n id: string;\n name: string;\n sql: string;\n createdAt: number;\n isFavorite?: boolean;\n}\n\ninterface SQLEditorSidebarProps {\n onSelectSnippet: (sql: string) => void;\n onTableClick: (tableName: string) => void;\n snippets: Snippet[];\n history: string[];\n onDeleteSnippet: (id: string) => void;\n schemas: Record<string, TableInfo[]>;\n isSchemaLoading: boolean;\n schemaError: string | null;\n onRetrySchema: () => void;\n}\n\nexport const SQLEditorSidebar = ({\n onSelectSnippet,\n onTableClick,\n snippets,\n history,\n onDeleteSnippet,\n schemas,\n isSchemaLoading,\n schemaError,\n onRetrySchema\n}: SQLEditorSidebarProps) => {\n const [activeTab, setActiveTab] = useState<\"schema\" | \"snippets\" | \"history\">(\"schema\");\n const { t } = useTranslation();\n\n return (\n <div className={cls(\"flex flex-col h-full w-full bg-surface-card border-r\", defaultBorderMixin)}>\n <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as \"schema\" | \"snippets\" | \"history\")} variant=\"boxy\" className=\"border-b border-hairline\">\n <Tab value=\"schema\">{t(\"studio_sql_sidebar_schema\")}</Tab>\n <Tab value=\"snippets\">{t(\"studio_sql_sidebar_snippets\")}</Tab>\n <Tab value=\"history\">{t(\"studio_sql_sidebar_history\")}</Tab>\n </Tabs>\n\n <div className=\"flex-grow overflow-hidden relative\">\n {activeTab === \"schema\" && (\n <SchemaBrowser\n onTableClick={onTableClick}\n schemas={schemas}\n isSchemaLoading={isSchemaLoading}\n schemaError={schemaError}\n onRetrySchema={onRetrySchema}\n />\n )}\n\n {activeTab === \"snippets\" && (() => {\n const favorites = snippets.filter(s => s.isFavorite);\n const others = snippets.filter(s => !s.isFavorite);\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className={cls(\"flex items-center justify-between px-3 py-2 border-b bg-surface-sheet min-h-[48px]\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\">{t(\"studio_sql_sidebar_snippets\")}</Typography>\n </div>\n <div className=\"flex-grow overflow-y-auto p-2 space-y-2 no-scrollbar\">\n {snippets.length === 0 ? (\n <div className=\"p-4 text-center\">\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">{t(\"studio_sql_sidebar_no_snippets\")}</Typography>\n </div>\n ) : (\n <>\n {favorites.length > 0 && (\n <div className=\"mb-4\">\n <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\">\n <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>\n Favorites\n </Typography>\n <div className=\"space-y-2\">\n {favorites.map(snippet => (\n <div\n key={snippet.id}\n className={cls(\"group p-2 rounded border hover:border-hairline-strong bg-surface-card transition-all cursor-pointer relative\", defaultBorderMixin)}\n onClick={() => onSelectSnippet(snippet.sql)}\n >\n <Typography variant=\"body2\" className=\"text-text-primary dark:text-text-primary-dark font-medium text-[13px] truncate pr-6\">{snippet.name}</Typography>\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark text-[10px] block mt-1 truncate\">{snippet.sql}</Typography>\n <IconButton\n size=\"smallest\"\n className=\"absolute right-1 top-1 opacity-0 group-hover:opacity-100 text-text-disabled hover:text-red-500 transition-opacity\"\n onClick={(e) => {\n e.stopPropagation();\n onDeleteSnippet(snippet.id);\n }}\n >\n <Trash2Icon size={iconSize.smallest}/>\n </IconButton>\n </div>\n ))}\n </div>\n </div>\n )}\n {others.length > 0 && (\n <div>\n {favorites.length > 0 && (\n <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\">\n Others\n </Typography>\n )}\n <div className=\"space-y-2\">\n {others.map(snippet => (\n <div\n key={snippet.id}\n className={cls(\"group p-2 rounded border hover:border-hairline-strong bg-surface-card transition-all cursor-pointer relative\", defaultBorderMixin)}\n onClick={() => onSelectSnippet(snippet.sql)}\n >\n <Typography variant=\"body2\" className=\"text-text-primary dark:text-text-primary-dark font-medium text-[13px] truncate pr-6\">{snippet.name}</Typography>\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark text-[10px] block mt-1 truncate\">{snippet.sql}</Typography>\n <IconButton\n size=\"smallest\"\n className=\"absolute right-1 top-1 opacity-0 group-hover:opacity-100 text-text-disabled hover:text-red-500 transition-opacity\"\n onClick={(e) => {\n e.stopPropagation();\n onDeleteSnippet(snippet.id);\n }}\n >\n <Trash2Icon size={iconSize.smallest}/>\n </IconButton>\n </div>\n ))}\n </div>\n </div>\n )}\n </>\n )}\n </div>\n </div>\n );\n })()}\n\n {activeTab === \"history\" && (\n <div className=\"flex flex-col h-full\">\n <div className={cls(\"flex items-center justify-between px-3 py-2 border-b bg-surface-sheet min-h-[48px]\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\">{t(\"studio_sql_sidebar_history\")}</Typography>\n </div>\n <div className=\"flex-grow overflow-y-auto p-1 space-y-1 no-scrollbar\">\n {history.length === 0 ? (\n <div className=\"p-4 text-center\">\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">{t(\"studio_sql_sidebar_no_history\")}</Typography>\n </div>\n ) : (\n [...history].reverse().map((sql, i) => (\n <div\n key={i}\n className=\"p-2 py-1.5 rounded hover:bg-surface-hover cursor-pointer group transition-colors flex items-start\"\n onClick={() => onSelectSnippet(sql)}\n >\n <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>\n <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>\n </div>\n ))\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n );\n};\n","import { astVisitor, parse, type Statement } from \"pgsql-ast-parser\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\nimport type { AdminCollection } from \"@rebasepro/cms-types\";\nimport type { SqlScriptResult } from \"@rebasepro/types\";\n\n/** The statements in `sqlText`, or `null` when the parser cannot read it. */\nfunction parseStatements(sqlText: string): Statement[] | null {\n try {\n return parse(sqlText);\n } catch {\n return null;\n }\n}\n\n/**\n * The `EXPLAIN` for one statement, or `null` when `sqlText` is not exactly one.\n *\n * Never `ANALYZE`: that executes the statement to time it, so explaining a\n * `DELETE` deleted the rows — without the confirmation \"Run\" asks for.\n *\n * And never more than one statement. Sent as one simple query, `EXPLAIN (…)\n * SELECT 1; DELETE …` explains the SELECT and *runs* the DELETE. Text the\n * parser cannot read is still explained, as long as no `;` could be hiding a\n * second statement in it.\n */\nexport function buildExplainSql(sqlText: string): string | null {\n const statement = sqlText.trim().replace(/;+$/, \"\").trim();\n if (!statement) return null;\n const statements = parseStatements(statement);\n const single = statements ? statements.length === 1 : !statement.includes(\";\");\n return single ? `EXPLAIN (FORMAT JSON) ${statement}` : null;\n}\n\n/**\n * Whether the console's automatic `LIMIT` may be appended to `sqlText`: one\n * top-level SELECT that has no limit of its own.\n *\n * Decided on the parsed statement, not the text. A search for the word SELECT\n * also found it inside `INSERT INTO … SELECT` and `CREATE TABLE … AS SELECT`,\n * which then copied a thousand rows and reported success, and at the start of\n * a script whose last statement the appended `LIMIT` turned into a syntax\n * error. Text the parser cannot read is left as written.\n */\nexport function acceptsAutoLimit(sqlText: string): boolean {\n const statements = parseStatements(sqlText);\n if (!statements || statements.length !== 1) return false;\n const [statement] = statements;\n return statement.type === \"select\" && !statement.limit;\n}\n\n/** A destructive command named anywhere in a text, comments included. */\nconst DESTRUCTIVE_COMMAND = /\\b(DELETE|UPDATE|DROP|TRUNCATE)\\b/i;\n\nfunction isDestructiveStatement(statement: Statement): boolean {\n switch (statement.type) {\n case \"delete\":\n case \"update\":\n return !statement.where;\n case \"truncate table\":\n case \"drop table\":\n case \"drop sequence\":\n case \"drop index\":\n case \"drop type\":\n case \"drop trigger\":\n case \"drop function\":\n return true;\n case \"alter table\":\n return statement.changes.some(change => change.type === \"drop column\" || change.type === \"drop constraint\");\n case \"with\":\n return statement.bind.some(binding => isDestructiveStatement(binding.statement)) ||\n isDestructiveStatement(statement.in);\n case \"with recursive\":\n return isDestructiveStatement(statement.in);\n case \"do\":\n return DESTRUCTIVE_COMMAND.test(statement.code);\n default:\n return false;\n }\n}\n\n/**\n * Whether \"Run\" asks before running `sqlText`: a statement in it deletes or\n * updates without a WHERE of its own, drops or truncates.\n *\n * Decided per parsed statement, not on the text. A search of the text for the\n * word WHERE found it in another statement or in a comment, so `DELETE FROM\n * posts; SELECT * FROM posts WHERE id = 1` deleted every post without asking.\n * Text the parser cannot read is confirmed whenever it names a destructive\n * command.\n */\nexport function needsDestructiveConfirmation(sqlText: string): boolean {\n const statements = parseStatements(sqlText);\n if (!statements) return DESTRUCTIVE_COMMAND.test(sqlText);\n return statements.some(isDestructiveStatement);\n}\n\n/**\n * A table as SQL: `\"schema\".\"table\"` when a schema is given, a bare `\"table\"`\n * otherwise.\n */\nexport function quoteTableName(tableName: string, schemaName?: string): string {\n return schemaName ? `${quoteIdentifier(schemaName)}.${quoteIdentifier(tableName)}` : quoteIdentifier(tableName);\n}\n\n/** An identifier as SQL, whatever it holds: double-quoted, with `\"` doubled. */\nexport function quoteIdentifier(identifier: string): string {\n return `\"${identifier.replace(/\"/g, \"\\\"\\\"\")}\"`;\n}\n\n/** What the console knows about a result: its columns, and the tables they came from. */\nexport type ResultProvenance = Pick<SqlScriptResult, \"columns\" | \"tables\">;\n\n/**\n * The stored cell a result cell is, and the key that finds its row.\n */\nexport interface CellEditTarget {\n schema: string;\n table: string;\n /** The table column the edited value is written to. */\n column: string;\n /** Each primary key column of the table, and the result column holding its value for the row. */\n key: { column: string; resultColumn: string }[];\n}\n\n/**\n * Why a cell cannot be edited: a translation key of the console's, and its\n * parameters.\n */\nexport interface CellEditRefusal {\n key:\n | \"studio_sql_edit_unreadable_query\"\n | \"studio_sql_edit_not_a_select\"\n | \"studio_sql_edit_duplicate_column\"\n | \"studio_sql_edit_computed_column\"\n | \"studio_sql_edit_not_a_table\"\n | \"studio_sql_edit_inherited_table\"\n | \"studio_sql_edit_no_primary_key\"\n | \"studio_sql_edit_key_missing\"\n | \"studio_sql_edit_table_read_twice\";\n params: Record<string, string>;\n}\n\nexport type CellEditResolution =\n | { target: CellEditTarget; refusal?: undefined }\n | { target?: undefined; refusal: CellEditRefusal };\n\nconst refuse = (key: CellEditRefusal[\"key\"], params: Record<string, string> = {}): CellEditResolution =>\n ({ refusal: { key, params } });\n\n/**\n * Which stored cell an edit of the result column `resultColumn` writes, and\n * how its row is found again — or why no edit is safe.\n *\n * Decided on what the database reported, not on the query's text. Each\n * column of the result says which table column it was read from, or that it\n * was computed; a cell is editable only when it was read from a column of a\n * table, every column of that table's primary key was read too, and each of\n * them is the only column of the result with its name — a row holds one value\n * per name.\n *\n * The text was the only evidence before, and it was wrong twice over. For\n * `SELECT p.id, a.name FROM posts p JOIN authors a …` it took the post's `id`\n * for the author's key and updated whichever author shared the post's id.\n * For `SELECT id, lower(email) AS name FROM users` it wrote the edited text\n * to `users.name`.\n *\n * The database reports a table, not which mention of it: in a self-join both\n * sides are `authors`, and a key from one with a value from the other finds\n * the wrong row. So the query is still read, for one thing — the table must\n * be read once, and nothing it is read through may be read twice.\n */\nexport function resolveCellEdit(sqlText: string, provenance: ResultProvenance, resultColumn: string): CellEditResolution {\n const sameName = provenance.columns.filter(column => column.name === resultColumn);\n if (sameName.length !== 1) return refuse(\"studio_sql_edit_duplicate_column\", { column: resultColumn });\n const source = sameName[0].source;\n if (!source) return refuse(\"studio_sql_edit_computed_column\", { column: resultColumn });\n\n const tableLabel = `${source.schema}.${source.table}`;\n const table = provenance.tables.find(t => t.schema === source.schema && t.table === source.table);\n if (!table || (table.kind !== \"table\" && table.kind !== \"partitioned table\")) {\n return refuse(\"studio_sql_edit_not_a_table\", { column: resultColumn, table: tableLabel });\n }\n if (table.hasInheritors) return refuse(\"studio_sql_edit_inherited_table\", { table: tableLabel });\n if (table.primaryKey.length === 0) return refuse(\"studio_sql_edit_no_primary_key\", { table: tableLabel });\n\n const key: CellEditTarget[\"key\"] = [];\n for (const keyColumn of table.primaryKey) {\n const holders = provenance.columns.filter(column =>\n column.source?.schema === source.schema &&\n column.source.table === source.table &&\n column.source.column === keyColumn);\n const holder = holders.length === 1 && provenance.columns.filter(column => column.name === holders[0].name).length === 1\n ? holders[0]\n : undefined;\n if (!holder) return refuse(\"studio_sql_edit_key_missing\", { table: tableLabel, columns: table.primaryKey.join(\", \") });\n key.push({ column: keyColumn, resultColumn: holder.name });\n }\n\n const readOnce = tableReadOnce(sqlText, source.table);\n if (readOnce !== true) return readOnce;\n\n return { target: { schema: source.schema, table: source.table, column: source.column, key } };\n}\n\n/**\n * Whether `sqlText` is one SELECT that reads `table` once — counting every\n * mention of a table of that name, in any schema and at any depth, and every\n * mention of a `WITH` query, since one read twice reads its tables twice.\n */\nfunction tableReadOnce(sqlText: string, table: string): true | CellEditResolution {\n const statements = parseStatements(sqlText);\n if (!statements) return refuse(\"studio_sql_edit_unreadable_query\");\n if (statements.length !== 1) return refuse(\"studio_sql_edit_not_a_select\");\n const [statement] = statements;\n const isRead = statement.type === \"select\" ||\n (statement.type === \"with\" &&\n statement.in.type === \"select\" &&\n statement.bind.every(binding => binding.statement.type === \"select\"));\n if (!isRead) return refuse(\"studio_sql_edit_not_a_select\");\n\n const mentions = new Map<string, number>();\n const withNames: string[] = [];\n astVisitor(visitor => ({\n fromTable: from => {\n const name = from.name.name.toLowerCase();\n mentions.set(name, (mentions.get(name) ?? 0) + 1);\n visitor.super().fromTable(from);\n },\n with: withStatement => {\n withNames.push(...withStatement.bind.map(binding => binding.alias.name.toLowerCase()));\n visitor.super().with(withStatement);\n }\n })).statement(statement);\n\n const readTwice = (mentions.get(table.toLowerCase()) ?? 0) !== 1 ||\n withNames.some(name => (mentions.get(name) ?? 0) > 1);\n return readTwice ? refuse(\"studio_sql_edit_table_read_twice\", { table }) : true;\n}\n\n/**\n * A collection whose records a query's rows can be opened as.\n */\nexport interface ResolvedQueryCollection {\n /** The table, as the database named it. */\n tableName: string;\n schemaName: string;\n /** The matched collection */\n collection: AdminCollection;\n /** The result column that holds the table's primary key. */\n pkColumn: string;\n}\n\n/**\n * The collections a result's rows can be opened as: those whose table one of\n * the result's columns was read from, with the table's (single-column) primary\n * key read too, under a name no other column of the result has.\n *\n * Matched on the provenance the database reported. A text match took the\n * result's `id` for every table of a join: `SELECT p.id, a.name FROM posts p\n * JOIN authors a …` offered to open the author whose id was the post's.\n */\nexport function resolveQueryCollections(provenance: ResultProvenance, collections: AdminCollection[]): ResolvedQueryCollection[] {\n const results: ResolvedQueryCollection[] = [];\n for (const table of provenance.tables) {\n const collection = collections.find(c => {\n const tableName = (\"table\" in c ? c.table : undefined) || toSnakeCase(c.slug);\n const schemaName = (\"schema\" in c ? c.schema : undefined) || \"public\";\n return tableName === table.table && schemaName === table.schema;\n });\n if (!collection || table.primaryKey.length !== 1) continue;\n const [keyColumn] = table.primaryKey;\n const holders = provenance.columns.filter(column =>\n column.source?.schema === table.schema &&\n column.source.table === table.table &&\n column.source.column === keyColumn);\n if (holders.length !== 1) continue;\n const pkColumn = holders[0].name;\n if (provenance.columns.filter(column => column.name === pkColumn).length !== 1) continue;\n results.push({ tableName: table.table, schemaName: table.schema, collection, pkColumn });\n }\n return results;\n}\n\n/**\n * A stretch of SQL text, by what it is to the database: code, or text that is\n * data and must not be rewritten — a string literal (quoted, `E'…'` or\n * dollar-quoted), a quoted identifier, or a comment.\n */\ninterface SqlSegment {\n text: string;\n kind: \"code\" | \"literal\" | \"identifier\" | \"line comment\" | \"block comment\";\n}\n\n/** A character an unquoted identifier or keyword can hold, `$` included. */\nconst IDENTIFIER_CHARACTER = /[A-Za-z0-9_$]/;\n\n/**\n * Cut `sqlText` into code and the things in it whose text is data.\n *\n * A lexer, not a parser: it reads text the parser refuses as well, which is\n * most of what a person types while writing a query.\n */\nfunction splitSql(sqlText: string): SqlSegment[] {\n const segments: SqlSegment[] = [];\n let code = \"\";\n const push = (text: string, kind: SqlSegment[\"kind\"]) => {\n if (code) segments.push({ text: code, kind: \"code\" });\n code = \"\";\n segments.push({ text, kind });\n };\n let i = 0;\n while (i < sqlText.length) {\n const ch = sqlText[i];\n const next = sqlText[i + 1];\n if (ch === \"-\" && next === \"-\") {\n const newline = sqlText.indexOf(\"\\n\", i);\n const end = newline === -1 ? sqlText.length : newline + 1;\n push(sqlText.slice(i, end), \"line comment\");\n i = end;\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n // Block comments nest in Postgres.\n let depth = 1;\n let j = i + 2;\n while (j < sqlText.length && depth > 0) {\n if (sqlText[j] === \"/\" && sqlText[j + 1] === \"*\") {\n depth++;\n j += 2;\n } else if (sqlText[j] === \"*\" && sqlText[j + 1] === \"/\") {\n depth--;\n j += 2;\n } else {\n j++;\n }\n }\n push(sqlText.slice(i, j), \"block comment\");\n i = j;\n continue;\n }\n if (ch === \"'\" || ch === \"\\\"\") {\n // `E'…'` takes backslash escapes; every quoted text takes its\n // quote doubled.\n const backslashEscapes = ch === \"'\" && /(^|[^A-Za-z0-9_$])[eE]$/.test(code);\n let j = i + 1;\n while (j < sqlText.length) {\n if (backslashEscapes && sqlText[j] === \"\\\\\") {\n j += 2;\n } else if (sqlText[j] === ch && sqlText[j + 1] === ch) {\n j += 2;\n } else if (sqlText[j] === ch) {\n j++;\n break;\n } else {\n j++;\n }\n }\n push(sqlText.slice(i, j), ch === \"'\" ? \"literal\" : \"identifier\");\n i = j;\n continue;\n }\n if (ch === \"$\" && !IDENTIFIER_CHARACTER.test(code.slice(-1))) {\n // `$tag$…$tag$` — not `$1`, a parameter, whose tag would start\n // with a digit.\n const tag = /^\\$(?:[A-Za-z_][A-Za-z0-9_]*)?\\$/.exec(sqlText.slice(i));\n if (tag) {\n const close = sqlText.indexOf(tag[0], i + tag[0].length);\n const end = close === -1 ? sqlText.length : close + tag[0].length;\n push(sqlText.slice(i, end), \"literal\");\n i = end;\n continue;\n }\n }\n code += ch;\n i++;\n }\n if (code) segments.push({ text: code, kind: \"code\" });\n return segments;\n}\n\n/**\n * The console's \"Format SQL\": runs of whitespace become one space, a comma is\n * followed by one, and an `=` standing alone gets one on each side — in the\n * code only.\n *\n * A literal, a quoted identifier and a comment are copied as they are: their\n * text is data, and rewriting `'a,b'` to `'a, b'` changes the value the next\n * Run writes. A line comment keeps the line break that ends it, or it would\n * swallow the code after it. `>=`, `<=`, `!=`, `:=` and `=>` are operators of\n * their own and are left whole.\n */\nexport function formatSql(sqlText: string): string {\n const segments = splitSql(sqlText);\n return segments.map((segment, index) => {\n if (segment.kind !== \"code\") return segment.text;\n const tidied = segment.text\n .replace(/\\s+/g, \" \")\n .replace(/\\s*,\\s*/g, \", \")\n .replace(/(?<![<>!:=~*+\\-/%^&|#@?])\\s*=\\s*(?![=>~*<])/g, \" = \");\n return segments[index - 1]?.kind === \"line comment\" ? tidied.trimStart() : tidied;\n }).join(\"\").trim();\n}\n\n/**\n * `sqlText` with the console's automatic `LIMIT` on it, or `null` when it\n * takes none (see {@link acceptsAutoLimit}).\n *\n * The limit goes straight after the statement's last token — before a\n * trailing comment, which would otherwise swallow it, and in place of a\n * trailing `;`. A query that computes an aggregate is limited like any other:\n * the limit applies to the rows it returns, so a `count(*)` still counts every\n * row, and a window `count(*) OVER ()` is no longer a way past the cap.\n */\nexport function withAutoLimit(sqlText: string, limit: number): string | null {\n if (!acceptsAutoLimit(sqlText)) return null;\n const segments = splitSql(sqlText);\n const isComment = (segment: SqlSegment) => segment.kind === \"line comment\" || segment.kind === \"block comment\";\n const isFiller = (segment: SqlSegment) => isComment(segment) || (segment.kind === \"code\" && /^[\\s;]*$/.test(segment.text));\n let last = segments.length - 1;\n while (last >= 0 && isFiller(segments[last])) last--;\n if (last < 0) return null;\n\n const lastSegment = segments[last];\n const head = lastSegment.kind === \"code\" ? lastSegment.text.replace(/[\\s;]+$/, \"\") : lastSegment.text;\n const after = lastSegment.kind === \"code\" ? lastSegment.text.slice(head.length) : \"\";\n // What follows the statement is comments and the space between them; its\n // `;` are dropped, the limit brings its own.\n const tail = after.replace(/;/g, \"\") + segments.slice(last + 1)\n .map(segment => segment.kind === \"code\" ? segment.text.replace(/;/g, \"\") : segment.text)\n .join(\"\");\n const before = segments.slice(0, last).map(segment => segment.text).join(\"\");\n return `${before}${head} LIMIT ${limit};${tail.trimEnd()}`;\n}\n","import React, { useState } from \"react\";\nimport { Typography, cls, defaultBorderMixin, Collapse } from \"@rebasepro/ui\";\n\nexport interface ExplainPlanNode {\n \"Node Type\": string;\n \"Relation Name\"?: string;\n \"Alias\"?: string;\n \"Startup Cost\": number;\n \"Total Cost\": number;\n \"Plan Rows\": number;\n \"Plan Width\": number;\n \"Actual Startup Time\"?: number;\n \"Actual Total Time\"?: number;\n \"Actual Rows\"?: number;\n \"Actual Loops\"?: number;\n \"Filter\"?: string;\n \"Index Cond\"?: string;\n \"Hash Cond\"?: string;\n \"Plans\"?: ExplainPlanNode[];\n [key: string]: unknown;\n}\n\nexport interface ExplainVisualizerProps {\n plan: ExplainPlanNode;\n isRoot?: boolean;\n}\n\nexport const ExplainVisualizer: React.FC<ExplainVisualizerProps> = ({ plan, isRoot = true }) => {\n const [expanded, setExpanded] = useState(true);\n\n const hasChildren = plan.Plans && plan.Plans.length > 0;\n\n // Determine color based on cost\n const cost = plan[\"Total Cost\"];\n let costColor = \"text-green-500 dark:text-green-400\";\n if (cost > 1000) costColor = \"text-red-500 dark:text-red-400\";\n else if (cost > 100) costColor = \"text-orange-500 dark:text-orange-400\";\n\n return (\n <div className={cls(\"flex flex-col\", isRoot ? \"p-4\" : \"pl-6 mt-2 relative\")}>\n {/* Tree branch line */}\n {!isRoot && (\n <div className=\"absolute left-2.5 top-0 bottom-0 w-px bg-hairline -z-10\"></div>\n )}\n {!isRoot && (\n <div className=\"absolute left-2.5 top-5 w-3 h-px bg-hairline -z-10\"></div>\n )}\n\n <div className={cls(\"border rounded-md bg-surface-card text-text-primary dark:text-text-primary-dark shadow-xs relative z-10 w-[420px] max-w-full\", defaultBorderMixin)}>\n <div\n className={cls(\"px-4 py-3 flex justify-between items-center cursor-pointer select-none\")}\n onClick={() => setExpanded(!expanded)}\n >\n <div className=\"flex items-center space-x-2\">\n {hasChildren ? (\n <svg className={cls(\"w-4 h-4 text-text-secondary transition-transform\", !expanded ? \"-rotate-90\" : \"\")} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\"/></svg>\n ) : (\n <div className=\"w-4 h-4\"></div>\n )}\n\n <Typography variant=\"body1\" className=\"flex items-center\">\n {plan[\"Node Type\"]}\n </Typography>\n\n {plan[\"Relation Name\"] && (\n <span className=\"font-mono text-[11px] text-text-secondary dark:text-text-secondary-dark px-2 py-0.5 rounded bg-hairline ml-2\">\n on {plan[\"Relation Name\"]} {plan.Alias && plan.Alias !== plan[\"Relation Name\"] ? `(${plan.Alias})` : \"\"}\n </span>\n )}\n </div>\n\n <div className=\"flex space-x-6 items-center\">\n <div className=\"flex flex-col text-right\">\n <span className=\"text-[10px] uppercase text-text-disabled dark:text-text-disabled-dark font-semibold tracking-wide leading-tight mb-0.5\">Cost</span>\n <span className={cls(\"font-mono font-medium text-[13px] leading-none\", costColor)}>{cost.toFixed(2)}</span>\n </div>\n <div className=\"flex flex-col text-right\">\n <span className=\"text-[10px] uppercase text-text-disabled dark:text-text-disabled-dark font-semibold tracking-wide leading-tight mb-0.5\">Rows</span>\n <span className=\"font-mono text-[13px] text-text-secondary dark:text-text-secondary-dark leading-none\">{plan[\"Plan Rows\"]}</span>\n </div>\n </div>\n </div>\n\n <Collapse in={expanded}>\n <div className=\"px-4 py-3 border-t border-hairline text-[13px] flex flex-col gap-2\">\n <div className=\"flex items-center space-x-6\">\n <div className=\"flex items-center space-x-2\">\n <span className=\"text-text-disabled dark:text-text-disabled-dark\">Startup Cost:</span>\n <span className=\"font-mono font-medium\">{plan[\"Startup Cost\"].toFixed(2)}</span>\n </div>\n <div className=\"flex items-center space-x-2\">\n <span className=\"text-text-disabled dark:text-text-disabled-dark\">Width:</span>\n <span className=\"font-mono font-medium\">{plan[\"Plan Width\"]} bytes</span>\n </div>\n </div>\n\n {plan.Filter && (\n <div className=\"mt-1\">\n <span className=\"text-text-disabled dark:text-text-disabled-dark block mb-1\">Filter:</span>\n <code className=\"block w-full p-2 bg-surface-well border border-hairline rounded font-mono text-[12px] truncate\">{plan.Filter}</code>\n </div>\n )}\n {plan[\"Index Cond\"] && (\n <div className=\"mt-1\">\n <span className=\"text-text-disabled dark:text-text-disabled-dark block mb-1\">Index Cond:</span>\n <code className=\"block w-full p-2 bg-surface-well border border-hairline rounded font-mono text-[12px] truncate\">{plan[\"Index Cond\"]}</code>\n </div>\n )}\n {plan[\"Hash Cond\"] && (\n <div className=\"mt-1\">\n <span className=\"text-text-disabled dark:text-text-disabled-dark block mb-1\">Hash Cond:</span>\n <code className=\"block w-full p-2 bg-surface-well border border-hairline rounded font-mono text-[12px] truncate\">{plan[\"Hash Cond\"]}</code>\n </div>\n )}\n </div>\n </Collapse>\n </div>\n\n {expanded && hasChildren && (\n <div className=\"flex flex-col space-y-2 mt-[-4px]\">\n {plan.Plans!.map((childPlan, idx) => (\n <ExplainVisualizer key={idx} plan={childPlan} isRoot={false}/>\n ))}\n </div>\n )}\n </div>\n );\n};\n","\nimport { IconForView } from \"@rebasepro/app\";\nimport { useStudioCollectionRegistry, useStudioSidePanelController } from \"@rebasepro/app\";\nimport React, { useState, useEffect, useCallback, useRef, useMemo } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n Alert,\n Button,\n Checkbox,\n Chip,\n CircularProgress,\n cls,\n DatabaseIcon,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n IconButton,\n iconSize,\n InputLabel,\n isKeyHandled,\n Menu,\n MenuIcon,\n MenuItem,\n MoreVerticalIcon,\n Paper,\n PencilIcon,\n PlayIcon,\n PlusIcon,\n ResizablePanels,\n Select,\n SelectItem,\n Tab,\n Tabs,\n TerminalIcon,\n TextareaAutosize,\n TextField,\n Tooltip,\n Typography,\n VirtualTable,\n VirtualTableColumn,\n XIcon\n} from \"@rebasepro/ui\";\n\nimport { useRebaseContext, useSnackbarController, ConfirmationDialog, ErrorView, useTranslation } from \"@rebasepro/app\";\nimport { isArrayValue, isRecordValue, readStoredJson, readStoredString, writeStoredJson, writeStoredString } from \"@rebasepro/utils\";\nimport { MonacoEditor, type MonacoEditorHandle } from \"./MonacoEditor\";\nimport { SQLEditorSidebar, Snippet } from \"./SQLEditorSidebar\";\nimport { buildExplainSql, formatSql, needsDestructiveConfirmation, quoteIdentifier, quoteTableName, resolveCellEdit, resolveQueryCollections, withAutoLimit, type CellEditResolution, type ResolvedQueryCollection, type ResultProvenance } from \"../../utils/sql_utils\";\nimport { ExplainVisualizer } from \"./ExplainVisualizer\";\n\nimport type { SQLEditorColumnInfo, TableInfo } from \"./sql_editor_types\";\nimport type { SqlScriptNotice } from \"@rebasepro/types\";\n\nexport type { SQLEditorColumnInfo, TableInfo };\n\nconst QueryLoadingView = () => {\n const [elapsed, setElapsed] = useState(0);\n\n useEffect(() => {\n const start = Date.now();\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let cancelled = false;\n\n const tick = () => {\n if (cancelled) return;\n if (document.visibilityState === \"visible\") {\n setElapsed(Date.now() - start);\n }\n timeoutId = setTimeout(tick, 100);\n };\n\n tick();\n\n const handleVisibility = () => {\n if (document.visibilityState === \"visible\") {\n setElapsed(Date.now() - start);\n }\n };\n document.addEventListener(\"visibilitychange\", handleVisibility);\n\n return () => {\n cancelled = true;\n if (timeoutId) clearTimeout(timeoutId);\n document.removeEventListener(\"visibilitychange\", handleVisibility);\n };\n }, []);\n\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 EXECUTING QUERY...\n </Typography>\n <div className=\"mt-2 text-xs font-mono text-text-disabled dark:text-text-disabled-dark\">\n {(elapsed / 1000).toFixed(1)}s elapsed\n </div>\n </div>\n </div>\n );\n};\n\nconst STORAGE_KEY_TABS = \"rebase_sql_tabs\";\nconst STORAGE_KEY_ACTIVE_TAB = \"rebase_sql_active_tab\";\n\nconst FixedEditorOverlay = ({\n displayValue,\n onSave,\n onCancel\n}: {\n displayValue: string,\n /** What the box holds when it closes — the text as typed. */\n onSave: (val: string) => void,\n onCancel: () => void\n}) => {\n const [rect, setRect] = useState<DOMRect | null>(null);\n const [windowSize, setWindowSize] = useState({ width: 1000,\nheight: 1000 });\n const anchorRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (anchorRef.current && anchorRef.current.parentElement) {\n setRect(anchorRef.current.parentElement.getBoundingClientRect());\n }\n if (typeof window !== \"undefined\") {\n setWindowSize({ width: window.innerWidth,\nheight: window.innerHeight });\n const handleResize = () => setWindowSize({ width: window.innerWidth,\nheight: window.innerHeight });\n window.addEventListener(\"resize\", handleResize);\n return () => window.removeEventListener(\"resize\", handleResize);\n }\n return undefined;\n }, []);\n\n if (!rect) {\n return <div ref={anchorRef} className=\"w-full h-full min-h-[20px]\" />;\n }\n\n let top = rect.top - 2;\n let left = rect.left - 2;\n const minWidth = Math.max(rect.width + 4, 250);\n const minHeight = rect.height + 4;\n\n if (left + minWidth > windowSize.width) {\n left = Math.max(10, windowSize.width - minWidth - 10);\n }\n\n // Calculate a max height that doesn't overflow the bottom\n const maxAvailableHeight = Math.max(50, windowSize.height - top - 10);\n const resolvedMaxHeight = Math.min(300, maxAvailableHeight);\n\n // If even the min height overflows, adjust top\n if (top + minHeight > windowSize.height) {\n top = Math.max(10, windowSize.height - minHeight - 10);\n }\n\n return (\n <div ref={anchorRef} className=\"w-full h-full min-h-[20px]\">\n {createPortal(\n <div\n className=\"fixed z-[9999] bg-surface-sheet border-2 border-primary dark:border-primary-dark shadow-xl flex flex-col\"\n style={{\n top,\n left,\n minWidth,\n minHeight,\n maxWidth: Math.min(400, windowSize.width - left - 10)\n }}\n >\n <TextareaAutosize\n className=\"w-full h-full bg-transparent outline-none border-none ring-0 font-mono text-[13px] text-text-primary dark:text-text-primary-dark px-4 py-1.5 resize-none overflow-y-auto\"\n defaultValue={displayValue}\n autoFocus\n style={{ minHeight: \"32px\",\nmaxHeight: resolvedMaxHeight }}\n onFocus={(e) => {\n const val = e.target.value;\n e.target.value = \"\";\n e.target.value = val;\n }}\n onBlur={(e) => {\n onSave(e.target.value);\n onCancel();\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n onSave(e.currentTarget.value);\n onCancel();\n }\n if (e.key === \"Escape\") onCancel();\n }}\n />\n </div>,\n document.body\n )}\n </div>\n );\n};\n\nconst getStoragePrefix = (baseUrl?: string) => {\n if (!baseUrl) return \"default\";\n return baseUrl.replace(/^https?:\\/\\//, \"\").replace(/[^a-zA-Z0-9]/g, \"_\");\n};\n\n/** Where a statement runs: the database, and the role it runs as. */\ntype SQLConnection = { database?: string; role?: string };\n\n/** The part of a tab that is persisted; the rest is per-session. */\ntype StoredTab = {\n id: string;\n name: string;\n sql: string;\n database?: string;\n role?: string;\n};\n\n/**\n * Whether a stored entry is a tab this release can restore. Storage holds\n * whatever the last version to run wrote, so the fields are checked rather\n * than assumed.\n */\nfunction isStoredTab(value: unknown): value is StoredTab {\n if (typeof value !== \"object\" || value === null) return false;\n const tab = value as Record<string, unknown>;\n return typeof tab.id === \"string\"\n && typeof tab.name === \"string\"\n && typeof tab.sql === \"string\";\n}\n\nexport const SQLEditor = () => {\n const { databaseAdmin, client } = useRebaseContext();\n const sidePanelController = useStudioSidePanelController();\n const snackbarController = useSnackbarController();\n const collectionRegistry = useStudioCollectionRegistry();\n\n const { t } = useTranslation();\n\n const projectPrefix = useMemo(() => getStoragePrefix(client?.baseUrl), [client?.baseUrl]);\n\n // Schema state\n const [schemas, setSchemas] = useState<Record<string, TableInfo[]>>({});\n const [isSchemaLoading, setIsSchemaLoading] = useState(true);\n const schemaFetchedRef = useRef(false);\n const [schemaError, setSchemaError] = useState<string | null>(null);\n\n // Connection state\n const [selectedDatabase, setSelectedDatabase] = useState<string | undefined>(() => {\n const projectPrefixSync = client?.baseUrl ? client.baseUrl.replace(/^https?:\\/\\//, \"\").replace(/[^a-zA-Z0-9]/g, \"_\") : \"default\";\n return readStoredString(`rebase_sql_selected_db_${projectPrefixSync}`) || undefined;\n });\n const [selectedRole, setSelectedRole] = useState<string | undefined>(() => {\n const projectPrefixSync = client?.baseUrl ? client.baseUrl.replace(/^https?:\\/\\//, \"\").replace(/[^a-zA-Z0-9]/g, \"_\") : \"default\";\n return readStoredString(`rebase_sql_selected_role_${projectPrefixSync}`) || undefined;\n });\n\n const [availableDatabases, setAvailableDatabases] = useState<string[]>([]);\n /**\n * The database the app reads and writes — the one the CMS opens a record\n * on. Rows read anywhere else are not that record.\n */\n const [appDatabase, setAppDatabase] = useState<string | undefined>(undefined);\n const [availableRoles, setAvailableRoles] = useState<string[]>([]);\n /**\n * Who every statement runs as, once a run has shown that the server does\n * not switch roles (`DISABLE_DB_ROLE_SWITCHING`): the role picker then\n * changes nothing, and says so.\n */\n const [connectionOwner, setConnectionOwner] = useState<string | null>(null);\n const [isLoadingConfig, setIsLoadingConfig] = useState(true);\n const [connectionConfigError, setConnectionConfigError] = useState<string | null>(null);\n\n // Tabbed interface state\n const [tabs, setTabs] = useState<Array<{\n id: string,\n name: string,\n sql: string,\n database?: string,\n role?: string,\n results: Record<string, unknown>[] | null,\n loading: boolean,\n error: string | null,\n execTime: number | null,\n lastExecutedSql: string | null,\n /**\n * The database and role `lastExecutedSql` ran on: the rows on screen\n * were read there, and an edit of one is written there or not at all.\n */\n lastExecutedConnection: SQLConnection | null,\n /**\n * The role the server says the rows on screen were read as — the\n * role picked, unless the server runs every statement as the\n * connection owner. `null` when it did not say.\n */\n ranAs: string | null,\n /**\n * What the database said about the rows on screen: each column's\n * name, and the table column it was read from. An edit is written\n * back by this, and refused without it.\n */\n lastProvenance: ResultProvenance | null,\n /**\n * What the database said while the last run ran — `there is no\n * transaction in progress` for a ROLLBACK that ended nothing, which\n * otherwise reads as a plain success.\n */\n notices: SqlScriptNotice[]\n }>>(() => {\n const projectPrefixSync = getStoragePrefix(client?.baseUrl);\n // This runs during the first render, so anything it throws takes the\n // whole editor down — and the value that threw is still in storage on\n // reload. `readStoredJson` turns unreadable and wrong-shaped state into\n // the default set of tabs instead, and `isStoredTab` drops the\n // individual entries an older release wrote differently: a tab with no\n // `id` never matches the active tab and cannot be closed.\n const saved = readStoredJson<unknown[]>(\n `rebase_sql_tabs_${projectPrefixSync}`,\n { fallback: [], accept: isArrayValue }\n );\n const restored = saved.filter(isStoredTab).map(t => ({\n ...t,\n results: null,\n loading: false,\n error: null,\n execTime: null,\n lastExecutedSql: null,\n lastExecutedConnection: null,\n ranAs: null,\n lastProvenance: null,\n notices: []\n }));\n if (restored.length > 0) return restored;\n return [{\n id: \"1\",\n name: \"Query 1\",\n sql: \"SELECT * FROM \",\n database: readStoredString(`rebase_sql_selected_db_${projectPrefixSync}`) || undefined,\n role: readStoredString(`rebase_sql_selected_role_${projectPrefixSync}`) || undefined,\n results: null,\n loading: false,\n error: null,\n execTime: null,\n lastExecutedSql: null,\n lastExecutedConnection: null,\n ranAs: null,\n lastProvenance: null,\n notices: []\n }];\n });\n const [activeTabId, setActiveTabId] = useState<string>(() => {\n const projectPrefixSync = client?.baseUrl ? client.baseUrl.replace(/^https?:\\/\\//, \"\").replace(/[^a-zA-Z0-9]/g, \"_\") : \"default\";\n return readStoredString(`rebase_sql_active_tab_${projectPrefixSync}`) || \"1\";\n });\n\n const activeTab = tabs.find(t => t.id === activeTabId) || tabs[0];\n\n // Helper to update active tab state\n const updateActiveTab = useCallback((update: Partial<typeof activeTab>) => {\n setTabs(prev => prev.map(t => t.id === activeTabId ? { ...t,\n...update } : t));\n }, [activeTabId]);\n\n const sql = activeTab.sql;\n const results = activeTab.results;\n const loading = activeTab.loading;\n const error = activeTab.error;\n const execTime = activeTab.execTime;\n\n const setSql = (newSql: string) => updateActiveTab({ sql: newSql });\n const setResults = (newResults: Record<string, unknown>[] | null) => updateActiveTab({ results: newResults });\n const setLoading = (newLoading: boolean) => updateActiveTab({ loading: newLoading });\n const setError = (newError: string | null) => updateActiveTab({ error: newError });\n\n useEffect(() => {\n let mounted = true;\n const fetchConnectionConfig = async () => {\n if (!databaseAdmin?.fetchAvailableDatabases || !databaseAdmin?.fetchAvailableRoles || !databaseAdmin?.executeSql) {\n setConnectionConfigError(t(\"studio_sql_sql_not_supported\"));\n setIsLoadingConfig(false);\n return;\n }\n\n try {\n const [dbs, roles, currentDbFromApi, currentUserResult] = await Promise.all([\n databaseAdmin.fetchAvailableDatabases(),\n databaseAdmin.fetchAvailableRoles(),\n typeof databaseAdmin?.fetchCurrentDatabase === \"function\" ? databaseAdmin.fetchCurrentDatabase() : Promise.resolve(undefined),\n databaseAdmin.executeSql(\"SELECT current_user AS role\").catch(() => [])\n ]);\n\n if (mounted) {\n setAvailableDatabases(dbs);\n setAvailableRoles(roles);\n setAppDatabase(currentDbFromApi);\n\n const loadedDb = readStoredString(`rebase_sql_selected_db_${projectPrefix}`) || undefined;\n const loadedRole = readStoredString(`rebase_sql_selected_role_${projectPrefix}`) || undefined;\n\n const initialActiveTabId = readStoredString(`rebase_sql_active_tab_${projectPrefix}`) || \"1\";\n // The old guard caught a parse failure but not a parse\n // *success* of the wrong shape, and the `.find` below sat\n // outside it — so an object where an array was expected\n // still threw.\n const initialTabs = readStoredJson<Array<{ id?: string; database?: string; role?: string }>>(\n `rebase_sql_tabs_${projectPrefix}`,\n { fallback: [], accept: isArrayValue }\n );\n const currentActiveTab = initialTabs.find(t => t.id === initialActiveTabId);\n\n let actualDb = currentActiveTab?.database || loadedDb;\n if (actualDb && !dbs.includes(actualDb)) actualDb = undefined;\n if (!actualDb && dbs.length > 0) {\n actualDb = currentDbFromApi && dbs.includes(currentDbFromApi) ? currentDbFromApi : dbs[0];\n }\n\n if (actualDb) {\n setSelectedDatabase(actualDb);\n writeStoredString(`rebase_sql_selected_db_${projectPrefix}`, actualDb);\n setTabs(prev => prev.map(t => t.id === initialActiveTabId && (!t.database || !dbs.includes(t.database)) ? { ...t,\ndatabase: actualDb } : t));\n }\n\n const currentUser = (currentUserResult?.[0] as Record<string, unknown> | undefined)?.role as string | undefined;\n let actualRole = currentActiveTab?.role || loadedRole;\n\n if (actualRole && !roles.includes(actualRole)) actualRole = undefined;\n if (!actualRole && roles.length > 0) {\n if (currentUser && roles.includes(currentUser)) {\n actualRole = currentUser;\n } else {\n actualRole = roles.includes(\"postgres\") ? \"postgres\" : roles[0];\n }\n }\n\n if (actualRole) {\n setSelectedRole(actualRole);\n writeStoredString(`rebase_sql_selected_role_${projectPrefix}`, actualRole);\n setTabs(prev => prev.map(t => t.id === initialActiveTabId && (!t.role || !roles.includes(t.role)) ? { ...t,\nrole: actualRole } : t));\n }\n }\n } catch (err: unknown) {\n console.error(\"Failed to fetch databases or roles:\", err);\n if (mounted) {\n const message = err instanceof Error ? err.message : String(err);\n setConnectionConfigError(t(\"studio_sql_fetch_error\", { message }));\n }\n } finally {\n if (mounted) {\n setIsLoadingConfig(false);\n }\n }\n };\n\n fetchConnectionConfig();\n\n return () => { mounted = false; };\n }, [databaseAdmin, projectPrefix]);\n\n const handleDatabaseChange = (db: string, tabId?: string) => {\n setSelectedDatabase(db);\n writeStoredString(`rebase_sql_selected_db_${projectPrefix}`, db);\n setTabs(prev => prev.map(t => t.id === (tabId || activeTabId) ? { ...t,\ndatabase: db } : t));\n // Reset so the schema will be re-fetched for the new database\n schemaFetchedRef.current = false;\n };\n\n const handleRoleChange = (role: string, tabId?: string) => {\n setSelectedRole(role);\n writeStoredString(`rebase_sql_selected_role_${projectPrefix}`, role);\n setTabs(prev => prev.map(t => t.id === (tabId || activeTabId) ? { ...t,\nrole } : t));\n };\n\n const handleTabChange = useCallback((newTabId: string) => {\n setActiveTabId(newTabId);\n const newTab = tabs.find(t => t.id === newTabId);\n if (newTab) {\n if (newTab.database && newTab.database !== selectedDatabase) {\n setSelectedDatabase(newTab.database);\n writeStoredString(`rebase_sql_selected_db_${projectPrefix}`, newTab.database);\n schemaFetchedRef.current = false;\n } else if (!newTab.database && selectedDatabase) {\n setTabs(prev => prev.map(t => t.id === newTabId ? { ...t,\ndatabase: selectedDatabase } : t));\n }\n\n if (newTab.role && newTab.role !== selectedRole) {\n setSelectedRole(newTab.role);\n writeStoredString(`rebase_sql_selected_role_${projectPrefix}`, newTab.role);\n } else if (!newTab.role && selectedRole) {\n setTabs(prev => prev.map(t => t.id === newTabId ? { ...t,\nrole: selectedRole } : t));\n }\n }\n }, [tabs, selectedDatabase, selectedRole, projectPrefix]);\n\n const fetchSchema = useCallback(async () => {\n if (!databaseAdmin?.executeSql) {\n setSchemaError(t(\"studio_sql_sql_not_supported\"));\n setIsSchemaLoading(false);\n return;\n }\n\n setIsSchemaLoading(true);\n setSchemaError(null);\n try {\n const sql = `\n SELECT \n c.table_schema as schema, \n c.table_name as \"table\", \n c.column_name as \"column\",\n c.data_type as \"data_type\",\n CASE WHEN kcu.column_name IS NOT NULL THEN true ELSE false END as \"is_pk\"\n FROM \n information_schema.columns c\n LEFT JOIN information_schema.table_constraints tc\n ON tc.table_schema = c.table_schema \n AND tc.table_name = c.table_name \n AND tc.constraint_type = 'PRIMARY KEY'\n LEFT JOIN information_schema.key_column_usage kcu\n ON kcu.constraint_name = tc.constraint_name\n AND kcu.table_schema = tc.table_schema\n AND kcu.table_name = tc.table_name\n AND kcu.column_name = c.column_name\n WHERE \n c.table_schema NOT IN ('information_schema', 'pg_catalog')\n ORDER BY \n c.table_schema, c.table_name, c.ordinal_position;\n `;\n // Pass the selected database so schema introspection targets the right DB.\n const result = await databaseAdmin!.executeSql!(sql, { database: selectedDatabase });\n\n const processGrouped = (data: Record<string, unknown>[]) => {\n const grouped = data.reduce((acc: Record<string, TableInfo[]>, curr: Record<string, unknown>) => {\n const schema = (curr.schema || curr.SCHEMA || curr.table_schema || \"public\") as string;\n const table = (curr.table || curr.TABLE || curr.table_name) as string;\n const column = (curr.column || curr.COLUMN || curr.column_name) as string;\n const dataType = (curr.data_type || curr.DATA_TYPE || \"\") as string;\n const isPrimaryKey = curr.is_pk === true || curr.is_pk === \"true\";\n\n if (!acc[schema]) acc[schema] = [];\n let tableInfo = acc[schema].find(t => t.tableName === table);\n if (!tableInfo) {\n tableInfo = { schemaName: schema,\ntableName: table,\ncolumns: [] };\n acc[schema].push(tableInfo);\n }\n tableInfo.columns.push({ name: column,\ndataType,\nisPrimaryKey });\n return acc;\n }, {});\n setSchemas(grouped);\n };\n\n if (!result || !Array.isArray(result)) {\n if (result && typeof result === \"object\" && \"rows\" in result && Array.isArray((result as { rows: Record<string, unknown>[] }).rows)) {\n processGrouped((result as { rows: Record<string, unknown>[] }).rows);\n } else {\n setSchemaError(t(\"studio_sql_unexpected_format\", { type: typeof result }));\n setSchemas({});\n }\n } else if (result.length === 0) {\n setSchemas({});\n setSchemaError(t(\"studio_sql_no_tables\"));\n } else {\n processGrouped(result);\n }\n\n schemaFetchedRef.current = true;\n } catch (e: unknown) {\n console.error(\"Schema fetch error:\", e);\n const message = e instanceof Error ? e.message : String(e);\n setSchemaError(t(\"studio_sql_schema_fetch_error\", { message }));\n } finally {\n setIsSchemaLoading(false);\n }\n }, [databaseAdmin, selectedDatabase]);\n\n useEffect(() => {\n // Fetch schema after config finishes loading, and re-fetch when the selected database changes.\n if (!isLoadingConfig && !schemaFetchedRef.current) {\n fetchSchema();\n }\n }, [fetchSchema, isLoadingConfig, selectedDatabase]);\n\n const [autoLimit, setAutoLimit] = useState(true);\n const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false);\n const [pendingAction, setPendingAction] = useState<(() => void) | null>(null);\n\n // Inline editing state\n /**\n * The cell being edited, and the text its editor opened with — what\n * closing the editor unchanged is compared against.\n */\n const [editingCell, setEditingCell] = useState<{ rowIndex: number, columnKey: string, initialValue: string } | null>(null);\n\n const isCurrentConnection = useCallback((connection: SQLConnection | null): connection is SQLConnection =>\n connection !== null && connection.database === selectedDatabase && connection.role === selectedRole,\n [selectedDatabase, selectedRole]);\n\n /**\n * Which stored cell a result cell is — by what the database said about the\n * last run's columns, and nothing else.\n */\n const resolveEdit = useCallback((columnKey: string): CellEditResolution => {\n if (!activeTab.lastExecutedSql || !activeTab.lastProvenance) {\n return { refusal: { key: \"studio_sql_edit_computed_column\", params: { column: columnKey } } };\n }\n return resolveCellEdit(activeTab.lastExecutedSql, activeTab.lastProvenance, columnKey);\n }, [activeTab.lastExecutedSql, activeTab.lastProvenance]);\n\n const handleDoubleClick = useCallback((rowIndex: number, columnKey: string, initialValue: string, rowData: Record<string, unknown>) => {\n if (!activeTab.lastExecutedSql) {\n snackbarController.open({\n type: \"error\",\n message: t(\"studio_sql_cannot_edit_missing_query\")\n });\n return;\n }\n\n // The rows were read on one database as one role; the schema the key\n // is resolved against, and the UPDATE, are the selected ones.\n if (!isCurrentConnection(activeTab.lastExecutedConnection)) {\n snackbarController.open({\n type: \"error\",\n message: t(\"studio_sql_cannot_edit_other_connection\")\n });\n return;\n }\n\n const resolution = resolveEdit(columnKey);\n if (!resolution.target) {\n snackbarController.open({\n type: \"error\",\n message: t(resolution.refusal.key, resolution.refusal.params)\n });\n return;\n }\n\n // A key read as NULL — the missing side of an outer join — finds no row.\n const missingPKs = resolution.target.key.filter(\n pk => rowData[pk.resultColumn] === undefined || rowData[pk.resultColumn] === null\n );\n if (missingPKs.length > 0) {\n snackbarController.open({\n type: \"error\",\n message: t(\"studio_sql_missing_pk\", { columns: missingPKs.map(pk => `\"${pk.resultColumn}\"`).join(\", \") })\n });\n return;\n }\n\n setEditingCell({ rowIndex,\ncolumnKey,\ninitialValue });\n }, [activeTab.lastExecutedSql, activeTab.lastExecutedConnection, isCurrentConnection, resolveEdit, snackbarController, t]);\n\n const handleCellSave = useCallback(async (text: string, rowData: Record<string, unknown>, columnKey: string, rowIndex: number) => {\n if (!editingCell || !activeTab.lastExecutedSql) return;\n\n setEditingCell(null); // Optimistically close\n\n // Compared as text, against the text the editor opened with: a cell\n // holding '' and a NULL one both open empty, and closing either\n // untouched is not an edit.\n if (text === editingCell.initialValue) return;\n // A box emptied by hand stores NULL, which the grid then draws as\n // NULL — what is on screen is what was stored.\n const newValue = text === \"\" ? null : text;\n\n const connection = activeTab.lastExecutedConnection;\n if (!isCurrentConnection(connection)) {\n snackbarController.open({ type: \"error\",\nmessage: t(\"studio_sql_cannot_edit_other_connection\") });\n return;\n }\n\n const resolution = resolveEdit(columnKey);\n if (!resolution.target) {\n snackbarController.open({ type: \"error\",\nmessage: t(resolution.refusal.key, resolution.refusal.params) });\n return;\n }\n const target = resolution.target;\n\n const formatValue = (val: unknown) => {\n if (val === null || val === undefined) return \"NULL\";\n if (typeof val === \"number\") return val;\n if (typeof val === \"boolean\") return val ? \"TRUE\" : \"FALSE\";\n return `'${String(val).replace(/'/g, \"''\")}'`;\n };\n\n // The table and column the database said the cell was read from, in\n // the schema it said — never a name read off the query, which\n // resolves through the search path or belongs to another table.\n const whereConditions = target.key.map(\n pk => `${quoteIdentifier(pk.column)} = ${formatValue(rowData[pk.resultColumn])}`\n ).join(\" AND \");\n\n const updateSql = `UPDATE ${quoteTableName(target.table, target.schema)} SET ${quoteIdentifier(target.column)} = ${formatValue(newValue)} WHERE ${whereConditions};`;\n\n try {\n // Through the script door, for the row count its answer carries.\n // An UPDATE that matches no row succeeds as far as the database\n // is concerned — a policy that lets the role read the row but not\n // update it, a row changed or deleted since it was read — and is\n // not an edit. Only a backend that describes its results reaches\n // here at all: without that, no cell resolves to a table column.\n const run = await databaseAdmin?.runSqlScript?.(updateSql, { database: connection.database,\nrole: connection.role });\n if (!run?.rowCount) {\n snackbarController.open({\n type: \"error\",\n message: t(\"studio_sql_update_no_row\")\n });\n return;\n }\n\n const newResults = [...(activeTab.results || [])];\n if (newResults[rowIndex]) {\n newResults[rowIndex] = { ...newResults[rowIndex],\n[columnKey]: newValue };\n }\n updateActiveTab({ results: newResults });\n\n snackbarController.open({\n type: \"success\",\n message: t(\"studio_sql_row_updated\")\n });\n } catch (e: unknown) {\n snackbarController.open({\n type: \"error\",\n message: t(\"studio_sql_update_failed\", { message: e instanceof Error ? e.message : String(e) })\n });\n }\n }, [editingCell, resolveEdit, activeTab.lastExecutedSql, activeTab.lastExecutedConnection, activeTab.results, isCurrentConnection, databaseAdmin, updateActiveTab, snackbarController, t]);\n\n const [columnWidths, setColumnWidths] = useState<Record<string, Record<string, number>>>(() => {\n const projectPrefixSync = client?.baseUrl ? client.baseUrl.replace(/^https?:\\/\\//, \"\").replace(/[^a-zA-Z0-9]/g, \"_\") : \"default\";\n return readStoredJson<Record<string, Record<string, number>>>(\n `rebase_sql_column_widths_${projectPrefixSync}`,\n { fallback: {}, accept: isRecordValue }\n );\n });\n const [snippets, setSnippets] = useState<Snippet[]>([]);\n const [history, setHistory] = useState<string[]>([]);\n const [isSaveDialogOpen, setIsSaveDialogOpen] = useState(false);\n const [newSnippetName, setNewSnippetName] = useState(\"\");\n\n // Load from local storage\n useEffect(() => {\n setSnippets(readStoredJson<Snippet[]>(\n `rebase_sql_snippets_${projectPrefix}`,\n { fallback: [], accept: isArrayValue }\n ));\n setHistory(readStoredJson<string[]>(\n `rebase_sql_history_${projectPrefix}`,\n { fallback: [], accept: isArrayValue }\n ));\n }, [projectPrefix]);\n\n // Save tabs and active tab to local storage. Tab text is persisted on every\n // edit, so this is the write that reaches the origin's quota first — and a\n // `QuotaExceededError` thrown out of an effect is not something the view\n // can do anything with.\n useEffect(() => {\n const sanitizedTabs = tabs.map(t => ({\n id: t.id,\n name: t.name,\n sql: t.sql,\n database: t.database,\n role: t.role\n }));\n writeStoredJson(`rebase_sql_tabs_${projectPrefix}`, sanitizedTabs);\n }, [tabs, projectPrefix]);\n\n useEffect(() => {\n writeStoredString(`rebase_sql_active_tab_${projectPrefix}`, activeTabId);\n }, [activeTabId, projectPrefix]);\n\n const saveSnippets = (newSnippets: Snippet[]) => {\n setSnippets(newSnippets);\n writeStoredJson(`rebase_sql_snippets_${projectPrefix}`, newSnippets);\n };\n\n const saveHistory = (newHistory: string[]) => {\n setHistory(newHistory);\n writeStoredJson(`rebase_sql_history_${projectPrefix}`, newHistory.slice(-50));\n };\n\n const handleDeleteSnippet = (id: string) => {\n saveSnippets(snippets.filter(s => s.id !== id));\n };\n\n const handleAddTab = () => {\n const newId = Math.random().toString(36).substring(2, 9);\n\n // Find the next available query number\n let maxNumber = 0;\n tabs.forEach(tab => {\n const match = tab.name.match(/^Query (\\d+)$/);\n if (match) {\n const num = parseInt(match[1], 10);\n if (num > maxNumber) maxNumber = num;\n }\n });\n const name = `Query ${maxNumber + 1}`;\n setTabs(prev => [...prev, {\n id: newId,\n name,\n sql: \"SELECT * FROM \",\n database: selectedDatabase,\n role: selectedRole,\n results: null,\n loading: false,\n error: null,\n execTime: null,\n lastExecutedSql: null,\n lastExecutedConnection: null,\n ranAs: null,\n lastProvenance: null,\n notices: []\n }]);\n setActiveTabId(newId);\n };\n\n const handleCloseTab = (id: string, e: React.MouseEvent) => {\n e.stopPropagation();\n if (tabs.length === 1) return;\n\n const tabIndex = tabs.findIndex(t => t.id === id);\n const newTabs = tabs.filter(t => t.id !== id);\n setTabs(newTabs);\n\n if (activeTabId === id) {\n // Find a new active tab: the one at the same index, or the last one if we closed the last\n const nextIndex = Math.min(tabIndex, newTabs.length - 1);\n if (newTabs[nextIndex]) {\n setActiveTabId(newTabs[nextIndex].id);\n }\n }\n };\n\n const handleColumnResize = useCallback(({ key, width }: { key: string, width: number }) => {\n setColumnWidths(prev => {\n const newWidths = {\n ...prev,\n [activeTab.sql]: {\n ...(prev[activeTab.sql] || {}),\n [key]: width\n }\n };\n writeStoredJson(`rebase_sql_column_widths_${projectPrefix}`, newWidths);\n return newWidths;\n });\n }, [activeTab.sql, projectPrefix]);\n\n const handlePrettify = () => {\n setSql(formatSql(activeTab.sql));\n };\n\n /** The editor, for the selection the toolbar's buttons act on. */\n const editorHandle = useRef<MonacoEditorHandle>(null);\n\n const handleExplain = async () => {\n // The selected statement when there is one, as Cmd+Enter runs it; and\n // planned, never executed — see `buildExplainSql`.\n const explainSql = buildExplainSql(editorHandle.current?.getSelectedText() ?? activeTab.sql);\n if (!explainSql) {\n updateActiveTab({ results: null,\nerror: t(\"studio_sql_explain_single_statement\") });\n return;\n }\n // The plan replaces the rows on screen, so nothing left there is a row\n // an edit could write back.\n updateActiveTab({ loading: true,\nerror: null,\nresults: null,\nlastExecutedSql: null,\nlastExecutedConnection: null,\nranAs: null,\nlastProvenance: null,\nnotices: [] });\n const start = performance.now();\n try {\n if (databaseAdmin?.executeSql) {\n const result = await databaseAdmin.executeSql(explainSql, { database: selectedDatabase,\nrole: selectedRole });\n updateActiveTab({ results: result,\nexecTime: Math.round(performance.now() - start) });\n }\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n updateActiveTab({ error: message || t(\"studio_sql_error_explaining\") });\n } finally {\n updateActiveTab({ loading: false });\n }\n };\n\n const executeRun = useCallback(async (sqlOverride?: string) => {\n const written = sqlOverride || activeTab.sql;\n const sqlToRun = (autoLimit ? withAutoLimit(written, 1000) : null) ?? written;\n\n updateActiveTab({ loading: true,\nerror: null,\nresults: null,\nnotices: [] });\n const start = performance.now();\n\n try {\n if (databaseAdmin?.runSqlScript || databaseAdmin?.executeSql) {\n const connection = { database: selectedDatabase, role: selectedRole };\n // A backend that cannot describe its result still runs the\n // script; nothing in what it returns is said to come from a\n // table, so no cell of it is edited.\n const run = databaseAdmin.runSqlScript\n ? await databaseAdmin.runSqlScript(sqlToRun, connection)\n : undefined;\n const rows: Record<string, unknown>[] = run ? run.rows : await databaseAdmin.executeSql!(sqlToRun, connection);\n updateActiveTab({\n results: rows,\n execTime: Math.round(performance.now() - start),\n lastExecutedSql: sqlToRun,\n lastExecutedConnection: connection,\n ranAs: run?.effectiveRole ?? null,\n lastProvenance: run\n ? { columns: run.columns, tables: run.tables }\n : { columns: Object.keys(rows[0] ?? {}).map(name => ({ name })), tables: [] },\n notices: run?.notices ?? []\n });\n // A server that ran the script as someone other than the role\n // picked runs every statement that way: the picker is moot.\n if (run?.effectiveRole && connection.role) {\n setConnectionOwner(run.effectiveRole !== connection.role ? run.effectiveRole : null);\n }\n\n if (history[history.length - 1] !== activeTab.sql) {\n saveHistory([...history, activeTab.sql]);\n }\n } else {\n updateActiveTab({ error: t(\"studio_sql_execution_not_supported\") });\n }\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n updateActiveTab({ error: message || t(\"studio_sql_error_executing\") });\n } finally {\n updateActiveTab({ loading: false });\n }\n }, [activeTab.sql, autoLimit, databaseAdmin, history, updateActiveTab, selectedDatabase, selectedRole]);\n\n const handleRun = useCallback(async (selectedText?: string) => {\n const sqlTarget = selectedText || activeTab.sql;\n if (!sqlTarget.trim()) return;\n\n if (needsDestructiveConfirmation(sqlTarget)) {\n setPendingAction(() => () => executeRun(selectedText));\n setIsConfirmDialogOpen(true);\n return;\n }\n\n executeRun(selectedText);\n }, [activeTab.sql, executeRun]);\n\n // Global keybindings\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // A layer above the editor (a dialog, a menu) already acted on it.\n if (isKeyHandled(e)) return;\n if ((e.metaKey || e.ctrlKey) && e.key === \"Enter\") {\n // If we are in an input or textarea (except the code editor which handles its own), we might not want to run\n const activeElement = document.activeElement;\n const isInput = activeElement?.tagName === \"INPUT\" || activeElement?.tagName === \"TEXTAREA\";\n // If it's the monaco editor textarea, it's fine, let's trigger handleRun\n // Actually the monaco editor already has its own action, so we don't need a global one IF focused in monaco.\n // But wait, if we have both, it might run twice.\n // Let's check if we're focused in monaco.\n const isMonaco = activeElement?.className?.includes(\"monaco-mouse-cursor-text\");\n\n if (!isMonaco && !isInput) {\n e.preventDefault();\n handleRun(editorHandle.current?.getSelectedText());\n }\n }\n };\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [handleRun]);\n\n const handleSaveSnippet = () => {\n if (!newSnippetName.trim() || !sql.trim()) return;\n\n const newSnippet: Snippet = {\n id: Math.random().toString(36).substring(2, 9),\n name: newSnippetName,\n sql: sql,\n createdAt: Date.now()\n };\n\n saveSnippets([...snippets, newSnippet]);\n setNewSnippetName(\"\");\n setIsSaveDialogOpen(false);\n snackbarController.open({\n type: \"success\",\n message: t(\"studio_sql_snippet_saved\", { name: newSnippetName })\n });\n };\n\n const handleExportCSV = () => {\n if (!results || results.length === 0) return;\n\n const headers = Object.keys(results[0]).join(\",\");\n const rows = results.map(row =>\n Object.values(row).map(val => {\n const str = String(val);\n return str.includes(\",\") ? `\"${str}\"` : str;\n }).join(\",\")\n );\n const csv = [headers, ...rows].join(\"\\n\");\n const blob = new Blob([csv], { type: \"text/csv\" });\n const url = window.URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `query_results_${new Date().toISOString().slice(0, 19)}.csv`;\n a.click();\n window.URL.revokeObjectURL(url);\n };\n\n const handleExportJSON = () => {\n if (!results || results.length === 0) return;\n\n const json = JSON.stringify(results, null, 2);\n const blob = new Blob([json], { type: \"application/json\" });\n const url = window.URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `query_results_${new Date().toISOString().slice(0, 19)}.json`;\n a.click();\n window.URL.revokeObjectURL(url);\n };\n\n const handleExportMarkdown = () => {\n if (!results || results.length === 0) return;\n\n const headers = Object.keys(results[0]);\n const headerRow = `| ${headers.join(\" | \")} |`;\n const dividerRow = `| ${headers.map(() => \"---\").join(\" | \")} |`;\n const dataRows = results.map(row =>\n `| ${headers.map(header => {\n const val = row[header];\n if (val === null) return \"null\";\n if (val === undefined) return \"\";\n // Replace pipes and newlines to avoid breaking the markdown table\n return String(val).replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \" \");\n }).join(\" | \")} |`\n );\n\n const markdown = [headerRow, dividerRow, ...dataRows].join(\"\\n\");\n navigator.clipboard.writeText(markdown).then(() => {\n snackbarController.open({\n type: \"success\",\n message: t(\"studio_sql_markdown_copied\")\n });\n }).catch(() => {\n snackbarController.open({\n type: \"error\",\n message: t(\"studio_sql_markdown_copy_failed\")\n });\n });\n };\n\n const renderResults = () => {\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\">{t(\"studio_sql_executing_query\")}</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={t(\"studio_sql_query_error\")} error={error}/>\n </div>\n );\n }\n\n if (!results) {\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\"><path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1} d=\"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4\"/></svg>\n <Typography variant=\"body2\">{t(\"studio_sql_run_query_placeholder\")}</Typography>\n </div>\n </div>\n );\n }\n\n // Check for EXPLAIN (FORMAT JSON) response\n if (results.length === 1 && results[0][\"QUERY PLAN\"] && Array.isArray(results[0][\"QUERY PLAN\"])) {\n try {\n const plan = results[0][\"QUERY PLAN\"][0].Plan;\n if (plan) {\n return (\n <div className=\"flex-grow overflow-auto p-4 bg-surface-sheet flex flex-col items-start\">\n <Typography variant=\"caption\" className=\"font-semibold text-text-secondary mb-4 tracking-wider uppercase\">{t(\"studio_sql_visual_execution_plan\")}</Typography>\n <div className=\"pb-12\">\n <ExplainVisualizer plan={plan}/>\n </div>\n </div>\n );\n }\n } catch (e) {\n console.warn(\"Failed to parse EXPLAIN JSON output:\", e);\n }\n }\n\n if (results.length === 0) {\n return (\n <div className=\"flex-grow p-6 flex flex-col items-center justify-center\">\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark font-mono border-b border-hairline pb-2 mb-2\">{t(\"studio_sql_success\")}</Typography>\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">{t(\"studio_sql_no_results\")}</Typography>\n </div>\n );\n }\n\n const savedWidths = columnWidths[activeTab.sql] || {};\n const resultColumnKeys = Object.keys(results[0]);\n\n // Compute matched collections for this query, including PK column detection\n // Only collections whose key the database says is in the result can be opened,\n // and only from rows read on the app's own database: the record the CMS\n // opens is that database's row with the key, whatever database the\n // rows on screen came from.\n const readFromAppDatabase = activeTab.lastExecutedConnection !== null &&\n (activeTab.lastExecutedConnection.database === undefined || activeTab.lastExecutedConnection.database === appDatabase);\n const actionableCollections: ResolvedQueryCollection[] = activeTab.lastProvenance && collectionRegistry.collections && readFromAppDatabase\n ? resolveQueryCollections(activeTab.lastProvenance, collectionRegistry.collections)\n .filter(mc => resultColumnKeys.includes(mc.pkColumn))\n : [];\n\n // For each row, determine which entities can be opened\n const getRowEntityActions = (rowData: Record<string, unknown>): { collection: ResolvedQueryCollection, entityId: string | number }[] => {\n if (!rowData) return [];\n return actionableCollections\n .filter(mc => rowData[mc.pkColumn] != null)\n .map(mc => ({\n collection: mc,\n entityId: rowData[mc.pkColumn] as string | number\n }));\n };\n\n // Build the columns array. If we have actionable collections, prepend a dedicated action column.\n const dataColumns: VirtualTableColumn[] = resultColumnKeys.map(key => ({\n key,\n title: key,\n width: savedWidths[key] ?? 150,\n sortable: false,\n resizable: true\n }));\n\n const columns: VirtualTableColumn[] = actionableCollections.length > 0\n ? [{ key: \"__cms_action__\",\ntitle: \"\",\nwidth: 36,\nsortable: false,\nresizable: false }, ...dataColumns]\n : dataColumns;\n\n return (\n <div className=\"flex-grow flex flex-col overflow-hidden min-h-0\">\n {/* Collection Badges Bar */}\n {actionableCollections.length > 0 && (\n <div className={cls(\"px-4 py-1.5 border-b flex items-center gap-2 shrink-0 bg-surface-sheet\", defaultBorderMixin)}>\n <Tooltip title={t(\"studio_sql_admin_collections_tooltip\")}>\n <Typography variant=\"caption\" className=\"text-[10px] font-semibold uppercase tracking-widest text-text-disabled dark:text-text-disabled-dark mr-1 shrink-0 cursor-help\">{t(\"studio_sql_collections_label\")}</Typography>\n </Tooltip>\n <div className=\"flex items-center gap-1.5 overflow-x-auto no-scrollbar\">\n {actionableCollections.map(mc => (\n <Tooltip key={mc.tableName} title={`Table \"${mc.tableName}\" → ${mc.collection.name} (PK: ${mc.pkColumn})`}>\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-medium bg-primary/10 dark:bg-primary-dark/15 text-primary dark:text-primary-dark whitespace-nowrap border border-primary/20 dark:border-primary-dark/20\">\n {typeof mc.collection.icon === \"string\" && (\n <IconForView collectionOrView={mc.collection} className=\"text-[12px]\"/>\n )}\n {mc.collection.name}\n </span>\n </Tooltip>\n ))}\n </div>\n </div>\n )}\n <div className=\"flex-grow relative h-full min-h-0 min-w-0\">\n <VirtualTable\n data={results}\n columns={columns}\n rowHeight={32}\n headerHeight={32}\n extraData={editingCell}\n onColumnResizeEnd={handleColumnResize}\n cellRenderer={({ rowData, column, rowIndex }) => {\n // Dedicated collection action column\n if (column.key === \"__cms_action__\") {\n const rowActions = getRowEntityActions(rowData ?? {});\n if (rowActions.length === 0) {\n return <div className=\"h-full w-full\"/>;\n }\n if (rowActions.length === 1) {\n const ra = rowActions[0];\n return (\n <div className=\"h-full flex items-center justify-center\">\n <Tooltip title={t(\"studio_sql_edit_entity\", { name: ra.collection.collection.name,\nid: String(ra.entityId) })}>\n <IconButton\n size=\"small\"\n aria-label={t(\"studio_sql_edit_entity\", { name: ra.collection.collection.name,\nid: String(ra.entityId) })}\n className=\"text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300 transition-colors\"\n onClick={(e) => {\n e.stopPropagation();\n sidePanelController?.open({\n path: ra.collection.collection.slug,\n entityId: ra.entityId,\n collection: ra.collection.collection,\n updateUrl: false\n });\n }}\n >\n <PencilIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n </div>\n );\n }\n // Multiple matched collections (JOIN) — show a dropdown\n return (\n <div className=\"h-full flex items-center justify-center\">\n <Menu\n trigger={\n <IconButton\n size=\"small\"\n className=\"text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300 transition-colors\"\n onClick={(e) => e.stopPropagation()}\n >\n <MoreVerticalIcon size={iconSize.smallest}/>\n </IconButton>\n }\n >\n {rowActions.map(ra => (\n <MenuItem\n key={ra.collection.tableName}\n dense\n onClick={() => {\n sidePanelController?.open({\n path: ra.collection.collection.slug,\n entityId: ra.entityId,\n collection: ra.collection.collection,\n updateUrl: false\n });\n }}\n >\n {t(\"studio_sql_edit_entity\", { name: ra.collection.collection.name,\nid: String(ra.entityId) })}\n </MenuItem>\n ))}\n </Menu>\n </div>\n );\n }\n\n // Regular data cell\n const isEditing = editingCell?.rowIndex === rowIndex && editingCell?.columnKey === column.key;\n const value = rowData ? rowData[column.key] : null;\n const displayValue = typeof value === \"object\" && value !== null ? JSON.stringify(value) : String(value ?? \"\");\n\n if (isEditing) {\n return (\n <FixedEditorOverlay\n displayValue={displayValue}\n onSave={(val) => handleCellSave(val, rowData ?? {}, column.key, rowIndex)}\n onCancel={() => setEditingCell(null)}\n />\n );\n }\n\n return (\n <div\n className=\"px-4 py-1.5 h-full flex items-center whitespace-nowrap text-[13px] text-text-primary dark:text-text-primary-dark font-mono cursor-text group/cell\"\n onDoubleClick={() => handleDoubleClick(rowIndex, column.key, displayValue, rowData ?? {})}\n >\n <div className=\"truncate flex-grow\" title={displayValue}>\n {value === null || value === undefined ? <span className=\"text-text-disabled dark:text-text-disabled-dark italic text-[11px]\">NULL</span> : displayValue}\n </div>\n </div>\n );\n }}\n />\n </div>\n\n <div className={cls(\"p-2 px-4 border-t bg-surface-sheet flex justify-between items-center shrink-0\", defaultBorderMixin)}>\n <div className=\"flex space-x-4\">\n <div className=\"flex items-center text-[11px]\">\n <span className=\"font-semibold text-text-disabled dark:text-text-disabled-dark mr-2 uppercase tracking-tighter\">{t(\"studio_sql_rows\")}</span>\n <span className=\"font-mono text-text-secondary dark:text-text-secondary-dark\">{results.length}</span>\n </div>\n <div className=\"flex items-center text-[11px]\">\n <span className=\"font-semibold text-text-disabled dark:text-text-disabled-dark mr-2 uppercase tracking-tighter\">{t(\"studio_sql_time\")}</span>\n <span className=\"font-mono text-text-secondary dark:text-text-secondary-dark\">{execTime}ms</span>\n </div>\n </div>\n <div className=\"flex gap-2 overflow-x-auto no-scrollbar items-center px-2\">\n <Button\n size=\"small\"\n variant=\"text\"\n className=\"text-[10px] uppercase font-semibold text-text-secondary dark:text-text-secondary-dark whitespace-nowrap\"\n onClick={handleExportMarkdown}\n >\n {t(\"studio_sql_copy_markdown\")}\n </Button>\n <Button\n size=\"small\"\n variant=\"text\"\n className=\"text-[10px] uppercase font-semibold text-text-secondary dark:text-text-secondary-dark whitespace-nowrap\"\n onClick={handleExportJSON}\n >\n {t(\"studio_sql_export_json\")}\n </Button>\n <Button\n size=\"small\"\n variant=\"text\"\n className=\"text-[10px] uppercase font-semibold text-text-secondary dark:text-text-secondary-dark whitespace-nowrap\"\n onClick={handleExportCSV}\n >\n {t(\"studio_sql_export_csv\")}\n </Button>\n </div>\n </div>\n </div>\n );\n };\n\n // `parseFloat` answers NaN for anything it cannot read, and a NaN pane size\n // lays the editor out to nothing — so the stored value has to be checked,\n // not just parsed.\n const readStoredSize = (key: string, fallback: number) => {\n const saved = readStoredString(`${key}_${getStoragePrefix(client?.baseUrl)}`);\n const parsed = saved === null ? NaN : parseFloat(saved);\n return Number.isFinite(parsed) ? parsed : fallback;\n };\n\n const [sidebarSize, setSidebarSize] = useState(() => readStoredSize(\"rebase_sql_editor_sidebar_size\", 20));\n const [editorHeight, setEditorHeight] = useState(() => readStoredSize(\"rebase_sql_editor_height\", 50));\n\n useEffect(() => {\n writeStoredString(`rebase_sql_editor_sidebar_size_${projectPrefix}`, sidebarSize.toString());\n }, [sidebarSize, projectPrefix]);\n\n useEffect(() => {\n writeStoredString(`rebase_sql_editor_height_${projectPrefix}`, editorHeight.toString());\n }, [editorHeight, projectPrefix]);\n\n const activeSnippet = snippets.find(s => s.sql === activeTab.sql);\n const isFavorite = activeSnippet?.isFavorite || false;\n\n return (\n <div className=\"flex h-full w-full bg-surface-card overflow-hidden text-text-primary dark:text-text-primary-dark\">\n <ResizablePanels\n orientation=\"horizontal\"\n panelSizePercent={sidebarSize}\n onPanelSizeChange={setSidebarSize}\n minPanelSizePx={220}\n firstPanel={\n <SQLEditorSidebar\n snippets={snippets}\n history={history}\n onSelectSnippet={setSql}\n onTableClick={setSql}\n onDeleteSnippet={handleDeleteSnippet}\n schemas={schemas}\n isSchemaLoading={isSchemaLoading}\n schemaError={schemaError}\n onRetrySchema={fetchSchema}\n />\n }\n secondPanel={\n <div className=\"flex-grow flex flex-col min-w-0 h-full w-full\">\n {/* Toolbar */}\n <div className={cls(\"flex items-center justify-between pr-2 border-b bg-surface-card\", defaultBorderMixin)}>\n <div className=\"flex items-center flex-grow overflow-hidden mr-4\">\n <div className=\"flex items-center no-scrollbar overflow-x-auto min-w-0\">\n <Tabs value={activeTabId} onValueChange={handleTabChange} variant=\"boxy\" className=\"w-[unset] flex-shrink-0\" innerClassName=\"bg-surface-card\">\n {tabs.map(tab => (\n <Tab key={tab.id} value={tab.id} className=\"flex items-center justify-between group max-w-[200px]\">\n <TerminalIcon size={iconSize.smallest} className=\"text-blue-500 mr-1.5 flex-shrink-0\"/>\n <span className=\"truncate\">{tab.name}</span>\n {tabs.length > 1 && (\n // A span, not an `IconButton`: `Tab` is a Radix\n // `TabsTrigger`, which is itself a `<button>`, and\n // React refuses the nesting — \"`<button>` cannot be\n // a descendant of `<button>`. This will cause a\n // hydration error\" on every second tab opened.\n //\n // `tabIndex={-1}` keeps the tab strip at one stop\n // per tab rather than two; the label is here so the\n // control is at least named, which the icon-only\n // button never was.\n <span\n role=\"button\"\n tabIndex={-1}\n aria-label={t(\"studio_sql_close_tab\")}\n title={t(\"studio_sql_close_tab\")}\n onClick={(e) => handleCloseTab(tab.id, e)}\n className=\"ml-1 p-0.5 rounded inline-flex items-center opacity-0 group-hover:opacity-100 hover:text-red-500 transition-opacity cursor-pointer\"\n >\n <XIcon size={iconSize.smallest}/>\n </span>\n )}\n </Tab>\n ))}\n </Tabs>\n <IconButton\n size=\"small\"\n onClick={handleAddTab}\n aria-label={t(\"studio_sql_new_tab\")}\n title={t(\"studio_sql_new_tab\")}\n className=\"ml-2 flex-shrink-0\"\n >\n <PlusIcon size={iconSize.smallest}/>\n </IconButton>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center justify-end pr-2 gap-1.5\">\n <Tooltip title={t(\"studio_sql_format_sql\")}>\n <IconButton size=\"small\" onClick={handlePrettify} aria-label={t(\"studio_sql_format_sql\")}>\n <MenuIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n\n <Button\n variant=\"text\"\n size=\"small\"\n onClick={handleExplain}\n disabled={loading}\n >\n {t(\"studio_sql_explain\")}\n </Button>\n\n <div className=\"h-4 w-px bg-hairline mx-1\"></div>\n\n <div className=\"flex items-center space-x-2 px-2\" onClick={(e) => {\n setAutoLimit(!autoLimit);\n e.stopPropagation();\n }}>\n <Typography variant=\"caption\" className=\"text-[11px] text-text-secondary cursor-pointer select-none\">{t(\"studio_sql_limit_1000\")}</Typography>\n <div onClick={(e) => e.stopPropagation()}>\n <Checkbox\n checked={autoLimit}\n onCheckedChange={setAutoLimit}\n size=\"smallest\"\n padding={false}\n />\n </div>\n </div>\n\n <div className=\"h-4 w-px bg-hairline mx-1\"></div>\n\n <Tooltip title={isFavorite ? t(\"studio_sql_remove_from_favorites\") : t(\"studio_sql_add_to_favorites\")}>\n <IconButton\n size=\"small\"\n onClick={() => {\n if (!activeSnippet) {\n snackbarController.open({\n type: \"info\",\n message: t(\"studio_sql_save_first_to_favorite\")\n });\n return;\n }\n saveSnippets(snippets.map(s => s.id === activeSnippet.id ? { ...s,\nisFavorite: !s.isFavorite } : s));\n }}\n >\n <svg className={`w-4 h-4 ${isFavorite ? \"text-red-500 fill-current\" : \"text-text-disabled dark:text-text-disabled-dark hover:text-text-primary\"}`} fill={isFavorite ? \"currentColor\" : \"none\"} stroke=\"currentColor\" strokeWidth={2} viewBox=\"0 0 24 24\"><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z\"/></svg>\n </IconButton>\n </Tooltip>\n\n <Button\n variant=\"text\"\n size=\"small\"\n onClick={() => setIsSaveDialogOpen(true)}\n >\n {t(\"studio_sql_save\")}\n </Button>\n\n <div className=\"h-4 w-px bg-hairline mx-1\"></div>\n\n <Menu\n trigger={\n <Button\n size=\"small\"\n variant=\"outlined\"\n className=\"text-text-secondary dark:text-text-secondary-dark font-medium mr-2\"\n >\n <DatabaseIcon size={iconSize.small} className=\"mr-1.5 text-text-disabled dark:text-text-disabled-dark\"/>\n <span className=\"max-w-[160px] truncate\">\n {isLoadingConfig\n ? \"...\"\n : `${selectedDatabase || t(\"studio_sql_select_db\")}${(connectionOwner ?? selectedRole) ? ` (${connectionOwner ?? selectedRole})` : \"\"}`}\n </span>\n </Button>\n }\n >\n <div className=\"max-h-64 overflow-y-auto\">\n <div className=\"px-3 py-1.5 border-b border-hairline mb-1\">\n <Typography variant=\"caption\" className=\"font-semibold uppercase tracking-wider text-[9px] text-text-disabled dark:text-text-disabled-dark\">{t(\"studio_sql_database\")}</Typography>\n </div>\n {isLoadingConfig ? (\n <div className=\"flex items-center justify-center p-4\">\n <CircularProgress size=\"small\"/>\n </div>\n ) : connectionConfigError ? (\n <div className=\"px-3 py-2 text-xs text-red-500 dark:text-red-400 max-w-[200px] break-words\">\n {connectionConfigError}\n </div>\n ) : (\n <>\n {availableDatabases.map(db => (\n <MenuItem key={db} dense onClick={() => handleDatabaseChange(db)} className={cls(\"text-xs\", selectedDatabase === db && \"text-primary dark:text-primary-dark\")}>\n {db}\n </MenuItem>\n ))}\n\n <div className=\"px-3 py-1.5 border-y border-hairline mb-1 mt-1\">\n <Typography variant=\"caption\" className=\"font-semibold uppercase tracking-wider text-[9px] text-text-disabled dark:text-text-disabled-dark\">{t(\"studio_sql_role\")}</Typography>\n </div>\n {connectionOwner && (\n <div className=\"px-3 py-1.5 text-xs text-text-secondary dark:text-text-secondary-dark max-w-[240px] break-words\">\n {t(\"studio_sql_role_switching_disabled\", { role: connectionOwner })}\n </div>\n )}\n {availableRoles.map(role => (\n <MenuItem key={role} dense disabled={connectionOwner !== null} onClick={() => handleRoleChange(role)} className={cls(\"text-xs\", selectedRole === role && \"text-primary dark:text-primary-dark\")}>\n {role}{role === \"postgres\" ? \" \" + t(\"studio_sql_collections_label\") : \"\"}\n </MenuItem>\n ))}\n </>\n )}\n </div>\n </Menu>\n\n <Button\n onClick={() => handleRun(editorHandle.current?.getSelectedText())}\n disabled={loading}\n size=\"small\"\n color=\"primary\"\n >\n {loading ? <CircularProgress size=\"smallest\" className=\"mr-2\"/> : <PlayIcon size={iconSize.smallest} className=\"mr-2\"/>}\n {t(\"studio_sql_run\")}\n </Button>\n </div>\n </div>\n\n <ResizablePanels\n orientation=\"vertical\"\n panelSizePercent={editorHeight}\n onPanelSizeChange={setEditorHeight}\n minPanelSizePx={100}\n firstPanel={\n <div className=\"h-full w-full relative flex flex-col min-h-0\">\n <MonacoEditor\n ref={editorHandle}\n value={sql}\n onChange={(v) => setSql(v || \"\")}\n onRun={handleRun}\n schemas={schemas}\n />\n </div>\n }\n secondPanel={\n <div className=\"h-full w-full flex flex-col bg-surface-well overflow-hidden min-h-0\">\n <div className={cls(\"p-2 px-4 bg-surface-raised border-b shrink-0 flex items-center\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold text-text-disabled dark:text-text-disabled-dark uppercase tracking-widest text-[10px]\">{t(\"studio_sql_query_results\")}</Typography>\n </div>\n {!loading && results && activeTab.ranAs && activeTab.lastExecutedConnection?.role && activeTab.ranAs !== activeTab.lastExecutedConnection.role && (\n <div className=\"px-4 pt-2 shrink-0\">\n <Alert color=\"warning\" size=\"small\">\n {t(\"studio_sql_ran_as_other_role\", { ranAs: activeTab.ranAs, role: activeTab.lastExecutedConnection.role })}\n </Alert>\n </div>\n )}\n {!loading && activeTab.notices.length > 0 && (\n <div className=\"px-4 pt-2 flex flex-col gap-1 shrink-0\">\n {activeTab.notices.map((notice, index) => (\n <Alert key={index} color=\"warning\" size=\"small\">\n {`${notice.severity}: ${notice.message}`}\n </Alert>\n ))}\n </div>\n )}\n <div className=\"flex-grow flex flex-col min-h-0 overflow-hidden\">\n {renderResults()}\n </div>\n </div>\n }\n />\n\n </div>\n }\n />\n\n <Dialog open={isSaveDialogOpen} onOpenChange={setIsSaveDialogOpen}>\n <DialogTitle>{t(\"studio_sql_save_snippet\")}</DialogTitle>\n <DialogContent>\n <div className=\"py-4 flex flex-col gap-4\">\n <TextField\n label={t(\"studio_sql_snippet_name\")}\n autoFocus\n placeholder={t(\"studio_sql_snippet_name_placeholder\")}\n value={newSnippetName}\n onChange={(e) => setNewSnippetName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n handleSaveSnippet();\n }\n }}\n />\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark block\">{t(\"studio_sql_snippet_saved_local\")}</Typography>\n </div>\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={() => setIsSaveDialogOpen(false)}>{t(\"studio_sql_cancel\")}</Button>\n <Button onClick={handleSaveSnippet} color=\"primary\" disabled={!newSnippetName.trim()}>{t(\"studio_sql_save\")}</Button>\n </DialogActions>\n </Dialog>\n {/* Confirmation Dialog */}\n <ConfirmationDialog\n open={isConfirmDialogOpen}\n onCancel={() => setIsConfirmDialogOpen(false)}\n title={t(\"studio_sql_dangerous_operation\")}\n body={t(\"studio_sql_dangerous_operation_body\")}\n onAccept={() => {\n if (pendingAction) pendingAction();\n setIsConfirmDialogOpen(false);\n }}\n />\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
3
3
|
import { Chip, CircularProgress, IconButton, ResizablePanels, SearchBar, Tooltip, Typography, cls, defaultBorderMixin, useIsDarkMode } from "@rebasepro/ui";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { isPostgresCollectionConfig } from "@rebasepro/types";
|
|
6
|
-
import { resolveCollectionRelations } from "@rebasepro/common";
|
|
6
|
+
import { getTableName, resolveCollectionRelations } from "@rebasepro/common";
|
|
7
7
|
import { Background, BackgroundVariant, BaseEdge, Controls, EdgeLabelRenderer, Handle, MarkerType, MiniMap, Position, ReactFlow, ReactFlowProvider, applyEdgeChanges, applyNodeChanges, getSmoothStepPath, useNodesInitialized, useReactFlow } from "@xyflow/react";
|
|
8
8
|
import "@xyflow/react/dist/style.css";
|
|
9
9
|
import dagre from "dagre";
|
|
@@ -80,16 +80,6 @@ var getLayoutedElements = (nodes, edges) => {
|
|
|
80
80
|
edges
|
|
81
81
|
};
|
|
82
82
|
};
|
|
83
|
-
var TYPE_LABELS = {
|
|
84
|
-
string: "varchar",
|
|
85
|
-
number: "integer",
|
|
86
|
-
boolean: "boolean",
|
|
87
|
-
date: "timestamp",
|
|
88
|
-
map: "jsonb",
|
|
89
|
-
array: "jsonb",
|
|
90
|
-
relation: "FK"
|
|
91
|
-
};
|
|
92
|
-
var getTypeLabel = (type) => TYPE_LABELS[type] ?? type;
|
|
93
83
|
var getCardinalityLabel = (cardinality, direction) => {
|
|
94
84
|
if (cardinality === "many") return "M:N";
|
|
95
85
|
if (direction === "inverse") return "1:1 ←";
|
|
@@ -113,7 +103,7 @@ var extractColumns = (collection) => {
|
|
|
113
103
|
columns.push({
|
|
114
104
|
name: propName,
|
|
115
105
|
type: prop.type,
|
|
116
|
-
typeLabel:
|
|
106
|
+
typeLabel: prop.type,
|
|
117
107
|
isPrimaryKey: isPk,
|
|
118
108
|
isForeignKey: false,
|
|
119
109
|
isRequired: Boolean(prop.validation?.required),
|
|
@@ -168,7 +158,7 @@ var buildGraph = (collections, liveRls) => {
|
|
|
168
158
|
};
|
|
169
159
|
for (const collection of collections) {
|
|
170
160
|
if (!isPostgresCollectionConfig(collection)) continue;
|
|
171
|
-
const tableName = collection
|
|
161
|
+
const tableName = getTableName(collection);
|
|
172
162
|
const nodeId = `table-${tableName}`;
|
|
173
163
|
tableToNodeId.set(tableName, nodeId);
|
|
174
164
|
const columns = extractColumns(collection);
|
|
@@ -197,7 +187,7 @@ var buildGraph = (collections, liveRls) => {
|
|
|
197
187
|
const processedJunctions = /* @__PURE__ */ new Set();
|
|
198
188
|
for (const collection of collections) {
|
|
199
189
|
if (!isPostgresCollectionConfig(collection)) continue;
|
|
200
|
-
const tableName = collection
|
|
190
|
+
const tableName = getTableName(collection);
|
|
201
191
|
const sourceNodeId = tableToNodeId.get(tableName);
|
|
202
192
|
if (!sourceNodeId) continue;
|
|
203
193
|
let resolvedRelations;
|
|
@@ -214,7 +204,7 @@ var buildGraph = (collections, liveRls) => {
|
|
|
214
204
|
continue;
|
|
215
205
|
}
|
|
216
206
|
if (!isPostgresCollectionConfig(targetCollection)) continue;
|
|
217
|
-
const targetTable = targetCollection
|
|
207
|
+
const targetTable = getTableName(targetCollection);
|
|
218
208
|
const targetNodeId = tableToNodeId.get(targetTable);
|
|
219
209
|
if (!targetNodeId) continue;
|
|
220
210
|
if (rel.kind !== "belongsTo" && rel.kind !== "manyToMany") continue;
|
|
@@ -705,6 +695,7 @@ var RelationEdge = memo(RelationEdgeInner);
|
|
|
705
695
|
var nodeTypes = { tableNode: TableNode };
|
|
706
696
|
var edgeTypes = { relationEdge: RelationEdge };
|
|
707
697
|
function SchemaVisualizerCanvas({ collections }) {
|
|
698
|
+
const { t } = useTranslation();
|
|
708
699
|
const reactFlowInstance = useReactFlow();
|
|
709
700
|
const liveRls = useLiveRlsTables();
|
|
710
701
|
const isDark = useIsDarkMode();
|
|
@@ -782,7 +773,7 @@ function SchemaVisualizerCanvas({ collections }) {
|
|
|
782
773
|
const filteredCollections = useMemo(() => {
|
|
783
774
|
if (!searchQuery.trim()) return postgresCollections;
|
|
784
775
|
const q = searchQuery.toLowerCase();
|
|
785
|
-
return postgresCollections.filter((c) => c.name.toLowerCase().includes(q) || c.
|
|
776
|
+
return postgresCollections.filter((c) => c.name.toLowerCase().includes(q) || getTableName(c).toLowerCase().includes(q) || c.slug?.toLowerCase().includes(q));
|
|
786
777
|
}, [postgresCollections, searchQuery]);
|
|
787
778
|
const junctionNodes = useMemo(() => nodes.filter((n) => n.data.isJunction), [nodes]);
|
|
788
779
|
/**
|
|
@@ -793,8 +784,7 @@ function SchemaVisualizerCanvas({ collections }) {
|
|
|
793
784
|
* agreeing.
|
|
794
785
|
*/
|
|
795
786
|
const rlsEnabledFor = useCallback((collection) => {
|
|
796
|
-
const
|
|
797
|
-
const node = nodes.find((n) => n.id === `table-${table}`);
|
|
787
|
+
const node = nodes.find((n) => n.id === `table-${getTableName(collection)}`);
|
|
798
788
|
return Boolean((node?.data)?.rlsEnabled);
|
|
799
789
|
}, [nodes]);
|
|
800
790
|
const stats = useMemo(() => ({
|
|
@@ -842,7 +832,7 @@ function SchemaVisualizerCanvas({ collections }) {
|
|
|
842
832
|
children: [/* @__PURE__ */ jsx("div", {
|
|
843
833
|
className: "space-y-0.5",
|
|
844
834
|
children: filteredCollections.map((collection) => {
|
|
845
|
-
const table = collection
|
|
835
|
+
const table = getTableName(collection);
|
|
846
836
|
const nodeId = `table-${table}`;
|
|
847
837
|
return /* @__PURE__ */ jsxs("div", {
|
|
848
838
|
onClick: () => handleTableSelect(nodeId),
|
|
@@ -968,18 +958,23 @@ function SchemaVisualizerCanvas({ collections }) {
|
|
|
968
958
|
className: "flex-grow flex flex-col min-w-0 h-full w-full bg-surface-card",
|
|
969
959
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
970
960
|
className: cls("flex items-center justify-between pr-2 border-b bg-surface-card min-h-[46px]", defaultBorderMixin),
|
|
971
|
-
children: [/* @__PURE__ */
|
|
972
|
-
className: "flex
|
|
973
|
-
children: /* @__PURE__ */ jsx(Typography, {
|
|
961
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
962
|
+
className: "flex flex-col min-w-0 px-4",
|
|
963
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
974
964
|
variant: "subtitle2",
|
|
975
965
|
className: "font-medium text-text-primary dark:text-text-primary-dark",
|
|
976
966
|
children: "Schema Visualizer"
|
|
977
|
-
})
|
|
967
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
968
|
+
variant: "caption",
|
|
969
|
+
className: "text-text-secondary dark:text-text-secondary-dark truncate",
|
|
970
|
+
children: t("studio_schema_visualizer_source")
|
|
971
|
+
})]
|
|
978
972
|
}), /* @__PURE__ */ jsxs("div", {
|
|
979
973
|
className: "flex shrink-0 items-center gap-1.5",
|
|
980
974
|
children: [/* @__PURE__ */ jsx(Tooltip, {
|
|
981
975
|
title: "Fit to view",
|
|
982
976
|
children: /* @__PURE__ */ jsx(IconButton, {
|
|
977
|
+
"aria-label": "Fit to view",
|
|
983
978
|
size: "small",
|
|
984
979
|
onClick: handleFitView,
|
|
985
980
|
children: /* @__PURE__ */ jsx("svg", {
|
|
@@ -998,6 +993,7 @@ function SchemaVisualizerCanvas({ collections }) {
|
|
|
998
993
|
}), /* @__PURE__ */ jsx(Tooltip, {
|
|
999
994
|
title: "Re-layout",
|
|
1000
995
|
children: /* @__PURE__ */ jsx(IconButton, {
|
|
996
|
+
"aria-label": "Re-layout",
|
|
1001
997
|
size: "small",
|
|
1002
998
|
onClick: relayout,
|
|
1003
999
|
children: /* @__PURE__ */ jsx("svg", {
|
|
@@ -1177,4 +1173,4 @@ var SchemaVisualizer = () => {
|
|
|
1177
1173
|
//#endregion
|
|
1178
1174
|
export { SchemaVisualizer };
|
|
1179
1175
|
|
|
1180
|
-
//# sourceMappingURL=SchemaVisualizer-
|
|
1176
|
+
//# sourceMappingURL=SchemaVisualizer-DCLVfA5t.js.map
|