What persists: every card (kind, position, size, title, note text, ptyId, z-order) + viewport pan/zoom + maxZ counter,
in a single board.json under app_data_dir() (~/.local/share/{appName}/board.json on Linux).
Write pattern: tmp file + atomic rename, debounced 400ms. A crash mid-write can't corrupt the existing file; worst
case you lose ~400ms of recent edits.
Read pattern: load on app start, gate the Canvas render on the result. Missing file → default welcome board. Parse
error → log and fall back to default (won't overwrite the bad file until the user makes changes; actually it will save
over it after the next change — if you want to preserve a corrupt file for recovery that's a one-line tweak).
Terminal caveat: live PTY processes die with the app session, so sanitize() in App.tsx clears every terminal card's
ptyId on load. Each terminal card respawns a fresh shell on mount via the existing pty.spawn path. The card's
position, size, and title are preserved.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
mod pty;
|
||||
mod storage;
|
||||
|
||||
use pty::{pty_kill, pty_resize, pty_spawn, pty_write, PtyState};
|
||||
use storage::{board_load, board_save};
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
@@ -33,6 +35,8 @@ pub fn run() {
|
||||
pty_write,
|
||||
pty_resize,
|
||||
pty_kill,
|
||||
board_save,
|
||||
board_load,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
36
src-tauri/src/storage.rs
Normal file
36
src-tauri/src/storage.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde_json::Value;
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
fn board_path(app: &AppHandle) -> Result<PathBuf, String> {
|
||||
let dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.map_err(|e| format!("app_data_dir: {e}"))?;
|
||||
fs::create_dir_all(&dir).map_err(|e| format!("create_dir_all: {e}"))?;
|
||||
Ok(dir.join("board.json"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn board_save(app: AppHandle, state: Value) -> Result<(), String> {
|
||||
let path = board_path(&app)?;
|
||||
let tmp = path.with_extension("json.tmp");
|
||||
let body = serde_json::to_string_pretty(&state).map_err(|e| e.to_string())?;
|
||||
fs::write(&tmp, body).map_err(|e| format!("write tmp: {e}"))?;
|
||||
// Atomic replace so a crash mid-write can't corrupt the existing file.
|
||||
fs::rename(&tmp, &path).map_err(|e| format!("rename: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn board_load(app: AppHandle) -> Result<Option<Value>, String> {
|
||||
let path = board_path(&app)?;
|
||||
if !path.exists() {
|
||||
return Ok(None);
|
||||
}
|
||||
let body = fs::read_to_string(&path).map_err(|e| format!("read: {e}"))?;
|
||||
let v = serde_json::from_str(&body).map_err(|e| format!("parse: {e}"))?;
|
||||
Ok(Some(v))
|
||||
}
|
||||
Reference in New Issue
Block a user