pi-one-ui 0.3.0 → 0.4.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 (63) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/README.en.md +39 -35
  3. package/README.md +20 -16
  4. package/extensions/app/commands/settings-previews.ts +4 -16
  5. package/extensions/app/config/renderer.ts +7 -6
  6. package/extensions/app/config/shell.ts +15 -137
  7. package/extensions/app/overlay/selector-border.ts +5 -5
  8. package/extensions/app/ownership.ts +1 -1
  9. package/extensions/app/panel.ts +19 -25
  10. package/extensions/app/runtime/tui-runtime.ts +31 -56
  11. package/extensions/features/context-inspector/index.ts +3 -3
  12. package/extensions/features/session-reference/index.ts +5 -5
  13. package/extensions/features/subagent-autocomplete.ts +1 -1
  14. package/extensions/layouts/context/index.ts +9 -12
  15. package/extensions/layouts/context/renderer/compact-mode.ts +24 -24
  16. package/extensions/layouts/context/renderer/default-mode.ts +21 -16
  17. package/extensions/layouts/context/renderer/index.ts +24 -24
  18. package/extensions/layouts/context/renderer/mouse/hover.ts +6 -6
  19. package/extensions/layouts/context/renderer/mouse/interaction.ts +35 -35
  20. package/extensions/layouts/context/renderer/mouse/packets.ts +3 -2
  21. package/extensions/layouts/context/renderer/mouse/scroll.ts +2 -2
  22. package/extensions/layouts/context/renderer/tool/diff/diff-component.ts +2 -1
  23. package/extensions/layouts/context/renderer/tool/diff/diff-edit-render.ts +21 -21
  24. package/extensions/layouts/context/renderer/tool/diff/diff-header.ts +4 -4
  25. package/extensions/layouts/context/renderer/tool/diff/diff-highlight.ts +6 -6
  26. package/extensions/layouts/context/renderer/tool/diff/diff-inline.ts +1 -1
  27. package/extensions/layouts/context/renderer/tool/diff/diff-layout.ts +18 -18
  28. package/extensions/layouts/context/renderer/tool/diff/diff-limits.ts +5 -5
  29. package/extensions/layouts/context/renderer/tool/diff/diff-palette.ts +3 -3
  30. package/extensions/layouts/context/renderer/tool/diff/diff-presentation.ts +1 -1
  31. package/extensions/layouts/context/renderer/tool/diff/diff-renderer.ts +2 -1
  32. package/extensions/layouts/context/renderer/tool/diff/diff-write-render.ts +31 -31
  33. package/extensions/layouts/context/renderer/tool/diff/index.ts +4 -4
  34. package/extensions/layouts/context/renderer/tool/diff/line-width-safety.ts +1 -1
  35. package/extensions/layouts/context/renderer/tool/diff/write-execution.ts +1 -1
  36. package/extensions/layouts/context/renderer/tool/grouping.ts +12 -12
  37. package/extensions/layouts/context/renderer/tool/message-display.ts +2 -2
  38. package/extensions/layouts/context/renderer/tool/result.ts +199 -19
  39. package/extensions/layouts/context/summary/core.ts +1 -0
  40. package/extensions/layouts/context/summary/index.ts +3 -3
  41. package/extensions/layouts/context/thinking/compact-thinking.ts +12 -13
  42. package/extensions/layouts/editor/controller.ts +3 -57
  43. package/extensions/layouts/editor/editor-metadata-format.ts +4 -4
  44. package/extensions/layouts/editor/factory.ts +5 -23
  45. package/extensions/layouts/editor/index.ts +2 -8
  46. package/extensions/layouts/editor/minimalist-editor.ts +1 -52
  47. package/extensions/layouts/editor/ui.ts +23 -255
  48. package/extensions/layouts/footer/controller.ts +3 -3
  49. package/extensions/layouts/footer/footer.ts +21 -21
  50. package/extensions/layouts/footer/index.ts +8 -8
  51. package/extensions/layouts/header/index.ts +1 -1
  52. package/extensions/layouts/header/startup-header.ts +3 -2
  53. package/extensions/layouts/working-line/controller.ts +6 -6
  54. package/extensions/layouts/working-line/interaction-summary.ts +47 -7
  55. package/extensions/layouts/working-line/working-line.ts +24 -3
  56. package/extensions/services/project-refresh.ts +2 -2
  57. package/extensions/services/session-state.ts +1 -1
  58. package/extensions/shared/format.ts +2 -2
  59. package/extensions/shared/icons.ts +0 -4
  60. package/package.json +9 -12
  61. package/extensions/features/legacy/working-message.ts +0 -226
  62. package/extensions/layouts/editor/accent-rail-editor.ts +0 -112
  63. package/extensions/layouts/editor/accent-rail-layout-patch.ts +0 -530
