@oh-my-pi/pi-tui 18.1.10 → 18.1.12

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,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.1.12] - 2026-09-06
6
+
7
+ ### Fixed
8
+
9
+ - Avoid inserting a trailing space when auto-completing directory paths with `@`, and keep autocomplete open when accepting a directory with Tab or Enter.
10
+ - Horizontal wheel reports (the sideways drift of a two-finger trackpad scroll) no longer decode as a vertical wheel direction, so fullscreen selectors such as `/copy` and the rewind picker stop jumping up and back down at the end of a scroll gesture.
11
+
5
12
  ## [18.1.9] - 2026-09-04
6
13
 
7
14
  ### Added
@@ -6,6 +6,13 @@
6
6
  */
7
7
  export declare function findLeadingSlashCommandStart(text: string): number | null;
8
8
  export declare function findTrailingSlashCommandStart(text: string): number | null;
9
+ /**
10
+ * Whether an autocomplete value represents a directory: trailing slash or
11
+ * backslash, optionally followed by a closing quote for quoted paths.
12
+ * Shared by the provider suffix logic and the editor chain-on-accept
13
+ * behavior so Tab and Enter acceptance stay in sync.
14
+ */
15
+ export declare function isDirectoryCompletionValue(value: string): boolean;
9
16
  export interface AutocompleteItem {
10
17
  value: string;
11
18
  label: string;
@@ -17,7 +17,12 @@ export interface SgrMouseEvent {
17
17
  row: number;
18
18
  /** True for a release report (`m` suffix). */
19
19
  release: boolean;
20
- /** Wheel direction: -1 up, 1 down, null when not a wheel event. */
20
+ /**
21
+ * Vertical wheel direction: -1 up, 1 down, null when not a vertical wheel
22
+ * event. Horizontal wheel reports (buttons 66/67, the sideways drift of a
23
+ * two-finger trackpad scroll) are not a direction: treating them as one
24
+ * scrolled the selectors up and back down at the end of a gesture.
25
+ */
21
26
  wheel: -1 | 1 | null;
22
27
  /** True when the pointer moved (hover or drag) rather than clicked. */
23
28
  motion: boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-tui",
4
- "version": "18.1.10",
4
+ "version": "18.1.12",
5
5
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Stencil Labs, Inc.",
@@ -37,8 +37,8 @@
37
37
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
38
38
  },
39
39
  "dependencies": {
40
- "@oh-my-pi/pi-natives": "18.1.10",
41
- "@oh-my-pi/pi-utils": "18.1.10"
40
+ "@oh-my-pi/pi-natives": "18.1.12",
41
+ "@oh-my-pi/pi-utils": "18.1.12"
42
42
  },
43
43
  "devDependencies": {
44
44
  "kitty-vt-wasm": "^0.2.0"
@@ -111,6 +111,16 @@ function parsePathPrefix(prefix: string): { rawPrefix: string; isAtPrefix: boole
111
111
  return { rawPrefix: prefix, isAtPrefix: false, isQuotedPrefix: false };
112
112
  }
113
113
 
114
+ /**
115
+ * Whether an autocomplete value represents a directory: trailing slash or
116
+ * backslash, optionally followed by a closing quote for quoted paths.
117
+ * Shared by the provider suffix logic and the editor chain-on-accept
118
+ * behavior so Tab and Enter acceptance stay in sync.
119
+ */
120
+ export function isDirectoryCompletionValue(value: string): boolean {
121
+ return /[\\/]["']?$/.test(value);
122
+ }
123
+
114
124
  function buildCompletionValue(
115
125
  path: string,
116
126
  options: { isDirectory: boolean; isAtPrefix: boolean; isQuotedPrefix: boolean },
@@ -738,14 +748,16 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
738
748
  beforePrefix = currentLine.slice(0, cursorCol - liveAtPrefix.length);
739
749
  }
740
750
  // This is a file attachment completion
741
- const newLine = `${beforePrefix + item.value} ${afterCursor}`;
751
+ const isDirectory = isDirectoryCompletionValue(item.value);
752
+ const suffix = isDirectory ? "" : " ";
753
+ const newLine = `${beforePrefix + item.value}${suffix}${afterCursor}`;
742
754
  const newLines = [...lines];
743
755
  newLines[cursorLine] = newLine;
744
756
 
745
757
  return {
746
758
  lines: newLines,
747
759
  cursorLine,
748
- cursorCol: beforePrefix.length + item.value.length + 1, // +1 for space
760
+ cursorCol: beforePrefix.length + item.value.length + suffix.length,
749
761
  };
750
762
  }
751
763
 
@@ -4,6 +4,7 @@ import {
4
4
  type AutocompleteProvider,
5
5
  findLeadingSlashCommandStart,
6
6
  findTrailingSlashCommandStart,
7
+ isDirectoryCompletionValue,
7
8
  midPromptSkillTokenMatches,
8
9
  SKILL_NAMESPACE,
9
10
  } from "../autocomplete";
@@ -1467,7 +1468,8 @@ export class Editor implements Component, Focusable {
1467
1468
  return;
1468
1469
  }
1469
1470
  if (selected && this.#autocompleteProvider) {
1470
- const shouldChainSlashCommandAutocomplete = this.#isSlashCommandNameAutocompleteSelection();
1471
+ const shouldChainAutocomplete =
1472
+ this.#isSlashCommandNameAutocompleteSelection() || isDirectoryCompletionValue(selected.value);
1471
1473
  const result = this.#autocompleteProvider.applyCompletion(
1472
1474
  this.#state.lines,
1473
1475
  this.#state.cursorLine,
@@ -1489,8 +1491,8 @@ export class Editor implements Component, Focusable {
1489
1491
 
1490
1492
  result.onApplied?.();
1491
1493
 
1492
- if (shouldChainSlashCommandAutocomplete && this.#isCompletedSlashCommandAtCursor()) {
1493
- void this.#tryTriggerAutocomplete();
1494
+ if (shouldChainAutocomplete) {
1495
+ queueMicrotask(() => void this.#tryTriggerAutocomplete());
1494
1496
  }
1495
1497
  }
1496
1498
  return;
@@ -1543,6 +1545,7 @@ export class Editor implements Component, Focusable {
1543
1545
  } else {
1544
1546
  if (selected && this.#autocompleteProvider) {
1545
1547
  const shouldChainSlashCommandAutocomplete = this.#isSlashCommandNameAutocompleteSelection();
1548
+ const shouldChainDirectoryCompletion = isDirectoryCompletionValue(selected.value);
1546
1549
  const result = this.#autocompleteProvider.applyCompletion(
1547
1550
  this.#state.lines,
1548
1551
  this.#state.cursorLine,
@@ -1563,7 +1566,9 @@ export class Editor implements Component, Focusable {
1563
1566
  }
1564
1567
 
1565
1568
  result.onApplied?.();
1566
- if (shouldChainSlashCommandAutocomplete && this.#isCompletedSlashCommandAtCursor()) {
1569
+ if (shouldChainDirectoryCompletion) {
1570
+ queueMicrotask(() => void this.#tryTriggerAutocomplete());
1571
+ } else if (shouldChainSlashCommandAutocomplete && this.#isCompletedSlashCommandAtCursor()) {
1567
1572
  void this.#tryTriggerAutocomplete();
1568
1573
  }
1569
1574
  }
package/src/mouse.ts CHANGED
@@ -18,7 +18,12 @@ export interface SgrMouseEvent {
18
18
  row: number;
19
19
  /** True for a release report (`m` suffix). */
20
20
  release: boolean;
21
- /** Wheel direction: -1 up, 1 down, null when not a wheel event. */
21
+ /**
22
+ * Vertical wheel direction: -1 up, 1 down, null when not a vertical wheel
23
+ * event. Horizontal wheel reports (buttons 66/67, the sideways drift of a
24
+ * two-finger trackpad scroll) are not a direction: treating them as one
25
+ * scrolled the selectors up and back down at the end of a gesture.
26
+ */
22
27
  wheel: -1 | 1 | null;
23
28
  /** True when the pointer moved (hover or drag) rather than clicked. */
24
29
  motion: boolean;
@@ -38,7 +43,7 @@ export function parseSgrMouse(data: string): SgrMouseEvent | null {
38
43
  const col = Number(match[2]) - 1;
39
44
  const row = Number(match[3]) - 1;
40
45
  const release = match[4] === "m";
41
- const wheel = button & 64 ? ((button & 1 ? 1 : -1) as 1 | -1) : null;
46
+ const wheel = button & 64 && !(button & 2) ? ((button & 1 ? 1 : -1) as 1 | -1) : null;
42
47
  const motion = (button & 32) !== 0 && wheel === null;
43
48
  const leftClick = !release && wheel === null && !motion && (button & 3) === 0;
44
49
  return { button, col, row, release, wheel, motion, leftClick };