@juanibiapina/pi-powerbar 0.6.1 → 0.7.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 CHANGED
@@ -40,6 +40,19 @@ pi.events.emit("powerbar:update", {
40
40
  });
41
41
  ```
42
42
 
43
+ Segments can include a progress bar with an optional block count hint:
44
+
45
+ ```typescript
46
+ pi.events.emit("powerbar:update", {
47
+ id: "context-usage",
48
+ text: "",
49
+ suffix: "30%",
50
+ bar: 30, // progress value 0–100
51
+ barSegments: 10, // optional: number of discrete blocks in blocks mode
52
+ color: "muted",
53
+ });
54
+ ```
55
+
43
56
  To remove a segment:
44
57
 
45
58
  ```typescript
@@ -71,6 +84,7 @@ Settings are managed through [`pi-extension-settings`](https://github.com/juanib
71
84
  | **Right segments** | Segments shown on the right side (ordered multi-select menu) | `provider,model,sub-hourly,sub-weekly` |
72
85
  | **Separator** | String drawn between segments on the same side | ` │ ` |
73
86
  | **Placement** | Where the powerbar appears (`belowEditor` or `aboveEditor`) | `belowEditor` |
87
+ | **Bar style** | Visual style of progress bars (`continuous` or `blocks`) | `blocks` |
74
88
  | **Bar width** | Width of progress bars in characters (4–24) | `10` |
75
89
 
76
90
  The left and right segment settings open an interactive menu where you can toggle segments on/off and reorder them with Shift+↑/↓. All segments registered via `powerbar:register-segment` appear as options. Segments not listed in either side are ignored.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/powerbar/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAA6B,MAAM,+BAA+B,CAAC;AAmB7F,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAwF9D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/powerbar/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAA6B,MAAM,+BAA+B,CAAC;AAoB7F,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAyF9D"}
@@ -51,6 +51,7 @@ export default function createExtension(pi) {
51
51
  icon: payload.icon,
52
52
  color: payload.color,
53
53
  bar: payload.bar,
54
+ barSegments: payload.barSegments,
54
55
  });
55
56
  }
56
57
  refresh();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/powerbar/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Powerbar Core Extension\n *\n * Listens for \"powerbar:update\" events from producer extensions,\n * maintains a segment store, and renders a powerline-style widget.\n */\n\nimport type { OrderedListOption } from \"@juanibiapina/pi-extension-settings\";\nimport type { ExtensionAPI, ExtensionUIContext, Theme } from \"@mariozechner/pi-coding-agent\";\nimport type { Component, TUI } from \"@mariozechner/pi-tui\";\nimport { renderBar, type Segment } from \"./render.js\";\nimport { loadSettings, type PowerbarSettings, registerSettings } from \"./settings.js\";\n\ninterface PowerbarUpdatePayload {\n\tid: string;\n\ttext?: string;\n\tsuffix?: string;\n\ticon?: string;\n\tcolor?: string;\n\tbar?: number;\n}\n\ninterface SegmentRegistration {\n\tid: string;\n\tlabel: string;\n}\n\nexport default function createExtension(pi: ExtensionAPI): void {\n\tconst segments: Map<string, Segment> = new Map();\n\tconst segmentCatalog: Map<string, OrderedListOption> = new Map();\n\tlet settings: PowerbarSettings;\n\tlet currentCtx: { ui: { setWidget: (...args: any[]) => void }; hasUI: boolean } | undefined;\n\n\t// Register settings with empty options initially (no segments known yet)\n\tregisterSettings(pi, []);\n\n\t// Listen for segment registrations from producer extensions\n\tpi.events.on(\"powerbar:register-segment\", (data: unknown) => {\n\t\tconst { id, label } = data as SegmentRegistration;\n\t\tsegmentCatalog.set(id, { id, label });\n\t\t// Re-register settings with updated segment options\n\t\tregisterSettings(pi, Array.from(segmentCatalog.values()));\n\t});\n\n\tfunction refresh(): void {\n\t\tif (!currentCtx?.hasUI) return;\n\n\t\tcurrentCtx.ui.setWidget(\n\t\t\t\"powerbar\",\n\t\t\t(_tui: TUI, theme: Theme): Component & { dispose?(): void } => {\n\t\t\t\treturn {\n\t\t\t\t\trender(width: number): string[] {\n\t\t\t\t\t\tconst line = renderBar(segments, settings, theme, width);\n\t\t\t\t\t\treturn [line];\n\t\t\t\t\t},\n\t\t\t\t\tinvalidate(): void {\n\t\t\t\t\t\t// No cached state to clear\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t},\n\t\t\t{ placement: settings.placement },\n\t\t);\n\t}\n\n\t// Listen for segment updates from any extension\n\tpi.events.on(\"powerbar:update\", (data: unknown) => {\n\t\tconst payload = data as PowerbarUpdatePayload;\n\t\tif (!payload?.id) return;\n\n\t\tif (!payload.text && payload.bar === undefined) {\n\t\t\tsegments.delete(payload.id);\n\t\t} else {\n\t\t\tsegments.set(payload.id, {\n\t\t\t\tid: payload.id,\n\t\t\t\ttext: payload.text ?? \"\",\n\t\t\t\tsuffix: payload.suffix,\n\t\t\t\ticon: payload.icon,\n\t\t\t\tcolor: payload.color,\n\t\t\t\tbar: payload.bar,\n\t\t\t});\n\t\t}\n\n\t\trefresh();\n\t});\n\n\tfunction hideFooter(ctx: { ui: ExtensionUIContext; hasUI: boolean }): void {\n\t\tif (!ctx.hasUI) return;\n\t\tctx.ui.setFooter((_tui, _theme, _footerData) => ({\n\t\t\trender(): string[] {\n\t\t\t\treturn [];\n\t\t\t},\n\t\t\tinvalidate(): void {},\n\t\t}));\n\t}\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tsettings = loadSettings();\n\t\tcurrentCtx = ctx;\n\t\thideFooter(ctx);\n\t\trefresh();\n\t});\n\n\tpi.on(\"session_switch\", async (_event, ctx) => {\n\t\tsettings = loadSettings();\n\t\tcurrentCtx = ctx;\n\t\thideFooter(ctx);\n\t\trefresh();\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tif (ctx.hasUI) {\n\t\t\tctx.ui.setWidget(\"powerbar\", undefined);\n\t\t}\n\t\tcurrentCtx = undefined;\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/powerbar/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Powerbar Core Extension\n *\n * Listens for \"powerbar:update\" events from producer extensions,\n * maintains a segment store, and renders a powerline-style widget.\n */\n\nimport type { OrderedListOption } from \"@juanibiapina/pi-extension-settings\";\nimport type { ExtensionAPI, ExtensionUIContext, Theme } from \"@mariozechner/pi-coding-agent\";\nimport type { Component, TUI } from \"@mariozechner/pi-tui\";\nimport { renderBar, type Segment } from \"./render.js\";\nimport { loadSettings, type PowerbarSettings, registerSettings } from \"./settings.js\";\n\ninterface PowerbarUpdatePayload {\n\tid: string;\n\ttext?: string;\n\tsuffix?: string;\n\ticon?: string;\n\tcolor?: string;\n\tbar?: number;\n\tbarSegments?: number;\n}\n\ninterface SegmentRegistration {\n\tid: string;\n\tlabel: string;\n}\n\nexport default function createExtension(pi: ExtensionAPI): void {\n\tconst segments: Map<string, Segment> = new Map();\n\tconst segmentCatalog: Map<string, OrderedListOption> = new Map();\n\tlet settings: PowerbarSettings;\n\tlet currentCtx: { ui: { setWidget: (...args: any[]) => void }; hasUI: boolean } | undefined;\n\n\t// Register settings with empty options initially (no segments known yet)\n\tregisterSettings(pi, []);\n\n\t// Listen for segment registrations from producer extensions\n\tpi.events.on(\"powerbar:register-segment\", (data: unknown) => {\n\t\tconst { id, label } = data as SegmentRegistration;\n\t\tsegmentCatalog.set(id, { id, label });\n\t\t// Re-register settings with updated segment options\n\t\tregisterSettings(pi, Array.from(segmentCatalog.values()));\n\t});\n\n\tfunction refresh(): void {\n\t\tif (!currentCtx?.hasUI) return;\n\n\t\tcurrentCtx.ui.setWidget(\n\t\t\t\"powerbar\",\n\t\t\t(_tui: TUI, theme: Theme): Component & { dispose?(): void } => {\n\t\t\t\treturn {\n\t\t\t\t\trender(width: number): string[] {\n\t\t\t\t\t\tconst line = renderBar(segments, settings, theme, width);\n\t\t\t\t\t\treturn [line];\n\t\t\t\t\t},\n\t\t\t\t\tinvalidate(): void {\n\t\t\t\t\t\t// No cached state to clear\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t},\n\t\t\t{ placement: settings.placement },\n\t\t);\n\t}\n\n\t// Listen for segment updates from any extension\n\tpi.events.on(\"powerbar:update\", (data: unknown) => {\n\t\tconst payload = data as PowerbarUpdatePayload;\n\t\tif (!payload?.id) return;\n\n\t\tif (!payload.text && payload.bar === undefined) {\n\t\t\tsegments.delete(payload.id);\n\t\t} else {\n\t\t\tsegments.set(payload.id, {\n\t\t\t\tid: payload.id,\n\t\t\t\ttext: payload.text ?? \"\",\n\t\t\t\tsuffix: payload.suffix,\n\t\t\t\ticon: payload.icon,\n\t\t\t\tcolor: payload.color,\n\t\t\t\tbar: payload.bar,\n\t\t\t\tbarSegments: payload.barSegments,\n\t\t\t});\n\t\t}\n\n\t\trefresh();\n\t});\n\n\tfunction hideFooter(ctx: { ui: ExtensionUIContext; hasUI: boolean }): void {\n\t\tif (!ctx.hasUI) return;\n\t\tctx.ui.setFooter((_tui, _theme, _footerData) => ({\n\t\t\trender(): string[] {\n\t\t\t\treturn [];\n\t\t\t},\n\t\t\tinvalidate(): void {},\n\t\t}));\n\t}\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tsettings = loadSettings();\n\t\tcurrentCtx = ctx;\n\t\thideFooter(ctx);\n\t\trefresh();\n\t});\n\n\tpi.on(\"session_switch\", async (_event, ctx) => {\n\t\tsettings = loadSettings();\n\t\tcurrentCtx = ctx;\n\t\thideFooter(ctx);\n\t\trefresh();\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tif (ctx.hasUI) {\n\t\t\tctx.ui.setWidget(\"powerbar\", undefined);\n\t\t}\n\t\tcurrentCtx = undefined;\n\t});\n}\n"]}
