feat: add connection colors, query history, SQL autocomplete, and EXPLAIN visualizer

Add four developer/QA features:
- Connection color coding: color picker in dialog, colored indicators across toolbar, tabs, status bar, and connection selectors
- Query history: Rust backend with JSON file storage (500 entry cap), sidebar panel with search/clear, auto-recording from workspace
- Schema-aware SQL autocomplete: backend fetches column metadata, CodeMirror receives schema namespace with public tables unprefixed
- EXPLAIN ANALYZE visualizer: recursive tree view with cost-colored bars, expand/collapse nodes, buffers info, Results/Explain tab toggle

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 20:22:10 +03:00
parent 72c362dfae
commit 3b3e225e8f
21 changed files with 791 additions and 37 deletions

View File

@@ -18,7 +18,18 @@ import {
import { useSaveConnection, useTestConnection } from "@/hooks/use-connections";
import { toast } from "sonner";
import type { ConnectionConfig } from "@/types";
import { Loader2 } from "lucide-react";
import { Loader2, X } from "lucide-react";
const CONNECTION_COLORS = [
{ name: "Red", value: "#ef4444" },
{ name: "Orange", value: "#f97316" },
{ name: "Yellow", value: "#eab308" },
{ name: "Green", value: "#22c55e" },
{ name: "Cyan", value: "#06b6d4" },
{ name: "Blue", value: "#3b82f6" },
{ name: "Purple", value: "#a855f7" },
{ name: "Pink", value: "#ec4899" },
];
interface Props {
open: boolean;
@@ -173,6 +184,37 @@ export function ConnectionDialog({ open, onOpenChange, connection }: Props) {
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-4 items-center gap-3">
<label className="text-right text-sm text-muted-foreground">
Color
</label>
<div className="col-span-3 flex items-center gap-1.5">
<button
type="button"
className={`flex h-6 w-6 items-center justify-center rounded-full border-2 ${
!form.color ? "border-primary" : "border-transparent"
} bg-muted`}
onClick={() => setForm((f) => ({ ...f, color: undefined }))}
title="No color"
>
<X className="h-3 w-3 text-muted-foreground" />
</button>
{CONNECTION_COLORS.map((c) => (
<button
key={c.value}
type="button"
className={`h-6 w-6 rounded-full border-2 ${
form.color === c.value
? "border-primary"
: "border-transparent"
}`}
style={{ backgroundColor: c.value }}
onClick={() => setForm((f) => ({ ...f, color: c.value }))}
title={c.name}
/>
))}
</div>
</div>
</div>
<DialogFooter>

View File

@@ -91,7 +91,14 @@ export function ConnectionList({ open, onOpenChange, onEdit, onNew }: Props) {
isActive ? "border-primary bg-accent" : ""
}`}
>
<Database className="h-4 w-4 shrink-0 text-muted-foreground" />
{conn.color ? (
<span
className="h-4 w-4 shrink-0 rounded-full"
style={{ backgroundColor: conn.color }}
/>
) : (
<Database className="h-4 w-4 shrink-0 text-muted-foreground" />
)}
<div className="min-w-0 flex-1">
<div className="truncate text-sm font-medium">
{conn.name}

View File

@@ -21,18 +21,36 @@ export function ConnectionSelector() {
);
}
const activeConn = connectedList.find((c) => c.id === activeConnectionId);
return (
<Select
value={activeConnectionId ?? undefined}
onValueChange={setActiveConnectionId}
>
<SelectTrigger className="h-7 w-[200px] text-xs">
<SelectValue placeholder="Select connection" />
<div className="flex items-center gap-1.5">
{activeConn?.color && (
<span
className="h-2.5 w-2.5 shrink-0 rounded-full"
style={{ backgroundColor: activeConn.color }}
/>
)}
<SelectValue placeholder="Select connection" />
</div>
</SelectTrigger>
<SelectContent>
{connectedList.map((conn) => (
<SelectItem key={conn.id} value={conn.id}>
{conn.name}
<div className="flex items-center gap-1.5">
{conn.color && (
<span
className="h-2.5 w-2.5 shrink-0 rounded-full"
style={{ backgroundColor: conn.color }}
/>
)}
{conn.name}
</div>
</SelectItem>
))}
</SelectContent>