pi-zentui 0.20.0 → 0.20.2

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
@@ -441,6 +441,7 @@ Default config values — copy this and change any value you want:
441
441
  - `editorAccent` styles Editor and User-message accent rails and the labeled message label.
442
442
  - `editorPrompt` styles the `opencode-copy-friendly` Editor prompt glyph. Omit it to use `editorAccent`, then the default accent fallback.
443
443
  - `editorBorder` styles the `framed` and `framed-copy-friendly` previous-message top/bottom borders and the active editor in static border color mode; the border glyph stays `─`.
444
+ - `editorGitBranch` and `editorThinkingMax` are optional editor-owned overrides, omitted above so source-specific and adaptive defaults remain active. `editorGitBranch` owns the minimalist Editor branch color independently from Footer `gitBranch`; where Zentui resolves configured thinking colors, `max` falls back through `editorThinkingXhigh` and then `editorThinking`.
444
445
  - `editorModel`, `editorProvider`, and `editorThinking*` style the editor metadata. `editorThinking` applies to every non-`off` thinking level unless a level-specific key is set.
445
446
 
446
447
  Tip: with `opencode-copy-friendly`, setting Pi's `editorPaddingX` to `1` in `~/.pi/agent/settings.json` keeps a small left gutter without copying a rail glyph.