package/CHANGELOG.md CHANGED
@@ -6,6 +6,34 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.0] - 2026-09-03
10
+
11
+ ### Changed
12
+
13
+ - Simplified Editor styling to the `opencode` and `minimalist` options; removed Minimalist context-usage metadata in favor of Footer's more complete context configuration.
14
+ - Added response-local live output throughput to the WorkingLine token segment after a bounded sampling window.
15
+
16
+ ### Removed
17
+
18
+ - Removed the `opencode-copy-friendly` and `accent-rail` Editor styles, including Accent Rail layout patching and style-specific settings.
19
+ - Removed the inherited upstream image from pi-one-ui's package-gallery metadata.
20
+
21
+ ### Migration
22
+
23
+ - Existing `opencode-copy-friendly` or `accent-rail` selections fall back to `opencode`; remove obsolete nested settings for those styles when convenient.
24
+ - Configure context percentage, token totals, thresholds, and gauges through Footer instead of Minimalist Editor settings.
25
+
26
+ ## [0.3.1] - 2026-08-31
27
+
28
+ ### Changed
29
+
30
+ - Unified the test suite on Vitest and reorganized tests into domain-oriented directories.
31
+
32
+ ### Fixed
33
+
34
+ - Keep `/oneui` open and focused during in-place Editor changes, restore effective values after persistence failures, and position the panel closer to the top.
35
+ - Reduced global tool expansion latency and preserved pi-subagents' dedicated live progress renderer instead of wrapping and grouping it as a generic tool.
36
+
9
37
  ## [0.3.0] - 2026-08-30
10
38
 
11
39
  ### Added
@@ -34,6 +62,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
34
62
 
35
63
  - Reduced rendering lag when expanding settled tool groups by reusing cached child output.
36
64
 
37
- [Unreleased]: https://github.com/kerolt/pi-one-ui/compare/v0.3.0...HEAD
65
+ [Unreleased]: https://github.com/kerolt/pi-one-ui/compare/v0.4.0...HEAD
66
+ [0.4.0]: https://github.com/kerolt/pi-one-ui/compare/v0.3.1...v0.4.0
67
+ [0.3.1]: https://github.com/kerolt/pi-one-ui/compare/v0.3.0...v0.3.1
38
68
  [0.3.0]: https://github.com/kerolt/pi-one-ui/compare/v0.2.2...v0.3.0
39
69
  [0.2.2]: https://github.com/kerolt/pi-one-ui/compare/v0.2.1...v0.2.2
package/README.en.md CHANGED
@@ -25,23 +25,24 @@ Header → Context → WorkingLine → Editor → Footer
25
25
 
26
26
  - **Header**: startup information, logo, and shortcut hints.
27
27
  - **Context**: the conversation area, including user messages, assistant messages, thinking, tools, diffs, Markdown, and summaries.
28
- - **WorkingLine**: working state, spinner, token/thought/elapsed information, and turn summaries.
29
- - **Editor**: input editor, completion, metadata, Accent Rail, and Minimalist styles.
28
+ - **WorkingLine**: working state, spinner, token/thought/elapsed information, live output rate, and turn summaries.
29
+ - **Editor**: input editor, completion, metadata, and the Opencode and Minimalist styles.
30
30
  - **Footer**: current directory, Git, runtime, token, cost, and extension status information.
31
31
  - **Overlay**: temporary interfaces such as the settings panel and Context Inspector, managed by a shared OverlayManager.
32
32
 
33
33
  ### Built-in functionality
34
34
 
35
- | Feature | Description | Entry point |
36
- | --- | --- | --- |
37
- | Unified settings panel | Organizes settings by Header, Context, WorkingLine, Editor, Footer, and Features | `/oneui` |
38
- | Context Inspector | Shows context usage and previews the system prompt, memory, skills, tools, and messages | `/context` |
39
- | Session reference | Searches previous Pi sessions or SubAgents and injects their useful context | `@` completion |
40
- | Subagent autocomplete | Completes SubAgent names and delegation hints | `@` completion |
41
- | Tool / Diff renderer | Provides unified rendering for tool calls, results, collapsed content, and Edit/Write diffs | Automatic |
42
- | Markdown enhancement | Adds Mermaid, admonitions, URL linking, and related rendering improvements | Automatic |
43
- | Built-in themes | Provides CC Dark and CC Light themes | `/theme` |
44
- | Compatibility aliases | Optionally provides common command aliases | `/clear`, `/exit` |
35
+ | Feature | Description | Entry point |
36
+ | ---------------------- | ------------------------------------------------------------------------------------------- | ----------------- |
37
+ | Unified settings panel | Organizes settings by Header, Context, WorkingLine, Editor, Footer, and Features | `/oneui` |
38
+ | Context Inspector | Shows context usage and previews the system prompt, memory, skills, tools, and messages | `/context` |
39
+ | Session reference | Searches previous Pi sessions or SubAgents and injects their useful context | `@` completion |
40
+ | Subagent autocomplete | Completes SubAgent names and delegation hints | `@` completion |
41
+ | Tool / Diff renderer | Provides unified rendering for tool calls, results, collapsed content, and Edit/Write diffs | Automatic |
42
+ | Subagent live renderer | Preserves pi-subagents' dedicated progress cards and keeps them out of generic tool groups | Automatic |
43
+ | Markdown enhancement | Adds Mermaid, admonitions, URL linking, and related rendering improvements | Automatic |
44
+ | Built-in themes | Provides CC Dark and CC Light themes | `/theme` |
45
+ | Compatibility aliases | Optionally provides common command aliases | `/clear`, `/exit` |
45
46
 
46
47
  ## Quick start
47
48
 
@@ -82,7 +83,7 @@ The configuration file is located at:
82
83
  ~/.pi/agent/pi-one-ui.json
83
84
  ```
84
85
 
85
- Using the `/oneui` settings panel is recommended. The current configuration uses the v1 structure, for example:
86
+ Using the `/oneui` settings panel is recommended. The panel uses a top-centered layout, stays open while Editor enablement or style changes are applied, restores focus after Editor replacement, and restores the effective list value when persistence fails. The current configuration uses the v1 structure, for example:
86
87
 
87
88
  ```json
