Files
gridline/AGENTS.md
T
adrianbonpin 3dab09f25d v0.2.0 — Object Explorer, Backup/Restore/Sync, Server-Side Filters, ENUM support, and more
* feat(grid): VirtualDataGrid component with TanStack Virtual (Task A1)

* fix(grid): address code review — header bg, row hover/select, table classes, measureElement removal

* feat(grid): column resize, FK popover, JSON popover in VirtualDataGrid (Task A2)

* fix(grid): address code review — accessibility, indeterminate checkbox, safe jp default

* feat(grid): wire VirtualDataGrid into DbViewerScreen, increase page size to 500 (Task A3)

* feat(grid): polish VirtualDataGrid — large datasets, hidden columns, select-all (Tasks A4+A5)

* feat(dnd): moveConnection store action with optimistic update + rollback (Task D1)

* feat(dnd): draggable connection cards + droppable folder tree (Task D2)

* feat(dnd): wire DndContext, onDragEnd, DragOverlay, drop targets in ConnectionGrid (Tasks D3-D5)

* feat(backup): models, v4 migration for backup_history, TypeScript types (Task C1)

* feat(backup): pg_dump, pg_restore, detect_tools, db_sync commands (Task C2)

* feat(backup): frontend wrappers, backup store, dialogs, tool detection, confirmations (Tasks C3-C5)

* feat(objects): add FunctionInfo, TriggerInfo, SequenceInfo, EnumInfo, ExtensionInfo models (Task B1)

* feat(objects): introspection queries + Tauri commands for functions, triggers, sequences, enums, extensions (Task B2)

* feat(objects): ObjectTree component, sidebar navigation, search, source expand (Tasks B3-B5)

* fix: drag handle position, env tag layout, transparent header, virtual row rendering

* fix: remove table toolbar bg, fix sticky header with flex layout, match header/row widths

* fix: vertical borders on all cells, horizontal border scrolls with header, checkbox column borders

* fix: full-height cell borders, centered checkboxes, horizontal scroll borders

* fix: header bg fills full width on scroll, page sizes match settings (50-500)

* fix: add subtle bg to panel resize handle to eliminate visual gap

* fix: FK preview showing 'db error' + make entire tab clickable

- VirtualDataGrid: added schema prop to fix empty schema sent to FK preview,
  which produced invalid SQL (SELECT * FROM ""."table")
- Rust get_fk_preview: use pg_error_message() instead of .to_string() so
  real PostgreSQL error messages surface instead of just 'db error'
- TabBar: moved onClick to the outer tab container so the entire tab area
  is clickable, not just the table name text

* feat: push filters and sorts to server-side SQL queries

- Added FilterRule/SortRule models to Rust backend with serde support
- Built dynamic WHERE (parameterized /?) and ORDER BY clauses
- Both COUNT(*) and data queries now include filter clauses for
  correct pagination with filtered results
- Store setFilterRules/setSortRules now trigger page-1 re-fetch
- Removed client-side applyFilters/applySorts — data arrives
  pre-filtered/sorted from the database
- Added column name validation (is_safe_identifier) to prevent
  SQL injection in filter/sort column names

* chore: center drag handle vertically on connection cards

* feat: convert backup/restore/sync from unreachable modals to in-page views

- Added BackupPage, RestorePage, SyncPage as full-page views inside
  the DB viewer, replacing the old unused modal dialogs
- Added sidebar navigation items: Backup (Download), Restore (Upload),
  DB Sync (ArrowLeftRight)
- Wired routing in DbViewerScreen — currentView switches to the new
  page components, all scoped to the active connection
- Styled with toolbar header, underline inputs, glass cards, and
  destructive confirmation checkboxes matching the app aesthetic
- Auto-detects pg_dump/pg_restore availability with platform-specific
  install instructions when tools are missing

* fix: backup/restore/sync event-driven flow and styling

- Registered tauri-plugin-dialog in Rust lib.rs (was missing — browse
  buttons were silently failing)
