@narumitw/pi-statusline 0.38.0 → 0.39.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.
Files changed (2) hide show
  1. package/package.json +5 -5
  2. package/src/commands.ts +27 -77
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-statusline",
3
- "version": "0.38.0",
3
+ "version": "0.39.0",
4
4
  "description": "Pi extension that replaces the footer with an information-rich statusline.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,10 +28,10 @@
28
28
  "typecheck": "tsc --noEmit"
29
29
  },
30
30
  "devDependencies": {
31
- "@biomejs/biome": "2.5.5",
32
- "@earendil-works/pi-coding-agent": "0.82.1",
33
- "@earendil-works/pi-tui": "0.82.1",
34
- "@types/node": "26.1.1",
31
+ "@biomejs/biome": "2.5.6",
32
+ "@earendil-works/pi-coding-agent": "0.83.0",
33
+ "@earendil-works/pi-tui": "0.83.0",
34
+ "@types/node": "26.1.2",
35
35
  "typescript": "7.0.2"
36
36
  },
37
37
  "repository": {
package/src/commands.ts CHANGED
@@ -125,8 +125,8 @@ async function showMainMenu(ctx: ExtensionCommandContext, options: StatuslineCom
125
125
  signal: fallbackController.signal,
126
126
  isCurrent: () => !fallbackController.signal.aborted,
127
127
  };
128
- type Screen = "main" | "advanced";
129
- type Action = "appearance" | "information" | "layout" | "edit" | "status" | "help" | "back";
128
+ type Screen = "main" | "advanced" | "information";
129
+ type Action = "appearance" | "setInformation" | "layout" | "edit" | "status" | "help" | "back";
130
130
  const menu = defineMenu<undefined, Screen, Action, ExtensionCommandContext>({
131
131
  start: "main",
132
132
  screens: {
@@ -144,7 +144,7 @@ async function showMainMenu(ctx: ExtensionCommandContext, options: StatuslineCom
144
144
  {
145
145
  id: "information",
146
146
  label: `Information (${inferInformationProfile(config.segments)})`,
147
- action: "information",
147
+ to: "information",
148
148
  },
149
149
  { id: "advanced", label: "Advanced", to: "advanced" },
150
150
  { id: "status", label: "Status", action: "status" },
@@ -153,6 +153,24 @@ async function showMainMenu(ctx: ExtensionCommandContext, options: StatuslineCom
153
153
  hint: "close",
154
154
  };
155
155
  },
156
+ information: () => {
157
+ const current = inferInformationProfile(options.getLoaded().config.segments);
158
+ return {
159
+ kind: "choice",
160
+ title: "Information level",
161
+ lines: [`Current profile: ${current}`],
162
+ items: INFORMATION_PROFILE_NAMES.map((profile) => ({
163
+ id: profile,
164
+ label: `${profile[0]?.toUpperCase() ?? ""}${profile.slice(1)}`,
165
+ description: `${INFORMATION_PROFILES[profile].length} segments`,
166
+ details: [`Segments: ${INFORMATION_PROFILES[profile].join(" · ")}`],
167
+ })),
168
+ action: "setInformation",
169
+ currentItemId: current,
170
+ initialItemId: current === "custom" ? "balanced" : current,
171
+ hint: "close",
172
+ };
173
+ },
156
174
  advanced: () => {
157
175
  const config = options.getLoaded().config;
158
176
  const visibleSegmentCount = config.segments.filter(
@@ -179,8 +197,10 @@ async function showMainMenu(ctx: ExtensionCommandContext, options: StatuslineCom
179
197
  await choosePalettePreset(ctx, options);
180
198
  return { kind: "close" };
181
199
  },
182
- information: async () => {
183
- await chooseInformationProfile(ctx, options);
200
+ setInformation: async ({ itemId }) => {
201
+ const profile = INFORMATION_PROFILE_NAMES.find((candidate) => candidate === itemId);
202
+ if (!profile) return { kind: "rejected" };
203
+ applyInformationProfile(ctx, options, profile);
184
204
  return { kind: "close" };
185
205
  },
186
206
  layout: async () => {
@@ -317,19 +337,12 @@ async function showPalettePresetPicker(
317
337
  return result ?? undefined;
318
338
  }
319
339
 
320
- async function chooseInformationProfile(
340
+ function applyInformationProfile(
321
341
  ctx: ExtensionCommandContext,
322
342
  options: StatuslineCommandOptions,
343
+ selection: InformationProfileName,
323
344
  ) {
324
- if (ctx.mode !== "tui") {
325
- if (ctx.hasUI) ctx.ui.notify(`Edit segments manually: ${options.settingsPath}`, "info");
326
- return;
327
- }
328
345
  const current = options.getLoaded();
329
- const currentProfile = inferInformationProfile(current.config.segments);
330
- const selection = await showInformationProfilePicker(ctx, currentProfile);
331
- if (selection === undefined) return;
332
-
333
346
  try {
334
347
  const change = informationProfileDocument(current, selection);
335
348
  const loaded = applySegmentsDocumentChange(change, ctx, options);
@@ -342,69 +355,6 @@ async function chooseInformationProfile(
342
355
  }
343
356
  }
344
357
 
345
- async function showInformationProfilePicker(
346
- ctx: ExtensionCommandContext,
347
- current: ReturnType<typeof inferInformationProfile>,
348
- ): Promise<InformationProfileName | undefined> {
349
- const items: SelectItem[] = INFORMATION_PROFILE_NAMES.map((profile) => ({
350
- value: profile,
351
- label: `${profile[0]?.toUpperCase() ?? ""}${profile.slice(1)}`,
352
- description: `${INFORMATION_PROFILES[profile].length} segments${profile === current ? " • current" : ""}`,
353
- }));
354
- const initialProfile = current === "custom" ? "balanced" : current;
355
- const selectedIndex = INFORMATION_PROFILE_NAMES.indexOf(initialProfile);
356
- const result = await ctx.ui.custom<InformationProfileName | null>(
357
- (tui, theme, _keybindings, done) => {
358
- const container = new Container();
359
- container.addChild(new DynamicBorder((text: string) => theme.fg("accent", text)));
360
- const title = new Text("", 1, 0);
361
- container.addChild(title);
362
- const list = new SelectList(items, items.length, {
363
- selectedPrefix: (text) => theme.fg("accent", text),
364
- selectedText: (text) => theme.fg("accent", text),
365
- description: (text) => theme.fg("muted", text),
366
- scrollInfo: (text) => theme.fg("dim", text),
367
- noMatch: (text) => theme.fg("warning", text),
368
- });
369
- list.setSelectedIndex(selectedIndex);
370
- let selectedProfile = initialProfile;
371
- const details = new Text("", 1, 0);
372
- const hint = new Text("", 1, 0);
373
- const updateThemedText = () => {
374
- title.setText(theme.fg("accent", theme.bold(`Information level (current: ${current})`)));
375
- details.setText(
376
- theme.fg("muted", `Segments: ${INFORMATION_PROFILES[selectedProfile].join(" · ")}`),
377
- );
378
- hint.setText(theme.fg("dim", "↑↓ preview contents • enter apply • esc cancel"));
379
- };
380
- list.onSelectionChange = (item) => {
381
- selectedProfile = item.value as InformationProfileName;
382
- updateThemedText();
383
- };
384
- list.onSelect = (item) => done(item.value as InformationProfileName);
385
- list.onCancel = () => done(null);
386
- container.addChild(list);
387
- container.addChild(details);
388
- container.addChild(hint);
389
- container.addChild(new DynamicBorder((text: string) => theme.fg("accent", text)));
390
- updateThemedText();
391
-
392
- return {
393
- render: (width: number) => container.render(width),
394
- invalidate() {
395
- container.invalidate();
396
- updateThemedText();
397
- },
398
- handleInput(data: string) {
399
- list.handleInput(data);
400
- tui.requestRender();
401
- },
402
- };
403
- },
404
- );
405
- return result ?? undefined;
406
- }
407
-
408
358
  async function chooseSegments(ctx: ExtensionCommandContext, options: StatuslineCommandOptions) {
409
359
  if (ctx.mode !== "tui") {
410
360
  if (ctx.hasUI) ctx.ui.notify(`Edit segments manually: ${options.settingsPath}`, "info");