chore: add package and ci foundation
- add README and public API boundary documentation - configure package metadata and CI validation
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: restore build format test pack
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore ttui.slnx
|
||||
|
||||
- name: Build
|
||||
run: dotnet build ttui.slnx --configuration Release --no-restore
|
||||
|
||||
- name: Format
|
||||
run: dotnet format ttui.slnx --verify-no-changes --no-restore
|
||||
|
||||
- name: Test
|
||||
run: dotnet test ttui.slnx --configuration Release --no-build --logger "trx;LogFileName=test-results.trx"
|
||||
|
||||
- name: Pack
|
||||
run: dotnet pack src/TinyTUI/TinyTUI.csproj --configuration Release --no-build --output build/artifacts/packages
|
||||
+15
-6
@@ -1,7 +1,16 @@
|
||||
<Project >
|
||||
<ItemGroup>
|
||||
<!--
|
||||
todo: 编译与发布相关配置
|
||||
-->
|
||||
</ItemGroup>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ArtifactsPath>$(MSBuildThisFileDirectory)build\artifacts</ArtifactsPath>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsPackable)' == ''">
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# TinyTUI
|
||||
|
||||
TinyTUI 是一个面向 .NET 的轻量终端 UI 框架。当前版本已经具备组件树、终端会话、输入解析、keybinding、差分渲染、overlay、ANSI 感知文本工具、自动补全基础、终端图像 fallback 和 xUnit 回归测试基础。
|
||||
|
||||
它的设计目标是让应用代码只描述组件和状态变化,框架负责把组件输出合成为终端行数组,并通过渲染器同步到真实终端。
|
||||
|
||||
## 快速开始
|
||||
|
||||
当前仓库使用 .NET 10。
|
||||
|
||||
```powershell
|
||||
dotnet restore ttui.slnx
|
||||
dotnet run --project src/Example/Example.csproj
|
||||
```
|
||||
|
||||
最小应用结构如下:
|
||||
|
||||
```csharp
|
||||
using TinyTUI.Components;
|
||||
using TinyTUI.Input;
|
||||
using TinyTUI.Rendering;
|
||||
using TinyTUI.Runtime;
|
||||
using TinyTUI.Terminal;
|
||||
using TinyTUI.Text;
|
||||
|
||||
using var terminal = new ConsoleTerminalSession();
|
||||
var parser = new DefaultInputParser();
|
||||
var textMeasurer = new TerminalTextMeasurer();
|
||||
var renderer = new DifferentialRenderer(terminal, textMeasurer);
|
||||
using var runtime = new TuiRuntime(terminal, parser, renderer);
|
||||
|
||||
var input = new Input(textMeasurer) { Prompt = "> " };
|
||||
var done = new ManualResetEventSlim();
|
||||
|
||||
input.OnSubmitted = value =>
|
||||
{
|
||||
runtime.Add(new Text($"submitted: {value}"));
|
||||
input.Clear();
|
||||
};
|
||||
input.OnCanceled = done.Set;
|
||||
|
||||
runtime.Add(new Text("TinyTUI demo"));
|
||||
runtime.Add(input);
|
||||
runtime.SetFocus(input);
|
||||
|
||||
runtime.Start();
|
||||
done.Wait();
|
||||
runtime.Stop();
|
||||
```
|
||||
|
||||
## 核心 API
|
||||
|
||||
`ITerminalSession` 是终端生命周期入口,负责输入事件、输出、尺寸、bracketed paste、光标和终端状态恢复。真实程序通常使用 `ConsoleTerminalSession`,测试可以实现同一个接口。
|
||||
|
||||
`TuiRuntime` 管理组件树、焦点、overlay、输入分发和渲染请求。应用通过 `Add`、`Remove`、`SetFocus`、`ShowOverlay`、`HideOverlay` 和 `RequestRender` 驱动界面。
|
||||
|
||||
`IComponent` 是最小组件接口,`Render(int width)` 返回终端行数组。组件输出的每一行都应该控制在 `width` 内;如果需要截断、切片或换行,优先使用 `ITextMeasurer`。
|
||||
|
||||
`IInputComponent` 表示可接收输入的组件,`IFocusableComponent` 表示渲染时可能输出硬件光标 marker 的组件。焦点由 runtime 统一设置,组件只根据 `Focused` 决定是否输出 marker。
|
||||
|
||||
`DifferentialRenderer` 维护上一帧、视口和变化范围,默认使用 synchronized output、行尾 SGR reset 和 OSC 8 reset 降低闪烁和样式泄漏。`FullScreenRenderer` 适合需要全量刷新策略的场景。
|
||||
|
||||
## 组件和能力
|
||||
|
||||
内置组件包括 `Text`、`Markdown`、`Box`、`Spacer`、`TruncatedText`、`Input`、`Editor`、`SelectList`、`Loader`、`CancellableLoader` 和 `Image`。
|
||||
|
||||
Overlay 通过 `runtime.ShowOverlay(component, options)` 显示,支持 anchor、百分比宽度、最大高度、margin 和 non-capturing tooltip。Overlay 在虚拟行数组阶段合成,因此仍复用同一套差分渲染路径。
|
||||
|
||||
Keybinding 通过 `KeybindingRegistry` 和 `TuiKeybindings` 描述动作到按键的映射。组件内部优先匹配动作名,而不是直接散落比较具体按键字符串。
|
||||
|
||||
文本工具集中在 `TerminalTextMeasurer`,提供 ANSI/OSC 感知的宽度计算、截断、按列切片和换行。自定义组件不要使用 `string.Length` 判断终端宽度。
|
||||
|
||||
自动补全通过 `IAutocompleteProvider` 扩展。当前 `Editor` 已支持 slash command provider,并复用 `SelectList` 展示候选。
|
||||
|
||||
图像能力通过 `ITerminalImageService` 选择 Kitty、iTerm2 或文本 fallback。渲染管线会识别图像行,避免把大段协议序列当普通文本截断。
|
||||
|
||||
## 自定义组件约束
|
||||
|
||||
自定义组件应该只返回行数组,不要直接写 stdout。终端写入、清屏、光标移动和 synchronized output 都应该交给 renderer。
|
||||
|
||||
如果组件会缓存渲染结果,需要实现 `Invalidate()` 并在内容、宽度、主题或焦点变化后清理缓存。
|
||||
|
||||
如果组件包含输入框或编辑器并需要 IME 光标定位,容器组件应该实现 `IFocusableComponent` 并把 `Focused` 传给内部输入组件。
|
||||
|
||||
## 工程命令
|
||||
|
||||
```powershell
|
||||
dotnet restore ttui.slnx
|
||||
dotnet build ttui.slnx --configuration Release --no-restore
|
||||
dotnet format ttui.slnx --verify-no-changes --no-restore
|
||||
dotnet test ttui.slnx --configuration Release --no-build
|
||||
dotnet pack src/TinyTUI/TinyTUI.csproj --configuration Release --no-build
|
||||
```
|
||||
|
||||
`test/TinyTUI.Tests` 是正式 xUnit 回归测试项目。`test/TinyTUI.TextChecks` 和 `test/TinyTUI.ComponentChecks` 是早期最小 smoke check,后续会逐步收敛到正式测试项目。
|
||||
|
||||
## 调试
|
||||
|
||||
渲染器支持环境变量调试日志:
|
||||
|
||||
```powershell
|
||||
$env:TINYTUI_DEBUG_REDRAW = "1"
|
||||
$env:TINYTUI_DEBUG_RENDER = "1"
|
||||
dotnet run --project src/Example/Example.csproj
|
||||
```
|
||||
|
||||
日志默认写入临时目录下的 `tinytui` 子目录。
|
||||
|
||||
@@ -398,6 +398,35 @@ TinyTUI 现在已经具备最小可运行的 C# TUI 框架骨架:终端输入
|
||||
- 拆分 Example 为多个真实场景:基础输入、overlay 菜单、聊天界面、Markdown 浏览、设置面板、图像 fallback
|
||||
- 明确公开 API 和内部实现边界,避免后续重构时破坏使用者代码
|
||||
|
||||
本次推进:
|
||||
|
||||
- README 从空文件补成可执行入口文档,覆盖快速开始、最小 C# 示例、核心 API、内置组件、overlay、keybinding、文本工具、自动补全、图像 fallback、自定义组件约束、工程命令和调试日志
|
||||
- `Directory.Build.props` 增加统一工程属性:`LangVersion=latest`、nullable、implicit usings、确定性构建、CI build 标记和统一 `build/artifacts` 输出目录
|
||||
- `TinyTUI.csproj` 增加 NuGet 包元数据、tags、README 打包、XML documentation、symbol package 和包输出目录,为 `dotnet pack` 产出可发布包做准备
|
||||
- 新增 GitHub Actions CI,按 restore、Release build、format verify、test、pack 的顺序覆盖工程化闭环
|
||||
- 新增 `docs/public-api.md`,把应用侧稳定入口、推荐组合、组件约束和仍在演进的公开边界写清楚,避免后续重构误伤使用者依赖
|
||||
|
||||
为什么先做:
|
||||
|
||||
- 对比 `tmp/tui/README.md` 和 `package.json`,参考实现已经能让使用者快速判断“怎么安装、怎么跑、有哪些 API、怎么测试和发布”;C# 侧 README 和包元数据为空或缺失,会阻塞集成和 NuGet 发布
|
||||
- 第 9 项已经有正式 xUnit 项目,先接 CI 和 pack 可以把测试基础设施转成持续验证能力,后续每个框架增量都有自动守门
|
||||
- 公开 API 边界先用文档声明,不急着重命名或收缩源码命名空间,避免在第 10 项里引入运行时行为变更
|
||||
|
||||
当前更好的点:
|
||||
|
||||
- C# 侧把 XML documentation 和 artifacts 输出放在根级 `Directory.Build.props`,类库、测试和示例的构建产物路径一致,CI 和本地命令更容易对齐
|
||||
- NuGet README 直接复用仓库根 README,包页面和源码文档不会维护两套快速开始
|
||||
- CI 同时跑 `dotnet format --verify-no-changes` 和 `dotnet pack`,比只跑测试更早暴露工程文件、包元数据和格式问题
|
||||
|
||||
后续仍需补齐:
|
||||
|
||||
- 当前还没有正式 `LICENSE` 文件,包使用 `PackageLicenseExpression=MIT` 只是发布元数据,后续应补仓库级 license 文本
|
||||
- `PackageProjectUrl` 和 `RepositoryUrl` 先使用占位仓库地址,真实发布前需要替换成实际远端
|
||||
- XML documentation 已启用,但大量 public API 还缺中文 summary,后续需要分模块补齐并决定是否把 CS1591 提升为 CI 约束
|
||||
- CI 已提供 `dotnet format`,但当前仓库还没有 `.editorconfig`,格式规则仍主要依赖 SDK 默认值,后续应补编码、缩进、nullable 和 analyzer 策略
|
||||
- benchmark 目录仍未建立,后续需要补大文本、频繁刷新、overlay 合成、Markdown 缓存和图像行保护的压力场景
|
||||
- Example 仍是单个综合示例,尚未像参考实现 `test/chat-simple.ts` 那样拆出聊天、设置、图像 fallback、Markdown 浏览等真实场景
|
||||
|
||||
## 建议执行顺序
|
||||
|
||||
1. 先补终端会话能力、输入协议和 keybinding,因为它们会影响所有交互组件
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# 公开 API 和内部边界
|
||||
|
||||
本文档用于说明 TinyTUI 当前可被应用代码依赖的公开边界,以及后续重构时应尽量保持稳定的区域。
|
||||
|
||||
## 应用侧稳定入口
|
||||
|
||||
应用代码优先依赖以下命名空间:
|
||||
|
||||
- `TinyTUI.Components`:组件接口和内置组件
|
||||
- `TinyTUI.Runtime`:运行时入口和 overlay 操作
|
||||
- `TinyTUI.Terminal`:真实终端会话
|
||||
- `TinyTUI.Rendering`:渲染器和渲染选项
|
||||
- `TinyTUI.Text`:ANSI 感知宽度、截断、切片和换行
|
||||
- `TinyTUI.Input`:输入事件、按键名称和 keybinding
|
||||
- `TinyTUI.Autocomplete`:自动补全 provider 契约
|
||||
- `TinyTUI.Terminal.Images`:终端图像服务和能力检测
|
||||
|
||||
## 推荐组合
|
||||
|
||||
真实终端应用建议使用:
|
||||
|
||||
```csharp
|
||||
using var terminal = new ConsoleTerminalSession();
|
||||
var parser = new DefaultInputParser();
|
||||
var textMeasurer = new TerminalTextMeasurer();
|
||||
var renderer = new DifferentialRenderer(terminal, textMeasurer);
|
||||
using var runtime = new TuiRuntime(terminal, parser, renderer);
|
||||
```
|
||||
|
||||
测试或嵌入场景可以替换 `ITerminalSession`、`ITerminalOutput`、`IInputParser`、`IRenderer` 或 `ITextMeasurer`,但仍保持 runtime 和组件不直接写终端。
|
||||
|
||||
## 组件约束
|
||||
|
||||
组件只负责 `Render(int width)` 返回行数组,输入组件通过 `HandleInput(TuiInputEvent input)` 接收规范化输入事件。
|
||||
|
||||
组件不应该直接调用 `Console.Write` 或依赖真实终端尺寸。需要终端宽度时使用 render 传入的 `width`,需要截断或换行时使用 `ITextMeasurer`。
|
||||
|
||||
可缓存组件应该在 `Invalidate()` 中清理缓存。runtime 会在焦点切换、resize 和显式失效时调用该入口。
|
||||
|
||||
## 仍在演进的区域
|
||||
|
||||
以下能力已具备基础形态,但公开契约后续仍可能细化:
|
||||
|
||||
- Kitty keyboard protocol 的完整协商和 release 事件
|
||||
- Runtime 级全局 input listener 和 keybinding 配置
|
||||
- Overlay 多层焦点恢复和复杂样式 segment 保持
|
||||
- Unicode RGI emoji 精确宽度和 CJK 标点断行
|
||||
- 终端图像 cell size 查询、resize 重新布局和 Kitty image 生命周期同步
|
||||
- 正式 benchmark 和多场景 Example 拆分
|
||||
|
||||
如果应用需要提前依赖这些能力,建议通过接口注入或封装适配层隔离变更。
|
||||
@@ -2,8 +2,26 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>true</IsPackable>
|
||||
<PackageId>TinyTUI</PackageId>
|
||||
<Title>TinyTUI</Title>
|
||||
<Description>Minimal C# terminal UI framework with component rendering, overlays, keybindings, ANSI-aware text tools, autocomplete, terminal image fallback, and testable rendering infrastructure.</Description>
|
||||
<Authors>TinyTUI Contributors</Authors>
|
||||
<Company>TinyTUI Contributors</Company>
|
||||
<PackageTags>tui;terminal;console;ansi;cli;dotnet;components;differential-rendering</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/tinytui/tinytui</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/tinytui/tinytui</RepositoryUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageOutputPath>$(ArtifactsPath)\packages</PackageOutputPath>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user