fix: process muse-action:// inside on_navigation (scheme protocol doesn't fire for blocked navs)
Browse files
src-tauri/src/browser/commands.rs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
use tauri::webview::{PageLoadEvent, WebviewBuilder};
|
| 2 |
-
use tauri::{AppHandle, LogicalPosition, LogicalSize, Manager, Url, WebviewUrl};
|
| 3 |
|
| 4 |
use super::layout::{hide_tab, resize_active, show_tab};
|
| 5 |
use super::navigation::{navigation_blocked, resolve_url};
|
|
@@ -113,7 +113,13 @@ pub(crate) async fn create_tab_inner(app: &AppHandle, url: &str, _layout: &Viewp
|
|
| 113 |
.on_document_title_changed(move |_wv, title| { update_tab_field(&app_for_title, &id_for_title, |t| { if !title.trim().is_empty() { t.title = title.clone(); } }); })
|
| 114 |
.on_navigation({ let app_nav = app.clone(); move |url| {
|
| 115 |
let s = url.as_str();
|
| 116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
if navigation_blocked(&app_nav, s) { return false; }
|
| 118 |
true
|
| 119 |
}});
|
|
@@ -130,4 +136,44 @@ pub(crate) async fn create_tab_inner(app: &AppHandle, url: &str, _layout: &Viewp
|
|
| 130 |
emit_snapshot(app)?; Ok(id)
|
| 131 |
}
|
| 132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
const FAVICON_SCRIPT: &str = r#"(function(){window.__muse_report_favicon=function(){const link=document.querySelector('link[rel~="icon"],link[rel="shortcut icon"],link[rel="apple-touch-icon"]');if(link&&link.href){try{window.__TAURI_INTERNALS__.invoke('__tab_favicon',{favicon:link.href});}catch{}}};if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',window.__muse_report_favicon);}else{window.__muse_report_favicon();}})();"#;
|
|
|
|
| 1 |
use tauri::webview::{PageLoadEvent, WebviewBuilder};
|
| 2 |
+
use tauri::{AppHandle, Emitter, LogicalPosition, LogicalSize, Manager, Url, WebviewUrl};
|
| 3 |
|
| 4 |
use super::layout::{hide_tab, resize_active, show_tab};
|
| 5 |
use super::navigation::{navigation_blocked, resolve_url};
|
|
|
|
| 113 |
.on_document_title_changed(move |_wv, title| { update_tab_field(&app_for_title, &id_for_title, |t| { if !title.trim().is_empty() { t.title = title.clone(); } }); })
|
| 114 |
.on_navigation({ let app_nav = app.clone(); move |url| {
|
| 115 |
let s = url.as_str();
|
| 116 |
+
// Process muse-action:// HERE because register_uri_scheme_protocol does NOT fire
|
| 117 |
+
// for navigations that are blocked (returning false prevents the request from
|
| 118 |
+
// reaching the network layer where the scheme protocol lives).
|
| 119 |
+
if s.starts_with("muse-action://") {
|
| 120 |
+
handle_muse_action(&app_nav, s);
|
| 121 |
+
return false; // Block navigation — page stays on current URL
|
| 122 |
+
}
|
| 123 |
if navigation_blocked(&app_nav, s) { return false; }
|
| 124 |
true
|
| 125 |
}});
|
|
|
|
| 136 |
emit_snapshot(app)?; Ok(id)
|
| 137 |
}
|
| 138 |
|
| 139 |
+
/// Process muse-action:// URLs from the hover overlay.
|
| 140 |
+
/// Called from on_navigation (the only reliable interception point for child webview navigations).
|
| 141 |
+
fn handle_muse_action(app: &AppHandle, raw: &str) {
|
| 142 |
+
let rest = raw.trim_start_matches("muse-action://");
|
| 143 |
+
let (action, query) = rest.split_once('?').unwrap_or((rest, ""));
|
| 144 |
+
let params: std::collections::HashMap<String, String> = query.split('&').filter_map(|pair| { let (k, v) = pair.split_once('=')?; Some((percent_decode(k), percent_decode(v))) }).collect();
|
| 145 |
+
let url = params.get("url").cloned().unwrap_or_default();
|
| 146 |
+
if url.is_empty() { return; }
|
| 147 |
+
let source = params.get("source").cloned().unwrap_or_default();
|
| 148 |
+
let title = params.get("title").cloned().unwrap_or_else(|| "Web Reference".to_string());
|
| 149 |
+
let w = params.get("w").and_then(|s| s.parse::<u32>().ok()).unwrap_or(300);
|
| 150 |
+
let h = params.get("h").and_then(|s| s.parse::<u32>().ok()).unwrap_or(200);
|
| 151 |
+
let action = action.trim_end_matches('/').to_string();
|
| 152 |
+
let app2 = app.clone();
|
| 153 |
+
|
| 154 |
+
tauri::async_runtime::spawn(async move {
|
| 155 |
+
match crate::library::library_add_item(app2.clone(), url.clone(), Some(source.clone()), Some(title.clone())).await {
|
| 156 |
+
Ok(item) => {
|
| 157 |
+
if action == "board" {
|
| 158 |
+
let _ = crate::board::board_add_image(app2.clone(), Some(item.id.clone()), item.data_url.clone(), 120.0, 120.0, 300.0, 200.0);
|
| 159 |
+
}
|
| 160 |
+
let _ = app2.emit("board://image_added", serde_json::json!({
|
| 161 |
+
"libraryId": item.id, "dataUrl": item.data_url, "url": item.url,
|
| 162 |
+
"sourceUrl": item.source_url, "title": item.title,
|
| 163 |
+
"width": item.width, "height": item.height, "colors": item.colors
|
| 164 |
+
}));
|
| 165 |
+
}
|
| 166 |
+
Err(_e) => {
|
| 167 |
+
// Fallback: emit with raw URL so image still appears on canvas (as remote img)
|
| 168 |
+
let _ = app2.emit("board://image_added", serde_json::json!({
|
| 169 |
+
"url": url, "dataUrl": url, "sourceUrl": source, "title": title,
|
| 170 |
+
"width": w, "height": h, "colors": []
|
| 171 |
+
}));
|
| 172 |
+
}
|
| 173 |
+
}
|
| 174 |
+
});
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
fn percent_decode(s: &str) -> String { let bytes = s.as_bytes(); let mut out = Vec::with_capacity(bytes.len()); let mut i = 0; while i < bytes.len() { if bytes[i] == b'%' && i + 2 < bytes.len() { if let Ok(hex) = std::str::from_utf8(&bytes[i+1..i+3]) { if let Ok(v) = u8::from_str_radix(hex, 16) { out.push(v); i += 3; continue; } } } out.push(if bytes[i] == b'+' { b' ' } else { bytes[i] }); i += 1; } String::from_utf8_lossy(&out).to_string() }
|
| 178 |
+
|
| 179 |
const FAVICON_SCRIPT: &str = r#"(function(){window.__muse_report_favicon=function(){const link=document.querySelector('link[rel~="icon"],link[rel="shortcut icon"],link[rel="apple-touch-icon"]');if(link&&link.href){try{window.__TAURI_INTERNALS__.invoke('__tab_favicon',{favicon:link.href});}catch{}}};if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',window.__muse_report_favicon);}else{window.__muse_report_favicon();}})();"#;
|