pi-zentui 0.1.10 → 0.1.11

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
@@ -25,9 +25,9 @@ Zentui brings two popular aesthetics to Pi:
25
25
 
26
26
  ### Editor (Opencode-inspired)
27
27
 
28
- - Bordered input box with theme accent rail and thinking-level border color
28
+ - Bordered input box with configurable accent rail and border colors
29
29
  - Model name and provider displayed inside the editor frame
30
- - Thinking level indicator when enabled
30
+ - Configurable model, provider, and thinking-level indicator colors
31
31
  - Prompt-box-style user messages matching the ZentUI input chrome
32
32
 
33
33
  ### Git Status Icons
@@ -49,65 +49,65 @@ Zentui brings two popular aesthetics to Pi:
49
49
 
50
50
  Detects Starship Nerd Font runtime/language modules, uses the Starship Nerd Font symbols, and keeps Starship-style defaults such as `bold green` for Node.js. By default Zentui maps those styles through your active Pi theme; switch the Starship/footer color source to `terminal` in `/zentui` if you want your terminal colorscheme to supply the exact ANSI colors.
51
51
 
52
- | Runtime/language | Detection examples |
53
- | ---------------- | ------------------------------------------------------------ |
54
- | Buf | `buf.yaml`, `buf.gen.yaml`, `buf.work.yaml` |
55
- | Bun | `bun.lock`, `bun.lockb` |
56
- | C | `.c`, `.h` files |
57
- | C++ | `.cpp`, `.cc`, `.cxx`, `.hpp` files |
58
- | CMake | `CMakeLists.txt`, `CMakeCache.txt` |
59
- | COBOL | `.cbl`, `.cob` files |
60
- | Conda | `CONDA_DEFAULT_ENV` environment |
61
- | Crystal | `.cr` files, `shard.yml` |
62
- | Dart | `.dart` files, `pubspec.yaml`, `.dart_tool/` |
63
- | Deno | `deno.json`, `deno.jsonc`, `deno.lock` |
64
- | .NET | `.csproj`, `.fsproj`, `global.json`, `Directory.Build.*` |
65
- | Elixir | `mix.exs` |
66
- | Elm | `.elm` files, `elm.json`, `elm-stuff/` |
67
- | Erlang | `rebar.config`, `erlang.mk` |
68
- | Fennel | `.fnl` files |
69
- | Fortran | `.f`, `.f90`, `.f95`, `.f03`, `.f08`, `.f18`, `fpm.toml` |
70
- | Gleam | `.gleam` files, `gleam.toml` |
71
- | Go | `go.mod` |
72
- | Gradle | `build.gradle`, `build.gradle.kts`, `gradle/` |
73
- | Guix shell | `GUIX_ENVIRONMENT` environment |
74
- | Haskell | `.hs`, `.cabal`, `stack.yaml`, `cabal.project` |
75
- | Haxe | `.hx`, `.hxml`, `haxelib.json`, `.haxerc` |
76
- | Helm | `helmfile.yaml`, `Chart.yaml` |
77
- | Java | `.java-version` |
78
- | Julia | `.jl` files, `Project.toml`, `Manifest.toml` |
79
- | Kotlin | `.kt`, `.kts` files |
80
- | Lua | `.lua` files, `stylua.toml`, `.luarc.json`, `lua/` dir |
81
- | Maven | `pom.xml` |
82
- | Meson | `MESON_DEVENV=1` and `MESON_PROJECT_NAME` environment |
83
- | Mojo | `.mojo` files |
84
- | Nim | `.nim`, `.nims`, `.nimble`, `nim.cfg` |
85
- | Nix shell | `IN_NIX_SHELL=pure` or `IN_NIX_SHELL=impure` environment |
86
- | Node.js | `package.json`, `.nvmrc`, `.node-version` |
87
- | OCaml | `.opam`, `.ml`, `.mli`, `dune`, `_opam/`, `esy.lock/` |
88
- | Odin | `.odin` files |
89
- | OPA/Rego | `.rego` files |
90
- | Perl | `.pl`, `.pm`, `Makefile.PL`, `cpanfile`, `META.*` |
91
- | PHP | `composer.json` |
52
+ | Runtime/language | Detection examples |
53
+ | ---------------- | ------------------------------------------------------------- |
54
+ | Buf | `buf.yaml`, `buf.gen.yaml`, `buf.work.yaml` |
55
+ | Bun | `bun.lock`, `bun.lockb` |
56
+ | C | `.c`, `.h` files |
57
+ | C++ | `.cpp`, `.cc`, `.cxx`, `.hpp` files |
58
+ | CMake | `CMakeLists.txt`, `CMakeCache.txt` |
59
+ | COBOL | `.cbl`, `.cob` files |
60
+ | Conda | `CONDA_DEFAULT_ENV` environment |
61
+ | Crystal | `.cr` files, `shard.yml` |
62
+ | Dart | `.dart` files, `pubspec.yaml`, `.dart_tool/` |
63
+ | Deno | `deno.json`, `deno.jsonc`, `deno.lock` |
64
+ | .NET | `.csproj`, `.fsproj`, `global.json`, `Directory.Build.*` |
65
+ | Elixir | `mix.exs` |
66
+ | Elm | `.elm` files, `elm.json`, `elm-stuff/` |
67
+ | Erlang | `rebar.config`, `erlang.mk` |
68
+ | Fennel | `.fnl` files |
69
+ | Fortran | `.f`, `.f90`, `.f95`, `.f03`, `.f08`, `.f18`, `fpm.toml` |
70
+ | Gleam | `.gleam` files, `gleam.toml` |
71
+ | Go | `go.mod` |
72
+ | Gradle | `build.gradle`, `build.gradle.kts`, `gradle/` |
73
+ | Guix shell | `GUIX_ENVIRONMENT` environment |
74
+ | Haskell | `.hs`, `.cabal`, `stack.yaml`, `cabal.project` |
75
+ | Haxe | `.hx`, `.hxml`, `haxelib.json`, `.haxerc` |
76
+ | Helm | `helmfile.yaml`, `Chart.yaml` |
77
+ | Java | `.java-version` |
78
+ | Julia | `.jl` files, `Project.toml`, `Manifest.toml` |
79
+ | Kotlin | `.kt`, `.kts` files |
80
+ | Lua | `.lua` files, `stylua.toml`, `.luarc.json`, `lua/` dir |
81
+ | Maven | `pom.xml` |
82
+ | Meson | `MESON_DEVENV=1` and `MESON_PROJECT_NAME` environment |
83
+ | Mojo | `.mojo` files |
84
+ | Nim | `.nim`, `.nims`, `.nimble`, `nim.cfg` |
85
+ | Nix shell | `IN_NIX_SHELL=pure` or `IN_NIX_SHELL=impure` environment |
86
+ | Node.js | `package.json`, `.nvmrc`, `.node-version` |
87
+ | OCaml | `.opam`, `.ml`, `.mli`, `dune`, `_opam/`, `esy.lock/` |
88
+ | Odin | `.odin` files |
89
+ | OPA/Rego | `.rego` files |
90
+ | Perl | `.pl`, `.pm`, `Makefile.PL`, `cpanfile`, `META.*` |
91
+ | PHP | `composer.json` |
92
92
  | Pixi | `pixi.toml`, `pixi.lock`, `PIXI_ENVIRONMENT_NAME` environment |
