refactor!: remove redundant compatibility layers

Use native browser and Tauri behavior instead of custom selector, dialog, and window-state implementations. Remove obsolete bridge, relay, asset, and dependency paths to keep the supported surface small.

BREAKING CHANGE: The Noctalia relay no longer accepts --agent, and the Tauri host no longer exposes the legacy load_agent command.
This commit is contained in:
2026-08-31 17:35:07 +02:00
parent a4ee3eb7a8
commit 0949c6f0d7
37 changed files with 510 additions and 1341 deletions
+5 -152
View File
@@ -1,7 +1,6 @@
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::{json, Value};
use std::env;
use std::path::Path;
use std::sync::{Arc, Mutex};
use tauri::{AppHandle, Emitter};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
@@ -346,72 +345,11 @@ pub async fn new_session(socket_path: &str, agent_id: &str) -> Result<Value, Str
request(socket_path, "new_session", Some(agent_id), None).await
}
fn fallback_transcript(state: &Value) -> Value {
let Some(path) = state.pointer("/data/sessionFile").and_then(Value::as_str) else {
return json!({ "data": { "messages": [] } });
};
let path = Path::new(path);
if !path.is_absolute()
|| path.extension().and_then(|extension| extension.to_str()) != Some("jsonl")
{
return json!({ "data": { "messages": [] } });
}
let messages = std::fs::read_to_string(path)
.ok()
.map(|content| {
content
.lines()
.filter_map(|line| serde_json::from_str::<Value>(line).ok())
.filter(|entry| entry.get("type").and_then(Value::as_str) == Some("message"))
.filter_map(|entry| entry.get("message").cloned())
.collect::<Vec<_>>()
})
.unwrap_or_default();
json!({ "data": { "messages": messages } })
}
fn compact_commands(value: Value) -> Value {
let commands = value
.pointer("/data/commands")
.and_then(Value::as_array)
.map(|commands| {
commands
.iter()
.map(|command| {
let mut compact = serde_json::Map::new();
for field in ["name", "description"] {
if let Some(value) = command.get(field) {
compact.insert(field.to_owned(), value.clone());
}
}
Value::Object(compact)
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
json!({ "data": { "commands": commands } })
}
pub async fn load_agent(socket_path: &str, agent_id: &str) -> Result<Value, String> {
// Older bridge daemons can still serve the conversation while they await a restart.
let stats = request(socket_path, "get_session_stats", Some(agent_id), None)
.await
.unwrap_or_else(|_| json!({ "data": {} }));
let (state, commands, models) = tokio::try_join!(
request(socket_path, "get_state", Some(agent_id), None),
request(socket_path, "get_commands", Some(agent_id), None),
request(socket_path, "get_available_models", Some(agent_id), None),
)?;
Ok(json!({
"state": state,
"stats": stats,
"transcript": fallback_transcript(&state),
"commands": compact_commands(commands),
"models": models
}))
}
pub async fn submit_prompt(socket_path: &str, agent_id: &str, message: &str) -> Result<Value, String> {
pub async fn submit_prompt(
socket_path: &str,
agent_id: &str,
message: &str,
) -> Result<Value, String> {
request(
socket_path,
"submit_prompt",
@@ -896,91 +834,6 @@ mod tests {
std::fs::remove_file(path).expect("socket cleanup");
}
#[test]
fn reads_messages_from_the_saved_session_file_when_transcript_rpc_is_unavailable() {
let path = format!(
"{}/pi-status-ui-history-{}.jsonl",
env::temp_dir().display(),
std::process::id()
);
std::fs::write(
&path,
concat!(
"{\"type\":\"session\",\"id\":\"session-1\"}\n",
"{\"type\":\"message\",\"message\":{\"role\":\"user\",\"content\":\"Hello\"}}\n",
"{\"type\":\"message\",\"message\":{\"role\":\"assistant\",\"content\":\"Hi\"}}\n"
),
)
.expect("session fixture");
let transcript = fallback_transcript(&json!({
"data": { "sessionFile": path }
}));
assert_eq!(
transcript["data"]["messages"],
json!([
{ "role": "user", "content": "Hello" },
{ "role": "assistant", "content": "Hi" }
])
);
std::fs::remove_file(path).expect("session cleanup");
}
#[tokio::test]
async fn loads_state_history_commands_and_models_as_one_snapshot() {
let path = format!(
"{}/pi-status-ui-load-{}.sock",
env::temp_dir().display(),
std::process::id()
);
let _ = std::fs::remove_file(&path);
let listener = UnixListener::bind(&path).expect("listener");
let server = tokio::spawn(async move {
for _ in 0..4 {
let (stream, _) = listener.accept().await.expect("connection");
tokio::spawn(async move {
let mut line = String::new();
let mut reader = BufReader::new(stream);
reader.read_line(&mut line).await.expect("request");
let request: Value = serde_json::from_str(&line).expect("JSON request");
assert_eq!(request["agentId"], "agent-1");
let result = match request["op"].as_str() {
Some("get_state") => json!({ "data": { "isStreaming": false } }),
Some("get_session_stats") => json!({
"data": { "contextUsage": { "tokens": 32000, "contextWindow": 200000 } }
}),
Some("get_commands") => json!({ "data": { "commands": [{
"name": "resume",
"description": "Resume a saved session",
"sourceInfo": { "path": "/very/large/extension/metadata" }
}] } }),
Some("get_available_models") => json!({ "data": { "models": [] } }),
other => panic!("unexpected operation: {other:?}"),
};
let mut response = serde_json::to_vec(&json!({
"id": "tauri-ui", "ok": true, "result": result
}))
.expect("response JSON");
response.push(b'\n');
reader
.get_mut()
.write_all(&response)
.await
.expect("response");
});
}
});
let snapshot = load_agent(&path, "agent-1").await.expect("snapshot");
assert_eq!(snapshot["state"]["data"]["isStreaming"], false);
assert_eq!(snapshot["stats"]["data"]["contextUsage"]["tokens"], 32000);
assert_eq!(snapshot["transcript"]["data"]["messages"], json!([]));
assert_eq!(
snapshot["commands"]["data"]["commands"],
json!([{ "name": "resume", "description": "Resume a saved session" }])
);
server.await.expect("server succeeds");
std::fs::remove_file(path).expect("socket cleanup");
}
fn runtime(id: &str) -> Value {
json!({
"runtimeId": id,
+84 -190
View File
@@ -14,33 +14,6 @@ use tauri::{
};
struct LegacySubscription(Mutex<Option<JoinHandle<()>>>);
#[derive(Debug, PartialEq, Eq)]
struct ManagedWindowVisibility {
main: bool,
quick_pane: bool,
}
impl ManagedWindowVisibility {
fn main_shown(&mut self) {
self.main = true;
self.quick_pane = false;
}
fn quick_pane_shown(&mut self) {
self.main = false;
self.quick_pane = true;
}
fn hidden(&mut self, label: &str) {
if label == "main" {
self.main = false;
} else if label == "quick-pane" {
self.quick_pane = false;
}
}
}
// One mutex serializes cross-window transitions, avoiding nested visibility-lock ordering.
struct WindowVisibility(Mutex<ManagedWindowVisibility>);
struct PendingPromotedRuntime(Mutex<Option<String>>);
struct WorkspaceSubscription {
generation: Arc<bridge::SubscriptionGeneration>,
@@ -131,22 +104,10 @@ fn apply_main_window_action(app: &AppHandle, args: &[String]) {
return;
}
};
let Some(state) = app.try_state::<WindowVisibility>() else {
eprintln!("Window visibility state is unavailable");
return;
};
let mut visibility = state
.0
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
if created {
visibility.main = false;
}
match window_action(args, visibility.main) {
let visible = !created && window.is_visible().unwrap_or(false);
match window_action(args, visible) {
WindowAction::Hide => {
if window.hide().is_ok() {
visibility.hidden("main");
}
let _ = window.hide();
}
WindowAction::ShowAndFocus => {
if let Some(quick_pane) = app.get_webview_window("quick-pane") {
@@ -155,9 +116,7 @@ fn apply_main_window_action(app: &AppHandle, args: &[String]) {
return;
}
}
visibility.hidden("quick-pane");
if window.show().is_ok() {
visibility.main_shown();
let _ = window.unminimize();
let _ = window.set_focus();
}
@@ -185,37 +144,24 @@ fn get_or_create_quick_pane(app: &AppHandle) -> Result<(WebviewWindow, bool), St
fn show_main_for_promoted_runtime(app: &AppHandle, runtime_id: String) -> Result<(), String> {
// A recreated Main starts hidden, so Quick is never visible with it.
let (window, created) = get_or_create_main_with_visibility(app, false)?;
let state = app
.try_state::<WindowVisibility>()
.ok_or_else(|| "Window visibility state is unavailable".to_owned())?;
let (window, _) = get_or_create_main_with_visibility(app, false)?;
let pending = app
.try_state::<PendingPromotedRuntime>()
.ok_or_else(|| "Promoted runtime state is unavailable".to_owned())?;
let mut visibility = state
.0
.lock()
.map_err(|_| "Could not update window visibility".to_owned())?;
let mut pending = pending
.0
.lock()
.map_err(|_| "Could not queue promoted runtime".to_owned())?;
if created {
visibility.main = false;
}
if let Some(quick_pane) = app.get_webview_window("quick-pane") {
quick_pane
.hide()
.map_err(|error| format!("Could not hide Quick Pane before promotion: {error}"))?;
}
visibility.hidden("quick-pane");
// Retain this on show failure; a later Main startup can consume it.
*pending = Some(runtime_id);
visibility.main = false;
window
.show()
.map_err(|error| format!("Could not show Main after hiding Quick Pane: {error}"))?;
visibility.main_shown();
let _ = window.unminimize();
let _ = window.set_focus();
Ok(())
@@ -229,22 +175,10 @@ fn toggle_quick_pane(app: &AppHandle) {
return;
}
};
let Some(state) = app.try_state::<WindowVisibility>() else {
eprintln!("Window visibility state is unavailable");
return;
};
let mut visibility = state
.0
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
if created {
visibility.quick_pane = false;
}
match quick_pane_action(visibility.quick_pane) {
let visible = !created && window.is_visible().unwrap_or(false);
match quick_pane_action(visible) {
WindowAction::Hide => {
if window.hide().is_ok() {
visibility.hidden("quick-pane");
}
let _ = window.hide();
}
WindowAction::ShowAndFocus => {
if let Some(main) = app.get_webview_window("main") {
@@ -253,9 +187,7 @@ fn toggle_quick_pane(app: &AppHandle) {
return;
}
}
visibility.hidden("main");
if window.show().is_ok() {
visibility.quick_pane_shown();
let _ = window.unminimize();
let _ = window.set_focus();
let _ = app.emit("quick-pane-shown", ());
@@ -288,30 +220,52 @@ fn requested_new_worktree(app: &AppHandle, args: &[String]) -> Option<String> {
}
fn launch_new_session(app: AppHandle, args: &[String]) {
let Some(worktree_path) = requested_new_worktree(&app, args) else { return };
let Some(worktree_path) = requested_new_worktree(&app, args) else {
return;
};
tauri::async_runtime::spawn(async move {
// Give a newly-created webview time to register its frontend listeners.
tokio::time::sleep(Duration::from_millis(350)).await;
let _ = app.emit("workspace-new-session", json!({
"phase": "starting",
"detail": "Starting a new Pi session…"
}));
let _ = app.emit(
"workspace-new-session",
json!({
"phase": "starting",
"detail": "Starting a new Pi session…"
}),
);
match socket_path().map(|socket| (socket, worktree_path)) {
Ok((socket, worktree_path)) => match bridge::create_session_runtime(&socket, &worktree_path).await {
Ok(result) => { let _ = app.emit("workspace-new-session", json!({
"phase": "ready",
"runtimeId": result.runtime.runtime_id,
"detail": "New session ready"
})); }
Err(error) => { let _ = app.emit("workspace-new-session", json!({
"phase": "error",
"detail": format!("Could not start session: {error}")
})); }
},
Err(error) => { let _ = app.emit("workspace-new-session", json!({
"phase": "error",
"detail": error
})); }
Ok((socket, worktree_path)) => {
match bridge::create_session_runtime(&socket, &worktree_path).await {
Ok(result) => {
let _ = app.emit(
"workspace-new-session",
json!({
"phase": "ready",
"runtimeId": result.runtime.runtime_id,
"detail": "New session ready"
}),
);
}
Err(error) => {
let _ = app.emit(
"workspace-new-session",
json!({
"phase": "error",
"detail": format!("Could not start session: {error}")
}),
);
}
}
}
Err(error) => {
let _ = app.emit(
"workspace-new-session",
json!({
"phase": "error",
"detail": error
}),
);
}
}
});
}
@@ -344,7 +298,9 @@ fn resize_quick_pane(app: AppHandle, height: f64) -> Result<(), String> {
let quick_pane = app
.get_webview_window("quick-pane")
.ok_or_else(|| "Quick Pane window is unavailable".to_owned())?;
let scale_factor = quick_pane.scale_factor().map_err(|error| error.to_string())?;
let scale_factor = quick_pane
.scale_factor()
.map_err(|error| error.to_string())?;
let current_size = quick_pane.inner_size().map_err(|error| error.to_string())?;
quick_pane
.set_size(LogicalSize::new(
@@ -355,20 +311,11 @@ fn resize_quick_pane(app: AppHandle, height: f64) -> Result<(), String> {
}
#[tauri::command]
fn dismiss_quick_pane(
app: AppHandle,
visibility: State<'_, WindowVisibility>,
) -> Result<(), String> {
let quick_pane = app
.get_webview_window("quick-pane")
.ok_or_else(|| "Quick Pane window is unavailable".to_owned())?;
let mut visibility = visibility
.0
.lock()
.map_err(|_| "Could not update window visibility".to_owned())?;
quick_pane.hide().map_err(|error| error.to_string())?;
visibility.hidden("quick-pane");
Ok(())
fn dismiss_quick_pane(app: AppHandle) -> Result<(), String> {
app.get_webview_window("quick-pane")
.ok_or_else(|| "Quick Pane window is unavailable".to_owned())?
.hide()
.map_err(|error| error.to_string())
}
#[tauri::command]
@@ -388,7 +335,10 @@ async fn promote_quick_runtime(
) -> Result<bridge::RuntimeResult, String> {
let result = bridge::promote_quick_runtime(&socket_path()?, &runtime_id).await?;
show_main_for_promoted_runtime(&app, result.runtime.runtime_id.clone())?;
let _ = app.emit("workspace-promoted-runtime", json!({ "runtimeId": result.runtime.runtime_id }));
let _ = app.emit(
"workspace-promoted-runtime",
json!({ "runtimeId": result.runtime.runtime_id }),
);
Ok(result)
}
@@ -490,11 +440,6 @@ async fn forget_directory(worktree_path: String) -> Result<Value, String> {
bridge::forget_directory(&socket_path()?, &worktree_path).await
}
#[tauri::command]
async fn load_agent(agent_id: String) -> Result<Value, String> {
bridge::load_agent(&socket_path()?, &agent_id).await
}
#[tauri::command]
async fn list_sessions(agent_id: String) -> Result<Value, String> {
bridge::list_sessions(&socket_path()?, &agent_id).await
@@ -557,7 +502,8 @@ async fn pi_rpc_command(agent_id: String, command: String, input: Value) -> Resu
"pi_rpc_command",
Some(&agent_id),
Some(serde_json::json!({ "command": command, "input": input })),
).await
)
.await
}
#[tauri::command]
@@ -665,10 +611,6 @@ pub fn run() {
let builder = tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.manage(LegacySubscription(Mutex::new(None)))
.manage(WindowVisibility(Mutex::new(ManagedWindowVisibility {
main: true,
quick_pane: false,
})))
.manage(PendingPromotedRuntime(Mutex::new(None)))
.manage(WorkspaceSubscription {
generation: Arc::new(bridge::SubscriptionGeneration::default()),
@@ -686,17 +628,7 @@ pub fn run() {
if should_hide_close_request(window.label()) {
if let WindowEvent::CloseRequested { api, .. } = event {
api.prevent_close();
if let Some(state) = window.app_handle().try_state::<WindowVisibility>() {
let mut visibility = state
.0
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
if window.hide().is_ok() {
visibility.hidden(window.label());
}
} else {
let _ = window.hide();
}
let _ = window.hide();
}
}
})
@@ -732,7 +664,6 @@ pub fn run() {
list_directories,
select_worktree,
forget_directory,
load_agent,
list_sessions,
switch_session,
new_session,
@@ -776,10 +707,26 @@ mod tests {
#[test]
fn new_session_uses_an_explicit_absolute_worktree() {
let args = vec!["pi-status-ui".into(), "--new".into(), "--worktree".into(), "/workspace".into()];
assert_eq!(requested_new_worktree_argument(&args), Some("/workspace".into()));
let relative = vec!["pi-status-ui".into(), "--new".into(), "--worktree".into(), "workspace".into()];
assert_eq!(requested_new_worktree_argument(&relative).filter(|path| Path::new(path).is_absolute()), None);
let args = vec![
"pi-status-ui".into(),
"--new".into(),
"--worktree".into(),
"/workspace".into(),
];
assert_eq!(
requested_new_worktree_argument(&args),
Some("/workspace".into())
);
let relative = vec![
"pi-status-ui".into(),
"--new".into(),
"--worktree".into(),
"workspace".into(),
];
assert_eq!(
requested_new_worktree_argument(&relative).filter(|path| Path::new(path).is_absolute()),
None
);
}
#[test]
@@ -797,42 +744,6 @@ mod tests {
assert!(!should_hide_close_request("other"));
}
#[test]
fn showing_main_clears_quick_pane_visibility() {
let mut visibility = ManagedWindowVisibility {
main: false,
quick_pane: true,
};
visibility.main_shown();
assert_eq!(
visibility,
ManagedWindowVisibility {
main: true,
quick_pane: false,
}
);
}
#[test]
fn showing_quick_pane_clears_main_visibility() {
let mut visibility = ManagedWindowVisibility {
main: true,
quick_pane: false,
};
visibility.quick_pane_shown();
assert_eq!(
visibility,
ManagedWindowVisibility {
main: false,
quick_pane: true,
}
);
}
#[test]
fn promoted_runtime_pending_id_clears_only_after_matching_acknowledgement() {
let pending = PendingPromotedRuntime(Mutex::new(Some("runtime-a".to_owned())));
@@ -843,23 +754,6 @@ mod tests {
assert_eq!(value.as_deref(), None);
}
#[test]
fn dismissed_quick_pane_reopens_with_one_toggle() {
let mut visibility = ManagedWindowVisibility {
main: false,
quick_pane: true,
};
visibility.hidden("quick-pane");
assert_eq!(
quick_pane_action(visibility.quick_pane),
WindowAction::ShowAndFocus
);
visibility.quick_pane_shown();
assert!(visibility.quick_pane);
assert!(!visibility.main);
}
#[test]
fn main_recreation_uses_the_complete_configured_window() {
let context: tauri::Context<tauri::Wry> = tauri::generate_context!();