pi-fancy-footer 0.3.2 → 0.4.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 CHANGED
@@ -23,6 +23,9 @@ pi install npm:pi-fancy-footer
23
23
 
24
24
  ## 📸 Screenshots
25
25
 
26
+ > [!NOTE]
27
+ > Some screenshots include `pi-banner`, which is now a separate extension.
28
+
26
29
  <!-- markdownlint-disable MD033 -->
27
30
 
28
31
  <img src="screenshots/light-blocks.png" alt="light theme – blocks style" width="600" /><br/>
@@ -40,6 +43,7 @@ pi install npm:pi-fancy-footer
40
43
  ## 🎮 Commands
41
44
 
42
45
  - `/fancy-footer` - open interactive footer config editor (small TUI)
46
+ - settings are grouped into General and Widgets sections
43
47
  - all widgets are listed directly (with icon prefixes)
44
48
  - select a widget and press Enter for detailed settings
45
49
  - in widget settings, adjust row/position/align/fill/min-width, visibility,
@@ -53,7 +57,6 @@ Create `~/.pi/agent/fancy-footer.json`:
53
57
  ```json
54
58
  {
55
59
  "refreshMs": 3000,
56
- "showPiBanner": true,
57
60
  "iconFamily": "unicode",
58
61
  "contextBarStyle": "blocks",
59
62
  "defaultTextColor": "dim",
@@ -80,7 +83,6 @@ Create `~/.pi/agent/fancy-footer.json`:
80
83
  Top-level settings:
81
84
 
82
85
  - `refreshMs` (number)
83
- - `showPiBanner` (boolean)
84
86
  - `iconFamily`
85
87
  (`nerd` | `emoji` | `unicode` | `ascii`)
86
88
  - `contextBarStyle`
@@ -150,7 +150,6 @@ function coerceFooterWidgetOverride(
150
150
  export function coerceFooterConfig(value: unknown): FooterConfigSnapshot {
151
151
  const out: FooterConfigSnapshot = {
152
152
  refreshMs: DEFAULT_FOOTER_CONFIG.refreshMs,
153
- showPiBanner: DEFAULT_FOOTER_CONFIG.showPiBanner,
154
153
  iconFamily: DEFAULT_FOOTER_CONFIG.iconFamily,
155
154
  contextBarStyle: DEFAULT_FOOTER_CONFIG.contextBarStyle,
156
155
  defaultTextColor: DEFAULT_FOOTER_CONFIG.defaultTextColor,
@@ -172,10 +171,6 @@ export function coerceFooterConfig(value: unknown): FooterConfigSnapshot {
172
171
  out.refreshMs = Math.max(MIN_FOOTER_REFRESH_MS, refreshMs);
173
172
  }
174
173
 
175
- if (typeof input.showPiBanner === "boolean") {
176
- out.showPiBanner = input.showPiBanner;
177
- }
178
-
179
174
  if (isFooterIconFamily(input.iconFamily)) {
180
175
  out.iconFamily = input.iconFamily;
181
176
  }
@@ -247,7 +242,6 @@ export function cloneFooterConfig(
247
242
 
248
243
  return {
249
244
  refreshMs: config.refreshMs,
250
- showPiBanner: config.showPiBanner,
251
245
  iconFamily: config.iconFamily,
252
246
  contextBarStyle: config.contextBarStyle,
253
247
  defaultTextColor: config.defaultTextColor,
@@ -285,7 +279,6 @@ function toFooterConfigObject(
285
279
  MIN_FOOTER_REFRESH_MS,
286
280
  MAX_FOOTER_REFRESH_MS,
287
281
  ),
288
- showPiBanner: config.showPiBanner,
289
282
  iconFamily: config.iconFamily,
290
283
  contextBarStyle: config.contextBarStyle,
291
284
  defaultTextColor: config.defaultTextColor,
@@ -740,20 +733,6 @@ export function coerceContextBarStyleValue(
740
733
  return undefined;
741
734
  }
742
735
 
743
- export function bannerFooterSettingsItems(
744
- draft: FooterConfigSnapshot,
745
- ): SettingItem[] {
746
- return [
747
- {
748
- id: "showPiBanner",
749
- label: "show π banner",
750
- currentValue: draft.showPiBanner ? "on" : "off",
751
- values: ["on", "off"],
752
- description: "Show the rainbow π banner in the header.",
753
- },
754
- ];
755
- }
756
-
757
736
  export function genericFooterSettingsItems(
758
737
  draft: FooterConfigSnapshot,
759
738
  ): SettingItem[] {
@@ -833,7 +812,6 @@ export function rootFooterSettingsItems(
833
812
  applyDraft: () => void,
834
813
  ): SettingItem[] {
835
814
  return [
836
- ...bannerFooterSettingsItems(draft),
837
815
  ...genericFooterSettingsItems(draft),
838
816
  ...widgetFooterSettingsItems(draft, theme, applyDraft),
839
817
  ];
@@ -351,7 +351,6 @@ export interface FooterWidgetConfigOverride {
351
351
 
352
352
  export interface FooterConfigSnapshot {
353
353
  refreshMs: number;
354
- showPiBanner: boolean;
355
354
  iconFamily: FooterIconFamily;
356
355
  contextBarStyle: ContextBarStyleId;
357
356
  defaultTextColor: FooterWidgetColor;
@@ -361,7 +360,6 @@ export interface FooterConfigSnapshot {
361
360
 
362
361
  export const DEFAULT_FOOTER_CONFIG: FooterConfigSnapshot = {
363
362
  refreshMs: GIT_REFRESH_MS,
364
- showPiBanner: true,
365
363
  iconFamily: "nerd",
366
364
  contextBarStyle: DEFAULT_CONTEXT_BAR_STYLE,
367
365
  defaultTextColor: "dim",
@@ -24,7 +24,6 @@ import {
24
24
  type SessionUsageMetrics,
25
25
  } from "./fancy-footer/shared.ts";
26
26
  import {
27
- bannerFooterSettingsItems,
28
27
  cloneFooterConfig,
29
28
  coerceCompactionSettings,
30
29
  coerceContextBarStyleValue,
@@ -47,12 +46,10 @@ import {
47
46
  collectSessionUsageMetrics,
48
47
  renderFooterLines,
49
48
  } from "./fancy-footer/render.ts";
50
- import { renderBannerLines } from "./fancy-footer/banner.ts";
51
49
 
52
50
  interface ActiveFooterControls {
53
51
  requestRender: () => void;
54
52
  reschedule: () => void;
55
- applyHeader: () => void;
56
53
  }
57
54
 
58
55
  export default function (pi: ExtensionAPI) {
@@ -61,7 +58,6 @@ export default function (pi: ExtensionAPI) {
61
58
  };
62
59
  let footerConfig: FooterConfigSnapshot = {
63
60
  refreshMs: DEFAULT_FOOTER_CONFIG.refreshMs,
64
- showPiBanner: DEFAULT_FOOTER_CONFIG.showPiBanner,
65
61
  iconFamily: DEFAULT_FOOTER_CONFIG.iconFamily,
66
62
  contextBarStyle: DEFAULT_FOOTER_CONFIG.contextBarStyle,
67
63
  defaultTextColor: DEFAULT_FOOTER_CONFIG.defaultTextColor,
@@ -77,22 +73,6 @@ export default function (pi: ExtensionAPI) {
77
73
  compactionSettings = loadCompactionSettings(ctx.cwd);
78
74
  footerConfig = loadFooterConfig();
79
75
 
80
- const applyHeader = () => {
81
- if (!footerConfig.showPiBanner) {
82
- ctx.ui.setHeader(undefined);
83
- return;
84
- }
85
-
86
- ctx.ui.setHeader((_tui, theme) => ({
87
- render(width: number): string[] {
88
- return renderBannerLines(theme, width);
89
- },
90
- invalidate() {},
91
- }));
92
- };
93
-
94
- applyHeader();
95
-
96
76
  ctx.ui.setFooter((tui, theme, footerData) => {
97
77
  let currentGit = { ...EMPTY_GIT_INFO };
98
78
  let usageMetrics: SessionUsageMetrics = collectSessionUsageMetrics(ctx);
@@ -214,7 +194,6 @@ export default function (pi: ExtensionAPI) {
214
194
  activeFooterControls = {
215
195
  requestRender,
216
196
  reschedule: scheduleRefresh,
217
- applyHeader,
218
197
  };
219
198
 
220
199
  void refreshGit();
@@ -263,7 +242,6 @@ export default function (pi: ExtensionAPI) {
263
242
  try {
264
243
  writeFooterConfigSnapshot(draft);
265
244
  footerConfig = loadFooterConfig();
266
- activeFooterControls?.applyHeader();
267
245
  activeFooterControls?.reschedule();
268
246
  activeFooterControls?.requestRender();
269
247
  } catch (error) {
@@ -272,20 +250,16 @@ export default function (pi: ExtensionAPI) {
272
250
  }
273
251
  };
274
252
 
275
- type ConfigSection = "banner" | "generic" | "widgets";
253
+ type ConfigSection = "generic" | "widgets";
276
254
 
277
- let activeSection: ConfigSection = "banner";
255
+ let activeSection: ConfigSection = "generic";
278
256
  const selection: Record<ConfigSection, number> = {
279
- banner: 0,
280
257
  generic: 0,
281
258
  widgets: 0,
282
259
  };
283
260
  let submenu: Component | undefined;
284
261
 
285
262
  const getSectionItems = (section: ConfigSection): SettingItem[] => {
286
- if (section === "banner") {
287
- return bannerFooterSettingsItems(draft);
288
- }
289
263
  if (section === "generic") {
290
264
  return genericFooterSettingsItems(draft);
291
265
  }
@@ -318,11 +292,7 @@ export default function (pi: ExtensionAPI) {
318
292
  return items[selection[activeSection]];
319
293
  };
320
294
 
321
- const orderedSections: ConfigSection[] = [
322
- "banner",
323
- "generic",
324
- "widgets",
325
- ];
295
+ const orderedSections: ConfigSection[] = ["generic", "widgets"];
326
296
 
327
297
  const getFlatSelections = () => {
328
298
  return orderedSections.flatMap((section) => {
@@ -338,11 +308,6 @@ export default function (pi: ExtensionAPI) {
338
308
  draft.refreshMs = refreshMs;
339
309
  applyDraft();
340
310
  }
341
- } else if (id === "showPiBanner") {
342
- if (newValue === "on" || newValue === "off") {
343
- draft.showPiBanner = newValue === "on";
344
- applyDraft();
345
- }
346
311
  } else if (id === "iconFamily") {
347
312
  const iconFamily = coerceIconFamily(newValue);
348
313
  if (iconFamily) {
@@ -502,8 +467,6 @@ export default function (pi: ExtensionAPI) {
502
467
  ),
503
468
  truncateToWidth(theme.fg("dim", configPath), width),
504
469
  "",
505
- ...renderSection(width, "Banner", "banner"),
506
- "",
507
470
  ...renderSection(width, "General", "generic"),
508
471
  "",
509
472
  ...renderSection(width, "Widgets", "widgets"),
@@ -578,8 +541,4 @@ export default function (pi: ExtensionAPI) {
578
541
  pi.on("session_start", async (_event, ctx) => {
579
542
  installFooter(ctx);
580
543
  });
581
-
582
- pi.on("session_switch", async (_event, ctx) => {
583
- installFooter(ctx);
584
- });
585
544
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-fancy-footer",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "A fancy footer extension for pi",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,58 +0,0 @@
1
- import type { Theme } from "@mariozechner/pi-coding-agent";
2
-
3
- const PI_ART = [
4
- " 3.141592653589793238462643383279",
5
- " 5028841971693993751058209749445923",
6
- " 07816406286208998628034825342117067",
7
- " 9821 48086 5132",
8
- " 823 06647 09384",
9
- "46 09550 58223",
10
- " 1725 3594",
11
- " 08128 48111",
12
- " 74502 84102",
13
- " 70193 85211 05",
14
- " 5596446 22948954930381",
15
- " 9644288 10975665933",
16
- ];
17
-
18
- function hslToRgb(h: number, s: number, l: number): [number, number, number] {
19
- h = ((h % 360) + 360) % 360;
20
- const c = (1 - Math.abs(2 * l - 1)) * s;
21
- const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
22
- const m = l - c / 2;
23
- let r = 0;
24
- let g = 0;
25
- let b = 0;
26
- if (h < 60) [r, g, b] = [c, x, 0];
27
- else if (h < 120) [r, g, b] = [x, c, 0];
28
- else if (h < 180) [r, g, b] = [0, c, x];
29
- else if (h < 240) [r, g, b] = [0, x, c];
30
- else if (h < 300) [r, g, b] = [x, 0, c];
31
- else [r, g, b] = [c, 0, x];
32
- return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255)];
33
- }
34
-
35
- const RESET = "\x1b[0m";
36
-
37
- function rainbowLine(line: string, rowOffset: number): string {
38
- const totalChars = 50;
39
- let result = "";
40
- for (let i = 0; i < line.length; i++) {
41
- const ch = line[i]!;
42
- if (ch === " ") {
43
- result += " ";
44
- continue;
45
- }
46
- const hue = ((i + rowOffset * 3) / totalChars) * 360;
47
- const [r, g, b] = hslToRgb(hue, 0.85, 0.65);
48
- result += `\x1b[38;2;${r};${g};${b}m${ch}`;
49
- }
50
- return result + RESET;
51
- }
52
-
53
- export function renderBannerLines(_theme: Theme, width: number): string[] {
54
- const maxLen = Math.max(...PI_ART.map((line) => line.length));
55
- const pad = Math.max(0, Math.floor((width - maxLen) / 2));
56
- const prefix = " ".repeat(pad);
57
- return ["", ...PI_ART.map((line, row) => prefix + rainbowLine(line, row)), ""];
58
- }