mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 22:05:50 +00:00
* feat: 第一版大重构 * fix: 修复类型问题 * chore: 更新版本到 1.3.2 * Add brave as alternative WebSearchTool * fix: 修正顺序 * fix: 修复对穷鬼模式的 auto dream 和 session memory 越过 * feat: 穷鬼模式去除 session-summary * feat: 创建 builtin-tools 包,搬运所有工具实现 将 src/tools/ 下的全部 60 个工具目录迁移至 packages/builtin-tools/src/tools/, 内部导入路径已更新为 src/ alias 模式。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: 更新 src/ 中所有工具引用至 builtin-tools 包,删除 src/tools/ - src/tools.ts 及 178 个 src/ 文件的 import 路径从 ./tools/ 改为 builtin-tools/tools/ - 删除 src/tools/ 整个目录(已迁移至 packages/builtin-tools/) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: 添加 builtin-tools 路径别名至 tsconfig,更新 bun.lock - tsconfig.json 新增 builtin-tools/* 和 builtin-tools 路径映射 - 新增 packages/builtin-tools/src 至 include Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: 为 builtin-tools、mcp-client、agent-tools 添加 @claude-code-best 作用域前缀 所有包名及 import 路径统一添加 @claude-code-best/ 前缀: - builtin-tools → @claude-code-best/builtin-tools - mcp-client → @claude-code-best/mcp-client - agent-tools → @claude-code-best/agent-tools Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: 修复 node 环境没有 bun 的问题 --------- Co-authored-by: Eric-Guo <eric.guocz@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
233 lines
4.9 KiB
Markdown
233 lines
4.9 KiB
Markdown
# Chapter 10: Events & Focus
|
|
|
|
## Event System
|
|
|
|
Ink implements a DOM-like event system with capture/bubble phases, propagation control, and prioritized dispatch.
|
|
|
|
### Event Classes
|
|
|
|
All events extend the base `Event` class:
|
|
|
|
```ts
|
|
class Event {
|
|
stopImmediatePropagation(): void
|
|
}
|
|
```
|
|
|
|
### InputEvent
|
|
|
|
Emitted for every keystroke or input action.
|
|
|
|
```ts
|
|
class InputEvent extends Event {
|
|
readonly input: string // Character(s) entered
|
|
readonly key: Key // Parsed key metadata
|
|
readonly keypress: ParsedKey // Raw keypress data
|
|
}
|
|
```
|
|
|
|
### KeyboardEvent
|
|
|
|
DOM-like keyboard event for focused elements.
|
|
|
|
```ts
|
|
class KeyboardEvent extends Event {
|
|
readonly key: Key
|
|
}
|
|
```
|
|
|
|
Dispatched via `onKeyDown` / `onKeyDownCapture` on `Box`.
|
|
|
|
### ClickEvent
|
|
|
|
Mouse click event (alt-screen only).
|
|
|
|
```ts
|
|
class ClickEvent extends Event {
|
|
readonly x: number // Column (0-indexed)
|
|
readonly y: number // Row (0-indexed)
|
|
}
|
|
```
|
|
|
|
Clicks bubble from the deepest hit Box up through ancestors.
|
|
|
|
### FocusEvent
|
|
|
|
Focus change event.
|
|
|
|
```ts
|
|
class FocusEvent extends Event {
|
|
readonly relatedTarget: DOMElement | null
|
|
}
|
|
```
|
|
|
|
### TerminalFocusEvent
|
|
|
|
Terminal window focus change.
|
|
|
|
```ts
|
|
class TerminalFocusEvent extends Event {
|
|
readonly type: 'terminalfocus' | 'terminalblur'
|
|
}
|
|
```
|
|
|
|
### ResizeEvent
|
|
|
|
Terminal resize event (internal).
|
|
|
|
### PasteEvent
|
|
|
|
Pasted text event (bracketed paste mode).
|
|
|
|
## Event Dispatch Flow
|
|
|
|
```
|
|
stdin data → parse-keypress → InputEvent
|
|
↓
|
|
App.handleInput (useInput handlers)
|
|
↓
|
|
Box.onKeyDown (focused element, bubble)
|
|
```
|
|
|
|
### Capture and Bubble Phases
|
|
|
|
```tsx
|
|
<Box
|
|
onKeyDownCapture={(e) => {
|
|
// Capture phase: fires top-down
|
|
console.log('Parent captures key')
|
|
}}
|
|
onKeyDown={(e) => {
|
|
// Bubble phase: fires bottom-up
|
|
console.log('Parent receives bubbled key')
|
|
}}
|
|
>
|
|
<Box
|
|
onKeyDown={(e) => {
|
|
// Target: fires first in bubble phase
|
|
console.log('Child handles key')
|
|
e.stopImmediatePropagation() // Stop here
|
|
}}
|
|
>
|
|
<Text>Focus here</Text>
|
|
</Box>
|
|
</Box>
|
|
```
|
|
|
|
### Event Propagation Methods
|
|
|
|
| Method | Effect |
|
|
|--------|--------|
|
|
| `event.stopImmediatePropagation()` | Stop all subsequent handlers |
|
|
| `event.preventDefault()` | Not supported in terminal context |
|
|
|
|
## FocusManager
|
|
|
|
DOM-like focus management system.
|
|
|
|
### How Focus Works
|
|
|
|
1. Elements with `tabIndex >= 0` participate in Tab/Shift+Tab cycling
|
|
2. Elements with `tabIndex === -1` are programmatically focusable only
|
|
3. Elements with `autoFocus` receive focus on mount
|
|
4. Clicking a focusable element focuses it
|
|
|
|
### Focus API
|
|
|
|
```ts
|
|
class FocusManager {
|
|
activeElement: DOMElement | null
|
|
|
|
focus(node: DOMElement): void
|
|
blur(): void
|
|
focusNext(root: DOMElement): void // Tab
|
|
focusPrevious(root: DOMElement): void // Shift+Tab
|
|
|
|
handleNodeRemoved(node: DOMElement, root: DOMElement): void
|
|
handleAutoFocus(node: DOMElement): void
|
|
handleClickFocus(node: DOMElement): void
|
|
|
|
enable(): void
|
|
disable(): void
|
|
}
|
|
```
|
|
|
|
### Tab Navigation
|
|
|
|
```tsx
|
|
<Box flexDirection="column">
|
|
<Button tabIndex={0} onAction={handleSave}>
|
|
{(s) => <Text>{s.focused ? '> Save' : ' Save'}</Text>}
|
|
</Button>
|
|
<Button tabIndex={0} onAction={handleCancel}>
|
|
{(s) => <Text>{s.focused ? '> Cancel' : ' Cancel'}</Text>}
|
|
</Button>
|
|
<Button tabIndex={-1} onAction={handleSecret}>
|
|
{/* Not reachable via Tab */}
|
|
{(s) => <Text>Secret</Text>}
|
|
</Button>
|
|
</Box>
|
|
```
|
|
|
|
### Auto Focus
|
|
|
|
```tsx
|
|
<Box tabIndex={0} autoFocus onKeyDown={handleKey}>
|
|
<Text>Receives focus immediately on mount</Text>
|
|
</Box>
|
|
```
|
|
|
|
### Focus Events
|
|
|
|
```tsx
|
|
<Box
|
|
tabIndex={0}
|
|
onFocus={(e) => console.log('Got focus')}
|
|
onBlur={(e) => console.log('Lost focus')}
|
|
onFocusCapture={(e) => console.log('Capture: focus in')}
|
|
onBlurCapture={(e) => console.log('Capture: focus out')}
|
|
>
|
|
<Text>Focusable element</Text>
|
|
</Box>
|
|
```
|
|
|
|
## Hit Testing
|
|
|
|
Mouse click/hover resolution:
|
|
|
|
1. Screen coordinates are mapped to DOM elements via Yoga layout
|
|
2. The deepest element at the click position is the target
|
|
3. Click events bubble upward through ancestors
|
|
4. Hover events use `mouseenter`/`mouseleave` semantics (no bubbling between children)
|
|
|
|
### Click Hit Testing
|
|
|
|
```ts
|
|
dispatchClick(rootNode, col, row): void
|
|
```
|
|
|
|
Walks the DOM tree, finds the deepest Box at (col, row), fires `onClick`, then bubbles to ancestors.
|
|
|
|
### Hover Hit Testing
|
|
|
|
```ts
|
|
dispatchHover(rootNode, col, row, hoveredNodes): void
|
|
```
|
|
|
|
Tracks which nodes are under the pointer. Fires `onMouseEnter`/`onMouseLeave` as the pointer moves between elements.
|
|
|
|
## EventEmitter
|
|
|
|
Custom event emitter for internal use:
|
|
|
|
```ts
|
|
class EventEmitter {
|
|
on(event: string, handler: Function): void
|
|
off(event: string, handler: Function): void
|
|
emit(event: string, ...args: any[]): void
|
|
removeListener(event: string, handler: Function): void
|
|
}
|
|
```
|
|
|
|
Used internally by the Ink instance for `input` events.
|