Files
PathEditor/src/hooks/use-keyboard.ts
T
Serendipity cbf99f12fd v5.1: 全面代码审查修复 — 安全加固 + 功能修复 + 测试补全 + 工程化
安全修复 (CRITICAL):
- 启用 CSP (default-src 'self')
- read_text_file 限制文件扩展名白名单 (.json/.csv/.txt)
- capabilities 显式声明窗口权限
- profile 名校验增强 (null 字节/控制字符/长度限制)

功能修复 (HIGH):
- AnalyzeDialog 重新打开时正确刷新数据
- UndoRedoButtons 订阅路径长度变化确保响应性
- 禁用状态持久化错误处理 (.catch → console.warn)
- 硬编码中文全部迁移到 i18n (6 处)
- PATH 长度检查改用 UTF-16 字符计数
- PATH 写入前 null 字节校验
- CLI export 拒绝写入系统目录
- savePaths 职责分离: window.confirm → Tauri ask() 对话框

代码质量 (MEDIUM):
- 导入路径统一过滤 (sanitize_paths: null 字节/分号/空白)
- 原子写入 (atomic_write: disabled.json + profiles)
- 验证缓存自动清理 (PathTable useEffect)
- Scanner 线程错误处理改进 (.unwrap → .map_err)
- Ctrl+F 去重 (移除 use-keyboard 重复处理)
- Profile 路径列表 key 修复 (index → path)
- 生产构建启用日志插件 (Warn 级别)
- export_paths JSON 序列化改 expect

测试:
- Rust: 35 → 48 测试 (+13)
- Frontend: 80 → 85 测试 (+5)
- Vitest 全局 jsdom + 覆盖率阈值 (80%)
- 安装 @vitest/coverage-v8 + test:coverage 脚本
- 移除未使用的 @testing-library/jest-dom

工程化:
- CI 添加 Cargo 缓存 (Swatinem/rust-cache@v2)
- CI 添加 cargo fmt --check
- tsconfig.test.json 覆盖测试文件类型检查
- cargo fmt 全量格式化

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 23:17:27 +08:00

69 lines
2.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEffect, useRef } from 'react';
import { useAppStore } from '@/store/app-store';
interface KeyboardActions {
onNew: () => void;
onSave: () => void;
onDelete: () => void;
onUndo: () => void;
onRedo: () => void;
onHelp: () => void;
}
/**
* 全局键盘快捷键
* Ctrl+N 新建, Ctrl+S 保存, Ctrl+Z 撤销, Ctrl+Y 重做, Delete 删除, F1 帮助
* 使用 ref 避免因 actions 对象每次渲染都是新引用而重复注册事件
*/
export function useKeyboard(actions: KeyboardActions) {
const isAdmin = useAppStore((s) => s.isAdmin);
const actionsRef = useRef(actions);
// eslint-disable-next-line react-hooks/refs -- React 官方推荐的 ref 同步模式,避免每次渲染重复注册事件监听器
actionsRef.current = actions;
useEffect(() => {
const handler = (e: KeyboardEvent) => {
const tag = (e.target as HTMLElement)?.tagName;
const isInput = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';
if (isInput) {
if (e.key === 'Escape') {
(e.target as HTMLElement).blur();
}
return;
}
const a = actionsRef.current;
const ctrl = e.ctrlKey || e.metaKey;
if (ctrl && e.key === 'z') {
if (!isAdmin) return;
e.preventDefault();
a.onUndo();
} else if (ctrl && e.key === 'y') {
if (!isAdmin) return;
e.preventDefault();
a.onRedo();
} else if (ctrl && e.key === 'n') {
if (!isAdmin) return;
e.preventDefault();
a.onNew();
} else if (ctrl && e.key === 's') {
if (!isAdmin) return;
e.preventDefault();
a.onSave();
} else if (e.key === 'Delete' || e.key === 'Backspace') {
if (!isAdmin) return;
e.preventDefault();
a.onDelete();
} else if (e.key === 'F1') {
e.preventDefault();
a.onHelp();
}
};
window.addEventListener('keydown', handler);
return () => window.removeEventListener('keydown', handler);
}, [isAdmin]); // 只依赖 isAdminactions 通过 ref 读取
}