@leo-alvarenga/pi-zen-frame 0.13.0 → 0.15.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 +0 -32
- package/extensions/components/frame.ts +3 -11
- package/extensions/components/header.ts +17 -11
- package/extensions/config/constants/defaults.ts +1 -18
- package/extensions/config/constants/visuals.ts +1 -1
- package/extensions/config/settings.ts +0 -35
- package/extensions/config/types.ts +3 -34
- package/extensions/editor/blocky-editor.ts +11 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,11 +52,6 @@ Configuration is loaded from `~/.pi/agent/pi-zen-frame.json`. All properties are
|
|
|
52
52
|
"enable": true,
|
|
53
53
|
// Header renderer by registered name (built-in: "basic")
|
|
54
54
|
"type": "basic",
|
|
55
|
-
"logo": ["█████████ ", "███ ███ ", "██████ ", "███ ███"],
|
|
56
|
-
"heading": "Welcome back!",
|
|
57
|
-
"subheading": "Ready for your next session? Terminal warm, context clean, tools ready to execute",
|
|
58
|
-
"logoColor": "text",
|
|
59
|
-
"accentColor": "customMessageLabel",
|
|
60
55
|
},
|
|
61
56
|
|
|
62
57
|
"frame": {
|
|
@@ -65,15 +60,6 @@ Configuration is loaded from `~/.pi/agent/pi-zen-frame.json`. All properties are
|
|
|
65
60
|
// Minimum terminal width required to render the border frame
|
|
66
61
|
"minWidth": 20,
|
|
67
62
|
|
|
68
|
-
// Padding inside the frame
|
|
69
|
-
"paddingTop": 1,
|
|
70
|
-
"paddingBottom": 1,
|
|
71
|
-
"paddingX": 2,
|
|
72
|
-
|
|
73
|
-
// Outer margin around the frame
|
|
74
|
-
"marginTop": 0,
|
|
75
|
-
"marginBottom": 0,
|
|
76
|
-
|
|
77
63
|
// Toggle individual status segments
|
|
78
64
|
"showCwd": true,
|
|
79
65
|
"showModel": true,
|
|
@@ -81,24 +67,6 @@ Configuration is loaded from `~/.pi/agent/pi-zen-frame.json`. All properties are
|
|
|
81
67
|
"showThinking": true,
|
|
82
68
|
"showSpinner": false,
|
|
83
69
|
"showAgentMode": true,
|
|
84
|
-
|
|
85
|
-
// Color options: "border", "accentColor", or "agentMode"
|
|
86
|
-
"borderColor": "border",
|
|
87
|
-
|
|
88
|
-
// Input prompt prefix glyph and color settings
|
|
89
|
-
"prefix": "┃",
|
|
90
|
-
"prefixColor": "muted", // Options: "agentMode", "frameBorder", or any ThemeColor
|
|
91
|
-
|
|
92
|
-
// Override default Nerd-Font glyphs
|
|
93
|
-
"icons": {
|
|
94
|
-
// Keys: folder, model, context, thinking, gitDirty, gitBranch
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
-
// Per-segment foreground color overrides (supersedes default theme colors)
|
|
98
|
-
"colors": {
|
|
99
|
-
"model": "accent",
|
|
100
|
-
"cwd": "accent",
|
|
101
|
-
},
|
|
102
70
|
},
|
|
103
71
|
|
|
104
72
|
// Randomized status messages shown while streaming responses
|
|
@@ -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
|
|
|
@@ -8,7 +8,6 @@ import type {
|
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
DEFAULT_ICONS,
|
|
11
|
-
DEFAULT_SETTINGS,
|
|
12
11
|
HEADER_TIPS,
|
|
13
12
|
} from "../config/constants";
|
|
14
13
|
import type { Settings } from "../config/types";
|
|
@@ -19,6 +18,18 @@ import { getShortCwd } from "../utils";
|
|
|
19
18
|
const MIN_BOX_WIDTH = 20;
|
|
20
19
|
const LEFT_COL_RATIO = 0.4; // logo column width / total width
|
|
21
20
|
|
|
21
|
+
/** BasicHeader preset styling — self-contained, not user-configurable. */
|
|
22
|
+
const LOGO_LINES = [
|
|
23
|
+
"█████████ ",
|
|
24
|
+
"███ ███ ",
|
|
25
|
+
"██████ ",
|
|
26
|
+
"███ ███",
|
|
27
|
+
];
|
|
28
|
+
const LOGO_COLOR: ThemeColor = "text";
|
|
29
|
+
const HEADING = "Welcome back!";
|
|
30
|
+
const SUBHEADING =
|
|
31
|
+
"Ready for your next session? Terminal warm, context clean, tools ready to execute";
|
|
32
|
+
|
|
22
33
|
/** Live env snapshot the header renders in the right column. */
|
|
23
34
|
export interface HeaderEnv {
|
|
24
35
|
gitBranch: string | undefined;
|
|
@@ -60,12 +71,11 @@ export function createHeader(
|
|
|
60
71
|
};
|
|
61
72
|
}
|
|
62
73
|
|
|
63
|
-
const accentColor =
|
|
64
|
-
settings.header?.accentColor ?? settings.accentColor ?? "accent";
|
|
74
|
+
const accentColor = settings.accentColor ?? "accent";
|
|
65
75
|
|
|
66
76
|
const logo = {
|
|
67
|
-
lines:
|
|
68
|
-
color:
|
|
77
|
+
lines: LOGO_LINES,
|
|
78
|
+
color: LOGO_COLOR,
|
|
69
79
|
};
|
|
70
80
|
|
|
71
81
|
const border = (s: string, fg?: ThemeColor) => theme.fg(fg ?? accentColor, s);
|
|
@@ -151,18 +161,14 @@ export function createHeader(
|
|
|
151
161
|
"",
|
|
152
162
|
|
|
153
163
|
...wrapLines(
|
|
154
|
-
[
|
|
164
|
+
[HEADING],
|
|
155
165
|
leftW - 2,
|
|
156
166
|
).map((line) => theme.bold(theme.fg("muted", line))),
|
|
157
167
|
|
|
158
168
|
"",
|
|
159
169
|
|
|
160
170
|
...wrapLines(
|
|
161
|
-
[
|
|
162
|
-
settings.header?.subheading ??
|
|
163
|
-
DEFAULT_SETTINGS.header?.subheading ??
|
|
164
|
-
"",
|
|
165
|
-
],
|
|
171
|
+
[SUBHEADING],
|
|
166
172
|
leftW - 2,
|
|
167
173
|
).map((line) => theme.italic(theme.fg("muted", line))),
|
|
168
174
|
];
|
|
@@ -8,14 +8,6 @@ export const DEFAULT_SETTINGS: Settings = {
|
|
|
8
8
|
header: {
|
|
9
9
|
enable: false,
|
|
10
10
|
type: "basic",
|
|
11
|
-
logoColor: "text",
|
|
12
|
-
accentColor: "accent",
|
|
13
|
-
|
|
14
|
-
heading: "Welcome back!",
|
|
15
|
-
subheading:
|
|
16
|
-
"Ready for your next session? Terminal warm, context clean, tools ready to execute",
|
|
17
|
-
|
|
18
|
-
logo: ["█████████ ", "███ ███ ", "██████ ", "███ ███"],
|
|
19
11
|
},
|
|
20
12
|
|
|
21
13
|
workingMessage: {
|
|
@@ -25,22 +17,13 @@ export const DEFAULT_SETTINGS: Settings = {
|
|
|
25
17
|
},
|
|
26
18
|
|
|
27
19
|
frame: {
|
|
28
|
-
icons: {},
|
|
29
|
-
marginX: 1,
|
|
30
|
-
paddingX: 2,
|
|
31
|
-
prefix: "┃",
|
|
32
20
|
enable: true,
|
|
33
21
|
minWidth: 20,
|
|
34
|
-
marginTop: 0,
|
|
35
22
|
showCwd: true,
|
|
36
|
-
paddingTop: 1,
|
|
37
|
-
marginBottom: 0,
|
|
38
23
|
showModel: true,
|
|
39
|
-
paddingBottom: 1,
|
|
40
|
-
showSpinner: false,
|
|
41
24
|
showContext: true,
|
|
42
25
|
showThinking: true,
|
|
43
26
|
showAgentMode: true,
|
|
44
|
-
|
|
27
|
+
showSpinner: false,
|
|
45
28
|
},
|
|
46
29
|
};
|
|
@@ -11,7 +11,7 @@ export const SPINNER_FRAMES: Record<SpinnerPhase, string[]> = {
|
|
|
11
11
|
// Sample ["░", "▒", "▓", "█", "▓", "▒"],
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
/** Nerd Font
|
|
14
|
+
/** Nerd Font glyphs used by the blocky editor preset. */
|
|
15
15
|
export const DEFAULT_ICONS: FrameIcons = {
|
|
16
16
|
model: "",
|
|
17
17
|
folder: " ",
|
|
@@ -31,9 +31,6 @@ function normalize(raw: unknown): Settings {
|
|
|
31
31
|
const bool = (v: unknown, fallback: boolean | undefined): boolean =>
|
|
32
32
|
typeof v === "boolean" ? v : (fallback ?? true);
|
|
33
33
|
|
|
34
|
-
const str = (v: unknown, fallback: string): string =>
|
|
35
|
-
typeof v === "string" ? v : fallback;
|
|
36
|
-
|
|
37
34
|
const d = DEFAULT_SETTINGS;
|
|
38
35
|
const r = raw as Record<string, unknown>;
|
|
39
36
|
|
|
@@ -50,31 +47,13 @@ function normalize(raw: unknown): Settings {
|
|
|
50
47
|
|
|
51
48
|
out.frame = {
|
|
52
49
|
enable: bool(f.enable, d.frame?.enable),
|
|
53
|
-
marginX: num(f.marginX, d.frame?.marginX ?? 1),
|
|
54
|
-
paddingX: num(f.paddingX, d.frame?.paddingX ?? 1),
|
|
55
50
|
minWidth: num(f.minWidth, d.frame?.minWidth ?? 20),
|
|
56
|
-
paddingTop: num(f.paddingTop, d.frame?.paddingTop ?? 0),
|
|
57
|
-
paddingBottom: num(f.paddingBottom, d.frame?.paddingBottom ?? 1),
|
|
58
|
-
marginTop: num(f.marginTop, d.frame?.marginTop ?? 0),
|
|
59
|
-
marginBottom: num(f.marginBottom, d.frame?.marginBottom ?? 0),
|
|
60
51
|
showModel: bool(f.showModel, d.frame?.showModel),
|
|
61
52
|
showThinking: bool(f.showThinking, d.frame?.showThinking),
|
|
62
53
|
showContext: bool(f.showContext, d.frame?.showContext),
|
|
63
54
|
showCwd: bool(f.showCwd, d.frame?.showCwd),
|
|
64
55
|
showAgentMode: bool(f.showAgentMode, d.frame?.showAgentMode),
|
|
65
56
|
showSpinner: bool(f.showSpinner, d.frame?.showSpinner),
|
|
66
|
-
icons:
|
|
67
|
-
typeof f.icons === "object" && f.icons
|
|
68
|
-
? { ...d.frame?.icons, ...(f.icons as Record<string, unknown>) }
|
|
69
|
-
: d.frame?.icons,
|
|
70
|
-
prefix:
|
|
71
|
-
typeof f.prefix === "string" ? f.prefix : (d.frame?.prefix ?? "┃"),
|
|
72
|
-
|
|
73
|
-
prefixColor:
|
|
74
|
-
typeof f.prefixColor === "string" &&
|
|
75
|
-
(f.prefixColor === "agentMode" || isThemeColor(f.prefixColor))
|
|
76
|
-
? f.prefixColor
|
|
77
|
-
: d.frame?.prefixColor, // unset → text
|
|
78
57
|
};
|
|
79
58
|
}
|
|
80
59
|
|
|
@@ -84,20 +63,6 @@ function normalize(raw: unknown): Settings {
|
|
|
84
63
|
out.header = {
|
|
85
64
|
type: typeof h.type === "string" ? h.type : (d.header?.type ?? "basic"),
|
|
86
65
|
enable: bool(h.enable, d.header?.enable),
|
|
87
|
-
heading: str(h.heading, d.header?.heading ?? ""),
|
|
88
|
-
subheading: str(h.subheading, d.header?.subheading ?? ""),
|
|
89
|
-
logoColor:
|
|
90
|
-
typeof h.logoColor === "string" && isThemeColor(h.logoColor)
|
|
91
|
-
? h.logoColor
|
|
92
|
-
: (d.header?.logoColor ?? "accent"),
|
|
93
|
-
accentColor:
|
|
94
|
-
typeof h.accentColor === "string" && isThemeColor(h.accentColor)
|
|
95
|
-
? h.accentColor
|
|
96
|
-
: (d.header?.accentColor ?? "accent"),
|
|
97
|
-
logo:
|
|
98
|
-
Array.isArray(h.logo) && h.logo.every((x) => typeof x === "string")
|
|
99
|
-
? (h.logo as string[])
|
|
100
|
-
: (d.header?.logo ?? []),
|
|
101
66
|
};
|
|
102
67
|
}
|
|
103
68
|
|
|
@@ -20,9 +20,9 @@ export interface FrameIcons {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* ``frame`` — the
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* ``frame`` — the editor frame preset. Styling and layout (prefix, colors,
|
|
24
|
+
* margins, paddings) are owned by the renderer; only these behavioral
|
|
25
|
+
* toggles are configurable.
|
|
26
26
|
*/
|
|
27
27
|
export interface FrameSettings {
|
|
28
28
|
enable?: boolean;
|
|
@@ -30,24 +30,6 @@ export interface FrameSettings {
|
|
|
30
30
|
/** Below this terminal width the frame is skipped entirely (passthrough) */
|
|
31
31
|
minWidth?: number;
|
|
32
32
|
|
|
33
|
-
/** Blank rows inside the band above the content. Default 1 */
|
|
34
|
-
paddingTop?: number;
|
|
35
|
-
|
|
36
|
-
/** Blank rows inside the band below the content. Default 1 */
|
|
37
|
-
paddingBottom?: number;
|
|
38
|
-
|
|
39
|
-
/** Horizontal inner padding for the content rows. Default 2 */
|
|
40
|
-
paddingX?: number;
|
|
41
|
-
|
|
42
|
-
/** Horizontal outer margin for the content rows. Default 1 */
|
|
43
|
-
marginX?: number;
|
|
44
|
-
|
|
45
|
-
/** Blank rows OUTSIDE the band, above it. Default 0 */
|
|
46
|
-
marginTop?: number;
|
|
47
|
-
|
|
48
|
-
/** Blank rows OUTSIDE the band, below the pseudo-footer. Default 0 */
|
|
49
|
-
marginBottom?: number;
|
|
50
|
-
|
|
51
33
|
showCwd?: boolean;
|
|
52
34
|
showModel?: boolean;
|
|
53
35
|
showContext?: boolean;
|
|
@@ -56,24 +38,11 @@ export interface FrameSettings {
|
|
|
56
38
|
|
|
57
39
|
/** Show the status-animation spinner segment while streaming. Default false */
|
|
58
40
|
showSpinner?: boolean;
|
|
59
|
-
|
|
60
|
-
icons?: Partial<FrameIcons>;
|
|
61
|
-
|
|
62
|
-
/** Text before the editor content. Default "┃" */
|
|
63
|
-
prefix?: string;
|
|
64
|
-
|
|
65
|
-
/** ThemeColor for the prefix; "agentMode" follows the agent's color if (@leo-alvarenga/pi-agent-manager is installed, defaults to "text otherwise"). Default: "agentMode" */
|
|
66
|
-
prefixColor?: "agentMode" | ThemeColor;
|
|
67
41
|
}
|
|
68
42
|
|
|
69
43
|
/** `header` — the top-line header, which can show a logo or other text */
|
|
70
44
|
export interface HeaderSettings {
|
|
71
|
-
logo: string[];
|
|
72
45
|
enable: boolean;
|
|
73
|
-
heading: string;
|
|
74
|
-
subheading: string;
|
|
75
|
-
logoColor: ThemeColor;
|
|
76
|
-
accentColor: ThemeColor;
|
|
77
46
|
|
|
78
47
|
/** Header renderer by registered name. Default "basic". */
|
|
79
48
|
type?: string;
|
|
@@ -20,7 +20,6 @@ import type {
|
|
|
20
20
|
SegmentContext,
|
|
21
21
|
} from "../components/types";
|
|
22
22
|
import type { EditorFrameRenderOptions } from "../renderers/types";
|
|
23
|
-
import { isThemeColor } from "../utils";
|
|
24
23
|
|
|
25
24
|
export class BlockyEditor extends CustomEditor {
|
|
26
25
|
private pi: ExtensionAPI;
|
|
@@ -77,15 +76,9 @@ export class BlockyEditor extends CustomEditor {
|
|
|
77
76
|
render(width: number): string[] {
|
|
78
77
|
const frame = this.opts.frame;
|
|
79
78
|
|
|
80
|
-
const padX = Math.min(
|
|
81
|
-
frame.paddingX ?? 1,
|
|
82
|
-
Math.max(0, Math.floor(width / 2)),
|
|
83
|
-
);
|
|
79
|
+
const padX = Math.min(2, Math.max(0, Math.floor(width / 2)));
|
|
84
80
|
|
|
85
|
-
const marginX = Math.min(
|
|
86
|
-
frame.marginX ?? 0,
|
|
87
|
-
Math.max(0, Math.floor(width / 2)),
|
|
88
|
-
);
|
|
81
|
+
const marginX = Math.min(1, Math.max(0, Math.floor(width / 2)));
|
|
89
82
|
|
|
90
83
|
const contentWidth = width - marginX * 2;
|
|
91
84
|
const innerWidth = width - padX * 2 - marginX * 2;
|
|
@@ -107,16 +100,11 @@ export class BlockyEditor extends CustomEditor {
|
|
|
107
100
|
accentColor: this.opts.accentColor,
|
|
108
101
|
};
|
|
109
102
|
|
|
110
|
-
let prefix =
|
|
103
|
+
let prefix = "┃";
|
|
111
104
|
|
|
112
105
|
if (ext.theme?.fg) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
prefixColor = ext.agentMode?.color ?? "text";
|
|
116
|
-
} else if (frame.prefixColor && isThemeColor(frame.prefixColor)) {
|
|
117
|
-
prefixColor = frame.prefixColor;
|
|
118
|
-
}
|
|
119
|
-
|
|
106
|
+
const prefixColor: ThemeColor =
|
|
107
|
+
ext.agentMode?.color ?? this.opts.accentColor ?? "text";
|
|
120
108
|
prefix = ext.theme.fg(prefixColor, prefix);
|
|
121
109
|
}
|
|
122
110
|
|
|
@@ -145,10 +133,7 @@ export class BlockyEditor extends CustomEditor {
|
|
|
145
133
|
const ctx: SegmentContext = {
|
|
146
134
|
cfg: frame,
|
|
147
135
|
theme: ext.theme,
|
|
148
|
-
icons:
|
|
149
|
-
...DEFAULT_ICONS,
|
|
150
|
-
...frame.icons,
|
|
151
|
-
},
|
|
136
|
+
icons: DEFAULT_ICONS,
|
|
152
137
|
};
|
|
153
138
|
|
|
154
139
|
const box = composeBand(content, autocomplete, {
|
|
@@ -156,9 +141,9 @@ export class BlockyEditor extends CustomEditor {
|
|
|
156
141
|
paint,
|
|
157
142
|
prefix,
|
|
158
143
|
width: contentWidth,
|
|
159
|
-
marginX:
|
|
160
|
-
paddingTop:
|
|
161
|
-
paddingBottom:
|
|
144
|
+
marginX: 1,
|
|
145
|
+
paddingTop: 1,
|
|
146
|
+
paddingBottom: 1,
|
|
162
147
|
boxBottom: fitInfoRow(
|
|
163
148
|
segmentsFor("topLeft", d, ctx),
|
|
164
149
|
segmentsFor("topRight", d, ctx),
|
|
@@ -174,10 +159,8 @@ export class BlockyEditor extends CustomEditor {
|
|
|
174
159
|
|
|
175
160
|
// Blank rows OUTSIDE the band, above/below it.
|
|
176
161
|
const marginRow = " ".repeat(contentWidth);
|
|
177
|
-
const marginTop =
|
|
178
|
-
const marginBottom =
|
|
179
|
-
marginRow,
|
|
180
|
-
);
|
|
162
|
+
const marginTop: string[] = [];
|
|
163
|
+
const marginBottom: string[] = [];
|
|
181
164
|
|
|
182
165
|
return [...marginTop, ...box, marginRow, pseudoFooter, ...marginBottom].map(
|
|
183
166
|
(row) => " ".repeat(marginX) + row + " ".repeat(marginX),
|
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.15.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github.com/leo-alvarenga/pi-mono",
|
|
8
8
|
"publishConfig": {
|