Files
claude-code/packages/@ant/ink/docs/02-layout.md
claude-code-best 2fb1c9dcd8 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>
2026-04-13 09:52:05 +08:00

8.5 KiB

Chapter 2: Layout System

Ink uses Yoga (Facebook's cross-platform layout engine) to implement CSS Flexbox in the terminal. Every layout is flexbox-based -- there is no CSS Grid or flow layout.

Box Component

Box is the fundamental layout primitive. It is the terminal equivalent of <div style="display: flex">.

import { Box, Text } from '@anthropic/ink'

<Box flexDirection="row" gap={1}>
  <Text>Left</Text>
  <Text>Right</Text>
</Box>

Box Props (Styles)

All layout props are passed directly as JSX props (no style={} wrapper needed):

Flex Direction

Controls the main axis direction.

<Box flexDirection="row">...</Box>            // Left to right (default)
<Box flexDirection="column">...</Box>         // Top to bottom
<Box flexDirection="row-reverse">...</Box>    // Right to left
<Box flexDirection="column-reverse">...</Box> // Bottom to top

Flex Grow / Shrink / Basis

<Box flexGrow={1}>...</Box>      // Grow to fill available space
<Box flexShrink={0}>...</Box>    // Don't shrink below intrinsic size
<Box flexBasis={20}>...</Box>    // Initial size before flex distribution
<Box flexBasis="50%">...</Box>   // Percentage basis

Default values: flexGrow={0}, flexShrink={1}, flexBasis=auto.

Flex Wrap

<Box flexWrap="nowrap">...</Box>       // Single line (default)
<Box flexWrap="wrap">...</Box>         // Multiple lines
<Box flexWrap="wrap-reverse">...</Box> // Reverse cross-axis stacking

Alignment

<Box alignItems="flex-start">...</Box>  // Cross-axis start
<Box alignItems="center">...</Box>      // Cross-axis center
<Box alignItems="flex-end">...</Box>    // Cross-axis end
<Box alignItems="stretch">...</Box>     // Stretch to fill (default)

<Box alignSelf="flex-start">...</Box>   // Override parent's alignItems
<Box alignSelf="center">...</Box>
<Box alignSelf="flex-end">...</Box>
<Box alignSelf="auto">...</Box>         // Inherit from parent

Justify Content

<Box justifyContent="flex-start">...</Box>   // Main-axis start (default)
<Box justifyContent="flex-end">...</Box>      // Main-axis end
<Box justifyContent="center">...</Box>        // Center
<Box justifyContent="space-between">...</Box> // Equal gaps, no edges
<Box justifyContent="space-around">...</Box>  // Equal gaps with edges
<Box justifyContent="space-evenly">...</Box>  // Evenly distributed

Gap

Spacing between children (only accepts integers):

<Box gap={1}>...</Box>              // Both row and column gap
<Box columnGap={2}>...</Box>        // Gap between columns only
<Box rowGap={1}>...</Box>           // Gap between rows only

Padding

Inner spacing (only accepts integers):

<Box padding={1}>...</Box>           // All sides
<Box paddingX={2}>...</Box>          // Left and right
<Box paddingY={1}>...</Box>          // Top and bottom
<Box paddingLeft={2}>...</Box>       // Left only
<Box paddingRight={2}>...</Box>      // Right only
<Box paddingTop={1}>...</Box>        // Top only
<Box paddingBottom={1}>...</Box>     // Bottom only

Margin

Outer spacing (only accepts integers):

<Box margin={1}>...</Box>            // All sides
<Box marginX={2}>...</Box>           // Left and right
<Box marginY={1}>...</Box>           // Top and bottom
<Box marginLeft={2}>...</Box>        // Left only
<Box marginRight={2}>...</Box>       // Right only
<Box marginTop={1}>...</Box>         // Top only
<Box marginBottom={1}>...</Box>      // Bottom only

Note: Fractional values for padding, margin, and gap are not supported. Ink will emit warnings if non-integer values are used.

Width & Height

<Box width={40}>...</Box>           // Fixed 40 characters wide
<Box height={10}>...</Box>          // Fixed 10 rows tall
<Box width="50%">...</Box>          // 50% of parent's width
<Box width="100%">...</Box>         // Full parent width

Min/Max Dimensions

<Box minWidth={20}>...</Box>
<Box maxWidth={80}>...</Box>
<Box minHeight={5}>...</Box>
<Box maxHeight={20}>...</Box>

Percentage values are supported: minWidth="30%".

Position

<Box position="absolute" top={0} right={0}>...</Box>
<Box position="absolute" top="10%" left="20%">...</Box>
<Box position="relative">...</Box>  // Default

Position absolute removes the element from normal flow and positions it relative to its nearest positioned ancestor. Useful for overlays.

Display

<Box display="flex">...</Box>    // Visible (default)
<Box display="none">...</Box>    // Hidden (removed from layout)

Border

<Box borderStyle="single">...</Box>    // Thin border
<Box borderStyle="double">...</Box>    // Double-line border
<Box borderStyle="round">...</Box>     // Rounded corners
<Box borderStyle="bold">...</Box>      // Bold border
<Box borderStyle="singleDouble">...</Box>  // Mixed
<Box borderStyle="doubleSingle">...</Box>  // Mixed
<Box borderStyle="classic">...</Box>   // ASCII art border

Control individual sides and colors:

<Box
  borderStyle="single"
  borderTop={false}           // Hide top border
  borderBottom={true}         // Show bottom border
  borderColor="rgb(255,0,0)"  // Red border
  borderDimColor={true}       // Dim the border
>
  ...
</Box>

Per-side colors:

<Box
  borderStyle="single"
  borderTopColor="rgb(255,0,0)"
  borderBottomColor="ansi:green"
  borderLeftColor="#0000FF"
  borderRightColor="ansi256(200)"
/>

Border text (labels in the border):

<Box
  borderStyle="round"
  borderText={{ title: "My Panel", align: "left" }}
/>

Background

<Box backgroundColor="rgb(40,40,40)">...</Box>

Overflow

<Box overflow="visible">...</Box>   // Content expands container (default)
<Box overflow="hidden">...</Box>    // Clip without scrolling
<Box overflow="scroll">...</Box>    // Enable scrolling (use ScrollBox)

overflowX and overflowY control each axis independently.

Opaque

<Box opaque={true}>...</Box>

Fills the box interior with spaces (using terminal's default background) before rendering children. Useful for absolute-positioned overlays where gaps would otherwise be transparent.

NoSelect

<Box noSelect={true}>...</Box>              // Exclude from text selection
<Box noSelect="from-left-edge">...</Box>    // Exclude from column 0 to box edge

Only affects alt-screen text selection. Useful for gutters (line numbers, diff markers).

Spacer

Spacer fills all available space along the main axis (equivalent to flexGrow: 1).

<Box flexDirection="row">
  <Text>Left</Text>
  <Spacer />
  <Text>Right</Text>
</Box>

Newline

Inserts line breaks.

<Text>
  Line 1
  <Newline />
  Line 2
  <Newline count={2} />
  Line 4 (after double break)
</Text>

Layout Examples

Two-column layout

<Box flexDirection="row" width={80}>
  <Box width="50%" padding={1}>
    <Text>Left column</Text>
  </Box>
  <Box width="50%" padding={1}>
    <Text>Right column</Text>
  </Box>
</Box>

Centered content

<Box justifyContent="center" alignItems="center" height={20}>
  <Text>Centered!</Text>
</Box>
<Box flexDirection="column" height={24}>
  <Box flexGrow={1}>
    <Text>Scrollable content area</Text>
  </Box>
  <Box>
    <Text>Status bar at bottom</Text>
  </Box>
</Box>

Bordered panel with title

<Box
  flexDirection="column"
  borderStyle="round"
  borderColor="rgb(87,105,247)"
  padding={1}
  width={60}
>
  <Text bold>Panel Title</Text>
  <Text>Panel content goes here.</Text>
</Box>

NoSelect

Wraps a region to exclude it from text selection in alt-screen mode. A convenience wrapper around Box with noSelect set.

import { NoSelect } from '@anthropic/ink'

<Box flexDirection="row">
  <NoSelect>
    <Text dimColor>1  </Text>
  </NoSelect>
  <Text>selectable code here</Text>
</Box>

Props

Prop Type Default Description
children ReactNode - Content
fromLeftEdge boolean false Extend exclusion from column 0 to box's right edge

Accepts all BoxProps except noSelect.

BaseBox vs ThemedBox

Two versions of Box are exported:

  • BaseBox (imported as BaseBox) -- Raw box, color props accept only raw Color values
  • Box (themed, imported as Box) -- Theme-aware, color props accept keyof Theme | Color
// Raw
<BaseBox borderStyle="single" borderColor="rgb(255,0,0)" />

// Theme-aware (resolves 'permission' to the current theme's blue)
<Box borderStyle="single" borderColor="permission" />