@@ -2,8 +2,9 @@
2
2
  * Rendering logic for the powerbar.
3
3
  *
4
4
  * Builds a single line with left-aligned and right-aligned segments,
5
- * joined by themed separators. Supports inline progress bars using
6
- * block characters (█ + partials ▏▎▍▌▋▊▉).
5
+ * joined by themed separators. Supports two progress bar styles:
6
+ * continuous (█ + partial-width glyphs ▏▎▍▌▋▊▉) and blocks
7
+ * (discrete partial-height glyphs ▁▂▃▄▅▆▇█ with dim background).
7
8
  */
8
9
  import type { Theme } from "@mariozechner/pi-coding-agent";
9
10
  import type { PowerbarSettings } from "./settings.js";
@@ -17,6 +18,8 @@ export interface Segment {
17
18
  color?: string;
18
19
  /** If set, renders a progress bar. Value is 0–100. */
19
20
  bar?: number;
21
+ /** Hint for how many discrete blocks to use in blocks mode. Falls back to barWidth setting. */
22
+ barSegments?: number;
20
23
  }
21
24
  export declare function renderBar(segments: Map<string, Segment>, settings: PowerbarSettings, theme: Theme, width: number): string;
22
25
  //# sourceMappingURL=render.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/powerbar/render.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAc,MAAM,+BAA+B,CAAC;AAEvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,WAAW,OAAO;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAgHD,wBAAgB,SAAS,CACxB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,GACX,MAAM,CAoCR"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/powerbar/render.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAc,MAAM,+BAA+B,CAAC;AAEvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,WAAW,OAAO;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+FAA+F;IAC/F,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAqJD,wBAAgB,SAAS,CACxB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,GACX,MAAM,CAoCR"}
@@ -2,15 +2,15 @@
2
2
  * Rendering logic for the powerbar.
3
3
  *
4
4
  * Builds a single line with left-aligned and right-aligned segments,
5
- * joined by themed separators. Supports inline progress bars using
6
- * block characters (█ + partials ▏▎▍▌▋▊▉).
5
+ * joined by themed separators. Supports two progress bar styles:
6
+ * continuous (█ + partial-width glyphs ▏▎▍▌▋▊▉) and blocks
7
+ * (discrete partial-height glyphs ▁▂▃▄▅▆▇█ with dim background).
7
8
  */
8
9
  import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
9
10
  /**
10
- * Render a progress bar using full-height block characters.
11
+ * Render a continuous progress bar using full-width block characters.
11
12
  *
12
- * █ for filled blocks, ▏▎▍▌▋▊▉ for partial, space for empty.
13
- * Matches the pi-sub visual style.
13
+ * █ for filled columns, ▏▎▍▌▋▊▉ for the partial column, space for empty.
14
14
  */