@@ -314,6 +314,7 @@ export type PolishedTuiColors = {
314
314
  editorAccent?: ColorSpec;
315
315
  editorPrompt?: ColorSpec;
316
316
  editorBorder?: ColorSpec;
317
+ editorGitBranch?: ColorSpec;
317
318
  editorModel?: ColorSpec;
318
319
  editorProvider?: ColorSpec;
319
320
  editorThinking?: ColorSpec;
@@ -322,6 +323,7 @@ export type PolishedTuiColors = {
322
323
  editorThinkingMedium?: ColorSpec;
323
324
  editorThinkingHigh?: ColorSpec;
324
325
  editorThinkingXhigh?: ColorSpec;
326
+ editorThinkingMax?: ColorSpec;
325
327
  workingLineLow?: ColorSpec;
326
328
  workingLineMid?: ColorSpec;
327
329
  workingLineHigh?: ColorSpec;
@@ -709,6 +711,7 @@ function normalizeColors(record: Record<string, unknown>): Partial<PolishedTuiCo
709
711
  editorAccent: colorValue(record, "editorAccent"),
710
712
  editorPrompt: colorValue(record, "editorPrompt"),
711
713
  editorBorder: colorValue(record, "editorBorder"),
714
+ editorGitBranch: colorValue(record, "editorGitBranch"),
712
715
  editorModel: colorValue(record, "editorModel"),
713
716
  editorProvider: colorValue(record, "editorProvider"),
714
717
  editorThinking: colorValue(record, "editorThinking"),
@@ -717,6 +720,7 @@ function normalizeColors(record: Record<string, unknown>): Partial<PolishedTuiCo
717
720
  editorThinkingMedium: colorValue(record, "editorThinkingMedium"),
718
721
  editorThinkingHigh: colorValue(record, "editorThinkingHigh"),
719
722
  editorThinkingXhigh: colorValue(record, "editorThinkingXhigh"),
723
+ editorThinkingMax: colorValue(record, "editorThinkingMax"),
720
724
  workingLineLow: colorValue(record, "workingLineLow"),
721
725
  workingLineMid: colorValue(record, "workingLineMid"),
722
726
  workingLineHigh: colorValue(record, "workingLineHigh"),
@@ -1425,13 +1429,20 @@ export function mergeConfig(parsed: unknown): PolishedTuiConfig {
1425
1429
  const config = isRecord(parsed) ? parsed : {};
1426
1430
  const iconsRecord = recordValue(config.icons);
1427
1431
  const colorsRecord = recordValue(config.colors);
1432
+ const colors = normalizeColors(colorsRecord);
1428
1433
  const canonical: ZentuiConfig = {
1429
1434
  projectRefreshIntervalMs: parseProjectRefreshIntervalMs(config.projectRefreshIntervalMs),
1430
1435
  icons: resolveConfiguredIcons(
1431
1436
  normalizeIconMode(iconsRecord.mode),
1432
1437
  normalizeIconOverrides(iconsRecord),
1433
1438
  ),
1434
- colors: { ...defaultConfig.colors, ...normalizeColors(colorsRecord) },
1439
+ colors: {
1440
+ ...defaultConfig.colors,
1441
+ ...colors,
1442
+ ...(colors.editorGitBranch === undefined && colors.gitBranch !== undefined
1443
+ ? { editorGitBranch: colors.gitBranch }
1444
+ : {}),
1445
+ },
1435
1446
  components: resolveComponents(config),
1436
1447
  };
1437
1448
  const view = compatibilityView(canonical);
@@ -132,6 +132,12 @@ function editorThinkingStyle(config: ZentuiConfig, level: string): string | unde
132
132
  return config.colors.editorThinkingHigh ?? config.colors.editorThinking;
133
133
  case "xhigh":
134
134
  return config.colors.editorThinkingXhigh ?? config.colors.editorThinking;
135
+ case "max":
136
+ return (
137
+ config.colors.editorThinkingMax ??
138
+ config.colors.editorThinkingXhigh ??
139
+ config.colors.editorThinking
140
+ );
135
141
  default:
136
142
  return config.colors.editorThinking;
137
143
  }
@@ -21,6 +21,21 @@ import {
21
21
  safeThemeFg,
22
22
  } from "./style";
23
23
 
24
+ const MINIMALIST_MODEL_FALLBACK = {
25
+ theme: "syntaxKeyword",
26
+ terminal: "bold purple",
27
+ };
28
+ const MINIMALIST_THINKING_FALLBACK = { theme: "warning", terminal: "bold yellow" };
29
+ const MINIMALIST_ADAPTIVE_TERMINAL_THINKING_FALLBACKS: Record<string, string> = {
30
+ minimal: "bright-black",
31
+ low: "blue",
32
+ medium: "cyan",
33
+ high: "yellow",
34
+ xhigh: "red",
35
+ max: "bright-red",
36
+ };
37
+ const MINIMALIST_BRANCH_FALLBACK = { theme: "bold syntaxKeyword", terminal: "bold blue" };
38
+
24
39
  export type MinimalistEditorMetadata = {
25
40
  cwd: string;
26
41
  projectRoot?: string;
@@ -100,6 +115,12 @@ function thinkingStyle(config: ZentuiConfig, level: string): string | undefined
100
115
  return config.colors.editorThinkingHigh ?? config.colors.editorThinking;
101
116
  case "xhigh":
102
117
  return config.colors.editorThinkingXhigh ?? config.colors.editorThinking;
118
+ case "max":
119
+ return (
120
+ config.colors.editorThinkingMax ??
121
+ config.colors.editorThinkingXhigh ??
122
+ config.colors.editorThinking
123
+ );
103
124
  default:
104
125
  return config.colors.editorThinking;
105
126
  }
@@ -155,6 +176,7 @@ function renderTopRight(
155
176
  config: ZentuiConfig,
156
177
  availableWidth: number,
157
178
  renderBorder: (text: string) => string,
179
+ renderThinking: (text: string) => string,
158
180
  ): string {
159
181
  const source = config.components.editor.colorSource;
160
182
  const parts: string[] = [];
@@ -173,22 +195,14 @@ function renderTopRight(
173
195
  uiTheme,
174
196
  source,
175
197
  config.colors.editorModel,
176
- EDITOR_ACCENT_FALLBACK,
198
+ MINIMALIST_MODEL_FALLBACK,
177
199
  model,
178
200
  ),
179
201
  );
180
202
  }
181
203
  const thinking = sanitizeEditorMetadataText(metadata.thinkingLevel ?? "");
182
204
  if (thinking && thinking.toLowerCase() !== "off") {
183
- parts.push(
184
- renderStyleForSourceOrFallback(
185
- uiTheme,
186
- source,
187
- thinkingStyle(config, thinking),
188
- "muted",
189
- thinking,
190
- ),
191
- );
205
+ parts.push(renderThinking(thinking));
192
206
  }
193
207
  if (metadata.contextPercent !== undefined && Number.isFinite(metadata.contextPercent)) {
194
208
  const percent = Math.round(Math.max(0, Math.min(999, metadata.contextPercent)));
@@ -236,7 +250,15 @@ function renderBottomLeft(
236
250
  const source = config.components.editor.colorSource;
237
251
  const branch = sanitizeEditorMetadataText(metadata.branch ?? "");
238
252
  const parts = branch
239
- ? [renderStyleForSource(uiTheme, source, config.colors.gitBranch, branch)]
253
+ ? [
254
+ renderStyleForSourceOrFallback(
255
+ uiTheme,
256
+ source,
257
+ config.colors.editorGitBranch,
258
+ MINIMALIST_BRANCH_FALLBACK,
259
+ branch,
260
+ ),
261
+ ]
240
262
  : [];
241
263
  if (metadata.dirty) {
242
264
  parts.push(renderStyleForSource(uiTheme, source, config.colors.gitStatus, "*"));
@@ -354,18 +376,31 @@ export function renderMinimalistFrame({
354
376
  }: MinimalistFrameOptions): string[] {
355
377
  if (width <= 4) return clampLines(editorLines, width);
356
378
  const contentWidth = Math.max(0, width - 4);
379
+ const source = config.components.editor.colorSource;
380
+ const adaptive = config.components.editor.borderColorMode === "adaptive";
381
+ const thinking = sanitizeEditorMetadataText(metadata.thinkingLevel ?? "");
382
+ const activeThinking = thinking && thinking.toLowerCase() !== "off" ? thinking : "";
357
383
  const renderStaticBorder = (text: string) =>
358
384
  renderStyleForSourceOrFallback(
359
385
  uiTheme,
360
- config.components.editor.colorSource,
386
+ source,
361
387
  config.colors.editorBorder,
362
388
  EDITOR_BORDER_FALLBACK,
363
389
  text,
364
390
  );
391
+ const terminalAdaptiveThinkingStyle = activeThinking
392
+ ? (thinkingStyle(config, activeThinking) ??
393
+ MINIMALIST_ADAPTIVE_TERMINAL_THINKING_FALLBACKS[activeThinking.toLowerCase()] ??
394
+ MINIMALIST_THINKING_FALLBACK.terminal)
395
+ : undefined;
365
396
  const renderBorder = (text: string) => {
366
- if (config.components.editor.borderColorMode !== "adaptive" || !borderColor) {
367
- return renderStaticBorder(text);
397
+ if (!adaptive) return renderStaticBorder(text);
398
+ if (source === "terminal") {
399
+ return terminalAdaptiveThinkingStyle
400
+ ? renderStyleForSource(uiTheme, source, terminalAdaptiveThinkingStyle, text)
401
+ : renderStaticBorder(text);
368
402
  }
403
+ if (!borderColor) return renderStaticBorder(text);
369
404
  try {
370
405
  const rendered = borderColor(text);
371
406
  return typeof rendered === "string" ? rendered : renderStaticBorder(text);
@@ -373,6 +408,15 @@ export function renderMinimalistFrame({
373
408
  return renderStaticBorder(text);
374
409
  }
375
410
  };
411
+ const renderStaticThinking = (text: string) =>
412
+ renderStyleForSourceOrFallback(
413
+ uiTheme,
414
+ source,
415
+ thinkingStyle(config, text),
416
+ MINIMALIST_THINKING_FALLBACK,
417
+ text,
418
+ );
419
+ const renderThinking = adaptive ? renderBorder : renderStaticThinking;
376
420
  const separator = safeThemeFg(uiTheme, "muted", " · ");
377
421
  const viewportLabel = (direction: "above" | "below", count: string | undefined) => {
378
422
  if (!count || !/^[1-9]\d*$/.test(count)) return "";
@@ -391,7 +435,7 @@ export function renderMinimalistFrame({
391
435
  width,
392
436
  left: topLeft,
393
437
  leftFallbacks: topFallbacks,
394
- right: renderTopRight(metadata, uiTheme, config, topRightBudget, renderBorder),
438
+ right: renderTopRight(metadata, uiTheme, config, topRightBudget, renderBorder, renderThinking),
395
439
  leftCorner: "╭",
396
440
  rightCorner: "╮",
397
441
  renderBorder,
@@ -146,6 +146,7 @@ const themeColorTokens = new Set<ThemeColor>([
146
146
  "thinkingMedium",
147
147
  "thinkingHigh",
148
148
  "thinkingXhigh",
149
+ "thinkingMax",
149
150
  "bashMode",
150
151
  ]);
151
152
 
@@ -7,6 +7,7 @@ import type {
7
7
  WorkingLineTextAnimation,
8
8
  ZentuiConfig,
9
9
  } from "./config";
10
+ import { formatCount } from "./format";
10
11
  import {
11
12
  isSafeSgrStylePrefix,
12
13
  isSupportedColorSpec,
@@ -561,7 +562,7 @@ export function formatWorkingLineTokens(
561
562
  ) {
562
563
  return undefined;
563
564
  }
564
- return `↑${tokens.input} ↓${tokens.output}`;
565
+ return `↑${formatCount(tokens.input)} ↓${formatCount(tokens.output)}`;
565
566
  }
566
567
 
567
568
  function truncateWithEllipsis(value: string, capacity: number): string {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-zentui",
3
- "version": "0.20.0",
3
+ "version": "0.20.2",
4
4
  "description": "A Starship-inspired statusline and Opencode-style TUI for Pi.",
5
5
  "type": "module",
6
6
  "license": "MIT",