@simon_he/vue-tui 0.0.3 → 0.0.5

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/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Vue Terminal
2
2
 
3
3
  用 Vue 组件语法绘制和交互的终端渲染框架。目标是让 “终端” 具备:
4
+
4
5
  - 类终端的写入/删除/更新/尺寸调整能力
5
6
  - ANSI 样式渲染(颜色、加粗、下划线等)
6
7
  - 事件机制(点击、移动、滚动、拖拽)
@@ -13,7 +14,13 @@
13
14
  ```vue
14
15
  <script setup lang="ts">
15
16
  import { computed, ref } from 'vue'
16
- import { TerminalProvider, TBox, TInput, TText, useLayout } from '@simon_he/vue-tui'
17
+ import {
18
+ TerminalProvider,
19
+ TBox,
20
+ TInput,
21
+ TText,
22
+ useLayout,
23
+ } from '@simon_he/vue-tui'
17
24
 
18
25
  const layout = useLayout()
19
26
  const cols = computed(() => layout.clipRect?.w ?? 80)
@@ -22,8 +29,21 @@ const input = ref('')
22
29
  </script>
23
30
 
24
31
  <template>
25
- <TerminalProvider :cols="80" :rows="24" :default-style="{ fg: 'whiteBright' }">
26
- <TBox :x="0" :y="0" :w="80" :h="24" border title="Demo" :padding="1" :style="{ fg: 'blueBright' }">
32
+ <TerminalProvider
33
+ :cols="80"
34
+ :rows="24"
35
+ :default-style="{ fg: 'whiteBright' }"
36
+ >
37
+ <TBox
38
+ :x="0"
39
+ :y="0"
40
+ :w="80"
41
+ :h="24"
42
+ border
43
+ title="Demo"
44
+ :padding="1"
45
+ :style="{ fg: 'blueBright' }"
46
+ >
27
47
  <TText :x="0" :y="0" :w="78" :value="`cols=${cols} rows=${rows}`" />
28
48
  <TInput :x="0" :y="20" :w="78" v-model="input" placeholder="Type..." />
29
49
  </TBox>
@@ -31,6 +51,15 @@ const input = ref('')
31
51
  </template>
32
52
  ```
33
53
 
54
+ ## 组件文档与验收标准
55
+
56
+ - VitePress 文档站点(本地):`bun run --filter '@simon_he/vue-tui' docs:dev`(入口 `docs/index.md`)
57
+ - 组件使用文档:`docs/components.md`
58
+ - 组件 Props/Events(自动生成):`docs/generated/components-api.md`(脚本:`scripts/generate-component-api-docs.ts`)
59
+ - 组件验收标准(可信赖组件定义 + 测试范围):`docs/components-acceptance.md`
60
+ - Terminal 兼容性与颜色一致性:`docs/terminal-compatibility.md`
61
+ - 性能关注点与回归策略:`docs/performance.md`
62
+
34
63
  ### 终端(真实 CLI)渲染
35
64
 
36
65
  内置 `vue-terminal run`,直接在真实终端中跑 UI(stdout renderer + stdin driver):
@@ -67,11 +96,13 @@ terminal 构建产物会生成 `dist-terminal/terminal.js`,可以直接作为
67
96
  GoatChain 示例应用目前用的是 mock LLM,但你可以很快替换成任意 LLM SDK(OpenAI/Anthropic/本地模型)。
68
97
 
69
98
  建议做法:
99
+
70
100
  1. 用 ACP 风格的流式协议驱动 UI(支持 `text_delta` / `tool_call_delta` / `tool_call_end` / `tool_result` / `done`)。
71
101
  2. tool_call 的执行可以交给 agent 侧(推荐,和 GoatChain CLI 一致),UI 只负责渲染与“暂停→继续”的用户输入。
72
102
  3. 在 UI 侧维护 token 统计与 context window(真实 tokenizer 或近似估算均可)。
73
103
 
74
104
  GoatChain 的核心协议定义在:
105
+
75
106
  - `apps/goatchain/src/core/acp.ts`
76
107
  - `apps/goatchain/src/core/model.ts`
77
108
  - `apps/goatchain/src/core/store.ts`
@@ -118,23 +149,27 @@ DOM 事件 -> EventManager -> HitTest -> Vue 组件事件回调
118
149
 
