From cf01ba30978573ea41c30f68a8d5eea443c41226 Mon Sep 17 00:00:00 2001 From: kdletters Date: Sat, 20 Jun 2026 15:40:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E7=B4=A7=E6=A1=8C=E9=9D=A2=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E6=8B=96=E6=8B=BD=E5=9D=90=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 桌面壳发送图片拖拽事件前将坐标归一为非负整数 桌面壳配置检查反查拖拽坐标归一和单测边界 共享决策记录补充桌面拖拽坐标边界约定 --- apps/desktop-shell/scripts/check-config.mjs | 3 +++ apps/desktop-shell/src-tauri/src/shell/file_drop.rs | 12 +++++++++++- docs/project-memory/shared-memory/decision-log.md | 8 ++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/desktop-shell/scripts/check-config.mjs b/apps/desktop-shell/scripts/check-config.mjs index 845913123..5caccb1f2 100644 --- a/apps/desktop-shell/scripts/check-config.mjs +++ b/apps/desktop-shell/scripts/check-config.mjs @@ -2357,6 +2357,9 @@ const requiredRustHostSnippets = [ '__genarrativeDesktopHistoryIndex', "console.warn('desktop navigation state sync failed', error)", 'file.imageDropped', + 'normalize_desktop_drop_position', + '.round().max(0.0) as i32', + 'desktop_drop_position_is_rounded_and_clamped_to_window_bounds', 'emit_desktop_image_drop_event(&drop_window, paths, drop_position)', 'app.notification().builder()', 'desktop_entry_url_with_host_context', diff --git a/apps/desktop-shell/src-tauri/src/shell/file_drop.rs b/apps/desktop-shell/src-tauri/src/shell/file_drop.rs index 992f601cc..7bfe31c79 100644 --- a/apps/desktop-shell/src-tauri/src/shell/file_drop.rs +++ b/apps/desktop-shell/src-tauri/src/shell/file_drop.rs @@ -15,6 +15,10 @@ fn first_valid_desktop_image_drop_payload( .find_map(|path| import_image_file_payload(path.clone(), "dropped", Some(position)).ok()) } +fn normalize_desktop_drop_position(x: f64, y: f64) -> (i32, i32) { + (x.round().max(0.0) as i32, y.round().max(0.0) as i32) +} + fn emit_desktop_image_drop_event( window: &WebviewWindow, paths: &[PathBuf], @@ -33,7 +37,7 @@ pub(crate) fn register_desktop_file_drop_events(window: &WebviewWindow) { let drop_window = window.clone(); window.on_window_event(move |event| { if let WindowEvent::DragDrop(DragDropEvent::Drop { paths, position }) = event { - let drop_position = (position.x.round() as i32, position.y.round() as i32); + let drop_position = normalize_desktop_drop_position(position.x, position.y); log_desktop_host_event_result( "file.imageDropped", emit_desktop_image_drop_event(&drop_window, paths, drop_position), @@ -103,4 +107,10 @@ mod tests { fs::remove_file(text_path).expect("remove text drop file"); fs::remove_dir(directory_path).expect("remove drop directory"); } + + #[test] + fn desktop_drop_position_is_rounded_and_clamped_to_window_bounds() { + assert_eq!(normalize_desktop_drop_position(12.4, 24.6), (12, 25)); + assert_eq!(normalize_desktop_drop_position(-4.2, -0.6), (0, 0)); + } } diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index c0dd38901..f9cbbfc73 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -16,6 +16,14 @@ --- +## 2026-06-20 桌面图片拖拽坐标非负边界 + +- 背景:Tauri 桌面壳通过系统拖拽事件向 H5 发送 `file.imageDropped`,拖拽坐标来自窗口事件;窗口边缘或平台差异可能产生负数或小数坐标,H5 只负责校验有限 number,不负责裁剪桌面系统坐标。 +- 决策:桌面壳在发送图片拖拽 HostBridge 事件前,必须把拖拽坐标 round 成整数并裁剪到非负值,再组装 import image payload;配置检查反查坐标归一 helper 和对应 Rust 单测。 +- 影响范围:`apps/desktop-shell/src-tauri/src/shell/file_drop.rs`、`apps/desktop-shell/scripts/check-config.mjs`。 +- 验证方式:`npm run desktop-shell:test -- file_drop`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。 +- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。 + ## 2026-06-20 移动扫码权限异步取消边界 - 背景:Expo 移动壳 `scanner.scanQrCode` 会打开真实相机权限请求和扫码 overlay;如果用户在系统权限 Promise 返回前关闭扫码,旧权限结果不能重新激活 CameraView,也不能完成已经取消的 HostBridge 请求。