93
- | Pulumi | `Pulumi.yaml`, `Pulumi.yml` |
94
- | PureScript | `.purs` files, `spago.dhall`, `spago.yaml`, `spago.lock` |
95
- | Python | `pyproject.toml`, `requirements.txt`, `setup.py`, `Pipfile` |
96
- | R | `.R`, `.Rmd`, `.Rproj`, `DESCRIPTION`, `.Rproj.user/` |
97
- | Raku | `.raku`, `.rakumod`, `.p6`, `.pm6`, `META6.json` |
98
- | Red | `.red`, `.reds` files |
99
- | Ruby | `Gemfile`, `.ruby-version` |
93
+ | Pulumi | `Pulumi.yaml`, `Pulumi.yml` |
94
+ | PureScript | `.purs` files, `spago.dhall`, `spago.yaml`, `spago.lock` |
95
+ | Python | `pyproject.toml`, `requirements.txt`, `setup.py`, `Pipfile` |
96
+ | R | `.R`, `.Rmd`, `.Rproj`, `DESCRIPTION`, `.Rproj.user/` |
97
+ | Raku | `.raku`, `.rakumod`, `.p6`, `.pm6`, `META6.json` |
98
+ | Red | `.red`, `.reds` files |
99
+ | Ruby | `Gemfile`, `.ruby-version` |
100
100
  | Rust | `Cargo.toml` |