88
89
  {
@@ -110,6 +111,10 @@ Using the `/oneui` settings panel is recommended. The current configuration uses
110
111
  }
111
112
  ```
112
113
 
114
+ The Editor now supports only the `opencode` and `minimalist` styles. `minimalist` no longer duplicates context-usage information; context percentage, token totals, thresholds, gauges, and related presentation are configured through Footer. Existing configurations that select the removed `opencode-copy-friendly` or `accent-rail` styles safely fall back to the default `opencode`, and their old nested style settings are ignored.
115
+
116
+ The WorkingLine token segment appends live output throughput, such as `⚡12 tok/s`, after a model response has run for at least 500ms. Throughput is calculated independently for the current response and resets on the next `turn_start`; disabling the token segment hides it as well.
117
+
113
118
  Available layout and renderer options may change between versions, so prefer configuring them through `/oneui`.
114
119
 
115
120
  ### Canonical configuration policy
@@ -126,9 +131,9 @@ It does not automatically read, merge, or migrate historical configuration files
126
131
 
127
132
  `pi-one-ui` originally combined source from the following open-source projects. Many thanks to their maintainers and contributors for providing the initial foundation.
128
133
 
129
- | Upstream project | Capabilities incorporated into `pi-one-ui` | Reference baseline |
130
- | --- | --- | --- |
131
- | [pi-zentui](https://github.com/lmilojevicc/pi-zentui) | Starship-style Footer, Opencode-style Editor, layouts, and shell capabilities | v0.21.0, commit `5341b38` |
134
+ | Upstream project | Capabilities incorporated into `pi-one-ui` | Reference baseline |
135
+ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------- |
136
+ | [pi-zentui](https://github.com/lmilojevicc/pi-zentui) | Starship-style Footer, Opencode-style Editor, layouts, and shell capabilities | v0.21.0, commit `5341b38` |
132
137
  | [pi-cc-extensions](https://github.com/minuque/pi-cc-extensions) | Claude Code-style Context renderer, Tool/Diff rendering, Context Inspector, and references | v0.8.67, commit `dba37e5` |
133
138
 
134
139
  Production code lives in `extensions/`. The project has since unified its entry point, configuration storage, lifecycle, layout ownership, overlays, and input routing. The current implementation is no longer equivalent to either upstream project and does not automatically track upstream changes.
@@ -177,18 +182,16 @@ After changing the source, run the following command in Pi:
177
182
 
178
183
  ### Development commands
179
184
 
180
- | Command | Purpose |
181
- | --- | --- |
182
- | `npm install` | Install dependencies |
183
- | `npm run format` | Format source, tests, and configuration files |
184
- | `npm run format:check` | Check formatting without modifying files |
185
- | `npm run lint` | Run Biome checks |
186
- | `npm run typecheck` | Run TypeScript type checking |
187
- | `npm test` | Run all tests |
188
- | `npm run test:node` | Run Node.js test runner tests |
189
- | `npm run test:vitest` | Run Vitest tests |
190
- | `npm run pack:check` | Preview the npm package contents |
191
- | `npm run verify` | Run formatting, lint, type checking, and all tests |
185
+ | Command | Purpose |
186
+ | -------------------- | ---------------------------------------------- |
187
+ | `npm install` | Install dependencies |
188
+ | `npm run format` | Format source, tests, and configuration files |
189
+ | `npm run check` | Check formatting and import organization |
190
+ | `npm run fix` | Fix formatting and organize imports |
191
+ | `npm run typecheck` | Run TypeScript type checking |
192
+ | `npm test` | Run all tests with Vitest |
193
+ | `npm run pack:check` | Preview the npm package contents |
194
+ | `npm run verify` | Run Biome checks, type checking, and all tests |
192
195
 
193
196
  Before submitting changes, run at least:
194
197
 
@@ -199,14 +202,15 @@ npm run pack:check
199
202
 
200
203
  ### Test organization
201
204
 
202
- Tests are grouped by domain:
205
+ All tests run through Vitest and are organized into domain directories:
203
206
 
204
- - `tests/context-*`: Context content, tools, diffs, thinking, and mouse interaction.
205
- - `tests/working-line-*`: WorkingLine and turn summaries.
206
- - `tests/editor-*`: Editor, completion, metadata, transfer, and Accent Rail.
207
- - `tests/footer-*`: Footer rendering, formatting, layout, and status.
208
- - `tests/services-*`: Git, runtime, project, session, and telemetry data.
209
- - `tests/shell-*`: Remaining layout lifecycle glue and standalone compatibility.
207
+ - `tests/config/`: canonical configuration, storage, and compatibility boundaries.
208
+ - `tests/context/`: Context content, tools, diffs, thinking, and mouse interaction.
209
+ - `tests/header/`, `tests/working-line/`, `tests/editor/`, and `tests/footer/`: Layout behavior and lifecycle coverage.
210
+ - `tests/runtime/`, `tests/overlay/`, and `tests/integration/`: runtime infrastructure, overlays, and the composed entry point.
211
+ - `tests/services/`: Git, runtime, project, session, and telemetry data.
212
+ - `tests/shell/`: remaining layout lifecycle glue and standalone compatibility.
213
+ - `tests/support/` and `tests/fixtures/`: shared test helpers and fixtures.
210
214
 
211
215
  Changes involving the TUI lifecycle should specifically cover reloads, session tree rebuilds, compaction, regular/fullscreen TUI modes, headless mode, overlays, and third-party patch ownership.
212
216
 
package/README.md CHANGED
@@ -25,8 +25,8 @@ Header → Context → WorkingLine → Editor → Footer
25
25
 
26
26
  - **Header**:启动信息、Logo 和快捷键提示。
27
27
  - **Context**:对话内容区,包含用户消息、Assistant 消息、Thinking、Tool、Diff、Markdown 和 Summary。
28
- - **WorkingLine**:工作状态、spinner、token/thought/elapsed 信息和回合摘要。
29
- - **Editor**:输入编辑器、completion、metadata、Accent Rail 和 Minimalist 样式。
28
+ - **WorkingLine**:工作状态、spinner、token/thought/elapsed、实时输出速率和回合摘要。
29
+ - **Editor**:输入编辑器、completion、metadata,以及 Opencode 和 Minimalist 样式。
30
30
  - **Footer**:目录、Git、runtime、token、cost 和扩展状态等信息。
31
31
  - **Overlay**:设置面板、Context Inspector 等临时浮层由统一的 OverlayManager 管理。
32
32
 
@@ -39,6 +39,7 @@ Header → Context → WorkingLine → Editor → Footer
39
39
  | Session reference | 搜索并注入历史 Pi session 或 SubAgent 的有效上下文 | `@` 补全 |
40
40
  | Subagent autocomplete | 提供 SubAgent 名称补全和委派提示 | `@` 补全 |
41
41
  | Tool / Diff renderer | 工具调用、结果、折叠内容和 Edit/Write diff 的统一展示 | 自动生效 |
42
+ | Subagent live renderer | 保留 pi-subagents 专用进度卡,并避免纳入通用工具分组 | 自动生效 |
42
43
  | Markdown enhancement | 支持 Mermaid、提示框和 URL 链接化等增强渲染 | 自动生效 |
43
44
  | Built-in themes | 提供 CC Dark 和 CC Light 主题 | `/theme` |
44
45
  | Compatibility aliases | 可选提供常用命令别名 | `/clear`、`/exit` |
@@ -82,7 +83,7 @@ pi install git:github.com/kerolt/pi-one-ui
82
83
  ~/.pi/agent/pi-one-ui.json
83
84
  ```