- Added #[serde(rename_all = "camelCase")] to BackupOptions,
  RestoreOptions, SyncOptions so frontend camelCase fields map
  correctly to Rust snake_case fields
- Made backup/restore/sync fully event-driven: pages now derive
  running/completed/failed state from the backupStore instead of
  the pgDump/pgRestore/dbSync promise (which returns immediately
  while the actual work runs in the background)
- Added initListener() call in App.tsx so the backupStore actually
  listens to Tauri backup-progress events
- failJob no longer clears activeJobId so the component can still
  find the job to read its error message
- BackupProgress now receives real job status and error_message;
  visible for all states (running/completed/failed) so the error
  bar renders on failure
- Schema field converted from free-text to database-populated
  dropdown (getSchemas) on all three pages
- Removed unused parseError functions and dead code
- Progress bar shows 100% on completed, 50% indeterminate while
  running, red bar + error text on failure

* feat: object explorer pages with split-pane layout and schema switching

Created ObjectExplorerPage — a reusable split-pane component for
Functions, Triggers, Sequences, Enums, and Extensions:

Left panel:
- Database/schema dropdowns for switching context (solves 'no
  functions showing' when they're in a non-default schema)
- Searchable object list with animated search input
- Refresh button to re-query
- Selected item highlighting

Right panel:
- Detail view with icon, name, schema
- Type-specific fields:
  - Functions: return type, language, kind, arguments (with
    mode/type badges), full source code with show/hide
  - Triggers: table (schema-qualified), event, timing,
    orientation, enabled status, trigger definition
  - Sequences: current value, increment, start, min, max,
    cycle flag
  - Enums: labeled pill badges for each enum value
  - Extensions: version, schema, comment
- Empty state showing count of available objects

Bug fix: pg_get_function_result() replaced with format_type()
in Rust introspection — the former returns multiple rows for
SETOF/TABLE functions (generate_series, etc.), causing the
entire query to fail silently. This was the root cause of
'no functions showing even though I know there are'.

* fix: correct PostgreSQL tgtype bitmask in trigger query

The trigger query used wrong bit positions for all events.
PostgreSQL tgtype bits:
  bit 0 (1): ROW vs STATEMENT
  bit 1 (2): BEFORE vs AFTER
  bit 2 (4): INSERT
  bit 3 (8): DELETE
  bit 4 (16): UPDATE
  bit 5 (32): TRUNCATE

The old query had every event shifted one bit down (INSERT=2
instead of 4, DELETE=4 instead of 8, etc.). Timing checked bit 0
instead of bit 1. Orientation label was inverted (showed STATEMENT
when bit 0=1 which actually means ROW).

* fix: object page re-mount on type switch + matching dropdown style

- Added key={type} to each ObjectExplorerPage instance so React
  forces a fresh mount when switching between Functions → Triggers
  → Sequences → Enums → Extensions (was reusing the same component
  instance, showing stale data from the previous type)
- Replaced raw <select> elements with SelectDropdown variant="ghost"
  to match the Explorer page toolbar style (ghost text buttons with
  chevron and dropdown menus)

* fix: match ObjectExplorerPage sidebar exactly to Explorer page

- Added draggable resize handle between panels (same style as
  Explorer: w-1 cursor-col-resize, bg-border/20, hover/accent)
- Panel width is now resizable (180–500px, double-click resets to
  280px, same as tablePanelWidth)
- Changed list row gap from gap-2 to gap-1 and py-1.5 to py-1
  so list items match TableTree row sizing exactly
- Removed py-1 from the scrollable list container so the first
  item sits flush against the toolbar border (matching TableTree)
- Fixed dropdown separator: gap-3→gap-2, removed text-sm from |
  (now identical to Explorer toolbar dropdown spacing)

* fix: handle overloaded PostgreSQL functions in object explorer

PostgreSQL supports function overloading — same name, different
argument types. The old code used bare name as React key and for
selection comparison, causing:
- Multiple overloads sharing the same key → rendering glitches
  (looked like 'duplicate instances' or missing items)