101
- | Scala | `.scala`, `.sbt`, `build.sbt`, `.metals/` |
102
- | Solidity | `.sol` files |
103
- | Spack | `SPACK_ENV` environment |
104
- | Swift | `.swift` files, `Package.swift` |
105
- | Terraform | `.tf`, `.tfplan`, `.tfstate`, `.terraform/` |
106
- | Typst | `.typ` files, `template.typ` |
107
- | Vagrant | `Vagrantfile` |
108
- | V | `.v` files, `v.mod`, `vpkg.json` |
109
- | Xmake | `xmake.lua` |
110
- | Zig | `.zig` files, `build.zig` |
101
+ | Scala | `.scala`, `.sbt`, `build.sbt`, `.metals/` |
102
+ | Solidity | `.sol` files |
103
+ | Spack | `SPACK_ENV` environment |
104
+ | Swift | `.swift` files, `Package.swift` |
105
+ | Terraform | `.tf`, `.tfplan`, `.tfstate`, `.terraform/` |
106
+ | Typst | `.typ` files, `template.typ` |
107
+ | Vagrant | `Vagrantfile` |
108
+ | V | `.v` files, `v.mod`, `vpkg.json` |
109
+ | Xmake | `xmake.lua` |
110
+ | Zig | `.zig` files, `build.zig` |
111
111
 
112
112
  ## Install
113
113
 
@@ -121,73 +121,66 @@ pi install git:github.com/lmilojevicc/pi-zentui
121
121
 
122
122
  ## Config
123
123
 
124
- Zentui uses built-in defaults when no config file exists. User settings live at:
124
+ User config lives at `~/.pi/agent/zentui.json`. The file is optional: missing or invalid known values fall back to Zentui defaults, unknown keys are ignored at runtime, and `/zentui` currently patches only color-source settings.
125
125
 
126
- ```
127
- ~/.pi/agent/zentui.json
128
- ```
129
-
130
- Zentui treats this file as user-owned and compatibility-sensitive: invalid known values fall back to runtime defaults, unknown keys are ignored at runtime, and `/zentui` patches only the settings it changes instead of rewriting the whole file.
131
-
132
- ### Runtime defaults
126
+ Default config values — copy this and change any value you want:
133
127
 
