@xzzpig/pi-starline 0.1.0 → 0.2.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 -27
- package/extensions/starline/config.ts +0 -17
- package/extensions/starline/index.ts +12 -0
- package/extensions/starline/mouse/capabilities.ts +36 -36
- package/extensions/starline/mouse/editor-caret.ts +3 -3
- package/extensions/starline/mouse/index.ts +163 -272
- package/extensions/starline/mouse/selection-state.ts +17 -39
- package/extensions/starline/mouse/transcript-copy.ts +1 -1
- package/extensions/starline/prototype-patch-registry.ts +1 -2
- package/extensions/starline/ui.ts +1 -1
- package/package.json +58 -59
- package/extensions/starline/mouse/word-select.ts +0 -145
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Starline
|
|
2
2
|
|
|
3
|
-
A Starship-inspired statusline
|
|
3
|
+
A Starship-inspired statusline, Opencode-style editor chrome, and fullscreen mouse control for [Pi](https://pi.dev).
|
|
4
4
|
|
|
5
5
|
> Starline is a fork of [pi-zentui](https://github.com/lmilojevicc/pi-zentui) by Luka, renamed and released on its own. It has diverged well past upstream and adds a pill footer style, a colour palette with `$ref` expansion, `model`/`thinking` footer segments, a git host icon, per-segment display options, configurable editor cursor styles, and mouse selection and copying.
|
|
6
6
|
|
|
@@ -10,10 +10,12 @@ A Starship-inspired statusline and Opencode-style TUI for [Pi](https://pi.dev).
|
|
|
10
10
|
|
|
11
11
|
## What is this?
|
|
12
12
|
|
|
13
|
-
Starline
|
|
13
|
+
Starline makes Pi's fullscreen mode feel like a finished terminal app — three
|
|
14
|
+
pieces of one product, not a theme:
|
|
14
15
|
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
16
|
+
- **See the state** — a Starship-inspired statusline and pill footer: current directory, git branch and status, runtime/version detection, context usage and token counts in a compact, icon-rich format
|
|
17
|
+
- **Type comfortably** — Opencode-style editor chrome: a clean bordered input box with accent rail and copy-friendly mode, model and provider inside the frame, paste collapse, and a `ctrl+g` hint pointing at your `$EDITOR` when a draft outgrows the box
|
|
18
|
+
- **Drive with the mouse** — fullscreen mouse control: select and copy anywhere, click a tool box to expand just it, click to move the caret, wheel to scroll the draft — [see Mouse](#mouse-fullscreen-control)
|
|
17
19
|
|
|
18
20
|
## Features
|
|
19
21
|
|
|
@@ -37,7 +39,26 @@ Starline brings two popular aesthetics to Pi:
|
|
|
37
39
|
- Configurable model, provider, and thinking-level indicator colors
|
|
38
40
|
- Prompt-box-style user messages matching the Starline input chrome
|
|
39
41
|
- Copy-friendly mode hides editor and previous-message rail glyphs so terminal selection copies less chrome
|
|
40
|
-
|
|
42
|
+
|
|
43
|
+
### Mouse (fullscreen control)
|
|
44
|
+
|
|
45
|
+
The second half of the product: the statusline tells you where you are, the
|
|
46
|
+
mouse lets you act on what you see. Starline drives Pi 0.84's own renderer
|
|
47
|
+
(`tuiMode: "fullscreen"`) by patching its methods directly — the extension API
|
|
48
|
+
stops at high-level hooks, and the selection hint, click-to-expand and clean-copy all
|
|
49
|
+
need to reach into the renderer itself. That is also why they live in Starline:
|
|
50
|
+
they sit on the same prototype patches the footer and editor already use, and
|
|
51
|
+
Pi keeps its core deliberately minimal — behaviours belong in extensions.
|
|
52
|
+
|
|
53
|
+
- **Select and copy anywhere.** Drag to select in the transcript or the input box; double-click a word (paths and kebab-case stay whole), triple-click a line. Releasing copies — or set Pi's `fullscreenCopyOnSelect: false` and the selection stays highlighted, waiting for the copy key ([Pi #7720](https://github.com/earendil-works/pi/issues/7720), shipped in Pi 0.84.4).
|
|
54
|
+
- **Clean copies.** Transcript selections drop Starline's rails, rule rows and pi-toolbox frame borders before they reach the clipboard; editor selections copy the draft's text, not the painted rows.
|
|
55
|
+
- **Selection hint.** A selection that is not auto-copying shows exactly how many characters the copy key (`ctrl+x` by default, whatever `app.message.copy` is bound to) will copy, and a selection inside the editor names your editor — `ctrl+g to edit in nvim`.
|
|
56
|
+
- **Click to expand one tool box.** A click on a box's hint row expands just that box (MCP boxes included), and the `(ctrl+o to collapse)` anchor row closes it again.
|
|
57
|
+
- **Click to move the caret** in the input box; **backspace/delete remove a selected range**; the **wheel scrolls the draft** over the input box, and a draft taller than the box keeps the `ctrl+g` external-editor hint on screen.
|
|
58
|
+
|
|
59
|
+
0.2.x's `fixedEditor` config block was renamed to `mouse` (migrated
|
|
60
|
+
automatically, see [the changelog](CHANGELOG.md)); the fixed editor itself is
|
|
61
|
+
gone, superseded by Pi's fullscreen mode.
|
|
41
62
|
|
|
42
63
|
### Git Status Icons
|
|
43
64
|
|
|
@@ -130,6 +151,9 @@ Detects Starship Nerd Font runtime/language modules, uses the Starship Nerd Font
|
|
|
130
151
|
|
|
131
152
|
## Install
|
|
132
153
|
|
|
154
|
+
Starline drives Pi 0.84's alternate-screen renderer, so it needs **Pi ≥ 0.84.4**.
|
|
155
|
+
If you are on a distribution that bundles an older Pi, upgrade it or wait for it to catch up.
|
|
156
|
+
|
|
133
157
|
```bash
|
|
134
158
|
# From npm (local fork @xzzpig/pi-starline)
|
|
135
159
|
pi install npm:@xzzpig/pi-starline
|
|
@@ -150,7 +174,7 @@ Settings live in `~/.pi/agent/starline.json`. The file is optional — anything
|
|
|
150
174
|
{
|
|
151
175
|
"footerStyle": "pill",
|
|
152
176
|
"colors": { "gitBranch": "bold purple" },
|
|
153
|
-
"mouse": { "
|
|
177
|
+
"mouse": { "clickToExpandTools": false }
|
|
154
178
|
}
|
|
155
179
|
```
|
|
156
180
|
|
|
@@ -158,29 +182,9 @@ Settings live in `~/.pi/agent/starline.json`. The file is optional — anything
|
|
|
158
182
|
|
|
159
183
|
**[Full configuration reference →](https://github.com/Andy8647/pi-starline/blob/main/docs/configuration.md)** — every option with its default: the pill footer, the colour palette and its `$name` references, footer format templates, per-segment display options, icon overrides, and editor styling.
|
|
160
184
|
|
|
161
|
-
## Mouse
|
|
162
|
-
|
|
163
|
-
Starline reads and drives Pi 0.84's own renderer, so mouse handling works in
|
|
164
|
-
both TUI modes — use Pi's fullscreen mode (`tuiMode: "fullscreen"`) for the
|
|
165
|
-
sticky editor and footer.
|
|
166
|
-
|
|
167
|
-
- **Select and copy anywhere.** Drag to select in the transcript or the input
|
|
168
|
-
box; double-click a word (paths and kebab-case stay whole), triple-click a
|
|
169
|
-
line. Releasing copies — or hold the selection for `ctrl+c` with
|
|
170
|
-
`mouse.copyOnSelect: false`. Transcript copies drop Starline's rails, rule
|
|
171
|
-
rows and pi-toolbox frame borders.
|
|
172
|
-
- **Click a tool box's hint row to expand just that box**, and collapse it via
|
|
173
|
-
the `(ctrl+o to collapse)` anchor row (pi-toolbox 0.2.2+).
|
|
174
|
-
- **Click to move the caret** in the input box; **backspace/delete remove a
|
|
175
|
-
selected range**; the **wheel scrolls the draft** over the input box.
|
|
176
|
-
|
|
177
|
-
0.2.x's `fixedEditor` config block was renamed to `mouse` (migrated
|
|
178
|
-
automatically, see [the changelog](CHANGELOG.md)); the fixed editor itself is
|
|
179
|
-
gone, superseded by Pi's fullscreen mode.
|
|
180
|
-
|
|
181
185
|
## Requirements
|
|
182
186
|
|
|
183
|
-
- [Pi](https://pi.dev) coding agent 0.
|
|
187
|
+
- [Pi](https://pi.dev) coding agent 0.84.4 or newer
|
|
184
188
|
- A [Nerd Font](https://www.nerdfonts.com/) for icons (or set `icons.mode` to `"ascii"`)
|
|
185
189
|
|
|
186
190
|
## Development
|
|
@@ -109,12 +109,8 @@ export type MouseConfig = {
|
|
|
109
109
|
wheelRouting: boolean;
|
|
110
110
|
/** Show a "copied to clipboard" notice for a copy Starline itself performs. */
|
|
111
111
|
copyNotice: boolean;
|
|
112
|
-
/** Copy on mouse release. When false the highlight waits for ctrl+c. */
|
|
113
|
-
copyOnSelect: boolean;
|
|
114
112
|
/** Clicking a tool box's `ctrl+o to expand` hint expands that one box. */
|
|
115
113
|
clickToExpandTools: boolean;
|
|
116
|
-
/** Double/triple-click word selection stops at path separators. */
|
|
117
|
-
pathAwareWords: boolean;
|
|
118
114
|
/** Copy transcript selections without the painted rails, rules and frames. */
|
|
119
115
|
transcriptCleanCopy: boolean;
|
|
120
116
|
};
|
|
@@ -123,7 +119,6 @@ const FIXED_EDITOR_KEY_MAP: Record<string, keyof MouseConfig> = {
|
|
|
123
119
|
enabled: "enabled",
|
|
124
120
|
mouseScroll: "wheelRouting",
|
|
125
121
|
copyNotice: "copyNotice",
|
|
126
|
-
copyOnSelect: "copyOnSelect",
|
|
127
122
|
clickToExpandTools: "clickToExpandTools",
|
|
128
123
|
};
|
|
129
124
|
|
|
@@ -415,9 +410,7 @@ export const defaultConfig: PolishedTuiConfig = {
|
|
|
415
410
|
enabled: true,
|
|
416
411
|
wheelRouting: true,
|
|
417
412
|
copyNotice: true,
|
|
418
|
-
copyOnSelect: true,
|
|
419
413
|
clickToExpandTools: true,
|
|
420
|
-
pathAwareWords: true,
|
|
421
414
|
transcriptCleanCopy: true,
|
|
422
415
|
},
|
|
423
416
|
};
|
|
@@ -831,18 +824,10 @@ function normalizeMouseConfig(record: Record<string, unknown>): MouseConfig {
|
|
|
831
824
|
: defaultConfig.mouse.wheelRouting,
|
|
832
825
|
copyNotice:
|
|
833
826
|
typeof record.copyNotice === "boolean" ? record.copyNotice : defaultConfig.mouse.copyNotice,
|
|
834
|
-
copyOnSelect:
|
|
835
|
-
typeof record.copyOnSelect === "boolean"
|
|
836
|
-
? record.copyOnSelect
|
|
837
|
-
: defaultConfig.mouse.copyOnSelect,
|
|
838
827
|
clickToExpandTools:
|
|
839
828
|
typeof record.clickToExpandTools === "boolean"
|
|
840
829
|
? record.clickToExpandTools
|
|
841
830
|
: defaultConfig.mouse.clickToExpandTools,
|
|
842
|
-
pathAwareWords:
|
|
843
|
-
typeof record.pathAwareWords === "boolean"
|
|
844
|
-
? record.pathAwareWords
|
|
845
|
-
: defaultConfig.mouse.pathAwareWords,
|
|
846
831
|
transcriptCleanCopy:
|
|
847
832
|
typeof record.transcriptCleanCopy === "boolean"
|
|
848
833
|
? record.transcriptCleanCopy
|
|
@@ -1304,11 +1289,9 @@ export function saveMousePatch(patch: Partial<MouseConfig>, path = configPath):
|
|
|
1304
1289
|
...(patch.enabled !== undefined ? { enabled: patch.enabled } : {}),
|
|
1305
1290
|
...(patch.wheelRouting !== undefined ? { wheelRouting: patch.wheelRouting } : {}),
|
|
1306
1291
|
...(patch.copyNotice !== undefined ? { copyNotice: patch.copyNotice } : {}),
|
|
1307
|
-
...(patch.copyOnSelect !== undefined ? { copyOnSelect: patch.copyOnSelect } : {}),
|
|
1308
1292
|
...(patch.clickToExpandTools !== undefined
|
|
1309
1293
|
? { clickToExpandTools: patch.clickToExpandTools }
|
|
1310
1294
|
: {}),
|
|
1311
|
-
...(patch.pathAwareWords !== undefined ? { pathAwareWords: patch.pathAwareWords } : {}),
|
|
1312
1295
|
...(patch.transcriptCleanCopy !== undefined
|
|
1313
1296
|
? { transcriptCleanCopy: patch.transcriptCleanCopy }
|
|
1314
1297
|
: {}),
|
|
@@ -255,6 +255,18 @@ export default function (pi: ExtensionAPI) {
|
|
|
255
255
|
const installMousePatches = () => {
|
|
256
256
|
uninstallMouse();
|
|
257
257
|
if (!getCurrentConfig().mouse.enabled) return;
|
|
258
|
+
// `TuiAltScreen` only ships with pi-tui >= 0.84.0 (the peer range below
|
|
259
|
+
// already says so). This guard is for environments that bypass peer
|
|
260
|
+
// resolution: fail with a readable hint instead of a TypeError on
|
|
261
|
+
// `TuiAltScreen.prototype`.
|
|
262
|
+
if (!TuiAltScreen) {
|
|
263
|
+
console.warn(
|
|
264
|
+
"starline: mouse support needs @earendil-works/pi-tui >= 0.84.0 (" +
|
|
265
|
+
"TuiAltScreen is not exported by the installed pi-tui). Upgrade Pi, " +
|
|
266
|
+
"or wait for your distribution to bundle a newer one.",
|
|
267
|
+
);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
258
270
|
// Repaints are not wired from here: each patch asks its own receiver —
|
|
259
271
|
// the live renderer it is running inside — to render. Handing it the
|
|
260
272
|
// extension's `refresh` instead tied the pending hint to the footer's
|
|
@@ -14,15 +14,20 @@
|
|
|
14
14
|
* install this table exists to prevent. The check being stricter than a plain
|
|
15
15
|
* call needs (it also demands writable and configurable) costs nothing, because
|
|
16
16
|
* a class method declared the ordinary way is both.
|
|
17
|
+
*
|
|
18
|
+
* Since 0.84.4 the peer floor guarantees `copyActiveSelectionToClipboard`,
|
|
19
|
+
* `getCopyOnSelect` and `hasActiveSelection` exist; the probe still runs so a
|
|
20
|
+
* future Pi that moves them degrades feature-by-feature instead of breaking
|
|
21
|
+
* the whole install.
|
|
17
22
|
*/
|
|
18
23
|
|
|
19
24
|
export type MouseCapability =
|
|
20
25
|
| "handleViewportInput"
|
|
21
26
|
| "routeWheel"
|
|
22
27
|
| "handleSelectionMouseEvent"
|
|
23
|
-
| "
|
|
24
|
-
| "
|
|
25
|
-
| "
|
|
28
|
+
| "copyActiveSelectionToClipboard"
|
|
29
|
+
| "getCopyOnSelect"
|
|
30
|
+
| "hasActiveSelection"
|
|
26
31
|
| "getSelectionBounds"
|
|
27
32
|
| "getSelectionColumns"
|
|
28
33
|
| "flash"
|
|
@@ -30,8 +35,7 @@ export type MouseCapability =
|
|
|
30
35
|
| "requestRender";
|
|
31
36
|
|
|
32
37
|
export type MouseFeature =
|
|
33
|
-
| "
|
|
34
|
-
| "pathAwareWords"
|
|
38
|
+
| "selectionHint"
|
|
35
39
|
| "clickToExpandTools"
|
|
36
40
|
| "editorWheelScroll"
|
|
37
41
|
| "editorClickToCaret"
|
|
@@ -42,9 +46,9 @@ const CAPABILITIES: readonly MouseCapability[] = [
|
|
|
42
46
|
"handleViewportInput",
|
|
43
47
|
"routeWheel",
|
|
44
48
|
"handleSelectionMouseEvent",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
49
|
+
"copyActiveSelectionToClipboard",
|
|
50
|
+
"getCopyOnSelect",
|
|
51
|
+
"hasActiveSelection",
|
|
48
52
|
"getSelectionBounds",
|
|
49
53
|
"getSelectionColumns",
|
|
50
54
|
"flash",
|
|
@@ -61,25 +65,20 @@ const CAPABILITIES: readonly MouseCapability[] = [
|
|
|
61
65
|
* is worse than no table.
|
|
62
66
|
*/
|
|
63
67
|
const REQUIREMENTS: Record<MouseFeature, readonly MouseCapability[]> = {
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"
|
|
68
|
+
// Derived from Pi 0.84.4's own select-without-copy state: the hint shows
|
|
69
|
+
// only while the renderer is NOT auto-copying (`getCopyOnSelect` false) and
|
|
70
|
+
// something is actually selected (`hasActiveSelection`). The character
|
|
71
|
+
// count is computed from the live selection through `getSelectionBounds`
|
|
72
|
+
// and `getSelectionColumns`, matching the columns the copy itself would
|
|
73
|
+
// slice — the hint promises what ctrl+x delivers. No release interception,
|
|
74
|
+
// no pending state: Pi's `copyOnSelect: false` already keeps the selection
|
|
75
|
+
// highlighted, and the hint rides Pi's own repaints.
|
|
76
|
+
selectionHint: [
|
|
77
|
+
"getCopyOnSelect",
|
|
78
|
+
"hasActiveSelection",
|
|
74
79
|
"getSelectionBounds",
|
|
75
80
|
"getSelectionColumns",
|
|
76
|
-
"flash",
|
|
77
|
-
"requestRender",
|
|
78
81
|
],
|
|
79
|
-
// Also reads the line under the pointer through the receiver's own
|
|
80
|
-
// `getSelectionSourceLine`, so the patch has a real line to hand
|
|
81
|
-
// `wordRangeAt` — without it there's nothing to compute a range over.
|
|
82
|
-
pathAwareWords: ["getWordSelection", "getSelectionSourceLine"],
|
|
83
82
|
// The press it acts on arrives through `handleSelectionMouseEvent`, and it
|
|
84
83
|
// asks `hasOverlay` the same question Pi's own press path asks before
|
|
85
84
|
// resolving a scroll view (`tui-alt-screen.js:684`) — without it, a click on
|
|
@@ -123,11 +122,12 @@ const REQUIREMENTS: Record<MouseFeature, readonly MouseCapability[]> = {
|
|
|
123
122
|
"hasOverlay",
|
|
124
123
|
"requestRender",
|
|
125
124
|
],
|
|
126
|
-
// It wraps `
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
125
|
+
// It wraps `copyActiveSelectionToClipboard` — the method Pi 0.84.4's
|
|
126
|
+
// Ctrl+X handler reaches (`handleCopyCommand` with `preferSelection`), so
|
|
127
|
+
// the clean copy answers exactly the copies that key performs — and reads
|
|
128
|
+
// the selection through `getSelectionBounds` to find out whether it is the
|
|
129
|
+
// editor's. `hasOverlay` again: a selection dropped on a dialog must not be
|
|
130
|
+
// read as text from the draft behind it.
|
|
131
131
|
//
|
|
132
132
|
// The clipboard write goes through `terminal.write`, which is not listed
|
|
133
133
|
// because it is not on this prototype — `terminal` is a plain instance field
|
|
@@ -135,18 +135,18 @@ const REQUIREMENTS: Record<MouseFeature, readonly MouseCapability[]> = {
|
|
|
135
135
|
// It is checked at the call site instead, and a terminal that cannot be
|
|
136
136
|
// written to makes the copy fall through to Pi rather than disabling the
|
|
137
137
|
// feature at install time.
|
|
138
|
-
editorBufferCopy: ["
|
|
139
|
-
// Shares the `
|
|
140
|
-
// it. It reads the selection through `getSelectionBounds` to find the
|
|
141
|
-
// through `getSelectionColumns` to slice the cleaned text along the
|
|
142
|
-
// grapheme-aligned columns Pi would have used, asks `hasOverlay` before
|
|
138
|
+
editorBufferCopy: ["copyActiveSelectionToClipboard", "getSelectionBounds", "hasOverlay", "flash"],
|
|
139
|
+
// Shares the `copyActiveSelectionToClipboard` patch with the two features
|
|
140
|
+
// above it. It reads the selection through `getSelectionBounds` to find the
|
|
141
|
+
// rows, through `getSelectionColumns` to slice the cleaned text along the
|
|
142
|
+
// same grapheme-aligned columns Pi would have used, asks `hasOverlay` before
|
|
143
143
|
// trusting those rows (an overlay is composited over a layout that still
|
|
144
|
-
// contains the transcript), and raises
|
|
144
|
+
// contains the transcript), and raises its own "Copied!" through `flash`
|
|
145
145
|
// when it answers the copy itself. The clipboard write goes through
|
|
146
146
|
// `terminal.write`, unlisted for the same reason as editorBufferCopy's — it
|
|
147
147
|
// is an instance field, checked at the call site instead.
|
|
148
148
|
transcriptCleanCopy: [
|
|
149
|
-
"
|
|
149
|
+
"copyActiveSelectionToClipboard",
|
|
150
150
|
"getSelectionBounds",
|
|
151
151
|
"getSelectionColumns",
|
|
152
152
|
"hasOverlay",
|
|
@@ -397,9 +397,9 @@ type EditableEditor = {
|
|
|
397
397
|
* range back a letter at a time. So one snapshot is pushed up front through the
|
|
398
398
|
* editor's own `pushUndoSnapshot`, and the editor's own is shadowed for the
|
|
399
399
|
* duration of the loop and put back in a `finally` — the same shadow-and-restore
|
|
400
|
-
* shape `
|
|
401
|
-
*
|
|
402
|
-
*
|
|
400
|
+
* shape `installPasteCollapse` uses on `handlePaste`. This is a plain
|
|
401
|
+
* assignment on Starline's own editor instance, never on anything reached
|
|
402
|
+
* through Pi's renderer Proxy.
|
|
403
403
|
*
|
|
404
404
|
* The loop is bounded twice over: by the character count of the range, and by a
|
|
405
405
|
* no-progress check, so an editor that stops deleting cannot spin.
|