补齐桌面拖拽图片失败日志

桌面图片拖拽候选 payload 读取失败记录稳定日志

配置门禁禁止拖拽图片候选静默跳过

同步原生壳方案文档和共享决策记录
This commit is contained in:
2026-06-20 21:43:29 +08:00
parent 2362c354cb
commit 00dce5914b
4 changed files with 71 additions and 7 deletions
+8 -2
View File
@@ -2564,9 +2564,14 @@ const requiredRustHostSnippets = [
'"minimized"',
'WindowEvent::DragDrop',
'first_valid_desktop_image_drop_payload',
'.filter(|path| path.is_file() && import_image_mime_type(path).is_some())',
'.find_map(|path| import_image_file_payload(path.clone(), "dropped", Some(position)).ok())',
'if !path.is_file() || import_image_mime_type(path).is_none()',
'match import_image_file_payload(path.clone(), "dropped", Some(position))',
'import_image_file_payload(path.clone(), "dropped", Some(position))',
'log_desktop_image_drop_payload_failure(&error)',
'file.imageDropped.payload',
'fn image_drop_without_valid_image_does_not_emit_payload()',
'fn image_drop_logs_invalid_candidate_then_uses_next_valid_image()',
'fn image_drop_payload_failure_reports_failure()',
'fs::create_dir_all(&directory_path)',
'assert_eq!(payload, None);',
'PageLoadEvent',
@@ -3206,6 +3211,7 @@ for (const blockedLifecycleSnippet of [
'let _ = emit_current_desktop_lifecycle_event(window)',
'let _ = emit_current_desktop_lifecycle_event(&lifecycle_window)',
'let _ = emit_desktop_image_drop_event(&drop_window',
'.find_map(|path| import_image_file_payload(path.clone(), "dropped", Some(position)).ok())',
'window.is_visible().unwrap_or(',
'window.is_minimized().unwrap_or(',
'window.is_focused().unwrap_or(',
@@ -9,10 +9,24 @@ fn first_valid_desktop_image_drop_payload(
paths: &[PathBuf],
position: (i32, i32),
) -> Option<Value> {
paths
.iter()
.filter(|path| path.is_file() && import_image_mime_type(path).is_some())
.find_map(|path| import_image_file_payload(path.clone(), "dropped", Some(position)).ok())
paths.iter().find_map(|path| {
if !path.is_file() || import_image_mime_type(path).is_none() {
return None;
}
match import_image_file_payload(path.clone(), "dropped", Some(position)) {
Ok(payload) => Some(payload),
Err(error) => {
log_desktop_image_drop_payload_failure(&error);
None
}
}
})
}
fn log_desktop_image_drop_payload_failure(error: &str) -> bool {
eprintln!("desktop host event failed for file.imageDropped.payload: {error}");
false
}
fn normalize_desktop_drop_position(x: f64, y: f64) -> (i32, i32) {
@@ -84,6 +98,42 @@ mod tests {
fs::remove_file(valid_path).expect("remove valid drop image");
}
#[test]
fn image_drop_logs_invalid_candidate_then_uses_next_valid_image() {
let disguised_path = std::env::temp_dir().join(format!(
"genarrative-desktop-drop-disguised-{}.png",
std::process::id()
));
let valid_path = std::env::temp_dir().join(format!(
"genarrative-desktop-drop-valid-after-error-{}.png",
std::process::id()
));
fs::write(&disguised_path, b"text").expect("write disguised drop image");
fs::write(&valid_path, png_bytes()).expect("write valid drop image");
let payload = first_valid_desktop_image_drop_payload(
&[disguised_path.clone(), valid_path.clone()],
(11, 13),
)
.expect("valid image drop payload after invalid candidate");
assert_eq!(
payload["fileName"],
valid_path.file_name().unwrap().to_str().unwrap()
);
assert_eq!(payload["position"], serde_json::json!({ "x": 11, "y": 13 }));
fs::remove_file(disguised_path).expect("remove disguised drop image");
fs::remove_file(valid_path).expect("remove valid drop image");
}
#[test]
fn image_drop_payload_failure_reports_failure() {
assert!(!log_desktop_image_drop_payload_failure(
"image bytes do not match MIME",
));
}
#[test]
fn image_drop_without_valid_image_does_not_emit_payload() {
let text_path = std::env::temp_dir().join(format!(
@@ -32,6 +32,14 @@
- 验证方式:`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 桌面图片拖拽候选读取失败不可静默
- 背景:桌面壳拖入图片时会按路径列表寻找第一个真实可导入图片;扩展名合法但内容损坏、超限或读取失败的文件如果被静默跳过,用户只会看到拖拽无反应,开发侧也缺少排障线索。
- 决策:`first_valid_desktop_image_drop_payload(...)` 对扩展名符合图片候选但 payload 组装失败的路径必须记录 `desktop host event failed for file.imageDropped.payload`,然后继续尝试后续候选;目录、非图片扩展名或没有任何有效图片仍保持不派发 `file.imageDropped` payload。
- 影响范围:`apps/desktop-shell/src-tauri/src/shell/file_drop.rs``apps/desktop-shell/scripts/check-config.mjs`、宿主壳方案文档。
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml shell::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 请求。
File diff suppressed because one or more lines are too long