134
128
  ```json
135
129
  {
136
- "projectRefreshIntervalMs": 30000,
137
- "icons": {
138
- "cwd": "󰝰",
139
- "git": "",
140
- "ahead": "↑",
141
- "behind": "↓",
142
- "diverged": "⇕",
143
- "conflicted": "=",
144
- "untracked": "?",
145
- "stashed": "$",
146
- "modified": "!",
147
- "staged": "+",
148
- "renamed": "»",
149
- "deleted": "✘",
150
- "typechanged": "T"
151
- },
152
- "colors": {
153
- "cwd": "bold cyan",
154
- "gitBranch": "bold purple",
155
- "gitStatus": "bold red",
156
- "contextNormal": "dimmed",
157
- "contextWarning": "bold yellow",
158
- "contextError": "bold red",
159
- "tokens": "dimmed",
160
- "cost": "bold green",
161
- "separator": "dimmed",
162
- "runtimePrefix": ""
163
- },
164
- "colorSources": {
165
- "starship": "theme",
166
- "editor": "theme",
167
- "userMessages": "theme"
168
- }
130
+ "projectRefreshIntervalMs": 30000,
131
+ "icons": {
132
+ "cwd": "󰝰",
133
+ "git": "",
134
+ "ahead": "↑",
135
+ "behind": "↓",
136
+ "diverged": "⇕",
137
+ "conflicted": "=",
138
+ "untracked": "?",
139
+ "stashed": "$",
140
+ "modified": "!",
141
+ "staged": "+",
142
+ "renamed": "»",
143
+ "deleted": "✘",
144
+ "typechanged": "T"
145
+ },
146
+ "colors": {
147
+ "cwd": "bold cyan",
148
+ "gitBranch": "bold purple",
149
+ "gitStatus": "bold red",
150
+ "contextNormal": "bright-black",
151
+ "contextWarning": "bold yellow",
152
+ "contextError": "bold red",
153
+ "tokens": "bright-black",
154
+ "cost": "bold green",
155
+ "separator": "bright-black",
156
+ "runtimePrefix": "",
157
+ "editorAccent": "accent",
158
+ "editorBorder": "borderMuted",
159
+ "editorModel": "accent",
160
+ "editorProvider": "text",
161
+ "editorThinking": "muted",
162
+ "editorThinkingMinimal": "thinkingMinimal",
163
+ "editorThinkingLow": "thinkingLow",
164
+ "editorThinkingMedium": "thinkingMedium",
165
+ "editorThinkingHigh": "thinkingHigh",
166
+ "editorThinkingXhigh": "thinkingXhigh"
167
+ },
168
+ "colorSources": {
169
+ "starship": "theme",
170
+ "editor": "theme",
171
+ "userMessages": "theme"
172
+ }
169
173
  }
170
174
  ```
171
175
 
172
- `projectRefreshIntervalMs` controls how often Zentui refreshes project status (git/runtime) while Pi is idle. Set it to `0` to disable polling; invalid values or values below 5000 ms fall back to `30000`.
173
-
174
- ### Color values
175
-
176
- Use `/zentui` inside Pi to switch color sources between Pi theme colors and terminal colors:
177
-
178
- - `starship` — footer/runtime/git/context/cost colors
179
- - `editor + previous messages` — input editor and previous user-message rails/borders
180
-
181
- Both settings default to `theme`. The config still stores editor and previous user-message sources separately as `editor` and `userMessages`, but `/zentui` changes them together so the prompt chrome stays consistent.
182
-
183
- Color values can use terminal-palette style strings, hex colors, or Pi theme color tokens:
184
-
185
- - Named terminal colors: `bold purple`, `yellow`, `bright-cyan`
186
- - 256-color / hex: `bold 149`, `fg:202`, `#89b4fa`
187
- - Backgrounds and modifiers: `bg:blue fg:bright-green`, `underline bg:#bf5700`
188
- - Pi theme tokens: `accent`, `borderMuted`, `syntaxKeyword`
189
-
190
- Use the `colors` config key for these values.
176
+ - Style values can be Starship/terminal strings (`bold purple`, `fg:202`, `#89b4fa`, `bg:blue fg:bright-green`) or Pi theme tokens (`accent`, `borderMuted`, `thinkingHigh`).
177
+ - `projectRefreshIntervalMs`: project status polling interval; `0` disables polling.
178
+ - `icons`: every shown icon key is configurable; omit any key to use the Zentui default.
179
+ - `colorSources`: `theme` maps styles through Pi theme tokens; `terminal` emits terminal colors. `/zentui` switches these sources; manual JSON controls specific style values.
180
+ - The shown `editor*` values match the default `theme` source. Omit those keys to keep Zentui's source-aware defaults when switching between `theme` and `terminal`.
181
+ - `editorAccent` styles the active editor rail and previous user-message rail.
182
+ - `editorBorder` styles the active editor and previous user-message top/bottom border color only; the border glyph stays `─`.
183
+ - `editorModel`, `editorProvider`, and `editorThinking*` style the editor metadata. `editorThinking` applies to every non-`off` thinking level unless a level-specific key is set.
191
184
 
