8.3 KiB
8.3 KiB
Step 2 - Worker 1: Hook 系统修复
前置条件
必须先完成 step_0_foundation_serial.md 的所有任务
项目背景
Kode CLI 使用自定义 React Hooks 处理终端输入和用户交互。主要问题是 Ink 的 Key 类型缺少某些属性,以及一些未使用的 @ts-expect-error 指令。
系统架构上下文
src/hooks/
├── useDoublePress.ts - 双击检测
├── useTextInput.ts - 文本输入处理
├── useUnifiedCompletion.ts - 自动完成功能
└── ...其他 hooks
任务目标
- 修复 Key 类型属性缺失问题
- 移除未使用的 @ts-expect-error 指令
- 确保所有输入处理正常工作
详细施工步骤
Phase 1: 修复 Key 类型问题 (25分钟)
Step 1.1: 修复 useTextInput.ts
文件: src/hooks/useTextInput.ts
问题位置: 第 143, 266, 268, 272, 274 行
第 143 行 - 移除未使用的指令:
// 删除这一行
// @ts-expect-error
第 266, 268 行 - fn 属性不存在:
// 原始代码
if (input.fn) {
// 处理功能键
}
// 修复方案 1 - 类型断言
if ((input as any).fn) {
// 处理功能键
}
// 修复方案 2 - 属性检查
if ('fn' in input && input.fn) {
// 处理功能键
}
// 修复方案 3 - 扩展类型(如果 step_0 已创建类型增强)
// 确保导入了增强类型
import type { Key } from 'ink';
// Key 类型应该已经包含 fn 属性
第 272, 274 行 - home 和 end 属性:
// 原始代码
if (input.home) {
// 光标移到开始
}
if (input.end) {
// 光标移到结束
}
// 修复 - 使用扩展的 Key 类型或类型守卫
if ('home' in input && input.home) {
setCursorPosition(0);
}
if ('end' in input && input.end) {
setCursorPosition(value.length);
}
Step 1.2: 创建 Key 类型辅助函数
在 useTextInput.ts 顶部添加:
// Key 类型辅助函数
interface ExtendedKey extends Key {
fn?: boolean;
home?: boolean;
end?: boolean;
}
function isExtendedKey(key: Key): key is ExtendedKey {
return true; // 因为我们已经扩展了类型
}
// 或者更安全的检查
function hasProperty<T extends object, K extends PropertyKey>(
obj: T,
key: K
): obj is T & Record<K, unknown> {
return key in obj;
}
Step 1.3: 重构键盘处理逻辑
优化键盘输入处理:
const handleKeyPress = (input: string, key: Key) => {
// 功能键处理
if (hasProperty(key, 'fn') && key.fn) {
handleFunctionKey(input);
return;
}
// Home 键
if (hasProperty(key, 'home') && key.home) {
setCursorPosition(0);
return;
}
// End 键
if (hasProperty(key, 'end') && key.end) {
setCursorPosition(value.length);
return;
}
// 普通按键处理
if (key.return) {
handleSubmit();
} else if (key.backspace) {
handleBackspace();
} else if (key.delete) {
handleDelete();
} else if (key.leftArrow) {
moveCursorLeft();
} else if (key.rightArrow) {
moveCursorRight();
} else {
insertText(input);
}
};
Phase 2: 修复 useUnifiedCompletion.ts (20分钟)
Step 2.1: 修复 space 属性
文件: src/hooks/useUnifiedCompletion.ts
定位: 第 1151 行
问题: space 属性不存在
查找代码:
if (key.space) {
// 处理空格键
}
修复为:
// 方案 1 - 检查输入字符
if (input === ' ') {
// 处理空格键
}
// 方案 2 - 扩展属性检查
if ('space' in key && key.space) {
// 处理空格键
}
// 方案 3 - 组合检查
if (input === ' ' || (hasProperty(key, 'space') && key.space)) {
// 处理空格键
}
Step 2.2: 优化自动完成逻辑
改进类型安全:
interface CompletionKey extends Key {
space?: boolean;
tab?: boolean;
}
const handleCompletionKey = (input: string, key: Key) => {
const extKey = key as CompletionKey;
// Tab 完成
if (key.tab) {
return performCompletion();
}
// 空格触发
if (input === ' ' || extKey.space) {
return checkForCompletion();
}
// Escape 取消
if (key.escape) {
return cancelCompletion();
}
};
Phase 3: 修复 useDoublePress.ts (10分钟)
Step 3.1: 移除未使用的指令
文件: src/hooks/useDoublePress.ts
定位: 第 33 行
操作:
// 删除这一行
// @ts-expect-error
Step 3.2: 检查相关代码
确保删除指令后代码仍然正确:
// 检查第 33 行附近的代码
// 如果有类型问题,正确修复而不是使用 @ts-expect-error
Phase 4: 创建通用 Hook 工具函数 (15分钟)
Step 4.1: 创建 hook 工具文件
创建文件: src/hooks/utils.ts
import type { Key } from 'ink';
// 扩展的 Key 类型
export interface ExtendedKey extends Key {
fn?: boolean;
home?: boolean;
end?: boolean;
space?: boolean;
pageUp?: boolean;
pageDown?: boolean;
}
// 类型守卫
export function isExtendedKey(key: Key): key is ExtendedKey {
return true;
}
// 属性检查
export function hasKeyProperty<K extends keyof ExtendedKey>(
key: Key,
property: K
): key is Key & Record<K, ExtendedKey[K]> {
return property in key;
}
// 键盘事件标准化
export function normalizeKey(input: string, key: Key): ExtendedKey {
return {
...key,
space: input === ' ',
// 添加其他标准化逻辑
} as ExtendedKey;
}
Step 4.2: 更新 hooks 使用工具函数
在需要的 hooks 中导入:
import { hasKeyProperty, normalizeKey } from './utils';
// 使用
const normalizedKey = normalizeKey(input, key);
if (normalizedKey.space) {
// 处理空格
}
Phase 5: 验证和测试 (15分钟)
Step 5.1: 检查 Hook 错误
# useTextInput
npx tsc --noEmit 2>&1 | grep "useTextInput"
# useUnifiedCompletion
npx tsc --noEmit 2>&1 | grep "useUnifiedCompletion"
# useDoublePress
npx tsc --noEmit 2>&1 | grep "useDoublePress"
# 所有 hooks
npx tsc --noEmit 2>&1 | grep "src/hooks/"
Step 5.2: 功能测试
# 启动 CLI
bun run dev
# 测试文本输入
# 输入一些文本,测试:
# - 光标移动 (箭头键)
# - Home/End 键
# - 退格/删除
# - 自动完成 (Tab)
# 测试双击
# 快速按两次相同的键
Step 5.3: 创建测试脚本
创建文件: test-hooks.js
// 简单的键盘输入测试
const readline = require('readline');
readline.emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);
console.log('Press keys to test (Ctrl+C to exit):');
process.stdin.on('keypress', (str, key) => {
console.log('Input:', str, 'Key:', key);
if (key && key.ctrl && key.name === 'c') {
process.exit();
}
});
完成标志
- useTextInput.ts 无类型错误
- useUnifiedCompletion.ts 无类型错误
- useDoublePress.ts 无未使用指令
- 所有键盘输入正常工作
- 自动完成功能正常
- TypeScript 错误减少至少 10 个
注意事项
- 保持输入响应 - 不要引入延迟
- 处理边缘情况 - 考虑特殊键组合
- 保留快捷键 - 确保所有快捷键仍然工作
- 浏览器兼容性 - 如果有 web 版本,考虑兼容性
调试技巧
监控键盘输入
useInput((input, key) => {
console.log('Raw input:', { input, key });
console.log('Key properties:', Object.keys(key));
});
测试特殊键
const testKeys = {
'Ctrl+C': { ctrl: true, name: 'c' },
'Home': { home: true },
'End': { end: true },
'F1': { fn: true, name: 'f1' },
};
性能监控
const handleInput = (input: string, key: Key) => {
const start = performance.now();
// 处理逻辑
const end = performance.now();
if (end - start > 16) { // 超过一帧
console.warn('Slow input handling:', end - start);
}
};
常见问题
Q: Key 类型从哪里来?
# 查看 ink 的类型定义
cat node_modules/ink/build/index.d.ts | grep "interface Key"
Q: 如何处理组合键?
if (key.ctrl && key.name === 'a') {
// Ctrl+A: 全选
}
if (key.meta && key.name === 'v') {
// Cmd+V (Mac) / Win+V: 粘贴
}
Q: 输入延迟问题?
// 使用防抖
const debouncedHandler = useMemo(
() => debounce(handleInput, 50),
[]
);
完成后
Hook 系统修复完成后,用户输入处理应该完全正常。这是用户体验的关键部分,确保充分测试。