@d3ara1n/pi-editor-shell 0.3.1 → 0.5.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/README.md +31 -8
- package/package.json +6 -4
- package/preview.png +0 -0
- package/src/config.ts +44 -0
- package/src/index.ts +24 -13
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# pi-editor-shell
|
|
2
2
|
|
|
3
|
-
Replaces pi's default editor and status bar with a unified rounded-corner shell drawn with box-drawing glyphs (`╭╮││╰╯`), with status info embedded in the border.
|
|
3
|
+
Replaces pi's default editor and status bar with a unified rounded-corner shell drawn with box-drawing glyphs (`╭╮││╰╯`), with status info embedded in the border. The frame and spinner use only standard Unicode; the six border icons are Nerd Font glyphs (overridable — see [Configuration](#configuration)).
|
|
4
4
|
|
|
5
5
|
## What shows up where
|
|
6
6
|
|
|
7
|
-
- **Top border** — ` model · thinking-level ` (left) + pinned extension statuses (right, via `pinnedStatus` config)
|
|
8
|
-
- **Bottom border** — ` ctx NN%/NNNk ·
|
|
7
|
+
- **Top border** — ` provider/model · thinking-level ` (left) + pinned extension statuses (right, via `pinnedStatus` config)
|
|
8
|
+
- **Bottom border** — ` ctx NN%/NNNk · ⚡ cacheRead (total) hitRate% ` (left) + ` ~/Projects (main +2 ~1) ` (right, shows git branch + dirty state when in a repo)
|
|
9
9
|
- **Below shell** — Auto-wrapping extension status line (all `setStatus` entries not pinned to the top)
|
|
10
10
|
- **Border color** follows pi's thinking-level / bash-mode indicator automatically.
|
|
11
11
|
|
|
12
|
-
All segments are re-read from live session state on every paint, so switching thinking level or burning context updates the frame on the next render with no extra wiring.
|
|
12
|
+
All segments are re-read from live session state on every paint, so switching thinking level or burning context updates the frame on the next render with no extra wiring. When the agent is active, the current phase spinner (thinking/outputting/toolcall/exec) replaces the model text in the top-left slot.
|
|
13
13
|
|
|
14
14
|
## Configuration
|
|
15
15
|
|
|
@@ -20,11 +20,30 @@ In `~/.pi/agent/settings.json` under the `editorShell` key:
|
|
|
20
20
|
"editorShell": {
|
|
21
21
|
// Status keys to pin to the top-right corner of the shell.
|
|
22
22
|
// Only keys set via ctx.ui.setStatus() are eligible.
|
|
23
|
-
"pinnedStatus": ["subagent", "access-denied"]
|
|
23
|
+
"pinnedStatus": ["subagent", "access-denied"],
|
|
24
|
+
|
|
25
|
+
// Per-slot border-icon overrides. Any subset; missing keys fall back
|
|
26
|
+
// to the built-in Nerd Font set (see table below). Values are raw
|
|
27
|
+
// characters — "\uf0e7" for a Nerd Font glyph, "🤖" for an emoji.
|
|
28
|
+
"icons": {
|
|
29
|
+
"model": "🤖",
|
|
30
|
+
"cache": "\uf0e7"
|
|
31
|
+
}
|
|
24
32
|
}
|
|
25
33
|
}
|
|
26
34
|
```
|
|
27
35
|
|
|
36
|
+
### Default icons
|
|
37
|
+
|
|
38
|
+
| Slot | Glyph | Nerd Font name |
|
|
39
|
+
|------|-------|----------------|
|
|
40
|
+
| `model` | `` | oct-cpu |
|
|
41
|
+
| `thinking` | `` | oct-light_bulb |
|
|
42
|
+
| `context` | `` | oct-cache |
|
|
43
|
+
| `cache` | `⚡` | oct-zap |
|
|
44
|
+
| `hitRate` | `` | fa-bullseye |
|
|
45
|
+
| `folder` | `` | fa-folder_open |
|
|
46
|
+
|
|
28
47
|
## Commands
|
|
29
48
|
|
|
30
49
|
| Command | Description |
|
|
@@ -35,9 +54,15 @@ In `~/.pi/agent/settings.json` under the `editorShell` key:
|
|
|
35
54
|
|
|
36
55
|
The default pi editor only draws a horizontal line above and below the input area (no side borders), and a separate footer renders the status bar. This extension replaces both — it wraps the built-in `CustomEditor`, renders it at `width - 2`, wraps every line with left/right glyphs, and embeds the status bar information (extension statuses) below the shell. The total width is unchanged. Border color follows pi's `borderColor` (which encodes thinking level / bash mode), so the shell stays semantically consistent and reacts to theme changes automatically.
|
|
37
56
|
|
|
57
|
+
When the autocomplete popup is open, the divider between editor content and popup items becomes a T-junction (`├─┤`) carrying the context/cwd info, closing everything into one connected card with two panes. Below `MIN_WIDTH` (20 columns), it falls back to the default editor.
|
|
58
|
+
|
|
38
59
|
## Installation
|
|
39
60
|
|
|
40
|
-
|
|
61
|
+
```bash
|
|
62
|
+
pi install npm:@d3ara1n/pi-editor-shell
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Or add to `~/.pi/agent/settings.json`:
|
|
41
66
|
|
|
42
67
|
```jsonc
|
|
43
68
|
{
|
|
@@ -47,8 +72,6 @@ Add the package directory to the `extensions` array in `~/.pi/agent/settings.jso
|
|
|
47
72
|
}
|
|
48
73
|
```
|
|
49
74
|
|
|
50
|
-
Then run `/reload` or restart pi.
|
|
51
|
-
|
|
52
75
|
## Caveats
|
|
53
76
|
|
|
54
77
|
- **Mutually exclusive** with other editor-replacing extensions (`border-status-editor`, `rainbow-editor`, `modal-editor`, …). Disable those when enabling this one — `setEditorComponent` is last-writer-wins.
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@d3ara1n/pi-editor-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Replaces pi's default editor and status bar with a unified rounded-corner shell
|
|
5
|
+
"description": "Replaces pi's default editor and status bar with a unified rounded-corner shell embedding status info in the border",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"pi-package",
|
|
8
|
-
"pi"
|
|
8
|
+
"pi",
|
|
9
|
+
"pi-extension"
|
|
9
10
|
],
|
|
10
11
|
"main": "src/index.ts",
|
|
11
12
|
"peerDependencies": {
|
|
@@ -15,7 +16,8 @@
|
|
|
15
16
|
"pi": {
|
|
16
17
|
"extensions": [
|
|
17
18
|
"./src/index.ts"
|
|
18
|
-
]
|
|
19
|
+
],
|
|
20
|
+
"image": "https://raw.githubusercontent.com/d3ara1n/pi-extensions/main/packages/pi-editor-shell/preview.png"
|
|
19
21
|
},
|
|
20
22
|
"license": "MIT",
|
|
21
23
|
"repository": {
|
package/preview.png
ADDED
|
Binary file
|
package/src/config.ts
CHANGED
|
@@ -10,16 +10,55 @@ import * as fs from "node:fs";
|
|
|
10
10
|
import * as os from "node:os";
|
|
11
11
|
import * as path from "node:path";
|
|
12
12
|
|
|
13
|
+
/** Border icon slots that users can override. Each holds a single glyph
|
|
14
|
+
* (Nerd Font codepoint, Unicode symbol, or emoji) — whatever the user's
|
|
15
|
+
* terminal can render. Defaults live next to the renderer in index.ts. */
|
|
16
|
+
export interface EditorShellIcons {
|
|
17
|
+
model: string;
|
|
18
|
+
thinking: string;
|
|
19
|
+
context: string;
|
|
20
|
+
cache: string;
|
|
21
|
+
hitRate: string;
|
|
22
|
+
folder: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
export interface EditorShellConfig {
|
|
14
26
|
/**
|
|
15
27
|
* Status keys to pin to the shell's top-right corner.
|
|
16
28
|
* Only keys set via ctx.ui.setStatus() are eligible.
|
|
17
29
|
*/
|
|
18
30
|
pinnedStatus: string[];
|
|
31
|
+
/**
|
|
32
|
+
* Per-slot border-icon overrides. Any subset; missing keys fall back to
|
|
33
|
+
* the built-in Nerd Font set. Values are raw characters — JSON `"\uf0e7"`
|
|
34
|
+
* for a Nerd Font glyph, or `"🤖"` for an emoji, etc.
|
|
35
|
+
*/
|
|
36
|
+
icons: Partial<EditorShellIcons>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const ICON_KEYS: ReadonlyArray<keyof EditorShellIcons> = [
|
|
40
|
+
"model",
|
|
41
|
+
"thinking",
|
|
42
|
+
"context",
|
|
43
|
+
"cache",
|
|
44
|
+
"hitRate",
|
|
45
|
+
"folder",
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/** Keep only known icon slots with string values — silently drops typos and
|
|
49
|
+
* wrong-typed entries so a bad config never crashes the renderer. */
|
|
50
|
+
function filterIcons(obj: Record<string, unknown>): Partial<EditorShellIcons> {
|
|
51
|
+
const out: Partial<EditorShellIcons> = {};
|
|
52
|
+
for (const key of ICON_KEYS) {
|
|
53
|
+
const v = obj[key];
|
|
54
|
+
if (typeof v === "string") out[key] = v;
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
19
57
|
}
|
|
20
58
|
|
|
21
59
|
export const DEFAULT_CONFIG: EditorShellConfig = {
|
|
22
60
|
pinnedStatus: [],
|
|
61
|
+
icons: {},
|
|
23
62
|
};
|
|
24
63
|
|
|
25
64
|
function getAgentDir(): string {
|
|
@@ -51,9 +90,14 @@ export function loadEditorShellConfig(cwd?: string): EditorShellConfig {
|
|
|
51
90
|
if (!raw) return { ...DEFAULT_CONFIG };
|
|
52
91
|
|
|
53
92
|
const pinned = raw.pinnedStatus;
|
|
93
|
+
const iconsRaw = raw.icons;
|
|
54
94
|
return {
|
|
55
95
|
pinnedStatus: Array.isArray(pinned)
|
|
56
96
|
? pinned.filter((k): k is string => typeof k === "string")
|
|
57
97
|
: DEFAULT_CONFIG.pinnedStatus,
|
|
98
|
+
icons:
|
|
99
|
+
iconsRaw && typeof iconsRaw === "object"
|
|
100
|
+
? filterIcons(iconsRaw as Record<string, unknown>)
|
|
101
|
+
: {},
|
|
58
102
|
};
|
|
59
103
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { spawn } from "node:child_process";
|
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { CardEditor, type FrameProvider, type SpinnerPhase } from "./card-editor";
|
|
7
|
-
import { loadEditorShellConfig, type EditorShellConfig } from "./config";
|
|
7
|
+
import { loadEditorShellConfig, type EditorShellConfig, type EditorShellIcons } from "./config";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* pi-editor-shell — Replaces pi's default editor and status bar with a
|
|
@@ -47,15 +47,18 @@ function contextToken(pct: number | null | undefined): ThemeColor {
|
|
|
47
47
|
return "success";
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
// ── Nerd Font
|
|
51
|
-
|
|
50
|
+
// ── Built-in icon set (Nerd Font). Users can override any subset via the
|
|
51
|
+
// `editorShell.icons` config — see config.ts. `cache` uses U+26A1, which
|
|
52
|
+
// Nerd Fonts maps `oct-zap` to directly (no dedicated glyph), so it is
|
|
53
|
+
// the same glyph in and out of a Nerd Font terminal.
|
|
54
|
+
const DEFAULT_ICONS: EditorShellIcons = {
|
|
52
55
|
model: "\uf4bc", // oct-cpu
|
|
53
|
-
thinking: "\uf400", // oct-
|
|
56
|
+
thinking: "\uf400", // oct-light_bulb
|
|
54
57
|
context: "\uf49b", // oct-cache
|
|
55
|
-
cache: "\u26a1", // ⚡ oct-zap
|
|
58
|
+
cache: "\u26a1", // ⚡ oct-zap (NF maps this codepoint to U+26A1)
|
|
56
59
|
hitRate: "\uf140", // fa-bullseye(靶心,缓存命中率)
|
|
57
|
-
folder: "\uf07c", // fa-
|
|
58
|
-
}
|
|
60
|
+
folder: "\uf07c", // fa-folder_open
|
|
61
|
+
};
|
|
59
62
|
|
|
60
63
|
/** Minimal inline types to read cache-read totals without importing the
|
|
61
64
|
* full pi-ai message union tree. */
|
|
@@ -192,7 +195,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
192
195
|
// The factory may run again when pi rebuilds the editor (model switch,
|
|
193
196
|
// reload, …), so always drive whichever instance is current.
|
|
194
197
|
let editor: CardEditor | undefined;
|
|
195
|
-
let config: EditorShellConfig = { pinnedStatus: [] };
|
|
198
|
+
let config: EditorShellConfig = { pinnedStatus: [], icons: {} };
|
|
199
|
+
// Resolved icons for the current session: built-in defaults merged with
|
|
200
|
+
// the user's overrides. Re-computed at session_start.
|
|
201
|
+
let icons: EditorShellIcons = { ...DEFAULT_ICONS };
|
|
196
202
|
// Shared footer-data ref — the provider (running inside CardEditor.render)
|
|
197
203
|
// reads it to resolve pinned status keys to their current text.
|
|
198
204
|
let footerSnap: FooterSnap | undefined;
|
|
@@ -241,6 +247,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
241
247
|
|
|
242
248
|
_cwd = ctx.cwd;
|
|
243
249
|
config = loadEditorShellConfig(ctx.cwd);
|
|
250
|
+
icons = { ...DEFAULT_ICONS, ...config.icons };
|
|
244
251
|
_cacheTotal = sumCacheRead(ctx);
|
|
245
252
|
_latestUsage = latestAssistantUsage(ctx);
|
|
246
253
|
refreshGitDirty(ctx.cwd, () => editor?.requestRender());
|
|
@@ -283,7 +290,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
283
290
|
const hitRate = cacheHitRate(_latestUsage);
|
|
284
291
|
const cachePart =
|
|
285
292
|
_cacheTotal > 0
|
|
286
|
-
? `${theme.fg("dim", " · ")}${theme.fg("warning", `${
|
|
293
|
+
? `${theme.fg("dim", " · ")}${theme.fg("warning", `${icons.cache} ${formatTokens(cacheReadNow)} (${formatTokens(_cacheTotal)})${hitRate != null ? ` ${icons.hitRate} ${hitRate.toFixed(1)}%` : ""}`)}`
|
|
287
294
|
: "";
|
|
288
295
|
|
|
289
296
|
// Git branch + dirty state — pi's format: ~/Projects (main).
|
|
@@ -292,16 +299,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
292
299
|
const dirty = branch ? gitDirtyDisplay() : "";
|
|
293
300
|
const cwdDisplay =
|
|
294
301
|
branch && branch !== "detached"
|
|
295
|
-
? `${
|
|
296
|
-
: `${
|
|
302
|
+
? `${icons.folder} ${cwdText} (${branch}${dirty})`
|
|
303
|
+
: `${icons.folder} ${cwdText}`;
|
|
297
304
|
|
|
298
305
|
// Model in accent; thinking label in its level token — same hue the
|
|
299
306
|
// border takes on, so switching levels visibly retints both together.
|
|
300
307
|
return {
|
|
301
|
-
topLeft: ` ${theme.fg("accent", `${
|
|
308
|
+
topLeft: ` ${theme.fg("accent", `${icons.model} ${model}`)}${theme.fg("dim", " · ")}${theme.fg(thinkingColor, `${icons.thinking} ${thinking}`)} `,
|
|
302
309
|
topRight: buildPinned(),
|
|
303
310
|
// Context in severity color; cwd stays muted so it never competes.
|
|
304
|
-
bottomLeft: ` ${theme.fg(contextToken(pct), `${
|
|
311
|
+
bottomLeft: ` ${theme.fg(contextToken(pct), `${icons.context} ${ctxText}`)}${cachePart} `,
|
|
305
312
|
bottomRight: theme.fg("muted", ` ${cwdDisplay} `),
|
|
306
313
|
};
|
|
307
314
|
};
|
|
@@ -310,6 +317,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
310
317
|
ctx.ui.setWorkingVisible(false);
|
|
311
318
|
|
|
312
319
|
ctx.ui.setEditorComponent((tui, theme, keybindings) => {
|
|
320
|
+
// pi may rebuild the editor mid-session (model switch, reload). Stop the
|
|
321
|
+
// previous editor's spinner timer so it can't keep firing requestRender()
|
|
322
|
+
// on a stale tui handle.
|
|
323
|
+
editor?.setSpinner(null);
|
|
313
324
|
editor = new CardEditor(tui, theme, keybindings, provider);
|
|
314
325
|
return editor;
|
|
315
326
|
});
|