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

316 lines
18 KiB
Markdown

# 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 drivers**`sqlx`/`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
```bash
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 &nbsp; 🟡 = Partial/Stub &nbsp; ❌ = 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 | ❌ | |
---
## Related Documents
- [Tauri 2.0 Documentation](https://tauri.app/develop/)
- [sqlx Documentation](https://docs.rs/sqlx)
- [Monaco Editor API](https://microsoft.github.io/monaco-editor/api/)
- [Project README](./README.md)
- [Feature Specification](./README.md#features)
---
*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.*