Skip to content

Commit 6359ae1

Browse files
committed
feat: update dependencies and improve SQL editor functionality
- Updated version and release date in package.json - Changed script source in VitePress config - Added shareable query URL functionality in SqlEditor - Enhanced DuckUITable with sorting capabilities - Implemented error boundary for table rendering in SqlTab - Integrated URL query loading in WorkspaceTabs - Created useQueryFromURL hook for handling URL parameters - Improved error handling in resultToJSON function, solved #17, #18, #19
1 parent e560b08 commit 6359ae1

File tree

10 files changed

+509
-305
lines changed

10 files changed

+509
-305
lines changed

bun.lock

Lines changed: 204 additions & 189 deletions
Large diffs are not rendered by default.

docs/.vitepress/config.mts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ export default defineConfig({
1919
'script',
2020
{
2121
defer: '',
22-
src: 'https://wt.yaat.io/s.js',
23-
'data-org': 'yaat',
22+
src: 'https://yaat.io/s.js'
2423
}
2524
]
2625
],

package.json

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "duck-ui",
33
"private": true,
4-
"version": "0.0.18",
5-
"release_date": "2025-10-24",
4+
"version": "0.0.19",
5+
"release_date": "2025-12-02",
66
"type": "module",
77
"scripts": {
88
"dev": "vite",
@@ -13,32 +13,32 @@
1313
"dependencies": {
1414
"@dnd-kit/core": "^6.3.1",
1515
"@dnd-kit/sortable": "^10.0.0",
16-
"@duckdb/duckdb-wasm": "1.30.0",
16+
"@duckdb/duckdb-wasm": "1.31.1-dev31.0",
1717
"@hookform/resolvers": "^5.2.2",
1818
"@radix-ui/react-accordion": "^1.2.12",
1919
"@radix-ui/react-alert-dialog": "^1.1.15",
20-
"@radix-ui/react-aspect-ratio": "^1.1.7",
20+
"@radix-ui/react-aspect-ratio": "^1.1.8",
2121
"@radix-ui/react-checkbox": "^1.3.3",
2222
"@radix-ui/react-collapsible": "^1.1.12",
2323
"@radix-ui/react-context-menu": "^2.2.16",
2424
"@radix-ui/react-dialog": "^1.1.15",
2525
"@radix-ui/react-dropdown-menu": "^2.1.16",
2626
"@radix-ui/react-hover-card": "^1.1.15",
27-
"@radix-ui/react-label": "^2.1.7",
27+
"@radix-ui/react-label": "^2.1.8",
2828
"@radix-ui/react-menubar": "^1.1.16",
2929
"@radix-ui/react-navigation-menu": "^1.2.14",
3030
"@radix-ui/react-popover": "^1.1.15",
31-
"@radix-ui/react-progress": "^1.1.7",
31+
"@radix-ui/react-progress": "^1.1.8",
3232
"@radix-ui/react-radio-group": "^1.3.8",
3333
"@radix-ui/react-scroll-area": "^1.2.10",
3434
"@radix-ui/react-select": "^2.2.6",
35-
"@radix-ui/react-separator": "^1.1.7",
36-
"@radix-ui/react-slot": "^1.2.3",
35+
"@radix-ui/react-separator": "^1.1.8",
36+
"@radix-ui/react-slot": "^1.2.4",
3737
"@radix-ui/react-switch": "^1.2.6",
3838
"@radix-ui/react-tabs": "^1.1.13",
3939
"@radix-ui/react-toast": "^1.2.15",
4040
"@radix-ui/react-tooltip": "^1.2.8",
41-
"@tailwindcss/vite": "^4.1.14",
41+
"@tailwindcss/vite": "^4.1.17",
4242
"@tanstack/react-table": "^8.21.3",
4343
"@tanstack/react-virtual": "^3.13.12",
4444
"buffer": "^6.0.3",
@@ -47,9 +47,9 @@
4747
"cmdk": "^1.1.1",
4848
"date-fns": "^4.1.0",
4949
"echarts": "^6.0.0",
50-
"echarts-for-react": "^3.0.2",
50+
"echarts-for-react": "^3.0.5",
5151
"fflate": "^0.8.2",
52-
"framer-motion": "^12.23.24",
52+
"framer-motion": "^12.23.25",
5353
"html2canvas": "^1.4.1",
5454
"lodash": "^4.17.21",
5555
"lucide-react": "^0.546.0",
@@ -59,38 +59,38 @@
5959
"react-dom": "^19.2.0",
6060
"react-dropzone": "^14.3.8",
6161
"react-error-boundary": "^6.0.0",
62-
"react-hook-form": "^7.65.0",
62+
"react-hook-form": "^7.67.0",
6363
"react-resizable-panels": "^3.0.6",
64-
"react-router": "^7.9.4",
65-
"recharts": "^3.3.0",
64+
"react-router": "^7.9.6",
65+
"recharts": "^3.5.1",
6666
"sonner": "^2.0.7",
6767
"sql-formatter": "^15.6.10",
68-
"tailwind-merge": "^3.3.1",
68+
"tailwind-merge": "^3.4.0",
6969
"tailwindcss-animate": "^1.0.7",
7070
"vaul": "^1.1.2",
7171
"xlsx": "^0.18.5",
72-
"zod": "^4.1.12",
73-
"zustand": "^5.0.8"
72+
"zod": "^4.1.13",
73+
"zustand": "^5.0.9"
7474
},
7575
"devDependencies": {
76-
"@eslint/js": "^9.38.0",
77-
"@types/lodash": "^4.17.20",
78-
"@types/node": "^24.8.1",
79-
"@types/papaparse": "^5.3.16",
80-
"@types/react": "^19.2.2",
81-
"@types/react-dom": "^19.2.2",
82-
"@vitejs/plugin-react": "^5.0.4",
83-
"autoprefixer": "^10.4.21",
84-
"esbuild": "^0.25.11",
85-
"eslint": "^9.38.0",
86-
"eslint-plugin-react-hooks": "^7.0.0",
76+
"@eslint/js": "^9.39.1",
77+
"@types/lodash": "^4.17.21",
78+
"@types/node": "^24.10.1",
79+
"@types/papaparse": "^5.5.1",
80+
"@types/react": "^19.2.7",
81+
"@types/react-dom": "^19.2.3",
82+
"@vitejs/plugin-react": "^5.1.1",
83+
"autoprefixer": "^10.4.22",
84+
"esbuild": "^0.25.12",
85+
"eslint": "^9.39.1",
86+
"eslint-plugin-react-hooks": "^7.0.1",
8787
"eslint-plugin-react-refresh": "^0.4.24",
88-
"globals": "^16.4.0",
88+
"globals": "^16.5.0",
8989
"postcss": "^8.5.6",
90-
"tailwindcss": "^4.1.14",
90+
"tailwindcss": "^4.1.17",
9191
"typescript": "~5.9.3",
92-
"typescript-eslint": "^8.46.1",
93-
"vite": "^7.1.10"
92+
"typescript-eslint": "^8.48.1",
93+
"vite": "^7.2.6"
9494
},
9595
"description": "Duck-UI is a web-based interface for interacting with DuckDB, a high-performance analytical database system. This project leverages DuckDB's WebAssembly (WASM) capabilities to provide a seamless and efficient user experience directly in the browser.",
9696
"main": "eslint.config.js",

src/components/editor/SqlEditor.tsx

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useRef, useEffect, useState, useCallback } from "react";
2-
import { Play, Loader2, Lightbulb, Command, Edit } from "lucide-react";
2+
import { Play, Loader2, Lightbulb, Command, Edit, Share2 } from "lucide-react";
33
import { Button } from "@/components/ui/button";
44
import { useDuckStore } from "@/store";
55
import { useTheme } from "../theme/theme-provider";
@@ -20,6 +20,7 @@ import { toast } from "sonner";
2020
import ConnectionPill from "@/components/common/ConnectionPill";
2121
import { Badge } from "@/components/ui/badge";
2222
import FloatingActionButton from "@/components/common/FloatingActionButton";
23+
import { copyQueryURL } from "@/hooks/useQueryFromURL";
2324