15
15
  function renderProgressBar(percent, width, theme, color) {
16
16
  const clamped = Math.max(0, Math.min(100, percent));
@@ -31,6 +31,34 @@ function renderProgressBar(percent, width, theme, color) {
31
31
  const emptyStr = " ".repeat(emptyCount);
32
32
  return theme.fg(themeColor, filledStr + partial) + emptyStr;
33
33
  }
34
+ /** Convert a foreground ANSI escape to background by replacing SGR 38 with 48. */
35
+ function fgToBgAnsi(fgAnsi) {
36
+ return fgAnsi.replace("\x1b[38;", "\x1b[48;");
37
+ }
38
+ /**
39
+ * Render a bar of discrete block characters with a dim background track.
40
+ *
41
+ * Splits the 0–100 percent range evenly across `segments` blocks and
42
+ * computes a fill level (0–8) per block. The dim theme color provides
43
+ * the background "track"; partial block glyphs fill from the bottom
44
+ * in the segment color.
45
+ */
46
+ function renderBlocksBar(percent, segments, theme, color) {
47
+ const glyphs = [" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
48
+ const dimBg = fgToBgAnsi(theme.getFgAnsi("dim"));
49
+ const fgColor = theme.getFgAnsi((color || "muted"));
50
+ const reset = "\x1b[39m\x1b[49m";
51
+ const clamped = Math.max(0, Math.min(100, percent));
52
+ const filledFloat = (clamped / 100) * segments;
53
+ const result = [];
54
+ for (let i = 0; i < segments; i++) {
55
+ const blockFill = Math.max(0, Math.min(1, filledFloat - i));
56
+ const level = Math.round(blockFill * 8);
57
+ const glyph = glyphs[level];
58
+ result.push(level > 0 ? `${dimBg}${fgColor}${glyph}${reset}` : `${dimBg}${glyph}${reset}`);
59
+ }
60
+ return result.join(" ");
61
+ }
34
62
  /**
35
63
  * Render a single segment.
36
64
  *
@@ -46,7 +74,14 @@ function renderSegmentText(segment, settings, theme) {
46
74
  parts.push(theme.fg(themeColor, segment.text));
47
75
  }
48
76
  if (segment.bar !== undefined) {
49
- parts.push(renderProgressBar(segment.bar, settings.barWidth, theme, segment.color || "muted"));
77
+ const color = segment.color || "muted";
78
+ if (settings.barStyle === "blocks") {
79
+ const blockCount = segment.barSegments ?? settings.barWidth;
80
+ parts.push(renderBlocksBar(segment.bar, blockCount, theme, color));
81
+ }
82
+ else {
83
+ parts.push(renderProgressBar(segment.bar, settings.barWidth, theme, color));
84
+ }
50
85
  }
51
86
  if (segment.suffix) {
52
87
  parts.push(theme.fg(themeColor, segment.suffix));
@@ -1 +1 @@
1
- {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/powerbar/render.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Rendering logic for the powerbar.\n *\n * Builds a single line with left-aligned and right-aligned segments,\n * joined by themed separators. Supports inline progress bars using\n * block characters (█ + partials ▏▎▍▌▋▊▉).\n */\n\nimport type { Theme, ThemeColor } from \"@mariozechner/pi-coding-agent\";\nimport { truncateToWidth, visibleWidth } from \"@mariozechner/pi-tui\";\nimport type { PowerbarSettings } from \"./settings.js\";\n\nexport interface Segment {\n\tid: string;\n\t/** Primary text, rendered before the bar. */\n\ttext: string;\n\t/** Text rendered after the bar (e.g., \"59%\"). */\n\tsuffix?: string;\n\ticon?: string;\n\tcolor?: string;\n\t/** If set, renders a progress bar. Value is 0–100. */\n\tbar?: number;\n}\n\n/**\n * Render a progress bar using full-height block characters.\n *\n * █ for filled blocks, ▏▎▍▌▋▊▉ for partial, space for empty.\n * Matches the pi-sub visual style.\n */\nfunction renderProgressBar(percent: number, width: number, theme: Theme, color: string): string {\n\tconst clamped = Math.max(0, Math.min(100, percent));\n\tconst filledFloat = (clamped / 100) * width;\n\tconst filledFull = Math.floor(filledFloat);\n\tconst remainder = filledFloat - filledFull;\n\n\t// Partial block levels: ▏(1/8) ▎(2/8) ▍(3/8) ▌(4/8) ▋(5/8) ▊(6/8) ▉(7/8)\n\tconst levels = [\"▏\", \"▎\", \"▍\", \"▌\", \"▋\", \"▊\", \"▉\"];\n\n\tconst themeColor = color as ThemeColor;\n\tconst filledStr = \"█\".repeat(filledFull);\n\n\tlet partial = \"\";\n\tlet emptyCount = width - filledFull;\n\n\tif (remainder >= 0.0625 && filledFull < width) {\n\t\tconst levelIndex = Math.max(0, Math.min(levels.length - 1, Math.round(remainder * 8) - 1));\n\t\tpartial = levels[levelIndex];\n\t\temptyCount = Math.max(0, emptyCount - 1);\n\t}\n\n\tconst emptyStr = \" \".repeat(emptyCount);\n\n\treturn theme.fg(themeColor, filledStr + partial) + emptyStr;\n}\n\n/**\n * Render a single segment.\n *\n * Layout: [icon] [text] [bar] [suffix]\n */\nfunction renderSegmentText(segment: Segment, settings: PowerbarSettings, theme: Theme): string {\n\tconst parts: string[] = [];\n\tconst themeColor = (segment.color || \"muted\") as ThemeColor;\n\n\tif (segment.icon) {\n\t\tparts.push(theme.fg(themeColor, segment.icon));\n\t}\n\n\tif (segment.text) {\n\t\tparts.push(theme.fg(themeColor, segment.text));\n\t}\n\n\tif (segment.bar !== undefined) {\n\t\tparts.push(renderProgressBar(segment.bar, settings.barWidth, theme, segment.color || \"muted\"));\n\t}\n\n\tif (segment.suffix) {\n\t\tparts.push(theme.fg(themeColor, segment.suffix));\n\t}\n\n\treturn parts.join(\" \");\n}\n\ninterface RenderedSegment {\n\ttext: string;\n\twidth: number;\n}\n\nfunction renderSideSegments(\n\tids: string[],\n\tsegments: Map<string, Segment>,\n\tsettings: PowerbarSettings,\n\ttheme: Theme,\n): RenderedSegment[] {\n\tconst rendered: RenderedSegment[] = [];\n\tfor (const id of ids) {\n\t\tconst seg = segments.get(id);\n\t\tif (!seg || (!seg.text && !seg.suffix && seg.bar === undefined)) continue;\n\t\tconst text = renderSegmentText(seg, settings, theme);\n\t\trendered.push({ text, width: visibleWidth(text) });\n\t}\n\treturn rendered;\n}\n\nfunction joinSegments(segments: RenderedSegment[], separator: string, separatorWidth: number): RenderedSegment {\n\tif (segments.length === 0) return { text: \"\", width: 0 };\n\tconst text = segments.map((s) => s.text).join(separator);\n\tconst width = segments.reduce((sum, s) => sum + s.width, 0) + separatorWidth * (segments.length - 1);\n\treturn { text, width };\n}\n\n/**\n * Truncate the widest segment to reclaim overflow space.\n * Mutates the array in place and returns the new total width.\n */\nfunction shrinkWidest(segments: RenderedSegment[], overflow: number): void {\n\tif (segments.length === 0) return;\n\n\tlet widestIdx = 0;\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tif (segments[i].width > segments[widestIdx].width) {\n\t\t\twidestIdx = i;\n\t\t}\n\t}\n\n\tconst seg = segments[widestIdx];\n\tconst targetWidth = Math.max(1, seg.width - overflow);\n\tsegments[widestIdx] = {\n\t\ttext: truncateToWidth(seg.text, targetWidth, \"…\"),\n\t\twidth: targetWidth,\n\t};\n}\n\nexport function renderBar(\n\tsegments: Map<string, Segment>,\n\tsettings: PowerbarSettings,\n\ttheme: Theme,\n\twidth: number,\n): string {\n\tconst separator = theme.fg(\"dim\", settings.separator);\n\tconst separatorWidth = visibleWidth(separator);\n\n\tconst leftSegs = renderSideSegments(settings.left, segments, settings, theme);\n\tconst rightSegs = renderSideSegments(settings.right, segments, settings, theme);\n\tconst allSegs = [...leftSegs, ...rightSegs];\n\n\t// Calculate total content width (segments + separators within each side + 1 for minimum padding)\n\tconst leftSepCount = Math.max(0, leftSegs.length - 1);\n\tconst rightSepCount = Math.max(0, rightSegs.length - 1);\n\tconst totalSepWidth = (leftSepCount + rightSepCount) * separatorWidth;\n\tconst totalSegWidth = allSegs.reduce((sum, s) => sum + s.width, 0);\n\tconst minPadding = 1;\n\tconst totalNeeded = totalSegWidth + totalSepWidth + minPadding;\n\n\t// Shrink the widest segment(s) until it fits\n\tif (totalNeeded > width) {\n\t\tlet overflow = totalNeeded - width;\n\t\tconst maxPasses = allSegs.length;\n\t\tfor (let i = 0; i < maxPasses && overflow > 0; i++) {\n\t\t\tshrinkWidest(allSegs, overflow);\n\t\t\tconst newSegWidth = allSegs.reduce((sum, s) => sum + s.width, 0);\n\t\t\toverflow = newSegWidth + totalSepWidth + minPadding - width;\n\t\t}\n\t}\n\n\t// Rebuild left/right from the (possibly truncated) segments\n\tconst left = joinSegments(allSegs.slice(0, leftSegs.length), separator, separatorWidth);\n\tconst right = joinSegments(allSegs.slice(leftSegs.length), separator, separatorWidth);\n\n\tconst padding = Math.max(minPadding, width - left.width - right.width);\n\tconst line = `${left.text}${\" \".repeat(padding)}${right.text}`;\n\n\t// Safety net\n\treturn truncateToWidth(line, width, \"…\");\n}\n"]}
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/powerbar/render.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Rendering logic for the powerbar.\n *\n * Builds a single line with left-aligned and right-aligned segments,\n * joined by themed separators. Supports two progress bar styles:\n * continuous (█ + partial-width glyphs ▏▎▍▌▋▊▉) and blocks\n * (discrete partial-height glyphs ▁▂▃▄▅▆▇█ with dim background).\n */\n\nimport type { Theme, ThemeColor } from \"@mariozechner/pi-coding-agent\";\nimport { truncateToWidth, visibleWidth } from \"@mariozechner/pi-tui\";\nimport type { PowerbarSettings } from \"./settings.js\";\n\nexport interface Segment {\n\tid: string;\n\t/** Primary text, rendered before the bar. */\n\ttext: string;\n\t/** Text rendered after the bar (e.g., \"59%\"). */\n\tsuffix?: string;\n\ticon?: string;\n\tcolor?: string;\n\t/** If set, renders a progress bar. Value is 0–100. */\n\tbar?: number;\n\t/** Hint for how many discrete blocks to use in blocks mode. Falls back to barWidth setting. */\n\tbarSegments?: number;\n}\n\n/**\n * Render a continuous progress bar using full-width block characters.\n *\n * █ for filled columns, ▏▎▍▌▋▊▉ for the partial column, space for empty.\n */\nfunction renderProgressBar(percent: number, width: number, theme: Theme, color: string): string {\n\tconst clamped = Math.max(0, Math.min(100, percent));\n\tconst filledFloat = (clamped / 100) * width;\n\tconst filledFull = Math.floor(filledFloat);\n\tconst remainder = filledFloat - filledFull;\n\n\t// Partial block levels: ▏(1/8) ▎(2/8) ▍(3/8) ▌(4/8) ▋(5/8) ▊(6/8) ▉(7/8)\n\tconst levels = [\"▏\", \"▎\", \"▍\", \"▌\", \"▋\", \"▊\", \"▉\"];\n\n\tconst themeColor = color as ThemeColor;\n\tconst filledStr = \"█\".repeat(filledFull);\n\n\tlet partial = \"\";\n\tlet emptyCount = width - filledFull;\n\n\tif (remainder >= 0.0625 && filledFull < width) {\n\t\tconst levelIndex = Math.max(0, Math.min(levels.length - 1, Math.round(remainder * 8) - 1));\n\t\tpartial = levels[levelIndex];\n\t\temptyCount = Math.max(0, emptyCount - 1);\n\t}\n\n\tconst emptyStr = \" \".repeat(emptyCount);\n\n\treturn theme.fg(themeColor, filledStr + partial) + emptyStr;\n}\n\n/** Convert a foreground ANSI escape to background by replacing SGR 38 with 48. */\nfunction fgToBgAnsi(fgAnsi: string): string {\n\treturn fgAnsi.replace(\"\\x1b[38;\", \"\\x1b[48;\");\n}\n\n/**\n * Render a bar of discrete block characters with a dim background track.\n *\n * Splits the 0–100 percent range evenly across `segments` blocks and\n * computes a fill level (0–8) per block. The dim theme color provides\n * the background \"track\"; partial block glyphs fill from the bottom\n * in the segment color.\n */\nfunction renderBlocksBar(percent: number, segments: number, theme: Theme, color: string): string {\n\tconst glyphs = [\" \", \"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\"];\n\tconst dimBg = fgToBgAnsi(theme.getFgAnsi(\"dim\"));\n\tconst fgColor = theme.getFgAnsi((color || \"muted\") as ThemeColor);\n\tconst reset = \"\\x1b[39m\\x1b[49m\";\n\tconst clamped = Math.max(0, Math.min(100, percent));\n\tconst filledFloat = (clamped / 100) * segments;\n\n\tconst result: string[] = [];\n\tfor (let i = 0; i < segments; i++) {\n\t\tconst blockFill = Math.max(0, Math.min(1, filledFloat - i));\n\t\tconst level = Math.round(blockFill * 8);\n\t\tconst glyph = glyphs[level];\n\t\tresult.push(level > 0 ? `${dimBg}${fgColor}${glyph}${reset}` : `${dimBg}${glyph}${reset}`);\n\t}\n\n\treturn result.join(\" \");\n}\n\n/**\n * Render a single segment.\n *\n * Layout: [icon] [text] [bar] [suffix]\n */\nfunction renderSegmentText(segment: Segment, settings: PowerbarSettings, theme: Theme): string {\n\tconst parts: string[] = [];\n\tconst themeColor = (segment.color || \"muted\") as ThemeColor;\n\n\tif (segment.icon) {\n\t\tparts.push(theme.fg(themeColor, segment.icon));\n\t}\n\n\tif (segment.text) {\n\t\tparts.push(theme.fg(themeColor, segment.text));\n\t}\n\n\tif (segment.bar !== undefined) {\n\t\tconst color = segment.color || \"muted\";\n\t\tif (settings.barStyle === \"blocks\") {\n\t\t\tconst blockCount = segment.barSegments ?? settings.barWidth;\n\t\t\tparts.push(renderBlocksBar(segment.bar, blockCount, theme, color));\n\t\t} else {\n\t\t\tparts.push(renderProgressBar(segment.bar, settings.barWidth, theme, color));\n\t\t}\n\t}\n\n\tif (segment.suffix) {\n\t\tparts.push(theme.fg(themeColor, segment.suffix));\n\t}\n\n\treturn parts.join(\" \");\n}\n\ninterface RenderedSegment {\n\ttext: string;\n\twidth: number;\n}\n\nfunction renderSideSegments(\n\tids: string[],\n\tsegments: Map<string, Segment>,\n\tsettings: PowerbarSettings,\n\ttheme: Theme,\n): RenderedSegment[] {\n\tconst rendered: RenderedSegment[] = [];\n\tfor (const id of ids) {\n\t\tconst seg = segments.get(id);\n\t\tif (!seg || (!seg.text && !seg.suffix && seg.bar === undefined)) continue;\n\t\tconst text = renderSegmentText(seg, settings, theme);\n\t\trendered.push({ text, width: visibleWidth(text) });\n\t}\n\treturn rendered;\n}\n\nfunction joinSegments(segments: RenderedSegment[], separator: string, separatorWidth: number): RenderedSegment {\n\tif (segments.length === 0) return { text: \"\", width: 0 };\n\tconst text = segments.map((s) => s.text).join(separator);\n\tconst width = segments.reduce((sum, s) => sum + s.width, 0) + separatorWidth * (segments.length - 1);\n\treturn { text, width };\n}\n\n/**\n * Truncate the widest segment to reclaim overflow space.\n * Mutates the array in place and returns the new total width.\n */\nfunction shrinkWidest(segments: RenderedSegment[], overflow: number): void {\n\tif (segments.length === 0) return;\n\n\tlet widestIdx = 0;\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tif (segments[i].width > segments[widestIdx].width) {\n\t\t\twidestIdx = i;\n\t\t}\n\t}\n\n\tconst seg = segments[widestIdx];\n\tconst targetWidth = Math.max(1, seg.width - overflow);\n\tsegments[widestIdx] = {\n\t\ttext: truncateToWidth(seg.text, targetWidth, \"…\"),\n\t\twidth: targetWidth,\n\t};\n}\n\nexport function renderBar(\n\tsegments: Map<string, Segment>,\n\tsettings: PowerbarSettings,\n\ttheme: Theme,\n\twidth: number,\n): string {\n\tconst separator = theme.fg(\"dim\", settings.separator);\n\tconst separatorWidth = visibleWidth(separator);\n\n\tconst leftSegs = renderSideSegments(settings.left, segments, settings, theme);\n\tconst rightSegs = renderSideSegments(settings.right, segments, settings, theme);\n\tconst allSegs = [...leftSegs, ...rightSegs];\n\n\t// Calculate total content width (segments + separators within each side + 1 for minimum padding)\n\tconst leftSepCount = Math.max(0, leftSegs.length - 1);\n\tconst rightSepCount = Math.max(0, rightSegs.length - 1);\n\tconst totalSepWidth = (leftSepCount + rightSepCount) * separatorWidth;\n\tconst totalSegWidth = allSegs.reduce((sum, s) => sum + s.width, 0);\n\tconst minPadding = 1;\n\tconst totalNeeded = totalSegWidth + totalSepWidth + minPadding;\n\n\t// Shrink the widest segment(s) until it fits\n\tif (totalNeeded > width) {\n\t\tlet overflow = totalNeeded - width;\n\t\tconst maxPasses = allSegs.length;\n\t\tfor (let i = 0; i < maxPasses && overflow > 0; i++) {\n\t\t\tshrinkWidest(allSegs, overflow);\n\t\t\tconst newSegWidth = allSegs.reduce((sum, s) => sum + s.width, 0);\n\t\t\toverflow = newSegWidth + totalSepWidth + minPadding - width;\n\t\t}\n\t}\n\n\t// Rebuild left/right from the (possibly truncated) segments\n\tconst left = joinSegments(allSegs.slice(0, leftSegs.length), separator, separatorWidth);\n\tconst right = joinSegments(allSegs.slice(leftSegs.length), separator, separatorWidth);\n\n\tconst padding = Math.max(minPadding, width - left.width - right.width);\n\tconst line = `${left.text}${\" \".repeat(padding)}${right.text}`;\n\n\t// Safety net\n\treturn truncateToWidth(line, width, \"…\");\n}\n"]}
@@ -10,6 +10,7 @@ export interface PowerbarSettings {
10
10
  separator: string;
11
11
  placement: "aboveEditor" | "belowEditor";
12
12
  barWidth: number;
13
+ barStyle: "continuous" | "blocks";
13
14
  }
