#2495: keep Duplicate from opening browser bookmarks

This commit is contained in:
Kristian Myram
2026-10-27 22:34:28 +00:00
parent af415aa6e1
commit 81cff0468e
3 changed files with 39 additions and 0 deletions
+14
View File
@@ -7,6 +7,20 @@
//
// Plain functions only: the Rust side owns the logic.
// ------------------------------------------------------------------ shortcuts
/// Keep Duplicate on the app canvas instead of also opening the browser's bookmarks.
/// eframe still receives the key; other chords and HTML inputs keep their browser behavior.
export function installCanvasShortcuts(canvas) {
canvas.addEventListener("keydown", (event) => {
if (event.target === canvas && (event.ctrlKey || event.metaKey)
&& !event.shiftKey && !event.altKey && !event.isComposing
&& event.key.toLowerCase() === "d") {
event.preventDefault();
}
}, { capture: true });
}
// ------------------------------------------------------------------ storage
const DIR = "effectcraft";
+3
View File
@@ -15,6 +15,8 @@ use wasm_bindgen::prelude::*;
extern "C" {
#[wasm_bindgen(js_name = showDeviceLost)]
fn show_device_lost(message: &str, kept: bool);
#[wasm_bindgen(js_name = installCanvasShortcuts)]
fn install_canvas_shortcuts(canvas: &web_sys::HtmlCanvasElement);
}
/// Work for the UI thread with access to the app (posted by async tasks: picked files…).
@@ -204,6 +206,7 @@ pub async fn start(canvas_id: String) -> Result<(), JsValue> {
effectcraft_ui_egui::i18n::set_system_locales(query_value("lang"), browser.collect());
let doc = web_sys::window().and_then(|w| w.document()).ok_or("no document")?;
let canvas: web_sys::HtmlCanvasElement = doc.get_element_by_id(&canvas_id).ok_or("no canvas")?.dyn_into()?;
install_canvas_shortcuts(&canvas);
let empty = query_flag("empty");
let home = query_flag("home");
// Browser storage: settings, recent projects, imported media, saved projects, auto-saves and
+22
View File
@@ -8,6 +8,8 @@
// - A lost WebGPU device: the page says so over the canvas and offers Save Project and Reload;
// the reload writes pending changes to browser storage first, and a failed write is reported
// instead of reloading (#225).
// - Duplicate cancels the browser's bookmark action on the app canvas, while still delivering
// the key to the app; other chords and HTML inputs keep their browser behavior (#2495).
// Exits non-zero on failure.
import assert from "node:assert/strict";
import { dirname, join } from "node:path";
@@ -58,6 +60,26 @@ globalThis.location = { reload: () => reloads++ };
const host = await import(pathToFileURL(join(here, "../js/host.js")));
// #2495: prevent the browser default without swallowing the app's Duplicate key.
const canvas = new EventTarget();
const input = new EventTarget();
host.installCanvasShortcuts(canvas);
let delivered = 0;
canvas.addEventListener("keydown", () => delivered++);
const key = (target, properties) => {
const event = new Event("keydown", { cancelable: true });
Object.assign(event, { key: "d", ...properties });
target.dispatchEvent(event);
return event.defaultPrevented;
};
assert.equal(key(canvas, { ctrlKey: true }), true);
assert.equal(key(canvas, { metaKey: true, key: "D" }), true);
for (const properties of [{}, { ctrlKey: true, shiftKey: true }, { ctrlKey: true, altKey: true }, { ctrlKey: true, isComposing: true }, { ctrlKey: true, key: "r" }]) {
assert.equal(key(canvas, properties), false);
}
assert.equal(delivered, 7, "all keys still reach the app");
assert.equal(key(input, { ctrlKey: true }), false, "an external HTML input keeps its shortcut");
// #226: one readable and one unreadable file.
const file = (name, read) => ({ name, webkitRelativePath: "", arrayBuffer: read });
const picked = host.browsePickFiles(false);