84
85
 
85
- 推荐通过 `/oneui` 设置面板修改配置。当前配置仍使用 v1 结构,例如:
86
+ 推荐通过 `/oneui` 设置面板修改配置。面板采用更靠近顶部的居中布局;切换 Editor 启用状态或样式时会保持打开并在 Editor 替换后恢复焦点,持久化失败时则恢复列表中的有效旧值。当前配置仍使用 v1 结构,例如:
86
87
 
87
88
  ```json
88
89
  {
@@ -110,6 +111,10 @@ pi install git:github.com/kerolt/pi-one-ui
110
111
  }
111
112
  ```
112
113
 
114
+ Editor 当前只支持 `opencode` 和 `minimalist` 两种样式。`minimalist` 不再重复显示上下文占用信息;上下文百分比、token 总量、阈值和 gauge 等展示统一由 Footer 配置。已有配置若选择了已移除的 `opencode-copy-friendly` 或 `accent-rail`,运行时会安全回退到默认的 `opencode`,相应的旧嵌套样式配置会被忽略。
115
+
116
+ WorkingLine 的 token segment 会在一次模型响应持续至少 500ms 后追加实时输出速率,例如 `⚡12 tok/s`。速率按当前响应独立计算,在下一次 `turn_start` 时重置;关闭 token segment 时也会一并隐藏。
117
+
113
118
  不同布局和渲染器的可用选项会随版本变化,建议优先使用 `/oneui` 面板进行配置。
114
119
 
115
120
  ### Canonical 配置约定
@@ -181,14 +186,12 @@ npm run pi:install-local
181
186
  | ---------------------- | ---------------------------------- |
182
187
  | `npm install` | 安装依赖 |
183
188
  | `npm run format` | 格式化源码、测试和配置文件 |
184
- | `npm run format:check` | 检查格式但不修改文件 |
185
- | `npm run lint` | 运行 Biome 检查 |
189
+ | `npm run check` | 检查格式和 import organization |
190
+ | `npm run fix` | 修复格式并整理 imports |
186
191
  | `npm run typecheck` | 执行 TypeScript 类型检查 |
187
- | `npm test` | 运行全部测试 |
188
- | `npm run test:node` | 运行 Node.js test runner 测试 |
189
- | `npm run test:vitest` | 运行 Vitest 测试 |
192
+ | `npm test` | 使用 Vitest 运行全部测试 |
190
193
  | `npm run pack:check` | 预览 npm 实际打包内容 |
191
- | `npm run verify` | 执行格式、lint、类型检查和全部测试 |
194
+ | `npm run verify` | 执行 Biome check、类型检查和全部测试 |
192
195
 
193
196
  提交修改前建议至少运行:
194
197
 
@@ -199,14 +202,15 @@ npm run pack:check
199
202
 
200
203
  ### 测试组织
201
204
 
202
- 测试按照功能领域划分:
205
+ 所有测试统一由 Vitest 执行,并按照功能领域组织在子目录中:
203
206
 
