Add Perforce-style command Log panel + UI scale setting
Backend emits a p4-log event per command (cmd + item count + status). Frontend adds a bottom Log panel toggled from a new Window menu (Ctrl+L), an always-on status bar showing the last command, and a hover log-peek popover over the activity indicator. Settings gains an interface-scale (zoom) control (50–200%, persisted). All new strings translated (5 langs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -4,8 +4,28 @@
|
||||
use serde_json::Value;
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::Mutex;
|
||||
use tauri::State;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use tauri::{AppHandle, Emitter, State};
|
||||
|
||||
/// App handle stashed at startup so the low-level p4 helpers can emit a live
|
||||
/// command log to the frontend (a Perforce-style "Log" panel).
|
||||
static APP: OnceLock<AppHandle> = OnceLock::new();
|
||||
|
||||
/// Emit one p4-log entry to the UI: the command line, how many items/lines it
|
||||
/// returned, whether it succeeded, and any error text.
|
||||
fn log_p4(args: &[&str], count: i64, ok: bool, err: Option<&str>) {
|
||||
if let Some(app) = APP.get() {
|
||||
let _ = app.emit(
|
||||
"p4-log",
|
||||
serde_json::json!({
|
||||
"cmd": format!("p4 {}", args.join(" ")),
|
||||
"count": count,
|
||||
"ok": ok,
|
||||
"err": err,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct Conn {
|
||||
@ -43,10 +63,14 @@ fn p4_cmd(conn: &Conn, tagged: bool) -> Command {
|
||||
|
||||
/// Run a tagged (JSON) p4 command and collect the objects it prints.
|
||||
fn run_json(conn: &Conn, args: &[&str]) -> Result<Vec<Value>, String> {
|
||||
let out = p4_cmd(conn, true)
|
||||
.args(args)
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to start p4: {e}"))?;
|
||||
let out = match p4_cmd(conn, true).args(args).output() {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
let m = format!("Failed to start p4: {e}");
|
||||
log_p4(args, 0, false, Some(&m));
|
||||
return Err(m);
|
||||
}
|
||||
};
|
||||
|
||||
let text = String::from_utf8_lossy(&out.stdout);
|
||||
let mut items = Vec::new();
|
||||
@ -69,6 +93,7 @@ fn run_json(conn: &Conn, args: &[&str]) -> Result<Vec<Value>, String> {
|
||||
.unwrap_or("unknown p4 error")
|
||||
.trim()
|
||||
.to_string();
|
||||
log_p4(args, 0, false, Some(&msg));
|
||||
return Err(msg);
|
||||
}
|
||||
// informational message (e.g. "File(s) not opened."), not a data record — skip
|
||||
@ -82,9 +107,11 @@ fn run_json(conn: &Conn, args: &[&str]) -> Result<Vec<Value>, String> {
|
||||
if items.is_empty() && !out.status.success() {
|
||||
let e = String::from_utf8_lossy(&out.stderr).trim().to_string();
|
||||
if !e.is_empty() {
|
||||
log_p4(args, 0, false, Some(&e));
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
log_p4(args, items.len() as i64, true, None);
|
||||
Ok(items)
|
||||
}
|
||||
|
||||
@ -279,15 +306,21 @@ async fn p4_disconnect(state: State<'_, AppState>) -> Result<(), String> {
|
||||
|
||||
/// Run a non-tagged p4 command (raw text out). Errors carry stderr.
|
||||
fn run_text(conn: &Conn, args: &[&str]) -> Result<String, String> {
|
||||
let out = p4_cmd(conn, false)
|
||||
.args(args)
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to start p4: {e}"))?;
|
||||
let out = match p4_cmd(conn, false).args(args).output() {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
let m = format!("Failed to start p4: {e}");
|
||||
log_p4(args, 0, false, Some(&m));
|
||||
return Err(m);
|
||||
}
|
||||
};
|
||||
let stdout = String::from_utf8_lossy(&out.stdout).trim().to_string();
|
||||
let stderr = String::from_utf8_lossy(&out.stderr).trim().to_string();
|
||||
if !out.status.success() && !stderr.is_empty() {
|
||||
log_p4(args, 0, false, Some(&stderr));
|
||||
return Err(stderr);
|
||||
}
|
||||
log_p4(args, stdout.lines().count() as i64, true, None);
|
||||
Ok(if stdout.is_empty() { stderr } else { stdout })
|
||||
}
|
||||
|
||||
@ -568,6 +601,11 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_process::init());
|
||||
}
|
||||
builder
|
||||
.setup(|app| {
|
||||
// stash the app handle so p4 helpers can emit the live command log
|
||||
let _ = APP.set(app.handle().clone());
|
||||
Ok(())
|
||||
})
|
||||
.manage(AppState::default())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
p4_clients,
|
||||
|
||||
Reference in New Issue
Block a user