@narumitw/pi-statusline 0.7.0 → 0.8.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
@@ -14,7 +14,7 @@ Use it to monitor model selection, thinking level, git branch, working directory
14
14
  - Owns extension status icons through optional JSON config, including per-extension icon suppression with `""`.
15
15
  - Warns when the same extension package is installed from multiple sources.
16
16
  - Uses emoji-labeled segments for readability in both classic and Tokyo Night presets.
17
- - Adapts to terminal width and truncates safely.
17
+ - Adapts to terminal width and wraps long extension status lines safely.
18
18
  - Requires no configuration, with optional preset selection through `PI_STATUSLINE_PRESET`.
19
19
 
20
20
  ## 📦 Install
@@ -59,6 +59,7 @@ Extension statuses use built-in icons by status key. Override or suppress them i
59
59
  {
60
60
  "extensionStatusIcons": {
61
61
  "caffeinate": "☕",
62
+ "github-pr": "🔎",
62
63
  "goal": "🎯",
63
64
  "pisync": "☁️",
64
65
  "unknown-error-retry": "",
@@ -90,13 +91,14 @@ The default `tokyo-night` statusline uses a Starship-inspired `░▒▓` / `
90
91
  - 💸 estimated cost.
91
92
  - 🕒 clock.
92
93
 
93
- Statuses from other extensions appear on their own compact line below the main statusline and use each preset's separator.
94
+ Statuses from other extensions appear below the main statusline, use each preset's separator, and wrap onto additional footer lines when they exceed the terminal width.
94
95
 
95
96
  `pi-statusline` is extension-agnostic: it consumes Pi's generic extension status API and does not import or depend on status-producing extensions.
96
97
 
97
98
  Examples:
98
99
 
99
100
  - `🎯 active` for `goal: active` using the built-in `goal` icon.
101
+ - `🔎 PR #123 checks passing` for `github-pr: PR #123 checks passing` using the built-in `github-pr` icon.
100
102
  - `☕ display` when JSON config sets `"caffeinate": "☕"`.
101
103
  - `receiving` when JSON config sets `"unknown-error-retry": ""`.
102
104
  - `🔌 running` for an unknown extension status key with no configured icon.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-statusline",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Pi extension that replaces the footer with an information-rich statusline.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/statusline.ts CHANGED
@@ -9,7 +9,7 @@ import type {
9
9
  Theme,
10
10
  ThemeColor,
11
11
  } from "@earendil-works/pi-coding-agent";
12
- import { truncateToWidth } from "@earendil-works/pi-tui";
12
+ import { truncateToWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
13
13
  import { classicExtensionSeparator, renderClassicStatusline } from "../presets/classic.js";
14
14
  import { renderTokyoNightStatusline, tokyoNightExtensionSeparator } from "../presets/tokyo-night.js";
15
15
  import type {
@@ -49,6 +49,7 @@ const DEFAULT_EXTENSION_STATUS_ICONS: Record<string, string> = {
49
49
  "codex-usage": "📊",
50
50
  caffeinate: "💊",
51
51
  firecrawl: "🔥",
52
+ "github-pr": "🔎",
52
53
  goal: "🎯",
53
54
  lsp: "🧰",
54
55
  "plan-mode": "📝",
@@ -112,14 +113,7 @@ export default function statusline(pi: ExtensionAPI) {
112
113
  invalidate() {},
113
114
  render(width: number): string[] {
114
115
  const lines = [renderStatusline(width, ctx, footerData, theme, config, runtime)];
115
- const extensionStatusLine = renderExtensionStatusline(
116
- width,
117
- footerData,
118
- theme,
119
- config,
120
- runtime,
121
- );
122
- if (extensionStatusLine) lines.push(extensionStatusLine);
116
+ lines.push(...renderExtensionStatusline(width, footerData, theme, config, runtime));
123
117
  return lines;
124
118
  },
125
119
  };
@@ -255,11 +249,9 @@ function renderExtensionStatusline(
255
249
  theme: Theme,
256
250
  config: StatuslineConfig,
257
251
  runtime: RuntimeState,
258
- ): string | undefined {
252
+ ): string[] {
259
253
  const status = formatExtensionStatuses(footerData.getExtensionStatuses(), theme, config, runtime);
260
- if (!status) return undefined;
261
-
262
- return truncateToWidth(status, width, "");
254
+ return wrapExtensionStatusline(status, width);
263
255
  }
264
256
 
265
257
  function buildSegment(
@@ -405,7 +397,7 @@ export function formatExtensionStatus(
405
397
  config: Pick<StatuslineConfig, "extensionStatusIcons">,
406
398
  ): string {
407
399
  const status = splitExtensionStatusIcon(stripExtensionStatusPrefix(key, value));
408
- const text = truncateToWidth(simplifyExtensionStatusText(status.text), 22, "…");
400
+ const text = simplifyExtensionStatusText(status.text);
409
401
  const color = extensionColor(key, value);
410
402
  const textColor = color === "warning" ? "warning" : "muted";
411
403
  const icon = extensionStatusIcon(key, status.icon, config.extensionStatusIcons);
@@ -422,6 +414,11 @@ function extensionStatusIcon(
422
414
  return leadingIcon ?? DEFAULT_EXTENSION_STATUS_ICONS[key] ?? "🔌";
423
415
  }
424
416
 
417
+ export function wrapExtensionStatusline(status: string, width: number): string[] {
418
+ if (!status || width <= 0) return [];
419
+ return wrapTextWithAnsi(status, width);
420
+ }
421
+
425
422
  function formatDuplicateExtensionStatus(runtime: RuntimeState, theme: Theme): string[] {
426
423
  if (runtime.duplicateExtensions.length === 0) return [];
427
424
  const names = runtime.duplicateExtensions.slice(0, 2).join(", ");