feat: persistent Db manager state in URI (#8134)

* DB Manager state in URL

* Fix state not saving

* shorted uri params

* infer db_type from prefix

* Revert "infer db_type from prefix"

This reverts commit 7415fbed3d.

* dbm syntax

* infer database type

* Omit main and public

* remove legacy #dbmanager:

* Preserve hash

* nit

* Fix remaining dbManagerDrawer objects
This commit is contained in:
Diego Imbert
2026-03-04 11:46:34 +01:00
committed by GitHub
parent 02b4e222ea
commit e5cc2f68dd
21 changed files with 266 additions and 124 deletions

View File

@@ -6,27 +6,20 @@
import DrawerContent from './common/drawer/DrawerContent.svelte'
import Select from './select/Select.svelte'
import { ArrowLeft, Expand, LoaderCircle, Minimize, RefreshCcw } from 'lucide-svelte'
import type { DbInput } from './dbTypes'
import DBManagerContent from './DBManagerContent.svelte'
import { resource } from 'runed'
import { untrack } from 'svelte'
import type { DbManagerUriState } from './dbManagerDrawerModel.svelte'
interface Props {
uriState: DbManagerUriState
/** Z-index offset for the drawer, useful when opening from within modals */
offset?: number
}
let { offset = 0 }: Props = $props()
let { uriState, offset = 0 }: Props = $props()
let input: DbInput | undefined = $state()
let open = $derived(!!input)
// For datatable inputs, track the selected datatable separately
let selectedDatatable = $state<string | undefined>(undefined)
// Check if input is a datatable type
const isDatatableInput = $derived(
input?.type === 'database' && input.resourcePath.startsWith('datatable://')
)
let open = $derived(uriState.open)
// Load available datatables when drawer opens with datatable input
const datatables = resource<string[]>([], async () => {
@@ -39,16 +32,6 @@
}
})
// Computed input that updates when selectedDatatable changes
const effectiveInput: DbInput | undefined = $derived.by(() => {
if (!input) return undefined
if (!isDatatableInput || !selectedDatatable) return input
return {
...input,
resourcePath: `datatable://${selectedDatatable}`
}
})
const datatableItems = $derived(
datatables.current.map((dt) => ({
value: dt,
@@ -56,32 +39,26 @@
}))
)
export function openDrawer(nInput: DbInput) {
input = nInput
if (isDatatableInput) {
datatables.refetch()
// Refetch datatables when switching to a datatable input
$effect(() => {
if (uriState.isDatatableInput) {
untrack(() => datatables.refetch())
}
// If it's a datatable input, extract the datatable name for the selector
if (nInput.type === 'database' && nInput.resourcePath.startsWith('datatable://')) {
selectedDatatable = nInput.resourcePath.replace('datatable://', '')
datatables.refetch()
} else {
selectedDatatable = undefined
}
}
export function closeDrawer() {
input = undefined
selectedDatatable = undefined
})
function handleClose() {
uriState.closeDrawer()
dbManagerContent?.clearReplResult()
if (window.location.hash.startsWith('#dbmanager:'))
history.replaceState('', document.title, window.location.href.replace(/#dbmanager:.*$/, ''))
}
let windowWidth = $state(window.innerWidth)
let expand = $state(false)
$effect(() => {
if (!open) expand = false
if (!open) {
expand = false
uriState.closeDrawer()
}
})
let dbManagerContent: DBManagerContent | undefined = $state()
@@ -96,7 +73,7 @@
size={expand ? `${windowWidth}px` : '1200px'}
preventEscape
{offset}
on:close={closeDrawer}
on:close={handleClose}
>
<DrawerContent
title={hasReplResult ? 'Query Result' : 'Database Manager'}
@@ -104,18 +81,24 @@
if (hasReplResult) {
dbManagerContent?.clearReplResult()
} else {
closeDrawer()
handleClose()
}
}}
CloseIcon={hasReplResult ? ArrowLeft : undefined}
noPadding
id="db-manager-drawer"
>
{#if effectiveInput && $workspaceStore}
{#key selectedDatatable}
<DBManagerContent bind:this={dbManagerContent} input={effectiveInput} bind:hasReplResult>
{#if uriState.effectiveInput && $workspaceStore}
{#key uriState.selectedDatatable}
<DBManagerContent
bind:this={dbManagerContent}
input={uriState.effectiveInput}
bind:hasReplResult
bind:selectedSchemaKey={uriState.selectedSchema}
bind:selectedTableKey={uriState.selectedTable}
>
{#snippet dbSelector()}
{#if isDatatableInput}
{#if uriState.isDatatableInput}
{#if datatables.loading}
<div class="flex items-center gap-2 text-tertiary ml-2">
<LoaderCircle size={14} class="animate-spin" />
@@ -125,7 +108,7 @@
<Select
transformInputSelectedText={(s) => `Datatable: ${s}`}
items={datatableItems}
bind:value={selectedDatatable}
bind:value={uriState.selectedDatatable}
placeholder="Select data table"
size="md"
/>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { WorkspaceService } from '$lib/gen'
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
import { workspaceStore } from '$lib/stores'
import Select from './select/Select.svelte'
import ExploreAssetButton, { assetCanBeExplored } from './ExploreAssetButton.svelte'
@@ -31,7 +31,7 @@
let datatables = usePromise(() =>
WorkspaceService.listDataTables({ workspace: $workspaceStore ?? '' })
)
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
</script>
<div class={className}>
@@ -49,7 +49,6 @@
<ExploreAssetButton
class="mt-1 w-fit"
asset={{ kind: 'datatable', path: value }}
{dbManagerDrawer}
/>
{/if}
</div>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { WorkspaceService } from '$lib/gen'
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
import { workspaceStore } from '$lib/stores'
import Select from './select/Select.svelte'
import ExploreAssetButton, { assetCanBeExplored } from './ExploreAssetButton.svelte'
@@ -31,7 +31,7 @@
let ducklakes = usePromise(() =>
WorkspaceService.listDucklakes({ workspace: $workspaceStore ?? '' })
)
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
</script>
<div class={className}>
@@ -49,7 +49,6 @@
<ExploreAssetButton
class="mt-1 w-fit"
asset={{ kind: 'ducklake', path: value }}
{dbManagerDrawer}
/>
{/if}
</div>

View File

@@ -16,9 +16,8 @@
import { isDbType } from '$lib/components/dbTypes'
import { formatAsset, type Asset } from '$lib/components/assets/lib'
import { Button, ButtonType } from '$lib/components/common'
import DbManagerDrawer from '$lib/components/DBManagerDrawer.svelte'
import S3FilePicker from '$lib/components/S3FilePicker.svelte'
import { userStore } from '$lib/stores'
import { globalDbManagerDrawer, userStore } from '$lib/stores'
import { isS3Uri } from '$lib/utils'
import { Database, File } from 'lucide-svelte'
import DucklakeIcon from './icons/DucklakeIcon.svelte'
@@ -27,7 +26,6 @@
asset,
_resourceMetadata,
s3FilePicker,
dbManagerDrawer,
onClick,
class: className = '',
noText = false,
@@ -38,7 +36,6 @@
asset: Asset
_resourceMetadata?: { resource_type?: string }
s3FilePicker?: S3FilePicker
dbManagerDrawer?: DbManagerDrawer
onClick?: () => void
class?: string
noText?: boolean
@@ -46,6 +43,8 @@
btnClasses?: string
disabled?: boolean
} = $props()
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
const assetUri = $derived(formatAsset(asset))
</script>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { ResourceService, WorkspaceService } from '$lib/gen'
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
import { workspaceStore } from '$lib/stores'
import { onMount, untrack } from 'svelte'
import AppConnect from './AppConnectDrawer.svelte'
import ResourceEditorDrawer from './ResourceEditorDrawer.svelte'
@@ -174,7 +174,6 @@
let appConnect: AppConnect | undefined = $state()
let resourceEditor: ResourceEditorDrawer | undefined = $state()
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
let hovering = $state(false)
let isDatatableSelected = $derived(value?.startsWith('datatable://') ?? false)
</script>
@@ -317,7 +316,6 @@
class="mt-1"
_resourceMetadata={{ resource_type: resourceType }}
asset={{ kind: 'resource', path: value }}
{dbManagerDrawer}
/>
{/if}
</div>

View File

@@ -148,7 +148,9 @@
(v) => {
v.maxTs ? (filters.val.max_ts = new Date(v.maxTs)) : delete filters.val.max_ts
v.minTs ? (filters.val.min_ts = new Date(v.minTs)) : delete filters.val.min_ts
v.timeframe ? (filters.val.timeframe = v.timeframe) : delete filters.val.timeframe
v.timeframe && v.timeframe !== 'Latest runs'
? (filters.val.timeframe = v.timeframe)
: delete filters.val.timeframe
}
)
let timeframe = $derived(_timeframe.val)

View File

@@ -8,7 +8,6 @@
type Props = {
s3FilePicker?: any | undefined
dbManagerDrawer?: any | undefined
resourceEditorDrawer?: ResourceEditorDrawer | undefined
resourceDataCache: Record<string, string | undefined>
asset: Asset
@@ -18,7 +17,6 @@
}
let {
s3FilePicker,
dbManagerDrawer,
resourceEditorDrawer,
resourceDataCache,
asset,
@@ -71,7 +69,6 @@
<ExploreAssetButton
{asset}
{s3FilePicker}
{dbManagerDrawer}
onClick={() => onClick?.()}
noText
_resourceMetadata={{ resource_type: resourceDataCacheValue }}

View File

@@ -14,7 +14,7 @@
} from './lib'
import { untrack } from 'svelte'
import { ResourceService, WorkspaceService } from '$lib/gen'
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
import { workspaceStore } from '$lib/stores'
import Tooltip from '../meltComponents/Tooltip.svelte'
import Tooltip2 from '../Tooltip.svelte'
import ResourceEditorDrawer from '../ResourceEditorDrawer.svelte'
@@ -48,7 +48,6 @@
let blueBgDiv: HTMLDivElement | undefined = $state()
let s3FilePicker: S3FilePicker | undefined = $state()
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
let resourceEditorDrawer: ResourceEditorDrawer | undefined = $state()
let isOpen = $state(false)
let resourceDataCache: Record<string, string | undefined> = $state({})
@@ -209,7 +208,6 @@
onClick={() => (isOpen = false)}
{asset}
{resourceDataCache}
{dbManagerDrawer}
{resourceEditorDrawer}
{s3FilePicker}
{ducklakeNotFound}

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { ResourceService, type Job } from '$lib/gen'
import { inferAssets } from '$lib/infer'
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
import { workspaceStore } from '$lib/stores'
import { usePromise } from '$lib/svelte5Utils.svelte'
import { pruneNullishArray, uniqueBy } from '$lib/utils'
import ResourceEditorDrawer from '../ResourceEditorDrawer.svelte'
@@ -64,7 +64,6 @@
})
let s3FilePicker: S3FilePicker | undefined = $state()
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
let resourceEditorDrawer: ResourceEditorDrawer | undefined = $state()
</script>
@@ -86,7 +85,6 @@
<AssetButtons
{asset}
{resourceDataCache}
{dbManagerDrawer}
{resourceEditorDrawer}
{s3FilePicker}
/>

View File

@@ -0,0 +1,207 @@
import { z } from 'zod'
import { useSearchParams } from '$lib/svelte5UtilsKit.svelte'
import type { DbInput, DbType } from './dbTypes'
import { isDbType } from './dbTypes'
/**
* Single URL param `dbm` encodes the full DB manager state:
* firstSegment~path~schema.table
*
* firstSegment:
* datatable database with datatable:// resource (resourceType always postgresql)
* ducklake ducklake connection
* postgresql, mysql, … regular database (the segment IS the resource type)
*
* schema.table (third segment, optional):
* schema.table both
* .table table only
* schema. schema only
* (omitted) neither
*
* Default schemas (omitted from URL, restored on parse):
* datatable → public
* ducklake → main
*
* Examples:
* datatable~main~.customers (schema "public" implied)
* ducklake~main~.orders (schema "main" implied)
* postgresql~$res:u/user/my_pg~public.customers
*/
const dbManagerSchema = z.object({
dbm: z.string().nullable()
})
interface ParsedDbm {
type: 'database' | 'datatable' | 'ducklake'
path: string
resType?: string
schema?: string
table?: string
}
function parseDbm(raw: unknown): ParsedDbm | null {
if (!raw || typeof raw !== 'string') return null
const parts = raw.split('~')
if (parts.length < 2 || !parts[1]) return null
const firstSeg = parts[0]
const path = parts[1]
const schemaTable = parts[2] ?? ''
let type: ParsedDbm['type']
let resType: string | undefined
if (firstSeg === 'datatable') {
type = 'datatable'
} else if (firstSeg === 'ducklake') {
type = 'ducklake'
} else if (isDbType(firstSeg)) {
type = 'database'
resType = firstSeg
} else {
return null
}
let schema: string | undefined
let table: string | undefined
if (schemaTable) {
const dotIdx = schemaTable.indexOf('.')
if (dotIdx === 0) {
table = schemaTable.slice(1) || undefined
} else if (dotIdx === schemaTable.length - 1) {
schema = schemaTable.slice(0, -1) || undefined
} else if (dotIdx > 0) {
schema = schemaTable.slice(0, dotIdx)
table = schemaTable.slice(dotIdx + 1)
}
}
// Restore default schema when omitted for datatable/ducklake
if (!schema && table && type in defaultSchemas) {
schema = defaultSchemas[type]
}
return { type, path, resType, schema, table }
}
const defaultSchemas: Record<string, string> = { datatable: 'public', ducklake: 'main' }
function buildDbm(p: ParsedDbm): string {
const firstSeg = p.type === 'database' ? p.resType! : p.type
const schema = p.schema === defaultSchemas[p.type] ? undefined : p.schema
let schemaTable = ''
if (schema && p.table) {
schemaTable = `${schema}.${p.table}`
} else if (p.table) {
schemaTable = `.${p.table}`
} else if (schema) {
schemaTable = `${schema}.`
}
return schemaTable ? `${firstSeg}~${p.path}~${schemaTable}` : `${firstSeg}~${p.path}`
}
export interface DbManagerUriState {
readonly input: DbInput | undefined
readonly effectiveInput: DbInput | undefined
readonly isDatatableInput: boolean
selectedDatatable: string | undefined
selectedSchema: string | undefined
selectedTable: string | undefined
readonly open: boolean
openDrawer: (nInput: DbInput) => void
closeDrawer: () => void
}
export function useDbManagerUriState(): DbManagerUriState {
const params = useSearchParams(dbManagerSchema)
const parsed = $derived(parseDbm(params.dbm))
let input: DbInput | undefined = $derived.by(() => {
if (!parsed) return undefined
if (parsed.type === 'ducklake') {
return {
type: 'ducklake' as const,
ducklake: parsed.path,
specificTable: parsed.table
}
}
// datatable or database
const resType = parsed.type === 'datatable' ? 'postgresql' : parsed.resType
if (!isDbType(resType ?? undefined)) return undefined
return {
type: 'database' as const,
resourceType: resType as DbType,
resourcePath: parsed.type === 'datatable' ? `datatable://${parsed.path}` : parsed.path,
specificSchema: parsed.schema,
specificTable: parsed.table
}
})
const isDatatableInput = $derived(parsed?.type === 'datatable')
function updateField(updates: Partial<ParsedDbm>) {
const p = parseDbm(params.dbm)
if (!p) return
Object.assign(p, updates)
params.dbm = buildDbm(p)
}
function openDrawer(nInput: DbInput) {
if (nInput.type === 'database') {
const isDatatable = nInput.resourcePath.startsWith('datatable://')
params.dbm = buildDbm({
type: isDatatable ? 'datatable' : 'database',
path: isDatatable ? nInput.resourcePath.slice('datatable://'.length) : nInput.resourcePath,
resType: isDatatable ? undefined : nInput.resourceType,
schema: nInput.specificSchema,
table: nInput.specificTable
})
} else {
params.dbm = buildDbm({
type: 'ducklake',
path: nInput.ducklake,
table: nInput.specificTable
})
}
}
function closeDrawer() {
params.dbm = null
}
return {
get input() {
return input
},
get effectiveInput() {
return input
},
get isDatatableInput() {
return isDatatableInput
},
get selectedDatatable() {
return parsed?.type === 'datatable' ? parsed.path : undefined
},
set selectedDatatable(v: string | undefined) {
if (v) updateField({ path: v })
},
get selectedSchema() {
return parsed?.schema
},
set selectedSchema(v: string | undefined) {
updateField({ schema: v })
},
get selectedTable() {
return parsed?.table
},
set selectedTable(v: string | undefined) {
updateField({ table: v })
},
get open() {
return !!input
},
openDrawer,
closeDrawer
}
}

View File

@@ -235,7 +235,7 @@
import type { Edge, Node } from '@xyflow/svelte'
import { getNodeColorClasses, NODE } from '../../util'
import { globalDbManagerDrawer, userStore } from '$lib/stores'
import { userStore } from '$lib/stores'
import { deepEqual } from 'fast-equals'
import { slide } from 'svelte/transition'
import AssetColumnBadges from '$lib/components/assets/AssetColumnBadges.svelte'
@@ -313,7 +313,6 @@
noText
buttonVariant="accent"
s3FilePicker={flowGraphAssetsCtx?.val.s3FilePicker}
dbManagerDrawer={globalDbManagerDrawer.val}
_resourceMetadata={cachedResourceMetadata}
/>
</div>

View File

@@ -10,7 +10,7 @@
flow: `/flows/get/${path}`,
app: `/apps/get/${path}`,
raw_app: `/apps_raw/get/${path}`,
asset: `#dbmanager:${path}`
asset: '#'
}[kind]
}
export function getFavoriteLabel(path: string, kind: FavoriteKind): string {

View File

@@ -8,14 +8,12 @@
import CustomInstanceDbWizardModal from './CustomInstanceDbWizardModal.svelte'
import { ArrowRight, TriangleAlert } from 'lucide-svelte'
import type { ConfirmationModalHandle } from '../common/confirmationModal/asyncConfirmationModal.svelte'
import DBManagerDrawer from '../DBManagerDrawer.svelte'
import type { Snippet } from 'svelte'
type Props = {
value: string | undefined
customInstanceDbs: ResourceReturn<ListCustomInstanceDbsResponse>
confirmationModal: ConfirmationModalHandle
dbManagerDrawer: DBManagerDrawer | undefined
wizardBottomHint?: Snippet | undefined
class?: string
tag?: CustomInstanceDbTag
@@ -24,7 +22,6 @@
value = $bindable(),
customInstanceDbs,
confirmationModal,
dbManagerDrawer,
wizardBottomHint,
class: className,
tag
@@ -90,7 +87,6 @@
<CustomInstanceDbWizardModal
{customInstanceDbs}
{confirmationModal}
{dbManagerDrawer}
{tag}
bottomHint={wizardBottomHint}
bind:opened={

View File

@@ -24,7 +24,6 @@
type Props = {
customInstanceDbs: ResourceReturn<ListCustomInstanceDbsResponse>
confirmationModal: ConfirmationModalHandle
dbManagerDrawer: any | undefined
bottomHint?: Snippet | undefined
opened: { status: CustomInstanceDb | undefined; dbname: string } | undefined
tag?: CustomInstanceDbTag
@@ -33,7 +32,6 @@
let {
customInstanceDbs,
confirmationModal,
dbManagerDrawer,
bottomHint,
opened = $bindable(),
tag
@@ -76,7 +74,6 @@
class="flex-1"
asset={{ kind: 'resource', path: 'CUSTOM_INSTANCE_DB/' + dbname }}
_resourceMetadata={{ resource_type: 'postgresql' }}
{dbManagerDrawer}
disabled={!$isCustomInstanceDbEnabled || !enableManageButton}
onClick={() => (opened = undefined)}
/>

View File

@@ -62,7 +62,7 @@
import { random_adj } from '../random_positive_adjetive'
import { sendUserToast } from '$lib/toast'
import { SettingService, WorkspaceService, type GetSettingsResponse } from '$lib/gen'
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
import { workspaceStore } from '$lib/stores'
import { createAsyncConfirmationModal } from '../common/confirmationModal/asyncConfirmationModal.svelte'
import ConfirmationModal from '../common/confirmationModal/ConfirmationModal.svelte'
import { resource } from 'runed'
@@ -141,7 +141,6 @@
}
let confirmationModal = createAsyncConfirmationModal()
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
let dirtyMap = $derived.by(() => {
const map: Record<string, boolean> = {}
for (let i = 0; i < tempSettings.dataTables.length; i++) {
@@ -245,7 +244,6 @@
<CustomInstanceDbSelect
class="flex-1"
{confirmationModal}
{dbManagerDrawer}
{customInstanceDbs}
bind:value={dataTable.database.resource_path}
tag="datatable"
@@ -265,7 +263,6 @@
<ExploreAssetButton
class="h-9"
asset={{ kind: 'datatable', path: dataTable.name }}
{dbManagerDrawer}
disabled
/>
</svelte:fragment>
@@ -275,7 +272,6 @@
<ExploreAssetButton
class="h-9"
asset={{ kind: 'datatable', path: dataTable.name }}
{dbManagerDrawer}
/>
{/if}
</Cell>

View File

@@ -68,7 +68,7 @@
import { SettingService, WorkspaceService } from '$lib/gen'
import type { GetSettingsResponse } from '$lib/gen'
import { globalDbManagerDrawer, workspaceStore } from '$lib/stores'
import { workspaceStore } from '$lib/stores'
import { sendUserToast } from '$lib/toast'
import ExploreAssetButton from '../ExploreAssetButton.svelte'
import Tooltip from '../Tooltip.svelte'
@@ -187,7 +187,6 @@
'Where the data is actually stored, in parquet format. You need to configure a workspace storage first'
}
let dbManagerDrawer = $derived(globalDbManagerDrawer.val)
let confirmationModal = createAsyncConfirmationModal()
</script>
@@ -293,7 +292,6 @@
bind:value={ducklake.catalog.resource_path}
{customInstanceDbs}
{confirmationModal}
{dbManagerDrawer}
tag="ducklake"
>
{#snippet wizardBottomHint()}
@@ -372,7 +370,6 @@
{:else}
<ExploreAssetButton
asset={{ kind: 'ducklake', path: ducklake.name }}
{dbManagerDrawer}
/>
{/if}
</div>

View File

@@ -14,6 +14,7 @@ import {
import { getLocalSetting, type StateStore } from './utils'
import { createState } from './svelte5Utils.svelte'
import { DEFAULT_HUB_BASE_URL } from './hub'
import type { DbManagerUriState } from './components/dbManagerDrawerModel.svelte'
export interface UserExt {
email: string
@@ -126,9 +127,7 @@ export const codeCompletionSessionEnabled = writable<boolean>(
export const usedTriggerKinds = writable<string[]>([])
export let globalDbManagerDrawer: StateStore<any | undefined> = createState({
val: undefined
})
export let globalDbManagerDrawer: StateStore<DbManagerUriState | undefined> = { val: undefined }
type SQLBaseSchema = {
[schemaKey: string]: {

View File

@@ -107,9 +107,10 @@ export function useSearchParams<S extends z.ZodType>(schema: S): SearchParamsRes
} else {
sp.set(key, serializeParam(v))
}
const hash = window.location.hash
const newUrl = sp.toString()
? `${window.location.pathname}?${sp}`
: window.location.pathname
? `${window.location.pathname}?${sp}${hash}`
: `${window.location.pathname}${hash}`
history.replaceState(history.state, '', newUrl)
},
enumerable: true,

View File

@@ -11,13 +11,7 @@
UserService,
WorkspaceService
} from '$lib/gen'
import {
capitalize,
classNames,
getModifierKey,
parseDbInputFromAssetSyntax,
sendUserToast
} from '$lib/utils'
import { capitalize, classNames, getModifierKey, sendUserToast } from '$lib/utils'
import WorkspaceMenu from '$lib/components/sidebar/WorkspaceMenu.svelte'
import SidebarContent from '$lib/components/sidebar/SidebarContent.svelte'
import CriticalAlertModal from '$lib/components/sidebar/CriticalAlertModal.svelte'
@@ -66,8 +60,8 @@
import AiChatLayout from '$lib/components/copilot/chat/AiChatLayout.svelte'
import { DEFAULT_HUB_BASE_URL } from '$lib/hub'
import DBManagerDrawer from '$lib/components/DBManagerDrawer.svelte'
import { watchOnce } from 'runed'
import { useIsDarkMode } from '$lib/components/DarkModeObserver.svelte'
import { useDbManagerUriState } from '$lib/components/dbManagerDrawerModel.svelte'
interface Props {
children?: import('svelte').Snippet
}
@@ -439,18 +433,8 @@
untrack(() => loadProtectionRules(workspace))
}
})
watchOnce(
() => globalDbManagerDrawer.val,
() => {
if (!globalDbManagerDrawer.val) return
const hash = window.location.hash
if (hash.startsWith('#dbmanager:')) {
const [_, path] = hash.split('#dbmanager:')
const dbInput = parseDbInputFromAssetSyntax(path)
if (dbInput) globalDbManagerDrawer.val?.openDrawer(dbInput)
}
}
)
globalDbManagerDrawer.val = useDbManagerUriState()
</script>
<svelte:window bind:innerWidth />
@@ -786,6 +770,6 @@
<CenteredModal title="Loading user..." loading={true}></CenteredModal>
{/if}
{#if $workspaceStore}
<DBManagerDrawer bind:this={globalDbManagerDrawer.val} />
{#if $workspaceStore && globalDbManagerDrawer.val}
<DBManagerDrawer uriState={globalDbManagerDrawer.val} />
{/if}

View File

@@ -99,7 +99,6 @@
let assets = $derived(_assets.current?.flatMap((page) => page.assets))
let s3FilePicker: S3FilePicker | undefined = $state()
let dbManagerDrawer = $derived(globalDbManagerDrawer.val) as any
let assetsUsageDropdown: AssetsUsageDrawer | undefined = $state()
let allS3Storages = resource(
@@ -192,7 +191,6 @@
<ExploreAssetButton
asset={{ kind: props.assetKind, path: item.value }}
{s3FilePicker}
{dbManagerDrawer}
btnClasses="dark:bg-surface"
/>
</div>
@@ -337,7 +335,6 @@
<ExploreAssetButton
{asset}
{s3FilePicker}
{dbManagerDrawer}
_resourceMetadata={asset.metadata}
/>
{/if}

View File

@@ -35,8 +35,7 @@
enterpriseLicense,
userStore,
workspaceStore,
userWorkspaces,
globalDbManagerDrawer
userWorkspaces
} from '$lib/stores'
import { sendUserToast } from '$lib/toast'
import {
@@ -557,8 +556,6 @@
}
})
let dbManagerDrawer = $derived(globalDbManagerDrawer.val) as any
let showTable = $derived(
tab == 'workspace' || tab == 'states' || tab == 'cache' || tab == 'theme'
)
@@ -1064,7 +1061,6 @@
{#if path && assetCanBeExplored({ kind: 'resource', path }, { resource_type }) && !$userStore?.operator}
<ExploreAssetButton
asset={{ kind: 'resource', path }}
{dbManagerDrawer}
_resourceMetadata={{ resource_type }}
class="w-24"
/>