mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-23 08:45:50 +00:00
feat: 工具层及 mcp 大重构 (#252)
* 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>
This commit is contained in:
301
packages/@ant/ink/docs/11-core-architecture.md
Normal file
301
packages/@ant/ink/docs/11-core-architecture.md
Normal file
@@ -0,0 +1,301 @@
|
||||
# Chapter 11: Core Architecture
|
||||
|
||||
This chapter covers the internal rendering pipeline, DOM model, and screen buffer system. This is advanced material -- most users only need the component and hooks APIs.
|
||||
|
||||
## Rendering Pipeline
|
||||
|
||||
```
|
||||
React Component Tree
|
||||
↓ (React reconciler)
|
||||
Ink DOM Tree (virtual terminal DOM)
|
||||
↓ (Yoga layout)
|
||||
Positioned DOM Tree (computed x, y, width, height)
|
||||
↓ (renderNodeToOutput)
|
||||
Output Buffer (styled characters)
|
||||
↓ (renderer → Screen)
|
||||
Screen Buffer (Int32Array of cells)
|
||||
↓ (diffEach)
|
||||
ANSI Diff Patches (minimal escape sequences)
|
||||
↓ (writeDiffToTerminal)
|
||||
Terminal stdout
|
||||
```
|
||||
|
||||
### Frame Lifecycle
|
||||
|
||||
Each render cycle (`onRender`) follows these phases:
|
||||
|
||||
1. **React Commit** -- React reconciles the virtual tree; host config updates Ink DOM
|
||||
2. **Yoga Layout** -- All dirty nodes have their styles applied and layout computed
|
||||
3. **Renderer** -- Creates Output buffer, calls `renderNodeToOutput` for the full tree
|
||||
4. **Screen Diff** -- New frame is compared against previous frame cell-by-cell
|
||||
5. **Optimize** -- Patches are merged and ordered for minimal cursor movement
|
||||
6. **Write** -- ANSI escape sequences are written to stdout
|
||||
|
||||
### Frame Timing
|
||||
|
||||
```ts
|
||||
const FRAME_INTERVAL_MS = 16 // ~60fps cap
|
||||
```
|
||||
|
||||
Renders are throttled. Multiple state updates in one frame are batched.
|
||||
|
||||
### Double Buffering
|
||||
|
||||
Two frames are maintained:
|
||||
|
||||
- **`frontFrame`** -- The currently displayed frame
|
||||
- **`backFrame`** -- The frame being rendered
|
||||
|
||||
After rendering, they are swapped. This prevents partial updates from being visible.
|
||||
|
||||
## Ink DOM
|
||||
|
||||
### Node Types
|
||||
|
||||
```ts
|
||||
type ElementNames =
|
||||
| 'ink-root' // Root container
|
||||
| 'ink-box' // Box component
|
||||
| 'ink-text' // Text component
|
||||
| 'ink-virtual-text' // Intermediate text wrapper
|
||||
| 'ink-link' // Link component
|
||||
| 'ink-raw-ansi' // Raw ANSI content
|
||||
```
|
||||
|
||||
### DOMElement
|
||||
|
||||
```ts
|
||||
type DOMElement = {
|
||||
nodeName: ElementNames
|
||||
attributes: Record<string, unknown>
|
||||
childNodes: DOMNode[] // DOMElement | TextNode
|
||||
yogaNode?: LayoutNode // Yoga layout node
|
||||
textStyles?: TextStyles // Inherited text styles
|
||||
|
||||
// Scroll state
|
||||
scrollTop?: number
|
||||
scrollHeight?: number
|
||||
scrollViewportHeight?: number
|
||||
scrollViewportTop?: number
|
||||
stickyScroll?: boolean
|
||||
pendingScrollDelta?: number
|
||||
scrollAnchor?: { el: DOMElement; offset: number }
|
||||
|
||||
// Dirty tracking
|
||||
dirty: boolean
|
||||
|
||||
// Event handlers (stored separately)
|
||||
onClick?: (event: ClickEvent) => void
|
||||
onFocus?: (event: FocusEvent) => void
|
||||
onBlur?: (event: FocusEvent) => void
|
||||
onKeyDown?: (event: KeyboardEvent) => void
|
||||
onMouseEnter?: () => void
|
||||
onMouseLeave?: () => void
|
||||
}
|
||||
```
|
||||
|
||||
### TextNode
|
||||
|
||||
```ts
|
||||
type TextNode = {
|
||||
nodeName: '#text'
|
||||
nodeValue: string
|
||||
yogaNode?: LayoutNode
|
||||
}
|
||||
```
|
||||
|
||||
### DOM Operations
|
||||
|
||||
```ts
|
||||
// Node creation
|
||||
createNode(nodeName: string): DOMElement
|
||||
createTextNode(text: string): TextNode
|
||||
|
||||
// Tree manipulation
|
||||
appendChildNode(parent: DOMElement, child: DOMNode): void
|
||||
insertBeforeNode(parent: DOMElement, child: DOMNode, before: DOMNode): void
|
||||
removeChildNode(parent: DOMElement, child: DOMNode): void
|
||||
|
||||
// Attribute manipulation
|
||||
setAttribute(node: DOMElement, key: string, value: unknown): void
|
||||
setStyle(node: DOMElement, style: Styles): void
|
||||
setTextStyles(node: DOMElement, styles: TextStyles): void
|
||||
|
||||
// Dirty tracking
|
||||
markDirty(node: DOMElement): void
|
||||
scheduleRenderFrom(node: DOMElement): void
|
||||
```
|
||||
|
||||
## Screen Buffer
|
||||
|
||||
### Cell Storage
|
||||
|
||||
The screen buffer uses packed `Int32Array` storage for memory efficiency:
|
||||
|
||||
```ts
|
||||
type Screen = {
|
||||
width: number
|
||||
height: number
|
||||
cells: Int32Array // 2 Int32s per cell: [charId, packed_style_hyperlink_width]
|
||||
cells64: BigInt64Array // For bulk fill operations
|
||||
charPool: CharPool // String interning
|
||||
stylePool: StylePool // ANSI code interning
|
||||
hyperlinkPool: HyperlinkPool
|
||||
emptyStyleId: number
|
||||
damage: Rectangle | undefined // Bounding box of changed cells
|
||||
noSelect: Uint8Array // Per-cell no-select bitmap
|
||||
softWrap: Int32Array // Per-row soft-wrap markers
|
||||
}
|
||||
```
|
||||
|
||||
### Cell Width
|
||||
|
||||
```ts
|
||||
enum CellWidth {
|
||||
Narrow = 0, // Regular character (1 column)
|
||||
Wide = 1, // CJK/emoji (2 columns)
|
||||
SpacerTail = 2, // Right half of wide character
|
||||
SpacerHead = 3, // Soft-wrapped wide character
|
||||
}
|
||||
```
|
||||
|
||||
### Style Pool
|
||||
|
||||
ANSI style codes are interned for efficiency:
|
||||
|
||||
```ts
|
||||
class StylePool {
|
||||
intern(codes: AnsiCode[]): number // Returns compact ID
|
||||
get(id: number): AnsiCode[]
|
||||
transition(from: number, to: number): string // Cached ANSI transition
|
||||
withInverse(id: number): number // Selection overlay
|
||||
setSelectionBg(bg: AnsiCode): void // Theme-aware selection bg
|
||||
}
|
||||
```
|
||||
|
||||
### Diff Algorithm
|
||||
|
||||
```ts
|
||||
diffEach(prev: Screen, next: Screen, callback: (x, y, oldCell, newCell) => void): void
|
||||
```
|
||||
|
||||
Only iterates cells within the damage bounding box. Unchanged regions are skipped entirely.
|
||||
|
||||
### Screen Operations
|
||||
|
||||
```ts
|
||||
createScreen(width, height, stylePool, charPool, hyperlinkPool): Screen
|
||||
setCellAt(screen, x, y, cell): void
|
||||
cellAt(screen, x, y): Cell
|
||||
clearRegion(screen, x, y, width, height): void
|
||||
blitRegion(dst, src, x, y, maxX, maxY): void
|
||||
shiftRows(screen, top, bottom, n): void
|
||||
```
|
||||
|
||||
## Layout Engine
|
||||
|
||||
### Yoga Integration
|
||||
|
||||
Ink wraps Facebook's Yoga layout engine for Flexbox computation:
|
||||
|
||||
```ts
|
||||
// Layout node types
|
||||
enum LayoutDisplay { Flex, None }
|
||||
enum LayoutPositionType { Absolute, Relative }
|
||||
enum LayoutOverflow { Visible, Hidden, Scroll }
|
||||
enum LayoutFlexDirection { Row, Column, RowReverse, ColumnReverse }
|
||||
enum LayoutWrap { NoWrap, Wrap, WrapReverse }
|
||||
enum LayoutAlign { FlexStart, Center, FlexEnd, Stretch }
|
||||
enum LayoutJustify { FlexStart, Center, FlexEnd, SpaceBetween, SpaceAround, SpaceEvenly }
|
||||
enum LayoutEdge { Top, Bottom, Left, Right, Start, End, Horizontal, Vertical, All }
|
||||
enum LayoutGutter { Column, Row, All }
|
||||
```
|
||||
|
||||
### Style Application
|
||||
|
||||
Styles from React props are applied to Yoga nodes during the commit phase:
|
||||
|
||||
```ts
|
||||
function styles(node: LayoutNode, style: Styles, resolvedStyle?: Styles): void
|
||||
```
|
||||
|
||||
This function maps each CSS-like prop to the corresponding Yoga setter.
|
||||
|
||||
## Output Buffer
|
||||
|
||||
Intermediate rendering target before screen diff:
|
||||
|
||||
```ts
|
||||
class Output {
|
||||
write(text: string, x: number, y: number, styles: TextStyles): void
|
||||
wrap(width: number, textWrap: TextWrap): void
|
||||
}
|
||||
```
|
||||
|
||||
`renderNodeToOutput` walks the DOM tree and writes styled characters into this buffer.
|
||||
|
||||
## Reconciler
|
||||
|
||||
Custom React reconciler that bridges React and the Ink DOM:
|
||||
|
||||
- **Host config** -- Defines how React operations map to Ink DOM mutations
|
||||
- **Concurrent mode** -- Supports `ConcurrentRoot` for React 19 features
|
||||
- **Yoga integration** -- Applies styles during commit phase
|
||||
- **DevTools** -- Connected in development mode
|
||||
|
||||
### Host Config Methods
|
||||
|
||||
| Method | Purpose |
|
||||
|--------|---------|
|
||||
| `createInstance` | Create `ink-box`, `ink-text`, etc. |
|
||||
| `createTextInstance` | Create `#text` node |
|
||||
| `appendChildNode` | Add child to parent |
|
||||
| `removeChildNode` | Remove child from parent |
|
||||
| `insertBefore` | Insert child before sibling |
|
||||
| `commitUpdate` | Update element attributes/styles |
|
||||
| `commitTextUpdate` | Update text content |
|
||||
| `getPublicInstance` | Return DOMElement for refs |
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
1. **String Interning** -- CharPool deduplicates character strings across frames
|
||||
2. **Style Caching** -- StylePool caches ANSI transition strings
|
||||
3. **Damage Tracking** -- Only diff cells within the changed bounding box
|
||||
4. **Bulk Operations** -- `Int32Array.set()` for fast region blit
|
||||
5. **Throttled Rendering** -- Frame rate capped at ~60fps
|
||||
6. **Viewport Culling** -- ScrollBox only renders visible children
|
||||
7. **Microtask Coalescing** -- Multiple scroll deltas merged into one render
|
||||
|
||||
## Frame Events
|
||||
|
||||
Debug instrumentation for render performance:
|
||||
|
||||
```ts
|
||||
type FrameEvent = {
|
||||
durationMs: number
|
||||
phases: {
|
||||
renderer: number // Yoga + renderNodeToOutput
|
||||
diff: number // Screen diff
|
||||
optimize: number // Patch optimization
|
||||
write: number // Terminal write
|
||||
patches: number // Number of ANSI patches
|
||||
yoga: number // Yoga layout time
|
||||
commit: number // React commit time
|
||||
yogaVisited: number // Yoga nodes visited
|
||||
yogaMeasured: number // Yoga nodes measured
|
||||
yogaCacheHits: number // Cached measurements
|
||||
yogaLive: number // Active Yoga nodes
|
||||
}
|
||||
flickers: FlickerReason[]
|
||||
}
|
||||
```
|
||||
|
||||
Enable with `onFrame` in RenderOptions:
|
||||
|
||||
```tsx
|
||||
render(<App />, {
|
||||
onFrame: (event) => {
|
||||
console.log(`Frame: ${event.durationMs}ms`)
|
||||
}
|
||||
})
|
||||
```
|
||||
Reference in New Issue
Block a user