@adeildo/pi-look 5.11.2 → 5.12.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/assets/preview.png +0 -0
- package/package.json +2 -2
- package/src/index.ts +13 -8
- package/src/render/format.ts +3 -11
- package/src/render/glyphs.ts +4 -0
- package/src/render/segments.ts +7 -2
- package/src/render/streaming.ts +2 -5
- package/src/ui/tool-frame.ts +6 -12
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.12.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.12.1"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@earendil-works/pi-ai": "*",
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { dirname, join } from "node:path";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
ACCOUNT,
|
|
5
|
+
type AccountState,
|
|
5
6
|
createApp,
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
PEERS,
|
|
8
|
+
TASKS,
|
|
8
9
|
defineFeature,
|
|
9
10
|
drawSkillRefs,
|
|
10
11
|
type FeatureScope,
|
|
@@ -321,7 +322,7 @@ function createScreen(
|
|
|
321
322
|
const snapshot = reader.read(ctx, {
|
|
322
323
|
branch: live.footerData?.getGitBranch() ?? null,
|
|
323
324
|
git: live.git,
|
|
324
|
-
account:
|
|
325
|
+
account: accountOf(scope, ctx.model?.provider),
|
|
325
326
|
request: telemetry.request(),
|
|
326
327
|
last: telemetry.last(),
|
|
327
328
|
run: telemetry.run(),
|
|
@@ -353,18 +354,22 @@ function createScreen(
|
|
|
353
354
|
decorate: (line) => {
|
|
354
355
|
const theme = live.ctx?.ui.theme;
|
|
355
356
|
if (theme === undefined) return line;
|
|
356
|
-
return drawSkillRefs(line, skillNames(scope
|
|
357
|
+
return drawSkillRefs(line, skillNames(scope), (name) =>
|
|
357
358
|
theme.fg("customMessageLabel", `${SKILL_REF}${name}`),
|
|
358
359
|
);
|
|
359
360
|
},
|
|
360
|
-
skills: () => skillNames(scope
|
|
361
|
+
skills: () => skillNames(scope).length > 0,
|
|
361
362
|
};
|
|
362
363
|
}
|
|
363
364
|
|
|
365
|
+
function accountOf(scope: FeatureScope, provider: string | undefined): AccountState | undefined {
|
|
366
|
+
return provider === undefined ? undefined : ACCOUNT.current(scope, provider);
|
|
367
|
+
}
|
|
368
|
+
|
|
364
369
|
function tasksOf(scope: FeatureScope): Pick<LiveInputs, "tasks" | "peers" | "peersUnread"> {
|
|
365
|
-
const peers =
|
|
370
|
+
const peers = PEERS.current(scope);
|
|
366
371
|
return {
|
|
367
|
-
tasks:
|
|
372
|
+
tasks: TASKS.current(scope)?.state,
|
|
368
373
|
peers: peers?.peers,
|
|
369
374
|
peersUnread: peers?.unread,
|
|
370
375
|
};
|
package/src/render/format.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sep } from "node:path";
|
|
2
2
|
|
|
3
|
-
import { formatCost, formatTokens } from "@adeildo/pi-kit";
|
|
3
|
+
import { formatCost, formatTokens, tildify } from "@adeildo/pi-kit";
|
|
4
4
|
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
5
5
|
|
|
6
6
|
export const CELL = {
|
|
@@ -57,7 +57,7 @@ export function percent(value: number): string {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export function shortenPath(cwd: string, home: string | undefined, maxWidth: number): string {
|
|
60
|
-
const full = home === undefined ? cwd :
|
|
60
|
+
const full = home === undefined ? cwd : tildify(cwd, home);
|
|
61
61
|
if (maxWidth <= 0 || visibleWidth(full) <= maxWidth) return full;
|
|
62
62
|
|
|
63
63
|
const parts = full.split(sep);
|
|
@@ -104,14 +104,6 @@ export function tabs(text: string): string {
|
|
|
104
104
|
return text.replace(/\t/g, " ");
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
function underHome(cwd: string, home: string): string {
|
|
108
|
-
const rest = relative(resolve(home), resolve(cwd));
|
|
109
|
-
const inside =
|
|
110
|
-
rest === "" || (rest !== ".." && !rest.startsWith(`..${sep}`) && !isAbsolute(rest));
|
|
111
|
-
if (!inside) return cwd;
|
|
112
|
-
return rest === "" ? "~" : `~${sep}${rest}`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
107
|
function trim(fixed: string): string {
|
|
116
108
|
return fixed.endsWith(".0") ? fixed.slice(0, -2) : fixed;
|
|
117
109
|
}
|
package/src/render/glyphs.ts
CHANGED
|
@@ -37,6 +37,8 @@ export interface Glyphs {
|
|
|
37
37
|
empty: string;
|
|
38
38
|
levels: readonly string[];
|
|
39
39
|
ellipsis: string;
|
|
40
|
+
/** Between the agents of the folder and those of each shared room. */
|
|
41
|
+
apart: string;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
const COMMON = {
|
|
@@ -48,6 +50,7 @@ const COMMON = {
|
|
|
48
50
|
conflicted: "!",
|
|
49
51
|
sent: "↑",
|
|
50
52
|
received: "↓",
|
|
53
|
+
apart: "│",
|
|
51
54
|
};
|
|
52
55
|
|
|
53
56
|
export const NERD: Glyphs = {
|
|
@@ -114,6 +117,7 @@ export const UNICODE: Glyphs = {
|
|
|
114
117
|
|
|
115
118
|
export const ASCII: Glyphs = {
|
|
116
119
|
set: "ascii",
|
|
120
|
+
apart: "|",
|
|
117
121
|
folder: "",
|
|
118
122
|
branch: "",
|
|
119
123
|
host: "@",
|
package/src/render/segments.ts
CHANGED
|
@@ -160,7 +160,7 @@ export const SEGMENTS: Record<BuiltinSegment, Segment> = {
|
|
|
160
160
|
peers: {
|
|
161
161
|
priority: 25,
|
|
162
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",
|
|
163
|
+
"the other agents in this folder, then those of each shared room after a rule, each by the name it chose and in its colour, then @ how many messages wait for an answer",
|
|
164
164
|
// The whole folder, then one name, then the bare mark, as the line runs out of room.
|
|
165
165
|
render: ({ snapshot, glyphs, paint }) => {
|
|
166
166
|
const peers = snapshot.peers;
|
|
@@ -170,7 +170,12 @@ export const SEGMENTS: Record<BuiltinSegment, Segment> = {
|
|
|
170
170
|
if (first === undefined) return mentions === "" ? undefined : { text: mentions };
|
|
171
171
|
const named = (peer: PeerMark) => paint.raw(peer.color, `${glyphs.peers} ${peer.handle}`);
|
|
172
172
|
const rest = peers.length > 1 ? paint.dim(` +${peers.length - 1}`) : "";
|
|
173
|
-
const
|
|
173
|
+
const rooms = new Map<string, string[]>();
|
|
174
|
+
for (const peer of peers)
|
|
175
|
+
rooms.set(peer.room ?? "", [...(rooms.get(peer.room ?? "") ?? []), named(peer)]);
|
|
176
|
+
const apart = ` ${paint.dim(glyphs.apart)} `;
|
|
177
|
+
const groups = [...rooms.values()].map((group) => group.join(" "));
|
|
178
|
+
const full = joined(groups.join(apart), mentions);
|
|
174
179
|
const short = joined(named(first) + rest, mentions);
|
|
175
180
|
const least = joined(paint.raw(first.color, glyphs.peers), mentions);
|
|
176
181
|
return {
|
package/src/render/streaming.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { readFileSync, statSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
2
|
import { resolve } from "node:path";
|
|
4
3
|
|
|
5
4
|
// While the model writes an edit or a write, pi shows the path, or the first lines of the file,
|
|
6
5
|
// which stopped changing long ago. These show the end instead: the lines arriving, with a cursor
|
|
7
6
|
// where the model is writing, and what it has written so far on the bottom rule.
|
|
8
|
-
import { GLYPH, isObject } from "@adeildo/pi-kit";
|
|
7
|
+
import { GLYPH, isObject, tildify } from "@adeildo/pi-kit";
|
|
9
8
|
import { getLanguageFromPath, highlightCode, type Theme } from "@earendil-works/pi-coding-agent";
|
|
10
9
|
import { stripTerminalSequences } from "@earendil-works/pi-tui";
|
|
11
10
|
|
|
@@ -154,9 +153,7 @@ function pathLine(tool: string, path: string | undefined, theme: Theme): string
|
|
|
154
153
|
const title = theme.fg("toolTitle", theme.bold(tool));
|
|
155
154
|
// Pi's own stand-in for a path not written yet.
|
|
156
155
|
if (path === undefined) return `${title} ${theme.fg("toolOutput", "...")}`;
|
|
157
|
-
|
|
158
|
-
const shown = path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
159
|
-
return `${title} ${theme.fg("accent", printable(shown))}`;
|
|
156
|
+
return `${title} ${theme.fg("accent", printable(tildify(path)))}`;
|
|
160
157
|
}
|
|
161
158
|
|
|
162
159
|
/**
|
package/src/ui/tool-frame.ts
CHANGED
|
@@ -4,11 +4,9 @@ import {
|
|
|
4
4
|
CUT,
|
|
5
5
|
type CallRuling,
|
|
6
6
|
type FeatureScope,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
RULING_CHANGED,
|
|
7
|
+
CALLS_FRAMED,
|
|
8
|
+
RULING,
|
|
10
9
|
type RulingTone,
|
|
11
|
-
rulingOf,
|
|
12
10
|
GLYPH,
|
|
13
11
|
LINE,
|
|
14
12
|
} from "@adeildo/pi-kit";
|
|
@@ -48,12 +46,8 @@ export function registerToolFrames(scope: FeatureScope): void {
|
|
|
48
46
|
const written = new Map<string, number>();
|
|
49
47
|
// The set is asked for on every drawing of every frame, and it changes only with the setting.
|
|
50
48
|
const set = glyphsOf(() => icons.get(scope));
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
scope.events.on(RULING_CHANGED, (data) => {
|
|
55
|
-
if (isObject(data) && typeof data.toolCallId === "string") redraws.get(data.toolCallId)?.();
|
|
56
|
-
});
|
|
49
|
+
CALLS_FRAMED.provide(scope, () => true);
|
|
50
|
+
RULING.onChanged(scope, (_ruling, toolCallId) => redraws.get(toolCallId)?.());
|
|
57
51
|
// Pi completes every call of a message only when the message ends, so a call the model finished
|
|
58
52
|
// before writing the next one would go on writing. The provider says when each one ends. Pi tells
|
|
59
53
|
// the extensions first and hands every call its arguments right after, which draws the frame.
|
|
@@ -119,7 +113,7 @@ function framed(
|
|
|
119
113
|
else clock.writtenAt ??= ended ?? now;
|
|
120
114
|
if (context.executionStarted) clock.startedAt ??= now;
|
|
121
115
|
const writing = clock.writtenAt === undefined;
|
|
122
|
-
const ruling =
|
|
116
|
+
const ruling = RULING.current(scope, context.toolCallId);
|
|
123
117
|
|
|
124
118
|
// Pi hands every call of a message its arguments again on each piece the model writes, so a
|
|
125
119
|
// call that did not change keeps the frame it has, and its renderer is not asked again.
|
|
@@ -178,7 +172,7 @@ function framed(
|
|
|
178
172
|
renderResult(shown, options, theme, context) {
|
|
179
173
|
const clock = clockOf(context);
|
|
180
174
|
// A blocked call prints nothing but the reason the line above already gives.
|
|
181
|
-
const blocked =
|
|
175
|
+
const blocked = RULING.current(scope, context.toolCallId)?.blocked === true;
|
|
182
176
|
// Pi draws the pictures of a result right below the frame, so the frame counts them.
|
|
183
177
|
const images = shown.content.filter((block) => block.type === "image").length;
|
|
184
178
|
if (!options.isPartial) clock.endedAt ??= Date.now();
|