pi-one-ui 0.7.2 → 0.8.0

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/README.en.md +5 -8
  3. package/README.md +6 -7
  4. package/extensions/app/config/footer.ts +21 -5
  5. package/extensions/app/config/renderer.ts +1 -1
  6. package/extensions/app/config/shell.ts +62 -60
  7. package/extensions/app/runtime/prototype-patch-registry.ts +13 -9
  8. package/extensions/app/runtime/tui-runtime.ts +19 -12
  9. package/extensions/app/settings/controller.ts +2 -2
  10. package/extensions/layouts/context/index.ts +2 -2
  11. package/extensions/layouts/context/message/user-message-osc.ts +1 -1
  12. package/extensions/layouts/context/message/user-message-styles.ts +9 -6
  13. package/extensions/layouts/context/message/user-message.ts +5 -5
  14. package/extensions/layouts/context/renderer/compact-mode.ts +2 -2
  15. package/extensions/layouts/context/renderer/default-mode.ts +10 -10
  16. package/extensions/layouts/context/renderer/index.ts +0 -1
  17. package/extensions/layouts/context/renderer/mouse/interaction.ts +5 -5
  18. package/extensions/layouts/context/renderer/mouse/scroll.ts +4 -4
  19. package/extensions/layouts/context/renderer/tool/diff/diff-edit-render.ts +1 -1
  20. package/extensions/layouts/context/renderer/tool/diff/index.ts +1 -1
  21. package/extensions/layouts/context/renderer/tool/grouping.ts +3 -3
  22. package/extensions/layouts/context/renderer/tool/message-display.ts +7 -7
  23. package/extensions/layouts/context/renderer/tool/result.ts +16 -16
  24. package/extensions/layouts/context/renderer/tool/toggle-render-cache.ts +9 -9
  25. package/extensions/layouts/editor/controller.ts +17 -17
  26. package/extensions/layouts/editor/editor-metadata-format.ts +5 -5
  27. package/extensions/layouts/editor/factory.ts +5 -5
  28. package/extensions/layouts/editor/minimalist-editor.ts +8 -8
  29. package/extensions/layouts/editor/ownership.ts +24 -20
  30. package/extensions/layouts/editor/ui.ts +10 -15
  31. package/extensions/layouts/footer/controller.ts +4 -4
  32. package/extensions/layouts/footer/data.ts +2 -2
  33. package/extensions/layouts/footer/extension-status.ts +4 -4
  34. package/extensions/layouts/footer/footer.ts +5 -2
  35. package/extensions/layouts/overlay/selector-border.ts +4 -4
  36. package/extensions/layouts/overlay/selector-controller.ts +1 -1
  37. package/extensions/layouts/overlay/settings-previews.ts +2 -2
  38. package/extensions/layouts/working-line/interaction-summary.ts +3 -1
  39. package/extensions/layouts/working-line/working-line.ts +4 -4
  40. package/extensions/services/package-data.ts +1 -1
  41. package/extensions/shared/style.ts +66 -56
  42. package/extensions/tools/patch-keys.ts +23 -23
  43. package/package.json +7 -7
@@ -2,9 +2,9 @@ import { visibleWidth } from "@earendil-works/pi-tui";
2
2
  import type {
3
3
  ColorSpec,
4
4
  PolishedTuiColors,
5
+ PolishedTuiConfig,
5
6
  WorkingLineComponentConfig,
6
7
  WorkingLineTextAnimation,
7
- ZentuiConfig,
8
8
  } from "../../app/config/shell.ts";
9
9
  import { PI_WORKING_LINE_MESSAGES } from "../../app/config/working-line-messages.ts";
10
10
  import { formatCount } from "../../shared/format.ts";