- Clicking one overload highlighted ALL overloads with that name

Changes:
- itemKey(): unique key using name(arg_type1,arg_type2) for
  functions, bare name for other object types
- itemLabel(): display name with argument signature for functions
  with args (e.g. 'calculate_tax(numeric, integer)')
- Selection comparison uses itemKey equality instead of name
- Search now matches against the full label (name + args)
- Detail view header shows the full label

* feat: card-based detail views with border separators for all object types

Redesigned detail views to match the Explorer page aesthetic:

Functions:
- Signature card: Returns, Language, Kind, Schema in border-separated rows
- Arguments card: each arg shown as 'name: type' with OUT/VARIADIC mode badges
- Source card: header bar with language label, bordered code block

Triggers:
- Details card: Table (schema-qualified), Event, Timing+Orientation,
  Status (color-coded enabled/disabled), Schema
- Definition card with SQL code block

Sequences:
- Compact card: Current Value (highlighted), Increment, Start,
  Min/Max (combined row), Cycle (amber/neutral)

Enums:
- Details card with schema
- Values card with label count, pill badges in rounded-md style

Extensions:
- Card with Version, Schema, optional Comment

Shared: Source code now has bg-surface-raised, rounded-lg, border,
better padding (p-4), and longer default truncation (800 chars).
Removed unused DetailRow helper.

* feat: syntax-highlighted line-numbered source code + flat border layout

SyntaxCode:
- Tokenizes PL/pgSQL/SQL: keywords (blue), types (emerald),
  strings (amber), comments (subtle italic), numbers (purple),
  operators (muted)
- Handles dollar-quoted strings (26325...26325 / $tag$...$tag$),
  single-quoted strings with escape (''), -- line comments,
  /* block comments */
- Line numbers on the left with border-r separator
- Hover row highlight (bg-surface/30)
- Collapsed by default at 60 lines with 'Show all N lines…'
  toggle; expand/collapse button

Flat border layout (matching Explorer table viewer):
- All cards (rounded-lg border bg-surface overflow-hidden)
  replaced with plain divs using border-b border-border rows
- Section headers use same border-b + tracking-wider style as
  the data grid column headers
- Each field row: px-4 py-2 flex with 24/28px label column
- Source code uses bg-canvas (lets app bg show through)
- No space-y-4 gaps or card wrappers — clean flat aesthetic

* fix: infinite loop in tokenizer + memoize syntax highlighting

- Added catch-all fallback for unrecognized characters (non-ASCII,
  unicode, symbols) that weren't matched by any tokenizer branch,
  causing an infinite loop and app hang
- Wrapped tokenization in useMemo to avoid re-tokenizing on every
  render (source and expanded state as deps)
- Pre-tokenize all visible lines at once instead of per-line in
  the render loop
- Added explicit type annotations to fix TS7022/TS7024 errors

* fix: horizontal scroll for long source lines in SyntaxCode

Changed the <pre> wrapper from block to inline-block min-w-full
so it grows past the viewport width when lines are long, enabling
the outer overflow-x-auto to kick in. Added overscroll-x-contain
for smoother scroll behavior on macOS.

* fix: proper horizontal scroll containment for source code

- Right panel: overflow-x-hidden prevents code content from
  pushing the entire panel past the viewport
- SyntaxCode pre: whitespace-pre on the pre itself (not just
  inner spans) so long lines stay on one line
- Removed inline-block min-w-full hack on the pre — instead
  the outer overflow-x-auto div clips and scrolls
- Removed unused overscroll-none class

* fix: horizontal scroll containment in object explorer detail view

- Removed overflow-x-hidden from right panel (was clipping
  scrollbars and preventing proper scroll context)
- Added max-w-full overflow-x-auto on the detail content wrapper
  so long lines scroll within the content area instead of pushing
  the entire panel
- Added w-max min-w-full on the SyntaxCode pre so it grows to
  fit content while the parent overflow-x-auto clips and scrolls

* fix: use w-0 overflow-x-hidden pattern to constrain right panel width

The Explorer page uses 'flex-1 w-0 min-w-0 overflow-hidden' on the
right panel. Without w-0, the flex item can expand past its
allocated share when content is wider. Adding w-0 forces the
initial width to zero so the panel can only grow via flex
allocation, not from content pressure.

Detail content wrapper uses inline overflowX: auto + width: 100%
to create the scroll context for the SyntaxCode pre.

* fix: add overscroll-x-none to source code scroll container

* style: refine object explorer page layout and detail views

* fix: replace enum pill badges with bordered row list

Enum values now display as numbered rows with border-b border-border,
matching the Arguments section style exactly. Each value shows a #N
index in muted mono + the label in accent mono, consistent with how
argument names and types are displayed.

* docs: update implementation status in AGENTS.md and README.md

Object Explorer (formerly all  stubs):
- Functions:  detail view, syntax highlighting, overload support
- Triggers:  detail view, tgtype bitmask fix, definition display
- Sequences:  detail view, schema-filtered
- Enums:  bordered row list matching Arguments style
- Extensions:  detail view, DB-scoped query

Database Viewer:
- Column filtering/sorting: now server-side (was client-side)

Backup & Restore (formerly all ):
- pg_dump/pg_restore wrappers:  with progress events
- Backup/Restore/Sync UI:  in-page views with event-driven status

README roadmap: Phases 3 & 5 marked complete

* fix: SQLite data rows showing null for non-text columns

The SQLite branch of get_table_data always read row values as
Option<String>, which silently failed for INTEGER, REAL, and other
non-text types — returning Null for every non-text column.

Added sqlite_value_to_json() that uses rusqlite's ValueRef enum
(Null/Integer/Real/Text/Blob) to properly convert each column to
the correct serde_json::Value type. Applied to all three query
branches (unfiltered, filtered, and FK preview).

* fix: handle PostgreSQL ENUM and USER-DEFINED types in table viewer

- pg_value_to_json: added robust String fallback that catches enum
  values and any other custom/user-defined types not matched by
  the specific type checks (i32, i64, f64, bool, uuid, etc.)
- pg_columns_query: replaced bare c.data_type with CASE WHEN
  c.data_type = 'USER-DEFINED' THEN c.udt_name ELSE c.data_type
  so enum columns show their actual type name (e.g. badge_category)
  instead of 'USER-DEFINED'
- The get_table_data inline column query already had this fix;
  now pg_columns_query in introspection.rs matches

* fix: cast PostgreSQL custom/enum columns to ::text in data query

tokio-postgres FromSql<String> rejects custom type OIDs (enums,
composite types, domains) even under simple query protocol where
all values arrive as text. This caused all enum column values to
display as NULL.

Fix: dynamically build the SELECT column list by inspecting the
column metadata. For standard PostgreSQL types (int4, text, bool,
jsonb, etc.) use the bare column name. For anything else (custom
enums, domains), append ::text so the value reaches the client as
a regular text column that FromSql<String> accepts.

* chore: replace app icons with 1024x1024 Default variant (proper padding)

* chore: fix Rust warnings — unused import and unnecessary mut

- Removed unused pub use backup::* from models/mod.rs (backup
  types are imported directly via crate::models::backup::*)
- Removed unnecessary mut on filter_params in db_viewer.rs

* chore: bump v0.2.0 + update docs status

AGENTS.md:
- Virtualized data grid:  (row-level virtualization
  via @tanstack/react-virtual)
- Column metadata: noted ENUM/custom type resolution and ::text
  casting for data retrieval

Version bump: 0.1.0 → 0.2.0 across package.json, tauri.conf.json,
and Cargo.toml

* docs: update README features to reflect current implementation

Rewrote aspirational sections with accurate current-state descriptions:

Object Explorer:
- Removed Indexes & Constraints (not yet implemented)
- Added syntax highlighting, line numbers, overload support for Functions
- Clarified Extensions are view-only (no enable/disable toggling yet)