204
- - `tests/context-*`:Context 内容区、Tool、Diff、Thinking 和鼠标交互。
205
- - `tests/working-line-*`:WorkingLine 和 turn summary。
206
- - `tests/editor-*`:Editor、completion、metadata、transfer 和 Accent Rail。
207
- - `tests/footer-*`:Footer、Footer format/layout/status。
208
- - `tests/services-*`:Git、runtime、project、session 和 telemetry。
209
- - `tests/shell-*`:剩余布局生命周期 glue 和 standalone compatibility。
207
+ - `tests/config/`:canonical 配置、存储和兼容性边界。
208
+ - `tests/context/`:Context 内容区、Tool、Diff、Thinking 和鼠标交互。
209
+ - `tests/header/`、`tests/working-line/`、`tests/editor/`、`tests/footer/`:各 Layout 的行为和生命周期。
210
+ - `tests/runtime/`、`tests/overlay/`、`tests/integration/`:运行时基础设施、Overlay 和组合入口。
211
+ - `tests/services/`:Git、runtime、project、session 和 telemetry。
212
+ - `tests/shell/`:剩余布局生命周期 glue 和 standalone compatibility。
213
+ - `tests/support/`、`tests/fixtures/`:共享测试工具和 fixtures。
210
214
 
211
215
  涉及 TUI 生命周期的修改,应特别覆盖 reload、session tree rebuild、compact、regular/fullscreen TUI、headless mode、overlay 和第三方 patch ownership 等场景。
212
216
 
@@ -1,12 +1,11 @@
1
1
  import type { Theme } from "@earendil-works/pi-coding-agent";
2
2
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
3
- import { renderAccentRailEditorFrame } from "../../layouts/editor/accent-rail-editor.ts";
4
- import type { PolishedTuiConfig } from "../config/shell.ts";
3
+ import { renderUserMessageStyle } from "../../layouts/context/message/user-message-styles.ts";
5
4
  import { sanitizeEditorMetadataText } from "../../layouts/editor/editor-metadata-format.ts";
6
5
  import { renderMinimalistFrame } from "../../layouts/editor/minimalist-editor.ts";
7
- import { safeThemeFg } from "../../shared/style.ts";
8
6
  import { renderPolishedEditorFrame } from "../../layouts/editor/ui.ts";
9
- import { renderUserMessageStyle } from "../../layouts/context/message/user-message-styles.ts";
7
+ import { safeThemeFg } from "../../shared/style.ts";
8
+ import type { PolishedTuiConfig } from "../config/shell.ts";
10
9
 
11
10
  export const SETTINGS_PREVIEW_MAX_WIDTH = 72;
12
11
  export const SETTINGS_PREVIEW_MAX_ROWS = 10;
