pi-one-ui 0.6.0 → 0.7.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.
- package/CHANGELOG.md +25 -1
- package/README.en.md +73 -43
- package/README.md +74 -45
- package/extensions/app/config/panel.ts +117 -0
- package/extensions/app/config/renderer.ts +4 -0
- package/extensions/app/config/shell.ts +10 -0
- package/extensions/app/panel.ts +81 -13
- package/extensions/features/effort-command.ts +79 -0
- package/extensions/layouts/context/index.ts +4 -0
- package/extensions/layouts/editor/minimalist-editor.ts +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,28 @@ 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.0] - 2026-09-13
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added `components.editor.styles.minimalist.showCwd` to independently show or hide the Editor's working-directory label, including the `/oneui` preview. It defaults to `true`, so existing configurations need no migration. Setting it to `false` preserves `pathDisplay` and leaves Git metadata and Footer directory settings unchanged; edit the JSON configuration and run `/reload` to apply it.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Restore keyboard focus to the visible Editor after `/oneui` replaces an editor while changing its settings, including native-to-custom transitions, third-party wrappers, and replacement rollback. Closing the panel no longer sends subsequent typing to the detached previous editor in regular or fullscreen mode; existing overlays and later editor owners retain focus.
|
|
18
|
+
|
|
19
|
+
### Migration
|
|
20
|
+
|
|
21
|
+
- 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
|
+
- 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
|
+
|
|
24
|
+
## [0.6.1] - 2026-09-06
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Built-in `/effort` command for selecting the thinking effort level (interactive picker, argument autocomplete, and model-clamping feedback), gated by `renderer.enableEffortCommand` (default `true`) and toggleable from the `/oneui` Features section. Replaces the need for a standalone local effort extension — remove any self-installed `/effort` extension to avoid duplicate command registration.
|
|
29
|
+
- The `/oneui` settings panel overlay placement is now configurable via the optional top-level `panel` key in `pi-one-ui.json` (`anchor`, `width`, `maxHeight`, `margin`), replacing the previously hardcoded `top-center`/`85%`/`90%` layout. Invalid values fall back to the defaults, and edits apply the next time the panel opens.
|
|
30
|
+
|
|
9
31
|
## [0.6.0] - 2026-09-05
|
|
10
32
|
|
|
11
33
|
### Removed
|
|
@@ -108,7 +130,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
108
130
|
|
|
109
131
|
- Reduced rendering lag when expanding settled tool groups by reusing cached child output.
|
|
110
132
|
|
|
111
|
-
[Unreleased]: https://github.com/kerolt/pi-one-ui/compare/v0.
|
|
133
|
+
[Unreleased]: https://github.com/kerolt/pi-one-ui/compare/v0.7.0...HEAD
|
|
134
|
+
[0.7.0]: https://github.com/kerolt/pi-one-ui/compare/v0.6.1...v0.7.0
|
|
135
|
+
[0.6.1]: https://github.com/kerolt/pi-one-ui/compare/v0.6.0...v0.6.1
|
|
112
136
|
[0.6.0]: https://github.com/kerolt/pi-one-ui/compare/v0.5.1...v0.6.0
|
|
113
137
|
[0.5.1]: https://github.com/kerolt/pi-one-ui/compare/v0.5.0...v0.5.1
|
|
114
138
|
[0.5.0]: https://github.com/kerolt/pi-one-ui/compare/v0.4.0...v0.5.0
|
package/README.en.md
CHANGED
|
@@ -6,43 +6,44 @@
|
|
|
6
6
|
|
|
7
7
|
[简体中文](./README.md) | English
|
|
8
8
|
|
|
9
|
-
`pi-one-ui` is a unified TUI extension package for [Pi](https://pi.dev)
|
|
9
|
+
`pi-one-ui` is a unified TUI extension package for [Pi](https://pi.dev), designed to deliver a clean, beautiful, and efficient terminal experience. It originated as a source-level combination of:
|
|
10
10
|
|
|
11
|
-
- the terminal shell capabilities of [pi-zentui](https://github.com/lmilojevicc/pi-zentui)
|
|
11
|
+
- the terminal shell and layout capabilities of [pi-zentui](https://github.com/lmilojevicc/pi-zentui)
|
|
12
12
|
- the conversation rendering and productivity features of [pi-cc-extensions](https://github.com/minuque/pi-cc-extensions)
|
|
13
13
|
|
|
14
|
-
The result is a single installable and configurable Pi package that continues to evolve through
|
|
14
|
+
The result is a single installable and configurable Pi package that continues to evolve through architectural refactoring, strict boundary ownership, and ongoing optimization.
|
|
15
15
|
|
|
16
16
|
## Features
|
|
17
17
|
|
|
18
18
|
### Unified interface layout
|
|
19
19
|
|
|
20
|
-
`pi-one-ui` organizes the Pi interface into
|
|
20
|
+
`pi-one-ui` organizes the Pi interface into a clean hierarchy:
|
|
21
21
|
|
|
22
22
|
```text
|
|
23
23
|
Header → Context → WorkingLine → Editor → Footer
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
- **Header**:
|
|
27
|
-
- **Context**:
|
|
28
|
-
- **WorkingLine**:
|
|
29
|
-
- **Editor**:
|
|
30
|
-
- **Footer**:
|
|
31
|
-
- **Overlay**:
|
|
26
|
+
- **Header**: Startup information, logo, and shortcut hints.
|
|
27
|
+
- **Context**: Conversation content area, including user messages, assistant messages, thinking blocks, tool calls, diffs, Markdown, and turn summaries.
|
|
28
|
+
- **WorkingLine**: Working state indicator, spinner, token/thought/elapsed statistics, live output throughput, and turn summaries.
|
|
29
|
+
- **Editor**: Input editor, completion menu, metadata display, and the Minimalist style (with effortless toggle to Pi native).
|
|
30
|
+
- **Footer**: Current directory, Git status, runtime info, token/cost tracking, and extension statuses.
|
|
31
|
+
- **Overlay**: Temporary views such as the settings panel and Context Inspector, managed centrally by OverlayManager.
|
|
32
32
|
|
|
33
33
|
### Built-in functionality
|
|
34
34
|
|
|
35
35
|
| Feature | Description | Entry point |
|
|
36
36
|
| ---------------------- | ------------------------------------------------------------------------------------------- | ----------------- |
|
|
37
|
-
| Unified settings panel |
|
|
38
|
-
| Context Inspector | Shows context usage and previews
|
|
39
|
-
| Session reference | Searches previous Pi sessions or
|
|
40
|
-
| Subagent autocomplete |
|
|
41
|
-
| Tool / Diff renderer |
|
|
42
|
-
| Subagent live renderer | Preserves
|
|
43
|
-
| Markdown enhancement | Adds Mermaid, admonitions, URL linking, and
|
|
44
|
-
| Built-in themes |
|
|
37
|
+
| Unified settings panel | Centrally manages settings for Header, Context, WorkingLine, Editor, Footer, and Features | `/oneui` |
|
|
38
|
+
| Context Inspector | Shows context usage and previews system prompt, memory, skills, tools, and message contents | `/context` |
|
|
39
|
+
| Session reference | Searches previous Pi sessions or subagents and injects their relevant context | `@` completion |
|
|
40
|
+
| Subagent autocomplete | Provides subagent name completion and delegation hints | `@` completion |
|
|
41
|
+
| Tool / Diff renderer | Unified rendering for tool executions, results, collapsible blocks, and Edit/Write diffs | Automatic |
|
|
42
|
+
| Subagent live renderer | Preserves subagents' dedicated progress cards without grouping them into generic tool calls | Automatic |
|
|
43
|
+
| Markdown enhancement | Adds Mermaid diagrams, admonitions, clickable URL linking, and rendering improvements | Automatic |
|
|
44
|
+
| Built-in themes | Includes CC Dark and CC Light themes | `/theme` |
|
|
45
45
|
| Compatibility aliases | Optionally provides common command aliases | `/clear`, `/exit` |
|
|
46
|
+
| Effort command | Interactively or directly switch the active model's thinking effort level | `/effort` |
|
|
46
47
|
|
|
47
48
|
## Quick start
|
|
48
49
|
|
|
@@ -75,15 +76,38 @@ Then open the unified settings panel:
|
|
|
75
76
|
/oneui
|
|
76
77
|
```
|
|
77
78
|
|
|
79
|
+
### Upgrading to 0.7.0
|
|
80
|
+
|
|
81
|
+
This release adds an Editor working-directory visibility switch and fixes invisible input after changing Editor settings and closing `/oneui`. Existing configurations need no migration; the directory remains visible by default.
|
|
82
|
+
|
|
83
|
+
For an unpinned npm installation, run:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pi update npm:pi-one-ui
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
If your installation pins a version, run `pi install npm:pi-one-ui@0.7.0` to update the pin. Fully exit and restart Pi after updating the package.
|
|
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-左侧显示).
|
|
92
|
+
|
|
78
93
|
## Configuration
|
|
79
94
|
|
|
80
|
-
|
|
95
|
+
### Configuration methods
|
|
96
|
+
|
|
97
|
+
`pi-one-ui` uses a single canonical v1 configuration file:
|
|
81
98
|
|
|
82
99
|
```text
|
|
83
100
|
~/.pi/agent/pi-one-ui.json
|
|
84
101
|
```
|
|
85
102
|
|
|
86
|
-
|
|
103
|
+
You can configure the extension in two ways:
|
|
104
|
+
|
|
105
|
+
1. **Interactive settings panel (recommended)**: Run `/oneui` in Pi to adjust common component toggles, styles, and border modes via a visual menu. Changes take effect immediately and are persisted automatically.
|
|
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
|
+
|
|
108
|
+
### Basic configuration example
|
|
109
|
+
|
|
110
|
+
Below is a typical v1 configuration structure:
|
|
87
111
|
|
|
88
112
|
```json
|
|
89
113
|
{
|
|
@@ -93,52 +117,58 @@ Using the `/oneui` settings panel is recommended. The panel uses a top-centered
|
|
|
93
117
|
"style": "on",
|
|
94
118
|
"borderColorMode": "static"
|
|
95
119
|
},
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"style": "framed"
|
|
120
|
+
"footer": {
|
|
121
|
+
"style": "starship"
|
|
99
122
|
},
|
|
100
123
|
"workingLine": {
|
|
101
124
|
"enabled": true
|
|
102
125
|
},
|
|
103
|
-
"
|
|
104
|
-
"
|
|
126
|
+
"userMessages": {
|
|
127
|
+
"enabled": true,
|
|
128
|
+
"style": "framed"
|
|
105
129
|
}
|
|
106
130
|
},
|
|
107
131
|
"renderer": {
|
|
108
132
|
"mode": "on",
|
|
109
133
|
"diffViewMode": "auto"
|
|
134
|
+
},
|
|
135
|
+
"panel": {
|
|
136
|
+
"anchor": "top-center",
|
|
137
|
+
"width": "85%",
|
|
138
|
+
"maxHeight": "90%",
|
|
139
|
+
"margin": { "top": 6, "right": 1, "bottom": 1, "left": 1 }
|
|
110
140
|
}
|
|
111
141
|
}
|
|
112
142
|
```
|
|
113
143
|
|
|
114
|
-
|
|
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.
|
|
144
|
+
### Advanced configuration & documentation guide
|
|
117
145
|
|
|
118
|
-
|
|
146
|
+
To keep the configuration section clean and focused, in-depth options, template variables, and color references are organized in dedicated documentation:
|
|
119
147
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
148
|
+
- **Component options & layout customization**: see [Editor & Footer Configuration Guide (docs/configuration.md)](./docs/configuration.md)
|
|
149
|
+
- **Editor**: Configure `style` (`on` for Minimalist decoration / `off` for Pi native), `borderColorMode` (`static` or `adaptive` to thinking effort), `modelLabel`, and granular Minimalist displays (directory visibility and format, session name, timer, cost, Git status, etc.). `components.editor.styles.minimalist.showCwd` (since 0.7.0, default `true`) can hide the directory independently of the Footer; `pathDisplay` continues to select its format.
|
|
150
|
+
- **Footer**: Starship-style layout powered by format templates (`$cwd`, `$git_branch`, `$tokens`, `$cost`, etc.), individual `segments` toggles, customizable separators, and context-usage indicators (gauge or text).
|
|
151
|
+
- **WorkingLine**: Built-in live output throughput tracking (appends e.g. `⚡12 tok/s` when a model response runs for at least 500ms, reset per turn).
|
|
152
|
+
- **Settings panel (`/oneui`) customization**: the optional top-level `panel` section controls the overlay placement and size (all fields default to the values shown in the example above).
|
|
153
|
+
- `anchor`: one of `center`, `top-left`, `top-right`, `bottom-left`, `bottom-right`, `top-center`, `bottom-center`, `left-center`, `right-center`.
|
|
154
|
+
- `width` / `maxHeight`: absolute column/row counts, or percentage strings like `"85%"`.
|
|
155
|
+
- `margin`: distance from the terminal edges — either a single number for all sides, or an object with per-edge `top`/`right`/`bottom`/`left` values.
|
|
156
|
+
- Saved changes apply the next time `/oneui` opens; no `/reload` needed.
|
|
157
|
+
- **Color system & theme customization**: see [Editor Colors Reference (docs/editor-colors.md)](./docs/editor-colors.md)
|
|
158
|
+
- 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.
|
|
160
|
+
- **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-变更与迁移).
|
|
129
161
|
|
|
130
162
|
## Upstream origins and project evolution
|
|
131
163
|
|
|
132
|
-
`pi-one-ui` originally
|
|
164
|
+
`pi-one-ui` was originally built upon source code from two open-source projects. We express our sincere appreciation to both upstream projects and their contributors:
|
|
133
165
|
|
|
134
166
|
| Upstream project | Capabilities incorporated into `pi-one-ui` | Reference baseline |
|
|
135
167
|
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------- |
|
|
136
|
-
| [pi-zentui](https://github.com/lmilojevicc/pi-zentui) | Starship-style Footer,
|
|
168
|
+
| [pi-zentui](https://github.com/lmilojevicc/pi-zentui) | Starship-style Footer, Editor layouts, and shell interaction capabilities | v0.21.0, commit `5341b38` |
|
|
137
169
|
| [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` |
|
|
138
170
|
|
|
139
|
-
Production code lives in `extensions/`.
|
|
140
|
-
|
|
141
|
-
The upstream projects provided the original foundation. Continued development focuses on turning these capabilities into one coherent product with clear seams and sustainable maintenance.
|
|
171
|
+
Production code lives in `extensions/`. `pi-one-ui` has unified the composition entry point, configuration storage, lifecycle management, layout ownership, overlay orchestration, and input routing. The project now evolves independently and no longer tracks upstream changes directly.
|
|
142
172
|
|
|
143
173
|
See [ARCHITECTURE.md](./ARCHITECTURE.md) for detailed module boundaries, event flow, and ownership conventions.
|
|
144
174
|
|
package/README.md
CHANGED
|
@@ -6,43 +6,44 @@
|
|
|
6
6
|
|
|
7
7
|
简体中文 | [English](./README.en.md)
|
|
8
8
|
|
|
9
|
-
`pi-one-ui`
|
|
9
|
+
`pi-one-ui` 是面向 [Pi](https://pi.dev) 的统一 TUI 扩展包,旨在打造简洁、美观且高效的终端交互界面。项目最初通过源码级融合,将:
|
|
10
10
|
|
|
11
|
-
- [pi-zentui](https://github.com/lmilojevicc/pi-zentui)
|
|
11
|
+
- [pi-zentui](https://github.com/lmilojevicc/pi-zentui) 的终端外壳与布局能力
|
|
12
12
|
- [pi-cc-extensions](https://github.com/minuque/pi-cc-extensions) 的对话内容渲染及生产力功能
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
整合为开箱即用、统一可配置的单一扩展包,并在此基础上持续进行模块重构、职责收敛和体验优化。
|
|
15
15
|
|
|
16
16
|
## 特性
|
|
17
17
|
|
|
18
18
|
### 统一的界面布局
|
|
19
19
|
|
|
20
|
-
`pi-one-ui` 将 Pi
|
|
20
|
+
`pi-one-ui` 将 Pi 的交互界面划分为以下布局层级:
|
|
21
21
|
|
|
22
22
|
```text
|
|
23
23
|
Header → Context → WorkingLine → Editor → Footer
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
- **Header**:启动信息、Logo 和快捷键提示。
|
|
27
|
-
- **Context**:对话内容区,包含用户消息、Assistant 消息、Thinking
|
|
28
|
-
- **WorkingLine
|
|
29
|
-
- **Editor
|
|
30
|
-
- **Footer
|
|
31
|
-
- **Overlay**:设置面板、Context Inspector
|
|
27
|
+
- **Context**:对话内容区,包含用户消息、Assistant 消息、Thinking 思考过程、Tool 执行、Diff 对比、Markdown 及回合摘要。
|
|
28
|
+
- **WorkingLine**:工作状态指示、Spinner、Token/思考时长统计、实时吞吐速率及回合摘要。
|
|
29
|
+
- **Editor**:输入编辑器、补全建议、元数据展示及 Minimalist 极简样式(支持一键切换 Pi 原生)。
|
|
30
|
+
- **Footer**:工作目录、Git 状态、运行时信息、Token/费用统计及扩展状态等。
|
|
31
|
+
- **Overlay**:设置面板、Context Inspector 等临时浮层,统一由 OverlayManager 调度管理。
|
|
32
32
|
|
|
33
33
|
### 内置功能
|
|
34
34
|
|
|
35
35
|
| 功能 | 说明 | 入口 |
|
|
36
36
|
| --------------------- | ---------------------------------------------------------------------- | ----------------- |
|
|
37
|
-
| 统一设置面板 |
|
|
38
|
-
| Context Inspector | 查看上下文占用,并预览 System prompt、Memory、Skills、Tools
|
|
39
|
-
| Session reference |
|
|
40
|
-
| Subagent autocomplete | 提供
|
|
41
|
-
| Tool / Diff renderer |
|
|
42
|
-
| Subagent live renderer | 保留
|
|
43
|
-
| Markdown enhancement | 支持 Mermaid
|
|
44
|
-
| Built-in themes |
|
|
37
|
+
| 统一设置面板 | 集中管理 Header、Context、WorkingLine、Editor、Footer 等组件及功能设置 | `/oneui` |
|
|
38
|
+
| Context Inspector | 查看上下文占用,并预览 System prompt、Memory、Skills、Tools 及消息内容 | `/context` |
|
|
39
|
+
| Session reference | 搜索并引用注入历史 Pi 会话或 Subagent 的有效上下文 | `@` 补全 |
|
|
40
|
+
| Subagent autocomplete | 提供 Subagent 名称补全与委派提示 | `@` 补全 |
|
|
41
|
+
| Tool / Diff renderer | 工具调用、执行结果、折叠内容与 Edit/Write Diff 的统一美化渲染 | 自动生效 |
|
|
42
|
+
| Subagent live renderer | 保留 subagents 专用进度卡片,避免混入通用工具分组 | 自动生效 |
|
|
43
|
+
| Markdown enhancement | 支持 Mermaid 图表、提示框与 URL 链接化等增强渲染 | 自动生效 |
|
|
44
|
+
| Built-in themes | 内置 CC Dark 和 CC Light 主题 | `/theme` |
|
|
45
45
|
| Compatibility aliases | 可选提供常用命令别名 | `/clear`、`/exit` |
|
|
46
|
+
| Effort command | 交互式或直接切换当前模型的 Thinking 思考档位 | `/effort` |
|
|
46
47
|
|
|
47
48
|
## 快速开始
|
|
48
49
|
|
|
@@ -75,16 +76,38 @@ pi install git:github.com/kerolt/pi-one-ui
|
|
|
75
76
|
/oneui
|
|
76
77
|
```
|
|
77
78
|
|
|
79
|
+
### 升级到 0.7.0
|
|
80
|
+
|
|
81
|
+
本次新增 Editor 工作目录显示开关,并修复修改 Editor 配置后关闭 `/oneui` 导致输入不可见的问题。现有配置无需迁移,工作目录仍默认显示。
|
|
82
|
+
|
|
83
|
+
未固定版本的 npm 安装可执行:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pi update npm:pi-one-ui
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
若安装时固定了版本,执行 `pi install npm:pi-one-ui@0.7.0` 更新版本指定。更新安装包后,完整退出并重启 Pi。
|
|
90
|
+
|
|
91
|
+
如需隐藏 Editor 目录,设置 `components.editor.styles.minimalist.showCwd: false`;Footer 中的目录显示独立配置。完整示例见 [配置指南](./docs/configuration.md#隐藏-editor-目录在-footer-左侧显示)。
|
|
92
|
+
|
|
78
93
|
## 配置
|
|
79
94
|
|
|
80
|
-
|
|
95
|
+
### 配置方式
|
|
96
|
+
|
|
97
|
+
`pi-one-ui` 统一使用 Canonical v1 格式的配置文件:
|
|
81
98
|
|
|
82
99
|
```text
|
|
83
100
|
~/.pi/agent/pi-one-ui.json
|
|
84
101
|
```
|
|
85
102
|
|
|
103
|
+
提供两种配置途径:
|
|
104
|
+
|
|
105
|
+
1. **交互式设置面板(推荐)**:在 Pi 会话中运行 `/oneui`,即可在可视化面板中即时调整常用组件开关、样式和边框模式。修改即刻生效并自动持久化。
|
|
106
|
+
2. **手动编辑配置文件**:高级用户可直接编辑 JSON 配置文件以启用更多细粒度选项。修改保存后,在 Pi 中执行 `/reload` 即可生效。文件不存在时将直接使用内置默认值。
|
|
86
107
|
|
|
87
|
-
|
|
108
|
+
### 基础配置示例
|
|
109
|
+
|
|
110
|
+
以下为一个典型的 v1 配置文件结构:
|
|
88
111
|
|
|
89
112
|
```json
|
|
90
113
|
{
|
|
@@ -94,54 +117,60 @@ pi install git:github.com/kerolt/pi-one-ui
|
|
|
94
117
|
"style": "on",
|
|
95
118
|
"borderColorMode": "static"
|
|
96
119
|
},
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"style": "framed"
|
|
120
|
+
"footer": {
|
|
121
|
+
"style": "starship"
|
|
100
122
|
},
|
|
101
123
|
"workingLine": {
|
|
102
124
|
"enabled": true
|
|
103
125
|
},
|
|
104
|
-
"
|
|
105
|
-
"
|
|
126
|
+
"userMessages": {
|
|
127
|
+
"enabled": true,
|
|
128
|
+
"style": "framed"
|
|
106
129
|
}
|
|
107
130
|
},
|
|
108
131
|
"renderer": {
|
|
109
132
|
"mode": "on",
|
|
110
133
|
"diffViewMode": "auto"
|
|
134
|
+
},
|
|
135
|
+
"panel": {
|
|
136
|
+
"anchor": "top-center",
|
|
137
|
+
"width": "85%",
|
|
138
|
+
"maxHeight": "90%",
|
|
139
|
+
"margin": { "top": 6, "right": 1, "bottom": 1, "left": 1 }
|
|
111
140
|
}
|
|
112
141
|
}
|
|
113
142
|
```
|
|
114
143
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
WorkingLine 的 token segment 会在一次模型响应持续至少 500ms 后追加实时输出速率,例如 `⚡12 tok/s`。速率按当前响应独立计算,在下一次 `turn_start` 时重置;关闭 token segment 时也会一并隐藏。
|
|
144
|
+
### 深度配置与文档指引
|
|
118
145
|
|
|
119
|
-
|
|
146
|
+
各项组件开关、模板变量与颜色字段的完整规范拆分收录于独立文档中,便于按需查阅:
|
|
120
147
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
148
|
+
- **组件开关与排版定制**:详见 [Editor 与 Footer 配置指南 (docs/configuration.md)](./docs/configuration.md)
|
|
149
|
+
- **Editor**:支持 `style`(`on` 极简装饰 / `off` 原生)、`borderColorMode`(固定色 / 思考档位自适应)、`modelLabel` 及 Minimalist 装饰细节(目录显示与格式、会话名、耗时、费用、Git 状态等)。`components.editor.styles.minimalist.showCwd`(0.7.0 起,默认 `true`)可单独隐藏工作目录,不影响 Footer;`pathDisplay` 继续控制目录格式。
|
|
150
|
+
- **Footer**:支持 Starship 风格排版,提供丰富的模板变量(`$cwd`、`$git_branch`、`$tokens`、`$cost` 等),支持通过 `format` 自定义或通过 `segments` 控制各段开关,并可自由配置上下文占用率的展示形式(gauge / text)。
|
|
151
|
+
- **WorkingLine**:内置实时输出速率检测(单次响应持续 >=500ms 自动追加如 `⚡12 tok/s`,按回合独立重置)。
|
|
152
|
+
- **设置面板(`/oneui`)定制**:通过顶层 `panel` 字段控制浮层位置与尺寸,全部字段可省略(缺省值见上文示例)。
|
|
153
|
+
- `anchor`:锚点,支持 `center`、`top-left`、`top-right`、`bottom-left`、`bottom-right`、`top-center`、`bottom-center`、`left-center`、`right-center`。
|
|
154
|
+
- `width` / `maxHeight`:列数 / 行数,或 `"85%"` 形式的百分比字符串。
|
|
155
|
+
- `margin`:距终端边缘的外边距,可以是四边统一数字,也可以是按 `top`/`right`/`bottom`/`left` 分别配置的对象。
|
|
156
|
+
- 修改保存后下次打开 `/oneui` 即生效,无需 `/reload`。
|
|
157
|
+
- **颜色体系与主题定制**:详见 [Editor 颜色配置说明 (docs/editor-colors.md)](./docs/editor-colors.md)
|
|
158
|
+
- 所有颜色统一按 Theme 语义解释,优先解析为当前主题语义 Token(随主题自动切换),ANSI 色名自动映射为语义色(如 `red` 对应 `error`);如需固定色彩,可直接指定 Hex、256 色索引或 `fg:`/`bg:` 前缀。
|
|
159
|
+
- 支持完整的 Thinking 思考档位(Low 至 Max)自适应边框与标签分级配色。
|
|
160
|
+
- **历史版本迁移**:旧版升级带来的字段收敛(如 `opencode` 样式统一合并入 `minimalist`、`colorSource` 双模式移除等)参见 [docs/configuration.md 变更与迁移节](./docs/configuration.md#6-变更与迁移)。
|
|
130
161
|
|
|
131
162
|
## 上游来源与项目演进
|
|
132
163
|
|
|
133
|
-
`pi-one-ui`
|
|
164
|
+
`pi-one-ui` 最初以两个开源项目的源码为基础进行整合。由衷感谢两个上游项目及其贡献者的出色工作:
|
|
134
165
|
|
|
135
|
-
| 上游项目 | 融入 `pi-one-ui` 的主要能力 | 参照
|
|
166
|
+
| 上游项目 | 融入 `pi-one-ui` 的主要能力 | 参照 Baseline |
|
|
136
167
|
| --------------------------------------------------------------- | -------------------------------------------------------------------------- | ------------------------- |
|
|
137
|
-
| [pi-zentui](https://github.com/lmilojevicc/pi-zentui) | Starship 风格 Footer、
|
|
138
|
-
| [pi-cc-extensions](https://github.com/minuque/pi-cc-extensions) | Claude Code 风格 Context
|
|
139
|
-
|
|
140
|
-
融合后的生产代码位于 `extensions/`。项目已经在原始实现之上统一入口、配置存储、生命周期、Layout ownership、Overlay 和输入路由,并会继续独立演进;当前实现不再等同于任一上游项目,也不会自动跟随上游同步。
|
|
168
|
+
| [pi-zentui](https://github.com/lmilojevicc/pi-zentui) | Starship 风格 Footer、Editor 基础布局与 Shell 交互能力 | v0.21.0,commit `5341b38` |
|
|
169
|
+
| [pi-cc-extensions](https://github.com/minuque/pi-cc-extensions) | Claude Code 风格 Context 渲染器、Tool/Diff 视图、Context Inspector 与会话引用 | v0.8.67,commit `dba37e5` |
|
|
141
170
|
|
|
142
|
-
|
|
171
|
+
融合后的生产代码位于 `extensions/`。`pi-one-ui` 在原始实现之上完成了入口统一、配置存储规范化、生命周期治理、Layout Ownership 收敛、Overlay 统一调度与输入路由解耦,并持续独立演进。当前实现已完全独立于上游,不依赖也不自动同步上游分支。
|
|
143
172
|
|
|
144
|
-
|
|
173
|
+
更详细的模块边界、事件流与 Ownership 约定参见 [ARCHITECTURE.md](./ARCHITECTURE.md)。
|
|
145
174
|
|
|
146
175
|
## 本地开发
|
|
147
176
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
OverlayAnchor,
|
|
3
|
+
OverlayMargin,
|
|
4
|
+
SizeValue,
|
|
5
|
+
} from "@earendil-works/pi-tui";
|
|
6
|
+
import { configStore } from "./store.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Overlay placement for the /oneui settings panel. Mirrors the Pi
|
|
10
|
+
* `OverlayOptions` subset the panel supports; persisted under the
|
|
11
|
+
* top-level `panel` key in `pi-one-ui.json`.
|
|
12
|
+
*/
|
|
13
|
+
export type PanelOverlayConfig = {
|
|
14
|
+
anchor: OverlayAnchor;
|
|
15
|
+
width: SizeValue;
|
|
16
|
+
maxHeight: SizeValue;
|
|
17
|
+
margin: OverlayMargin | number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Defaults preserve the historically hardcoded /oneui panel placement. */
|
|
21
|
+
export const DEFAULT_PANEL_OVERLAY: PanelOverlayConfig = {
|
|
22
|
+
anchor: "top-center",
|
|
23
|
+
width: "85%",
|
|
24
|
+
maxHeight: "90%",
|
|
25
|
+
margin: { top: 6, right: 1, bottom: 1, left: 1 },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const PANEL_ANCHORS: readonly OverlayAnchor[] = [
|
|
29
|
+
"center",
|
|
30
|
+
"top-left",
|
|
31
|
+
"top-right",
|
|
32
|
+
"bottom-left",
|
|
33
|
+
"bottom-right",
|
|
34
|
+
"top-center",
|
|
35
|
+
"bottom-center",
|
|
36
|
+
"left-center",
|
|
37
|
+
"right-center",
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
41
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseAnchor(value: unknown): OverlayAnchor {
|
|
45
|
+
return PANEL_ANCHORS.some((anchor) => anchor === value)
|
|
46
|
+
? (value as OverlayAnchor)
|
|
47
|
+
: DEFAULT_PANEL_OVERLAY.anchor;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Accepts a positive column/row count or a "N%" string within 0..100. */
|
|
51
|
+
function parseSizeValue(value: unknown, fallback: SizeValue): SizeValue {
|
|
52
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
53
|
+
return Math.floor(value);
|
|
54
|
+
}
|
|
55
|
+
if (typeof value === "string") {
|
|
56
|
+
const match = /^(\d+(?:\.\d+)?)%$/.exec(value.trim());
|
|
57
|
+
if (match) {
|
|
58
|
+
const percent = Number(match[1]);
|
|
59
|
+
if (percent > 0 && percent <= 100) {
|
|
60
|
+
return value.trim() as SizeValue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return fallback;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parseMarginEdge(value: unknown): number | undefined {
|
|
68
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0
|
|
69
|
+
? value
|
|
70
|
+
: undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Accepts one non-negative integer for all edges or a per-edge object. */
|
|
74
|
+
function parseMargin(value: unknown): OverlayMargin | number {
|
|
75
|
+
const fallback = DEFAULT_PANEL_OVERLAY.margin;
|
|
76
|
+
if (typeof value === "number") {
|
|
77
|
+
return parseMarginEdge(value) ?? fallback;
|
|
78
|
+
}
|
|
79
|
+
if (!isRecord(value)) {
|
|
80
|
+
return fallback;
|
|
81
|
+
}
|
|
82
|
+
const defaults = isRecord(fallback) ? (fallback as OverlayMargin) : {};
|
|
83
|
+
const margin: OverlayMargin = {};
|
|
84
|
+
for (const edge of ["top", "right", "bottom", "left"] as const) {
|
|
85
|
+
margin[edge] = parseMarginEdge(value[edge]) ?? defaults[edge];
|
|
86
|
+
}
|
|
87
|
+
return margin;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Normalizes the raw `panel` config section, replacing invalid fields with
|
|
92
|
+
* defaults so a broken entry never blocks the /oneui panel from opening.
|
|
93
|
+
*/
|
|
94
|
+
export function normalizePanelOverlay(input: unknown): PanelOverlayConfig {
|
|
95
|
+
const source = isRecord(input) ? input : {};
|
|
96
|
+
return {
|
|
97
|
+
anchor: parseAnchor(source.anchor),
|
|
98
|
+
width: parseSizeValue(source.width, DEFAULT_PANEL_OVERLAY.width),
|
|
99
|
+
maxHeight: parseSizeValue(
|
|
100
|
+
source.maxHeight,
|
|
101
|
+
DEFAULT_PANEL_OVERLAY.maxHeight,
|
|
102
|
+
),
|
|
103
|
+
margin: parseMargin(source.margin),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Loads the panel overlay placement from the shared store. Read fresh on
|
|
109
|
+
* every /oneui open so JSON edits apply without `/reload`.
|
|
110
|
+
*/
|
|
111
|
+
export function loadPanelOverlayConfig(): PanelOverlayConfig {
|
|
112
|
+
try {
|
|
113
|
+
return normalizePanelOverlay(configStore.read().panel);
|
|
114
|
+
} catch {
|
|
115
|
+
return normalizePanelOverlay(undefined);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -62,6 +62,7 @@ export type Config = {
|
|
|
62
62
|
enableContextCommand: boolean;
|
|
63
63
|
enableAgentSummary: boolean;
|
|
64
64
|
enableAliases: boolean;
|
|
65
|
+
enableEffortCommand: boolean;
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
function rendererConfigFrom(record: ConfigRecord): ConfigRecord {
|
|
@@ -166,6 +167,7 @@ export const DEFAULT_CONFIG: Config = {
|
|
|
166
167
|
enableContextCommand: true,
|
|
167
168
|
enableAgentSummary: true,
|
|
168
169
|
enableAliases: true,
|
|
170
|
+
enableEffortCommand: true,
|
|
169
171
|
};
|
|
170
172
|
|
|
171
173
|
function pickEnum<T extends string>(
|
|
@@ -296,6 +298,7 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
296
298
|
enableContextCommand: source.enableContextCommand !== false,
|
|
297
299
|
enableAgentSummary: source.enableAgentSummary !== false,
|
|
298
300
|
enableAliases: source.enableAliases !== false,
|
|
301
|
+
enableEffortCommand: source.enableEffortCommand !== false,
|
|
299
302
|
};
|
|
300
303
|
}
|
|
301
304
|
|
|
@@ -350,6 +353,7 @@ export function formatConfigStatus(source: Config = config): string {
|
|
|
350
353
|
`context=${source.enableContextCommand ? "on" : "off"}`,
|
|
351
354
|
`agentSummary=${source.enableAgentSummary ? "on" : "off"}`,
|
|
352
355
|
`aliases=${source.enableAliases ? "on" : "off"}`,
|
|
356
|
+
`effort=${source.enableEffortCommand ? "on" : "off"}`,
|
|
353
357
|
].join(" · ");
|
|
354
358
|
}
|
|
355
359
|
|
|
@@ -97,6 +97,8 @@ export type FooterSegmentsConfig = {
|
|
|
97
97
|
|
|
98
98
|
export type MinimalistEditorStyleConfig = {
|
|
99
99
|
pathDisplay: MinimalistPathDisplayMode;
|
|
100
|
+
/** Controls the Editor directory label independently of its format and the Footer. */
|
|
101
|
+
showCwd: boolean;
|
|
100
102
|
showSessionName: boolean;
|
|
101
103
|
showTimer: boolean;
|
|
102
104
|
showCost: boolean;
|
|
@@ -395,6 +397,7 @@ const defaultFooterSegments: FooterSegmentsConfig = {
|
|
|
395
397
|
|
|
396
398
|
const defaultMinimalistStyle: MinimalistEditorStyleConfig = {
|
|
397
399
|
pathDisplay: "compact",
|
|
400
|
+
showCwd: true,
|
|
398
401
|
showSessionName: true,
|
|
399
402
|
showTimer: true,
|
|
400
403
|
showCost: true,
|
|
@@ -1001,6 +1004,10 @@ function resolveComponents(config: ConfigRecord): ComponentsConfig {
|
|
|
1001
1004
|
minimalist.pathDisplay === "full"
|
|
1002
1005
|
? minimalist.pathDisplay
|
|
1003
1006
|
: defaultMinimalistStyle.pathDisplay,
|
|
1007
|
+
showCwd: parseBoolean(
|
|
1008
|
+
minimalist.showCwd,
|
|
1009
|
+
defaultMinimalistStyle.showCwd,
|
|
1010
|
+
),
|
|
1004
1011
|
showSessionName: parseBoolean(
|
|
1005
1012
|
minimalist.showSessionName,
|
|
1006
1013
|
defaultMinimalistStyle.showSessionName,
|
|
@@ -1386,6 +1393,9 @@ function applyMinimalistStylePatch(
|
|
|
1386
1393
|
patch: Partial<MinimalistEditorStyleConfig>,
|
|
1387
1394
|
): void {
|
|
1388
1395
|
if (patch.pathDisplay !== undefined) style.pathDisplay = patch.pathDisplay;
|
|
1396
|
+
if (patch.showCwd !== undefined) {
|
|
1397
|
+
style.showCwd = patch.showCwd;
|
|
1398
|
+
}
|
|
1389
1399
|
if (patch.showSessionName !== undefined)
|
|
1390
1400
|
style.showSessionName = patch.showSessionName;
|
|
1391
1401
|
if (patch.showTimer !== undefined) style.showTimer = patch.showTimer;
|
package/extensions/app/panel.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ExtensionCommandContext,
|
|
3
3
|
type ExtensionContext,
|
|
4
|
+
type ExtensionUIContext,
|
|
4
5
|
getSettingsListTheme,
|
|
5
6
|
} from "@earendil-works/pi-coding-agent";
|
|
6
7
|
import {
|
|
8
|
+
type Component,
|
|
7
9
|
matchesKey,
|
|
10
|
+
type OverlayHandle,
|
|
8
11
|
type SettingItem,
|
|
9
12
|
SettingsList,
|
|
10
13
|
truncateToWidth,
|
|
@@ -13,6 +16,7 @@ import {
|
|
|
13
16
|
renderEditorSettingsPreview,
|
|
14
17
|
renderUserMessageSettingsPreview,
|
|
15
18
|
} from "./commands/settings-previews.ts";
|
|
19
|
+
import { loadPanelOverlayConfig } from "./config/panel.ts";
|
|
16
20
|
import {
|
|
17
21
|
type CompactStyleMode,
|
|
18
22
|
type Config as ContextConfig,
|
|
@@ -303,6 +307,13 @@ function featureItems(config: ContextConfig): SettingItem[] {
|
|
|
303
307
|
currentValue: onOff(config.enableAliases),
|
|
304
308
|
values: ["on", "off"],
|
|
305
309
|
},
|
|
310
|
+
{
|
|
311
|
+
id: "enableEffortCommand",
|
|
312
|
+
label: "Effort command",
|
|
313
|
+
description: "Register the /effort thinking-level command.",
|
|
314
|
+
currentValue: onOff(config.enableEffortCommand),
|
|
315
|
+
values: ["on", "off"],
|
|
316
|
+
},
|
|
306
317
|
];
|
|
307
318
|
}
|
|
308
319
|
|
|
@@ -547,6 +558,7 @@ function updateSetting(
|
|
|
547
558
|
id === "enableContextCommand" ||
|
|
548
559
|
id === "enableAgentSummary" ||
|
|
549
560
|
id === "enableAliases" ||
|
|
561
|
+
id === "enableEffortCommand" ||
|
|
550
562
|
id === "showStartupHeader"
|
|
551
563
|
) {
|
|
552
564
|
const patch = { [id]: isOn(value) } as Partial<ContextConfig>;
|
|
@@ -598,10 +610,24 @@ export async function showOneUiPanel(
|
|
|
598
610
|
|
|
599
611
|
unifiedPanelOpen = true;
|
|
600
612
|
try {
|
|
601
|
-
|
|
613
|
+
// Read fresh on every open so pi-one-ui.json edits apply without /reload.
|
|
614
|
+
const panelOverlay = loadPanelOverlayConfig();
|
|
615
|
+
let panelHandle: Pick<OverlayHandle, "focus" | "unfocus"> | undefined;
|
|
602
616
|
await overlayManager.run(() =>
|
|
603
617
|
ctx.ui.custom(
|
|
604
618
|
(tui, theme, _keybindings, done) => {
|
|
619
|
+
// Pi's concrete renderers expose focus inspection beyond the narrow TUI port.
|
|
620
|
+
const focusTui = tui as typeof tui & {
|
|
621
|
+
getFocusedComponent?: () => Component | null;
|
|
622
|
+
isOverlayFocused?: () => boolean;
|
|
623
|
+
};
|
|
624
|
+
const originalFocus = focusTui.getFocusedComponent?.();
|
|
625
|
+
let editorFocus:
|
|
626
|
+
| {
|
|
627
|
+
component: Component;
|
|
628
|
+
factory: ReturnType<ExtensionUIContext["getEditorComponent"]>;
|
|
629
|
+
}
|
|
630
|
+
| undefined;
|
|
605
631
|
let activeIndex = 0;
|
|
606
632
|
let list: SettingsList;
|
|
607
633
|
const createList = () => {
|
|
@@ -610,6 +636,16 @@ export async function showOneUiPanel(
|
|
|
610
636
|
10,
|
|
611
637
|
getSettingsListTheme(),
|
|
612
638
|
(id, value) => {
|
|
639
|
+
const updatesEditor =
|
|
640
|
+
id === "editorStyle" || id === "editorBorderColorMode";
|
|
641
|
+
if (updatesEditor && originalFocus !== undefined) {
|
|
642
|
+
// Suspend automatic overlay focus restoration while Pi may replace
|
|
643
|
+
// and roll back an editor, keeping the final instance observable.
|
|
644
|
+
panelHandle?.unfocus?.({
|
|
645
|
+
target: editorFocus?.component ?? originalFocus,
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
const previousFocus = focusTui.getFocusedComponent?.();
|
|
613
649
|
try {
|
|
614
650
|
updateSetting(id, value, ctx, deps);
|
|
615
651
|
list.updateValue(id, value);
|
|
@@ -618,13 +654,50 @@ export async function showOneUiPanel(
|
|
|
618
654
|
createList();
|
|
619
655
|
list.selectItem(id);
|
|
620
656
|
} finally {
|
|
621
|
-
if (
|
|
657
|
+
if (updatesEditor) {
|
|
658
|
+
// Replacements (including rollback) focus the new instance, but the
|
|
659
|
+
// overlay still retains its old preFocus. Capture before refocusing.
|
|
660
|
+
const nextFocus = focusTui.getFocusedComponent?.();
|
|
661
|
+
if (
|
|
662
|
+
nextFocus &&
|
|
663
|
+
nextFocus !== previousFocus &&
|
|
664
|
+
!focusTui.isOverlayFocused?.()
|
|
665
|
+
) {
|
|
666
|
+
try {
|
|
667
|
+
editorFocus = {
|
|
668
|
+
component: nextFocus,
|
|
669
|
+
factory: ctx.ui.getEditorComponent(),
|
|
670
|
+
};
|
|
671
|
+
} catch {
|
|
672
|
+
editorFocus = undefined;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
622
675
|
panelHandle?.focus();
|
|
623
676
|
}
|
|
624
677
|
tui.requestRender();
|
|
625
678
|
}
|
|
626
679
|
},
|
|
627
|
-
() =>
|
|
680
|
+
() => {
|
|
681
|
+
done(undefined);
|
|
682
|
+
// Only correct this panel's stale return target; other overlays and
|
|
683
|
+
// later editor owners keep their focus. Stale session contexts may throw.
|
|
684
|
+
if (
|
|
685
|
+
!editorFocus ||
|
|
686
|
+
tui.hasOverlay() ||
|
|
687
|
+
focusTui.getFocusedComponent?.() !== originalFocus
|
|
688
|
+
) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
try {
|
|
692
|
+
if (ctx.ui.getEditorComponent() !== editorFocus.factory) {
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
} catch {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
tui.setFocus(editorFocus.component);
|
|
699
|
+
tui.requestRender();
|
|
700
|
+
},
|
|
628
701
|
);
|
|
629
702
|
};
|
|
630
703
|
createList();
|
|
@@ -680,17 +753,12 @@ export async function showOneUiPanel(
|
|
|
680
753
|
{
|
|
681
754
|
overlay: true,
|
|
682
755
|
overlayOptions: {
|
|
683
|
-
anchor:
|
|
684
|
-
width:
|
|
685
|
-
maxHeight:
|
|
686
|
-
margin:
|
|
687
|
-
top: 6,
|
|
688
|
-
right: 1,
|
|
689
|
-
bottom: 1,
|
|
690
|
-
left: 1,
|
|
691
|
-
},
|
|
756
|
+
anchor: panelOverlay.anchor,
|
|
757
|
+
width: panelOverlay.width,
|
|
758
|
+
maxHeight: panelOverlay.maxHeight,
|
|
759
|
+
margin: panelOverlay.margin,
|
|
692
760
|
},
|
|
693
|
-
onHandle: (handle:
|
|
761
|
+
onHandle: (handle: OverlayHandle) => {
|
|
694
762
|
panelHandle = handle;
|
|
695
763
|
},
|
|
696
764
|
},
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effort Command Feature
|
|
3
|
+
*
|
|
4
|
+
* Registers the /effort command for selecting the active thinking level:
|
|
5
|
+
* /effort → interactive level picker
|
|
6
|
+
* /effort high → apply a level directly (with autocomplete)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
|
|
11
|
+
const EFFORT_LEVELS = [
|
|
12
|
+
"off",
|
|
13
|
+
"minimal",
|
|
14
|
+
"low",
|
|
15
|
+
"medium",
|
|
16
|
+
"high",
|
|
17
|
+
"xhigh",
|
|
18
|
+
"max",
|
|
19
|
+
] as const;
|
|
20
|
+
type EffortLevel = (typeof EFFORT_LEVELS)[number];
|
|
21
|
+
|
|
22
|
+
/** Reports whether a command argument is a supported thinking-effort level. */
|
|
23
|
+
function isEffortLevel(value: string): value is EffortLevel {
|
|
24
|
+
return EFFORT_LEVELS.some((level) => level === value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Registers the /effort command for selecting the active thinking level. */
|
|
28
|
+
export default function effortCommand(pi: ExtensionAPI): void {
|
|
29
|
+
pi.registerCommand("effort", {
|
|
30
|
+
description:
|
|
31
|
+
"Set thinking effort: off, minimal, low, medium, high, xhigh, or max",
|
|
32
|
+
getArgumentCompletions: (prefix) => {
|
|
33
|
+
const normalizedPrefix = prefix.toLowerCase();
|
|
34
|
+
const matches = EFFORT_LEVELS.filter((level) =>
|
|
35
|
+
level.startsWith(normalizedPrefix),
|
|
36
|
+
).map((level) => ({
|
|
37
|
+
value: level,
|
|
38
|
+
label: level,
|
|
39
|
+
}));
|
|
40
|
+
return matches.length > 0 ? matches : null;
|
|
41
|
+
},
|
|
42
|
+
handler: async (args, ctx) => {
|
|
43
|
+
let requested = args.trim().toLowerCase();
|
|
44
|
+
if (!requested) {
|
|
45
|
+
if (!ctx.hasUI) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const selected = await ctx.ui.select(
|
|
49
|
+
`Thinking effort (current: ${pi.getThinkingLevel()})`,
|
|
50
|
+
[...EFFORT_LEVELS],
|
|
51
|
+
);
|
|
52
|
+
if (!selected) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
requested = selected;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!isEffortLevel(requested)) {
|
|
59
|
+
ctx.ui.notify(
|
|
60
|
+
`Unknown effort "${requested}". Expected: ${EFFORT_LEVELS.join(", ")}`,
|
|
61
|
+
"error",
|
|
62
|
+
);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
pi.setThinkingLevel(requested);
|
|
67
|
+
// The active model may clamp unsupported levels; surface the applied value.
|
|
68
|
+
const applied = pi.getThinkingLevel();
|
|
69
|
+
if (applied !== requested) {
|
|
70
|
+
ctx.ui.notify(
|
|
71
|
+
`Requested ${requested}; active model applied ${applied}`,
|
|
72
|
+
"warning",
|
|
73
|
+
);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
ctx.ui.notify(`Thinking effort: ${applied}`, "info");
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -16,6 +16,7 @@ import { type ConfigRecord, configStore } from "../../app/config/store.ts";
|
|
|
16
16
|
// shell
|
|
17
17
|
import piAliases from "../../features/aliases.ts";
|
|
18
18
|
import context from "../../features/context-inspector/index.ts";
|
|
19
|
+
import effortCommand from "../../features/effort-command.ts";
|
|
19
20
|
import { installFlushDockedBash } from "../../features/flush-docked-bash.ts";
|
|
20
21
|
import sessionReference from "../../features/session-reference/index.ts";
|
|
21
22
|
// feature
|
|
@@ -154,6 +155,9 @@ export default function (
|
|
|
154
155
|
): void {
|
|
155
156
|
// shell chrome
|
|
156
157
|
if (config.enableAliases) piAliases(pi);
|
|
158
|
+
if (config.enableEffortCommand) {
|
|
159
|
+
effortCommand(pi);
|
|
160
|
+
}
|
|
157
161
|
installFlushDockedBash();
|
|
158
162
|
// The thinking controller is queried directly by the context render stack.
|
|
159
163
|
markdownEnhance(pi);
|
|
@@ -262,6 +262,9 @@ function renderBottomRight(
|
|
|
262
262
|
uiTheme: Theme,
|
|
263
263
|
config: ZentuiConfig,
|
|
264
264
|
): string {
|
|
265
|
+
if (config.components.editor.styles.minimalist.showCwd === false) {
|
|
266
|
+
return "";
|
|
267
|
+
}
|
|
265
268
|
const cwd = sanitizeEditorMetadataText(minimalistCwdLabel(metadata, config));
|
|
266
269
|
return cwd
|
|
267
270
|
? renderSourceColor(uiTheme, config.colors.cwd, "cwd", "bold cyan", cwd)
|