@@ -1048,7 +1048,7 @@ export class WorkingLineController {
1048
1048
  private stopElapsedUpdates: (() => void) | undefined;
1049
1049
 
1050
1050
  constructor(
1051
- private readonly getConfig: () => ZentuiConfig,
1051
+ private readonly getConfig: () => PolishedTuiConfig,
1052
1052
  private readonly getTheme: () => ThemeLike,
1053
1053
  private readonly durationClock: AgentDurationClock = new AgentDurationClock(),
1054
1054
  private readonly random: () => number = Math.random,
@@ -1214,7 +1214,7 @@ export class WorkingLineController {
1214
1214
  }
1215
1215
 
1216
1216
  private makeFrameKey(
1217
- rootConfig: ZentuiConfig,
1217
+ rootConfig: PolishedTuiConfig,
1218
1218
  selectedMessage: string | undefined,
1219
1219
  ): string {
1220
1220
  const config = rootConfig.components.workingLine;
@@ -1253,7 +1253,7 @@ export class WorkingLineController {
1253
1253
 
1254
1254
  private applyIndicator(
1255
1255
  ui: WorkingLineUi,
1256
- rootConfig: ZentuiConfig,
1256
+ rootConfig: PolishedTuiConfig,
1257
1257
  selectedMessage = this.selectedMessage,
1258
1258
  force = false,
1259
1259
  rebase = false,
@@ -6,7 +6,7 @@
6
6
  * same top-level entries the runtime detector sees, then hands back a
7
7
  * silent null when no manifest is present or any parser fails.
8
8
  *
9
- * Supported manifest sources (intersected with runtimes Zentui detects):
9
+ * Supported manifest sources (intersected with runtimes pi-one-ui detects):
10
10
  * bun / nodejs → package.json
11
11
  * deno → deno.json / deno.jsonc
12
12
  * maven → pom.xml
@@ -71,28 +71,9 @@ const terminalStyleModifiers = new Map([
71
71
  ]);
72
72
 
73
73
  /**
74
- * ANSI 色名到主题语义 token 的映射:theme 语义下写 `red` 得到主题错误色,
75
- * 写 `green` 得到主题成功色,保证默认值与主题观感一致。
74
+ * Theme token modifiers are handled by the active Pi theme. Terminal-only
75
+ * modifiers such as `dimmed` are accepted with explicit terminal colors.
76
76
  */
77
- const themeColorNameMap = new Map([
78
- ["red", "error"],
79
- ["bright-red", "error"],
80
- ["green", "success"],
81
- ["bright-green", "success"],
82
- ["yellow", "warning"],
83
- ["bright-yellow", "warning"],
84
- ["blue", "syntaxFunction"],
85
- ["bright-blue", "syntaxFunction"],
86
- ["cyan", "syntaxFunction"],
87
- ["bright-cyan", "syntaxFunction"],
88
- ["purple", "syntaxKeyword"],
89
- ["bright-purple", "syntaxKeyword"],
90
- ["black", "muted"],
91
- ["bright-black", "muted"],
92
- ["white", "text"],
93
- ["bright-white", "text"],
94
- ]);
95
-
96
77
  const themeStyleModifiers = new Set(["bold", "italic", "underline"]);
97
78
 
98
79
  const themeColorTokens = new Set<ThemeColor>([
@@ -107,6 +88,9 @@ const themeColorTokens = new Set<ThemeColor>([
107
88
  "dim",
108
89
  "text",
109
90
  "thinkingText",
91
+ "scrollbarTrack",
92
+ "scrollbarThumb",
93
+ "searchMatchText",
110
94
  "userMessageText",
111
95
  "customMessageText",
112
96
  "customMessageLabel",
@@ -175,24 +159,51 @@ function isExplicitTerminalColorToken(token: string): boolean {
175
159
  );
176
160
  }
177
161
 
178
- function isSupportedStyleToken(token: string): boolean {
162
+ function isExplicitTerminalStyleToken(token: string): boolean {
179
163
  const normalized = token.toLowerCase();
180
- if (terminalStyleModifiers.has(normalized)) return true;
181
- if (terminalColorToAnsi(normalized) !== undefined) return true;
164
+ return (
165
+ terminalStyleModifiers.has(normalized) ||
166
+ isExplicitTerminalColorToken(token)
167
+ );
168
+ }
182
169
 
170
+ function isTerminalColorToken(token: string): boolean {
171
+ const normalized = token.toLowerCase();
183
172
  const isForeground = normalized.startsWith("fg:");
184
173
  const isBackground = normalized.startsWith("bg:");
185
- if (isForeground || isBackground) {
186
- return terminalColorToAnsi(normalized.slice(3), isBackground) !== undefined;
187
- }
174
+ const colorName =
175
+ isForeground || isBackground ? normalized.slice(3) : normalized;
176
+ return terminalColorToAnsi(colorName, isBackground) !== undefined;
177
+ }
188
178
 
179
+ function isThemeColorToken(token: string): boolean {
180
+ if (isTerminalColorToken(token)) return false;
189
181
  return themeColorTokens.has(token as ThemeColor);
190
182
  }
191
183
 
192
184
  export function isSupportedColorSpec(style: ColorSpec): boolean {
193
185
  const trimmed = style.trim();
194
186
  if (trimmed === "") return true;
195
- return trimmed.split(/\s+/).every(isSupportedStyleToken);
187
+
188
+ const tokens = trimmed.split(/\s+/).filter(Boolean);
189
+ const hasExplicitTerminalColor = tokens.some(isExplicitTerminalColorToken);
190
+ if (hasExplicitTerminalColor) {
191
+ return (
192
+ tokens.every(isExplicitTerminalStyleToken) &&
193
+ tokens.filter(isExplicitTerminalColorToken).length === 1
194
+ );
195
+ }
196
+
197
+ const colorTokens = tokens.filter((token) => {
198
+ const normalized = token.toLowerCase();
199
+ return !themeStyleModifiers.has(normalized) && normalized !== "dimmed";
200
+ });
201
+ if (colorTokens.length === 0) {
202
+ return tokens.every((token) =>
203
+ terminalStyleModifiers.has(token.toLowerCase()),
204
+ );
205
+ }
206
+ return colorTokens.length === 1 && isThemeColorToken(colorTokens[0]);
196
207
  }
197
208
 
198
209
  function applyThemeModifiers(
@@ -225,20 +236,12 @@ export function safeThemeFg(
225
236
  }
226
237
 
227
238
  function mapThemeColor(styleTokens: string[]): string | undefined {
228
- let fallback: string | undefined;
229
239
  for (const token of styleTokens) {
230
- const normalized = token.toLowerCase();
231
- if (themeStyleModifiers.has(normalized)) continue;
232
- if (normalized === "dim" || normalized === "dimmed") {
233
- fallback = "muted";
234
- continue;
235
- }
236
-
237
- const mapped = themeColorNameMap.get(normalized);
238
- if (mapped) return mapped;
240
+ if (themeStyleModifiers.has(token.toLowerCase())) continue;
241
+ if (isTerminalColorToken(token)) continue;
239
242
  return token;
240
243
  }
241
- return fallback;
244
+ return undefined;
242
245
  }
243
246
 
244
247
  /**
@@ -303,7 +306,7 @@ function isSafeSgrParameters(parameters: string): boolean {
303
306
  return true;
304
307
  }
305
308
 
306
- /** Accept only the bounded SGR subset emitted by Zentui's supported style tokens. */
309
+ /** Accept only the bounded SGR subset emitted by pi-one-ui's supported style tokens. */
307
310
  export function isSafeSgrStylePrefix(value: unknown): value is string {
308
311
  if (
309
312
  typeof value !== "string" ||
@@ -351,8 +354,7 @@ export function renderTerminalStyle(style: string, text: string): string {
351
354
  /**
352
355
  * 单模式颜色渲染:配置值按 theme 语义解释。
353
356
  * - 含显式终端 token(hex/数字/fg:/bg:)→ 直接输出固定终端色;
354
- * - 其余按主题 token 解析(ANSI 色名映射到语义 token,如 red→error),
355
- * 修饰符通过主题的 bold/italic/underline 应用;
357
+ * - 其余按官方主题 token 解析,修饰符通过主题的 bold/italic/underline 应用;
356
358
  * - 空值原样返回。
357
359
  */
358
360
  export function renderThemeStyle(
@@ -364,8 +366,9 @@ export function renderThemeStyle(
364
366
  if (trimmed === "") return text;
365
367
 
366
368
  const tokens = trimmed.split(/\s+/).filter(Boolean);
367
- if (tokens.some(isExplicitTerminalColorToken))
369
+ if (tokens.some(isTerminalColorToken)) {
368
370
  return renderTerminalStyle(style, text);
371
+ }
369
372
 
370
373
  const color = mapThemeColor(tokens) ?? "text";
371
374
  return safeThemeFg(theme, color, applyThemeModifiers(theme, tokens, text));
@@ -379,8 +382,9 @@ function renderThemeStyleStrict(
379
382
  const trimmed = style.trim();
380
383
  if (trimmed === "") return text;
381
384
  const tokens = trimmed.split(/\s+/).filter(Boolean);
382
- if (tokens.some(isExplicitTerminalColorToken))
385
+ if (tokens.some(isTerminalColorToken)) {
383
386
  return renderTerminalStyle(style, text);
387
+ }
384
388
  const color = mapThemeColor(tokens) ?? "text";
385
389
  return theme.fg(color, applyThemeModifiers(theme, tokens, text));
386
390
  }
@@ -407,21 +411,27 @@ export function renderThemeStyleOrFallbackStrict(
407
411
  }
408
412
 
409
413
  /**
410
- * 主题画板存在性探测:pi Theme 暴露 fgColors Map;测试或其他实现可
411
- * 提供 hasThemeToken 回调,缺失时保守按"无该 token"处理。
414
+ * 主题画板存在性探测:识别 Pi 当前 Theme 暴露的 fgAnsi Map 和解析完成的
415
+ * colors 记录;测试或其他实现可提供 hasThemeToken 回调,缺失时保守按
416
+ * "无该 token"处理。
412
417
  */
413
418
  function themeHasToken(theme: ThemeLike, token: string): boolean {
414
- if (
415
- typeof (theme as { hasThemeToken?: (t: string) => boolean })
416
- .hasThemeToken === "function"
417
- ) {
418
- return (
419
- theme as unknown as { hasThemeToken(t: string): boolean }
420
- ).hasThemeToken(token);
419
+ const probe = theme as unknown as {
420
+ hasThemeToken?: (t: string) => boolean;
421
+ fgAnsi?: Map<string, string>;
422
+ colors?: Record<string, unknown>;
423
+ };
424
+ if (typeof probe.hasThemeToken === "function") {
425
+ return probe.hasThemeToken(token);
426
+ }
427
+ if (probe.fgAnsi instanceof Map && probe.fgAnsi.has(token)) {
428
+ return true;
421
429
  }
422
- const fgColors = (theme as unknown as { fgColors?: Map<string, string> })
423
- .fgColors;
424
- return fgColors instanceof Map && fgColors.has(token);
430
+ return (
431
+ typeof probe.colors === "object" &&
432
+ probe.colors !== null &&
433
+ token in probe.colors
434
+ );
425
435
  }
426
436
 
427
437
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * ccstyle 补丁所有权与跨模块协调键的单一来源(single source of truth)。
2
+ * pi-one-ui 补丁所有权与跨模块协调键的单一来源(single source of truth)。
3
3
  *
4
4
  * 全部使用 Symbol.for(全局注册表):/reload 后新模块实例拿到的是同一个 symbol,
5
5
  * 这是跨 reload 识别/让渡补丁所有权的关键。切勿把这些 Symbol.for 改成模块局部 Symbol。
@@ -14,25 +14,25 @@
14
14
 
15
15
  // ── compact / compact-thinking 协调 ──
16
16
  export const COMPACT_MODE_PATCH_KEY = Symbol.for(
17
- "pi.ccstyle.compact-mode-patch",
17
+ "pi-one-ui.compact-mode-patch",
18
18
  );
19
19
  export const COMPACT_THINKING_PATCH_KEY = Symbol.for(
20
- "pi.ccstyle.compact-thinking-update",
20
+ "pi-one-ui.compact-thinking-update",
21
21
  );
22
22
  export const PROTOTYPE_ORIGINAL_KEY = Symbol.for(
23
- "pi.ccstyle.prototype-original",
23
+ "pi-one-ui.prototype-original",
24
24
  );
25
25
  export const COMPACT_THINKING_OWNER = Symbol.for(
26
- "pi.ccstyle.compact-thinking-owner",
26
+ "pi-one-ui.compact-thinking-owner",
27
27
  );
28
28
 
29
29
  // ── assistant 展开状态(实例标记;description 供旧模块按 symbol.description 清理) ──
30
30
  export const ASSISTANT_SET_EXPANDED_DESCRIPTION =
31
- "pi.ccstyle.compact-assistant-set-expanded";
31
+ "pi-one-ui.compact-assistant-set-expanded";
32
32
  export const ASSISTANT_TOGGLE_ROUND_DESCRIPTION =
33
- "pi.ccstyle.compact-assistant-toggle-round";
33
+ "pi-one-ui.compact-assistant-toggle-round";
34
34
  export const ASSISTANT_REENTRY_DESCRIPTION =
35
- "pi.ccstyle.compact-assistant-reentry";
35
+ "pi-one-ui.compact-assistant-reentry";
36
36
  export const ASSISTANT_SET_EXPANDED_KEY = Symbol.for(
37
37
  ASSISTANT_SET_EXPANDED_DESCRIPTION,
38
38
  );
@@ -43,54 +43,54 @@ export const ASSISTANT_REENTRY_KEY = Symbol.for(ASSISTANT_REENTRY_DESCRIPTION);
43
43
 
44
44
  // ── 工具渲染补丁 ──
45
45
  export const GLOBAL_TOOL_RENDER_PATCH = Symbol.for(
46
- "pi.ccstyle.global-tool-render-patch",
46
+ "pi-one-ui.global-tool-render-patch",
47
47
  );
48
48
  export const COMPONENT_TOOL_RENDER_MODE = Symbol.for(
49
- "pi.ccstyle.component-tool-render-mode",
49
+ "pi-one-ui.component-tool-render-mode",
50
50
  );
51
51
  export const TOOL_EXPANDED_BACKGROUND_PATCH = Symbol.for(
52
- "pi.ccstyle.tool-expanded-background-patch",
52
+ "pi-one-ui.tool-expanded-background-patch",
53
53
  );
54
54
 
55
55
  // ── 工具 updateDisplay 跨 toggle 重建缓存 ──
56
56
  export const TOGGLE_RENDER_CACHE_PATCH = Symbol.for(
57
- "pi.ccstyle.toggle-render-cache-patch",
57
+ "pi-one-ui.toggle-render-cache-patch",
58
58
  );
59
59
 
60
60
  // ── 消息组件补丁 ──
61
61
  export const MESSAGE_DISPLAY_PATCH = Symbol.for(
62
- "pi.ccstyle.message-display-patch",
62
+ "pi-one-ui.message-display-patch",
63
63
  );
64
64
 
65
65
  // ── 压缩渲染补丁(/reload 残留 deactivate 用) ──
66
66
  export const GLOBAL_COMPACTION_RENDER_PATCH = Symbol.for(
67
- "pi.ccstyle.compaction-render-patch",
67
+ "pi-one-ui.compaction-render-patch",
68
68
  );
69
69
 
70
70
  // ── 工具分组 ──
71
71
  export const TOOL_GROUPING_PATCH_KEY = Symbol.for(
72
- "pi.ccstyle.tool-grouping-patch",
72
+ "pi-one-ui.tool-grouping-patch",
73
73
  );
74
74
  export const TOOL_GROUPING_PARENT_KEY = Symbol.for(
75
- "pi.ccstyle.tool-grouping-parent",
75
+ "pi-one-ui.tool-grouping-parent",
76
76
  );
77
77
  export const TOOL_GROUPING_GENERATION_KEY = Symbol.for(
78
- "pi.ccstyle.tool-grouping-generation",
78
+ "pi-one-ui.tool-grouping-generation",
79
79
  );
80
80
 
81
81
  // ── 鼠标交互 ──
82
- export const TOOL_MOUSE_OWNER_KEY = Symbol.for("pi.ccstyle.tool-mouse-owner");
83
- export const TOOL_MOUSE_TUI_SLOT = Symbol.for("pi.ccstyle.tool-mouse-tui");
82
+ export const TOOL_MOUSE_OWNER_KEY = Symbol.for("pi-one-ui.tool-mouse-owner");
83
+ export const TOOL_MOUSE_TUI_SLOT = Symbol.for("pi-one-ui.tool-mouse-tui");
84
84
  export const SCROLL_BUTTON_STATE_SLOT = Symbol.for(
85
- "pi.ccstyle.scroll-button-state",
85
+ "pi-one-ui.scroll-button-state",
86
86
  );
87
87
  export const FLUSH_DOCKED_BASH_PATCH = Symbol.for(
88
- "pi.ccstyle.flush-docked-bash-patch",
88
+ "pi-one-ui.flush-docked-bash-patch",
89
89
  );
90
- export const TOOL_HOVER_STATE_KEY = Symbol.for("pi.ccstyle.tool-hover-state");
90
+ export const TOOL_HOVER_STATE_KEY = Symbol.for("pi-one-ui.tool-hover-state");
91
91
 
92
92
  // ── rich diff 组件标记 ──
93
- export const RICH_DIFF_COMPONENT = Symbol.for("pi.ccstyle.rich-diff-component");
93
+ export const RICH_DIFF_COMPONENT = Symbol.for("pi-one-ui.rich-diff-component");
94
94
 
95
95
  // ── PatchRegistry:所有权生命周期统一收口 ──
96
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-one-ui",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "A single, customizable Pi UI package with layout-oriented Context and Shell rendering for Pi.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -44,9 +44,9 @@
44
44
  "jiti": "^2.7.0"
45
45
  },
46
46
  "peerDependencies": {
47
- "@earendil-works/pi-ai": ">=0.84.0",
48
- "@earendil-works/pi-coding-agent": ">=0.84.0",
49
- "@earendil-works/pi-tui": ">=0.84.0"
47
+ "@earendil-works/pi-ai": ">=0.99.0",
48
+ "@earendil-works/pi-coding-agent": ">=0.99.0",
49
+ "@earendil-works/pi-tui": ">=0.99.0"
50
50
  },
51
51
  "engines": {
52
52
  "node": ">=22.19.0"
@@ -62,9 +62,9 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "@biomejs/biome": "^2.5.5",
65
- "@earendil-works/pi-ai": "^0.84.0",
66
- "@earendil-works/pi-coding-agent": "^0.84.0",
67
- "@earendil-works/pi-tui": "^0.84.0",
65
+ "@earendil-works/pi-ai": "^0.99.1",
66
+ "@earendil-works/pi-coding-agent": "^0.99.1",
67
+ "@earendil-works/pi-tui": "^0.99.1",
68
68
  "@types/node": "^26.1.1",
69
69
  "typescript": "^6.0.3",
70
70
  "vitest": "^4.1.11"