feat: add connection colors, query history, SQL autocomplete, and EXPLAIN visualizer

Add four developer/QA features:
- Connection color coding: color picker in dialog, colored indicators across toolbar, tabs, status bar, and connection selectors
- Query history: Rust backend with JSON file storage (500 entry cap), sidebar panel with search/clear, auto-recording from workspace
- Schema-aware SQL autocomplete: backend fetches column metadata, CodeMirror receives schema namespace with public tables unprefixed
- EXPLAIN ANALYZE visualizer: recursive tree view with cost-colored bars, expand/collapse nodes, buffers info, Results/Explain tab toggle

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 20:22:10 +03:00
parent 72c362dfae
commit 3b3e225e8f
21 changed files with 791 additions and 37 deletions

View File

@@ -2,6 +2,7 @@ use crate::error::{TuskError, TuskResult};
use crate::models::schema::{ColumnInfo, ConstraintInfo, IndexInfo, SchemaObject};
use crate::state::AppState;
use sqlx::Row;
use std::collections::HashMap;
use tauri::State;
#[tauri::command]
@@ -340,3 +341,40 @@ pub async fn get_table_indexes(
})
.collect())
}
#[tauri::command]
pub async fn get_completion_schema(
state: State<'_, AppState>,
connection_id: String,
) -> TuskResult<HashMap<String, HashMap<String, Vec<String>>>> {
let pools = state.pools.read().await;
let pool = pools
.get(&connection_id)
.ok_or(TuskError::NotConnected(connection_id))?;
let rows = sqlx::query(
"SELECT table_schema, table_name, column_name \
FROM information_schema.columns \
WHERE table_schema NOT IN ('pg_catalog', 'information_schema', 'pg_toast') \
ORDER BY table_schema, table_name, ordinal_position",
)
.fetch_all(pool)
.await
.map_err(TuskError::Database)?;
let mut result: HashMap<String, HashMap<String, Vec<String>>> = HashMap::new();
for row in &rows {
let schema: String = row.get(0);
let table: String = row.get(1);
let column: String = row.get(2);
result
.entry(schema)
.or_default()
.entry(table)
.or_default()
.push(column);
}
Ok(result)
}