192
185
  ## Requirements
193
186
 
@@ -43,6 +43,16 @@ export type PolishedTuiConfig = {
43
43
  cost: ColorSpec;
44
44
  separator: ColorSpec;
45
45
  runtimePrefix: ColorSpec;
46
+ editorAccent?: ColorSpec;
47
+ editorBorder?: ColorSpec;
48
+ editorModel?: ColorSpec;
49
+ editorProvider?: ColorSpec;
50
+ editorThinking?: ColorSpec;
51
+ editorThinkingMinimal?: ColorSpec;
52
+ editorThinkingLow?: ColorSpec;
53
+ editorThinkingMedium?: ColorSpec;
54
+ editorThinkingHigh?: ColorSpec;
55
+ editorThinkingXhigh?: ColorSpec;
46
56
  };
47
57
  colorSources: ColorSourcesConfig;
48
58
  };
@@ -70,12 +80,12 @@ export const defaultConfig: PolishedTuiConfig = {
70
80
  cwd: "bold cyan",
71
81
  gitBranch: "bold purple",
72
82
  gitStatus: "bold red",
73
- contextNormal: "dimmed",
83
+ contextNormal: "bright-black",
74
84
  contextWarning: "bold yellow",
75
85
  contextError: "bold red",
76
- tokens: "dimmed",
86
+ tokens: "bright-black",
77
87
  cost: "bold green",
78
- separator: "dimmed",
88
+ separator: "bright-black",
79
89
  runtimePrefix: "",
80
90
  },
81
91
  colorSources: {
@@ -168,6 +178,16 @@ function normalizeColors(record: Record<string, unknown>): Partial<PolishedTuiCo
168
178
  cost: colorValue(record, "cost"),
169
179
  separator: colorValue(record, "separator"),
170
180
  runtimePrefix: colorValue(record, "runtimePrefix"),
181
+ editorAccent: colorValue(record, "editorAccent"),
182
+ editorBorder: colorValue(record, "editorBorder"),
183
+ editorModel: colorValue(record, "editorModel"),
184
+ editorProvider: colorValue(record, "editorProvider"),
185
+ editorThinking: colorValue(record, "editorThinking"),
186
+ editorThinkingMinimal: colorValue(record, "editorThinkingMinimal"),
187
+ editorThinkingLow: colorValue(record, "editorThinkingLow"),
188
+ editorThinkingMedium: colorValue(record, "editorThinkingMedium"),
189
+ editorThinkingHigh: colorValue(record, "editorThinkingHigh"),
190
+ editorThinkingXhigh: colorValue(record, "editorThinkingXhigh"),
171
191
  });
172
192
  }
173
193
 
@@ -19,7 +19,6 @@ import { readRuntimeInfo } from "./runtime";
19
19
  import { installSelectorBorderStyle } from "./selector-border";
20
20
  import { registerZentuiSettingsCommand } from "./settings-command";
21
21
  import { type FooterState, createInitialState, syncState } from "./state";
22
- import { renderAccentLine, safeThemeFg } from "./style";
23
22
  import { PolishedEditor } from "./ui";
24
23
  import { installUserMessageStyle } from "./user-message";
25
24
 
@@ -81,15 +80,10 @@ export default function (pi: ExtensionAPI) {
81
80
  keybindings,
82
81
  ctx.ui.theme,
83
82
  getCurrentConfig,
84
- () =>
85
- [
86
- renderAccentLine(
87
- ctx.ui.theme,
88
- getCurrentConfig().colorSources.editor,
89
- state.modelLabel,
90
- ),
91
- safeThemeFg(ctx.ui.theme, "text", state.providerLabel),
92
- ].join(safeThemeFg(ctx.ui.theme, "borderMuted", " ")),
83
+ () => ({
84
+ modelLabel: state.modelLabel,
85
+ providerLabel: state.providerLabel,
86
+ }),
93
87
  getThinkingLevel,
94
88
  ),
95
89
  );
@@ -13,6 +13,21 @@ export type { ThemeLike };
13
13
  export const EDITOR_ACCENT_STYLE = "blue";