119
150
  ```ts
120
151
  type Style = {
121
- fg?: string; bg?: string;
122
- bold?: boolean; dim?: boolean; italic?: boolean;
123
- underline?: boolean; inverse?: boolean;
124
- };
152
+ fg?: string
153
+ bg?: string
154
+ bold?: boolean
155
+ dim?: boolean
156
+ italic?: boolean
157
+ underline?: boolean
158
+ inverse?: boolean
159
+ }
125
160
 
126
161
  type Cell = {
127
- ch: string;
128
- style: Style;
129
- width: 1 | 2; // 宽字符支持
130
- };
162
+ ch: string
163
+ style: Style
164
+ width: 1 | 2 // 宽字符支持
165
+ }
131
166
 
132
167
  type Buffer = {
133
- rows: Cell[][];
134
- cols: number;
135
- rowsCount: number;
136
- dirtyRows: boolean[];
137
- };
168
+ rows: Cell[][]
169
+ cols: number
170
+ rowsCount: number
171
+ dirtyRows: boolean[]
172
+ }
138
173
  ```
139
174
 
140
175
  ## 终端语义范围(初版约束)
@@ -155,18 +190,25 @@ type Buffer = {
155
190
 
156
191
  ```ts
157
192
  interface Terminal {
158
- resize(cols: number, rows: number): void;
159
- clear(x?: number, y?: number, w?: number, h?: number): void;
160
- write(text: string, opts?: { x?: number; y?: number; style?: Style }): void;
161
- writeAnsi(text: string, opts?: { x?: number; y?: number }): void;
162
- put(x: number, y: number, ch: string, style?: Style): void;
163
- fill(x: number, y: number, w: number, h: number, ch?: string, style?: Style): void;
164
- scroll(lines: number): void;
165
- setCursor(x: number, y: number, visible?: boolean): void;
166
- batch(fn: () => void): void;
167
- commit(): void;
168
- on(event: TerminalEvent, cb: (e: TerminalEventPayload) => void): () => void;
169
- dispose(): void;
193
+ resize(cols: number, rows: number): void
194
+ clear(x?: number, y?: number, w?: number, h?: number): void
195
+ write(text: string, opts?: { x?: number; y?: number; style?: Style }): void
196
+ writeAnsi(text: string, opts?: { x?: number; y?: number }): void
197
+ put(x: number, y: number, ch: string, style?: Style): void
198
+ fill(
199
+ x: number,
200
+ y: number,
201
+ w: number,
202
+ h: number,
203
+ ch?: string,
204
+ style?: Style,
205
+ ): void
206
+ scroll(lines: number): void
207
+ setCursor(x: number, y: number, visible?: boolean): void
208
+ batch(fn: () => void): void
209
+ commit(): void
210
+ on(event: TerminalEvent, cb: (e: TerminalEventPayload) => void): () => void
211
+ dispose(): void
170
212
  }
171
213
  ```
172
214
 
@@ -178,52 +220,67 @@ interface Terminal {
178
220
 
179
221
  ```ts
180
222
  type TerminalEvent =
181
- | 'click' | 'dblclick' | 'contextmenu'
182
- | 'pointerdown' | 'pointerup' | 'pointermove' | 'pointerenter' | 'pointerleave'
183
- | 'mousedown' | 'mouseup' | 'mousemove' | 'mouseenter' | 'mouseleave'
223
+ | 'click'
224
+ | 'dblclick'
225
+ | 'contextmenu'
226
+ | 'pointerdown'
227
+ | 'pointerup'
228
+ | 'pointermove'
229
+ | 'pointerenter'
230
+ | 'pointerleave'
231
+ | 'mousedown'
232
+ | 'mouseup'
233
+ | 'mousemove'
234
+ | 'mouseenter'
235
+ | 'mouseleave'
184
236
  | 'wheel'
185
- | 'keydown' | 'keyup'
186
- | 'input' | 'change'
187
- | 'compositionstart' | 'compositionupdate' | 'compositionend'
188
- | 'focus' | 'blur'
189
- | 'drag';
237
+ | 'keydown'
238
+ | 'keyup'
239
+ | 'input'
240
+ | 'change'
241
+ | 'compositionstart'
242
+ | 'compositionupdate'
243
+ | 'compositionend'
244
+ | 'focus'
245
+ | 'blur'
246
+ | 'drag'
190
247
 
191
248
  type TerminalBaseEvent = {
192
- type: TerminalEvent;
193
- target: TerminalNode | null;
194
- currentTarget: TerminalNode | null;
195
- bubbles: boolean;
196
- cancelable: boolean;
197
- defaultPrevented: boolean;
198
- timeStamp: number;
199
- stopPropagation(): void;
200
- preventDefault(): void;
201
- nativeEvent?: Event;
202
- };
249
+ type: TerminalEvent
250
+ target: TerminalNode | null
251
+ currentTarget: TerminalNode | null
252
+ bubbles: boolean
253
+ cancelable: boolean
254
+ defaultPrevented: boolean
255
+ timeStamp: number
256
+ stopPropagation(): void
257
+ preventDefault(): void
258
+ nativeEvent?: Event
259
+ }
203
260
 
204
261
  type TerminalPointerEvent = TerminalBaseEvent & {
205
- clientX: number;
206
- clientY: number;
207
- cellX: number;
208
- cellY: number;
209
- button?: number;
210
- buttons?: number;
211
- ctrlKey?: boolean;
212
- shiftKey?: boolean;
213
- altKey?: boolean;
214
- metaKey?: boolean;
215
- deltaY?: number;
216
- };
262
+ clientX: number
263
+ clientY: number
264
+ cellX: number
265
+ cellY: number
266
+ button?: number
267
+ buttons?: number
268
+ ctrlKey?: boolean
269
+ shiftKey?: boolean
270
+ altKey?: boolean
271
+ metaKey?: boolean
272
+ deltaY?: number
273
+ }
217
274
 
218
275
  type TerminalKeyboardEvent = TerminalBaseEvent & {
219
- key: string;
220
- code: string;
221
- ctrlKey?: boolean;
222
- shiftKey?: boolean;
223
- altKey?: boolean;
224
- metaKey?: boolean;
225
- repeat?: boolean;
226
- };
276
+ key: string
277
+ code: string
278
+ ctrlKey?: boolean
279
+ shiftKey?: boolean
280
+ altKey?: boolean
281
+ metaKey?: boolean
282
+ repeat?: boolean
283
+ }
227
284
  ```
228
285
 
229
286
  ## 事件派发与冒泡(建议)
@@ -308,15 +365,19 @@ const { mount } = useTerminalRuntime()
308
365
  function onInputCommit(value: string) {
309
366
  if (value.startsWith('/')) {
310
367
  mount(TSelect, {
311
- x: 2, y: 10, w: 20, h: 6,
368
+ x: 2,
369
+ y: 10,
370
+ w: 20,
371
+ h: 6,
312
372
  options: ['a', 'b', 'c'],
313
- onSelect: v => console.log(v),
373
+ onSelect: (v) => console.log(v),
314
374
  })
315
375
  }
316
376
  }
317
377
  ```
318
378
 
319
379
  约束:
380
+
320
381
  - 动态插入的组件必须显式提供布局 (x/y/w/h) 或依赖父容器布局
321
382
  - 插入/卸载会触发局部重绘,不影响其它区域
322
383
 
@@ -328,10 +389,10 @@ function onInputCommit(value: string) {
328
389
 
329
390
  ```ts
330
391
  type TerminalHandle = {
331
- update: (props: Record<string, unknown>) => void;
332
- move: (x: number, y: number) => void;
333
- unmount: () => void;
334
- };
392
+ update: (props: Record<string, unknown>) => void
393
+ move: (x: number, y: number) => void
394
+ unmount: () => void
395
+ }
335
396
  ```
336
397
 
337
398
  ## 组件生命周期与资源管理(建议)
@@ -341,6 +402,7 @@ type TerminalHandle = {
341
402
  - 卸载:释放事件节点、清理 buffer 区域、移除焦点/输入绑定
342
403
 
343
404
  对 `v-if`/`v-show` 的行为约定:
405
+
344
406
  - `v-if`: 触发卸载与资源释放(清 buffer、解绑事件)
345
407
  - `v-show`: 仅隐藏渲染,保留节点与状态(不解绑事件)
346
408
 
@@ -399,6 +461,7 @@ type TerminalHandle = {
399
461
  ## 带边框 Box 的内容保护
400
462
 
401
463
  当 `TBox` 带边框时,需要保护内容区域不被破坏:
464
+
402
465
  - 外框占 1 cell 边距,内容区域为 `(x+1, y+1, w-2, h-2)`
403
466
  - 内容写入必须裁剪到内容区域,避免覆盖边框
404
467
  - 当内容超出时,支持 `overflow: clip | scroll`
@@ -425,7 +488,7 @@ type TerminalHandle = {
425
488
  ## ANSI 支持
426
489
 
427
490
  - `writeAnsi()` 接受 ANSI 字符串,解析后写入 Buffer
428
- - 可先实现轻量解析器,后续切换到 `ansi-styles`/`ansis`
491
+ - 可先实现轻量解析器,后续切换到更完整的 ANSI 样式库(如 `ansis`)
429
492
 
430
493
  ## 文本分段与宽字符策略(建议)
431
494