Compare commits

...
Author SHA1 Message Date
Bailey Dixon d8a6bf0ce6 Merge pull request #607 from AYin-Z/fix/tray-notice-capability
fix(desktop): grant tray capability to the notice and evidence windows
2026-09-23 18:20:13 -04:00
Bailey Dixon 9e96111813 fix(desktop): restore secondary tray window dismissal
Give notice and evidence only the Tauri hide permission, retain secondary windows on native close, and ignore stale screenshot responses.

Based on contributor report and PR #607 by AYin-Z. Refs #606.

(cherry picked from commit 0d7ea149cf3e0e7605607733499e7ac821ce4ff2)
2026-09-23 18:06:31 -04:00
Bailey Dixon 42efc01d58 Merge PR #607 tray capability fix into current dev 2026-09-23 18:06:06 -04:00
Bailey Dixon dc93d5eab2 Merge pull request #628 from Codename-11/fix/media-consumer-parity
fix: render proactive and desktop media with private cleanup
2026-09-23 18:02:47 -04:00
AYin-Z b21b9c225c fix(desktop): grant tray capability to the notice and evidence windows
The tray capability listed only main and grant while tauri.conf.json declares four windows: main, grant, notice and evidence. The notice and evidence windows are created with decorations:false and dismiss themselves through the core window API, so without the capability their close controls and auto-hide timers are rejected - silently, because the callers use void on the promise.

Closes #606.
2026-09-19 20:58:07 +08:00
5 changed files with 65 additions and 5 deletions
+1
View File
@@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
### Fixed
- Desktop tray notices, screenshot evidence, and grant prompts stay reusable after dismissal; screenshot evidence keeps the most recently selected image. (#606)
- Proactive phone Thread messages render relay-token and host-path media as attachments while preserving multiline text; notification previews omit media markers. (#485)
- Desktop computer screenshots attach validated image bytes to the host tool result instead of returning base64 as plain text.
- Relay-owned media uploads are removed on token expiry, eviction, and shutdown; media activity logs omit tokens, file paths, and screenshot bytes.
@@ -0,0 +1,7 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "secondary-windows",
"description": "Dismiss the connection notice and screenshot evidence windows",
"windows": ["notice", "evidence"],
"permissions": ["core:window:allow-hide"]
}
+7 -3
View File
@@ -2514,10 +2514,14 @@ mod app {
label,
event: WindowEvent::CloseRequested { api, .. },
..
} if label == "main" => {
} if matches!(label.as_str(), "main" | "grant" | "notice" | "evidence") => {
api.prevent_close();
if let Some(window) = handle.get_webview_window("main") {
request_main_hide(&window);
if let Some(window) = handle.get_webview_window(&label) {
if label == "main" {
request_main_hide(&window);
} else {
let _ = window.hide();
}
}
}
_ => {}
+44
View File
@@ -340,3 +340,47 @@ fn management_window_keeps_the_reviewed_compact_geometry() {
assert!(ui.contains("hide().finally(() => setWindowVisible(true))"));
assert!(ui.contains("document.visibilityState === 'visible'"));
}
#[test]
fn secondary_windows_have_only_the_dismissal_permission() {
let config: serde_json::Value =
serde_json::from_str(include_str!("../tauri.conf.json")).unwrap();
let main: serde_json::Value =
serde_json::from_str(include_str!("../capabilities/default.json")).unwrap();
let secondary: serde_json::Value =
serde_json::from_str(include_str!("../capabilities/secondary-windows.json")).unwrap();
let configured: std::collections::BTreeSet<&str> = config["app"]["windows"]
.as_array()
.unwrap()
.iter()
.map(|window| window["label"].as_str().unwrap())
.collect();
let main_windows: std::collections::BTreeSet<&str> = main["windows"]
.as_array()
.unwrap()
.iter()
.map(|label| label.as_str().unwrap())
.collect();
let secondary_windows: std::collections::BTreeSet<&str> = secondary["windows"]
.as_array()
.unwrap()
.iter()
.map(|label| label.as_str().unwrap())
.collect();
assert_eq!(main_windows, ["main", "grant"].into_iter().collect());
assert_eq!(
secondary_windows,
["notice", "evidence"].into_iter().collect()
);
assert_eq!(
configured,
main_windows.union(&secondary_windows).copied().collect()
);
assert!(main_windows.is_disjoint(&secondary_windows));
assert_eq!(
secondary["permissions"],
serde_json::json!(["core:window:allow-hide"])
);
}
+6 -2
View File
@@ -286,16 +286,20 @@ function EvidenceWindow() {
const [evidenceId, setEvidenceId] = useState<string | null>(null)
const [source, setSource] = useState<string | null>(null)
const [error, setError] = useState<string | null>(null)
const requestGeneration = useRef(0)
useEffect(() => {
const receive = (event: Event) => {
const id = (event as CustomEvent<{ evidenceId: string }>).detail.evidenceId
const generation = ++requestGeneration.current
setEvidenceId(id); setSource(null); setError(null)
void call<string>('get_activity_screenshot', { evidenceId: id }).then(setSource).catch(value => setError(String(value)))
void call<string>('get_activity_screenshot', { evidenceId: id })
.then(value => { if (generation === requestGeneration.current) setSource(value) })
.catch(value => { if (generation === requestGeneration.current) setError(String(value)) })
}
const close = (event: KeyboardEvent) => { if (event.key === 'Escape') void getCurrentWindow().hide() }
window.addEventListener('hermes-screenshot-evidence', receive)
window.addEventListener('keydown', close)
return () => { window.removeEventListener('hermes-screenshot-evidence', receive); window.removeEventListener('keydown', close) }
return () => { requestGeneration.current++; window.removeEventListener('hermes-screenshot-evidence', receive); window.removeEventListener('keydown', close) }
}, [])
return <div className="evidence-shell">
<header><span><Eye /><strong>Screenshot evidence</strong><small>Stored locally with this activity event</small></span><button aria-label="Close screenshot" onClick={() => getCurrentWindow().hide()}><X /></button></header>