14
14
  export const EDITOR_BORDER_STYLE = "bright-black";
15
15
 
16
+ export type SourceStyleFallback = {
17
+ theme: ColorSpec;
18
+ terminal: ColorSpec;
19
+ };
20
+
21
+ export const EDITOR_ACCENT_FALLBACK: SourceStyleFallback = {
22
+ theme: "accent",
23
+ terminal: EDITOR_ACCENT_STYLE,
24
+ };
25
+
26
+ export const EDITOR_BORDER_FALLBACK: SourceStyleFallback = {
27
+ theme: "borderMuted",
28
+ terminal: EDITOR_BORDER_STYLE,
29
+ };
30
+
16
31
  function isHexColor(value: string): boolean {
17
32
  return /^#(?:[0-9a-fA-F]{6})$/.test(value);
18
33
  }
@@ -267,6 +282,17 @@ export function renderStyleForSource(
267
282
  : renderThemeStyle(theme, style, text);
268
283
  }
269
284
 
285
+ export function renderStyleForSourceOrFallback(
286
+ theme: ThemeLike,
287
+ source: ColorSource,
288
+ style: ColorSpec | undefined,
289
+ fallback: ColorSpec | SourceStyleFallback,
290
+ text: string,
291
+ ): string {
292
+ const fallbackStyle = typeof fallback === "string" ? fallback : fallback[source];
293
+ return renderStyleForSource(theme, source, style ?? fallbackStyle, text);
294
+ }
295
+
270
296
  export function renderEditorAccent(text: string): string {
271
297
  return renderTerminalStyle(EDITOR_ACCENT_STYLE, text);
272
298
  }
@@ -276,7 +302,7 @@ export function renderEditorBorder(text: string): string {
276
302
  }
277
303
 
278
304
  export function renderAccentLine(theme: ThemeLike, source: ColorSource, text: string): string {
279
- return source === "terminal" ? renderEditorAccent(text) : safeThemeFg(theme, "accent", text);
305
+ return renderStyleForSourceOrFallback(theme, source, undefined, EDITOR_ACCENT_FALLBACK, text);
280
306
  }
281
307
 