2425
interface SqlEditorProps {
2526
tabId: string;
@@ -121,6 +122,24 @@ const SqlEditor: React.FC<SqlEditorProps> = ({ tabId, title, className }) => {
121122
setIsEditingTitle(true);
122123
};
123124

125+
const handleShareQuery = async () => {
126+
const editor = editorInstanceRef.current?.editor;
127+
if (!editor) return;
128+
129+
const query = editor.getValue().trim();
130+
if (!query) {
131+
toast.error("No query to share");
132+
return;
133+
}
134+
135+
const success = await copyQueryURL(query, false);
136+
if (success) {
137+
toast.success("Query URL copied to clipboard");
138+
} else {
139+
toast.error("Failed to copy URL");
140+
}
141+
};
142+
124143
return (
125144
<div className={cn("flex flex-col h-full relative", className)}>
126145
{/* Header */}
@@ -198,6 +217,23 @@ const SqlEditor: React.FC<SqlEditorProps> = ({ tabId, title, className }) => {
198217
</Tooltip>
199218
</TooltipProvider>
200219
</div>
220+
<TooltipProvider>
221+
<Tooltip delayDuration={200}>
222+
<TooltipTrigger asChild>
223+
<Button
224+
onClick={handleShareQuery}
225+
variant="ghost"
226+
size="icon"
227+
className="h-9 w-9"
228+
>
229+
<Share2 className="h-4 w-4" />
230+
</Button>
231+
</TooltipTrigger>
232+
<TooltipContent side="bottom">
233+
<p>Copy shareable URL</p>
234+
</TooltipContent>
235+
</Tooltip>
236+
</TooltipProvider>
201237
<Button
202238
onClick={handleExecuteQuery}
203239
disabled={isExecuting}

src/components/table/DuckUItable.tsx

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import {
44
getCoreRowModel,
55
getFilteredRowModel,
66
getPaginationRowModel,
7+
getSortedRowModel,
78
useReactTable,
89
type ColumnDef,
910
type FilterFn,
1011
type ColumnFiltersState,
1112
type ColumnResizeMode,
1213
type PaginationState,
1314
type ColumnSizingState,
15+
type SortingState,
1416
} from "@tanstack/react-table";
1517
import { useVirtualizer } from "@tanstack/react-virtual";
1618
import { formatBytes, formatDuration } from "@/lib/utils";
@@ -25,6 +27,8 @@ import {
2527
MousePointer,
2628
MoreHorizontal,
2729
ChevronDown,
30+
ChevronUp,
31+
ChevronsUpDown,
2832
BarChart3,
2933
} from "lucide-react";
3034
import { Button } from "@/components/ui/button";
@@ -150,7 +154,7 @@ const DuckUITable: React.FC<DuckTableProps> = ({
150154
Record<string, number>
151155
>({});
152156
const [columnSelectorFilter, setColumnSelectorFilter] = useState("");
153-
// const [sorting, setSorting] = useState<SortingState>([]); // REMOVED
157+
const [sorting, setSorting] = useState<SortingState>([]);
154158

155159
// New spreadsheet features
156160
const [showRowNumbers, setShowRowNumbers] = useState(false);
@@ -301,13 +305,23 @@ const DuckUITable: React.FC<DuckTableProps> = ({
301305
accessorFn: (row) => row[key], // Use accessorFn instead of accessorKey for direct property access
302306
minSize: MIN_COLUMN_WIDTH,
303307
maxSize: MAX_COLUMN_WIDTH,
304-
header: () => (
305-
// Simplified header
308+
enableSorting: true,
309+
header: ({ column }) => (
306310
<div
307-
className="h-7 text-xs w-full flex items-center justify-start pl-0"
308-
title={key}
311+
className="h-7 text-xs w-full flex items-center justify-between pl-0 pr-1 cursor-pointer select-none hover:bg-muted/50"
312+
title={`${key} (click to sort)`}
313+
onClick={() => column.toggleSorting()}
309314
>
310315
<span className="truncate">{key}</span>
316+
<span className="ml-1 flex-shrink-0">
317+
{column.getIsSorted() === "asc" ? (
318+
<ChevronUp className="h-3 w-3" />
319+
) : column.getIsSorted() === "desc" ? (
320+
<ChevronDown className="h-3 w-3" />
321+
) : (
322+
<ChevronsUpDown className="h-3 w-3 opacity-30" />
323+
)}
324+
</span>
311325
</div>
312326
),
313327
cell: ({ row }) => {
@@ -382,21 +396,21 @@ const DuckUITable: React.FC<DuckTableProps> = ({
382396
globalFilter,
383397
pagination,
384398
columnSizing,
385-
// sorting, // REMOVED
399+
sorting,
386400
},
387401
onColumnFiltersChange: setColumnFilters,
388402
onGlobalFilterChange: setGlobalFilter,
389403
onPaginationChange: setPagination,
390404
onColumnSizingChange: handleColumnSizeChange,
391-
// onSortingChange: setSorting, // REMOVED
405+
onSortingChange: setSorting,
392406
getCoreRowModel: getCoreRowModel(),
393407
getFilteredRowModel: getFilteredRowModel(),
394408
getPaginationRowModel: getPaginationRowModel(),
395-
// getSortedRowModel: getSortedRowModel(), // REMOVED
409+
getSortedRowModel: getSortedRowModel(),
396410
enableGlobalFilter: true,
397411
enableColumnResizing: true,
398-
// enableSorting: true, // REMOVED (or set to false)
399-
debugTable: false, // Set to true for debugging if needed
412+
enableSorting: true,
413+
debugTable: false,
400414
});
401415

402416
const { rows } = table.getRowModel();

src/components/workspace/SqlTab.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,28 @@ import {
1010
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
1111
import DuckUiTable from "@/components/table/DuckUItable";
1212
import ChartVisualizationPro from "@/components/charts/ChartVisualizationPro";
13-
import { FileX2, Table, BarChart3 } from "lucide-react";
13+
import { FileX2, Table, BarChart3, AlertTriangle } from "lucide-react";
1414
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
1515
import { Skeleton } from "../ui/skeleton";
16+
import { ErrorBoundary, FallbackProps } from "react-error-boundary";
17+
18+
const TableErrorFallback = ({ error, resetErrorBoundary }: FallbackProps) => (
19+
<div className="h-full flex items-center justify-center p-4">
20+
<div className="text-center max-w-md">
21+
<AlertTriangle className="mx-auto mb-4 text-destructive" size={32} />
22+
<h3 className="text-sm font-medium mb-2">Failed to render table</h3>
23+
<p className="text-xs text-muted-foreground mb-4">
24+
{error.message || "An error occurred while displaying the results."}
25+
</p>
26+
<button
27+
onClick={resetErrorBoundary}
28+
className="text-xs px-3 py-1.5 bg-primary text-primary-foreground rounded hover:bg-primary/90"
29+
>
30+
Try again
31+
</button>
32+
</div>
33+
</div>
34+
);
1635

1736
interface SqlTabProps {
1837
tabId: string;
@@ -100,7 +119,9 @@ const SqlTab: React.FC<SqlTabProps> = ({ tabId }) => {
100119
</TabsList>
101120
<TabsContent value="table" className="flex-1 min-h-0">
102121
<div className="h-full">
103-
<DuckUiTable data={currentTab.result.data} />
122+
<ErrorBoundary FallbackComponent={TableErrorFallback}>
123+
<DuckUiTable data={currentTab.result.data} />
124+
</ErrorBoundary>
104125
</div>
105126
</TabsContent>
106127
<TabsContent value="charts" className="flex-1 min-h-0">

src/components/workspace/WorkspaceTabs.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Tabs, TabsList, TabsContent } from "@/components/ui/tabs";
44
import { Button } from "@/components/ui/button";
55
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
66
import { Plus, XSquareIcon } from "lucide-react";
7+
import { useQueryFromURL } from "@/hooks/useQueryFromURL";
78
import {
89
DndContext,
910
closestCenter,
@@ -49,6 +50,9 @@ export default function WorkspaceTabs() {
4950
}
5051
}, [isInitialized, initialize]);
5152

53+
// Handle loading query from URL parameters
54+
useQueryFromURL();
55+
5256
const sensors = useSensors(
5357
useSensor(PointerSensor),
5458
useSensor(KeyboardSensor, {

0 commit comments

Comments
 (0)