-
diff --git a/apps/studio/components/interfaces/Auth/EmailTemplates/CustomEmailTemplateRestrictionAdmonition.tsx b/apps/studio/components/interfaces/Auth/EmailTemplates/CustomEmailTemplateRestrictionAdmonition.tsx
index 63a04b6ced915..23c6ba82701ea 100644
--- a/apps/studio/components/interfaces/Auth/EmailTemplates/CustomEmailTemplateRestrictionAdmonition.tsx
+++ b/apps/studio/components/interfaces/Auth/EmailTemplates/CustomEmailTemplateRestrictionAdmonition.tsx
@@ -28,7 +28,7 @@ export const CustomEmailTemplateRestrictionAdmonition = () => {
Set up SMTP
@@ -37,7 +37,7 @@ export const CustomEmailTemplateRestrictionAdmonition = () => {
}
/>
diff --git a/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus/ReplicationPipelineStatus.tsx b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus/ReplicationPipelineStatus.tsx
index a670c455cf29a..866975ddd27e4 100644
--- a/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus/ReplicationPipelineStatus.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus/ReplicationPipelineStatus.tsx
@@ -419,7 +419,7 @@ export const ReplicationPipelineStatus = () => {
}
disabled={isAnyRestartInProgress || showDisabledState || isPipelineError}
loading={isAnyRestartInProgress}
@@ -435,7 +435,7 @@ export const ReplicationPipelineStatus = () => {
}
- className="w-7 rounded-l-none -ml-px"
+ className="w-7 rounded-l-none -ml-px focus-visible:z-10"
disabled={showDisabledState || isPipelineError}
/>
diff --git a/apps/studio/components/interfaces/Explorer/ExplorerHome.tsx b/apps/studio/components/interfaces/Explorer/ExplorerHome.tsx
index b26f10ed33ed3..786f4ad6fd220 100644
--- a/apps/studio/components/interfaces/Explorer/ExplorerHome.tsx
+++ b/apps/studio/components/interfaces/Explorer/ExplorerHome.tsx
@@ -1,15 +1,14 @@
-import { untrustedSql } from '@supabase/pg-meta'
import { MessageCirclePlus, NotebookText, SquareCode } from 'lucide-react'
import { useState } from 'react'
-import { useCreateNotebook } from './hooks'
+import { useCreateNotebook, useCreateQuery } from './hooks'
import { ActionCard } from '@/components/layouts/Tabs/ActionCard'
import { AssistantChatForm } from '@/components/ui/AIAssistantPanel/AssistantChatForm'
-import { generateUuid } from '@/lib/api/snippets.browser'
import { AssistantModel } from '@/state/ai-assistant-state'
export const ExplorerHome = () => {
const { createNotebook } = useCreateNotebook()
+ const { createQuery } = useCreateQuery()
const [value, setValue] = useState
('')
const [selectedModel, setSelectedModal] = useState('gpt-5.4-nano')
@@ -52,19 +51,7 @@ export const ExplorerHome = () => {
title="Run SQL"
description="Write and run an ad-hoc query"
bgColor="bg-blue-500"
- onClick={() =>
- createNotebook({
- name: 'SQL query',
- cells: [
- {
- _tag: 'database_cell',
- id: generateUuid(),
- unchecked_sql: untrustedSql(''),
- row_limit: 100,
- },
- ],
- })
- }
+ onClick={createQuery}
/>
diff --git a/apps/studio/components/interfaces/Explorer/ExplorerQueryTabCoordinator.tsx b/apps/studio/components/interfaces/Explorer/ExplorerQueryTabCoordinator.tsx
new file mode 100644
index 0000000000000..14e8e58284106
--- /dev/null
+++ b/apps/studio/components/interfaces/Explorer/ExplorerQueryTabCoordinator.tsx
@@ -0,0 +1,45 @@
+import { useParams } from 'common'
+import { useContext, useEffect } from 'react'
+
+import { explorerQueryState } from '@/state/explorer-query'
+import { TabsStateContext } from '@/state/tabs'
+
+/**
+ * Owns local query-draft cleanup and close confirmation for every Explorer page,
+ * including Explorer home where no individual query editor is mounted.
+ */
+export const ExplorerQueryTabCoordinator = () => {
+ const { ref } = useParams()
+ const tabs = useContext(TabsStateContext)
+
+ useEffect(() => {
+ return tabs.registerTabTypeHandler('query', {
+ confirmClose: (queryTabs) => {
+ const populatedDraftCount = queryTabs.filter((tab) => {
+ const queryId = tab.metadata?.queryId
+ if (!ref || !queryId) return false
+
+ explorerQueryState.restoreDraft({ id: queryId, projectRef: ref })
+
+ return explorerQueryState.drafts[queryId]?.uncheckedSql.trim().length > 0
+ }).length
+
+ if (populatedDraftCount === 0) return null
+
+ return {
+ title: populatedDraftCount === 1 ? 'Discard query?' : 'Discard queries?',
+ description:
+ populatedDraftCount === 1
+ ? 'This ad-hoc query is stored only in this browser. Closing the tab will discard it.'
+ : `These ${populatedDraftCount} ad-hoc queries are stored only in this browser. Closing their tabs will discard them.`,
+ }
+ },
+ onClose: (tab) => {
+ const queryId = tab.metadata?.queryId
+ if (ref && queryId) explorerQueryState.removeDraft({ id: queryId, projectRef: ref })
+ },
+ })
+ }, [ref, tabs])
+
+ return null
+}
diff --git a/apps/studio/components/interfaces/Explorer/NotebookEditor.tsx b/apps/studio/components/interfaces/Explorer/NotebookEditor.tsx
index bb680a21b1d16..6cc223ce9e270 100644
--- a/apps/studio/components/interfaces/Explorer/NotebookEditor.tsx
+++ b/apps/studio/components/interfaces/Explorer/NotebookEditor.tsx
@@ -14,7 +14,6 @@ import {
} from '@dnd-kit/sortable'
import { useParams } from 'common'
import { Notebook, NotebookText, Play, Save } from 'lucide-react'
-import { useEffect, useEffectEvent } from 'react'
import { AiIconAnimation, Button } from 'ui'
import { EmptyStatePresentational } from 'ui-patterns/EmptyStatePresentational'
@@ -63,19 +62,6 @@ export const NotebookEditor = () => {
snap.updateCells({ id, cells: arrayMove([...cells], oldIndex, newIndex) })
}
- const registerTab = useEffectEvent(() => {
- if (!id) return
- tabs.addTab({
- id: createTabId('notebook', { id }),
- type: 'notebook',
- label: name ?? 'New Notebook',
- metadata: { notebookId: id },
- isPreview: false,
- })
- })
-
- useEffect(() => registerTab(), [id])
-
return (