mcpField
I
ink

ink

github.com/google-labs-code开发28.7k169 次安装2 周前更新
代码仓库

关于 ink

@json-render/ink 将 JSON 规范渲染为交互式终端组件树,适用于构建由数据驱动的终端界面。它提供标准组件、状态绑定、条件显示、事件动作和流式渲染能力。

主要功能

  1. 01JSON 规范渲染为终端界面
  2. 02提供布局与内容组件
  3. 03输入、选择和确认交互
  4. 04状态绑定与条件显示
  5. 05内置状态操作与动态列表
  6. 06支持 JSONL 流式渲染

适用场景

  1. 01依据 JSON 规范生成终端界面
  2. 02构建带输入和选择控件的表单
  3. 03按状态显示或更新列表
  4. 04逐步呈现 API 流式界面规范

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/google-labs-code/design.md --skill ink

安装后在 Claude Code 中输入/ink确认已加载。

详细介绍

@json-render/ink

Ink 终端渲染器,可将 JSON 规范转换为交互式终端组件树,支持标准组件、数据绑定、可见性、动作和动态属性。

快速开始

import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/ink/schema";
import {
  standardComponentDefinitions,
  standardActionDefinitions,
} from "@json-render/ink/catalog";
import { defineRegistry, Renderer, type Components } from "@json-render/ink";
import { z } from "zod";

// Create catalog with standard + custom components
const catalog = defineCatalog(schema, {
  components: {
    ...standardComponentDefinitions,
    CustomWidget: {
      props: z.object({ title: z.string() }),
      slots: [],
      description: "Custom widget",
    },
  },
  actions: standardActionDefinitions,
});

// Register only custom components (standard ones are built-in)
const { registry } = defineRegistry(catalog, {
  components: {
    CustomWidget: ({ props }) => <Text>{props.title}</Text>,
  } as Components<typeof catalog>,
});

// Render
function App({ spec }) {
  return (
    <JSONUIProvider initialState={{}}>
      <Renderer spec={spec} registry={registry} />
    </JSONUIProvider>
  );
}

规范结构(扁平元素映射)

Ink 模式使用带根键的扁平元素映射:

{
  "root": "main",
  "elements": {
    "main": {
      "type": "Box",
      "props": { "flexDirection": "column", "padding": 1 },
      "children": ["heading", "content"]
    },
    "heading": {
      "type": "Heading",
      "props": { "text": "Dashboard", "level": "h1" },
      "children": []
    },
    "content": {
      "type": "Text",
      "props": { "text": "Hello from the terminal!" },
      "children": []
    }
  }
}

标准组件

布局
  • Box - Flexbox 布局容器(类似终端中的 <div>)。用于分组、间距、边框和对齐。默认 flexDirection 为 row。
  • Text - 带可选样式(颜色、粗体、斜体等)的文本输出
  • Newline - 插入空行。必须位于 flexDirection 为 column 的 Box 内。
  • Spacer - 沿主轴方向扩展的弹性空白区域。
内容
  • Heading - 章节标题(h1:粗体+下划线,h2:粗体,h3:粗体+暗淡,h4:暗淡)
  • Divider - 带可选居中标题的水平分隔线
  • Badge - 彩色行内标签(变体:default、info、success、warning、error)
  • Spinner - 带可选标签的动画加载指示器
  • ProgressBar - 水平进度条(0-1)
  • Sparkline - 使用 Unicode 块字符的行内图表
  • BarChart - 带标签和数值的水平柱状图
  • Table - 带表头和行的表格数据
  • List - 无序或有序列表
  • ListItem - 包含标题、副标题、前导/尾随文本的结构化列表行
  • Card - 带可选标题的边框容器
  • KeyValue - 键值对展示
  • Link - 带可选标签的可点击 URL
  • StatusLine - 带彩色图标的状态消息(info、success、warning、error)
  • Markdown - 以终端样式渲染 Markdown 文本
交互
  • TextInput - 文本输入框(事件:submit、change)
  • Select - 支持方向键导航的选择菜单(事件:change)
  • MultiSelect - 使用空格键切换的多选(事件:change、submit)
  • ConfirmInput - 是/否确认提示(事件:confirm、deny)
  • Tabs - 支持左右方向键导航的标签栏(事件:change)

可见性条件

在元素上使用 visible 可根据状态显示或隐藏。语法:{ "$state": "/path" }、{ "$state": "/path", "eq": value }、{ "$state": "/path", "not": true },{ "$and": [cond1, cond2] } 表示与,{ "$or": [cond1, cond2] } 表示或。

动态属性表达式

任何属性值都可以是在渲染时解析的数据驱动表达式:

  • { "$state": "/state/key" } - 从状态模型读取(单向读取)
  • { "$bindState": "/path" } - 双向绑定:用于表单组件的自然值属性
  • { "$bindItem": "field" } - 双向绑定到重复项的字段
  • { "$cond": <condition>, "$then": <value>, "$else": <value> } - 条件值
  • { "$template": "Hello, ${/name}!" } - 将状态值插入字符串