282
308
  export function renderChromeBorder(
@@ -285,6 +311,11 @@ export function renderChromeBorder(
285
311
  terminalFallbackStyle: ColorSpec,
286
312
  text: string,
287
313
  ): string {
288
- if (source === "terminal") return renderTerminalStyle(terminalFallbackStyle, text);
289
- return safeThemeFg(theme, "borderMuted", text);
314
+ return renderStyleForSourceOrFallback(
315
+ theme,
316
+ source,
317
+ undefined,
318
+ { theme: "borderMuted", terminal: terminalFallbackStyle },
319
+ text,
320
+ );
290
321
  }
@@ -7,20 +7,30 @@ import {
7
7
  visibleWidth,
8
8
  } from "@earendil-works/pi-tui";
9
9
  import type { PolishedTuiConfig } from "./config";
10
- import { EDITOR_BORDER_STYLE, renderAccentLine, renderChromeBorder, safeThemeFg } from "./style";
10
+ import {
11
+ EDITOR_ACCENT_FALLBACK,
12
+ EDITOR_BORDER_FALLBACK,
13
+ renderStyleForSourceOrFallback,
14
+ safeThemeFg,
15
+ } from "./style";
11
16
 
12
17
  type AutocompleteEditorInternals = {
13
18
  autocompleteList?: Pick<Component, "render">;
14
19
  isShowingAutocomplete?: () => boolean;
15
20
  };
16
21
 
22
+ type EditorMeta = {
23
+ modelLabel: string;
24
+ providerLabel: string;
25
+ };
26
+
17
27
  function clampRenderedLines(lines: string[], width: number): string[] {
18
28
  const maxWidth = Math.max(0, width);
19
29
  return lines.map((line) => truncateToWidth(line, maxWidth, ""));
20
30
  }
21
31
 
22
32
  export class PolishedEditor extends CustomEditor {
23
- private readonly getModelMeta: () => string;
33
+ private readonly getModelMeta: () => EditorMeta;
24
34
  private readonly getThinkingLevel: () => string | undefined;
25
35
  private readonly getConfig: () => PolishedTuiConfig;
26
36
  private readonly uiTheme: Theme;
@@ -32,7 +42,7 @@ export class PolishedEditor extends CustomEditor {
32
42
  keybindings: KeybindingsManager,
33
43
  uiTheme: Theme,
34
44
  getConfig: () => PolishedTuiConfig,
35
- getModelMeta: () => string,
45
+ getModelMeta: () => EditorMeta,
36
46
  getThinkingLevel: () => string | undefined,
37
47
  ) {
38
48
  super(tui, theme, keybindings, { paddingX: 0 });
@@ -49,6 +59,23 @@ export class PolishedEditor extends CustomEditor {
49
59
  return `${truncated}${pad}`;
50
60
  }
51
61
 
62
+ private editorThinkingStyle(config: PolishedTuiConfig, level: string): string | undefined {
63
+ switch (level.toLowerCase()) {
64
+ case "minimal":
65
+ return config.colors.editorThinkingMinimal ?? config.colors.editorThinking;
66
+ case "low":
67
+ return config.colors.editorThinkingLow ?? config.colors.editorThinking;
68
+ case "medium":
69
+ return config.colors.editorThinkingMedium ?? config.colors.editorThinking;
70
+ case "high":
71
+ return config.colors.editorThinkingHigh ?? config.colors.editorThinking;
72
+ case "xhigh":
73
+ return config.colors.editorThinkingXhigh ?? config.colors.editorThinking;
74
+ default:
75
+ return config.colors.editorThinking;
76
+ }
77
+ }
78
+
52
79
  render(width: number): string[] {
53
80
  if (width <= 2) {
54
81
  return clampRenderedLines(super.render(width), width);
@@ -84,26 +111,61 @@ export class PolishedEditor extends CustomEditor {
84
111
  return clampRenderedLines(rendered, width);
85
112
  }
86
113
 
114
+ const config = this.getConfig();
115
+ const colorSource = config.colorSources.editor;
87
116
  const editorLines = editorFrame.slice(1, -1);
88
- const metaParts = [this.getModelMeta()];
117
+ const { modelLabel, providerLabel } = this.getModelMeta();
118
+ const model = renderStyleForSourceOrFallback(
119
+ this.uiTheme,
120
+ colorSource,
121
+ config.colors.editorModel,
122
+ EDITOR_ACCENT_FALLBACK,
123
+ modelLabel,
124
+ );
125
+ const provider = renderStyleForSourceOrFallback(
126
+ this.uiTheme,
127
+ colorSource,
128
+ config.colors.editorProvider,
129
+ "text",
130
+ providerLabel,
131
+ );
132
+ const modelMeta = [model, provider]
133
+ .filter(Boolean)
134
+ .join(safeThemeFg(this.uiTheme, "borderMuted", " "));
135
+ const metaParts = [modelMeta];
89
136
  const thinkingLevel = this.getThinkingLevel();
90
137
  if (thinkingLevel && thinkingLevel !== "off") {
91
- metaParts.push(safeThemeFg(this.uiTheme, "muted", thinkingLevel));
138
+ metaParts.push(
139
+ renderStyleForSourceOrFallback(
140
+ this.uiTheme,
141
+ colorSource,
142
+ this.editorThinkingStyle(config, thinkingLevel),
143
+ "muted",
144
+ thinkingLevel,
145
+ ),
146
+ );
92
147
  }
93
148
  const meta = metaParts.filter(Boolean).join(safeThemeFg(this.uiTheme, "border", " "));
94
149
 
95
- const colorSource = this.getConfig().colorSources.editor;
96
- const rail = `${renderAccentLine(this.uiTheme, colorSource, "│")}${this.reset} `;
97
- const top = renderChromeBorder(
150
+ const rail = `${renderStyleForSourceOrFallback(
151
+ this.uiTheme,
152
+ colorSource,
153
+ config.colors.editorAccent,
154
+ EDITOR_ACCENT_FALLBACK,
155
+ "│",
156
+ )}${this.reset} `;
157
+ const top = renderStyleForSourceOrFallback(
98
158
  this.uiTheme,
99
159
  colorSource,
100
- EDITOR_BORDER_STYLE,
160
+ config.colors.editorBorder,
161
+ EDITOR_BORDER_FALLBACK,
101
162
  "─".repeat(width),
102
163
  );
103
- const bottom = renderChromeBorder(
164
+ const bottom = renderStyleForSourceOrFallback(
104
165
  this.uiTheme,
105
166
  colorSource,
106
- EDITOR_BORDER_STYLE,
167
+ config.colors.editorBorder,
168
+ EDITOR_BORDER_FALLBACK,
107
169
  "─".repeat(width),
108
170
  );
109
171
  const lines = ["", ...editorLines, "", meta];
@@ -6,7 +6,11 @@ import {
6
6
  visibleWidth,
7
7
  } from "@earendil-works/pi-tui";
8
8
  import type { PolishedTuiConfig } from "./config";
9
- import { EDITOR_BORDER_STYLE, renderAccentLine, renderChromeBorder } from "./style";
9
+ import {
10
+ EDITOR_ACCENT_FALLBACK,
11
+ EDITOR_BORDER_FALLBACK,
12
+ renderStyleForSourceOrFallback,
13
+ } from "./style";
10
14
 
11
15
  const OSC133_ZONE_START = "\x1b]133;A\x07";
12
16
  const OSC133_ZONE_END = "\x1b]133;B\x07";
@@ -92,7 +96,17 @@ function renderPromptBoxLine(
92
96
  config: PolishedTuiConfig,
93
97
  ): string {
94
98
  if (width <= 0) return "";
95
- const rail = `${theme ? renderAccentLine(theme, config.colorSources.userMessages, "│") : "│"} `;
99
+ const rail = `${
100
+ theme
101
+ ? renderStyleForSourceOrFallback(
102
+ theme,
103
+ config.colorSources.userMessages,
104
+ config.colors.editorAccent,
105
+ EDITOR_ACCENT_FALLBACK,
106
+ "│",
107
+ )
108
+ : "│"
109
+ } `;
96
110
  const contentWidth = Math.max(0, width - visibleWidth(rail));
97
111
  return truncateToWidth(`${rail}${fillLine(line, contentWidth)}`, width, "");
98
112
  }
@@ -108,7 +122,17 @@ function renderZentuiUserMessage(
108
122
  if (width <= 0) return [""];
109
123
 
110
124
  const railWidth = visibleWidth(
111
- `${theme ? renderAccentLine(theme, config.colorSources.userMessages, "│") : "│"} `,
125
+ `${
126
+ theme
127
+ ? renderStyleForSourceOrFallback(
128
+ theme,
129
+ config.colorSources.userMessages,
130
+ config.colors.editorAccent,
131
+ EDITOR_ACCENT_FALLBACK,
132
+ "│",
133
+ )
134
+ : "│"
135
+ } `,
112
136
  );
113
137
  const contentWidth = Math.max(1, width - railWidth);
114
138
  const renderer = new Markdown(text, 0, 0, makeMarkdownTheme(theme), {
@@ -117,10 +141,11 @@ function renderZentuiUserMessage(
117
141
  const body = renderer.render(contentWidth);
118
142
  const contentLines = body.length > 0 ? body : [""];
119
143
  const border = theme
120
- ? renderChromeBorder(
144
+ ? renderStyleForSourceOrFallback(
121
145
  theme,
122
146
  config.colorSources.userMessages,
123
- EDITOR_BORDER_STYLE,
147
+ config.colors.editorBorder,
148
+ EDITOR_BORDER_FALLBACK,
124
149
  "─".repeat(width),
125
150
  )
126
151
  : "─".repeat(width);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-zentui",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "A Starship-inspired statusline and Opencode-style TUI for Pi.",
5
5
  "type": "module",
6
6
  "license": "MIT",