diff --git a/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx b/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx
index d084a7aeb62..ec966dbe214 100644
--- a/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx
+++ b/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx
@@ -1,7 +1,13 @@
-import { AnimatePresence } from 'framer-motion'
+import { useParams } from 'common'
+import { AnimatePresence, motion } from 'framer-motion'
+import { Home, MessageCirclePlus, NotebookText, Plus } from 'lucide-react'
+import Link from 'next/link'
+import { useRouter } from 'next/router'
import { ComponentProps, ReactNode, useState } from 'react'
+import { cn, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from 'ui'
import { ProjectLayoutWithAuth } from '../ProjectLayout'
+import { EditorTabs } from '../Tabs/Tabs'
import { type ExplorerResourceType } from './ExplorerLayout.constants'
import { ExplorerNavChats } from './ExplorerNavChats'
import { ExplorerNavHome } from './ExplorerNavHome'
@@ -42,7 +48,70 @@ export const ExplorerLayout = ({ browserTitle, children, title }: ExplorerLayout
}
>
- {children}
+
+
+ }
+ newTabButton={}
+ />
+
+
{children}
+
)
}
+
+const TabClassName =
+ 'flex items-center justify-center min-w-(--header-height) min-h-(--header-height) hover:bg-surface-100 shrink-0 border-b'
+
+const HomeTabButton = () => {
+ const router = useRouter()
+ const { ref } = useParams()
+ const isActive = router.pathname.endsWith('/explorer')
+
+ return (
+
+
+ Open Explorer home
+
+ )
+}
+
+const NewTabButton = () => {
+ return (
+
+
+ {}}
+ initial={{ opacity: 0, scale: 0.8, x: -10 }}
+ animate={{ opacity: 1, scale: 1, x: 0 }}
+ transition={{ duration: 0.2 }}
+ >
+
+
+
+
+
+
+ New notebook
+
+
+
+ New chat
+
+
+
+ )
+}
diff --git a/apps/studio/components/layouts/Tabs/Tabs.tsx b/apps/studio/components/layouts/Tabs/Tabs.tsx
index 9ee549892b3..73b0dd1c43a 100644
--- a/apps/studio/components/layouts/Tabs/Tabs.tsx
+++ b/apps/studio/components/layouts/Tabs/Tabs.tsx
@@ -11,7 +11,7 @@ import { useParams } from 'common'
import { AnimatePresence, motion } from 'framer-motion'
import { Plus, X } from 'lucide-react'
import { useRouter } from 'next/router'
-import { useState } from 'react'
+import { ReactNode, useState } from 'react'
import {
cn,
ContextMenu,
@@ -37,7 +37,19 @@ import {
type TabCloseConfirmation,
} from '@/state/tabs'
-export const EditorTabs = () => {
+interface EditorTabsProps {
+ customTabs?: ReactNode
+ newTabButton?: ReactNode
+ isCollapseButtonHidden?: boolean
+}
+
+// [Joshen] Will be adjusting this component to support Explorer
+// Will require quite a bit of cleaning up once Explorer supercedes SQL Editor
+export const EditorTabs = ({
+ customTabs,
+ newTabButton,
+ isCollapseButtonHidden,
+}: EditorTabsProps) => {
const { ref, id } = useParams()
const router = useRouter()
const { setLastVisitedSnippet, setLastVisitedTable } = useDashboardHistory()
@@ -175,7 +187,10 @@ export const EditorTabs = () => {
value={hasNewTab ? 'new' : (tabs.activeTab ?? undefined)}
onValueChange={handleTabChange}
>
-
+ {!isCollapseButtonHidden && }
+
+ {customTabs}
+
{
)}
- {!hasNewTab && (
-
- router.push(
- `/project/${router.query.ref}/${editor === 'table' ? 'editor' : 'sql'}/new?skip=true`
- )
- }
- initial={{ opacity: 0, scale: 0.8, x: -10 }}
- animate={{ opacity: 1, scale: 1, x: 0 }}
- transition={{ duration: 0.2 }}
- >
-
-
- )}
+ {!hasNewTab &&
+ (newTabButton ?? (
+
+ router.push(
+ `/project/${router.query.ref}/${editor === 'table' ? 'editor' : 'sql'}/new?skip=true`
+ )
+ }
+ initial={{ opacity: 0, scale: 0.8, x: -10 }}
+ animate={{ opacity: 1, scale: 1, x: 0 }}
+ transition={{ duration: 0.2 }}
+ >
+
+
+ ))}
diff --git a/apps/studio/components/layouts/editors/EditorsLayout.hooks.ts b/apps/studio/components/layouts/editors/EditorsLayout.hooks.ts
index 1495cc2b374..3b0f0cdb705 100644
--- a/apps/studio/components/layouts/editors/EditorsLayout.hooks.ts
+++ b/apps/studio/components/layouts/editors/EditorsLayout.hooks.ts
@@ -1,13 +1,15 @@
import { useParams } from 'common'
import { usePathname } from 'next/navigation'
-export function useEditorType(): 'table' | 'sql' | undefined {
+export type EditorType = 'table' | 'sql' | 'explorer' | undefined
+
+export function useEditorType(): EditorType {
const pathname = usePathname()
const { ref } = useParams()
- return pathname?.includes(`/project/${ref}/editor`)
- ? 'table'
- : pathname?.includes(`/project/${ref}/sql`)
- ? 'sql'
- : undefined
+ if (pathname?.includes(`/project/${ref}/editor`)) return 'table'
+ if (pathname?.includes(`/project/${ref}/sql`)) return 'sql'
+ if (pathname?.includes(`/project/${ref}/explorer`)) return 'explorer'
+
+ return undefined
}
diff --git a/apps/studio/state/tabs.tsx b/apps/studio/state/tabs.tsx
index 85c1dc9d56a..dc8ba7e00fe 100644
--- a/apps/studio/state/tabs.tsx
+++ b/apps/studio/state/tabs.tsx
@@ -13,11 +13,13 @@ import { proxy, subscribe, useSnapshot } from 'valtio'
import { buildTableEditorUrl } from '@/components/grid/SupabaseGrid.utils'
import type { SqlSnippetSource } from '@/components/interfaces/SQLEditor/querySource'
-import { ENTITY_TYPE } from '@/data/entity-types/entity-type-constants'
+import type { EditorType } from '@/components/layouts/editors/EditorsLayout.hooks'
+import type { ENTITY_TYPE } from '@/data/entity-types/entity-type-constants'
export const editorEntityTypes = {
table: ['r', 'v', 'm', 'f', 'p'],
sql: ['sql'],
+ explorer: ['notebook'],
}
export type TabType = ENTITY_TYPE | 'sql'
@@ -469,7 +471,7 @@ export function createTabsState(projectRef: string) {
}: {
id: string
router: NextRouter
- editor?: 'sql' | 'table'
+ editor?: EditorType
onClose?: (id: string) => void
onClearDashboardHistory: () => void
}) => {