补齐桌面拖拽图片失败日志
桌面图片拖拽候选 payload 读取失败记录稳定日志 配置门禁禁止拖拽图片候选静默跳过 同步原生壳方案文档和共享决策记录
This commit is contained in:
@@ -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
Reference in New Issue
Block a user