jink 开源框架做到了
一个 Java 版的终端 UI 框架,用声明式的方式构建带颜色、边框、动画的命令行应用。兼容 Java 8,纯 Maven 依赖,拿来即用。
Gitee: https://gitee.com/free/jink
GitHub: https://github.com/abel533/jink
它能做什么?
先看效果,比解释更直观。
下面这个类似 top/htop 的系统监控界面,完全用 Java 写出来的:

- 实时 CPU 使用率(进度条 + 颜色告警)
- 系统内存、JVM 堆、线程数
- 进程列表,支持排序和实时搜索
- 鼠标滚轮滚动
- 方向键导航,选中行高亮
还有这个控制台迷宫游戏:
加载中…
- 随机 Prim 算法生成完美迷宫
- 方向键移动,绿色轨迹记录路径
- 自适应终端尺寸
这个框架叫 jink,是 ink(React for CLI)的 Java 版实现。
核心概念:3 行代码理解它
// 就像 React 一样,Box = div,Text = span
Ink.renderOnce(
Box.of(
Text.of("Hello, jink!").color(Color.GREEN).bold()
).borderStyle(BorderStyle.ROUND)
.borderColor(Color.BRIGHT_MAGENTA)
.paddingX(1),
40, 5
);
输出:
╭──────────────────────────────────────╮
│ Hello, jink! │
╰──────────────────────────────────────╯
没有手写 ANSI 转义码,没有坐标计算,就像写 HTML 一样自然。
它是怎么运作的?
整个渲染管道分 4 步:
组件树(Box/Text)
↓
虚拟 DOM(ElementNode)
↓
Flexbox 布局计算(纯 Java 实现)
↓
VirtualScreen → ANSI 字符串 → 终端
Flexbox 布局是纯 Java 实现的,不依赖任何 native 库(ink 用的是 Facebook 的 Yoga C++ 引擎)。支持 flexDirection、justifyContent、alignItems、flexGrow、gap、padding、margin,和 CSS Flexbox 一脉相承。
写一个有状态的交互组件
类似 React 的 useState + useEffect:
public class Counter extends Component<Counter.State> {
// 状态定义(用普通类或 record 都行)
static class State {
final int count;
State(int count) { this.count = count; }
}
public Counter() { super(new State(0)); }
@Override
public void onMount() {
// 类比 useEffect:组件挂载后启动定时器
scheduler.scheduleAtFixedRate(() -> {
setState(new State(getState().count + 1));
}, 100, 100, TimeUnit.MILLISECONDS);
}
@Override
public void onUnmount() {
scheduler.shutdownNow();
}
@Override
public Renderable render() {
return Text.of(getState().count + " tests passed")
.color(Color.GREEN);
}
}
setState() 触发重渲染,框架自动做差量更新,只刷新变化的行——和 React 的虚拟 DOM diff 思路一样。
键盘交互
重写 onInput 方法即可:
@Override
public void onInput(String input, Key key) {
if (key.upArrow()) {
setState(new State(getState().selected - 1));
} else if (key.downArrow()) {
setState(new State(getState().selected + 1));
} else if ("q".equals(input)) {
System.exit(0);
}
}
Key 对象封装了方向键、功能键、Ctrl 组合键、PageUp/Down 等常用按键的判断方法,不用再手动解析 ANSI 转义序列。
样式系统:丰富但不繁琐
Text.of("粗体红色").bold().color(Color.RED)
Text.of("RGB 真彩色").color(Color.rgb(255, 165, 0))
Text.of("Hex 颜色").color(Color.hex("FF8800"))
Text.of("反色(Inverse)").inverse()
Text.of("暗淡").dimmed()
// 嵌套文本样式
Text.of(
Text.of("前缀 ").color(Color.CYAN),
Text.of("高亮").color(Color.RED).bold(),
Text.of(" 后缀").dimmed()
)
支持 16 色、256 色、RGB 真彩色,中日韩字符自动占 2 列宽度。
9 种边框样式:
┌──────┐ ╔══════╗ ╭─────╮ ┏━━━━┓
│single│ ║double║ │round│ ┃bold┃
└──────┘ ╚══════╝ ╰─────╯ ┗━━━━┛
╓────────────╖ ╒════════════╕ +-------+ ↘↓↓↓↓↓↙
║singleDouble║ │doubleSingle│ |classic| →arrow←
╙────────────╜ ╘════════════╛ +-------+ ↗↑↑↑↑↑↖
Flexbox 布局:写起来就像 CSS
// 水平等分面板
Box.of(
Box.of(Text.of("左侧"))
.flexGrow(1)
.borderStyle(BorderStyle.SINGLE),
Box.of(Text.of("右侧"))
.flexGrow(1)
.borderStyle(BorderStyle.SINGLE)
).width(60).height(5);
// 垂直布局 + 弹性填充
Box.of(
Text.of("标题").bold(),
Spacer.create(), // 自动撑开中间空白
Text.of("底部提示").dimmed()
).flexDirection(FlexDirection.COLUMN).height(10);
// 内容对齐
Box.of(Text.of("X"), Text.of("Y"))
.justifyContent(JustifyContent.SPACE_BETWEEN)
.width(20);
// 输出:[X Y]
与 ink 的 API 对照
| 功能 | ink (TypeScript) | jink (Java) |
|---|---|---|
| 声明式 UI | JSX | Builder API |
| 状态管理 | useState | Component.setState() |
| 副作用 | useEffect | onMount / onUnmount |
| 输入处理 | useInput | onInput(String, Key) |
| Flexbox | Yoga (C++) | 纯 Java 实现 |
| 颜色 | chalk | 内置 Color |
| 边框 | boxen | 9 种 BorderStyle |
| 最低版本 | Node.js 18+ | Java 8+ |
开箱即用的 Demo 库
项目自带 20+ 个 Demo,覆盖各种使用场景:
| Demo | 说明 |
|---|---|
Counter | 自动递增计数器(定时器 + setState) |
TopDemo | 类 top/htop 系统监控 TUI |
MazeDemo | 控制台迷宫游戏(Prim 算法) |
CopilotDemo | GitHub Copilot CLI 界面复刻 |
FeatureShowcase | 4 个标签页综合展示 |
SelectInputDemo | 方向键列表选择 |
ChatDemo | 简单聊天输入框 |
JestDemo | 并发测试运行器模拟 |
一行命令运行任意 Demo:
.\scripts\run.ps1 # 交互式菜单(Windows)
./scripts/run.sh # 交互式菜单(Linux/macOS)
快速上手
1. 添加 Maven 依赖
<dependency>
<groupId>io.mybatis.jink</groupId>
<artifactId>jink</artifactId>
<version>0.5.0</version>
</dependency>
2. 写第一个组件
public class Hello {
public static void main(String[] args) {
Ink.renderOnce(
Box.of(
Text.of("Hello, World!")
.color(Color.GREEN).bold()
).borderStyle(BorderStyle.ROUND).paddingX(1),
40, 3
);
}
}
3. 或者用交互式渲染
Ink.render(new MyComponent()).waitUntilExit();
项目信息
- GitHub:abel533/jink
- License:Apache 2.0
- Java 版本:Java 8+
- 测试:146 个单元测试
- 当前版本:0.5.0(稳定发布)
如果你正在用 Java 构建 CLI 工具、DevOps 脚本、监控面板,或者只是想让命令行输出酷起来,不妨试试 jink。
项目还在持续迭代,欢迎提 Issue 和 PR。
1392

被折叠的 条评论
为什么被折叠?



