feat: compact boundary divider, overlay fix, approval window, PiP, Tauri enhancements
- Add CompactBoundaryDivider component for compact_boundary system messages - Fix readability overlay: v-if removes element entirely at 0% opacity - Add approval page and window composable - Add PiP window support and loading screen - Tauri: add window management commands and capabilities - Disable Ctrl+1..5 shortcuts in Tauri (handled by global shortcuts)
This commit is contained in:
41
src-tauri/Cargo.lock
generated
41
src-tauri/Cargo.lock
generated
@@ -18,6 +18,7 @@ dependencies = [
|
||||
"tauri-build",
|
||||
"tauri-plugin-clipboard-manager",
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-global-shortcut",
|
||||
"tauri-plugin-http",
|
||||
"tauri-plugin-notification",
|
||||
"tauri-plugin-store",
|
||||
@@ -1487,6 +1488,24 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "global-hotkey"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"keyboard-types",
|
||||
"objc2",
|
||||
"objc2-app-kit",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"thiserror 2.0.18",
|
||||
"windows-sys 0.59.0",
|
||||
"x11rb",
|
||||
"xkeysym",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gobject-sys"
|
||||
version = "0.18.0"
|
||||
@@ -4097,6 +4116,7 @@ dependencies = [
|
||||
"gtk",
|
||||
"heck 0.5.0",
|
||||
"http",
|
||||
"image",
|
||||
"jni",
|
||||
"libc",
|
||||
"log",
|
||||
@@ -4266,6 +4286,21 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-global-shortcut"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "424af23c7e88d05e4a1a6fc2c7be077912f8c76bd7900fd50aa2b7cbf5a2c405"
|
||||
dependencies = [
|
||||
"global-hotkey",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-http"
|
||||
version = "2.5.7"
|
||||
@@ -6009,6 +6044,12 @@ version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
|
||||
|
||||
[[package]]
|
||||
name = "xkeysym"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.1"
|
||||
|
||||
@@ -13,11 +13,12 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri = { version = "2", features = ["tray-icon", "image-png"] }
|
||||
tauri-plugin-http = "2"
|
||||
tauri-plugin-store = "2"
|
||||
tauri-plugin-notification = "2"
|
||||
tauri-plugin-clipboard-manager = "2"
|
||||
tauri-plugin-dialog = "2"
|
||||
tauri-plugin-global-shortcut = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-utils/schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Default permissions for Agent UI",
|
||||
"windows": ["main", "pip-terminal"],
|
||||
"windows": ["main", "pip-terminal", "pip-terminal-1", "pip-terminal-2", "pip-terminal-3", "pip-terminal-4", "pip-terminal-5", "approval-window", "loading-window"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
{
|
||||
@@ -28,9 +28,21 @@
|
||||
"core:window:allow-set-position",
|
||||
"core:window:allow-set-focus",
|
||||
"core:window:allow-set-decorations",
|
||||
"core:window:allow-show",
|
||||
"core:window:allow-hide",
|
||||
"core:window:allow-inner-position",
|
||||
"core:window:allow-inner-size",
|
||||
"core:window:allow-outer-position",
|
||||
"core:window:allow-outer-size",
|
||||
"core:webview:default",
|
||||
"core:webview:allow-create-webview-window",
|
||||
"core:webview:allow-webview-close",
|
||||
"core:window:allow-destroy"
|
||||
"core:window:allow-destroy",
|
||||
"global-shortcut:default",
|
||||
"global-shortcut:allow-register",
|
||||
"global-shortcut:allow-unregister",
|
||||
"global-shortcut:allow-is-registered",
|
||||
"global-shortcut:allow-unregister-all",
|
||||
"core:tray:default"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<!-- AndroidTV support -->
|
||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||
|
||||
|
||||
@@ -1,3 +1,46 @@
|
||||
use tauri::{
|
||||
menu::{Menu, MenuItem},
|
||||
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
||||
Manager, WebviewWindowBuilder, WindowEvent,
|
||||
};
|
||||
use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, Modifiers, Shortcut, ShortcutState};
|
||||
|
||||
fn show_main_window(app: &tauri::AppHandle) {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
let _ = window.show();
|
||||
let _ = window.unminimize();
|
||||
let _ = window.set_focus();
|
||||
}
|
||||
}
|
||||
|
||||
fn open_pip_terminal(app: &tauri::AppHandle, idx: u8) {
|
||||
let label = format!("pip-terminal-{}", idx);
|
||||
|
||||
// If PiP already exists, just focus it
|
||||
if let Some(win) = app.get_webview_window(&label) {
|
||||
let _ = win.set_focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new PiP window — the page itself handles terminal connection or showing "new session" modal
|
||||
let url = format!("/transcript-debug/{}?pip=1", idx);
|
||||
let x = 1520.0_f64; // sensible default, will be near right edge on 1920px screens
|
||||
let y = 60.0 + (idx as f64 - 1.0) * 40.0;
|
||||
|
||||
let _ = WebviewWindowBuilder::new(
|
||||
app,
|
||||
&label,
|
||||
tauri::WebviewUrl::App(url.into()),
|
||||
)
|
||||
.title(&format!("T{} - Agent UI", idx))
|
||||
.inner_size(380.0, 620.0)
|
||||
.position(x, y)
|
||||
.decorations(false)
|
||||
.resizable(true)
|
||||
.focused(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
@@ -6,6 +49,96 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.plugin(tauri_plugin_clipboard_manager::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(
|
||||
tauri_plugin_global_shortcut::Builder::new()
|
||||
.with_handler(|app, shortcut, event| {
|
||||
if event.state != ShortcutState::Pressed {
|
||||
return;
|
||||
}
|
||||
|
||||
let mods = shortcut.mods;
|
||||
let key = shortcut.key;
|
||||
|
||||
// Ctrl+Alt+E → show main window
|
||||
if mods == Modifiers::CONTROL | Modifiers::ALT && key == Code::KeyE {
|
||||
show_main_window(app);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+1-5 → open PiP terminal
|
||||
if mods == Modifiers::CONTROL {
|
||||
match key {
|
||||
Code::Digit1 => open_pip_terminal(app, 1),
|
||||
Code::Digit2 => open_pip_terminal(app, 2),
|
||||
Code::Digit3 => open_pip_terminal(app, 3),
|
||||
Code::Digit4 => open_pip_terminal(app, 4),
|
||||
Code::Digit5 => open_pip_terminal(app, 5),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
})
|
||||
.build(),
|
||||
)
|
||||
.setup(|app| {
|
||||
// Register global shortcuts (desktop only)
|
||||
#[cfg(desktop)]
|
||||
{
|
||||
let ctrl_alt = Modifiers::CONTROL | Modifiers::ALT;
|
||||
let ctrl = Modifiers::CONTROL;
|
||||
let shortcuts = [
|
||||
Shortcut::new(Some(ctrl_alt), Code::KeyE),
|
||||
Shortcut::new(Some(ctrl), Code::Digit1),
|
||||
Shortcut::new(Some(ctrl), Code::Digit2),
|
||||
Shortcut::new(Some(ctrl), Code::Digit3),
|
||||
Shortcut::new(Some(ctrl), Code::Digit4),
|
||||
Shortcut::new(Some(ctrl), Code::Digit5),
|
||||
];
|
||||
for s in &shortcuts {
|
||||
let _ = app.global_shortcut().register(*s);
|
||||
}
|
||||
}
|
||||
|
||||
// Build system tray (desktop only)
|
||||
#[cfg(desktop)]
|
||||
{
|
||||
let show_item = MenuItem::with_id(app, "show", "Show", true, None::<&str>)?;
|
||||
let quit_item = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
|
||||
let menu = Menu::with_items(app, &[&show_item, &quit_item])?;
|
||||
|
||||
TrayIconBuilder::new()
|
||||
.icon(app.default_window_icon().unwrap().clone())
|
||||
.menu(&menu)
|
||||
.show_menu_on_left_click(false)
|
||||
.on_menu_event(|app, event| match event.id.as_ref() {
|
||||
"show" => show_main_window(app),
|
||||
"quit" => app.exit(0),
|
||||
_ => {}
|
||||
})
|
||||
.on_tray_icon_event(|tray, event| {
|
||||
if let TrayIconEvent::Click {
|
||||
button: MouseButton::Left,
|
||||
button_state: MouseButtonState::Up,
|
||||
..
|
||||
} = event
|
||||
{
|
||||
show_main_window(tray.app_handle());
|
||||
}
|
||||
})
|
||||
.build(app)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.on_window_event(|window, event| {
|
||||
// Hide main window to tray instead of closing (desktop only)
|
||||
#[cfg(desktop)]
|
||||
if let WindowEvent::CloseRequested { api, .. } = event {
|
||||
if window.label() == "main" {
|
||||
api.prevent_close();
|
||||
let _ = window.hide();
|
||||
}
|
||||
}
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user