@leo-alvarenga/pi-zen-frame 0.14.0 → 0.16.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/docs/preview.png +0 -0
- package/extensions/components/cwd.ts +3 -1
- package/extensions/components/frame.ts +3 -11
- package/extensions/components/model.ts +2 -2
- package/extensions/components/token-count.ts +15 -7
- package/extensions/components/types.ts +2 -5
- package/extensions/config/constants/visuals.ts +2 -1
- package/extensions/config/types.ts +2 -0
- package/extensions/editor/blocky-editor.ts +2 -1
- package/extensions/index.ts +3 -11
- package/extensions/utils/token.ts +49 -0
- package/package.json +1 -1
package/docs/preview.png
CHANGED
|
Binary file
|
|
@@ -13,7 +13,9 @@ export const cwdSegment: SegmentDef = {
|
|
|
13
13
|
|
|
14
14
|
if (d.gitBranch) {
|
|
15
15
|
let git = ` ${icons.gitBranch} ${d.gitBranch}`;
|
|
16
|
-
text +=
|
|
16
|
+
text +=
|
|
17
|
+
theme.fg("dim", " ·") +
|
|
18
|
+
theme.fg(d.zenMode ? "muted" : d.accentColor, git);
|
|
17
19
|
|
|
18
20
|
if (d.gitDirty > 0) {
|
|
19
21
|
text += theme.fg(
|
|
@@ -43,21 +43,13 @@ export function composeBand(
|
|
|
43
43
|
autocomplete: string[],
|
|
44
44
|
opts: BandOptions,
|
|
45
45
|
): string[] {
|
|
46
|
-
const {
|
|
47
|
-
|
|
48
|
-
paint,
|
|
49
|
-
width,
|
|
50
|
-
prefix,
|
|
51
|
-
marginX,
|
|
52
|
-
boxBottom,
|
|
53
|
-
paddingTop,
|
|
54
|
-
paddingBottom,
|
|
55
|
-
} = opts;
|
|
46
|
+
const { padX, paint, width, prefix, boxBottom, paddingTop, paddingBottom } =
|
|
47
|
+
opts;
|
|
56
48
|
|
|
57
49
|
const lead = Math.max(0, padX - visibleWidth(prefix));
|
|
58
50
|
|
|
59
51
|
const inset = (row: string) =>
|
|
60
|
-
|
|
52
|
+
prefix + paint(" ".repeat(lead) + row + " ".repeat(padX));
|
|
61
53
|
|
|
62
54
|
const fill = () => inset(paint(" ".repeat(Math.max(0, width - 2 * padX))));
|
|
63
55
|
|
|
@@ -5,9 +5,9 @@ export const modelSegment: SegmentDef = {
|
|
|
5
5
|
id: "model",
|
|
6
6
|
slot: "topLeft",
|
|
7
7
|
enabled: (_d, cfg) => cfg.showModel !== false,
|
|
8
|
-
render: (d, { theme }) => {
|
|
8
|
+
render: (d, { icons, theme }) => {
|
|
9
9
|
if (!d.modelName) return "";
|
|
10
10
|
|
|
11
|
-
return ` ${theme.fg(
|
|
11
|
+
return ` ${theme.fg(d.accentColor, `${icons.model} ${d.modelName}`)} ${theme.fg("muted", `(${d.modelProvider})`)} `;
|
|
12
12
|
},
|
|
13
13
|
};
|
|
@@ -4,12 +4,13 @@ import type { SegmentDef } from "./types";
|
|
|
4
4
|
|
|
5
5
|
function trimFixed1(n: number): string {
|
|
6
6
|
const t = n.toFixed(1);
|
|
7
|
+
|
|
7
8
|
return t.endsWith(".0") ? t.slice(0, -2) : t;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
function formatTokens(n: number): string {
|
|
11
12
|
if (n >= 1_000_000) return `${trimFixed1(n / 1_000_000)}M`;
|
|
12
|
-
if (n >= 1_000) return `${(n / 1_000).toFixed(
|
|
13
|
+
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`;
|
|
13
14
|
|
|
14
15
|
return `${n}`;
|
|
15
16
|
}
|
|
@@ -28,13 +29,15 @@ export const tokenCountSegment: SegmentDef = {
|
|
|
28
29
|
const c = d.context;
|
|
29
30
|
if (!c) return "";
|
|
30
31
|
|
|
31
|
-
const
|
|
32
|
+
const { input, output, percent, tokens, window } = c;
|
|
33
|
+
|
|
34
|
+
const pct = percent === null ? "?" : `${Math.round(percent)}%`;
|
|
32
35
|
let color: ThemeColor = "muted";
|
|
33
36
|
|
|
34
|
-
if (
|
|
35
|
-
if (
|
|
37
|
+
if (percent !== null) {
|
|
38
|
+
if (percent >= 80) {
|
|
36
39
|
color = "error";
|
|
37
|
-
} else if (
|
|
40
|
+
} else if (percent >= 50) {
|
|
38
41
|
color = "warning";
|
|
39
42
|
} else {
|
|
40
43
|
color = "success";
|
|
@@ -42,12 +45,17 @@ export const tokenCountSegment: SegmentDef = {
|
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
const color2 = d.zenMode ? "muted" : color;
|
|
45
|
-
const used =
|
|
48
|
+
const used = tokens === null ? "?" : formatTokens(tokens);
|
|
46
49
|
|
|
47
50
|
return (
|
|
48
51
|
theme.fg(color2, ` ${icons.context} ctx ${pct} `) +
|
|
49
52
|
theme.fg("dim", "·") +
|
|
50
|
-
theme.fg(color2, ` ${used}/${formatWindow(
|
|
53
|
+
theme.fg(color2, ` ${used}/${formatWindow(window)} `) +
|
|
54
|
+
theme.fg("dim", "·") +
|
|
55
|
+
theme.fg(
|
|
56
|
+
"dim",
|
|
57
|
+
` ${icons.tokenIn} ${formatTokens(input)} ${icons.tokenOut} ${formatTokens(output)} `,
|
|
58
|
+
)
|
|
51
59
|
);
|
|
52
60
|
},
|
|
53
61
|
};
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import type { Theme, ThemeColor } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
|
|
13
13
|
import type { FrameIcons, FrameSettings, SpinnerPhase } from "../config/types";
|
|
14
|
+
import { TokenUsage } from "../utils/token";
|
|
14
15
|
|
|
15
16
|
/** Where a segment renders. "top*" → the box-bottom row inside the band,
|
|
16
17
|
* "bottom*" → the pseudo-footer row below it (names kept from the old
|
|
@@ -54,11 +55,7 @@ export interface FrameData {
|
|
|
54
55
|
/** To render the agent mode (from @leo-alvarenga/pi-agent-manager) segment, if any */
|
|
55
56
|
agentMode: AgentMode;
|
|
56
57
|
|
|
57
|
-
context:
|
|
58
|
-
window: number;
|
|
59
|
-
tokens: number | null;
|
|
60
|
-
percent: number | null;
|
|
61
|
-
} | null;
|
|
58
|
+
context: TokenUsage | null;
|
|
62
59
|
}
|
|
63
60
|
|
|
64
61
|
/** Data + rendering helpers handed to every segment. */
|
|
@@ -15,7 +15,9 @@ export const SPINNER_FRAMES: Record<SpinnerPhase, string[]> = {
|
|
|
15
15
|
export const DEFAULT_ICONS: FrameIcons = {
|
|
16
16
|
model: "",
|
|
17
17
|
folder: " ",
|
|
18
|
+
tokenIn: "",
|
|
18
19
|
context: "",
|
|
20
|
+
tokenOut: "",
|
|
19
21
|
gitDirty: "",
|
|
20
22
|
thinking: "",
|
|
21
23
|
gitBranch: "",
|
|
@@ -32,7 +34,6 @@ export const THINKING_TOKEN: Record<string, string> = {
|
|
|
32
34
|
max: "thinkingMax",
|
|
33
35
|
};
|
|
34
36
|
|
|
35
|
-
|
|
36
37
|
export const THEME_COLORS: Record<ThemeColor, true> = {
|
|
37
38
|
accent: true,
|
|
38
39
|
border: true,
|
|
@@ -103,7 +103,8 @@ export class BlockyEditor extends CustomEditor {
|
|
|
103
103
|
let prefix = "┃";
|
|
104
104
|
|
|
105
105
|
if (ext.theme?.fg) {
|
|
106
|
-
const prefixColor: ThemeColor =
|
|
106
|
+
const prefixColor: ThemeColor =
|
|
107
|
+
ext.agentMode?.color ?? this.opts.accentColor ?? "text";
|
|
107
108
|
prefix = ext.theme.fg(prefixColor, prefix);
|
|
108
109
|
}
|
|
109
110
|
|
package/extensions/index.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
type GitInfo,
|
|
38
38
|
} from "./utils";
|
|
39
39
|
import { type HeaderEnv } from "./components/header";
|
|
40
|
+
import { getUsage } from "./utils/token";
|
|
40
41
|
|
|
41
42
|
let editor: EditorFrameRenderer | null = null;
|
|
42
43
|
let currentCtx: ExtensionContext | null = null;
|
|
@@ -84,7 +85,6 @@ function getHeaderEnv(pi: ExtensionAPI): HeaderEnv {
|
|
|
84
85
|
|
|
85
86
|
function provideExternal(pi: ExtensionAPI): ExternalData {
|
|
86
87
|
const ctx = currentCtx;
|
|
87
|
-
const usage = ctx?.getContextUsage?.();
|
|
88
88
|
const model = ctx?.model;
|
|
89
89
|
|
|
90
90
|
return {
|
|
@@ -95,16 +95,10 @@ function provideExternal(pi: ExtensionAPI): ExternalData {
|
|
|
95
95
|
gitDirty: git.dirty,
|
|
96
96
|
gitBranch: git.branch,
|
|
97
97
|
theme: ctx?.ui.theme,
|
|
98
|
+
context: getUsage(ctx ?? undefined),
|
|
98
99
|
thinkingLevel: pi.getThinkingLevel(),
|
|
99
100
|
modelName: model?.name ?? model?.id ?? "Unknown",
|
|
100
101
|
modelProvider: capitalize(model?.provider ?? "unknown"),
|
|
101
|
-
context: usage
|
|
102
|
-
? {
|
|
103
|
-
tokens: usage.tokens,
|
|
104
|
-
window: usage.contextWindow,
|
|
105
|
-
percent: usage.percent,
|
|
106
|
-
}
|
|
107
|
-
: null,
|
|
108
102
|
};
|
|
109
103
|
}
|
|
110
104
|
|
|
@@ -232,9 +226,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
232
226
|
{
|
|
233
227
|
frame: settings.frame ?? DEFAULT_SETTINGS.frame!,
|
|
234
228
|
accentColor:
|
|
235
|
-
settings.accentColor ??
|
|
236
|
-
DEFAULT_SETTINGS.accentColor ??
|
|
237
|
-
"accent",
|
|
229
|
+
settings.accentColor ?? DEFAULT_SETTINGS.accentColor ?? "accent",
|
|
238
230
|
},
|
|
239
231
|
...args,
|
|
240
232
|
);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
export type TokenUsage = TokenThroughput & {
|
|
4
|
+
tokens: number;
|
|
5
|
+
window: number;
|
|
6
|
+
percent: number;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type TokenThroughput = {
|
|
10
|
+
input: number;
|
|
11
|
+
output: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type EntryUsage = {
|
|
15
|
+
usage?: TokenThroughput;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function getUsageFromEntries(ctx: ExtensionContext): TokenThroughput {
|
|
19
|
+
let input = 0,
|
|
20
|
+
output = 0;
|
|
21
|
+
|
|
22
|
+
for (const entry of ctx.sessionManager.getEntries()) {
|
|
23
|
+
const usage =
|
|
24
|
+
entry.type === "compaction" || entry.type === "branch_summary"
|
|
25
|
+
? (entry as EntryUsage).usage
|
|
26
|
+
: (entry as { message?: EntryUsage }).message?.usage;
|
|
27
|
+
|
|
28
|
+
if (!usage) continue;
|
|
29
|
+
|
|
30
|
+
input += usage.input ?? 0;
|
|
31
|
+
output += usage.output ?? 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { input, output };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getUsage(ctx?: ExtensionContext): TokenUsage | null {
|
|
38
|
+
if (!ctx) return null;
|
|
39
|
+
|
|
40
|
+
const usage = ctx.getContextUsage();
|
|
41
|
+
if (!usage) return null;
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
...getUsageFromEntries(ctx),
|
|
45
|
+
tokens: usage?.tokens ?? 0,
|
|
46
|
+
percent: usage?.percent ?? 0,
|
|
47
|
+
window: usage?.contextWindow ?? 0,
|
|
48
|
+
};
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@leo-alvarenga/pi-zen-frame",
|
|
3
3
|
"description": "A pi-coding-agent extension that gives the TUI editor a simple yet polished look and feel",
|
|
4
4
|
"author": "Leonardo A. Alvarenga",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.16.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github.com/leo-alvarenga/pi-mono",
|
|
8
8
|
"publishConfig": {
|