@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 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.hasOwn(after, key) : true;
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.at(-1);
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.replaceAll("\x1B", "\x1B\x1B");
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.replaceAll(ESC, ESC + ESC)}${ST}`;
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?.replaceAll("\\", "\\\\").replaceAll(";", "\\;") ?? ""}`
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(clips.at(-1), operation.clip));
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 = clips.at(-1);
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 = clips.at(-1);
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.hasOwn(after, key) : true;
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.at(-1);
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.replaceAll("\x1B", "\x1B\x1B");
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.replaceAll(ESC, ESC + ESC)}${ST}`;
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?.replaceAll("\\", "\\\\").replaceAll(";", "\\;") ?? ""}`
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(clips.at(-1), operation.clip));
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 = clips.at(-1);
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 = clips.at(-1);
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.0",
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.0"
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
  }