组件不使用 statePath 属性进行双向绑定。请在自然值属性上使用 { "$bindState": "/path" }。

事件系统

组件使用 emit 触发命名事件。元素的 on 字段将事件映射到动作绑定:

CustomButton: ({ props, emit }) => (
  <Box>
    <Text>{props.label}</Text>
    {/* emit("press") triggers the action bound in the spec's on.press */}
  </Box>
),
{
  "type": "CustomButton",
  "props": { "label": "Submit" },
  "on": { "press": { "action": "submit" } },
  "children": []
}

内置动作

setState、pushState 和 removeState 为内置动作,会被自动处理:

{ "action": "setState", "params": { "statePath": "/activeTab", "value": "home" } }
{ "action": "pushState", "params": { "statePath": "/items", "value": { "text": "New" } } }
{ "action": "removeState", "params": { "statePath": "/items", "index": 0 } }

重复(动态列表)

在容器元素上使用 repeat 字段,可从状态数组渲染项目:

{
  "type": "Box",
  "props": { "flexDirection": "column" },
  "repeat": { "statePath": "/items", "key": "id" },
  "children": ["item-row"]
}

在重复的子元素中,使用 { "$item": "field" } 读取当前项,使用 { "$index": true } 获取当前索引。

流式渲染

使用 useUIStream 从 JSONL 补丁流中逐步渲染规范:

import { useUIStream } from "@json-render/ink";

const { spec, send, isStreaming } = useUIStream({ api: "/api/generate" });

服务端提示词生成

使用 ./server 导出从你的目录生成 AI 系统提示词:

import { catalog } from "./catalog";

const systemPrompt = catalog.prompt({ system: "You are a terminal assistant." });

Providers

ProviderPurpose
StateProvider在组件之间共享状态(使用 JSON Pointer 路径)。接受可选的 store 属性以启用受控模式。
ActionProvider处理通过事件系统分发的操作
VisibilityProvider基于状态启用条件渲染
ValidationProvider表单字段验证
FocusProvider管理交互组件之间的焦点
JSONUIProvider组合所有上下文的 Provider
外部 Store(受控模式)

将 StateStore 传递给 StateProvider(或 JSONUIProvider),即可使用外部状态管理:

import { createStateStore, type StateStore } from "@json-render/ink";

const store = createStateStore({ count: 0 });

<StateProvider store={store}>{children}</StateProvider>

store.set("/count", 1); // React re-renders automatically

提供 store 时,initialState 和 onStateChange 会被忽略。

createRenderer(高层 API)

import { createRenderer } from "@json-render/ink";
import { standardComponents } from "@json-render/ink";
import { catalog } from "./catalog";

const InkRenderer = createRenderer(catalog, {
  ...standardComponents,
  // custom component overrides here
});

// InkRenderer includes all providers (state, visibility, actions, focus)
render(
  <InkRenderer spec={spec} state={{ activeTab: "overview" }} />
);

主要导出

导出用途
defineRegistry基于 catalog 创建类型安全的组件注册表
Renderer使用注册表渲染 spec
createRenderer高层 API:创建带有内置 Provider 的组件
JSONUIProvider组合所有上下文的 Provider
schemaInk 扁平元素映射的 schema(包含内置的状态操作)
standardComponentDefinitions所有标准组件的 catalog 定义
standardActionDefinitions标准操作的 catalog 定义
standardComponents预构建的组件实现
useStateStore访问状态上下文
useStateValue获取状态中的单个值
useBoundProp为 $bindState/$bindItem 表达式提供双向绑定
useActions访问操作上下文
useAction获取单个操作分发函数
useOptionalValidationuseValidation 的非抛错变体
useUIStream从 API 端点流式接收 spec
createStateStore创建与框架无关的内存 StateStore
StateStore用于接入外部状态管理的接口
Components带类型的组件映射(感知 catalog)
Actions带类型的操作映射(感知 catalog)
ComponentContext带类型的组件上下文(感知 catalog)
flatToTree将扁平元素映射转换为树结构

终端 UI 设计指南

  • 使用 Box 进行布局(flexDirection、padding、gap)。默认 flexDirection 为 row。
  • 终端宽度约为 80-120 列。主结构优先使用纵向布局(flexDirection: column)。
  • 在 Box 上使用 borderStyle 进行视觉分组(single、double、round、bold)。
  • 使用具名终端颜色:red、green、yellow、blue、magenta、cyan、white、gray。
  • 使用 Heading 作为分区标题,Divider 分隔各分区,Badge 显示状态,KeyValue 显示带标签的数据,Card 用于带边框的分组。
  • 在多视图 UI 中使用 Tabs,并为子内容设置 visible 条件。
  • 使用 Sparkline 展示行内趋势,使用 BarChart 比较数值。

评论

支持 Markdown