SQL Editor & Query Workbench:
- Removed Monaco, autocomplete, formatter, history (not yet built)
- Kept what's real: multi-tab workspace, changes queue, smart sort
- Added 'coming soon' note

Data Grid:
- Removed inline editing, visual filter builder, import (not yet built)
- Added what's real: server-side filtering, FK preview, JSON viewer

Admin Tools:
- Updated backup/restore/sync descriptions to match actual UI
- Removed aspirational 'drag-and-drop', 'dry-run', 'diff viewer'

Tech Stack:
- Monaco marked as planned; Glide removed (we use TanStack Virtual)
2026-07-29 03:23:52 +08:00

18 KiB

AGENTS.md

Guidance for AI coding agents working on Gridline.


Project Identity

Gridline is an open-source, cross-platform database GUI client for PostgreSQL (with MySQL, SQLite, and Redis to follow). It is built as a Tauri 2.0 desktop app — a lightweight native shell (~40MB baseline) around a React web frontend, with a Rust backend handling all database operations, CLI tool orchestration, and local persistence.

Core differentiators from commercial alternatives (DB Pro, TablePlus, etc.):

  • No paywalls — unlimited tabs, connections, and saved queries by default
  • First-class PostgreSQL administration: pg_dump, pg_restore, DB-to-DB sync
  • Full object explorer: Functions, Triggers, Sequences, Enums, Extensions — not just tables

Target audience: Developers managing multiple database environments across projects (Personal, Work, Client). The workspace/folder hierarchy is a first-class concept.


Tech Stack

Layer Technology Notes
Desktop shell Tauri 2.0 Native webview wrapper, Rust backend
Frontend React 19 + TypeScript 5.8 Vite 7 for bundling/HMR
Styling Tailwind CSS Dark-first, glassmorphic aesthetic
State Zustand or Jotai Pick one and stay consistent per feature
Editor Monaco Editor SQL mode with custom autocomplete providers
Data grid Glide Data Grid or TanStack Virtual Virtualized, canvas-rendered
Backend Rust (tokio async runtime) Connection pools, IPC commands, shell execution
DB drivers sqlx + tokio-postgres Async, pure-Rust PostgreSQL driver
Local storage SQLite via rusqlite User settings, workspace state, query history
Credentials OS keychain macOS Keychain, Linux Secret Service, Windows Credential Manager

Project Structure

gridline/
├── src/                          # React frontend (TypeScript)
│   ├── components/               # Reusable UI components
│   │   ├── layout/               # App shell, sidebar, tabs
│   │   ├── editor/               # Monaco wrapper, autocomplete
│   │   ├── grid/                 # Data grid, filters, export
│   │   ├── tree/                 # Workspace/object explorer tree
│   │   └── ui/                   # Primitives (buttons, modals, inputs)
│   ├── stores/                   # Zustand/Jotai stores
│   ├── hooks/                    # Custom hooks (useConnection, useQuery, etc.)
│   ├── lib/                      # Utilities, types, Tauri bindings
│   │   ├── commands.ts           # Typed wrappers around Tauri invoke()
│   │   ├── types.ts              # Shared TypeScript interfaces
│   │   └── utils.ts              # Formatting, validation helpers
│   ├── App.tsx
│   ├── main.tsx
│   └── index.css                 # Tailwind directives + custom theme tokens
├── src-tauri/                    # Rust backend
│   ├── src/
│   │   ├── main.rs               # Binary entry point
│   │   ├── lib.rs                # Tauri builder, command registration
│   │   ├── db/                   # Connection pooling, query execution
│   │   │   ├── mod.rs
│   │   │   ├── pool.rs           # Connection pool manager
│   │   │   └── introspection.rs  # Schema/system catalog queries
│   │   ├── commands/             # Tauri #[tauri::command] handlers
│   │   │   ├── mod.rs
│   │   │   ├── connections.rs    # CRUD for saved connections
│   │   │   ├── query.rs          # SQL execution
│   │   │   ├── schema.rs         # Object tree introspection
│   │   │   ├── backup.rs         # pg_dump / pg_restore wrappers
│   │   │   └── workspace.rs      # Workspace/folder persistence
│   │   ├── models/               # Serde structs shared across commands
│   │   │   ├── mod.rs
│   │   │   ├── connection.rs
│   │   │   ├── query.rs
│   │   │   └── workspace.rs
│   │   └── store/                # SQLite local persistence layer
│   │       ├── mod.rs
│   │       └── migrations.rs
│   ├── Cargo.toml
│   ├── tauri.conf.json
│   └── capabilities/             # Tauri capability permissions
├── public/                       # Static frontend assets
├── package.json
├── tsconfig.json
├── vite.config.ts
├── tailwind.config.ts
└── AGENTS.md                     # This file

