@adeildo/pi-look 5.8.0 → 5.9.1
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 +5 -0
- package/assets/preview.png +0 -0
- package/package.json +2 -2
- package/src/data/snapshot.ts +11 -1
- package/src/index.ts +9 -4
- package/src/render/glyphs.ts +4 -0
- package/src/render/paint.ts +8 -0
- package/src/render/segments.ts +31 -1
- package/src/settings.ts +1 -1
package/README.md
CHANGED
|
@@ -72,3 +72,8 @@ The look never names a colour. Every piece names a theme token, so it follows wh
|
|
|
72
72
|
The framed editor, and the idea of writing into its borders, come from [pi-open-tui](https://github.com/OldSuns/pi-open-tui), which this replaces. The segments with priorities come from [oh-my-pi](https://github.com/can1357/oh-my-pi).
|
|
73
73
|
|
|
74
74
|
Part of [pi-harness](https://github.com/felipeadeildo/pi-harness), which brings every piece in one install.
|
|
75
|
+
|
|
76
|
+
<p align="center">
|
|
77
|
+
<a href="https://adeildo.dev"><img src="https://adeildo.dev/assets/images/cat.svg" width="42" height="42" alt="adeildo.dev"></a><br>
|
|
78
|
+
Brought to you by <a href="https://adeildo.dev">adeildo.dev</a>
|
|
79
|
+
</p>
|
package/assets/preview.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adeildo/pi-look",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.9.1",
|
|
4
4
|
"description": "Pi's start screen, a framed editor and the footer, redrawn around the data that matters and painted only with the theme's colours.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@adeildo/pi-kit": "5.
|
|
41
|
+
"@adeildo/pi-kit": "5.9.1"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@earendil-works/pi-ai": "*",
|
package/src/data/snapshot.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { hostname } from "node:os";
|
|
2
2
|
|
|
3
|
-
import type { AccountState, SessionTasks } from "@adeildo/pi-kit";
|
|
3
|
+
import type { AccountState, PeerMark, SessionTasks } from "@adeildo/pi-kit";
|
|
4
4
|
import { VERSION, type ExtensionContext, type SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
|
|
6
6
|
import type { GitState } from "./git.ts";
|
|
@@ -51,6 +51,10 @@ export interface Snapshot {
|
|
|
51
51
|
statuses: ReadonlyMap<string, string>;
|
|
52
52
|
/** What the session is doing, when a tasks feature keeps it. */
|
|
53
53
|
tasks: SessionTasks | undefined;
|
|
54
|
+
/** The other sessions in this folder, when a peers feature keeps them. */
|
|
55
|
+
peers: PeerMark[];
|
|
56
|
+
/** Messages a peer sent this session that its agent has not read yet. */
|
|
57
|
+
peersUnread: number;
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
export function emptySnapshot(): Snapshot {
|
|
@@ -77,6 +81,8 @@ export function emptySnapshot(): Snapshot {
|
|
|
77
81
|
averages: { decode: undefined, prefill: undefined },
|
|
78
82
|
statuses: new Map(),
|
|
79
83
|
tasks: undefined,
|
|
84
|
+
peers: [],
|
|
85
|
+
peersUnread: 0,
|
|
80
86
|
};
|
|
81
87
|
}
|
|
82
88
|
|
|
@@ -89,6 +95,8 @@ export interface LiveInputs {
|
|
|
89
95
|
run: RunView | undefined;
|
|
90
96
|
statuses: ReadonlyMap<string, string>;
|
|
91
97
|
tasks?: SessionTasks | undefined;
|
|
98
|
+
peers?: PeerMark[] | undefined;
|
|
99
|
+
peersUnread?: number | undefined;
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
interface Derived {
|
|
@@ -139,6 +147,8 @@ export class SnapshotReader {
|
|
|
139
147
|
.filter(([, text]) => text !== ""),
|
|
140
148
|
),
|
|
141
149
|
tasks: live.tasks,
|
|
150
|
+
peers: live.peers ?? [],
|
|
151
|
+
peersUnread: live.peersUnread ?? 0,
|
|
142
152
|
};
|
|
143
153
|
}
|
|
144
154
|
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
accountStateOf,
|
|
3
3
|
createApp,
|
|
4
|
+
currentPeers,
|
|
4
5
|
currentTasks,
|
|
5
6
|
defineFeature,
|
|
6
7
|
drawSkillRefs,
|
|
7
8
|
type FeatureScope,
|
|
8
9
|
SKILL_REF,
|
|
9
|
-
type SessionTasks,
|
|
10
10
|
skillNames,
|
|
11
11
|
} from "@adeildo/pi-kit";
|
|
12
12
|
import type {
|
|
@@ -17,7 +17,7 @@ import type {
|
|
|
17
17
|
import type { TUI } from "@earendil-works/pi-tui";
|
|
18
18
|
|
|
19
19
|
import { type GitState, readGit } from "./data/git.ts";
|
|
20
|
-
import { emptySnapshot, type Snapshot, SnapshotReader } from "./data/snapshot.ts";
|
|
20
|
+
import { emptySnapshot, type LiveInputs, type Snapshot, SnapshotReader } from "./data/snapshot.ts";
|
|
21
21
|
import { Telemetry } from "./data/telemetry.ts";
|
|
22
22
|
import type { DeltaKind } from "./data/telemetry.ts";
|
|
23
23
|
import { REQUEST_ENTRY, type RequestRecord } from "./data/totals.ts";
|
|
@@ -323,8 +323,13 @@ function createScreen(
|
|
|
323
323
|
};
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
function tasksOf(scope: FeatureScope):
|
|
327
|
-
|
|
326
|
+
function tasksOf(scope: FeatureScope): Pick<LiveInputs, "tasks" | "peers" | "peersUnread"> {
|
|
327
|
+
const peers = currentPeers(scope.events);
|
|
328
|
+
return {
|
|
329
|
+
tasks: currentTasks(scope.events)?.state,
|
|
330
|
+
peers: peers?.peers,
|
|
331
|
+
peersUnread: peers?.unread,
|
|
332
|
+
};
|
|
328
333
|
}
|
|
329
334
|
|
|
330
335
|
function installEditor(
|
package/src/render/glyphs.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface Glyphs {
|
|
|
8
8
|
host: string;
|
|
9
9
|
session: string;
|
|
10
10
|
tasks: string;
|
|
11
|
+
peers: string;
|
|
11
12
|
waiting: string;
|
|
12
13
|
queued: string;
|
|
13
14
|
version: string;
|
|
@@ -57,6 +58,7 @@ export const NERD: Glyphs = {
|
|
|
57
58
|
host: "\u{f108}", // nf-fa-desktop
|
|
58
59
|
session: "\u{f02b}", // nf-fa-tag
|
|
59
60
|
tasks: "\u{f04fe}", // nf-md-target
|
|
61
|
+
peers: "\u{f0c0}", // nf-fa-users
|
|
60
62
|
waiting: "\u{f024}", // nf-fa-flag
|
|
61
63
|
queued: "\u{f051f}", // nf-md-timer_sand
|
|
62
64
|
version: "π",
|
|
@@ -87,6 +89,7 @@ export const UNICODE: Glyphs = {
|
|
|
87
89
|
host: "⌂",
|
|
88
90
|
session: "#",
|
|
89
91
|
tasks: "▸",
|
|
92
|
+
peers: "◆",
|
|
90
93
|
waiting: "⚑",
|
|
91
94
|
queued: "⧗",
|
|
92
95
|
version: "π",
|
|
@@ -116,6 +119,7 @@ export const ASCII: Glyphs = {
|
|
|
116
119
|
host: "@",
|
|
117
120
|
session: "#",
|
|
118
121
|
tasks: ">",
|
|
122
|
+
peers: "@",
|
|
119
123
|
waiting: "",
|
|
120
124
|
queued: "",
|
|
121
125
|
version: "v",
|
package/src/render/paint.ts
CHANGED
|
@@ -80,6 +80,8 @@ export interface Paint {
|
|
|
80
80
|
stress(percent: number, text: string): string;
|
|
81
81
|
money(amount: number, text: string): string;
|
|
82
82
|
frame(text: string): string;
|
|
83
|
+
/** A theme colour a package named in a contract, like a peer's own colour. */
|
|
84
|
+
raw(color: string, text: string): string;
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
export function themePaint(theme: Theme, frame?: (text: string) => string): Paint {
|
|
@@ -94,6 +96,11 @@ export function themePaint(theme: Theme, frame?: (text: string) => string): Pain
|
|
|
94
96
|
stress: (percent, text) => theme.fg(stressToken(percent), text),
|
|
95
97
|
money: (amount, text) => theme.fg(moneyToken(amount), text),
|
|
96
98
|
frame: frame ?? ((text) => theme.fg("border", text)),
|
|
99
|
+
raw: (color, text) =>
|
|
100
|
+
theme.fg(
|
|
101
|
+
theme.colors[color as ThemeColor] === undefined ? "muted" : (color as ThemeColor),
|
|
102
|
+
text,
|
|
103
|
+
),
|
|
97
104
|
};
|
|
98
105
|
}
|
|
99
106
|
|
|
@@ -120,6 +127,7 @@ export const PLAIN: Paint = {
|
|
|
120
127
|
stress: (_percent, text) => text,
|
|
121
128
|
money: (_amount, text) => text,
|
|
122
129
|
frame: identity,
|
|
130
|
+
raw: (_color, text) => text,
|
|
123
131
|
};
|
|
124
132
|
|
|
125
133
|
function identity(text: string): string {
|
package/src/render/segments.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AccountWindow, tasksWith } from "@adeildo/pi-kit";
|
|
1
|
+
import { type AccountWindow, type PeerMark, tasksWith } from "@adeildo/pi-kit";
|
|
2
2
|
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
3
3
|
|
|
4
4
|
import type { Snapshot } from "../data/snapshot.ts";
|
|
@@ -28,6 +28,7 @@ export const SEGMENT_IDS = [
|
|
|
28
28
|
"host",
|
|
29
29
|
"session",
|
|
30
30
|
"tasks",
|
|
31
|
+
"peers",
|
|
31
32
|
"version",
|
|
32
33
|
"clock",
|
|
33
34
|
"provider",
|
|
@@ -67,6 +68,7 @@ export function isSegmentId(value: string): value is SegmentId {
|
|
|
67
68
|
const CLAIMED_BY_SEGMENT: Partial<Record<BuiltinSegment, string>> = {
|
|
68
69
|
model: "pi-providers:account",
|
|
69
70
|
tasks: "pi-tasks",
|
|
71
|
+
peers: "pi-peers",
|
|
70
72
|
};
|
|
71
73
|
|
|
72
74
|
export function claimedStatuses(slots: readonly (readonly SegmentId[])[]): Set<string> {
|
|
@@ -155,6 +157,29 @@ export const SEGMENTS: Record<BuiltinSegment, Segment> = {
|
|
|
155
157
|
? undefined
|
|
156
158
|
: { text: `${mark(glyphs.host, paint, "host")}${paint.role("host", snapshot.host)}` },
|
|
157
159
|
},
|
|
160
|
+
peers: {
|
|
161
|
+
priority: 25,
|
|
162
|
+
describe:
|
|
163
|
+
"the other agents in this folder, each by the name it chose and in its colour, then @ how many messages wait for an answer",
|
|
164
|
+
// The whole folder, then one name, then the bare mark, as the line runs out of room.
|
|
165
|
+
render: ({ snapshot, glyphs, paint }) => {
|
|
166
|
+
const peers = snapshot.peers;
|
|
167
|
+
const mentions =
|
|
168
|
+
snapshot.peersUnread > 0 ? paint.role("waiting", `@${snapshot.peersUnread}`) : "";
|
|
169
|
+
const first = peers[0];
|
|
170
|
+
if (first === undefined) return mentions === "" ? undefined : { text: mentions };
|
|
171
|
+
const named = (peer: PeerMark) => paint.raw(peer.color, `${glyphs.peers} ${peer.handle}`);
|
|
172
|
+
const rest = peers.length > 1 ? paint.dim(` +${peers.length - 1}`) : "";
|
|
173
|
+
const full = joined([...peers].map(named).join(" "), mentions);
|
|
174
|
+
const short = joined(named(first) + rest, mentions);
|
|
175
|
+
const least = joined(paint.raw(first.color, glyphs.peers), mentions);
|
|
176
|
+
return {
|
|
177
|
+
text: full,
|
|
178
|
+
shrink: (width) =>
|
|
179
|
+
visibleWidth(full) <= width ? full : visibleWidth(short) <= width ? short : least,
|
|
180
|
+
};
|
|
181
|
+
},
|
|
182
|
+
},
|
|
158
183
|
tasks: {
|
|
159
184
|
priority: 30,
|
|
160
185
|
describe:
|
|
@@ -516,6 +541,11 @@ function quotaWindow(paint: Paint, window: AccountWindow): string {
|
|
|
516
541
|
return `${paint.dim(window.name)} ${used}${reset}`;
|
|
517
542
|
}
|
|
518
543
|
|
|
544
|
+
/** The parts that have something to say, with the gap the footer uses between things. */
|
|
545
|
+
function joined(...parts: string[]): string {
|
|
546
|
+
return parts.filter((part) => part !== "").join(" ");
|
|
547
|
+
}
|
|
548
|
+
|
|
519
549
|
function mark(glyph: string, paint: Paint, role: Role): string {
|
|
520
550
|
return glyph === "" ? "" : `${paint.role(role, glyph)} `;
|
|
521
551
|
}
|
package/src/settings.ts
CHANGED
|
@@ -89,7 +89,7 @@ export const stripRight = slot(
|
|
|
89
89
|
"stripRight",
|
|
90
90
|
"Answer strip, on the right",
|
|
91
91
|
"The other side of that line. When it does not fit beside the strip, it gets a line of its own under it.",
|
|
92
|
-
["tasks"],
|
|
92
|
+
["tasks", "peers"],
|
|
93
93
|
);
|
|
94
94
|
export const topLeft = slot("frame.topLeft", "Frame, top left", "After the working spinner.", [
|
|
95
95
|
"branch",
|