v0.5.0 — Grid Interactivity, Home Polish, Deeper PostgreSQL (#8)

* chore: bump version to 0.5.0 (Task 1)

* feat(store): v7 migration — favorites + recent_connections (Task 2)

* feat(models): add favorite to Connection + Store CRUD (Task 3)

* feat(types): ColumnInfo editability + IndexInfo/ConstraintInfo/RecentConnection (Task 4)

* chore: bump version to 0.5.0 (Task 1) — lockfile

* feat(commands): typed wrappers for favorites/recents/indexes/constraints (Task 5)

* feat(db): PG indexes/constraints queries + matview UNION in tables (Task 6)

* feat(db): get_table_data editability flags + ctid/rowid locator (Task 7)

* feat(db): execute_change no-PK locator guard + affected-count check (Task 8)

* feat(db): preserve bigint precision as string on PG read path (Task 9)

* feat(db): get_indexes / get_constraints commands (Task 10)

* feat(store): favorites + recents Store methods (Task 11)

* feat(commands): favorites/recents IPC + register indexes/constraints (Task 12)

* feat(store): connectionStore favorites/recents/move-selection (Task 13)

* feat(lib): recent-connections pure helpers (Task 14)

* feat(store): dbViewerStore indexes/constraints + stageCellEdit (Task 15)

* feat(grid): pure editability + filter-operator + cell transform (Task 16)

* feat(grid): pure keyboard-nav helper (Task 17)

* feat(grid): CellEditor inline editor (Task 18)

* feat(grid): CellContextMenu + RowDetailDrawer (Task 19)

* feat(grid): focus model + keyboard nav + inline edit + copy + context menu (Task 20)

* feat(db-viewer): FilterBuilder drag-and-drop + type-aware operators (Task 21)

* feat(db-viewer): ObjectExplorer indexes/constraints/procedures + matview icon (Task 22)

* feat(home): ConnectionCard favorite star + on-demand StatusDot (Task 23)

* feat(home): move-to-folder + recents strip + status wiring (Task 24)

* feat(db-viewer): grid wiring + matview read-only + post-commit refetch (Task 25)

* docs: v0.5.0 status + roadmap updates (Task 26)

* polish: empty/error/loading states for v0.5.0 surfaces (Task 28)

* feat(home): duplicateConnection + useConnectionStatus hook, drop StatusDot (FEAT-A)

* feat(home): connection card kebab menu — favorite/test/manage (FEAT-B)

* fix(home): populate server_version/latency_ms in test_connection + clean online display

* style(home): swap grab handle and kebab positions on connection card

* style(home): nudge kebab menu to right-1

* style(home): nudge kebab menu to right-0.5

* feat(home): Escape clears + exits focused search

* feat(grid): context-menu View/Select Row, outside-click close, Esc cancels edit, FK reference (GRID-A)

* feat(grid): smart CellEditor — enum select, FK searchable dropdown, textarea height (GRID-B)

* feat(grid): enums + FK options fed into CellEditor (GRID-C)

* feat(grid): FK dropdown display-column labels + placeholder + empty state

* fix(grid): FK dropdown renders as fixed overlay to avoid clipping

* fix(grid): portal FK dropdown to body + FK reference icon instead of click-to-open

* style(grid): move FK reference icon to the start of the cell

* feat(grid): optimistic staged cell values + pending dot, cleared on refetch

* fix(grid): queue is source of truth for staged values — value diff, Clear All clears dots, same-cell edits replace

* fix(db-viewer): type getLocator for staged-value matching

* test(db-viewer): unit-test deriveStagedValues; fix activeTab null guard

* fix(db-viewer): pass table prop to VirtualDataGrid — staged edits now carry the table name

* test(db-viewer): use index access instead of .at() for TS lib target

* feat(grid): FK dropdown options as one-row column cells (FK-reference style, cap 5)

* style(grid): FK dropdown — values only, fixed 360px width, FK-viewer surface styling

* style(grid): harden FK dropdown minWidth to 360px

* style(grid): cap FK dropdown cells at 3

* style(grid): cap FK dropdown cells at 4

* fix(grid): pending dot clears on commit — values stay until refetch

* fix(db): deserialize pg_attribute char columns as i8 — no more panic on get_table_data

* docs: reflect grid interactivity, smart editors, optimistic queue, FK reference, kebab status
This commit is contained in:
2026-08-03 02:42:24 +08:00
committed by GitHub
parent e0c0db8352
commit 16888460b7
77 changed files with 5671 additions and 265 deletions
+18
View File
@@ -12,6 +12,7 @@ pub struct Connection {
pub folder_id: Option<String>,
pub keychain_ref: Option<String>,
pub environment: Option<String>,
pub favorite: bool,
pub ssh_host: Option<String>,
pub ssh_port: Option<i64>,
pub ssh_user: Option<String>,
@@ -118,6 +119,7 @@ mod tests {
folder_id: Some("folder".to_string()),
keychain_ref: Some("keychain-ref".to_string()),
environment: None,
favorite: false,
tag_ids: vec![],
created_at: "2024-01-01T00:00:00Z".to_string(),
updated_at: "2024-01-01T00:00:00Z".to_string(),
@@ -136,4 +138,20 @@ mod tests {
let json = serde_json::to_string(&conn).unwrap();
assert!(!json.contains("password"), "Connection JSON should not contain password field");
}
#[test]
fn connection_serializes_favorite_field() {
let conn = Connection {
id: "x".into(), name: "n".into(), db_type: "postgresql".into(),
host: "h".into(), port: Some(5432), username: None, database: None,
folder_id: None, keychain_ref: None, environment: None,
ssh_host: None, ssh_port: None, ssh_user: None, ssh_auth_method: None,
ssh_private_key_path: None, ssl_mode: None, ssl_ca_path: None,
ssl_cert_path: None, ssl_key_path: None, tag_ids: vec![],
favorite: true,
created_at: "2024-01-01T00:00:00Z".into(), updated_at: "2024-01-01T00:00:00Z".into(),
};
let json = serde_json::to_string(&conn).unwrap();
assert!(json.contains("\"favorite\":true"));
}
}
+41
View File
@@ -33,6 +33,33 @@ pub struct ColumnInfo {
pub is_fk: bool,
pub fk_ref: Option<(String, String)>,
pub default_value: Option<String>,
pub editable: bool,
pub is_generated: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexInfo {
pub name: String,
pub schema: String,
pub table: String,
pub definition: String,
pub is_unique: bool,
pub method: String,
pub columns: Vec<String>,
pub size_bytes: Option<i64>,
pub tablespace: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConstraintInfo {
pub name: String,
pub schema: String,
pub table: String,
pub contype: String, // "CHECK" | "UNIQUE" | "EXCLUSION"
pub definition: String,
pub deferrable: bool,
pub validated: bool,
pub columns: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -338,12 +365,26 @@ mod tests {
is_fk: true,
fk_ref: Some(("users".to_string(), "id".to_string())),
default_value: None,
editable: true,
is_generated: false,
};
let json = serde_json::to_string(&col).unwrap();
assert!(json.contains("user_id"));
assert!(json.contains("users"));
}
#[test]
fn column_info_has_editability_fields() {
let c = ColumnInfo {
name: "id".into(), data_type: "integer".into(), is_nullable: false,
is_pk: true, is_fk: false, fk_ref: None, default_value: None,
editable: false, is_generated: false,
};
let json = serde_json::to_string(&c).unwrap();
assert!(json.contains("\"editable\":false"));
assert!(json.contains("\"is_generated\":false"));
}
#[test]
fn pagination_serialization() {
let pagination = Pagination {
+2
View File
@@ -2,11 +2,13 @@ pub mod backup;
pub mod connection;
pub mod db_viewer;
pub mod folder;
pub mod recent;
pub mod ssh;
pub mod tag;
pub mod settings;
pub use connection::{Connection, ConnectionInput};
pub use recent::RecentConnection;
#[allow(unused_imports)]
pub use db_viewer::{Change, ColumnInfo, FilterRule, Pagination, QueryResult, SortRule, TableInfo};
pub use folder::{Folder, FolderInput};
+7
View File
@@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecentConnection {
pub connection_id: String,
pub opened_at: String,
}