Compare commits

...

2 Commits

Author SHA1 Message Date
Vantz Stockwell
ffe79e9a54 fix: add Tauri v2 capabilities — unblock event:listen for SSH terminal
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 2m50s
The app had no capabilities file, so Tauri v2's ACL blocked all
frontend listen() calls. SSH connections succeeded on the Rust side
but the terminal never received data events, appearing as "nothing
happened." Grants core:default, core:event:default, core🪟default,
and shell:allow-open.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:00:54 -04:00
Vantz Stockwell
6e33bbdcf1 fix: serialize tags as array (not JSON string), DevTools debug-only
- ConnectionRecord.tags changed from String to Vec<String> so the
  frontend receives a proper array instead of a raw JSON string.
  The old behavior caused v-for to iterate characters, corrupting
  the connection display in the sidebar.
- DevTools now only auto-opens in debug builds (cfg(debug_assertions)),
  not in production.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:58:58 -04:00
3 changed files with 27 additions and 13 deletions

View File

@ -0,0 +1,11 @@
{
"identifier": "default",
"description": "Default capabilities for the main Wraith window",
"windows": ["main"],
"permissions": [
"core:default",
"core:event:default",
"core:window:default",
"shell:allow-open"
]
}

View File

@ -38,10 +38,8 @@ pub struct ConnectionRecord {
pub group_id: Option<i64>,
pub credential_id: Option<i64>,
pub color: Option<String>,
/// JSON array string, e.g. `["linux","prod"]`
pub tags: String,
pub tags: Vec<String>,
pub notes: Option<String>,
/// JSON object string, e.g. `{"keepalive":30}`
pub options: String,
pub sort_order: i64,
pub last_connected: Option<String>,
@ -254,7 +252,7 @@ impl ConnectionService {
group_id: input.group_id,
credential_id: input.credential_id,
color: input.color,
tags: tags_json,
tags: input.tags,
notes: input.notes,
options: options_json,
sort_order,
@ -439,6 +437,8 @@ impl ConnectionService {
///
/// Column order must match the SELECT lists used throughout this module.
fn map_connection_row(row: &rusqlite::Row<'_>) -> rusqlite::Result<ConnectionRecord> {
let tags_json: String = row.get(8)?;
let tags: Vec<String> = serde_json::from_str(&tags_json).unwrap_or_default();
Ok(ConnectionRecord {
id: row.get(0)?,
name: row.get(1)?,
@ -448,7 +448,7 @@ fn map_connection_row(row: &rusqlite::Row<'_>) -> rusqlite::Result<ConnectionRec
group_id: row.get::<_, Option<i64>>(5)?,
credential_id: row.get::<_, Option<i64>>(6)?,
color: row.get::<_, Option<String>>(7)?,
tags: row.get(8)?,
tags,
notes: row.get::<_, Option<String>>(9)?,
options: row.get(10)?,
sort_order: row.get(11)?,
@ -556,12 +556,10 @@ mod tests {
}
#[test]
fn tags_serialised_as_json_array() {
fn tags_serialised_as_vec() {
let svc = make_service();
let rec = svc.create_connection(default_input("tagged")).unwrap();
// tags should be a valid JSON array
let parsed: Vec<String> = serde_json::from_str(&rec.tags).unwrap();
assert_eq!(parsed, vec!["linux", "prod"]);
assert_eq!(rec.tags, vec!["linux", "prod"]);
}
#[test]
@ -629,8 +627,7 @@ mod tests {
.unwrap();
let updated = svc.get_connection(rec.id).unwrap();
let parsed: Vec<String> = serde_json::from_str(&updated.tags).unwrap();
assert_eq!(parsed, vec!["windows"]);
assert_eq!(updated.tags, vec!["windows"]);
}
#[test]

View File

@ -87,8 +87,14 @@ pub fn run() {
.plugin(tauri_plugin_shell::init())
.manage(app_state)
.setup(|app| {
#[cfg(debug_assertions)]
{
use tauri::Manager;
if let Some(window) = app.get_webview_window("main") { let _ = window.open_devtools(); }
if let Some(window) = app.get_webview_window("main") {
window.open_devtools();
}
}
let _ = app;
Ok(())
})
.invoke_handler(tauri::generate_handler![