pinakes-ui: fix reactive dependencies in backlinks panel; improve wikilink click handling

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ib9a36bbaa16a7aa46b624027c1eb00fe6a6a6964
This commit is contained in:
raf 2026-02-09 14:26:15 +03:00
commit bf76820ddd
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
2 changed files with 113 additions and 37 deletions

View file

@ -1,3 +1,4 @@
use dioxus::document::eval;
use dioxus::prelude::*;
/// Event handler for wikilink clicks. Called with the target note name.
@ -43,6 +44,49 @@ pub fn MarkdownViewer(
});
});
// Set up global wikilink click handler that the inline onclick attributes will call
// This bridges JavaScript → Rust communication
use_effect(move || {
if let Some(handler) = on_wikilink_click {
spawn(async move {
// Set up a global function that wikilink onclick handlers can call
// The function stores the clicked target in localStorage
let setup_js = r#"
window.__dioxus_wikilink_click = function(target) {
console.log('Wikilink clicked:', target);
localStorage.setItem('__wikilink_clicked', target);
};
"#;
let _ = eval(setup_js).await;
// Poll localStorage to detect wikilink clicks
loop {
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let check_js = r#"
(function() {
const target = localStorage.getItem('__wikilink_clicked');
if (target) {
localStorage.removeItem('__wikilink_clicked');
return target;
}
return '';
})();
"#;
if let Ok(result) = eval(check_js).await {
if let Some(target) = result.as_str() {
if !target.is_empty() {
handler.call(target.to_string());
}
}
}
}
});
}
});
let is_loading = *loading.read();
rsx! {
@ -159,7 +203,7 @@ fn render_markdown(text: &str) -> String {
}
/// Convert wikilinks [[target]] and [[target|display]] to styled HTML links.
/// Uses data attributes only - no inline JavaScript for security.
/// Uses a special URL scheme that can be intercepted by click handlers.
fn convert_wikilinks(text: &str) -> String {
use regex::Regex;
@ -181,12 +225,13 @@ fn convert_wikilinks(text: &str) -> String {
let text = wikilink_re.replace_all(&text, |caps: &regex::Captures| {
let target = caps.get(1).unwrap().as_str().trim();
let display = caps.get(2).map(|m| m.as_str().trim()).unwrap_or(target);
// Create a styled link with data attributes only - no inline JavaScript.
// Event handling is done via event delegation in the frontend.
// Create a styled link that uses a special pseudo-protocol scheme
// This makes it easier to intercept clicks via JavaScript
format!(
"<a href=\"#wikilink\" class=\"wikilink\" data-wikilink-target=\"{}\">{}</a>",
escape_html_attr(target),
escape_html(display)
"<a href=\"javascript:void(0)\" class=\"wikilink\" data-wikilink-target=\"{target}\" onclick=\"if(window.__dioxus_wikilink_click){{window.__dioxus_wikilink_click('{target_escaped}')}}\">{display}</a>",
target = escape_html_attr(target),
target_escaped = escape_html_attr(&target.replace('\\', "\\\\").replace('\'', "\\'")),
display = escape_html(display)
)
});