@claude-code-kit/ink-renderer 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/index.js +10 -8
- package/dist/index.mjs +10 -8
- package/package.json +8 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Minnzen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @claude-code-kit/ink-renderer
|
|
2
|
+
|
|
3
|
+
Terminal rendering engine for claude-code-kit — React reconciler, Yoga Flexbox layout, keyboard/mouse events, ANSI output.
|
|
4
|
+
|
|
5
|
+
Part of [claude-code-kit](https://github.com/Minnzen/claude-code-kit).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @claude-code-kit/ink-renderer react
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import React from 'react'
|
|
17
|
+
import { render, Box, Text } from '@claude-code-kit/ink-renderer'
|
|
18
|
+
|
|
19
|
+
function App() {
|
|
20
|
+
return (
|
|
21
|
+
<Box flexDirection="column" padding={1}>
|
|
22
|
+
<Text bold color="green">Hello from claude-code-kit</Text>
|
|
23
|
+
<Text>Build terminal UIs like React apps.</Text>
|
|
24
|
+
</Box>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await render(<App />)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Included
|
|
32
|
+
|
|
33
|
+
- Rendering API: `render`, `renderSync`, `createRoot`
|
|
34
|
+
- Primitives: `Box`, `Text`, `Spacer`, `Newline`, `Link`, `Button`, `ScrollBox`
|
|
35
|
+
- Hooks: `useInput`, `useApp`, `useStdin`, `useInterval`, `useAnimationFrame`
|
|
36
|
+
- Terminal helpers: `AlternateScreen`, `RawAnsi`, `Ansi`, `ErrorOverview`
|
|
37
|
+
|
|
38
|
+
## Docs
|
|
39
|
+
|
|
40
|
+
- Full project docs: [github.com/Minnzen/claude-code-kit](https://github.com/Minnzen/claude-code-kit)
|
|
41
|
+
- Components overview: [docs/components.md](https://github.com/Minnzen/claude-code-kit/blob/main/docs/components.md)
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -2813,7 +2813,7 @@ var diff = (before, after) => {
|
|
|
2813
2813
|
const changed = {};
|
|
2814
2814
|
let isChanged = false;
|
|
2815
2815
|
for (const key of Object.keys(before)) {
|
|
2816
|
-
const isDeleted = after ? !Object.
|
|
2816
|
+
const isDeleted = after ? !Object.prototype.hasOwnProperty.call(after, key) : true;
|
|
2817
2817
|
if (isDeleted) {
|
|
2818
2818
|
changed[key] = void 0;
|
|
2819
2819
|
isChanged = true;
|
|
@@ -4142,7 +4142,7 @@ function findPlainTextUrlAt(screen, col, row) {
|
|
|
4142
4142
|
let url = token.slice(urlStart, urlEnd);
|
|
4143
4143
|
const OPENER = { ")": "(", "]": "[", "}": "{" };
|
|
4144
4144
|
while (url.length > 0) {
|
|
4145
|
-
const last = url.
|
|
4145
|
+
const last = url.charAt(url.length - 1);
|
|
4146
4146
|
if (".,;:!?".includes(last)) {
|
|
4147
4147
|
url = url.slice(0, -1);
|
|
4148
4148
|
continue;
|
|
@@ -4506,7 +4506,7 @@ function osc(...parts) {
|
|
|
4506
4506
|
}
|
|
4507
4507
|
function wrapForMultiplexer(sequence) {
|
|
4508
4508
|
if (process.env["TMUX"]) {
|
|
4509
|
-
const escaped = sequence.
|
|
4509
|
+
const escaped = sequence.split("\x1B").join("\x1B\x1B");
|
|
4510
4510
|
return `\x1BPtmux;${escaped}\x1B\\`;
|
|
4511
4511
|
}
|
|
4512
4512
|
if (process.env["STY"]) {
|
|
@@ -4515,7 +4515,7 @@ function wrapForMultiplexer(sequence) {
|
|
|
4515
4515
|
return sequence;
|
|
4516
4516
|
}
|
|
4517
4517
|
function tmuxPassthrough(payload) {
|
|
4518
|
-
return `${ESC}Ptmux;${payload.
|
|
4518
|
+
return `${ESC}Ptmux;${payload.split(ESC).join(ESC + ESC)}${ST}`;
|
|
4519
4519
|
}
|
|
4520
4520
|
async function tmuxLoadBuffer(text) {
|
|
4521
4521
|
if (!process.env["TMUX"]) return false;
|
|
@@ -4755,11 +4755,12 @@ function supportsTabStatus() {
|
|
|
4755
4755
|
function tabStatus(fields) {
|
|
4756
4756
|
const parts = [];
|
|
4757
4757
|
const rgb2 = (c) => c.type === "rgb" ? `#${[c.r, c.g, c.b].map((n) => n.toString(16).padStart(2, "0")).join("")}` : "";
|
|
4758
|
+
const escapeStatusValue = (value) => value.split("\\").join("\\\\").split(";").join("\\;");
|
|
4758
4759
|
if ("indicator" in fields)
|
|
4759
4760
|
parts.push(`indicator=${fields.indicator ? rgb2(fields.indicator) : ""}`);
|
|
4760
4761
|
if ("status" in fields)
|
|
4761
4762
|
parts.push(
|
|
4762
|
-
`status=${fields.status
|
|
4763
|
+
`status=${fields.status ? escapeStatusValue(fields.status) : ""}`
|
|
4763
4764
|
);
|
|
4764
4765
|
if ("statusColor" in fields)
|
|
4765
4766
|
parts.push(
|
|
@@ -6797,12 +6798,13 @@ var Output = class {
|
|
|
6797
6798
|
if (operation.fromAbsolute) absoluteClears.push(rect);
|
|
6798
6799
|
}
|
|
6799
6800
|
const clips = [];
|
|
6801
|
+
const getCurrentClip = () => clips[clips.length - 1];
|
|
6800
6802
|
for (const operation of this.operations) {
|
|
6801
6803
|
switch (operation.type) {
|
|
6802
6804
|
case "clear":
|
|
6803
6805
|
continue;
|
|
6804
6806
|
case "clip":
|
|
6805
|
-
clips.push(intersectClip(
|
|
6807
|
+
clips.push(intersectClip(getCurrentClip(), operation.clip));
|
|
6806
6808
|
continue;
|
|
6807
6809
|
case "unclip":
|
|
6808
6810
|
clips.pop();
|
|
@@ -6815,7 +6817,7 @@ var Output = class {
|
|
|
6815
6817
|
width: regionWidth,
|
|
6816
6818
|
height: regionHeight
|
|
6817
6819
|
} = operation;
|
|
6818
|
-
const clip =
|
|
6820
|
+
const clip = getCurrentClip();
|
|
6819
6821
|
const startX = Math.max(regionX, clip?.x1 ?? 0);
|
|
6820
6822
|
const startY = Math.max(regionY, clip?.y1 ?? 0);
|
|
6821
6823
|
const maxY = Math.min(
|
|
@@ -6861,7 +6863,7 @@ var Output = class {
|
|
|
6861
6863
|
let lines = text.split("\n");
|
|
6862
6864
|
let swFrom = 0;
|
|
6863
6865
|
let prevContentEnd = 0;
|
|
6864
|
-
const clip =
|
|
6866
|
+
const clip = getCurrentClip();
|
|
6865
6867
|
if (clip) {
|
|
6866
6868
|
const clipHorizontally = typeof clip?.x1 === "number" && typeof clip?.x2 === "number";
|
|
6867
6869
|
const clipVertically = typeof clip?.y1 === "number" && typeof clip?.y2 === "number";
|
package/dist/index.mjs
CHANGED
|
@@ -2755,7 +2755,7 @@ var diff = (before, after) => {
|
|
|
2755
2755
|
const changed = {};
|
|
2756
2756
|
let isChanged = false;
|
|
2757
2757
|
for (const key of Object.keys(before)) {
|
|
2758
|
-
const isDeleted = after ? !Object.
|
|
2758
|
+
const isDeleted = after ? !Object.prototype.hasOwnProperty.call(after, key) : true;
|
|
2759
2759
|
if (isDeleted) {
|
|
2760
2760
|
changed[key] = void 0;
|
|
2761
2761
|
isChanged = true;
|
|
@@ -4087,7 +4087,7 @@ function findPlainTextUrlAt(screen, col, row) {
|
|
|
4087
4087
|
let url = token.slice(urlStart, urlEnd);
|
|
4088
4088
|
const OPENER = { ")": "(", "]": "[", "}": "{" };
|
|
4089
4089
|
while (url.length > 0) {
|
|
4090
|
-
const last = url.
|
|
4090
|
+
const last = url.charAt(url.length - 1);
|
|
4091
4091
|
if (".,;:!?".includes(last)) {
|
|
4092
4092
|
url = url.slice(0, -1);
|
|
4093
4093
|
continue;
|
|
@@ -4451,7 +4451,7 @@ function osc(...parts) {
|
|
|
4451
4451
|
}
|
|
4452
4452
|
function wrapForMultiplexer(sequence) {
|
|
4453
4453
|
if (process.env["TMUX"]) {
|
|
4454
|
-
const escaped = sequence.
|
|
4454
|
+
const escaped = sequence.split("\x1B").join("\x1B\x1B");
|
|
4455
4455
|
return `\x1BPtmux;${escaped}\x1B\\`;
|
|
4456
4456
|
}
|
|
4457
4457
|
if (process.env["STY"]) {
|
|
@@ -4460,7 +4460,7 @@ function wrapForMultiplexer(sequence) {
|
|
|
4460
4460
|
return sequence;
|
|
4461
4461
|
}
|
|
4462
4462
|
function tmuxPassthrough(payload) {
|
|
4463
|
-
return `${ESC}Ptmux;${payload.
|
|
4463
|
+
return `${ESC}Ptmux;${payload.split(ESC).join(ESC + ESC)}${ST}`;
|
|
4464
4464
|
}
|
|
4465
4465
|
async function tmuxLoadBuffer(text) {
|
|
4466
4466
|
if (!process.env["TMUX"]) return false;
|
|
@@ -4700,11 +4700,12 @@ function supportsTabStatus() {
|
|
|
4700
4700
|
function tabStatus(fields) {
|
|
4701
4701
|
const parts = [];
|
|
4702
4702
|
const rgb2 = (c) => c.type === "rgb" ? `#${[c.r, c.g, c.b].map((n) => n.toString(16).padStart(2, "0")).join("")}` : "";
|
|
4703
|
+
const escapeStatusValue = (value) => value.split("\\").join("\\\\").split(";").join("\\;");
|
|
4703
4704
|
if ("indicator" in fields)
|
|
4704
4705
|
parts.push(`indicator=${fields.indicator ? rgb2(fields.indicator) : ""}`);
|
|
4705
4706
|
if ("status" in fields)
|
|
4706
4707
|
parts.push(
|
|
4707
|
-
`status=${fields.status
|
|
4708
|
+
`status=${fields.status ? escapeStatusValue(fields.status) : ""}`
|
|
4708
4709
|
);
|
|
4709
4710
|
if ("statusColor" in fields)
|
|
4710
4711
|
parts.push(
|
|
@@ -6748,12 +6749,13 @@ var Output = class {
|
|
|
6748
6749
|
if (operation.fromAbsolute) absoluteClears.push(rect);
|
|
6749
6750
|
}
|
|
6750
6751
|
const clips = [];
|
|
6752
|
+
const getCurrentClip = () => clips[clips.length - 1];
|
|
6751
6753
|
for (const operation of this.operations) {
|
|
6752
6754
|
switch (operation.type) {
|
|
6753
6755
|
case "clear":
|
|
6754
6756
|
continue;
|
|
6755
6757
|
case "clip":
|
|
6756
|
-
clips.push(intersectClip(
|
|
6758
|
+
clips.push(intersectClip(getCurrentClip(), operation.clip));
|
|
6757
6759
|
continue;
|
|
6758
6760
|
case "unclip":
|
|
6759
6761
|
clips.pop();
|
|
@@ -6766,7 +6768,7 @@ var Output = class {
|
|
|
6766
6768
|
width: regionWidth,
|
|
6767
6769
|
height: regionHeight
|
|
6768
6770
|
} = operation;
|
|
6769
|
-
const clip =
|
|
6771
|
+
const clip = getCurrentClip();
|
|
6770
6772
|
const startX = Math.max(regionX, clip?.x1 ?? 0);
|
|
6771
6773
|
const startY = Math.max(regionY, clip?.y1 ?? 0);
|
|
6772
6774
|
const maxY = Math.min(
|
|
@@ -6812,7 +6814,7 @@ var Output = class {
|
|
|
6812
6814
|
let lines = text.split("\n");
|
|
6813
6815
|
let swFrom = 0;
|
|
6814
6816
|
let prevContentEnd = 0;
|
|
6815
|
-
const clip =
|
|
6817
|
+
const clip = getCurrentClip();
|
|
6816
6818
|
if (clip) {
|
|
6817
6819
|
const clipHorizontally = typeof clip?.x1 === "number" && typeof clip?.x2 === "number";
|
|
6818
6820
|
const clipVertically = typeof clip?.y1 === "number" && typeof clip?.y2 === "number";
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-code-kit/ink-renderer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Terminal rendering engine extracted from Claude Code — React reconciler + Yoga layout + TTY output",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.mjs",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
11
12
|
],
|
|
12
13
|
"exports": {
|
|
13
14
|
".": {
|
|
@@ -35,13 +36,14 @@
|
|
|
35
36
|
"type-fest": "*",
|
|
36
37
|
"usehooks-ts": "*",
|
|
37
38
|
"wrap-ansi": "*",
|
|
38
|
-
"@claude-code-kit/shared": "0.1.
|
|
39
|
+
"@claude-code-kit/shared": "0.1.1"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
42
|
"react": ">=18.0.0",
|
|
42
43
|
"react-reconciler": ">=0.29.0"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
46
|
+
"@types/react-reconciler": "0.33.0",
|
|
45
47
|
"react": "*",
|
|
46
48
|
"react-reconciler": "*",
|
|
47
49
|
"tsup": "*",
|
|
@@ -70,6 +72,8 @@
|
|
|
70
72
|
"access": "public"
|
|
71
73
|
},
|
|
72
74
|
"scripts": {
|
|
73
|
-
"build": "tsup"
|
|
75
|
+
"build": "tsup",
|
|
76
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
77
|
+
"lint": "pnpm run typecheck"
|
|
74
78
|
}
|
|
75
79
|
}
|