feat(quick-pane): promote active runtime
This commit is contained in:
@@ -228,6 +228,17 @@ pub async fn create_quick_runtime(
|
||||
)
|
||||
.await
|
||||
}
|
||||
pub async fn promote_quick_runtime(
|
||||
socket_path: &str,
|
||||
runtime_id: &str,
|
||||
) -> Result<RuntimeResult, String> {
|
||||
typed_request(
|
||||
socket_path,
|
||||
"promote_quick_runtime",
|
||||
Some(json!({ "runtimeId": runtime_id })),
|
||||
)
|
||||
.await
|
||||
}
|
||||
pub async fn open_session_runtime(
|
||||
socket_path: &str,
|
||||
worktree_path: &str,
|
||||
|
||||
+101
-1
@@ -41,6 +41,7 @@ impl ManagedWindowVisibility {
|
||||
}
|
||||
// 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>,
|
||||
task: Mutex<Option<JoinHandle<()>>>,
|
||||
@@ -96,7 +97,10 @@ fn should_hide_close_request(label: &str) -> bool {
|
||||
label == "main" || label == "quick-pane"
|
||||
}
|
||||
|
||||
fn get_or_create_main(app: &AppHandle) -> Result<(WebviewWindow, bool), String> {
|
||||
fn get_or_create_main_with_visibility(
|
||||
app: &AppHandle,
|
||||
visible_when_created: bool,
|
||||
) -> Result<(WebviewWindow, bool), String> {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
return Ok((window, false));
|
||||
}
|
||||
@@ -109,11 +113,16 @@ fn get_or_create_main(app: &AppHandle) -> Result<(WebviewWindow, bool), String>
|
||||
.ok_or_else(|| "Main window configuration is missing".to_owned())?;
|
||||
let window = WebviewWindowBuilder::from_config(app, config)
|
||||
.map_err(|error| error.to_string())?
|
||||
.visible(visible_when_created)
|
||||
.build()
|
||||
.map_err(|error| error.to_string())?;
|
||||
Ok((window, true))
|
||||
}
|
||||
|
||||
fn get_or_create_main(app: &AppHandle) -> Result<(WebviewWindow, bool), String> {
|
||||
get_or_create_main_with_visibility(app, true)
|
||||
}
|
||||
|
||||
fn apply_main_window_action(app: &AppHandle, args: &[String]) {
|
||||
let (window, created) = match get_or_create_main(app) {
|
||||
Ok(result) => result,
|
||||
@@ -174,6 +183,44 @@ fn get_or_create_quick_pane(app: &AppHandle) -> Result<(WebviewWindow, bool), St
|
||||
Ok((window, true))
|
||||
}
|
||||
|
||||
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 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(())
|
||||
}
|
||||
|
||||
fn toggle_quick_pane(app: &AppHandle) {
|
||||
let (window, created) = match get_or_create_quick_pane(app) {
|
||||
Ok(result) => result,
|
||||
@@ -333,6 +380,45 @@ async fn create_quick_runtime(worktree_path: String) -> Result<bridge::RuntimeRe
|
||||
bridge::create_quick_runtime(&socket_path()?, &worktree_path).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn promote_quick_runtime(
|
||||
app: AppHandle,
|
||||
runtime_id: String,
|
||||
) -> 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 }));
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_pending_promoted_runtime(
|
||||
pending: State<'_, PendingPromotedRuntime>,
|
||||
) -> Result<Option<String>, String> {
|
||||
pending
|
||||
.0
|
||||
.lock()
|
||||
.map(|pending| pending.clone())
|
||||
.map_err(|_| "Could not read promoted runtime".to_owned())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn consume_pending_promoted_runtime(
|
||||
runtime_id: String,
|
||||
pending: State<'_, PendingPromotedRuntime>,
|
||||
) -> Result<bool, String> {
|
||||
let mut pending = pending
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|_| "Could not consume promoted runtime".to_owned())?;
|
||||
if pending.as_deref() == Some(runtime_id.as_str()) {
|
||||
*pending = None;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn open_session_runtime(
|
||||
worktree_path: String,
|
||||
@@ -582,6 +668,7 @@ pub fn run() {
|
||||
main: true,
|
||||
quick_pane: false,
|
||||
})))
|
||||
.manage(PendingPromotedRuntime(Mutex::new(None)))
|
||||
.manage(WorkspaceSubscription {
|
||||
generation: Arc::new(bridge::SubscriptionGeneration::default()),
|
||||
task: Mutex::new(None),
|
||||
@@ -628,6 +715,9 @@ pub fn run() {
|
||||
dismiss_quick_pane,
|
||||
create_session_runtime,
|
||||
create_quick_runtime,
|
||||
promote_quick_runtime,
|
||||
get_pending_promoted_runtime,
|
||||
consume_pending_promoted_runtime,
|
||||
open_session_runtime,
|
||||
close_session_runtime,
|
||||
close_quick_runtime,
|
||||
@@ -742,6 +832,16 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn promoted_runtime_pending_id_clears_only_after_matching_acknowledgement() {
|
||||
let pending = PendingPromotedRuntime(Mutex::new(Some("runtime-a".to_owned())));
|
||||
let mut value = pending.0.lock().unwrap();
|
||||
assert_ne!(value.as_deref(), Some("runtime-b"));
|
||||
assert_eq!(value.as_deref(), Some("runtime-a"));
|
||||
*value = None;
|
||||
assert_eq!(value.as_deref(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dismissed_quick_pane_reopens_with_one_toggle() {
|
||||
let mut visibility = ManagedWindowVisibility {
|
||||
|
||||
Reference in New Issue
Block a user