@@ -61,16 +60,7 @@ export function renderEditorSettingsPreview(
61
60
  ? { above: "2", below: "3" }
62
61
  : undefined;
63
62
  let frame: string[];
64
- if (editor.style === "accent-rail") {
65
- frame = renderAccentRailEditorFrame({
66
- width: previewWidth,
67
- editorLines,
68
- autocompleteLines,
69
- viewport,
70
- uiTheme: theme,
71
- config: safeConfig,
72
- });
73
- } else if (editor.style === "minimalist") {
63
+ if (editor.style === "minimalist") {
74
64
  frame = renderMinimalistFrame({
75
65
  width: previewWidth,
76
66
  editorLines,
@@ -86,8 +76,6 @@ export function renderEditorSettingsPreview(
86
76
  costLabel: "$.12",
87
77
  modelLabel,
88
78
  thinkingLevel: "high",
89
- contextPercent: 75,
90
- contextWindow: 372_000,
91
79
  sessionName: "Preview",
92
80
  agentDurationMs: 12_000,
93
81
  agentActive: true,
@@ -1,5 +1,5 @@
1
1
  import type { CompactThinkingConfig } from "../../layouts/context/thinking/compact-thinking.ts";
2
- import { configStore, type ConfigRecord } from "./store.ts";
2
+ import { type ConfigRecord, configStore } from "./store.ts";
3
3
 
4
4
  export type CompactStyleMode = "on" | "compact" | "off";
5
5
 
@@ -371,17 +371,18 @@ function loadConfig(): Config {
371
371
  /**
372
372
  * Persists the in-memory renderer projection through the shared store.
373
373
  */
374
- export function saveConfig() {
374
+ export function saveConfig(next: Config = config) {
375
375
  configStore.update((record) => {
376
376
  record.version = 1;
377
- record.renderer = config;
377
+ record.renderer = next;
378
378
  });
379
379
  }
380
380
 
381
- /** 运行时配置写入的唯一入口:合并 + 规范化 + 持久化。 */
381
+ /** 运行时配置写入的唯一入口:规范化并持久化成功后再提交到活动配置。 */
382
382
  export function updateConfig(partial: Partial<Config>): void {
383
- Object.assign(config, normalizeConfig({ ...config, ...partial }));
384
- saveConfig();
383
+ const next = normalizeConfig({ ...config, ...partial });
384
+ saveConfig(next);
385
+ Object.assign(config, next);
385
386
  }
386
387
 
387
388
  /** 整体替换配置(default export 的 configOverride 注入路径;就地覆盖,不持久化)。 */
@@ -1,3 +1,5 @@
1
+ import { normalizeWorkingLineMessages } from "../../layouts/working-line/working-line.ts";
2
+ import { PI_WORKING_LINE_MESSAGES } from "../../layouts/working-line/working-line-messages.ts";
1
3
  import {
2
4
  ICON_GLYPH_KEYS,
3
5
  type IconGlyphs,
@@ -8,13 +10,11 @@ import {
8
10
  resolveConfiguredIcons,
9
11
  } from "../../shared/icons.ts";
10
12
  import { isSupportedColorSpec } from "../../shared/style.ts";
11
- import { normalizeWorkingLineMessages } from "../../layouts/working-line/working-line.ts";
12
- import { PI_WORKING_LINE_MESSAGES } from "../../layouts/working-line/working-line-messages.ts";
13
13
  import {
14
- configPath as defaultConfigPath,
14
+ type ConfigRecord,
15
15
  configStore,
16
+ configPath as defaultConfigPath,
16
17
  mutateConfigFile,
17
- type ConfigRecord,
18
18
  } from "./store.ts";
19
19
 
20
20
  export type ColorSpec = string;
@@ -24,11 +24,7 @@ export type { IconMode } from "../../shared/icons.ts";
24
24
  export type ContextStyle = "text" | "gauge" | "text+gauge";
25
25
  export type SeparatorStyle = "pipe" | "dot" | "chevron" | "none";
26
26
  export type ModelLabelSource = "id" | "name";
27
- export type EditorStyle =
28
- | "opencode"
29
- | "opencode-copy-friendly"
30
- | "accent-rail"
31
- | "minimalist";
27
+ export type EditorStyle = "opencode" | "minimalist";
32
28
  export type UserMessageStyle =
33
29
  | "framed"
34
30
  | "framed-copy-friendly"
@@ -49,7 +45,6 @@ export type ComponentStyleOwner =
49
45
  | "selectorBorders"
50
46
  | "footer";
51
47
  export type MinimalistPathDisplayMode = "compact" | "project" | "full";
52
- export type MinimalistContextFormat = "percent" | "percent-total";
53
48
  export type EditorBorderColorMode = "static" | "adaptive";
54
49
  export type CompletionMenuStyle = "native" | "palette";
55
50
  export type CompactFooterMaxLines = 1 | 2 | 3 | "unlimited";
@@ -113,26 +108,12 @@ export type PolishedEditorStyleConfig = {
113
108
  completionMenu: CompletionMenuStyle;
114
109
  };
115
110
 
116
- export type PolishedCopyFriendlyEditorStyleConfig = {
117
- metadataFormat: string;
118
- completionMenu: CompletionMenuStyle;
119
- };
120
-
121
- export type AccentRailEditorStyleConfig = {
122
- rail: string;
123
- asciiRail: string;
124
- transparent: boolean;
125
- };
126
-
127
111
  export type MinimalistEditorStyleConfig = {
128
112
  pathDisplay: MinimalistPathDisplayMode;
129
- contextFormat: MinimalistContextFormat;
130
- contextGauge: boolean;
131
113
  showSessionName: boolean;
132
114
  showTimer: boolean;
133
115
  showCost: boolean;
134
116
  showGit: boolean;
135
- contextThresholds: ContextThresholds;
136
117
  };
137
118
 
138
119
  /** Temporary name retained for existing settings consumers. */
@@ -151,8 +132,6 @@ export type EditorComponentConfig = {
151
132
  viewportIndicators: boolean;
152
133
  styles: {
153
134
  opencode: PolishedEditorStyleConfig;
154
- "opencode-copy-friendly": PolishedCopyFriendlyEditorStyleConfig;
155
- "accent-rail": AccentRailEditorStyleConfig;
156
135
  minimalist: MinimalistEditorStyleConfig;
157
136
  };
158
137
  };
@@ -323,8 +302,6 @@ export type PolishedTuiColors = {
323
302
  time: ColorSpec;
324
303
  os: ColorSpec;
325
304
  editorAccent?: ColorSpec;
326
- editorRail?: ColorSpec;
327
- editorPrompt?: ColorSpec;
328
305
  editorBorder?: ColorSpec;
329
306
  editorGitBranch?: ColorSpec;
330
307
  editorModel?: ColorSpec;
@@ -439,21 +416,12 @@ const defaultFooterSegments: FooterSegmentsConfig = {
439
416
 
440
417
  const DEFAULT_COMPLETION_MENU: CompletionMenuStyle = "palette";
441
418
 
442
- const defaultAccentRailStyle: AccentRailEditorStyleConfig = {
443
- rail: "▎",
444
- asciiRail: "|",
445
- transparent: false,
446
- };
447
-
448
419
  const defaultMinimalistStyle: MinimalistEditorStyleConfig = {
449
420
  pathDisplay: "compact",
450
- contextFormat: "percent",
451
- contextGauge: false,
452
421
  showSessionName: true,
453
422
  showTimer: true,
454
423
  showCost: true,
455
424
  showGit: true,
456
- contextThresholds: { warning: 70, error: 90 },
457
425
  };
458
426
 
459
427
  const defaultStarshipStyle: StarshipFooterStyleConfig = {
@@ -489,11 +457,6 @@ const defaultComponents: ComponentsConfig = {
489
457
  metadataFormat: DEFAULT_EDITOR_METADATA_FORMAT,
490
458
  completionMenu: DEFAULT_COMPLETION_MENU,
491
459
  },
492
- "opencode-copy-friendly": {
493
- metadataFormat: DEFAULT_EDITOR_METADATA_FORMAT,
494
- completionMenu: DEFAULT_COMPLETION_MENU,
495
- },
496
- "accent-rail": defaultAccentRailStyle,
497
460
  minimalist: defaultMinimalistStyle,
498
461
  },
499
462
  },
@@ -615,14 +578,7 @@ function parseEditorModelLabel(
615
578
  }
616
579
 
617
580
  function parseEditorStyle(value: unknown): EditorStyle {
618
- if (
619
- value === "opencode" ||
620
- value === "opencode-copy-friendly" ||
621
- value === "accent-rail" ||
622
- value === "minimalist"
623
- ) {
624
- return value;
625
- }
581
+ if (value === "opencode" || value === "minimalist") return value;
626
582
  return defaultComponents.editor.style;
627
583
  }
628
584
 
@@ -775,8 +731,6 @@ function normalizeColors(
775
731
  time: colorValue(record, "time"),
776
732
  os: colorValue(record, "os"),
777
733
  editorAccent: colorValue(record, "editorAccent"),
778
- editorRail: colorValue(record, "editorRail"),
779
- editorPrompt: colorValue(record, "editorPrompt"),
780
734
  editorBorder: colorValue(record, "editorBorder"),
781
735
  editorGitBranch: colorValue(record, "editorGitBranch"),
782
736
  editorModel: colorValue(record, "editorModel"),
@@ -1074,10 +1028,6 @@ function resolveComponents(config: ConfigRecord): ComponentsConfig {
1074
1028
  const editor = recordValue(components.editor);
1075
1029
  const editorStyles = recordValue(editor.styles);
1076
1030
  const opencode = recordValue(editorStyles.opencode);
1077
- const opencodeCopyFriendly = recordValue(
1078
- editorStyles["opencode-copy-friendly"],
1079
- );
1080
- const accentRail = recordValue(editorStyles["accent-rail"]);
1081
1031
  const minimalist = recordValue(editorStyles.minimalist);
1082
1032
  const userMessages = recordValue(components.userMessages);
1083
1033
  const workingLine = recordValue(components.workingLine);
@@ -1113,29 +1063,6 @@ function resolveComponents(config: ConfigRecord): ComponentsConfig {
1113
1063
  ),
1114
1064
  completionMenu: parseCompletionMenuStyle(opencode.completionMenu),
1115
1065
  },
1116
- "opencode-copy-friendly": {
1117
- metadataFormat: parseNonEmptyString(
1118
- opencodeCopyFriendly.metadataFormat,
1119
- DEFAULT_EDITOR_METADATA_FORMAT,
1120
- ),
1121
- completionMenu: parseCompletionMenuStyle(
1122
- opencodeCopyFriendly.completionMenu,
1123
- ),
1124
- },
1125
- "accent-rail": {
1126
- rail: parseNonEmptyString(
1127
- accentRail.rail,
1128
- defaultAccentRailStyle.rail,
1129
- ),
1130
- asciiRail: parseNonEmptyString(
1131
- accentRail.asciiRail,
1132
- defaultAccentRailStyle.asciiRail,
1133
- ),
1134
- transparent: parseBoolean(
1135
- accentRail.transparent,
1136
- defaultAccentRailStyle.transparent,
1137
- ),
1138
- },
1139
1066
  minimalist: {
1140
1067
  pathDisplay:
1141
1068
  minimalist.pathDisplay === "compact" ||
@@ -1143,15 +1070,6 @@ function resolveComponents(config: ConfigRecord): ComponentsConfig {
1143
1070
  minimalist.pathDisplay === "full"
1144
1071
  ? minimalist.pathDisplay
1145
1072
  : defaultMinimalistStyle.pathDisplay,
1146
- contextFormat:
1147
- minimalist.contextFormat === "percent" ||
1148
- minimalist.contextFormat === "percent-total"
1149
- ? minimalist.contextFormat
1150
- : defaultMinimalistStyle.contextFormat,
1151
- contextGauge: parseBoolean(
1152
- minimalist.contextGauge,
1153
- defaultMinimalistStyle.contextGauge,
1154
- ),
1155
1073
  showSessionName: parseBoolean(
1156
1074
  minimalist.showSessionName,
1157
1075
  defaultMinimalistStyle.showSessionName,
@@ -1168,10 +1086,6 @@ function resolveComponents(config: ConfigRecord): ComponentsConfig {
1168
1086
  minimalist.showGit,
1169
1087
  defaultMinimalistStyle.showGit,
1170
1088
  ),
1171
- contextThresholds: resolveContextThresholds(
1172
- minimalist.contextThresholds,
1173
- defaultMinimalistStyle.contextThresholds,
1174
- ),
1175
1089
  },
1176
1090
  },
1177
1091
  },
@@ -1349,16 +1263,17 @@ const unsupportedComponentStyles = new WeakMap<
1349
1263
  ReadonlySet<ComponentStyleOwner>
1350
1264
  >();
1351
1265
 
1266
+ // Retired selections migrate to Opencode instead of disabling the custom Editor.
1267
+ const retiredEditorStyleIds: ReadonlySet<string> = new Set([
1268
+ "opencode-copy-friendly",
1269
+ "accent-rail",
1270
+ ]);
1271
+
1352
1272
  const knownComponentStyleIds: Record<
1353
1273
  ComponentStyleOwner,
1354
1274
  ReadonlySet<string>
1355
1275
  > = {
1356
- editor: new Set([
1357
- "opencode",
1358
- "opencode-copy-friendly",
1359
- "accent-rail",
1360
- "minimalist",
1361
- ]),
1276
+ editor: new Set(["opencode", "minimalist"]),
1362
1277
  userMessages: new Set([
1363
1278
  "framed",
1364
1279
  "framed-copy-friendly",
@@ -1378,7 +1293,8 @@ function unsupportedSelectedStyleId(
1378
1293
  const style = component.style;
1379
1294
  return typeof style === "string" &&
1380
1295
  style.trim() &&
1381
- !knownComponentStyleIds[owner].has(style)
1296
+ !knownComponentStyleIds[owner].has(style) &&
1297
+ !(owner === "editor" && retiredEditorStyleIds.has(style))
1382
1298
  ? style
1383
1299
  : undefined;
1384
1300
  }
@@ -1580,50 +1496,16 @@ export function savePolishedEditorStylePatch(
1580
1496
  }, path);
1581
1497
  }
1582
1498
 
1583
- export function savePolishedCopyFriendlyEditorStylePatch(
1584
- patch: Partial<PolishedCopyFriendlyEditorStyleConfig>,
1585
- path = configPath,
1586
- ): PolishedTuiConfig {
1587
- return saveComponentsMutation((components) => {
1588
- const style = components.editor.styles["opencode-copy-friendly"];
1589
- if (patch.metadataFormat !== undefined)
1590
- style.metadataFormat = patch.metadataFormat;
1591
- if (patch.completionMenu !== undefined)
1592
- style.completionMenu = patch.completionMenu;
1593
- }, path);
1594
- }
1595
-
1596
- export function saveAccentRailEditorStylePatch(
1597
- patch: Partial<AccentRailEditorStyleConfig>,
1598
- path = configPath,
1599
- ): PolishedTuiConfig {
1600
- return saveComponentsMutation((components) => {
1601
- const style = components.editor.styles["accent-rail"];
1602
- if (patch.rail !== undefined) style.rail = patch.rail;
1603
- if (patch.asciiRail !== undefined) style.asciiRail = patch.asciiRail;
1604
- if (patch.transparent !== undefined) style.transparent = patch.transparent;
1605
- }, path);
1606
- }
1607
-
1608
1499
  function applyMinimalistStylePatch(
1609
1500
  style: MinimalistEditorStyleConfig,
1610
1501
  patch: Partial<MinimalistEditorStyleConfig>,
1611
1502
  ): void {
1612
1503
  if (patch.pathDisplay !== undefined) style.pathDisplay = patch.pathDisplay;
1613
- if (patch.contextFormat !== undefined)
1614
- style.contextFormat = patch.contextFormat;
1615
- if (patch.contextGauge !== undefined) style.contextGauge = patch.contextGauge;
1616
1504
  if (patch.showSessionName !== undefined)
1617
1505
  style.showSessionName = patch.showSessionName;
1618
1506
  if (patch.showTimer !== undefined) style.showTimer = patch.showTimer;
1619
1507
  if (patch.showCost !== undefined) style.showCost = patch.showCost;
1620
1508
  if (patch.showGit !== undefined) style.showGit = patch.showGit;
1621
- if (patch.contextThresholds !== undefined) {
1622
- style.contextThresholds = {
1623
- ...style.contextThresholds,
1624
- ...patch.contextThresholds,
1625
- };
1626
- }
1627
1509
  }
1628
1510
 
1629
1511
  export function saveMinimalistEditorStylePatch(
@@ -1912,10 +1794,6 @@ export function saveContextThresholdsPatch(
1912
1794
  throw new TypeError("Context thresholds must be JSON-serializable numbers");
1913
1795
  }
1914
1796
  return saveComponentsMutation((components) => {
1915
- components.editor.styles.minimalist.contextThresholds = {
1916
- ...components.editor.styles.minimalist.contextThresholds,
1917
- ...thresholds,
1918
- };
1919
1797
  components.footer.styles.starship.contextThresholds = {
1920
1798
  ...components.footer.styles.starship.contextThresholds,
1921
1799
  ...thresholds,
@@ -3,16 +3,16 @@ import {
3
3
  SettingsSelectorComponent,
4
4
  type Theme,
5
5
  } from "@earendil-works/pi-coding-agent";
6
- import type { ZentuiConfig } from "../config/shell.ts";
7
- import {
8
- installPrototypePatch,
9
- removePrototypePatch,
10
- } from "../ownership/prototype-patch-registry.ts";
11
6
  import {
12
7
  EDITOR_BORDER_STYLE,
13
8
  renderChromeBorder,
14
9
  renderEditorBorder,
15
10
  } from "../../shared/style.ts";
11
+ import type { ZentuiConfig } from "../config/shell.ts";
12
+ import {
13
+ installPrototypePatch,
14
+ removePrototypePatch,
15
+ } from "../ownership/prototype-patch-registry.ts";
16
16
 
17
17
  type PatchableSelectorPrototype = {
18
18
  render: (width: number) => string[];
@@ -26,7 +26,7 @@ export function ownerFor(slot: UiSlot): UiOwner {
26
26
 
27
27
  export {
28
28
  LAYOUT_OWNERSHIP,
29
- LayoutRegistry,
30
29
  type LayoutId,
31
30
  type LayoutOwner,
31
+ LayoutRegistry,
32
32
  } from "./ownership/layout-registry.ts";