Conventions

TypeScript / React

  • Components: PascalCase files, default exports for page-level, named exports for reusable primitives
  • Hooks: use prefix, one hook per file unless tightly coupled
  • Stores: One Zustand store per domain (connectionStore, queryStore, workspaceStore)
  • Types: Define interfaces in src/lib/types.ts; use type for unions/aliases
  • No any: Always type Tauri invoke() calls with explicit generics
  • CSS: Tailwind utility classes only; no CSS modules unless unavoidable (Monaco configuration is the exception)

Rust

  • Modules: One module file per concern; re-export through mod.rs
  • Errors: Use anyhow for application errors, thiserror for library-style enums
  • Commands: Keep #[tauri::command] functions thin — delegate to db/ or store/ modules
  • State: Use Tauri managed state (app.manage()) for connection pool handles
  • Naming: snake_case for functions/modules, CamelCase for types/structs

General

  • IPC flow: Frontend calls typed wrapper → wrapper calls invoke() → Rust command → Rust logic → returns Result<T, String>
  • Error handling: Rust commands return Result<T, String> (map errors to user-readable strings before crossing IPC boundary)
  • No secrets in logs: Never log connection strings, passwords, or query parameters
  • Dark mode first: All UI components must look correct in dark theme; light theme is secondary

Key Design Decisions

  1. Tauri over Electron — ~40MB RAM vs 250MB+. Native file dialogs, OS keychain access, and std::process::Command for pg_dump/pg_restore without Node.js overhead.

  2. Rust-native DB driverssqlx/tokio-postgres connect directly to PostgreSQL from the Rust backend. No Node.js pg library, no sidecar Node process. The frontend never touches database connections directly.

  3. System CLI tools for backup/restore — Rather than implementing pg_dump format parsers in Rust (enormous scope), we shell out to the user's installed pg_dump/pg_restore binaries. The app will detect missing tools and guide installation.

  4. SQLite for local state — Workspace tree, saved queries, connection metadata (NOT passwords), and query history go into a local SQLite database in the Tauri app data directory. This enables fast full-text search and relational queries without loading everything into memory.

  5. Virtualized grid from day one — Query results can be 100k+ rows. We must render with canvas/DOM virtualization (Glide Data Grid or TanStack Virtual), never with naive DOM row rendering.


Development Workflow

Commands

bun install              # Install frontend dependencies
bun run dev              # Vite dev server only (no Tauri)
bun run tauri dev        # Full Tauri app with hot-reload
bun run tauri build      # Production build
cargo build              # Rust backend only (from src-tauri/)
cargo test               # Rust tests

Adding a Tauri Command

  1. Define the command function in the appropriate src-tauri/src/commands/ module
  2. Register it in src-tauri/src/lib.rs via .invoke_handler(tauri::generate_handler![...])
  3. Create a typed wrapper function in src/lib/commands.ts
  4. Call the wrapper from your React component/store

Adding a New Dependency

  • Frontend: bun add <package> (runtime) or bun add -d <package> (dev)
  • Rust: Add to src-tauri/Cargo.toml under [dependencies]

