fix: preserve undo for attachment link deletion
This commit is contained in:
+1
@@ -975,6 +975,7 @@ dependencies = [
|
||||
"leptos",
|
||||
"leptos-tiptap",
|
||||
"leptos_dom",
|
||||
"send_wrapper",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde_json",
|
||||
|
||||
@@ -20,6 +20,7 @@ leptos_dom = "0.8.8"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde-wasm-bindgen = "0.6"
|
||||
send_wrapper = "0.6"
|
||||
js-sys = "0.3.77"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
|
||||
+1
@@ -71,6 +71,7 @@ export interface InitOutput {
|
||||
readonly wasm_bindgen__convert__closures_____invoke__had771ddc65647798: (a: number, b: number, c: any) => void;
|
||||
readonly wasm_bindgen__convert__closures_____invoke__h0abd2b2fe4652e2a: (a: number, b: number, c: any) => void;
|
||||
readonly wasm_bindgen__convert__closures_____invoke__h396914cf76a9e7a5_4: (a: number, b: number, c: any) => void;
|
||||
readonly wasm_bindgen__convert__closures_____invoke__hd90af689bc3e71bf: (a: number, b: number, c: any) => void;
|
||||
readonly wasm_bindgen__convert__closures_____invoke__h794babeffd4f821f: (a: number, b: number) => void;
|
||||
readonly wasm_bindgen__convert__closures_____invoke__h2b648e7ac8ec1fb5: (a: number, b: number) => void;
|
||||
readonly wasm_bindgen__convert__closures_____invoke__h0b7fb40a8610550c: (a: number, b: number) => void;
|
||||
|
||||
+32
-14
@@ -297,6 +297,9 @@ function __wbg_get_imports() {
|
||||
const ret = arg0.activeElement;
|
||||
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||
},
|
||||
__wbg_addEventListener_2ed1344165a839a7: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
arg0.addEventListener(getStringFromWasm0(arg1, arg2), arg3, arg4 !== 0);
|
||||
}, arguments); },
|
||||
__wbg_addEventListener_8176dab41b09531c: function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
arg0.addEventListener(getStringFromWasm0(arg1, arg2), arg3);
|
||||
}, arguments); },
|
||||
@@ -1022,6 +1025,9 @@ function __wbg_get_imports() {
|
||||
const ret = arg0.removeChild(arg1);
|
||||
return ret;
|
||||
}, arguments); },
|
||||
__wbg_removeEventListener_0069d57d090a1674: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
arg0.removeEventListener(getStringFromWasm0(arg1, arg2), arg3, arg4 !== 0);
|
||||
}, arguments); },
|
||||
__wbg_removeEventListener_7bdf07404d9b24bd: function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
arg0.removeEventListener(getStringFromWasm0(arg1, arg2), arg3);
|
||||
}, arguments); },
|
||||
@@ -1169,6 +1175,9 @@ function __wbg_get_imports() {
|
||||
const ret = arg0.status;
|
||||
return ret;
|
||||
},
|
||||
__wbg_stopImmediatePropagation_2228838acf177dc6: function(arg0) {
|
||||
arg0.stopImmediatePropagation();
|
||||
},
|
||||
__wbg_stopPropagation_e088fca8231e68c4: function(arg0) {
|
||||
arg0.stopPropagation();
|
||||
},
|
||||
@@ -1242,61 +1251,66 @@ function __wbg_get_imports() {
|
||||
}
|
||||
}, arguments); },
|
||||
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1128, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1134, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h396914cf76a9e7a5);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1180, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1186, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h3a3182d847094e12);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 956, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 962, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`.
|
||||
const ret = makeClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__had771ddc65647798);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000004: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 1078, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 1084, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h0abd2b2fe4652e2a);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000005: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 1128, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 1134, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h396914cf76a9e7a5_4);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000006: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1080, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h794babeffd4f821f);
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 297, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hd90af689bc3e71bf);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000007: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1095, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`.
|
||||
const ret = makeClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h2b648e7ac8ec1fb5);
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1086, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h794babeffd4f821f);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000008: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1131, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1101, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`.
|
||||
const ret = makeClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h2b648e7ac8ec1fb5);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000009: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1137, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h0b7fb40a8610550c);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000009: function(arg0) {
|
||||
__wbindgen_cast_000000000000000a: function(arg0) {
|
||||
// Cast intrinsic for `F64 -> Externref`.
|
||||
const ret = arg0;
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_000000000000000a: function(arg0) {
|
||||
__wbindgen_cast_000000000000000b: function(arg0) {
|
||||
// Cast intrinsic for `I64 -> Externref`.
|
||||
const ret = arg0;
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_000000000000000b: function(arg0, arg1) {
|
||||
__wbindgen_cast_000000000000000c: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Ref(String) -> Externref`.
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_000000000000000c: function(arg0) {
|
||||
__wbindgen_cast_000000000000000d: function(arg0) {
|
||||
// Cast intrinsic for `U64 -> Externref`.
|
||||
const ret = BigInt.asUintN(64, arg0);
|
||||
return ret;
|
||||
@@ -1349,6 +1363,10 @@ function wasm_bindgen__convert__closures_____invoke__h396914cf76a9e7a5_4(arg0, a
|
||||
wasm.wasm_bindgen__convert__closures_____invoke__h396914cf76a9e7a5_4(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
function wasm_bindgen__convert__closures_____invoke__hd90af689bc3e71bf(arg0, arg1, arg2) {
|
||||
wasm.wasm_bindgen__convert__closures_____invoke__hd90af689bc3e71bf(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
function wasm_bindgen__convert__closures_____invoke__h3a3182d847094e12(arg0, arg1, arg2) {
|
||||
const ret = wasm.wasm_bindgen__convert__closures_____invoke__h3a3182d847094e12(arg0, arg1, arg2);
|
||||
if (ret[1]) {
|
||||
|
||||
BIN
Binary file not shown.
Vendored
+1
@@ -24,6 +24,7 @@ export const wasm_bindgen__convert__closures_____invoke__h396914cf76a9e7a5: (a:
|
||||
export const wasm_bindgen__convert__closures_____invoke__had771ddc65647798: (a: number, b: number, c: any) => void;
|
||||
export const wasm_bindgen__convert__closures_____invoke__h0abd2b2fe4652e2a: (a: number, b: number, c: any) => void;
|
||||
export const wasm_bindgen__convert__closures_____invoke__h396914cf76a9e7a5_4: (a: number, b: number, c: any) => void;
|
||||
export const wasm_bindgen__convert__closures_____invoke__hd90af689bc3e71bf: (a: number, b: number, c: any) => void;
|
||||
export const wasm_bindgen__convert__closures_____invoke__h794babeffd4f821f: (a: number, b: number) => void;
|
||||
export const wasm_bindgen__convert__closures_____invoke__h2b648e7ac8ec1fb5: (a: number, b: number) => void;
|
||||
export const wasm_bindgen__convert__closures_____invoke__h0b7fb40a8610550c: (a: number, b: number) => void;
|
||||
|
||||
@@ -9,6 +9,7 @@ use leptos_tiptap::{
|
||||
TiptapNodeName, TiptapRange, TiptapSchemaTarget, TiptapSelectionState, TiptapTextAlign,
|
||||
TiptapTocNodeAttrs,
|
||||
};
|
||||
use send_wrapper::SendWrapper;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Map, Value};
|
||||
use std::{any::Any, cell::Cell, collections::HashMap, fmt::Display};
|
||||
@@ -5261,14 +5262,19 @@ fn hovered_block_from_selection() -> Option<HoveredBlockState> {
|
||||
block_state_from_index(index)
|
||||
}
|
||||
|
||||
fn selected_local_attachment_block_index() -> Option<usize> {
|
||||
let selection = window().and_then(|win| win.get_selection().ok().flatten())?;
|
||||
fn selected_local_attachment_link_text() -> bool {
|
||||
let selection = match window().and_then(|win| win.get_selection().ok().flatten()) {
|
||||
Some(selection) if !selection.is_collapsed() => selection,
|
||||
_ => return false,
|
||||
};
|
||||
let selected_text = String::from(selection.to_string()).trim().to_string();
|
||||
if selected_text.is_empty() {
|
||||
return None;
|
||||
return false;
|
||||
}
|
||||
|
||||
let root = editor_root_element()?;
|
||||
let Some(root) = editor_root_element() else {
|
||||
return false;
|
||||
};
|
||||
let links = root.get_elements_by_tag_name("a");
|
||||
for index in 0..links.length() {
|
||||
let Some(link) = links.item(index) else {
|
||||
@@ -5278,15 +5284,49 @@ fn selected_local_attachment_block_index() -> Option<usize> {
|
||||
if !href.contains("/api/local-folder/files/open") {
|
||||
continue;
|
||||
}
|
||||
let text = link.text_content().unwrap_or_default();
|
||||
if text.trim() != selected_text {
|
||||
continue;
|
||||
if link.text_content().unwrap_or_default().trim() == selected_text {
|
||||
return true;
|
||||
}
|
||||
let block = direct_block_from_element(link, &root)?;
|
||||
return top_level_block_index(&root, &block);
|
||||
}
|
||||
|
||||
None
|
||||
false
|
||||
}
|
||||
|
||||
fn selected_local_attachment_link_in_editor_state() -> bool {
|
||||
let Some(root) = editor_root_element() else {
|
||||
return false;
|
||||
};
|
||||
let Ok(editor_value) = js_sys::Reflect::get(root.as_ref(), &JsValue::from_str("editor"))
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let Ok(state_value) = js_sys::Reflect::get(&editor_value, &JsValue::from_str("state")) else {
|
||||
return false;
|
||||
};
|
||||
let Ok(selection_value) = js_sys::Reflect::get(&state_value, &JsValue::from_str("selection"))
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let is_empty = js_sys::Reflect::get(&selection_value, &JsValue::from_str("empty"))
|
||||
.ok()
|
||||
.and_then(|value| value.as_bool())
|
||||
.unwrap_or(true);
|
||||
if is_empty {
|
||||
return false;
|
||||
}
|
||||
let Some(get_attributes) = js_sys::Reflect::get(&editor_value, &JsValue::from_str("getAttributes"))
|
||||
.ok()
|
||||
.and_then(|value| value.dyn_into::<js_sys::Function>().ok())
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let Ok(attributes) = get_attributes.call1(&editor_value, &JsValue::from_str("link")) else {
|
||||
return false;
|
||||
};
|
||||
js_sys::Reflect::get(&attributes, &JsValue::from_str("href"))
|
||||
.ok()
|
||||
.and_then(|value| value.as_string())
|
||||
.is_some_and(|href| href.contains("/api/local-folder/files/open"))
|
||||
}
|
||||
|
||||
fn block_state_from_index(index: usize) -> Option<HoveredBlockState> {
|
||||
@@ -8736,9 +8776,7 @@ fn App(mount_options: MountOptions) -> impl IntoView {
|
||||
let editor = editor;
|
||||
let document_id = document_id;
|
||||
let workspace_id = workspace_id;
|
||||
let dirty_count = dirty_count;
|
||||
let title = title;
|
||||
let editor_focused = editor_focused;
|
||||
let set_dirty_count = set_dirty_count;
|
||||
let set_html_output = set_html_output;
|
||||
let set_document_json = set_document_json;
|
||||
@@ -8759,9 +8797,6 @@ fn App(mount_options: MountOptions) -> impl IntoView {
|
||||
let set_color_menu_open = set_color_menu_open;
|
||||
let more_menu_open = more_menu_open;
|
||||
let set_more_menu_open = set_more_menu_open;
|
||||
let revision = revision;
|
||||
let conflict_detection_key = conflict_detection_key;
|
||||
let read_only = read_only;
|
||||
let set_locked_toolbar_anchor = set_locked_toolbar_anchor;
|
||||
let set_text_selection_active = set_text_selection_active;
|
||||
let set_floating_toolbar_anchor = set_floating_toolbar_anchor;
|
||||
@@ -8779,65 +8814,66 @@ fn App(mount_options: MountOptions) -> impl IntoView {
|
||||
let set_block_turn_into_open = set_block_turn_into_open;
|
||||
let set_block_folded_title_open = set_block_folded_title_open;
|
||||
let set_command_feedback = set_command_feedback;
|
||||
let keydown_capture_closure =
|
||||
SendWrapper::new(Closure::wrap(Box::new(move |event: ev::KeyboardEvent| {
|
||||
if !active_editor_stage()
|
||||
|| event.ctrl_key()
|
||||
|| event.meta_key()
|
||||
|| event.alt_key()
|
||||
|| !matches!(event.key().as_str(), "Backspace" | "Delete")
|
||||
|| !(selected_local_attachment_link_in_editor_state()
|
||||
|| selected_local_attachment_link_text())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
event.prevent_default();
|
||||
event.stop_immediate_propagation();
|
||||
match editor.delete_selection() {
|
||||
Ok(()) => {
|
||||
sync_persisted_editor_command(
|
||||
editor,
|
||||
&runtime_persisted_identity(document_id, workspace_id),
|
||||
set_dirty_count,
|
||||
set_html_output,
|
||||
set_document_json,
|
||||
set_json_output,
|
||||
title,
|
||||
set_command_feedback,
|
||||
"已删除附件引用",
|
||||
);
|
||||
let _ = set_hovered_block.try_set(None);
|
||||
let _ = set_block_menu_open.try_set(false);
|
||||
let _ = set_block_menu_anchor.try_set(None);
|
||||
}
|
||||
Err(err) => {
|
||||
let _ = set_command_feedback.try_set(format!("删除附件引用失败:{err}"));
|
||||
}
|
||||
}
|
||||
}) as Box<dyn FnMut(_)>));
|
||||
if let Some(win) = window() {
|
||||
let _ = win.add_event_listener_with_callback_and_bool(
|
||||
"keydown",
|
||||
keydown_capture_closure.as_ref().unchecked_ref(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
on_cleanup(move || {
|
||||
if let Some(win) = window() {
|
||||
let _ = win.remove_event_listener_with_callback_and_bool(
|
||||
"keydown",
|
||||
keydown_capture_closure.as_ref().unchecked_ref(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
drop(keydown_capture_closure);
|
||||
});
|
||||
let keydown_handle = window_event_listener(ev::keydown, move |event| {
|
||||
let focused = active_editor_stage();
|
||||
if !focused {
|
||||
return;
|
||||
}
|
||||
|
||||
if !event.ctrl_key()
|
||||
&& !event.meta_key()
|
||||
&& !event.alt_key()
|
||||
&& matches!(event.key().as_str(), "Backspace" | "Delete")
|
||||
{
|
||||
if let Some(block_index) = selected_local_attachment_block_index() {
|
||||
event.prevent_default();
|
||||
match editor.get_html() {
|
||||
Ok(current_html) => {
|
||||
match delete_top_level_block_html(¤t_html, block_index) {
|
||||
Ok(next_html) => {
|
||||
apply_html_update(
|
||||
editor,
|
||||
&runtime_persisted_identity(document_id, workspace_id),
|
||||
next_html,
|
||||
document_id,
|
||||
workspace_id,
|
||||
dirty_count,
|
||||
set_dirty_count,
|
||||
set_html_output,
|
||||
set_document_json,
|
||||
set_json_output,
|
||||
title,
|
||||
hovered_block,
|
||||
editor_focused,
|
||||
slash_open,
|
||||
turn_into_open,
|
||||
color_menu_open,
|
||||
more_menu_open,
|
||||
revision,
|
||||
conflict_detection_key,
|
||||
read_only,
|
||||
set_command_feedback,
|
||||
"已删除附件引用",
|
||||
);
|
||||
let _ = set_hovered_block.try_set(None);
|
||||
let _ = set_block_menu_open.try_set(false);
|
||||
let _ = set_block_menu_anchor.try_set(None);
|
||||
}
|
||||
Err(err) => {
|
||||
let _ = set_command_feedback.try_set(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let _ =
|
||||
set_command_feedback.try_set(format!("读取当前 HTML 失败:{err}"));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.ctrl_key() || event.meta_key())
|
||||
&& event.shift_key()
|
||||
&& event.key().eq_ignore_ascii_case("u")
|
||||
|
||||
Reference in New Issue
Block a user