14
15
  export declare function registerSettings(pi: ExtensionAPI, segmentOptions: OrderedListOption[]): void;
15
16
  export declare function loadSettings(): PowerbarSettings;
@@ -1 +1 @@
1
- {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/powerbar/settings.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAqB,MAAM,qCAAqC,CAAC;AAEhG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,aAAa,GAAG,aAAa,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,GAAG,IAAI,CA2C5F;AAED,wBAAgB,YAAY,IAAI,gBAAgB,CAoB/C"}
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/powerbar/settings.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAqB,MAAM,qCAAqC,CAAC;AAEhG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,aAAa,GAAG,aAAa,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,YAAY,GAAG,QAAQ,CAAC;CAClC;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAkD5F;AAED,wBAAgB,YAAY,IAAI,gBAAgB,CAsB/C"}
@@ -33,6 +33,13 @@ export function registerSettings(pi, segmentOptions) {
33
33
  defaultValue: "belowEditor",
34
34
  values: ["belowEditor", "aboveEditor"],
35
35
  },
36
+ {
37
+ id: "bar-style",
38
+ label: "Bar style",
39
+ description: "Visual style of progress bars",
40
+ defaultValue: "blocks",
41
+ values: ["continuous", "blocks"],
42
+ },
36
43
  {
37
44
  id: "bar-width",
38
45
  label: "Bar width",
@@ -51,6 +58,7 @@ export function loadSettings() {
51
58
  const rightStr = getSetting(EXTENSION_NAME, "right", "provider,model,sub-hourly,sub-weekly") ?? "";
52
59
  const separator = getSetting(EXTENSION_NAME, "separator", " │ ") ?? " │ ";
53
60
  const placement = getSetting(EXTENSION_NAME, "placement", "belowEditor") ?? "belowEditor";
61
+ const barStyle = getSetting(EXTENSION_NAME, "bar-style", "blocks") ?? "blocks";
54
62
  const barWidthStr = getSetting(EXTENSION_NAME, "bar-width", "10") ?? "10";
55
63
  return {
56
64
  left: leftStr
@@ -63,6 +71,7 @@ export function loadSettings() {
63
71
  .filter(Boolean),
64
72
  separator,
65
73
  placement: placement === "aboveEditor" ? "aboveEditor" : "belowEditor",
74
+ barStyle: barStyle === "continuous" ? "continuous" : "blocks",
66
75
  barWidth: Math.max(4, Math.min(24, Number.parseInt(barWidthStr, 10) || 10)),
67
76
  };
68
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/powerbar/settings.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAGjE,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;AAUzC,MAAM,UAAU,gBAAgB,CAAC,EAAgB,EAAE,cAAmC;IACrF,MAAM,WAAW,GAAwB;QACxC;YACC,EAAE,EAAE,MAAM;YACV,KAAK,EAAE,eAAe;YACtB,WAAW,EAAE,iDAAiD;YAC9D,YAAY,EAAE,iCAAiC;YAC/C,OAAO,EAAE,cAAc;SACvB;QACD;YACC,EAAE,EAAE,OAAO;YACX,KAAK,EAAE,gBAAgB;YACvB,WAAW,EAAE,kDAAkD;YAC/D,YAAY,EAAE,sCAAsC;YACpD,OAAO,EAAE,cAAc;SACvB;QACD;YACC,EAAE,EAAE,WAAW;YACf,KAAK,EAAE,WAAW;YAClB,WAAW,EAAE,4BAA4B;YACzC,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC;SAC1C;QACD;YACC,EAAE,EAAE,WAAW;YACf,KAAK,EAAE,WAAW;YAClB,WAAW,EAAE,4BAA4B;YACzC,YAAY,EAAE,aAAa;YAC3B,MAAM,EAAE,CAAC,aAAa,EAAE,aAAa,CAAC;SACtC;QACD;YACC,EAAE,EAAE,WAAW;YACf,KAAK,EAAE,WAAW;YAClB,WAAW,EAAE,sCAAsC;YACnD,YAAY,EAAE,IAAI;YAClB,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;SACpC;KACD,CAAC;IAEF,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,gCAAgC,EAAE;QAChD,IAAI,EAAE,cAAc;QACpB,QAAQ,EAAE,WAAW;KACrB,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC3B,MAAM,OAAO,GAAG,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,iCAAiC,CAAC,IAAI,EAAE,CAAC;IAC5F,MAAM,QAAQ,GAAG,UAAU,CAAC,cAAc,EAAE,OAAO,EAAE,sCAAsC,CAAC,IAAI,EAAE,CAAC;IACnG,MAAM,SAAS,GAAG,UAAU,CAAC,cAAc,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC;IAC1E,MAAM,SAAS,GAAG,UAAU,CAAC,cAAc,EAAE,WAAW,EAAE,aAAa,CAAC,IAAI,aAAa,CAAC;IAC1F,MAAM,WAAW,GAAG,UAAU,CAAC,cAAc,EAAE,WAAW,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC;IAE1E,OAAO;QACN,IAAI,EAAE,OAAO;aACX,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,MAAM,CAAC,OAAO,CAAC;QACjB,KAAK,EAAE,QAAQ;aACb,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,MAAM,CAAC,OAAO,CAAC;QACjB,SAAS;QACT,SAAS,EAAE,SAAS,KAAK,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa;QACtE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;KAC3E,CAAC;AACH,CAAC","sourcesContent":["/**\n * Settings for the powerbar via pi-extension-settings.\n */\n\nimport type { OrderedListOption, SettingDefinition } from \"@juanibiapina/pi-extension-settings\";\nimport { getSetting } from \"@juanibiapina/pi-extension-settings\";\nimport type { ExtensionAPI } from \"@mariozechner/pi-coding-agent\";\n\nexport const EXTENSION_NAME = \"powerbar\";\n\nexport interface PowerbarSettings {\n\tleft: string[];\n\tright: string[];\n\tseparator: string;\n\tplacement: \"aboveEditor\" | \"belowEditor\";\n\tbarWidth: number;\n}\n\nexport function registerSettings(pi: ExtensionAPI, segmentOptions: OrderedListOption[]): void {\n\tconst definitions: SettingDefinition[] = [\n\t\t{\n\t\t\tid: \"left\",\n\t\t\tlabel: \"Left segments\",\n\t\t\tdescription: \"Segments shown on the left side of the powerbar\",\n\t\t\tdefaultValue: \"git-branch,tokens,context-usage\",\n\t\t\toptions: segmentOptions,\n\t\t},\n\t\t{\n\t\t\tid: \"right\",\n\t\t\tlabel: \"Right segments\",\n\t\t\tdescription: \"Segments shown on the right side of the powerbar\",\n\t\t\tdefaultValue: \"provider,model,sub-hourly,sub-weekly\",\n\t\t\toptions: segmentOptions,\n\t\t},\n\t\t{\n\t\t\tid: \"separator\",\n\t\t\tlabel: \"Separator\",\n\t\t\tdescription: \"Separator between segments\",\n\t\t\tdefaultValue: \" │ \",\n\t\t\tvalues: [\" │ \", \" ┃ \", \" | \", \" · \", \" \"],\n\t\t},\n\t\t{\n\t\t\tid: \"placement\",\n\t\t\tlabel: \"Placement\",\n\t\t\tdescription: \"Where the powerbar appears\",\n\t\t\tdefaultValue: \"belowEditor\",\n\t\t\tvalues: [\"belowEditor\", \"aboveEditor\"],\n\t\t},\n\t\t{\n\t\t\tid: \"bar-width\",\n\t\t\tlabel: \"Bar width\",\n\t\t\tdescription: \"Width of progress bars in characters\",\n\t\t\tdefaultValue: \"10\",\n\t\t\tvalues: [\"6\", \"8\", \"10\", \"12\", \"16\"],\n\t\t},\n\t];\n\n\tpi.events.emit(\"pi-extension-settings:register\", {\n\t\tname: EXTENSION_NAME,\n\t\tsettings: definitions,\n\t});\n}\n\nexport function loadSettings(): PowerbarSettings {\n\tconst leftStr = getSetting(EXTENSION_NAME, \"left\", \"git-branch,tokens,context-usage\") ?? \"\";\n\tconst rightStr = getSetting(EXTENSION_NAME, \"right\", \"provider,model,sub-hourly,sub-weekly\") ?? \"\";\n\tconst separator = getSetting(EXTENSION_NAME, \"separator\", \" │ \") ?? \" │ \";\n\tconst placement = getSetting(EXTENSION_NAME, \"placement\", \"belowEditor\") ?? \"belowEditor\";\n\tconst barWidthStr = getSetting(EXTENSION_NAME, \"bar-width\", \"10\") ?? \"10\";\n\n\treturn {\n\t\tleft: leftStr\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean),\n\t\tright: rightStr\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean),\n\t\tseparator,\n\t\tplacement: placement === \"aboveEditor\" ? \"aboveEditor\" : \"belowEditor\",\n\t\tbarWidth: Math.max(4, Math.min(24, Number.parseInt(barWidthStr, 10) || 10)),\n\t};\n}\n"]}
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/powerbar/settings.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Settings for the powerbar via pi-extension-settings.\n */\n\nimport type { OrderedListOption, SettingDefinition } from \"@juanibiapina/pi-extension-settings\";\nimport { getSetting } from \"@juanibiapina/pi-extension-settings\";\nimport type { ExtensionAPI } from \"@mariozechner/pi-coding-agent\";\n\nexport const EXTENSION_NAME = \"powerbar\";\n\nexport interface PowerbarSettings {\n\tleft: string[];\n\tright: string[];\n\tseparator: string;\n\tplacement: \"aboveEditor\" | \"belowEditor\";\n\tbarWidth: number;\n\tbarStyle: \"continuous\" | \"blocks\";\n}\n\nexport function registerSettings(pi: ExtensionAPI, segmentOptions: OrderedListOption[]): void {\n\tconst definitions: SettingDefinition[] = [\n\t\t{\n\t\t\tid: \"left\",\n\t\t\tlabel: \"Left segments\",\n\t\t\tdescription: \"Segments shown on the left side of the powerbar\",\n\t\t\tdefaultValue: \"git-branch,tokens,context-usage\",\n\t\t\toptions: segmentOptions,\n\t\t},\n\t\t{\n\t\t\tid: \"right\",\n\t\t\tlabel: \"Right segments\",\n\t\t\tdescription: \"Segments shown on the right side of the powerbar\",\n\t\t\tdefaultValue: \"provider,model,sub-hourly,sub-weekly\",\n\t\t\toptions: segmentOptions,\n\t\t},\n\t\t{\n\t\t\tid: \"separator\",\n\t\t\tlabel: \"Separator\",\n\t\t\tdescription: \"Separator between segments\",\n\t\t\tdefaultValue: \" │ \",\n\t\t\tvalues: [\" │ \", \" ┃ \", \" | \", \" · \", \" \"],\n\t\t},\n\t\t{\n\t\t\tid: \"placement\",\n\t\t\tlabel: \"Placement\",\n\t\t\tdescription: \"Where the powerbar appears\",\n\t\t\tdefaultValue: \"belowEditor\",\n\t\t\tvalues: [\"belowEditor\", \"aboveEditor\"],\n\t\t},\n\t\t{\n\t\t\tid: \"bar-style\",\n\t\t\tlabel: \"Bar style\",\n\t\t\tdescription: \"Visual style of progress bars\",\n\t\t\tdefaultValue: \"blocks\",\n\t\t\tvalues: [\"continuous\", \"blocks\"],\n\t\t},\n\t\t{\n\t\t\tid: \"bar-width\",\n\t\t\tlabel: \"Bar width\",\n\t\t\tdescription: \"Width of progress bars in characters\",\n\t\t\tdefaultValue: \"10\",\n\t\t\tvalues: [\"6\", \"8\", \"10\", \"12\", \"16\"],\n\t\t},\n\t];\n\n\tpi.events.emit(\"pi-extension-settings:register\", {\n\t\tname: EXTENSION_NAME,\n\t\tsettings: definitions,\n\t});\n}\n\nexport function loadSettings(): PowerbarSettings {\n\tconst leftStr = getSetting(EXTENSION_NAME, \"left\", \"git-branch,tokens,context-usage\") ?? \"\";\n\tconst rightStr = getSetting(EXTENSION_NAME, \"right\", \"provider,model,sub-hourly,sub-weekly\") ?? \"\";\n\tconst separator = getSetting(EXTENSION_NAME, \"separator\", \" │ \") ?? \" │ \";\n\tconst placement = getSetting(EXTENSION_NAME, \"placement\", \"belowEditor\") ?? \"belowEditor\";\n\tconst barStyle = getSetting(EXTENSION_NAME, \"bar-style\", \"blocks\") ?? \"blocks\";\n\tconst barWidthStr = getSetting(EXTENSION_NAME, \"bar-width\", \"10\") ?? \"10\";\n\n\treturn {\n\t\tleft: leftStr\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean),\n\t\tright: rightStr\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean),\n\t\tseparator,\n\t\tplacement: placement === \"aboveEditor\" ? \"aboveEditor\" : \"belowEditor\",\n\t\tbarStyle: barStyle === \"continuous\" ? \"continuous\" : \"blocks\",\n\t\tbarWidth: Math.max(4, Math.min(24, Number.parseInt(barWidthStr, 10) || 10)),\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/powerbar-context/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AA6BpF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAW9D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/powerbar-context/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AAgCpF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAW9D"}
@@ -5,6 +5,7 @@
5
5
  * Color changes based on usage level: accent → warning → error.
6
6
  * Segment ID: "context-usage"
7
7
  */
8
+ const CHUNK_SIZE = 100_000;
8
9
  function getColor(pct) {
9
10
  if (pct > 80)
10
11
  return "error";
@@ -21,6 +22,7 @@ function emitContextUsage(pi, ctx) {
21
22
  text: "",
22
23
  suffix: `${pct}%`,
23
24
  bar: pct,
25
+ barSegments: Math.ceil(usage.contextWindow / CHUNK_SIZE),
24
26
  color: getColor(pct),
25
27
  });
26
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/powerbar-context/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,SAAS,QAAQ,CAAC,GAAW;IAC5B,IAAI,GAAG,GAAG,EAAE;QAAE,OAAO,OAAO,CAAC;IAC7B,IAAI,GAAG,GAAG,EAAE;QAAE,OAAO,SAAS,CAAC;IAC/B,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,SAAS,gBAAgB,CAAC,EAAgB,EAAE,GAAqB;IAChE,MAAM,KAAK,GAAG,GAAG,CAAC,eAAe,EAAE,CAAC;IACpC,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,CAAC;QACnE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;YACjC,EAAE,EAAE,eAAe;YACnB,IAAI,EAAE,EAAE;YACR,MAAM,EAAE,GAAG,GAAG,GAAG;YACjB,GAAG,EAAE,GAAG;YACR,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC;SACpB,CAAC,CAAC;IACJ,CAAC;AACF,CAAC;AAED,SAAS,iBAAiB,CAAC,EAAgB;IAC1C,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;QACjC,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,SAAS;KACf,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAgB;IACvD,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAE,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;IAE7F,gCAAgC;IAChC,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1D,EAAE,CAAC,EAAE,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC;IAE3D,sCAAsC;IACtC,EAAE,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IACtE,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IACvE,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;AACrE,CAAC","sourcesContent":["/**\n * Powerbar Context Producer\n *\n * Shows context window usage as a progress bar with percentage.\n * Color changes based on usage level: accent → warning → error.\n * Segment ID: \"context-usage\"\n */\n\nimport type { ExtensionAPI, ExtensionContext } from \"@mariozechner/pi-coding-agent\";\n\nfunction getColor(pct: number): string {\n\tif (pct > 80) return \"error\";\n\tif (pct > 60) return \"warning\";\n\treturn \"muted\";\n}\n\nfunction emitContextUsage(pi: ExtensionAPI, ctx: ExtensionContext): void {\n\tconst usage = ctx.getContextUsage();\n\tif (usage && usage.tokens != null) {\n\t\tconst pct = Math.round((usage.tokens / usage.contextWindow) * 100);\n\t\tpi.events.emit(\"powerbar:update\", {\n\t\t\tid: \"context-usage\",\n\t\t\ttext: \"\",\n\t\t\tsuffix: `${pct}%`,\n\t\t\tbar: pct,\n\t\t\tcolor: getColor(pct),\n\t\t});\n\t}\n}\n\nfunction resetContextUsage(pi: ExtensionAPI): void {\n\tpi.events.emit(\"powerbar:update\", {\n\t\tid: \"context-usage\",\n\t\ttext: undefined,\n\t});\n}\n\nexport default function createExtension(pi: ExtensionAPI): void {\n\tpi.events.emit(\"powerbar:register-segment\", { id: \"context-usage\", label: \"Context Usage\" });\n\n\t// Reset on new/switched session\n\tpi.on(\"session_start\", async () => resetContextUsage(pi));\n\tpi.on(\"session_switch\", async () => resetContextUsage(pi));\n\n\t// Update frequently during agent work\n\tpi.on(\"turn_start\", async (_event, ctx) => emitContextUsage(pi, ctx));\n\tpi.on(\"tool_result\", async (_event, ctx) => emitContextUsage(pi, ctx));\n\tpi.on(\"turn_end\", async (_event, ctx) => emitContextUsage(pi, ctx));\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/powerbar-context/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,UAAU,GAAG,OAAO,CAAC;AAE3B,SAAS,QAAQ,CAAC,GAAW;IAC5B,IAAI,GAAG,GAAG,EAAE;QAAE,OAAO,OAAO,CAAC;IAC7B,IAAI,GAAG,GAAG,EAAE;QAAE,OAAO,SAAS,CAAC;IAC/B,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,SAAS,gBAAgB,CAAC,EAAgB,EAAE,GAAqB;IAChE,MAAM,KAAK,GAAG,GAAG,CAAC,eAAe,EAAE,CAAC;IACpC,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,CAAC;QACnE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;YACjC,EAAE,EAAE,eAAe;YACnB,IAAI,EAAE,EAAE;YACR,MAAM,EAAE,GAAG,GAAG,GAAG;YACjB,GAAG,EAAE,GAAG;YACR,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,UAAU,CAAC;YACxD,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC;SACpB,CAAC,CAAC;IACJ,CAAC;AACF,CAAC;AAED,SAAS,iBAAiB,CAAC,EAAgB;IAC1C,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;QACjC,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,SAAS;KACf,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAgB;IACvD,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAE,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;IAE7F,gCAAgC;IAChC,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1D,EAAE,CAAC,EAAE,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC;IAE3D,sCAAsC;IACtC,EAAE,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IACtE,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IACvE,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;AACrE,CAAC","sourcesContent":["/**\n * Powerbar Context Producer\n *\n * Shows context window usage as a progress bar with percentage.\n * Color changes based on usage level: accent → warning → error.\n * Segment ID: \"context-usage\"\n */\n\nimport type { ExtensionAPI, ExtensionContext } from \"@mariozechner/pi-coding-agent\";\n\nconst CHUNK_SIZE = 100_000;\n\nfunction getColor(pct: number): string {\n\tif (pct > 80) return \"error\";\n\tif (pct > 60) return \"warning\";\n\treturn \"muted\";\n}\n\nfunction emitContextUsage(pi: ExtensionAPI, ctx: ExtensionContext): void {\n\tconst usage = ctx.getContextUsage();\n\tif (usage && usage.tokens != null) {\n\t\tconst pct = Math.round((usage.tokens / usage.contextWindow) * 100);\n\t\tpi.events.emit(\"powerbar:update\", {\n\t\t\tid: \"context-usage\",\n\t\t\ttext: \"\",\n\t\t\tsuffix: `${pct}%`,\n\t\t\tbar: pct,\n\t\t\tbarSegments: Math.ceil(usage.contextWindow / CHUNK_SIZE),\n\t\t\tcolor: getColor(pct),\n\t\t});\n\t}\n}\n\nfunction resetContextUsage(pi: ExtensionAPI): void {\n\tpi.events.emit(\"powerbar:update\", {\n\t\tid: \"context-usage\",\n\t\ttext: undefined,\n\t});\n}\n\nexport default function createExtension(pi: ExtensionAPI): void {\n\tpi.events.emit(\"powerbar:register-segment\", { id: \"context-usage\", label: \"Context Usage\" });\n\n\t// Reset on new/switched session\n\tpi.on(\"session_start\", async () => resetContextUsage(pi));\n\tpi.on(\"session_switch\", async () => resetContextUsage(pi));\n\n\t// Update frequently during agent work\n\tpi.on(\"turn_start\", async (_event, ctx) => emitContextUsage(pi, ctx));\n\tpi.on(\"tool_result\", async (_event, ctx) => emitContextUsage(pi, ctx));\n\tpi.on(\"turn_end\", async (_event, ctx) => emitContextUsage(pi, ctx));\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/powerbar-sub/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA0ClE,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAuB9D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/powerbar-sub/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA2ClE,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAuB9D"}
@@ -14,7 +14,7 @@ function getColor(pct) {
14
14
  return "warning";
15
15
  return "muted";
16
16
  }
17
- function emitWindow(pi, segmentId, window) {
17
+ function emitWindow(pi, segmentId, window, barSegments) {
18
18
  if (!window) {
19
19
  pi.events.emit("powerbar:update", { id: segmentId, text: undefined });
20
20
  return;
@@ -32,6 +32,7 @@ function emitWindow(pi, segmentId, window) {
32
32
  text: textParts.join(" "),
33
33
  suffix: `${pct}%`,
34
34
  bar: pct,
35
+ barSegments,
35
36
  color: getColor(pct),
36
37
  });
37
38
  }
@@ -41,8 +42,8 @@ function emitUsage(pi, usage) {
41
42
  pi.events.emit("powerbar:update", { id: "sub-weekly", text: undefined });
42
43
  return;
43
44
  }
44
- emitWindow(pi, "sub-hourly", usage.windows[0]);
45
- emitWindow(pi, "sub-weekly", usage.windows[1]);
45
+ emitWindow(pi, "sub-hourly", usage.windows[0], 5);
46
+ emitWindow(pi, "sub-weekly", usage.windows[1], 7);
46
47
  }
47
48
  export default function createExtension(pi) {
48
49
  pi.events.emit("powerbar:register-segment", { id: "sub-hourly", label: "Sub Hourly" });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/powerbar-sub/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Powerbar Sub Producer\n *\n * Shows subscription usage from pi-sub-core.\n * Sub-core is loaded by pi as a sibling extension (declared in package.json pi.extensions).\n * This producer just listens to sub-core events and emits powerbar segments.\n *\n * Segment IDs: \"sub-hourly\", \"sub-weekly\"\n */\n\nimport type { RateWindow, SubCoreAllState, SubCoreState, UsageSnapshot } from \"@marckrenn/pi-sub-shared\";\nimport type { ExtensionAPI } from \"@mariozechner/pi-coding-agent\";\n\nfunction getColor(pct: number): string {\n\tif (pct > 80) return \"error\";\n\tif (pct > 60) return \"warning\";\n\treturn \"muted\";\n}\n\nfunction emitWindow(pi: ExtensionAPI, segmentId: string, window: RateWindow | undefined): void {\n\tif (!window) {\n\t\tpi.events.emit(\"powerbar:update\", { id: segmentId, text: undefined });\n\t\treturn;\n\t}\n\n\tconst pct = Math.round(window.usedPercent);\n\tconst label = window.label || \"\";\n\tconst reset = window.resetDescription || \"\";\n\n\tconst textParts: string[] = [];\n\tif (label) textParts.push(label);\n\tif (reset) textParts.push(reset);\n\n\tpi.events.emit(\"powerbar:update\", {\n\t\tid: segmentId,\n\t\ttext: textParts.join(\" \"),\n\t\tsuffix: `${pct}%`,\n\t\tbar: pct,\n\t\tcolor: getColor(pct),\n\t});\n}\n\nfunction emitUsage(pi: ExtensionAPI, usage: UsageSnapshot | undefined): void {\n\tif (!usage || usage.windows.length === 0) {\n\t\tpi.events.emit(\"powerbar:update\", { id: \"sub-hourly\", text: undefined });\n\t\tpi.events.emit(\"powerbar:update\", { id: \"sub-weekly\", text: undefined });\n\t\treturn;\n\t}\n\n\temitWindow(pi, \"sub-hourly\", usage.windows[0]);\n\temitWindow(pi, \"sub-weekly\", usage.windows[1]);\n}\n\nexport default function createExtension(pi: ExtensionAPI): void {\n\tpi.events.emit(\"powerbar:register-segment\", { id: \"sub-hourly\", label: \"Sub Hourly\" });\n\tpi.events.emit(\"powerbar:register-segment\", { id: \"sub-weekly\", label: \"Sub Weekly\" });\n\n\tpi.events.on(\"sub-core:ready\", (payload: unknown) => {\n\t\tconst data = payload as { state?: SubCoreState };\n\t\temitUsage(pi, data.state?.usage);\n\t});\n\n\tpi.events.on(\"sub-core:update-current\", (payload: unknown) => {\n\t\tconst data = payload as { state?: SubCoreState };\n\t\temitUsage(pi, data.state?.usage);\n\t});\n\n\tpi.events.on(\"sub-core:update-all\", (payload: unknown) => {\n\t\tconst data = payload as { state?: SubCoreAllState };\n\t\t// Find the entry matching the current provider, or fall back to first entry\n\t\tconst currentProvider = data.state?.provider;\n\t\tconst entry = currentProvider\n\t\t\t? data.state?.entries?.find((e) => e.provider === currentProvider)\n\t\t\t: data.state?.entries?.[0];\n\t\temitUsage(pi, entry?.usage);\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/powerbar-sub/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Powerbar Sub Producer\n *\n * Shows subscription usage from pi-sub-core.\n * Sub-core is loaded by pi as a sibling extension (declared in package.json pi.extensions).\n * This producer just listens to sub-core events and emits powerbar segments.\n *\n * Segment IDs: \"sub-hourly\", \"sub-weekly\"\n */\n\nimport type { RateWindow, SubCoreAllState, SubCoreState, UsageSnapshot } from \"@marckrenn/pi-sub-shared\";\nimport type { ExtensionAPI } from \"@mariozechner/pi-coding-agent\";\n\nfunction getColor(pct: number): string {\n\tif (pct > 80) return \"error\";\n\tif (pct > 60) return \"warning\";\n\treturn \"muted\";\n}\n\nfunction emitWindow(pi: ExtensionAPI, segmentId: string, window: RateWindow | undefined, barSegments: number): void {\n\tif (!window) {\n\t\tpi.events.emit(\"powerbar:update\", { id: segmentId, text: undefined });\n\t\treturn;\n\t}\n\n\tconst pct = Math.round(window.usedPercent);\n\tconst label = window.label || \"\";\n\tconst reset = window.resetDescription || \"\";\n\n\tconst textParts: string[] = [];\n\tif (label) textParts.push(label);\n\tif (reset) textParts.push(reset);\n\n\tpi.events.emit(\"powerbar:update\", {\n\t\tid: segmentId,\n\t\ttext: textParts.join(\" \"),\n\t\tsuffix: `${pct}%`,\n\t\tbar: pct,\n\t\tbarSegments,\n\t\tcolor: getColor(pct),\n\t});\n}\n\nfunction emitUsage(pi: ExtensionAPI, usage: UsageSnapshot | undefined): void {\n\tif (!usage || usage.windows.length === 0) {\n\t\tpi.events.emit(\"powerbar:update\", { id: \"sub-hourly\", text: undefined });\n\t\tpi.events.emit(\"powerbar:update\", { id: \"sub-weekly\", text: undefined });\n\t\treturn;\n\t}\n\n\temitWindow(pi, \"sub-hourly\", usage.windows[0], 5);\n\temitWindow(pi, \"sub-weekly\", usage.windows[1], 7);\n}\n\nexport default function createExtension(pi: ExtensionAPI): void {\n\tpi.events.emit(\"powerbar:register-segment\", { id: \"sub-hourly\", label: \"Sub Hourly\" });\n\tpi.events.emit(\"powerbar:register-segment\", { id: \"sub-weekly\", label: \"Sub Weekly\" });\n\n\tpi.events.on(\"sub-core:ready\", (payload: unknown) => {\n\t\tconst data = payload as { state?: SubCoreState };\n\t\temitUsage(pi, data.state?.usage);\n\t});\n\n\tpi.events.on(\"sub-core:update-current\", (payload: unknown) => {\n\t\tconst data = payload as { state?: SubCoreState };\n\t\temitUsage(pi, data.state?.usage);\n\t});\n\n\tpi.events.on(\"sub-core:update-all\", (payload: unknown) => {\n\t\tconst data = payload as { state?: SubCoreAllState };\n\t\t// Find the entry matching the current provider, or fall back to first entry\n\t\tconst currentProvider = data.state?.provider;\n\t\tconst entry = currentProvider\n\t\t\t? data.state?.entries?.find((e) => e.provider === currentProvider)\n\t\t\t: data.state?.entries?.[0];\n\t\temitUsage(pi, entry?.usage);\n\t});\n}\n"]}
