Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
{
"permissions": {
"allow": [
"Bash(mkdir:*)"
"Bash(mkdir:*)",
"Bash(uv run:*)",
"Bash(pnpm exec tsc:*)",
"Bash(pnpm run lint)",
"Bash(for file in balanceMapSlice.ts rowDataSlice.ts bubblePlotSlice.ts)",
"Bash(do)",
"Bash(echo:*)",
"Bash(done)",
"Bash(pnpm run typecheck:*)",
"Bash(for file in src/lib/features/bubblePlot/bubblePlotSlice.ts src/lib/features/heatmap/heatmapSlice.ts src/lib/features/histogram/histogramSlice.ts)",
"Bash(pnpm test:*)",
"Bash(make typecheck:*)"
],
"deny": [],
"ask": []
Expand Down
400 changes: 200 additions & 200 deletions data/dummy_data_various_types.csv

Large diffs are not rendered by default.

Binary file modified data/dummy_data_various_types.parquet
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions smoosense-gui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
Expand Down
62 changes: 62 additions & 0 deletions smoosense-gui/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 67 additions & 0 deletions smoosense-gui/src/app/DB/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use client'

import { useEffect, Suspense } from 'react'
import { useSearchParams } from 'next/navigation'
import { pathBasename } from '@/lib/utils/pathUtils'
import FolderBrowserNavbar from '@/components/layout/FolderBrowserNavbar'
import DBContent from '@/components/db/DBContent'

function DBPageContent() {
const searchParams = useSearchParams()
const rootFolder = searchParams.get('rootFolder')

// Set document title based on rootFolder
useEffect(() => {
if (rootFolder) {
const folderName = pathBasename(rootFolder)
document.title = `${folderName} - Lance DB` || 'SmooSense - Lance DB'
} else {
document.title = 'SmooSense - Lance DB'
}
}, [rootFolder])

// If rootFolder is missing, show error
if (!rootFolder) {
return (
<div className="min-h-screen bg-background">
<FolderBrowserNavbar />
<main className="h-[calc(100vh-56px)]">
<div className="h-full flex items-center justify-center">
<div className="flex flex-col items-center justify-center space-y-4">
<h1 className="text-4xl font-bold text-destructive">
Error: Missing Database Path
</h1>
<p className="text-lg text-muted-foreground text-center max-w-md">
This page requires a <code className="bg-muted px-2 py-1 rounded">rootFolder</code> parameter in the URL.
</p>
<p className="text-sm text-muted-foreground">
Example: <code className="bg-muted px-2 py-1 rounded">/DB?rootFolder=~/data/lance</code>
</p>
</div>
</div>
</main>
</div>
)
}

return (
<div className="min-h-screen bg-background">
<FolderBrowserNavbar />
<main className="h-[calc(100vh-56px)]">
<div className="h-full flex flex-col">
<div className="flex-1">
<DBContent rootFolder={rootFolder} />
</div>
</div>
</main>
</div>
)
}

export default function DB() {
return (
<Suspense fallback={<div>Loading...</div>}>
<DBPageContent />
</Suspense>
)
}
158 changes: 158 additions & 0 deletions smoosense-gui/src/components/db/DBContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
'use client'

import { useEffect, useState } from 'react'
import { ResizablePanels } from '@/components/ui/resizable-panels'
import { Badge } from '@/components/ui/badge'
import { AlertCircle, Database, Loader2, Table } from 'lucide-react'
import { pathJoin } from '@/lib/utils/pathUtils'
import TablePreview from './TablePreview'

export interface TableInfo {
name: string
cnt_rows: number | null
cnt_columns: number | null
cnt_versions: number | null
cnt_indices: number | null
}

function TablesList({
tables,
selectedTable,
onTableClick,
onTableDoubleClick
}: {
tables: TableInfo[]
selectedTable: string | null
onTableClick: (tableName: string) => void
onTableDoubleClick: (tableName: string) => void
}) {
return (
<div className="h-full overflow-auto p-4">
<div className="space-y-2">
{tables.map((table) => (
<div
key={table.name}
onClick={() => onTableClick(table.name)}
onDoubleClick={() => onTableDoubleClick(table.name)}
className={`flex items-center justify-between p-3 rounded-lg border bg-card hover:bg-accent cursor-pointer transition-colors ${
selectedTable === table.name ? 'border-primary bg-accent' : ''
}`}
>
<div className="flex items-center gap-2">
<Table className="h-4 w-4 text-muted-foreground" />
<span className="font-medium">{table.name}</span>
</div>
<div className="flex items-center gap-2">
{table.cnt_rows !== null && table.cnt_columns !== null && (
<Badge variant="secondary" className="text-xs">
{table.cnt_rows.toLocaleString()} × {table.cnt_columns}
</Badge>
)}
{table.cnt_versions !== null && (
<Badge variant="outline" className="text-xs">
v {table.cnt_versions}
</Badge>
)}
</div>
</div>
))}
</div>
</div>
)
}

export default function DBContent({ rootFolder }: { rootFolder: string }) {
const [tables, setTables] = useState<TableInfo[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [selectedTable, setSelectedTable] = useState<string | null>(null)

useEffect(() => {
if (!rootFolder) return

const fetchTables = async () => {
setLoading(true)
setError(null)
try {
const response = await fetch(`/api/lance/list-tables?rootFolder=${encodeURIComponent(rootFolder)}`)
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'Failed to fetch tables')
}
const data = await response.json()
setTables(data)
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load tables')
} finally {
setLoading(false)
}
}

fetchTables()
}, [rootFolder])

const handleTableClick = (tableName: string) => {
setSelectedTable(tableName)
}

const handleTableDoubleClick = (tableName: string) => {
const tablePath = pathJoin(rootFolder, `${tableName}.lance`)
const url = `./Table?tablePath=${tablePath}`
window.open(url, '_blank')
}

if (loading) {
return (
<div className="h-full flex items-center justify-center">
<div className="flex items-center space-x-2">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
<span className="text-lg text-muted-foreground">Loading tables...</span>
</div>
</div>
)
}

if (error) {
return (
<div className="h-full flex items-center justify-center p-6">
<div className="text-center space-y-2">
<AlertCircle className="h-12 w-12 mx-auto text-destructive" />
<h3 className="text-lg font-semibold text-destructive">Error Loading Tables</h3>
<p className="text-sm text-muted-foreground">{error}</p>
</div>
</div>
)
}

if (tables.length === 0) {
return (
<div className="h-full flex items-center justify-center p-6">
<div className="text-center space-y-2">
<Database className="h-12 w-12 mx-auto text-muted-foreground" />
<h3 className="text-lg font-semibold">No Tables Found</h3>
<p className="text-sm text-muted-foreground">
The Lance database at <code className="bg-muted px-2 py-1 rounded">{rootFolder}</code> does not contain any tables.
</p>
</div>
</div>
)
}

// Find the selected table's info
const selectedTableInfo = selectedTable ? tables.find(t => t.name === selectedTable) : null

return (
<div className="h-full w-full">
<ResizablePanels
direction="horizontal"
defaultSizes={[30, 70]}
minSize={20}
maxSize={80}
className="h-full"
>
<TablesList tables={tables} selectedTable={selectedTable} onTableClick={handleTableClick} onTableDoubleClick={handleTableDoubleClick} />
<TablePreview rootFolder={rootFolder} tableName={selectedTable} tableInfo={selectedTableInfo} />
</ResizablePanels>
</div>
)
}
Loading