pi-one-ui 0.7.0 → 0.7.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/CHANGELOG.md +47 -1
- package/README.en.md +20 -7
- package/README.md +20 -7
- package/extensions/app/config/context.ts +42 -0
- package/extensions/app/config/editor.ts +80 -0
- package/extensions/app/config/footer.ts +291 -0
- package/extensions/app/config/panel.ts +1 -5
- package/extensions/app/config/renderer.ts +1 -6
- package/extensions/app/config/shell.ts +358 -1435
- package/extensions/app/config/store.ts +22 -1
- package/extensions/app/config/values.ts +33 -0
- package/extensions/app/config/working-line.ts +100 -0
- package/extensions/app/overlay/input-router.ts +56 -32
- package/extensions/app/overlay/overlay-manager.ts +183 -21
- package/extensions/app/runtime/event-coordinator.ts +90 -42
- package/extensions/{features → app/runtime}/flush-docked-bash.ts +12 -2
- package/extensions/app/runtime/render-scheduler.ts +35 -31
- package/extensions/app/runtime/tui-runtime.ts +315 -312
- package/extensions/app/settings/controller.ts +202 -0
- package/extensions/app/settings/panel.ts +81 -0
- package/extensions/app/settings/presets.ts +43 -0
- package/extensions/features/context-inspector/index.ts +83 -662
- package/extensions/layouts/context/index.ts +58 -161
- package/extensions/layouts/context/message/user-message.ts +1 -1
- package/extensions/layouts/context/renderer/index.ts +31 -13
- package/extensions/layouts/context/renderer/mouse/interaction.ts +29 -14
- package/extensions/layouts/context/renderer/tool/toggle-render-cache.ts +25 -0
- package/extensions/layouts/editor/controller.ts +9 -18
- package/extensions/layouts/editor/factory.ts +3 -3
- package/extensions/layouts/editor/minimalist-editor.ts +17 -1
- package/extensions/layouts/footer/controller.ts +30 -19
- package/extensions/layouts/footer/footer.ts +2 -2
- package/extensions/layouts/overlay/context-inspector.ts +497 -0
- package/extensions/{app → layouts}/overlay/selector-border.ts +5 -5
- package/extensions/{app → layouts}/overlay/selector-controller.ts +1 -1
- package/extensions/layouts/overlay/settings-panel.ts +359 -0
- package/extensions/{app/commands → layouts/overlay}/settings-previews.ts +4 -4
- package/extensions/layouts/working-line/controller.ts +3 -29
- package/extensions/layouts/working-line/working-line.ts +17 -114
- package/extensions/shared/working-line-text.ts +108 -0
- package/package.json +1 -1
- package/extensions/app/host/pi-extension-port.ts +0 -25
- package/extensions/app/host/pi-ui-port.ts +0 -52
- package/extensions/app/host/tui-capabilities.ts +0 -27
- package/extensions/app/ownership/layout-registry.ts +0 -65
- package/extensions/app/ownership.ts +0 -32
- package/extensions/app/panel.ts +0 -770
- package/extensions/app/presets.ts +0 -35
- package/extensions/app/runtime/runtime-state.ts +0 -83
- /package/extensions/{layouts/working-line → app/config}/working-line-messages.ts +0 -0
- /package/extensions/app/{ownership → runtime}/prototype-patch-registry.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,45 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.7.2] - 2026-09-28
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- The Editor completion dropdown now paints the selected row with the editor frame color, so the selection follows the same static/adaptive thinking-level chain as the frame border while unselected rows keep Pi's native styling.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Keep the pending tool loading animation alive during long-running commands: the updateDisplay render cache now rebuilds pending tool rows so each animation tick re-enters the renderer, preventing the spinner from freezing while a command is still executing.
|
|
18
|
+
|
|
19
|
+
### Migration
|
|
20
|
+
|
|
21
|
+
- No configuration migration is required. Existing canonical v1 files and command names remain supported.
|
|
22
|
+
- For an unpinned npm installation, run `pi update npm:pi-one-ui`; for an existing version pin, run `pi install npm:pi-one-ui@0.7.2`. Fully exit and restart Pi after updating the package.
|
|
23
|
+
|
|
24
|
+
## [0.7.1] - 2026-09-21
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Centralize component composition, settings application, and session lifecycle management in app, with settings and Context Inspector views owned by Overlay Layout.
|
|
29
|
+
- Apply presets with one canonical configuration write and update every affected Layout. Component settings, including Header visibility, apply through their owning Layout; Feature registration switches still require `/reload`.
|
|
30
|
+
- Report corrupt or unreadable configuration files during loading, and distinguish a failed save from a saved configuration that could not be applied to the UI.
|
|
31
|
+
- Coalesce shared Layout redraw requests while preserving forced redraws and session ownership.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- Close managed overlays and input listeners during session replacement, and prevent Context Inspector from reopening a preview's parent window in an ended session.
|
|
36
|
+
|
|
37
|
+
### Migration
|
|
38
|
+
|
|
39
|
+
- No configuration migration is required. Existing canonical v1 files and command names remain supported.
|
|
40
|
+
- For an unpinned npm installation, run `pi update npm:pi-one-ui`; for an existing version pin, run `pi install npm:pi-one-ui@0.7.1`. Fully exit and restart Pi after updating the package.
|
|
41
|
+
|
|
42
|
+
### Release verification
|
|
43
|
+
|
|
44
|
+
- Release source: [`e4451a2`](https://github.com/kerolt/pi-one-ui/commit/e4451a244838550f0914469bd8ff5ddbaebc1f8f). [CI](https://github.com/kerolt/pi-one-ui/actions/runs/35567977181) and [Publish](https://github.com/kerolt/pi-one-ui/actions/runs/35567977360) completed successfully; local and CI `npm run verify` passed 1,176 tests across 69 files, and `npm run pack:check` confirmed 124 package files.
|
|
45
|
+
- Real Pi regular/fullscreen TUI probes passed, covering settings, Context Inspector, Editor input and focus, presets, reload, session tree, compaction, session replacement, input listeners, and third-party Editor ownership. User-specific terminal, theme, and extension combinations still require manual verification.
|
|
46
|
+
- At publication, npm `latest` resolved to `0.7.1` with the matching release `gitHead` and package SHA-1 `0252f3b21190f56170a96ff8c3a2a1e8df74874f`. The [provenance statement](https://registry.npmjs.org/-/npm/v1/attestations/pi-one-ui@0.7.1) was published at [Sigstore log index 2905054424](https://search.sigstore.dev/?logIndex=2905054424).
|
|
47
|
+
|
|
9
48
|
## [0.7.0] - 2026-09-13
|
|
10
49
|
|
|
11
50
|
### Added
|
|
@@ -21,6 +60,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
21
60
|
- No configuration migration is required. Existing configurations keep showing the Editor directory; set `components.editor.styles.minimalist.showCwd` to `false` only if you want to hide it. Footer directory settings remain independent.
|
|
22
61
|
- For an unpinned npm installation, run `pi update npm:pi-one-ui`; for an existing version pin, run `pi install npm:pi-one-ui@0.7.0`. Fully exit and restart Pi after updating the package. Subsequent JSON configuration edits can be applied with `/reload`.
|
|
23
62
|
|
|
63
|
+
### Release verification
|
|
64
|
+
|
|
65
|
+
- Release source: [`10ac3de`](https://github.com/kerolt/pi-one-ui/commit/10ac3dea3f0ac804fef94cbeea937c6f1ae0d347). [CI](https://github.com/kerolt/pi-one-ui/actions/runs/34740987283) and [Publish](https://github.com/kerolt/pi-one-ui/actions/runs/34741047850) completed successfully; `npm run verify` passed 1,165 tests and `npm run pack:check` confirmed 121 package files.
|
|
66
|
+
- At publication, npm `latest` resolved to `0.7.0` with the matching release `gitHead` and [provenance metadata](https://registry.npmjs.org/-/npm/v1/attestations/pi-one-ui@0.7.0). The provenance statement was published at [Sigstore log index 2814335389](https://search.sigstore.dev/?logIndex=2814335389).
|
|
67
|
+
|
|
24
68
|
## [0.6.1] - 2026-09-06
|
|
25
69
|
|
|
26
70
|
### Added
|
|
@@ -130,7 +174,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
130
174
|
|
|
131
175
|
- Reduced rendering lag when expanding settled tool groups by reusing cached child output.
|
|
132
176
|
|
|
133
|
-
[Unreleased]: https://github.com/kerolt/pi-one-ui/compare/v0.7.
|
|
177
|
+
[Unreleased]: https://github.com/kerolt/pi-one-ui/compare/v0.7.2...HEAD
|
|
178
|
+
[0.7.2]: https://github.com/kerolt/pi-one-ui/compare/v0.7.1...v0.7.2
|
|
179
|
+
[0.7.1]: https://github.com/kerolt/pi-one-ui/compare/v0.7.0...v0.7.1
|
|
134
180
|
[0.7.0]: https://github.com/kerolt/pi-one-ui/compare/v0.6.1...v0.7.0
|
|
135
181
|
[0.6.1]: https://github.com/kerolt/pi-one-ui/compare/v0.6.0...v0.6.1
|
|
136
182
|
[0.6.0]: https://github.com/kerolt/pi-one-ui/compare/v0.5.1...v0.6.0
|
package/README.en.md
CHANGED
|
@@ -76,9 +76,11 @@ Then open the unified settings panel:
|
|
|
76
76
|
/oneui
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
### Upgrading to 0.7.
|
|
79
|
+
### Upgrading to 0.7.2
|
|
80
80
|
|
|
81
|
-
This release
|
|
81
|
+
This release paints the Editor completion dropdown selection with the editor frame color so it follows the adaptive thinking-level chain, and keeps the tool loading animation alive during long-running commands.
|
|
82
|
+
|
|
83
|
+
Existing canonical v1 configurations and commands remain compatible; no migration is required.
|
|
82
84
|
|
|
83
85
|
For an unpinned npm installation, run:
|
|
84
86
|
|
|
@@ -86,9 +88,7 @@ For an unpinned npm installation, run:
|
|
|
86
88
|
pi update npm:pi-one-ui
|
|
87
89
|
```
|
|
88
90
|
|
|
89
|
-
If your installation pins a version, run `pi install npm:pi-one-ui@0.7.
|
|
90
|
-
|
|
91
|
-
To hide the Editor directory, set `components.editor.styles.minimalist.showCwd: false`; Footer directory visibility is configured independently. See the complete example in the [configuration guide](./docs/configuration.md#隐藏-editor-目录在-footer-左侧显示).
|
|
91
|
+
If your installation pins a version, run `pi install npm:pi-one-ui@0.7.2` to update the pin. Fully exit and restart Pi after updating the package.
|
|
92
92
|
|
|
93
93
|
## Configuration
|
|
94
94
|
|
|
@@ -102,9 +102,11 @@ To hide the Editor directory, set `components.editor.styles.minimalist.showCwd:
|
|
|
102
102
|
|
|
103
103
|
You can configure the extension in two ways:
|
|
104
104
|
|
|
105
|
-
1. **Interactive settings panel (recommended)**: Run `/oneui` in Pi to adjust
|
|
105
|
+
1. **Interactive settings panel (recommended)**: Run `/oneui` in Pi to adjust component toggles, styles, and border modes. Component settings apply after saving; Features switches apply after `/reload`. Each preset saves all related settings in one update and applies them to the corresponding components.
|
|
106
106
|
2. **Direct JSON editing**: Advanced users can edit the JSON configuration file directly for finer-grained control. After saving edits, run `/reload` in Pi to apply changes. When the file does not exist, safe runtime defaults are used in memory.
|
|
107
107
|
|
|
108
|
+
Corrupt or unreadable configuration files produce an error. Failed saves retain the current settings; if saving succeeds but a component cannot apply the change, the panel reports the application failure explicitly.
|
|
109
|
+
|
|
108
110
|
### Basic configuration example
|
|
109
111
|
|
|
110
112
|
Below is a typical v1 configuration structure:
|
|
@@ -156,7 +158,7 @@ To keep the configuration section clean and focused, in-depth options, template
|
|
|
156
158
|
- Saved changes apply the next time `/oneui` opens; no `/reload` needed.
|
|
157
159
|
- **Color system & theme customization**: see [Editor Colors Reference (docs/editor-colors.md)](./docs/editor-colors.md)
|
|
158
160
|
- All colors resolve through unified theme semantics. Configured `colors.*` values resolve as theme tokens (adapting automatically across themes), while ANSI names map to semantic tokens (e.g. `red` to `error`). Fixed terminal colors can be specified using hex codes, 256-color indexes, or `fg:`/`bg:` prefixes.
|
|
159
|
-
- Full support for adaptive thinking-effort border colors (from Low to Max) and labels.
|
|
161
|
+
- Full support for adaptive thinking-effort border colors (from Low to Max) and labels. The completion menu selection shares the editor frame color and follows the same chain.
|
|
160
162
|
- **Migration from legacy versions**: Field cleanups from 0.5.x/0.6.0 (such as merging `opencode` into `minimalist`, or removing `colorSource`) are covered in [docs/configuration.md: Changes and Migration](./docs/configuration.md#6-变更与迁移).
|
|
161
163
|
|
|
162
164
|
## Upstream origins and project evolution
|
|
@@ -230,6 +232,17 @@ npm run verify
|
|
|
230
232
|
npm run pack:check
|
|
231
233
|
```
|
|
232
234
|
|
|
235
|
+
### Source responsibilities
|
|
236
|
+
|
|
237
|
+
- `extensions/app/runtime/`: composition, shared events, session lifecycle, render scheduling, and patch management.
|
|
238
|
+
- `extensions/app/config/`: configuration types, normalization, and shared storage.
|
|
239
|
+
- `extensions/app/settings/`: settings operations, presets, and the `/oneui` opening flow.
|
|
240
|
+
- `extensions/app/overlay/`: overlay lifecycle, focus coordination, and input routing.
|
|
241
|
+
- `extensions/layouts/`: component behavior and rendering; settings, Context Inspector, and Selector views live in `layouts/overlay/`.
|
|
242
|
+
- `extensions/features/` and `extensions/services/`: features and shared data, composed by app.
|
|
243
|
+
|
|
244
|
+
See [ARCHITECTURE.md](./ARCHITECTURE.md) for module rules and real Pi TUI verification commands.
|
|
245
|
+
|
|
233
246
|
### Test organization
|
|
234
247
|
|
|
235
248
|
All tests run through Vitest and are organized into domain directories:
|
package/README.md
CHANGED
|
@@ -76,9 +76,11 @@ pi install git:github.com/kerolt/pi-one-ui
|
|
|
76
76
|
/oneui
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
### 升级到 0.7.
|
|
79
|
+
### 升级到 0.7.2
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
本次统一编辑器补全下拉选中项与编辑器外框的颜色,选中项随 thinking 档位自适应变化;同时修复长时间命令执行期间工具加载动画停止刷新的问题。
|
|
82
|
+
|
|
83
|
+
现有 canonical v1 配置和命令保持兼容,无需迁移。
|
|
82
84
|
|
|
83
85
|
未固定版本的 npm 安装可执行:
|
|
84
86
|
|
|
@@ -86,9 +88,7 @@ pi install git:github.com/kerolt/pi-one-ui
|
|
|
86
88
|
pi update npm:pi-one-ui
|
|
87
89
|
```
|
|
88
90
|
|
|
89
|
-
若安装时固定了版本,执行 `pi install npm:pi-one-ui@0.7.
|
|
90
|
-
|
|
91
|
-
如需隐藏 Editor 目录,设置 `components.editor.styles.minimalist.showCwd: false`;Footer 中的目录显示独立配置。完整示例见 [配置指南](./docs/configuration.md#隐藏-editor-目录在-footer-左侧显示)。
|
|
91
|
+
若安装时固定了版本,执行 `pi install npm:pi-one-ui@0.7.2` 更新版本指定。更新安装包后,完整退出并重启 Pi。
|
|
92
92
|
|
|
93
93
|
## 配置
|
|
94
94
|
|
|
@@ -102,9 +102,11 @@ pi update npm:pi-one-ui
|
|
|
102
102
|
|
|
103
103
|
提供两种配置途径:
|
|
104
104
|
|
|
105
|
-
1. **交互式设置面板(推荐)**:在 Pi 会话中运行 `/oneui
|
|
105
|
+
1. **交互式设置面板(推荐)**:在 Pi 会话中运行 `/oneui`,即可调整常用组件开关、样式和边框模式。组件设置保存后即时应用;Features 开关在 `/reload` 后应用。Preset 一次保存全部相关设置并更新对应组件。
|
|
106
106
|
2. **手动编辑配置文件**:高级用户可直接编辑 JSON 配置文件以启用更多细粒度选项。修改保存后,在 Pi 中执行 `/reload` 即可生效。文件不存在时将直接使用内置默认值。
|
|
107
107
|
|
|
108
|
+
配置文件损坏或无法读取时会报告错误。保存失败时保持当前设置;保存成功但组件应用失败时,面板会明确提示应用失败。
|
|
109
|
+
|
|
108
110
|
### 基础配置示例
|
|
109
111
|
|
|
110
112
|
以下为一个典型的 v1 配置文件结构:
|
|
@@ -156,7 +158,7 @@ pi update npm:pi-one-ui
|
|
|
156
158
|
- 修改保存后下次打开 `/oneui` 即生效,无需 `/reload`。
|
|
157
159
|
- **颜色体系与主题定制**:详见 [Editor 颜色配置说明 (docs/editor-colors.md)](./docs/editor-colors.md)
|
|
158
160
|
- 所有颜色统一按 Theme 语义解释,优先解析为当前主题语义 Token(随主题自动切换),ANSI 色名自动映射为语义色(如 `red` 对应 `error`);如需固定色彩,可直接指定 Hex、256 色索引或 `fg:`/`bg:` 前缀。
|
|
159
|
-
- 支持完整的 Thinking 思考档位(Low 至 Max
|
|
161
|
+
- 支持完整的 Thinking 思考档位(Low 至 Max)自适应边框与标签分级配色,补全下拉选中项与编辑器外框同色,跟随同一条颜色链。
|
|
160
162
|
- **历史版本迁移**:旧版升级带来的字段收敛(如 `opencode` 样式统一合并入 `minimalist`、`colorSource` 双模式移除等)参见 [docs/configuration.md 变更与迁移节](./docs/configuration.md#6-变更与迁移)。
|
|
161
163
|
|
|
162
164
|
## 上游来源与项目演进
|
|
@@ -230,6 +232,17 @@ npm run verify
|
|
|
230
232
|
npm run pack:check
|
|
231
233
|
```
|
|
232
234
|
|
|
235
|
+
### 源码职责
|
|
236
|
+
|
|
237
|
+
- `extensions/app/runtime/`:装配、共享事件、session 生命周期、渲染调度和 patch 管理。
|
|
238
|
+
- `extensions/app/config/`:配置类型、规范化和统一存储。
|
|
239
|
+
- `extensions/app/settings/`:设置操作、Preset 与 `/oneui` 打开流程。
|
|
240
|
+
- `extensions/app/overlay/`:Overlay 生命周期、焦点协作和输入路由。
|
|
241
|
+
- `extensions/layouts/`:组件行为与渲染;设置面板、Context Inspector 和 Selector 视图位于 `layouts/overlay/`。
|
|
242
|
+
- `extensions/features/`、`extensions/services/`:功能实现与共享数据,由 app 统一组织。
|
|
243
|
+
|
|
244
|
+
模块约束和真实 Pi TUI 验证命令见 [ARCHITECTURE.md](./ARCHITECTURE.md)。
|
|
245
|
+
|
|
233
246
|
### 测试组织
|
|
234
247
|
|
|
235
248
|
所有测试统一由 Vitest 执行,并按照功能领域组织在子目录中:
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { booleanValue, recordValue } from "./values.ts";
|
|
2
|
+
|
|
3
|
+
export type UserMessageStyle =
|
|
4
|
+
| "framed"
|
|
5
|
+
| "framed-copy-friendly"
|
|
6
|
+
| "compact"
|
|
7
|
+
| "labeled";
|
|
8
|
+
export type FramedUserMessageStyleConfig = Record<string, never>;
|
|
9
|
+
export type FramedCopyFriendlyUserMessageStyleConfig = Record<string, never>;
|
|
10
|
+
export type CompactUserMessageStyleConfig = Record<string, never>;
|
|
11
|
+
export type LabeledUserMessageStyleConfig = Record<string, never>;
|
|
12
|
+
export type UserMessagesComponentConfig = {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
style: UserMessageStyle;
|
|
15
|
+
styles: {
|
|
16
|
+
framed: FramedUserMessageStyleConfig;
|
|
17
|
+
"framed-copy-friendly": FramedCopyFriendlyUserMessageStyleConfig;
|
|
18
|
+
compact: CompactUserMessageStyleConfig;
|
|
19
|
+
labeled: LabeledUserMessageStyleConfig;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function normalizeUserMessages(
|
|
24
|
+
value: unknown,
|
|
25
|
+
): UserMessagesComponentConfig {
|
|
26
|
+
const source = recordValue(value);
|
|
27
|
+
return {
|
|
28
|
+
enabled: booleanValue(source.enabled, true),
|
|
29
|
+
style:
|
|
30
|
+
source.style === "framed-copy-friendly" ||
|
|
31
|
+
source.style === "compact" ||
|
|
32
|
+
source.style === "labeled"
|
|
33
|
+
? source.style
|
|
34
|
+
: "framed",
|
|
35
|
+
styles: {
|
|
36
|
+
framed: {},
|
|
37
|
+
"framed-copy-friendly": {},
|
|
38
|
+
compact: {},
|
|
39
|
+
labeled: {},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { booleanValue, recordValue } from "./values.ts";
|
|
2
|
+
|
|
3
|
+
export type ModelLabelSource = "id" | "name";
|
|
4
|
+
export type EditorStyle = "on" | "off";
|
|
5
|
+
export type EditorBorderColorMode = "static" | "adaptive";
|
|
6
|
+
export type MinimalistPathDisplayMode = "compact" | "project" | "full";
|
|
7
|
+
export type MinimalistEditorStyleConfig = {
|
|
8
|
+
pathDisplay: MinimalistPathDisplayMode;
|
|
9
|
+
showCwd: boolean;
|
|
10
|
+
showSessionName: boolean;
|
|
11
|
+
showTimer: boolean;
|
|
12
|
+
showCost: boolean;
|
|
13
|
+
showGit: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type MinimalistConfig = MinimalistEditorStyleConfig;
|
|
16
|
+
export type EditorStylesConfig = { minimalist: MinimalistEditorStyleConfig };
|
|
17
|
+
export type EditorComponentConfig = {
|
|
18
|
+
style: EditorStyle;
|
|
19
|
+
borderColorMode: EditorBorderColorMode;
|
|
20
|
+
modelLabel: ModelLabelSource;
|
|
21
|
+
viewportIndicators: boolean;
|
|
22
|
+
styles: EditorStylesConfig;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const DEFAULT_EDITOR_METADATA_FORMAT = "$model $provider( $thinking)";
|
|
26
|
+
export const defaultMinimalistStyle: MinimalistEditorStyleConfig = {
|
|
27
|
+
pathDisplay: "compact",
|
|
28
|
+
showCwd: true,
|
|
29
|
+
showSessionName: true,
|
|
30
|
+
showTimer: true,
|
|
31
|
+
showCost: true,
|
|
32
|
+
showGit: true,
|
|
33
|
+
};
|
|
34
|
+
export const defaultEditor: EditorComponentConfig = {
|
|
35
|
+
style: "on",
|
|
36
|
+
borderColorMode: "static",
|
|
37
|
+
modelLabel: "id",
|
|
38
|
+
viewportIndicators: true,
|
|
39
|
+
styles: { minimalist: defaultMinimalistStyle },
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function parseEditorModelLabel(value: unknown): ModelLabelSource {
|
|
43
|
+
return value === "name" ? "name" : "id";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function parseEditorStyle(value: unknown): EditorStyle {
|
|
47
|
+
return value === "off" ? "off" : "on";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function parseEditorBorderColorMode(
|
|
51
|
+
value: unknown,
|
|
52
|
+
): EditorBorderColorMode {
|
|
53
|
+
return value === "adaptive" ? "adaptive" : "static";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** 配置规范化仅生成 canonical 字段。 */
|
|
57
|
+
export function normalizeEditor(value: unknown): EditorComponentConfig {
|
|
58
|
+
const editor = recordValue(value);
|
|
59
|
+
const minimalist = recordValue(recordValue(editor.styles).minimalist);
|
|
60
|
+
return {
|
|
61
|
+
style: editor.enabled === false ? "off" : parseEditorStyle(editor.style),
|
|
62
|
+
borderColorMode: parseEditorBorderColorMode(editor.borderColorMode),
|
|
63
|
+
modelLabel: parseEditorModelLabel(editor.modelLabel),
|
|
64
|
+
viewportIndicators: booleanValue(editor.viewportIndicators, true),
|
|
65
|
+
styles: {
|
|
66
|
+
minimalist: {
|
|
67
|
+
pathDisplay:
|
|
68
|
+
minimalist.pathDisplay === "project" ||
|
|
69
|
+
minimalist.pathDisplay === "full"
|
|
70
|
+
? minimalist.pathDisplay
|
|
71
|
+
: "compact",
|
|
72
|
+
showCwd: booleanValue(minimalist.showCwd, true),
|
|
73
|
+
showSessionName: booleanValue(minimalist.showSessionName, true),
|
|
74
|
+
showTimer: booleanValue(minimalist.showTimer, true),
|
|
75
|
+
showCost: booleanValue(minimalist.showCost, true),
|
|
76
|
+
showGit: booleanValue(minimalist.showGit, true),
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { type ModelLabelSource, parseEditorModelLabel } from "./editor.ts";
|
|
2
|
+
import { booleanValue, recordValue } from "./values.ts";
|
|
3
|
+
|
|
4
|
+
export type ContextStyle = "text" | "gauge" | "text+gauge";
|
|
5
|
+
export type SeparatorStyle = "pipe" | "dot" | "chevron" | "none";
|
|
6
|
+
export type FooterStyle = "native" | "starship" | "hidden";
|
|
7
|
+
export type CompactFooterMaxLines = 1 | 2 | 3 | "unlimited";
|
|
8
|
+
export type ContextThresholds = { warning: number; error: number };
|
|
9
|
+
export type PathDisplayMode = "basename" | "full";
|
|
10
|
+
export type PathDisplayConfig = { mode: PathDisplayMode; depth: number };
|
|
11
|
+
export type GitBranchMaxLength = "full" | number;
|
|
12
|
+
export type GitBranchConfig = { maxLength: GitBranchMaxLength };
|
|
13
|
+
export type GitCommitConfig = {
|
|
14
|
+
hashLength: number;
|
|
15
|
+
onlyDetached: boolean;
|
|
16
|
+
showTag: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type GitMetricsConfig = {
|
|
19
|
+
onlyNonzero: boolean;
|
|
20
|
+
ignoreSubmodules: boolean;
|
|
21
|
+
};
|
|
22
|
+
export type ExtensionStatusPlacement = "off" | "left" | "middle" | "right";
|
|
23
|
+
export type ExtensionStatusColorMode = "zentui" | "original";
|
|
24
|
+
export type ExtensionStatusesConfig = {
|
|
25
|
+
defaultPlacement: ExtensionStatusPlacement;
|
|
26
|
+
placements: Record<string, ExtensionStatusPlacement>;
|
|
27
|
+
colorModes: Record<string, ExtensionStatusColorMode>;
|
|
28
|
+
};
|
|
29
|
+
export type FooterSegmentsConfig = {
|
|
30
|
+
cwd: boolean;
|
|
31
|
+
sessionName: boolean;
|
|
32
|
+
gitBranch: boolean;
|
|
33
|
+
gitStatus: boolean;
|
|
34
|
+
gitCounts: boolean;
|
|
35
|
+
gitCommit: boolean;
|
|
36
|
+
gitMetrics: boolean;
|
|
37
|
+
runtime: boolean;
|
|
38
|
+
modelInfo: boolean;
|
|
39
|
+
context: boolean;
|
|
40
|
+
tokens: boolean;
|
|
41
|
+
cost: boolean;
|
|
42
|
+
sessionDuration: boolean;
|
|
43
|
+
username: boolean;
|
|
44
|
+
time: boolean;
|
|
45
|
+
os: boolean;
|
|
46
|
+
packageVersion: boolean;
|
|
47
|
+
};
|
|
48
|
+
export type StarshipFooterStyleConfig = {
|
|
49
|
+
format: string;
|
|
50
|
+
responsive: boolean;
|
|
51
|
+
compactFormat: string;
|
|
52
|
+
compactMaxLines: CompactFooterMaxLines;
|
|
53
|
+
separator: SeparatorStyle;
|
|
54
|
+
contextStyle: ContextStyle;
|
|
55
|
+
contextThresholds: ContextThresholds;
|
|
56
|
+
pathDisplay: PathDisplayConfig;
|
|
57
|
+
segments: FooterSegmentsConfig;
|
|
58
|
+
gitBranch: GitBranchConfig;
|
|
59
|
+
gitCommit: GitCommitConfig;
|
|
60
|
+
gitMetrics: GitMetricsConfig;
|
|
61
|
+
extensionStatuses: ExtensionStatusesConfig;
|
|
62
|
+
};
|
|
63
|
+
export type FooterComponentConfig = {
|
|
64
|
+
style: FooterStyle;
|
|
65
|
+
modelLabel: ModelLabelSource;
|
|
66
|
+
styles: { starship: StarshipFooterStyleConfig };
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const DEFAULT_COMPACT_FOOTER_FORMAT =
|
|
70
|
+
"$cwd$wrap(in $session_name)$wrap(on $git_branch) $git_status$wrap$context$wrap_sep$tokens";
|
|
71
|
+
export const FOOTER_FORMAT_VARIABLES = [
|
|
72
|
+
"cwd",
|
|
73
|
+
"session_name",
|
|
74
|
+
"git_branch",
|
|
75
|
+
"git_status",
|
|
76
|
+
"git_state",
|
|
77
|
+
"runtime",
|
|
78
|
+
"model",
|
|
79
|
+
"provider",
|
|
80
|
+
"session_duration",
|
|
81
|
+
"username",
|
|
82
|
+
"os",
|
|
83
|
+
"time",
|
|
84
|
+
"context",
|
|
85
|
+
"tokens",
|
|
86
|
+
"cache_read",
|
|
87
|
+
"cache_write",
|
|
88
|
+
"cost",
|
|
89
|
+
"subscription",
|
|
90
|
+
"auto_compaction",
|
|
91
|
+
"package",
|
|
92
|
+
"package_version",
|
|
93
|
+
"git_commit",
|
|
94
|
+
"git_tag",
|
|
95
|
+
"git_metrics",
|
|
96
|
+
"git_added",
|
|
97
|
+
"git_deleted",
|
|
98
|
+
"sep",
|
|
99
|
+
] as const;
|
|
100
|
+
export const FOOTER_FORMAT_ALIASES: Record<string, string> = {
|
|
101
|
+
directory: "cwd",
|
|
102
|
+
branch: "git_branch",
|
|
103
|
+
status: "git_status",
|
|
104
|
+
state: "git_state",
|
|
105
|
+
commit: "git_commit",
|
|
106
|
+
tag: "git_tag",
|
|
107
|
+
duration: "session_duration",
|
|
108
|
+
separator: "sep",
|
|
109
|
+
};
|
|
110
|
+
export const defaultFooterSegments: FooterSegmentsConfig = {
|
|
111
|
+
cwd: true,
|
|
112
|
+
sessionName: true,
|
|
113
|
+
gitBranch: true,
|
|
114
|
+
gitStatus: true,
|
|
115
|
+
gitCounts: false,
|
|
116
|
+
gitCommit: false,
|
|
117
|
+
gitMetrics: false,
|
|
118
|
+
runtime: true,
|
|
119
|
+
modelInfo: false,
|
|
120
|
+
context: true,
|
|
121
|
+
tokens: true,
|
|
122
|
+
cost: true,
|
|
123
|
+
sessionDuration: false,
|
|
124
|
+
username: false,
|
|
125
|
+
time: false,
|
|
126
|
+
os: false,
|
|
127
|
+
packageVersion: false,
|
|
128
|
+
};
|
|
129
|
+
export const defaultStarshipStyle: StarshipFooterStyleConfig = {
|
|
130
|
+
format: "",
|
|
131
|
+
responsive: true,
|
|
132
|
+
compactFormat: DEFAULT_COMPACT_FOOTER_FORMAT,
|
|
133
|
+
compactMaxLines: 2,
|
|
134
|
+
separator: "pipe",
|
|
135
|
+
contextStyle: "text",
|
|
136
|
+
contextThresholds: { warning: 70, error: 90 },
|
|
137
|
+
pathDisplay: { mode: "basename", depth: 0 },
|
|
138
|
+
segments: defaultFooterSegments,
|
|
139
|
+
gitBranch: { maxLength: "full" },
|
|
140
|
+
gitCommit: { hashLength: 7, onlyDetached: true, showTag: true },
|
|
141
|
+
gitMetrics: { onlyNonzero: true, ignoreSubmodules: false },
|
|
142
|
+
extensionStatuses: {
|
|
143
|
+
defaultPlacement: "right",
|
|
144
|
+
placements: {},
|
|
145
|
+
colorModes: {},
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
export const defaultFooter: FooterComponentConfig = {
|
|
149
|
+
style: "starship",
|
|
150
|
+
modelLabel: "id",
|
|
151
|
+
styles: { starship: defaultStarshipStyle },
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export function isSeparatorStyle(value: unknown): value is SeparatorStyle {
|
|
155
|
+
return (
|
|
156
|
+
value === "pipe" ||
|
|
157
|
+
value === "dot" ||
|
|
158
|
+
value === "chevron" ||
|
|
159
|
+
value === "none"
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
export function parseSeparatorStyle(value: unknown): SeparatorStyle {
|
|
163
|
+
return isSeparatorStyle(value) ? value : "pipe";
|
|
164
|
+
}
|
|
165
|
+
export function parseContextStyle(value: unknown): ContextStyle {
|
|
166
|
+
return value === "gauge" || value === "text+gauge" ? value : "text";
|
|
167
|
+
}
|
|
168
|
+
export function parseCompactFooterMaxLines(
|
|
169
|
+
value: unknown,
|
|
170
|
+
): CompactFooterMaxLines {
|
|
171
|
+
return value === 1 || value === 2 || value === 3 || value === "unlimited"
|
|
172
|
+
? value
|
|
173
|
+
: 2;
|
|
174
|
+
}
|
|
175
|
+
export function isExtensionStatusPlacement(
|
|
176
|
+
value: unknown,
|
|
177
|
+
): value is ExtensionStatusPlacement {
|
|
178
|
+
return (
|
|
179
|
+
value === "off" ||
|
|
180
|
+
value === "left" ||
|
|
181
|
+
value === "middle" ||
|
|
182
|
+
value === "right"
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
export function isExtensionStatusColorMode(
|
|
186
|
+
value: unknown,
|
|
187
|
+
): value is ExtensionStatusColorMode {
|
|
188
|
+
return value === "zentui" || value === "original";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function thresholds(value: unknown): ContextThresholds {
|
|
192
|
+
const source = recordValue(value);
|
|
193
|
+
const percent = (input: unknown, defaultValue: number) =>
|
|
194
|
+
typeof input === "number" && Number.isFinite(input)
|
|
195
|
+
? Math.max(0, Math.min(100, Math.round(input)))
|
|
196
|
+
: defaultValue;
|
|
197
|
+
const warning = percent(source.warning, 70);
|
|
198
|
+
const error = percent(source.error, 90);
|
|
199
|
+
return { warning: Math.min(warning, error), error: Math.max(warning, error) };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** 每个 Footer 字段独立规范化,返回完整的 canonical 配置。 */
|
|
203
|
+
export function normalizeFooter(value: unknown): FooterComponentConfig {
|
|
204
|
+
const footer = recordValue(value);
|
|
205
|
+
const source = recordValue(recordValue(footer.styles).starship);
|
|
206
|
+
const path = recordValue(source.pathDisplay);
|
|
207
|
+
const branch = recordValue(source.gitBranch);
|
|
208
|
+
const commit = recordValue(source.gitCommit);
|
|
209
|
+
const metrics = recordValue(source.gitMetrics);
|
|
210
|
+
const statuses = recordValue(source.extensionStatuses);
|
|
211
|
+
const segments = recordValue(source.segments);
|
|
212
|
+
const hashLength =
|
|
213
|
+
typeof commit.hashLength === "number"
|
|
214
|
+
? commit.hashLength
|
|
215
|
+
: Number(commit.hashLength);
|
|
216
|
+
return {
|
|
217
|
+
style:
|
|
218
|
+
footer.style === "native" || footer.style === "hidden"
|
|
219
|
+
? footer.style
|
|
220
|
+
: "starship",
|
|
221
|
+
modelLabel: parseEditorModelLabel(footer.modelLabel),
|
|
222
|
+
styles: {
|
|
223
|
+
starship: {
|
|
224
|
+
format: typeof source.format === "string" ? source.format : "",
|
|
225
|
+
responsive: booleanValue(source.responsive, true),
|
|
226
|
+
compactFormat:
|
|
227
|
+
typeof source.compactFormat === "string" &&
|
|
228
|
+
source.compactFormat.length > 0
|
|
229
|
+
? source.compactFormat
|
|
230
|
+
: DEFAULT_COMPACT_FOOTER_FORMAT,
|
|
231
|
+
compactMaxLines: parseCompactFooterMaxLines(source.compactMaxLines),
|
|
232
|
+
separator: parseSeparatorStyle(source.separator),
|
|
233
|
+
contextStyle: parseContextStyle(source.contextStyle),
|
|
234
|
+
contextThresholds: thresholds(source.contextThresholds),
|
|
235
|
+
pathDisplay: {
|
|
236
|
+
mode: path.mode === "full" ? "full" : "basename",
|
|
237
|
+
depth:
|
|
238
|
+
typeof path.depth === "number" &&
|
|
239
|
+
Number.isFinite(path.depth) &&
|
|
240
|
+
path.depth >= 0
|
|
241
|
+
? Math.min(5, Math.floor(path.depth))
|
|
242
|
+
: 0,
|
|
243
|
+
},
|
|
244
|
+
segments: Object.fromEntries(
|
|
245
|
+
Object.entries(defaultFooterSegments).map(([key, enabled]) => [
|
|
246
|
+
key,
|
|
247
|
+
booleanValue(segments[key], enabled),
|
|
248
|
+
]),
|
|
249
|
+
) as FooterSegmentsConfig,
|
|
250
|
+
gitBranch: {
|
|
251
|
+
maxLength:
|
|
252
|
+
typeof branch.maxLength === "number" &&
|
|
253
|
+
Number.isInteger(branch.maxLength) &&
|
|
254
|
+
branch.maxLength > 0
|
|
255
|
+
? branch.maxLength
|
|
256
|
+
: "full",
|
|
257
|
+
},
|
|
258
|
+
gitCommit: {
|
|
259
|
+
hashLength: Number.isFinite(hashLength)
|
|
260
|
+
? Math.min(40, Math.max(4, Math.round(hashLength)))
|
|
261
|
+
: 7,
|
|
262
|
+
onlyDetached: booleanValue(commit.onlyDetached, true),
|
|
263
|
+
showTag: booleanValue(commit.showTag, true),
|
|
264
|
+
},
|
|
265
|
+
gitMetrics: {
|
|
266
|
+
onlyNonzero: booleanValue(metrics.onlyNonzero, true),
|
|
267
|
+
ignoreSubmodules: booleanValue(metrics.ignoreSubmodules, false),
|
|
268
|
+
},
|
|
269
|
+
extensionStatuses: {
|
|
270
|
+
defaultPlacement: isExtensionStatusPlacement(
|
|
271
|
+
statuses.defaultPlacement,
|
|
272
|
+
)
|
|
273
|
+
? statuses.defaultPlacement
|
|
274
|
+
: "right",
|
|
275
|
+
placements: Object.fromEntries(
|
|
276
|
+
Object.entries(recordValue(statuses.placements)).filter(
|
|
277
|
+
(entry): entry is [string, ExtensionStatusPlacement] =>
|
|
278
|
+
isExtensionStatusPlacement(entry[1]),
|
|
279
|
+
),
|
|
280
|
+
),
|
|
281
|
+
colorModes: Object.fromEntries(
|
|
282
|
+
Object.entries(recordValue(statuses.colorModes)).filter(
|
|
283
|
+
(entry): entry is [string, ExtensionStatusColorMode] =>
|
|
284
|
+
isExtensionStatusColorMode(entry[1]),
|
|
285
|
+
),
|
|
286
|
+
),
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
@@ -109,9 +109,5 @@ export function normalizePanelOverlay(input: unknown): PanelOverlayConfig {
|
|
|
109
109
|
* every /oneui open so JSON edits apply without `/reload`.
|
|
110
110
|
*/
|
|
111
111
|
export function loadPanelOverlayConfig(): PanelOverlayConfig {
|
|
112
|
-
|
|
113
|
-
return normalizePanelOverlay(configStore.read().panel);
|
|
114
|
-
} catch {
|
|
115
|
-
return normalizePanelOverlay(undefined);
|
|
116
|
-
}
|
|
112
|
+
return normalizePanelOverlay(configStore.read().panel);
|
|
117
113
|
}
|
|
@@ -364,12 +364,7 @@ export const config: Config = loadConfig();
|
|
|
364
364
|
* Loads and normalizes the renderer projection from the shared store.
|
|
365
365
|
*/
|
|
366
366
|
function loadConfig(): Config {
|
|
367
|
-
|
|
368
|
-
return normalizeConfig(rendererConfigFrom(configStore.read()));
|
|
369
|
-
} catch {
|
|
370
|
-
// Ignore bad config and fall back to defaults.
|
|
371
|
-
}
|
|
372
|
-
return { ...DEFAULT_CONFIG };
|
|
367
|
+
return normalizeConfig(rendererConfigFrom(configStore.read()));
|
|
373
368
|
}
|
|
374
369
|
|
|
375
370
|
/**
|