v0.7.8: MySQL/SQLite backup-sync, Excel export, query cancel, settings import/export, Windows title-bar fix, SQLite table editor (#16)

* [P1-T1] feat(backup): MySQL/SQLite backup models + db_type on SyncOptions (Task 1.1)

* [P1-T2] feat: enable tools(mysql,sqlite) + tableManagement(sqlite) + add fflate (Task 1.2)

* [P1-T3] feat(cancel): CancelHandle enum + CancelRegistry (Task 1.3)

* [P2-T1] feat(export): hand-rolled XLSX writer with inline-string cells (Task 2.1)

* [P2-T2] feat(backup): SQLite .dump/restore/sync core, fail-closed virtual tables (Task 2.2)

* [P2-T3] feat(backup): MySQL dump/restore/sync arg builders + tool resolution (Task 2.3)

* [P2-T4] feat(settings): pure import validator + SettingsExport envelope + Store.apply_settings (Task 2.4)

* [P2-T5] feat(table-editor): SQLite create/diff/rebuild SQL generation, fail-closed AUTOINCREMENT (Task 2.5)

* [P3-T1] feat(commands): MySQL/SQLite backup + settings export/import commands + wrappers (Task 3.1)

* [P3-T2] feat(cancel): capture cancel primitives at connect; cancel_query command; SQLite interrupt test (Task 3.2)

* [P3-T3] feat(table-editor): SQLite object-change dispatch + execute_change Ddl/RebuildTable (Task 3.3)

* [P4-T1] feat(tools): DB-aware backup/restore/sync pages (Task 4.1)

* [P4-T2] feat(export): xlsx export in grid toolbar + overflow menu (Task 4.2)

* [P4-T3] feat(query): cancel button wired to cancelQuery (Task 4.3)

* [P4-T4] feat(settings): export/import buttons + validation gate (Task 4.4)

* [P4-T5] fix(ui): gate macOS overlay drag strip to macOS only (Task 4.5)

* [P4-T6] feat(table-editor): SQLite Create/Edit Table mode (Task 4.6)

* [P5-T1] chore: bump 0.7.7 -> 0.7.8 + README/AGENTS/ROADMAP status (Task 5.1)

* [P5-T2] build(release): bundle mariadb-dump + mariadb client (system-first fallback) (Task 5.2)

* fix(cancel): propagate cancellations past wrapped->raw fallback (SQLite/PG/MySQL) + MySQL CONNECTION_ID cast

* fix(export): Excel export from overflow menu did nothing + add export success/error toasts

* fix(export): tree kebab export fetches table data when rows not loaded

* docs(readme): surface v0.7.8 features (MySQL/SQLite backup-sync, Excel export, query cancel, SQLite table editor, settings import/export)
This commit is contained in:
2026-08-08 00:09:36 +08:00
committed by GitHub
parent 9fb3222956
commit 32a7b852ec
59 changed files with 4068 additions and 572 deletions
+125
View File
@@ -26,6 +26,12 @@ pub struct SyncOptions {
pub target_connection_id: String,
pub schema: Option<String>,
pub tables: Option<Vec<String>>,
#[serde(default = "default_sync_db_type")]
pub db_type: String,
}
fn default_sync_db_type() -> String {
"postgresql".into()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -46,6 +52,73 @@ pub struct PgToolPaths {
pub psql: String,
}
/// Connection params for a MySQL server (decoupled from store/keychain so the
/// dump/restore/sync core stays headless-testable). Mirrors `PgConnParams`.
#[derive(Debug, Clone)]
pub struct MySqlConnParams {
pub host: String,
pub port: i64,
pub username: String,
pub database: String,
pub password: String,
}
impl MySqlConnParams {
pub fn new(host: String, port: i64, username: String, database: String, password: String) -> Self {
Self { host, port, username, database, password }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MySqlBackupOptions {
pub database: String,
pub file_path: String,
pub single_transaction: bool,
pub no_data: bool,
pub routines: bool,
pub triggers: bool,
pub events: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MySqlRestoreOptions {
pub database: String,
pub file_path: String,
pub clean: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SqliteBackupOptions {
pub file_path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SqliteRestoreOptions {
pub file_path: String,
pub clean: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MySqlToolStatus {
pub mysqldump_found: bool,
pub mysql_found: bool,
pub mysqldump_version: Option<String>,
pub mysql_version: Option<String>,
pub mysqldump_source: Option<String>,
pub mysql_source: Option<String>,
}
#[derive(Debug, Clone)]
pub struct MySqlToolPaths {
pub mysqldump: String,
pub mysql: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackupJob {
pub id: String,
@@ -117,4 +190,56 @@ mod tests {
assert!(json.contains("\"pg_dump_source\":\"system\""));
assert!(json.contains("\"pg_restore_source\":\"bundled\""));
}
#[test]
fn mysql_backup_options_serialize_camel_case() {
let opts = MySqlBackupOptions {
database: "shop".into(),
file_path: "/tmp/dump.sql".into(),
single_transaction: true,
no_data: false,
routines: true,
triggers: true,
events: false,
};
let json = serde_json::to_string(&opts).unwrap();
assert!(json.contains("\"singleTransaction\":true"));
assert!(json.contains("\"filePath\":\"/tmp/dump.sql\""));
assert!(!json.contains("no_owner"));
}
#[test]
fn sqlite_restore_options_serialize_camel_case() {
let opts = SqliteRestoreOptions { file_path: "/tmp/in.sql".into(), clean: true };
let json = serde_json::to_string(&opts).unwrap();
assert!(json.contains("\"filePath\":\"/tmp/in.sql\""));
assert!(json.contains("\"clean\":true"));
}
#[test]
fn mysql_tool_status_reports_source() {
let s = MySqlToolStatus {
mysqldump_found: true,
mysql_found: true,
mysqldump_version: Some("mariadb-dump 10.6".into()),
mysql_version: Some("mariadb 10.6".into()),
mysqldump_source: Some("bundled".into()),
mysql_source: Some("system".into()),
};
let json = serde_json::to_string(&s).unwrap();
assert!(json.contains("\"mysqldumpSource\":\"bundled\""));
}
#[test]
fn sync_options_carry_db_type() {
let s = SyncOptions {
source_connection_id: "a".into(),
target_connection_id: "b".into(),
schema: None,
tables: None,
db_type: "mysql".into(),
};
let json = serde_json::to_string(&s).unwrap();
assert!(json.contains("\"dbType\":\"mysql\""));
}
}
+7
View File
@@ -20,3 +20,10 @@ pub struct Settings {
pub editor_minimap: bool,
pub editor_tab_size: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SettingsExport {
pub schema_version: u32,
pub settings: Settings,
}