Testing Strategy

  • Rust: Unit tests for database logic, connection pool management, and command handlers. Use sqlx::test with a test PostgreSQL instance for integration tests.
  • Frontend: Vitest + React Testing Library for component tests. Focus on store logic, command wrappers, and critical UI flows (connection form, query execution).
  • E2E: (Future) Tauri WebDriver or Playwright for critical paths.

Constraints & Guardrails

  • Do NOT implement pg_dump file format parsing — always shell out to system binaries
  • Do NOT store passwords in SQLite or local files — use OS keychain APIs exclusively
  • Do NOT render large query results in raw DOM — always use the virtualized grid component
  • Do NOT log credentials, connection strings, or query data
  • Do NOT introduce Electron, Node.js server processes, or Docker dependencies
  • Do NOT execute data-modifying SQL (INSERT, UPDATE, DELETE, DROP, ALTER) or any destructive CRUD operation (deleting connections, folders, tags) directly without explicit user confirmation. For database data, always push to the changes queue first and require "Commit All". For app entities (connections, folders, tags), show a confirmation dialog before executing.
  • DO keep Tauri commands thin — business logic lives in db/ and store/ modules
  • DO type all IPC boundaries explicitly
  • DO validate and sanitize all user-provided SQL and connection parameters before execution

Implementation Status

= Complete   🟡 = Partial/Stub   = Not Started

Connection Management

Feature Status Details
Connections CRUD (PostgreSQL, MySQL, SQLite, Redis) Full create/read/update/delete with form validation
Connection testing (all DB types) PostgreSQL, MySQL, SQLite, Redis all testable
DB Viewer: PostgreSQL browse + query Schemas, tables, paginated data, FK preview, JSON viewer
DB Viewer: SQLite browse + query Full support via rusqlite
DB Viewer: MySQL browse Test connection works; browsing not wired
DB Viewer: Redis browse Test connection works; browsing not wired
Password storage in OS keychain macOS Keychain, Linux Secret Service, Windows Credential Manager
SSH tunnel config UI Host, port, user, auth method, key path, passphrase fields
SSH tunnel runtime 🟡 UI exists; backend is a placeholder (TODO: ssh2 crate integration)
SSL/TLS config UI Mode (disable/require/verify-ca/verify-full), cert paths
SSL/TLS runtime 🟡 Config persisted; not yet passed to sqlx/tokio-postgres

Home Screen & Organization

Feature Status Details
Connection cards grid (by folder) Grouped display, single-click to open DB viewer
Folders CRUD Nested folders, reparent on delete, breadcrumb nav
Tags CRUD Colors, drag reorder, filter connections by tag
DB type filter (Postgres/MySQL/SQLite/Redis) Toggle chips to filter connection grid
Global search (Cmd+K) Connection URL detection auto-fills new-connection form
Import/Export connections (JSON) Bulk import with validation, skipped-record reporting
Bulk select + delete connections/folders Checkbox selection with confirmation dialog
Drag-and-drop connections to folders Currently only via edit form
Move-to-folder bulk action
Favorites / Recent connections
Connection status indicator on cards

Database Viewer

