@oh-my-pi/pi-tui 17.0.8 → 17.0.9

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/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [17.0.9] - 2026-07-23
6
+
7
+ ### Added
8
+
9
+ - Added `SelectList.setMaxVisible()` so hosts can refit the visible row budget to available height after construction.
10
+
5
11
  ## [17.0.8] - 2026-07-22
6
12
 
7
13
  ### Fixed
@@ -6,6 +6,8 @@ export declare class Input implements Component, Focusable {
6
6
  #private;
7
7
  /** Rendered before the editable area; set to "" for chrome-less embedding. */
8
8
  prompt: string;
9
+ /** Render the editable value as bullets while retaining the real value internally. */
10
+ mask: boolean;
9
11
  onSubmit?: (value: string) => void;
10
12
  onEscape?: () => void;
11
13
  /** Focusable interface - set by TUI when focus changes */
@@ -43,13 +43,14 @@ export interface SelectListLayoutOptions {
43
43
  export declare class SelectList implements Component, MouseRoutable {
44
44
  #private;
45
45
  private readonly items;
46
- private readonly maxVisible;
47
46
  private readonly theme;
48
47
  private readonly layout;
49
48
  onSelect?: (item: SelectItem) => void;
50
49
  onCancel?: () => void;
51
50
  onSelectionChange?: (item: SelectItem) => void;
52
51
  constructor(items: ReadonlyArray<SelectItem>, maxVisible: number, theme: SelectListTheme, layout?: SelectListLayoutOptions);
52
+ /** Refit the visible row budget (hosts clamp the list to available height). */
53
+ setMaxVisible(rows: number): void;
53
54
  setFilter(filter: string): void;
54
55
  setSelectedIndex(index: number): void;
55
56
  /** Resolve a 0-based rendered-line index to a filtered-item index. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-tui",
4
- "version": "17.0.8",
4
+ "version": "17.0.9",
5
5
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -37,8 +37,8 @@
37
37
  "fmt": "biome format --write ."
38
38
  },
39
39
  "dependencies": {
40
- "@oh-my-pi/pi-natives": "17.0.8",
41
- "@oh-my-pi/pi-utils": "17.0.8",
40
+ "@oh-my-pi/pi-natives": "17.0.9",
41
+ "@oh-my-pi/pi-utils": "17.0.9",
42
42
  "lru-cache": "11.5.2",
43
43
  "marked": "^18.0.6"
44
44
  },
@@ -30,6 +30,8 @@ export class Input implements Component, Focusable {
30
30
  #useTerminalCursor = false;
31
31
  /** Rendered before the editable area; set to "" for chrome-less embedding. */
32
32
  prompt = "> ";
33
+ /** Render the editable value as bullets while retaining the real value internally. */
34
+ mask = false;
33
35
  onSubmit?: (value: string) => void;
34
36
  onEscape?: () => void;
35
37
 
@@ -415,9 +417,15 @@ export class Input implements Component, Focusable {
415
417
  return [prompt];
416
418
  }
417
419
 
418
- const cursorIndex = this.#cursor;
420
+ let cursorIndex = this.#cursor;
419
421
  // Ensure we always have a grapheme to invert at the cursor (space at end).
420
- const displayValue = cursorIndex >= this.#value.length ? `${this.#value} ` : this.#value;
422
+ let visibleValue = this.#value;
423
+ if (this.mask) {
424
+ const graphemes = [...segmenter.segment(this.#value)];
425
+ visibleValue = "•".repeat(graphemes.length);
426
+ cursorIndex = graphemes.filter(grapheme => grapheme.index < this.#cursor).length;
427
+ }
428
+ const displayValue = this.#cursor >= this.#value.length ? `${visibleValue} ` : visibleValue;
421
429
 
422
430
  const totalCols = visibleWidth(displayValue);
423
431
  const cursorCols = visibleWidth(displayValue.slice(0, cursorIndex));
@@ -84,6 +84,7 @@ type SelectItemLayout =
84
84
  };
85
85
 
86
86
  export class SelectList implements Component, MouseRoutable {
87
+ #maxVisible: number;
87
88
  #filteredItems: ReadonlyArray<SelectItem>;
88
89
  #filterQuery = "";
89
90
  #selectedIndex: number = 0;
@@ -97,13 +98,19 @@ export class SelectList implements Component, MouseRoutable {
97
98
 
98
99
  constructor(
99
100
  private readonly items: ReadonlyArray<SelectItem>,
100
- private readonly maxVisible: number,
101
+ maxVisible: number,
101
102
  private readonly theme: SelectListTheme,
102
103
  private readonly layout: SelectListLayoutOptions = {},
103
104
  ) {
105
+ this.#maxVisible = Math.max(1, Math.trunc(maxVisible));
104
106
  this.#filteredItems = items;
105
107
  }
106
108
 
109
+ /** Refit the visible row budget (hosts clamp the list to available height). */
110
+ setMaxVisible(rows: number): void {
111
+ this.#maxVisible = Math.max(1, Math.trunc(rows));
112
+ }
113
+
107
114
  setFilter(filter: string): void {
108
115
  this.#setFilter(filter, true);
109
116
  }
@@ -168,7 +175,7 @@ export class SelectList implements Component, MouseRoutable {
168
175
  const wrapEnabled = this.layout.wrapDescription === true;
169
176
  // `maxVisible` is the picker's visual row budget. For non-wrap layouts
170
177
  // every item is one row, so the budget matches the original item count.
171
- const visualBudget = this.maxVisible;
178
+ const visualBudget = this.#maxVisible;
172
179
 
173
180
  // Compute per-item visual row counts at the conservative width (i.e.
174
181
  // assume the scrollbar column might be reserved). For non-wrap layouts
@@ -256,12 +263,12 @@ export class SelectList implements Component, MouseRoutable {
256
263
  }
257
264
  // PageUp - jump up by one visible page
258
265
  else if (kb.matches(keyData, "tui.select.pageUp")) {
259
- this.#selectedIndex = Math.max(0, this.#selectedIndex - this.maxVisible);
266
+ this.#selectedIndex = Math.max(0, this.#selectedIndex - this.#maxVisible);
260
267
  this.#notifySelectionChange();
261
268
  }
262
269
  // PageDown - jump down by one visible page
263
270
  else if (kb.matches(keyData, "tui.select.pageDown")) {
264
- this.#selectedIndex = Math.min(this.#filteredItems.length - 1, this.#selectedIndex + this.maxVisible);
271
+ this.#selectedIndex = Math.min(this.#filteredItems.length - 1, this.#selectedIndex + this.#maxVisible);
265
272
  this.#notifySelectionChange();
266
273
  }
267
274
  // Enter
@@ -459,12 +466,12 @@ export class SelectList implements Component, MouseRoutable {
459
466
 
460
467
  #shouldRenderSearchStatus(): boolean {
461
468
  return (
462
- this.layout.overflowSearch !== false && (this.items.length > this.maxVisible || this.#filterQuery.length > 0)
469
+ this.layout.overflowSearch !== false && (this.items.length > this.#maxVisible || this.#filterQuery.length > 0)
463
470
  );
464
471
  }
465
472
 
466
473
  #canEditSearch(): boolean {
467
- return this.layout.overflowSearch !== false && this.items.length > this.maxVisible;
474
+ return this.layout.overflowSearch !== false && this.items.length > this.#maxVisible;
468
475
  }
469
476
 
470
477
  #handleSearchInput(keyData: string): boolean {