asdf98 commited on
Commit
760eaf3
·
verified ·
1 Parent(s): 2656ff9

fix: process muse-action:// inside on_navigation (scheme protocol doesn't fire for blocked navs)

Browse files
Files changed (1) hide show
  1. src-tauri/src/browser/commands.rs +48 -2
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
- if s.starts_with("muse-action://") { return false; }
 
 
 
 
 
 
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();}})();"#;