host: the tab bridge ships from waybetter-host; create-waybetter uses it
tab-bridge.js (moved from wayBetter for X) is served by bridge::SOURCE, or by bridge::script(BridgeOptions) with shortcut and link-drag forwarding switched off for a shell that does not handle them (a forwarded shortcut is taken from the page). Tests: one options line, only tab_report/tab_shortcut/tab_link_drag, every forwarded shortcut is one tabs::valid_shortcut accepts. Generated apps now inject bridge::script(BridgeOptions::REPORT_ONLY) instead of writing their own duplicated src-tauri/runtime/tab-bridge.js.
This commit is contained in:
parent
3e6cbe0773
commit
d3ed72ea4d
4 changed files with 209 additions and 37 deletions
67
crates/waybetter-host/src/bridge.rs
Normal file
67
crates/waybetter-host/src/bridge.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
//! The tab bridge: the script every tab webview gets as a main-frame initialization script
|
||||
//! ([`script`]). It reports the tab's own URL, title, favicon and focus to the shell (`tab_report`)
|
||||
//! and, when the app enables them, forwards browser shortcuts pressed in the page (`tab_shortcut`,
|
||||
//! [`crate::tabs::valid_shortcut`]) and link drags (`tab_link_drag`, [`crate::tabs::link_drag`]).
|
||||
//! The app's `tab-chrome` capability grants exactly the commands it calls, about the tab itself.
|
||||
|
||||
/// The bridge source (`tab-bridge.js` next to this crate's `Cargo.toml`), all features on.
|
||||
pub const SOURCE: &str = include_str!("../tab-bridge.js");
|
||||
const DEFAULT_OPTIONS: &str = "const options = { shortcuts: true, linkDrags: true };";
|
||||
|
||||
/// What the bridge forwards besides the tab report. Turn a feature off when the app's shell does
|
||||
/// not handle it: a forwarded shortcut is taken from the page (`preventDefault`).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct BridgeOptions {
|
||||
/// Browser shortcuts (new/close/reopen tab, reload, back/forward, split, palette, deck keys).
|
||||
pub shortcuts: bool,
|
||||
/// Link drags out of (or into) the page, for the shell's split/tab drop zones.
|
||||
pub link_drags: bool,
|
||||
}
|
||||
|
||||
impl BridgeOptions {
|
||||
pub const ALL: Self = Self { shortcuts: true, link_drags: true };
|
||||
/// Only `tab_report` (URL, title, favicon, focus).
|
||||
pub const REPORT_ONLY: Self = Self { shortcuts: false, link_drags: false };
|
||||
}
|
||||
|
||||
/// The bridge script with `options`.
|
||||
pub fn script(options: BridgeOptions) -> String {
|
||||
let set = format!("const options = {{ shortcuts: {}, linkDrags: {} }};", options.shortcuts, options.link_drags);
|
||||
SOURCE.replacen(DEFAULT_OPTIONS, &set, 1)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn options_are_set_in_the_one_options_line() {
|
||||
assert_eq!(SOURCE.matches(DEFAULT_OPTIONS).count(), 1);
|
||||
assert_eq!(script(BridgeOptions::ALL), SOURCE);
|
||||
let report = script(BridgeOptions::REPORT_ONLY);
|
||||
assert!(report.contains("const options = { shortcuts: false, linkDrags: false };"));
|
||||
assert!(!report.contains(DEFAULT_OPTIONS));
|
||||
assert!(report.contains("if (options.shortcuts) addEventListener('keydown'"));
|
||||
assert!(report.contains("if (options.linkDrags) {"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bridge_calls_only_the_tab_chrome_commands() {
|
||||
let mut commands: Vec<&str> = SOURCE.match_indices("invoke('").map(|(at, _)| SOURCE[at + 8..].split('\'').next().unwrap()).collect();
|
||||
commands.sort();
|
||||
commands.dedup();
|
||||
assert_eq!(commands, ["tab_link_drag", "tab_report", "tab_shortcut"]);
|
||||
assert!(SOURCE.contains("if (window.top !== window || window.__wbTabBridge) return;"), "top frame only, once");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forwarded_shortcuts_are_ones_the_host_accepts() {
|
||||
for name in ["deck-toggle", "deck-compose", "column-page-prev", "column-page-next", "column-prev", "column-next", "reopen-tab", "new-tab",
|
||||
"open-panel", "close-tab", "focus-address", "command-palette", "prev-tab", "next-tab", "hard-reload", "reload", "split-down",
|
||||
"split-right", "back", "forward"] {
|
||||
assert!(SOURCE.contains(&format!("'{name}'")), "{name} is not in the bridge");
|
||||
assert!(crate::tabs::valid_shortcut(name), "{name} would be refused");
|
||||
}
|
||||
assert!(crate::tabs::valid_shortcut("column-3"));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue