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)
This commit is contained in:
2026-07-29 03:23:52 +08:00
committed by GitHub
parent d872e429e4
commit 3dab09f25d
86 changed files with 7125 additions and 1320 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB