{"_id":"@cyia/ngx-ink","_rev":"3-12d57d05917464f81246abd8239c37a4","name":"@cyia/ngx-ink","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.0":{"name":"@cyia/ngx-ink","version":"1.0.0","author":{"name":"wszgrcy","email":"wszgrcy@gmail.com"},"_id":"@cyia/ngx-ink@1.0.0","maintainers":[{"name":"wszgrcy","email":"wszgrcy@gmail.com"}],"homepage":"https://github.com/wszgrcy/ngx-ink#readme","bugs":{"url":"https://github.com/wszgrcy/ngx-ink/issues"},"dist":{"shasum":"37df893aa1f7f55ae8c2405b9482dac7046838cf","tarball":"https://registry.npmjs.org/@cyia/ngx-ink/-/ngx-ink-1.0.0.tgz","fileCount":6,"integrity":"sha512-3vW6o9VogUHJnadIfxpg+XBkLG0GUw2a59gf1MFxqmekx0RZcasVD1Xjb483SToz8rfVPO5WFB5YSouhF9MgFQ==","signatures":[{"sig":"MEQCIHuMWST0d0u62XABwJ7auUB6sJTC8W/3Ch5CBxJFOVFOAiACVrxXMryLwiQ44FgQ+BFZrou01FUFX31PE0y+8JEx/A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":782265},"type":"module","module":"fesm2022/cyia-ngx-ink.mjs","exports":{".":{"types":"./types/cyia-ngx-ink.d.ts","default":"./fesm2022/cyia-ngx-ink.mjs"},"./package.json":{"default":"./package.json"}},"gitHead":"99b9945cd2ab11dcf19a279f5158a64b9eee3925","typings":"types/cyia-ngx-ink.d.ts","_npmUser":{"name":"wszgrcy","email":"wszgrcy@gmail.com"},"repository":{"url":"git+https://github.com/wszgrcy/ngx-ink.git"},"_npmVersion":"11.12.1","description":"Ink for React — An Angular Implementation / Ink（React 终端 UI 框架）的 Angular 实现","directories":{},"sideEffects":false,"_nodeVersion":"24.15.0","dependencies":{"rxjs":"~7.8.0","chalk":"^5.6.2","tslib":"^2.3.0","is-in-ci":"^2.0.0","auto-bind":"^5.0.1","cli-boxes":"^4.0.1","type-fest":"^5.7.0","wrap-ansi":"^10.0.0","cli-cursor":"^5.0.0","es-toolkit":"^1.47.1","slice-ansi":"^9.0.0","signal-exit":"^3.0.7","stack-utils":"^2.0.6","widest-line":"^6.0.0","yoga-layout":"^3.2.1","ansi-escapes":"^7.3.0","cli-truncate":"^6.0.0","code-excerpt":"^4.0.0","string-width":"^8.2.1","indent-string":"^5.0.0","patch-console":"^2.0.0","terminal-size":"^4.0.1","@alcalzone/ansi-tokenize":"^0.3.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"peerDependencies":{"@angular/core":"^22.0.0","@angular/compiler":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/ngx-ink_1.0.0_1782220950931_0.1437498789003886","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@cyia/ngx-ink","version":"1.0.1","author":{"name":"wszgrcy","email":"wszgrcy@gmail.com"},"_id":"@cyia/ngx-ink@1.0.1","maintainers":[{"name":"wszgrcy","email":"wszgrcy@gmail.com"}],"homepage":"https://github.com/wszgrcy/ngx-ink#readme","bugs":{"url":"https://github.com/wszgrcy/ngx-ink/issues"},"dist":{"shasum":"3f9187dd35f5c4b5a1e150ffbe9ad368e075aa04","tarball":"https://registry.npmjs.org/@cyia/ngx-ink/-/ngx-ink-1.0.1.tgz","fileCount":6,"integrity":"sha512-t2Jtg0PgOUOSIAJjUnBsf9kfIaMUVmqa5BVfzFdijUVN4kKM+N3JpRc//XCWpoADiNVJ7f1+a3YM6ZQiKtqIeA==","signatures":[{"sig":"MEUCIBaveudGmW4/xiP8AuQH5LAjQ4bNtss+tvwVTq2+lORHAiEAzF/IbaYPpBQS3Jcr+M8Q/DIWP4HBTg+WGUQwbB9PsCg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cyia%2fngx-ink@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":781097},"type":"module","module":"fesm2022/cyia-ngx-ink.mjs","exports":{".":{"types":"./types/cyia-ngx-ink.d.ts","default":"./fesm2022/cyia-ngx-ink.mjs"},"./package.json":{"default":"./package.json"}},"gitHead":"38c938c5ab1eb2a5774d00e8e209c2d2a6da0184","typings":"types/cyia-ngx-ink.d.ts","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:435b4eb2-4e2e-4e98-bee4-d9428df37db3"}},"repository":{"url":"git+https://github.com/wszgrcy/ngx-ink.git"},"_npmVersion":"11.13.0","description":"Ink for React — An Angular Implementation / Ink（React 终端 UI 框架）的 Angular 实现","directories":{},"sideEffects":false,"_nodeVersion":"24.16.0","dependencies":{"rxjs":"~7.8.0","chalk":"^5.6.2","tslib":"^2.3.0","is-in-ci":"^2.0.0","auto-bind":"^5.0.1","cli-boxes":"^4.0.1","type-fest":"^5.7.0","wrap-ansi":"^10.0.0","cli-cursor":"^5.0.0","es-toolkit":"^1.47.1","slice-ansi":"^9.0.0","signal-exit":"^3.0.7","stack-utils":"^2.0.6","widest-line":"^6.0.0","yoga-layout":"^3.2.1","ansi-escapes":"^7.3.0","cli-truncate":"^6.0.0","code-excerpt":"^4.0.0","string-width":"^8.2.1","indent-string":"^5.0.0","patch-console":"^2.0.0","terminal-size":"^4.0.1","@alcalzone/ansi-tokenize":"^0.3.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"peerDependencies":{"@angular/core":"^22.0.0","@angular/compiler":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/ngx-ink_1.0.1_1782224943430_0.8746457318573992","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@cyia/ngx-ink","version":"1.0.2","description":"Ink for React — An Angular Implementation / Ink（React 终端 UI 框架）的 Angular 实现","keywords":["ink","angular","cli","stdout","components","command-line","print","render","colors","text"],"type":"module","sideEffects":false,"repository":{"url":"git+https://github.com/wszgrcy/ngx-ink.git"},"author":{"name":"wszgrcy","email":"wszgrcy@gmail.com"},"peerDependencies":{"@angular/compiler":"^22.0.0","@angular/core":"^22.0.0"},"dependencies":{"@alcalzone/ansi-tokenize":"^0.3.0","cli-truncate":"^6.0.0","tslib":"^2.3.0","ansi-escapes":"^7.3.0","auto-bind":"^5.0.1","chalk":"^5.6.2","cli-boxes":"^4.0.1","cli-cursor":"^5.0.0","code-excerpt":"^4.0.0","es-toolkit":"^1.47.1","indent-string":"^5.0.0","is-in-ci":"^2.0.0","patch-console":"^2.0.0","rxjs":"~7.8.0","signal-exit":"^3.0.7","slice-ansi":"^9.0.0","stack-utils":"^2.0.6","string-width":"^8.2.1","terminal-size":"^4.0.1","type-fest":"^5.7.0","widest-line":"^6.0.0","wrap-ansi":"^10.0.0","yoga-layout":"^3.2.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"module":"fesm2022/cyia-ngx-ink.mjs","typings":"types/cyia-ngx-ink.d.ts","exports":{"./package.json":{"default":"./package.json"},".":{"types":"./types/cyia-ngx-ink.d.ts","default":"./fesm2022/cyia-ngx-ink.mjs"}},"gitHead":"47ec77b7a06b3ff6d6597cf433a06164b0b66347","_id":"@cyia/ngx-ink@1.0.2","bugs":{"url":"https://github.com/wszgrcy/ngx-ink/issues"},"homepage":"https://github.com/wszgrcy/ngx-ink#readme","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-HTNyQQhe6yFIQnW1S0g1yoJQ25KRFHBJ80GzulVCNygmkXQnYYzPop3DI/E71q7/tLlw+IOSrp+PWL7xGGVZfQ==","shasum":"0cc49f60df585cfb38bd38ce1810b0968deb7002","tarball":"https://registry.npmjs.org/@cyia/ngx-ink/-/ngx-ink-1.0.2.tgz","fileCount":6,"unpackedSize":788845,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cyia%2fngx-ink@1.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCMLOBXL7t/SxM3pknAF6KcaPyoOBgilBUYO6gzYdt2gwIhAPo5Kr6C4z+SiL0Y7GbSeZuImykmCPf2tsFv3ey1arLZ"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:435b4eb2-4e2e-4e98-bee4-d9428df37db3"}},"directories":{},"maintainers":[{"name":"wszgrcy","email":"wszgrcy@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ngx-ink_1.0.2_1782229329714_0.7087516332542321"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-23T13:22:30.745Z","modified":"2026-06-23T15:42:10.163Z","1.0.0":"2026-06-23T13:22:31.096Z","1.0.1":"2026-06-23T14:29:03.646Z","1.0.2":"2026-06-23T15:42:09.863Z"},"bugs":{"url":"https://github.com/wszgrcy/ngx-ink/issues"},"author":{"name":"wszgrcy","email":"wszgrcy@gmail.com"},"homepage":"https://github.com/wszgrcy/ngx-ink#readme","repository":{"url":"git+https://github.com/wszgrcy/ngx-ink.git"},"description":"Ink for React — An Angular Implementation / Ink（React 终端 UI 框架）的 Angular 实现","maintainers":[{"name":"wszgrcy","email":"wszgrcy@gmail.com"}],"readme":"# ngx-ink\n\n> **语言**: [English](./README.md) | 中文\n\nAngular 终端 UI 框架。基于 [Ink](https://github.com/vadimdemedes/ink) 实现，使用 Angular 组件在终端中构建 CLI 应用的用户界面。\n\n> 当前同步到7.1.0\n\n## 快速启动模板\n\n最快的上手方式是使用 [ngx-ink-starter](https://github.com/wszgrcy/ngx-ink-starter) 模板。在 GitHub 上点击 **Use this template** 或直接克隆：\n\n```bash\ngit clone https://github.com/wszgrcy/ngx-ink-starter.git\ncd ngx-ink-starter\nnpm install\nnpm start\n```\n\n无需任何构建配置，即可直接运行终端应用。\n\n## 安装\n\n```bash\nnpm install @cyia/ngx-ink\n```\n\n需要 `@angular/core ^22.0.0` 作为 peer dependency。\n\n> **前置依赖**：需要安装 `@types/node`，因为 `process.stdout/stdin/stderr` 在 TypeScript 编译时需要类型定义。\n\n```bash\nnpm install @types/node --save-dev\n```\n\n## 快速开始\n\n### 集成配置\n\n在使用 ngx-ink 之前，需要完成以下构建配置。\n\n#### 1. 安装 patch-package\n\n本项目需要修改 `@angular/build` 的行为以支持 ES Module 输出：\n\n```bash\nnpm install patch-package --save-dev\n```\n\n在 `package.json` 中添加 postinstall 脚本：\n\n```json\n{\n    \"scripts\": {\n        \"postinstall\": \"patch-package\"\n    }\n}\n```\n\n#### 2. 创建补丁文件\n\n创建 `patches/@angular+build+22.0.1.patch`：\n\n```diff\ndiff --git a/node_modules/@angular/build/src/builders/application/index.js b/node_modules/@angular/build/src/builders/application/index.js\nindex 6bee086..0a54ee8 100644\n--- a/node_modules/@angular/build/src/builders/application/index.js\n+++ b/node_modules/@angular/build/src/builders/application/index.js\n@@ -64,6 +64,13 @@ const results_1 = require(\"./results\");\n async function* buildApplicationInternal(options,\n // TODO: Integrate abort signal support into builder system\n context, extensions) {\n+    const path1 = require('path')\n+    const filePath=path1.join(context.workspaceRoot, 'script/patch/codePlugins.js')\n+    console.log('codePlugins filePath:',filePath)\n+    const codePlugins = require(filePath)\n+    extensions??={}\n+    extensions.codePlugins??=[]\n+    extensions.codePlugins.push(...('default' in  codePlugins?codePlugins.default:codePlugins))\n     const { workspaceRoot, logger, target } = context;\n     // Check Angular version.\n     (0, version_1.assertCompatibleAngularVersion)(workspaceRoot);\n```\n\n#### 3. 创建构建脚本\n\n创建 `script/patch/` 目录，添加以下两个文件：\n\n**`script/patch/codePlugins.js`** — esbuild 插件配置：\n\n> 注意：`fast-glob` 仅在 `test` 模式下使用（用于构建测试入口）。如果不需要运行内部测试，可以移除 `fg` 相关的 `require` 和代码分支。\n\n```javascript\nlet path = require('path');\n/** @type {import('esbuild').Plugin} */\nconst defaultPlugin = {\n    name: 'raw-code',\n    setup(build) {\n        build.initialOptions.platform = 'node';\n        build.initialOptions.outExtension ??= {\n            ...build.initialOptions.outExtension,\n            '.js': '.mjs',\n        };\n        build.initialOptions.inject = [path.join(__dirname, './cjs-shim.js')];\n        if (build.initialOptions.sourcemap) {\n            build.initialOptions.sourcemap = 'linked';\n            build.initialOptions.sourceRoot = path.join(build.initialOptions.absWorkingDir, '/');\n        }\n    },\n};\nexports.default = [defaultPlugin];\n```\n\n**`script/patch/cjs-shim.js`** — CommonJS 兼容性 shim：\n\n```javascript\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport url from 'node:url';\n\nglobalThis.require = createRequire(import.meta.url);\nglobalThis.__filename = url.fileURLToPath(import.meta.url);\nglobalThis.__dirname = path.dirname(__filename);\n```\n\n> `codePlugins.js` 中的 `if (process.argv.includes('test'))` 分支仅用于项目内部测试，用户集成时不需要。\n\n#### 4. 配置 angular.json\n\n```json\n{\n    \"projects\": {\n        \"your-app\": {\n            \"architect\": {\n                \"build\": {\n                    \"builder\": \"@angular/build:application\",\n                    \"options\": {\n                        \"index\": false,\n                        \"browser\": \"src/main.ts\",\n                        \"tsConfig\": \"tsconfig.app.json\",\n                        \"assets\": [\n                            {\n                                \"glob\": \"**/*\",\n                                \"input\": \"public\"\n                            }\n                        ],\n                        \"styles\": [],\n                        \"outputHashing\": \"none\",\n                        \"outputPath\": {\n                            \"browser\": \"\",\n                            \"base\": \"./dist/your-app\"\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n```\n\n| 配置项               | 值                  | 说明             |\n| -------------------- | ------------------- | ---------------- |\n| `index`              | `false`             | 不需要 HTML 文件 |\n| `styles`             | `[]`                | 不需要 CSS 样式  |\n| `index.html`         | —                   | 如果存在请删除 `index.html`，CLI 应用不需要 |\n| `outputHashing`      | `\"none\"`            | 禁用文件名哈希   |\n| `outputPath.browser` | `\"\"`                | 输出到当前目录   |\n| `outputPath.base`    | `\"./dist/your-app\"` | 基础输出路径     |\n\n#### 5. 构建和运行\n\n```bash\nnpm run build\nnode ./dist/your-app/main.mjs\n```\n\n### 渲染应用\n\nngx-ink 使用标准的 Angular 应用启动方式，与常规 Angular Web 项目无异。通过 `bootstrapApplication` 启动应用，并使用 `InkOptionToken` 配置终端选项。\n\n```typescript\n// main.ts — 应用入口\nimport { App } from './app/app';\nimport { bootstrapApplication, InkOption, InkOptionToken } from '@cyia/ngx-ink';\n\nbootstrapApplication(App, {\n    providers: [\n        {\n            provide: InkOptionToken,\n            useValue: {\n                stdout: process.stdout,\n                stdin: process.stdin,\n                stderr: process.stderr,\n                debug: false,\n                exitOnCtrlC: true,\n                patchConsole: true,\n                maxFps: 30,\n                incrementalRendering: false,\n                concurrent: false,\n                alternateScreen: true,\n            } as InkOption,\n        },\n    ],\n});\n```\n\n```typescript\n// app.ts — 根组件\nimport { Component } from '@angular/core';\nimport { BoxComponent, TextComponent } from '@cyia/ngx-ink';\n\n@Component({\n    selector: 'app-root',\n    standalone: true,\n    imports: [BoxComponent, TextComponent],\n    template: `\n        <box>\n            <text [color]=\"'green'\">Hello, ngx-ink!</text>\n        </box>\n    `,\n})\nexport class AppComponent {}\n```\n\n### 使用内置组件\n\n#### Box — 弹性盒子布局\n\n```typescript\nimport { Component } from '@angular/core';\nimport { BoxComponent, TextComponent } from '@cyia/ngx-ink';\n\n@Component({\n    selector: 'my-app',\n    standalone: true,\n    imports: [BoxComponent, TextComponent],\n    template: `\n        <box [style]=\"{ flexDirection: 'column', padding: 1 }\">\n            <text [color]=\"'cyan'\">Title</text>\n            <text>Content</text>\n        </box>\n    `,\n})\nexport class MyAppComponent {}\n```\n\n`<box>` 类似于浏览器中的 `<div style=\"display: flex\">`。**所有样式通过单个 `style` 输入绑定**，接受一个包含样式属性的对象：\n\n```html\n<box [style]=\"{ flexDirection: 'column', padding: 1, gap: 2 }\"></box>\n```\n\n支持的样式属性（对应 `Styles` 类型）：\n\n| 类别      | 属性                                                                                                    |\n| --------- | ------------------------------------------------------------------------------------------------------- |\n| 布局方向  | `flexDirection`: `'row' \\| 'column' \\| 'row-reverse' \\| 'column-reverse'`                               |\n| Flex 属性 | `flexGrow`, `flexShrink`, `flexBasis`, `flexWrap`                                                       |\n| 对齐      | `alignItems`, `alignSelf`, `alignContent`, `justifyContent`                                             |\n| 尺寸      | `width`, `height`, `minWidth`, `minHeight`, `maxWidth`, `maxHeight`（数字或 `'100%'` 等字符串）         |\n| 边距      | `margin`, `marginTop`, `marginBottom`, `marginLeft`, `marginRight`, `marginX`, `marginY`                |\n| 内边距    | `padding`, `paddingTop`, `paddingBottom`, `paddingLeft`, `paddingRight`, `paddingX`, `paddingY`         |\n| 定位      | `position`: `'static' \\| 'relative' \\| 'absolute'`，以及 `top`, `right`, `bottom`, `left`               |\n| 溢出      | `overflow`: `'visible' \\| 'hidden'`，以及 `overflowX`, `overflowY`                                      |\n| 文本换行  | `textWrap`: `'wrap' \\| 'hard' \\| 'truncate' \\| 'truncate-end' \\| 'truncate-middle' \\| 'truncate-start'` |\n| 间距      | `gap`, `rowGap`, `columnGap`                                                                            |\n| 边框      | `borderStyle`, `borderTop`, `borderBottom`, `borderLeft`, `borderRight`, `borderColor` 等               |\n| ARIA      | `aria-label`, `aria-hidden`, `aria-role`, `aria-state`                                                  |\n\n#### Text — 文本显示\n\n```typescript\nimport { Component } from '@angular/core';\nimport { BoxComponent, TextComponent } from '@cyia/ngx-ink';\n\n@Component({\n    selector: 'my-app',\n    standalone: true,\n    imports: [BoxComponent, TextComponent],\n    template: `\n        <text\n            [color]=\"'green'\"\n            [backgroundColor]=\"'blue'\"\n            [bold]=\"true\"\n            [italic]=\"true\"\n            [underline]=\"true\"\n            [strikethrough]=\"true\"\n            [dimColor]=\"true\"\n            [inverse]=\"true\"\n            [wrap]=\"'wrap'\"\n            >Styled text</text\n        >\n    `,\n})\nexport class MyAppComponent {}\n```\n\n支持的颜色和样式属性（**所有属性均为 `input()` 类型，布尔值和字符串值需要使用 `[属性]=\"值\"` 绑定**）：\n\n| 属性              | 类型                                                                                        | 说明                                         |\n| ----------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------- |\n| `color`           | `string`                                                                                    | 前景色（使用 Chalk，支持所有 ANSI 颜色名称） |\n| `backgroundColor` | `string`                                                                                    | 背景色                                       |\n| `dimColor`        | `boolean`                                                                                   | 降低亮度（需 `[dimColor]=\"true\"`）           |\n| `bold`            | `boolean`                                                                                   | 粗体                                         |\n| `italic`          | `boolean`                                                                                   | 斜体                                         |\n| `underline`       | `boolean`                                                                                   | 下划线                                       |\n| `strikethrough`   | `boolean`                                                                                   | 删除线                                       |\n| `inverse`         | `boolean`                                                                                   | 反转前景/背景色                              |\n| `wrap`            | `'wrap' \\| 'hard' \\| 'truncate-end' \\| 'truncate' \\| 'truncate-middle' \\| 'truncate-start'` | 文本换行或截断策略，默认 `'wrap'`            |\n\n#### Newline — 换行\n\n```typescript\nimport { Component } from '@angular/core';\nimport { NewlineComponent, TextComponent } from '@cyia/ngx-ink';\n\n@Component({\n    selector: 'my-app',\n    standalone: true,\n    imports: [NewlineComponent, TextComponent],\n    template: `<text>Line 1<newline [count]=\"2\" />Line 2</text>`,\n})\nexport class MyAppComponent {}\n```\n\n#### Spacer — 弹性空白\n\n```typescript\nimport { Component } from '@angular/core';\nimport { BoxComponent, SpacerComponent, TextComponent } from '@cyia/ngx-ink';\n\n@Component({\n    selector: 'my-app',\n    standalone: true,\n    imports: [BoxComponent, SpacerComponent, TextComponent],\n    template: `\n        <box [style]=\"{ flexDirection: 'row' }\">\n            <text>Left</text>\n            <spacer></spacer>\n            <text>Right</text>\n        </box>\n    `,\n})\nexport class MyAppComponent {}\n```\n\n#### Static — 静态内容\n\n```typescript\nimport { Component } from '@angular/core';\nimport { StaticComponent, TextComponent } from '@cyia/ngx-ink';\n\n@Component({\n    selector: 'my-app',\n    standalone: true,\n    imports: [StaticComponent, TextComponent],\n    template: `\n        <static [items]=\"logs\" [ngTemplateOutlet]=\"logTemplate\"></static>\n        <text>Live progress: {{ progress }}%</text>\n\n        <ng-template #logTemplate let-log>\n            <text>{{ log }}</text>\n        </ng-template>\n    `,\n})\nexport class MyAppComponent {}\n```\n\n适用于不改变的输出，如日志、已完成的任务列表等。\n\n#### Transform — 文本变换\n\n```typescript\nimport { Component } from '@angular/core';\nimport { TextComponent, TransformComponent } from '@cyia/ngx-ink';\n\n@Component({\n    selector: 'my-app',\n    standalone: true,\n    imports: [TextComponent, TransformComponent],\n    template: `\n        <transform [transform]=\"transformFn\" accessibilityLabel=\"Transformed text\">\n            <text>Original</text>\n        </transform>\n    `,\n})\nexport class MyAppComponent {\n    transformFn = (children: string, index: number) => {\n        return children.toUpperCase();\n    };\n}\n```\n\n在输出到终端前转换子组件的字符串表示。\n\n### 使用 Hooks\n\n#### useInput — 键盘输入\n\n```typescript\nimport { Component } from '@angular/core';\nimport { useInput } from '@cyia/ngx-ink';\n\n@Component({\n    /* ... */\n})\nexport class InputComponent {\n    constructor() {\n        useInput((input, key) => {\n            if (key.return) {\n                // 回车键\n            }\n            if (key.upArrow) {\n                // 上箭头\n            }\n            if (input === 'q') {\n                // 按 Q 退出\n            }\n        });\n    }\n}\n```\n\n返回的 `key` 对象包含：`upArrow`, `downArrow`, `leftArrow`, `rightArrow`, `pageDown`, `pageUp`, `home`, `end`, `return`, `escape`, `ctrl`, `shift`, `tab`, `backspace`, `delete`, `alt`, `f1`-`f19`, `space`, `enter`, `slash`, `dot`, `comma`，以及按键的原始字符。\n\n#### useWindowSize — 窗口大小\n\n```typescript\nimport { Component } from '@angular/core';\nimport { useWindowSize } from '@cyia/ngx-ink';\n\n@Component({\n    /* ... */\n})\nexport class WindowSizeComponent {\n    size = useWindowSize();\n}\n```\n\n终端调整大小时自动更新。\n\n#### useAnimation — 动画\n\n```typescript\nimport { Component } from '@angular/core';\nimport { useAnimation } from '@cyia/ngx-ink';\n\n@Component({\n    /* ... */\n})\nexport class SpinnerComponent {\n    animation = useAnimation({ interval: 80 });\n\n    get spinnerChar() {\n        const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n        return chars[this.animation().frame % chars.length];\n    }\n}\n```\n\n返回：\n\n- `frame`: 每 interval 递增的计数器\n- `time`: 自开始以来的总毫秒数\n- `delta`: 上一帧以来的毫秒数\n- `reset()`: 重置所有值\n\n#### useFocus — 焦点管理\n\n```typescript\nimport { Component } from '@angular/core';\nimport { useFocus } from '@cyia/ngx-ink';\n\n@Component({\n    /* ... */\n})\nexport class FocusItemComponent {\n    focus = useFocus({\n        autoFocus: false,\n        id: 'item-1',\n    });\n}\n```\n\n使用 Tab 键在组件间切换焦点。\n\n#### useCursor — 光标控制\n\n```typescript\nimport { Component } from '@angular/core';\nimport { useCursor } from '@cyia/ngx-ink';\n\n@Component({\n    /* ... */\n})\nexport class CursorComponent {\n    constructor() {\n        const { setCursorPosition } = useCursor();\n    }\n}\n```\n\n#### useStdin / useStdout / useStderr — 流访问\n\n```typescript\nimport { useStdin, useStdout } from '@cyia/ngx-ink';\n\n@Component({\n    /* ... */\n})\nexport class StreamComponent {\n    stdin = useStdin();\n    stdout = useStdout();\n    stderr = useStderr();\n}\n```\n\n#### 其他 Hooks\n\n- `useApp` — 应用上下文\n- `useFocusManager` — 焦点管理器\n- `usePaste` — 粘贴事件监听\n- `useIsScreenReaderEnabled` — 检测屏幕阅读器\n- `useBoxMetrics` — 获取 Box 尺寸信息\n\n## Context Tokens\n\n可通过 Angular 的 `inject` 注入：\n\n- `APP_CONTEXT_TOKEN` — 应用上下文\n- `STDIN_CONTEXT_TOKEN` — stdin 上下文\n- `STDOUT_CONTEXT_TOKEN` — stdout 上下文\n- `STDERR_CONTEXT_TOKEN` — stderr 上下文\n- `FOCUS_CONTEXT_TOKEN` — 焦点上下文\n- `CURSOR_CONTEXT_TOKEN` — 光标上下文\n- `ANIMATION_CONTEXT_TOKEN` — 动画上下文\n- `ACCESSIBILITY_CONTEXT_TOKEN` — 无障碍上下文\n- `BACKGROUND_CONTEXT_TOKEN` — 背景色上下文\n\n## 导出列表\n\n### 核心\n\n| 导出                      | 说明                                |\n| ------------------------- | ----------------------------------- |\n| `render`                  | 渲染应用到终端，返回 `RenderResult` |\n| `TerminalRenderer`        | 终端渲染器                          |\n| `TerminalRendererFactory` | 渲染器工厂                          |\n\n### 组件\n\n| 组件                     | 说明               |\n| ------------------------ | ------------------ |\n| `BoxComponent`           | 弹性盒子布局容器   |\n| `TextComponent`          | 文本显示，支持样式 |\n| `NewlineComponent`       | 换行符             |\n| `SpacerComponent`        | 弹性空白           |\n| `StaticComponent`        | 静态内容渲染       |\n| `TransformComponent`     | 文本变换           |\n| `ErrorOverviewComponent` | 错误信息展示       |\n\n### Hooks\n\n| Hook                       | 返回类型                     | 说明           |\n| -------------------------- | ---------------------------- | -------------- |\n| `useInput`                 | `void`                       | 监听键盘输入   |\n| `useWindowSize`            | `Signal<WindowSize>`         | 终端窗口尺寸   |\n| `useAnimation`             | `Signal<AnimationResult>`    | 动画驱动       |\n| `useFocus`                 | `Signal<FocusResult>`        | 焦点状态和控制 |\n| `useFocusManager`          | `void`                       | 焦点管理器     |\n| `useCursor`                | `{ setCursorPosition }`      | 光标位置控制   |\n| `useStdin`                 | `Signal<StdinContextValue>`  | stdin 访问     |\n| `useStdinContext`          | `Signal<StdinContextValue>`  | stdin 上下文   |\n| `useStdout`                | `Signal<StdoutContextValue>` | stdout 访问    |\n| `useStderr`                | `Signal<StderrContextValue>` | stderr 访问    |\n| `useApp`                   | `Signal<AppContextValue>`    | 应用上下文     |\n| `usePaste`                 | `void`                       | 粘贴事件       |\n| `useIsScreenReaderEnabled` | `boolean`                    | 屏幕阅读器检测 |\n| `useBoxMetrics`            | `Signal<BoxMetrics>`         | Box 尺寸信息   |\n\n### 工具\n\n| 导出                           | 说明               |\n| ------------------------------ | ------------------ |\n| `colorize`                     | ANSI 颜色处理      |\n| `sanitizeAnsi`                 | 清理 ANSI 转义序列 |\n| `kittyFlags`, `kittyModifiers` | Kitty 键盘协议支持 |\n\n## 与 Ink 的差异\n\nngx-ink 复用 Ink 的核心逻辑（ANSI 处理、Yoga 布局计算、渲染管道），但适配为 Angular：\n\n- 使用 Angular 组件（`@Component`）而非 React 函数组件\n- 使用 `input()` / `output()` 替代 props\n- 使用 Angular Signals 进行状态管理\n- 标准的 Angular 应用启动方式（`bootstrapApplication`）\n\n## 项目结构\n\n```\nprojects/ngx-ink-lib/\n├── src/\n│   ├── lib/ngx-ink/\n│   │   ├── components/       # Box, Text, Newline, Spacer, Static, Transform, ErrorOverview\n│   │   ├── contexts/         # Angular 上下文 Token 定义\n│   │   ├── hooks/            # useInput, useWindowSize, useAnimation 等\n│   │   ├── service/          # AppContextService 等服务\n│   │   ├── render.ts         # 渲染入口\n│   │   ├── renderer2.ts      # 渲染器实现\n│   │   ├── application.ts    # Angular 应用启动\n│   │   ├── platform.ts       # Platform 提供器\n│   │   └── ...              # 核心工具函数\n│   └── public-api.ts        # 公共 API 导出\n└── package.json\n```\n","readmeFilename":"README.zh-hans.md","keywords":["ink","angular","cli","stdout","components","command-line","print","render","colors","text"]}