Feature Status Details
Multi-tab table browser Open tables in tabs, close with Cmd/Ctrl+W
Schema/database selector Ghost-style dropdowns, single-row layout
Refresh database (spin + success/error feedback) Re-fetches databases, schemas, and tables
Search tables filter Animated input, real-time filter by name, auto-hide on blur
Column metadata (PK, FK, type, nullable, default) Expand table row to see columns with icons. ENUM/custom types resolved via udt_name, cast ::text for data retrieval.
FK detection information_schema.constraint_column_usage + PRAGMA foreign_key_list
FK preview popover Click FK cell → popover with referenced row → "Open" button creates filtered tab
JSON/JSONB cell popover Formatted/Raw tabs with copy button
Smart default sort 12-tier priority: updated_at → created_at → *_at → *_id → seq/rank/version
Data grid pagination Page nav, page size selector persisted in settings
Column filtering (server-side) eq, neq, contains, starts, ends, gt, lt, null, notnull pushed to SQL WHERE
Column sorting (server-side) Multi-column asc/desc pushed to SQL ORDER BY
Column show/hide Toggle visibility per column
Column resize (drag handle) Double-click to auto-fit
Row selection (checkboxes + select all) Bulk copy (JSON/CSV/SQL) and delete
Export toolbar (JSON, CSV, SQL, Markdown) Client-side Blob download of visible rows
Auto-refresh timer Configurable interval in settings
Changes queue (INSERT, UPDATE, DELETE) Queue changes → Commit All; cancel individual changes
Edit connection modal (from DB viewer) AnimatedModal with keychain password fetch on test
Connection drop banner Auto-detects broken connections with reconnect prompt
Inline cell editing Cells are read-only; changes via queue Insert button only
Virtualized data grid Row-level virtualization via @tanstack/react-virtual useVirtualizer; handles 100k+ rows
Row detail / expandable row view
Keyboard cell navigation (arrow keys, Tab)
Cell-level copy (right-click or Ctrl+C) Only bulk copy via toolbar

Object Explorer (non-table objects)

Feature Status Details
Functions Full detail view: signature, arguments with mode/type, syntax-highlighted line-numbered source. Overloads disambiguated by argument signature. Schema-filtered via pg_proc query.
Triggers Full detail view: table, event, timing, orientation, status (color-coded), definition. tgtype bitmask corrected. Schema-filtered.
Sequences Full detail view: current value, increment, start, min/max, cycle flag. Schema-filtered via information_schema.sequences.
Enums Full detail view: numbered bordered list matching Arguments style. Schema-filtered via pg_type WHERE typtype='e'.
Extensions Full detail view: version, schema, comment. Queried from pg_extension (no schema filter — extensions are DB-scoped).
Indexes (per table)
Constraints (CHECK, UNIQUE beyond PK/FK)
Materialized views Not distinguished from regular views
Stored procedures 🟡 Included in Functions via p.prokind IN ('f','p'); no separate view yet
Schema visualizer (ER diagram) Stub button in sidebar

Query Editor

Feature Status Details
SQL text editor (Monaco) src/components/editor/ does not exist yet
SQL autocomplete (keywords, tables, columns)
Custom query execution (arbitrary SQL) Only SELECT * FROM table via tab open
Multiple result sets
Query history / recent queries No persistence or UI
Saved queries (named, organized) No queries table in local SQLite
Query favorites / pinning
Editor settings (font, tab size, word wrap, minimap) Settings page has "Editor" tab with "coming soon" placeholder

Backup & Restore

Feature Status Details
pg_dump wrapper Rust command spawns pg_dump with real-time progress events (backup-progress)
pg_restore wrapper Rust command spawns pg_restore with progress events
Backup UI In-page view: format selector, file browse (Tauri dialog), schema dropdown, no-owner toggle, progress bar with event-driven status
Restore UI In-page view: file browse, format, clean toggle, destructive confirmation checkbox, progress bar
DB-to-DB sync In-page view: source/target connection pickers, schema dropdown, pipe-based pg_dump → pg_restore
SQLite .dump
Table structure export (DDL)

Settings

Feature Status Details
Theme (dark/light/system) Tailwind dark-first with ThemePicker
Font size
Default folder for new connections
Table page size default
Auto-refresh rate
Tags management Full CRUD with color picker, drag reorder
Shortcuts (2 configurable) Open command palette, Close tab
Confirm-before-delete toggle
Default ports per DB type
More keyboard shortcuts Only 2 configurable actions
Editor settings Placeholder tab
SSH key management Only path inputs, no key file reading
Settings export/import

Demo & Onboarding

Feature Status Details
Demo SQLite database (auto-seeded) users, products, orders, order_items tables
Re-add demo DB button Settings → Advanced
Getting started / onboarding flow
Welcome tooltips / tour


This file is read by AI coding agents (Claude, Cursor, Copilot, etc.) to understand project conventions and architecture before making changes. Keep it current as the project evolves.