pi-one-ui 0.5.1 → 0.6.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.
@@ -11,8 +11,7 @@ import { formatCount } from "../../shared/format.ts";
11
11
  import {
12
12
  isSafeSgrStylePrefix,
13
13
  isSupportedColorSpec,
14
- renderStyleForSourceOrFallback,
15
- type SourceStyleFallback,
14
+ renderThemeStyleOrFallback,
16
15
  type ThemeLike,
17
16
  } from "../../shared/style.ts";
18
17
  import { PI_WORKING_LINE_MESSAGES } from "./working-line-messages.ts";
@@ -40,13 +39,10 @@ export const MAX_WORKING_LINE_STYLE_TOKENS = 4;
40
39
  export const MAX_WORKING_LINE_STYLE_CODE_UNITS = 48;
41
40
  export const MAX_WORKING_LINE_ENTRIES_EXAMINED = 256;
42
41
 
43
- const WORKING_LINE_FALLBACKS: Record<
44
- "low" | "mid" | "high",
45
- SourceStyleFallback
46
- > = {
47
- low: { theme: "dim", terminal: "bright-black" },
48
- mid: { theme: "muted", terminal: "cyan" },
49
- high: { theme: "bold accent", terminal: "bold cyan" },
42
+ const WORKING_LINE_FALLBACKS: Record<"low" | "mid" | "high", ColorSpec> = {
43
+ low: "dim",
44
+ mid: "muted",
45
+ high: "bold accent",
50
46
  };
51
47
  const CLASSIC_PADDING_CELLS = 4;
52
48
  const CLASSIC_HIGHLIGHT_HALF_WIDTH = 4;
@@ -478,9 +474,8 @@ function renderTier(
478
474
  tier: Tier,
479
475
  text: string,
480
476
  ): string {
481
- return renderStyleForSourceOrFallback(
477
+ return renderThemeStyleOrFallback(
482
478
  theme,
483
- config.colorSource,
484
479
  normalizeWorkingLineStyleSpec(styleForTier(colors, tier)),
485
480
  WORKING_LINE_FALLBACKS[tier],
486
481
  text,
@@ -490,13 +485,11 @@ function renderTier(
490
485
  /** Resolve the fixed, nonanimated high-tier style used by persisted Turn summaries. */
491
486
  export function renderWorkingLineHigh(
492
487
  theme: ThemeLike,
493
- colorSource: WorkingLineComponentConfig["colorSource"],
494
488
  style: ColorSpec | undefined,
495
489
  text: string,
496
490
  ): string {
497
- return renderStyleForSourceOrFallback(
491
+ return renderThemeStyleOrFallback(
498
492
  theme,
499
- colorSource,
500
493
  normalizeWorkingLineStyleSpec(style),
501
494
  WORKING_LINE_FALLBACKS.high,
502
495
  text,
@@ -505,13 +498,11 @@ export function renderWorkingLineHigh(
505
498
 
506
499
  export function snapshotWorkingLineHighStyle(
507
500
  theme: ThemeLike,
508
- config: WorkingLineComponentConfig,
509
501
  colors: PolishedTuiColors,
510
502
  ): string {
511
503
  const sentinel = "\u{f0000}";
512
504
  const rendered = renderWorkingLineHigh(
513
505
  theme,
514
- config.colorSource,
515
506
  colors.workingLineHigh,
516
507
  sentinel,
517
508
  );
@@ -1334,16 +1325,11 @@ export class WorkingLineController {
1334
1325
  config.spinner,
1335
1326
  config.spinnerIntervalMs,
1336
1327
  ...(config.textAnimation === "disabled"
1337
- ? [
1338
- config.textAnimation,
1339
- config.colorSource,
1340
- rootConfig.colors.workingLineMid,
1341
- ]
1328
+ ? [config.textAnimation, rootConfig.colors.workingLineMid]
1342
1329
  : [
1343
1330
  config.textIntervalMs,
1344
1331
  config.textAnimation,
1345
1332
  config.animateSpinnerColor,
1346
- config.colorSource,
1347
1333
  rootConfig.colors.workingLineLow,
1348
1334
  rootConfig.colors.workingLineMid,
1349
1335
  rootConfig.colors.workingLineHigh,
@@ -2,7 +2,6 @@ import { homedir, hostname, userInfo } from "node:os";
2
2
  import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
3
3
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
4
4
  import type {
5
- ColorSource,
6
5
  ColorSpec,
7
6
  ContextStyle,
8
7
  ContextThresholds,
@@ -18,7 +17,7 @@ import {
18
17
  resolvePackageIcon,
19
18
  resolveRuntimeSymbol,
20
19
  } from "./icons.ts";
21
- import { renderStyleForSource } from "./style.ts";
20
+ import { renderThemeStyle } from "./style.ts";
22
21
 
23
22
  /**
24
23
  * Starship `git_commit` style — render a short hash, optionally with an
@@ -31,7 +30,6 @@ export function formatGitCommitSegment(
31
30
  theme: Pick<Theme, "fg">,
32
31
  commit: GitCommitInfo | undefined,
33
32
  config: { hashLength: number; onlyDetached: boolean; showTag: boolean },
34
- colorSource: ColorSource,
35
33
  style: ColorSpec,
36
34
  ): string {
37
35
  if (!commit?.oid) return "";
@@ -41,7 +39,7 @@ export function formatGitCommitSegment(
41
39
  const tag = config.showTag && commit.tag ? commit.tag : "";
42
40
  if (!hash && !tag) return "";
43
41
  const label = [hash, tag].filter(Boolean).join(" ");
44
- return renderStyleForSource(theme, colorSource, style, label);
42
+ return renderThemeStyle(theme, style, label);
45
43
  }
46
44
 
47
45
  /**
@@ -55,7 +53,6 @@ export function formatGitMetricsSegment(
55
53
  theme: Pick<Theme, "fg">,
56
54
  metrics: GitMetricsInfo | null | undefined,
57
55
  config: { onlyNonzero: boolean },
58
- colorSource: ColorSource,
59
56
  addedStyle: ColorSpec,
60
57
  deletedStyle: ColorSpec,
61
58
  ): string {
@@ -65,19 +62,10 @@ export function formatGitMetricsSegment(
65
62
  if (!showAdded && !showDeleted) return "";
66
63
  const parts: string[] = [];
67
64
  if (showAdded) {
68
- parts.push(
69
- renderStyleForSource(theme, colorSource, addedStyle, `+${metrics.added}`),
70
- );
65
+ parts.push(renderThemeStyle(theme, addedStyle, `+${metrics.added}`));
71
66
  }
72
67
  if (showDeleted) {
73
- parts.push(
74
- renderStyleForSource(
75
- theme,
76
- colorSource,
77
- deletedStyle,
78
- `−${metrics.deleted}`,
79
- ),
80
- );
68
+ parts.push(renderThemeStyle(theme, deletedStyle, `−${metrics.deleted}`));
81
69
  }
82
70
  return parts.join(" ");
83
71
  }
@@ -428,13 +416,12 @@ export function formatRuntimeSegment(
428
416
  theme: Pick<Theme, "fg">,
429
417
  runtime: RuntimeInfo | undefined,
430
418
  prefixStyle: ColorSpec,
431
- colorSource: ColorSource,
432
419
  mode: IconMode = "auto",
433
420
  ): string {
434
421
  if (!runtime) return "";
435
422
  const symbol = resolveRuntimeSymbol(runtime.name, runtime.symbol, mode);
436
423
  const label = runtime.version ? `${symbol} ${runtime.version}` : symbol;
437
- return `${renderStyleForSource(theme, colorSource, prefixStyle, "via")} ${renderStyleForSource(theme, colorSource, runtime.style, label)}`;
424
+ return `${renderThemeStyle(theme, prefixStyle, "via")} ${renderThemeStyle(theme, runtime.style, label)}`;
438
425
  }
439
426
 
440
427
  /**
@@ -450,7 +437,6 @@ export function formatRuntimeSegment(
450
437
  export function formatPackageVersionSegment(
451
438
  theme: Pick<Theme, "fg">,
452
439
  pkg: PackageVersionResult | undefined,
453
- colorSource: ColorSource,
454
440
  mode: IconMode = "auto",
455
441
  configuredIcon: string = "",
456
442
  versionStyle: ColorSpec = "208",
@@ -458,7 +444,7 @@ export function formatPackageVersionSegment(
458
444
  if (!pkg) return "";
459
445
  const icon = resolvePackageIcon(configuredIcon, mode);
460
446
  const label = `${icon} ${pkg.version}`;
461
- return `${renderStyleForSource(theme, colorSource, "", "is")} ${renderStyleForSource(theme, colorSource, versionStyle, label)}`;
447
+ return `${renderThemeStyle(theme, "", "is")} ${renderThemeStyle(theme, versionStyle, label)}`;
462
448
  }
463
449
 
464
450
  export type FormatCwdOptions = {
@@ -1,5 +1,5 @@
1
1
  import type { ThemeColor } from "@earendil-works/pi-coding-agent";
2
- import type { ColorSource, ColorSpec } from "../app/config/shell.ts";
2
+ import type { ColorSpec } from "../app/config/shell.ts";
3
3
 
4
4
  type ThemeLike = {
5
5
  fg(color: string, text: string): string;
@@ -10,25 +10,15 @@ type ThemeLike = {
10
10
 
11
11
  export type { ThemeLike };
12
12
 
13
- export const EDITOR_ACCENT_STYLE = "blue";
14
- export const EDITOR_BORDER_STYLE = "bright-black";
15
13
  export const MAX_SAFE_SGR_PREFIX_CODE_UNITS = 96;
16
14
  export const MAX_SAFE_SGR_PREFIX_SEQUENCES = 4;
17
15
 
18
- export type SourceStyleFallback = {
19
- theme: ColorSpec;
20
- terminal: ColorSpec;
21
- };
22
-
23
- export const EDITOR_ACCENT_FALLBACK: SourceStyleFallback = {
24
- theme: "accent",
25
- terminal: EDITOR_ACCENT_STYLE,
26
- };
27
-
28
- export const EDITOR_BORDER_FALLBACK: SourceStyleFallback = {
29
- theme: "borderMuted",
30
- terminal: EDITOR_BORDER_STYLE,
31
- };
16
+ /**
17
+ * 单模式下的默认回退色。Editor 与用户消息的强调/边框默认跟随主题
18
+ * 语义色(unconfigured 时与 Pi 原生默认一致)。
19
+ */
20
+ export const EDITOR_ACCENT_FALLBACK: ColorSpec = "accent";
21
+ export const EDITOR_BORDER_FALLBACK: ColorSpec = "borderMuted";
32
22
 
33
23
  function isHexColor(value: string): boolean {
34
24
  return /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value);
@@ -80,6 +70,10 @@ const terminalStyleModifiers = new Map([
80
70
  ["underline", 4],
81
71
  ]);
82
72
 
73
+ /**
74
+ * ANSI 色名到主题语义 token 的映射:theme 语义下写 `red` 得到主题错误色,
75
+ * 写 `green` 得到主题成功色,保证默认值与主题观感一致。
76
+ */
83
77
  const themeColorNameMap = new Map([
84
78
  ["red", "error"],
85
79
  ["bright-red", "error"],
@@ -168,6 +162,10 @@ function terminalColorToAnsi(
168
162
  return undefined;
169
163
  }
170
164
 
165
+ /**
166
+ * 显式终端 token:hex、0-255 数字、fg:/bg: 前缀。这类值在 theme 语义下
167
+ * 仍直接输出固定终端色,不经过主题。
168
+ */
171
169
  function isExplicitTerminalColorToken(token: string): boolean {
172
170
  const normalized = token.toLowerCase();
173
171
  if (normalized.startsWith("fg:") || normalized.startsWith("bg:")) return true;
@@ -351,19 +349,12 @@ export function renderTerminalStyle(style: string, text: string): string {
351
349
  }
352
350
 
353
351
  /**
354
- * Apply Starship-style terminal styling first, falling back to Pi theme tokens for
355
- * legacy config values such as "accent" or "syntaxKeyword".
352
+ * 单模式颜色渲染:配置值按 theme 语义解释。
353
+ * - 含显式终端 token(hex/数字/fg:/bg:)→ 直接输出固定终端色;
354
+ * - 其余按主题 token 解析(ANSI 色名映射到语义 token,如 red→error),
355
+ * 修饰符通过主题的 bold/italic/underline 应用;
356
+ * - 空值原样返回。
356
357
  */
357
- export function renderStyle(
358
- theme: ThemeLike,
359
- style: ColorSpec,
360
- text: string,
361
- ): string {
362
- if (style.trim() === "") return text;
363
- const styled = renderTerminalStyle(style, text);
364
- return styled === text ? colorize(theme, style, text) : styled;
365
- }
366
-
367
358
  export function renderThemeStyle(
368
359
  theme: ThemeLike,
369
360
  style: ColorSpec,
@@ -380,16 +371,6 @@ export function renderThemeStyle(
380
371
  return safeThemeFg(theme, color, applyThemeModifiers(theme, tokens, text));
381
372
  }
382
373
 
383
- function renderStyleStrict(
384
- theme: ThemeLike,
385
- style: ColorSpec,
386
- text: string,
387
- ): string {
388
- if (style.trim() === "") return text;
389
- const styled = renderTerminalStyle(style, text);
390
- return styled === text ? theme.fg(style, text) : styled;
391
- }
392
-
393
374
  function renderThemeStyleStrict(
394
375
  theme: ThemeLike,
395
376
  style: ColorSpec,
@@ -404,32 +385,30 @@ function renderThemeStyleStrict(
404
385
  return theme.fg(color, applyThemeModifiers(theme, tokens, text));
405
386
  }
406
387
 
407
- export function renderStyleForSource(
388
+ /**
389
+ * 配置优先、未配置回退默认的渲染入口(fallback 为单一主题语义色)。
390
+ */
391
+ export function renderThemeStyleOrFallback(
408
392
  theme: ThemeLike,
409
- source: ColorSource,
410
- style: ColorSpec,
393
+ style: ColorSpec | undefined,
394
+ fallback: ColorSpec,
411
395
  text: string,
412
396
  ): string {
413
- return source === "terminal"
414
- ? renderStyle(theme, style, text)
415
- : renderThemeStyle(theme, style, text);
397
+ return renderThemeStyle(theme, style ?? fallback, text);
416
398
  }
417
399
 
418
- export function renderStyleForSourceOrFallback(
400
+ export function renderThemeStyleOrFallbackStrict(
419
401
  theme: ThemeLike,
420
- source: ColorSource,
421
402
  style: ColorSpec | undefined,
422
- fallback: ColorSpec | SourceStyleFallback,
403
+ fallback: ColorSpec,
423
404
  text: string,
424
405
  ): string {
425
- const fallbackStyle =
426
- typeof fallback === "string" ? fallback : fallback[source];
427
- return renderStyleForSource(theme, source, style ?? fallbackStyle, text);
406
+ return renderThemeStyleStrict(theme, style ?? fallback, text);
428
407
  }
429
408
 
430
409
  /**
431
410
  * 主题画板存在性探测:pi Theme 暴露 fgColors Map;测试或其他实现可
432
- * 提供 hasThemeToken 回调,缺失时保守按“无该 token”处理。
411
+ * 提供 hasThemeToken 回调,缺失时保守按"无该 token"处理。
433
412
  */
434
413
  function themeHasToken(theme: ThemeLike, token: string): boolean {
435
414
  if (
@@ -447,25 +426,21 @@ function themeHasToken(theme: ThemeLike, token: string): boolean {
447
426
 
448
427
  /**
449
428
  * 颜色统一渲染入口(配置优先,其次主题 token,最后原生默认):
450
- * - 配置了 spec:按 colorSource 现有语义解释(theme=token/hex,terminal=终端色);
451
- * - 未配置且 theme 源:主题定义了 themeToken 时用主题色(可指向 vars 变量),
452
- * 否则回落原生默认 themeDefault;
453
- * - 未配置且 terminal 源:使用固定终端默认 terminalDefault。
429
+ * - 配置了 spec:按 theme 语义解释(token/hex/数字/fg:/bg:);
430
+ * - 未配置:主题定义了 themeToken(cwd/editorModel/editorBorder)时用主题色,
431
+ * 否则回落原生默认 themeDefault。
454
432
  */
455
433
  export function renderSourceColor(
456
434
  theme: ThemeLike,
457
- source: ColorSource,
458
435
  spec: ColorSpec | undefined,
459
436
  themeToken: string,
460
437
  themeDefault: ColorSpec,
461
438
  text: string,
462
439
  ): string {
463
440
  if (typeof spec === "string" && spec.trim() !== "") {
464
- return renderStyleForSource(theme, source, spec, text);
441
+ return renderThemeStyle(theme, spec, text);
465
442
  }
466
- // 未配置时:theme 源优先主题专有 token,terminal 源与主题缺 token 时
467
- // 都回落 Pi 原生默认(主题语义色),保证两种 colorSource 默认观感一致。
468
- if (source !== "terminal" && themeHasToken(theme, themeToken)) {
443
+ if (themeHasToken(theme, themeToken)) {
469
444
  try {
470
445
  return theme.fg(themeToken, text);
471
446
  } catch {
@@ -475,54 +450,6 @@ export function renderSourceColor(
475
450
  return renderThemeStyle(theme, themeDefault, text);
476
451
  }
477
452
 
478
- export function renderStyleForSourceOrFallbackStrict(
479
- theme: ThemeLike,
480
- source: ColorSource,
481
- style: ColorSpec | undefined,
482
- fallback: ColorSpec | SourceStyleFallback,
483
- text: string,
484
- ): string {
485
- const fallbackStyle =
486
- typeof fallback === "string" ? fallback : fallback[source];
487
- const resolvedStyle = style ?? fallbackStyle;
488
- return source === "terminal"
489
- ? renderStyleStrict(theme, resolvedStyle, text)
490
- : renderThemeStyleStrict(theme, resolvedStyle, text);
491
- }
492
-
493
- export function renderEditorAccent(text: string): string {
494
- return renderTerminalStyle(EDITOR_ACCENT_STYLE, text);
495
- }
496
-
497
453
  export function renderEditorBorder(text: string): string {
498
- return renderTerminalStyle(EDITOR_BORDER_STYLE, text);
499
- }
500
-
501
- export function renderAccentLine(
502
- theme: ThemeLike,
503
- source: ColorSource,
504
- text: string,
505
- ): string {
506
- return renderStyleForSourceOrFallback(
507
- theme,
508
- source,
509
- undefined,
510
- EDITOR_ACCENT_FALLBACK,
511
- text,
512
- );
513
- }
514
-
515
- export function renderChromeBorder(
516
- theme: ThemeLike,
517
- source: ColorSource,
518
- terminalFallbackStyle: ColorSpec,
519
- text: string,
520
- ): string {
521
- return renderStyleForSourceOrFallback(
522
- theme,
523
- source,
524
- undefined,
525
- { theme: "borderMuted", terminal: terminalFallbackStyle },
526
- text,
527
- );
454
+ return renderTerminalStyle("bright-black", text);
528
455
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-one-ui",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
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",