package/package.json CHANGED
@@ -1,57 +1,56 @@
1
1
  {
2
- "name": "@juanibiapina/pi-powerbar",
3
- "version": "0.6.1",
4
- "description": "Pi extension that renders a persistent powerline status bar with left/right segments updated via events",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "scripts": {
9
- "clean": "rm -rf dist",
10
- "build": "tsc -p tsconfig.build.json",
11
- "dev": "tsc -p tsconfig.build.json --watch",
12
- "check": "biome check --write --error-on-warnings . && tsc --noEmit",
13
- "prepublishOnly": "npm run clean && npm run build && npm run check"
14
- },
15
- "files": [
16
- "dist/**/*",
17
- "README.md"
18
- ],
19
- "keywords": [
20
- "pi",
21
- "pi-package",
22
- "extension",
23
- "powerline",
24
- "status-bar"
25
- ],
26
- "pi": {
27
- "extensions": [
28
- "./dist",
29
- "node_modules/@marckrenn/pi-sub-core/index.ts"
30
- ]
31
- },
32
- "author": "Juan Ibiapina",
33
- "license": "MIT",
34
- "repository": {
35
- "type": "git",
36
- "url": "https://github.com/juanibiapina/pi-powerbar"
37
- },
38
- "engines": {
39
- "node": ">=20.0.0"
40
- },
41
- "peerDependencies": {
42
- "@mariozechner/pi-coding-agent": "*",
43
- "@mariozechner/pi-tui": "*"
44
- },
45
- "dependencies": {
46
- "@juanibiapina/pi-extension-settings": "^0.5.0",
47
- "@marckrenn/pi-sub-core": "^1.3.0"
48
- },
49
- "devDependencies": {
50
- "@biomejs/biome": "2.4.1",
51
- "@juanibiapina/pi-extension-settings": "^0.5.0",
52
- "@marckrenn/pi-sub-shared": "^1.3.0",
53
- "@mariozechner/pi-coding-agent": "^0.52.12",
54
- "@types/node": "^25.2.3",
55
- "typescript": "^5.9.3"
56
- }
2
+ "name": "@juanibiapina/pi-powerbar",
3
+ "version": "0.7.1",
4
+ "description": "Pi extension that renders a persistent powerline status bar with left/right segments updated via events",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "scripts": {
9
+ "clean": "rm -rf dist",
10
+ "build": "tsc -p tsconfig.build.json",
11
+ "dev": "tsc -p tsconfig.build.json --watch",
12
+ "check": "biome check --write --error-on-warnings . && tsc --noEmit",
13
+ "prepublishOnly": "npm run clean && npm run build && npm run check"
14
+ },
15
+ "files": [
16
+ "dist/**/*",
17
+ "README.md"
18
+ ],
19
+ "keywords": [
20
+ "pi",
21
+ "pi-package",
22
+ "extension",
23
+ "powerline",
24
+ "status-bar"
25
+ ],
26
+ "pi": {
27
+ "extensions": [
28
+ "./dist",
29
+ "node_modules/@marckrenn/pi-sub-core/index.ts"
30
+ ]
31
+ },
32
+ "author": "Juan Ibiapina",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/juanibiapina/pi-powerbar"
37
+ },
38
+ "engines": {
39
+ "node": ">=20.0.0"
40
+ },
41
+ "peerDependencies": {
42
+ "@mariozechner/pi-coding-agent": "*",
43
+ "@mariozechner/pi-tui": "*"
44
+ },
45
+ "dependencies": {
46
+ "@marckrenn/pi-sub-core": "^1.3.0"
47
+ },
48
+ "devDependencies": {
49
+ "@biomejs/biome": "2.4.1",
50
+ "@juanibiapina/pi-extension-settings": "^0.6.0",
51
+ "@marckrenn/pi-sub-shared": "^1.3.0",
52
+ "@mariozechner/pi-coding-agent": "^0.62.0",
53
+ "@types/node": "^25.2.3",
54
+ "typescript": "^5.9.3"
55
+ }
57
56
  }