@oh-my-pi/pi-tui 16.3.8 → 16.3.10

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,19 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.3.10] - 2026-07-06
6
+
7
+ ### Fixed
8
+
9
+ - Registered emergency terminal restore with the postmortem fatal path whenever a real terminal starts, so fatal exits restore raw mode/alternate screen on the normal CLI graph (previously the registration was a side effect of a barrel module the CLI never imported).
10
+
11
+ ## [16.3.9] - 2026-07-06
12
+
13
+ ### Fixed
14
+
15
+ - Fixed autocompletion for absolute paths (such as `/tmp/...` or `/Users/...`) at the start of a prompt, ensuring they fall back to file-path completion instead of being incorrectly treated as slash commands.
16
+ - Updated absolute path autocompletion behavior so that accepting a suggestion inserts the path without submitting the prompt.
17
+
5
18
  ## [16.3.7] - 2026-07-05
6
19
 
7
20
  ### Fixed
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-tui",
4
- "version": "16.3.8",
4
+ "version": "16.3.10",
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": "16.3.8",
41
- "@oh-my-pi/pi-utils": "16.3.8",
40
+ "@oh-my-pi/pi-natives": "16.3.10",
41
+ "@oh-my-pi/pi-utils": "16.3.10",
42
42
  "lru-cache": "11.5.1",
43
43
  "marked": "^18.0.5"
44
44
  },
@@ -425,9 +425,9 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
425
425
  prefix: isMidPromptSkillLookup ? commandText : textBeforeCursor,
426
426
  };
427
427
  }
428
- if (!isMidPromptSkillLookup) return null;
429
- // A mid-prompt slash token with no matching skill may still be an
430
- // absolute path (`see /tmp`); fall through to file-path completion.
428
+ // A slash token with no matching command may still be an absolute
429
+ // path (`/tmp/fo` at prompt start, `see /tmp` mid-prompt); fall
430
+ // through to file-path completion.
431
431
  } else if (!isMidPromptSkillLookup) {
432
432
  // Submitted slash commands own their argument text only when the
433
433
  // matched command accepts args. No-arg slash-looking prompts such
@@ -557,7 +557,11 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
557
557
  // Slash command suggestions can be accepted before the debounced refresh
558
558
  // catches up to newly typed characters. Replace the live command token,
559
559
  // not only the prefix captured when the suggestion list was rendered.
560
- if (findLeadingSlashCommandStart(prefix) !== null && leadingSlashStart !== null) {
560
+ // Absolute-path completions share the leading-slash prefix shape but
561
+ // insert values starting with `/` (or `"` when quoted); those must take
562
+ // the path tail below instead of command-style `/<name> ` insertion.
563
+ const isPathCompletionItem = item.value.startsWith("/") || item.value.startsWith('"');
564
+ if (findLeadingSlashCommandStart(prefix) !== null && leadingSlashStart !== null && !isPathCompletionItem) {
561
565
  const slashPrefix = textBeforeCursor.slice(leadingSlashStart);
562
566
  if (!slashPrefix.includes(" ") && !slashPrefix.slice(1).includes("/")) {
563
567
  const beforeSlash = currentLine.slice(0, leadingSlashStart);
@@ -1159,10 +1159,12 @@ export class Editor implements Component, Focusable {
1159
1159
  return;
1160
1160
  }
1161
1161
 
1162
- // If Enter was pressed on a slash command, apply completion and submit
1162
+ // If Enter was pressed on a slash command (not an absolute-path
1163
+ // completion sharing the leading-slash prefix), apply and submit
1163
1164
  if (
1164
1165
  (kb.matches(data, "tui.input.submit") || data === "\n") &&
1165
- findLeadingSlashCommandStart(this.#autocompletePrefix) !== null
1166
+ findLeadingSlashCommandStart(this.#autocompletePrefix) !== null &&
1167
+ !this.#selectedCompletionIsPath()
1166
1168
  ) {
1167
1169
  // Check for stale autocomplete state due to debounce
1168
1170
  const currentLine = this.#state.lines[this.#state.cursorLine] ?? "";
@@ -2875,7 +2877,10 @@ export class Editor implements Component, Focusable {
2875
2877
  * provider's default slice at `cursorCol - prefix.length` then hits the right span.
2876
2878
  * - Slash branch re-anchors when both the prefix and the current text carry a
2877
2879
  * leading slash command and the current slash token is clean (no whitespace or
2878
- * inner slash), matching `applyCompletion`'s slash-branch guard.
2880
+ * inner slash), matching `applyCompletion`'s slash-branch guard. It only
2881
+ * engages for command-shaped selections: absolute-path completions (`/tmp/fo`
2882
+ * via the no-command-match fall-through) share the leading-slash prefix shape
2883
+ * but must use the live-suffix path rule so the apply slice stays anchored.
2879
2884
  * - `@`-file branch re-anchors via `#extractAtPrefix`; safe when the current text
2880
2885
  * still ends in a whitespace-anchored `@<token>`.
2881
2886
  * - Everything else is stale — accepting it would corrupt the buffer (issue #4295).
@@ -2883,7 +2888,7 @@ export class Editor implements Component, Focusable {
2883
2888
  #autocompletePrefixMatchesCursorText(currentTextBeforeCursor: string): boolean {
2884
2889
  if (currentTextBeforeCursor === this.#autocompletePrefix) return true;
2885
2890
 
2886
- if (findLeadingSlashCommandStart(this.#autocompletePrefix) !== null) {
2891
+ if (findLeadingSlashCommandStart(this.#autocompletePrefix) !== null && !this.#selectedCompletionIsPath()) {
2887
2892
  const currentLeadingStart = findLeadingSlashCommandStart(currentTextBeforeCursor);
2888
2893
  if (currentLeadingStart !== null) {
2889
2894
  const token = currentTextBeforeCursor.slice(currentLeadingStart);
@@ -2899,6 +2904,19 @@ export class Editor implements Component, Focusable {
2899
2904
  return currentTextBeforeCursor.endsWith(this.#autocompletePrefix);
2900
2905
  }
2901
2906
 
2907
+ /**
2908
+ * Whether the current popup selection inserts a file path rather than a
2909
+ * slash command. Leading-slash prefixes are ambiguous: the provider falls
2910
+ * through to absolute-path completion when no command matches, and those
2911
+ * item values start with `/` (or `"` when quoted) while command values are
2912
+ * bare names.
2913
+ */
2914
+ #selectedCompletionIsPath(): boolean {
2915
+ const selected = this.#autocompleteList?.getSelectedItem();
2916
+ if (!selected) return false;
2917
+ return selected.value.startsWith("/") || selected.value.startsWith('"');
2918
+ }
2919
+
2902
2920
  #isSlashCommandNameAutocompleteSelection(): boolean {
2903
2921
  if (this.#autocompleteState !== "regular") {
2904
2922
  return false;
package/src/terminal.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { dlopen, FFIType, ptr } from "bun:ffi";
2
2
  import * as fs from "node:fs";
3
- import { $env, isBunTestRuntime, isTerminalHeadless, logger } from "@oh-my-pi/pi-utils";
3
+ import { $env, isBunTestRuntime, isTerminalHeadless, logger, postmortem } from "@oh-my-pi/pi-utils";
4
4
  import { setKittyProtocolActive } from "./keys";
5
5
  import { StdinBuffer } from "./stdin-buffer";
6
6
  import {
@@ -161,6 +161,15 @@ let terminalEverStarted = false;
161
161
  // jumps to the viewport home, dropping the parent shell prompt on top of the
162
162
  // dead frame after exit.
163
163
  let altScreenActive = false;
164
+ let terminalRestoreRegistered = false;
165
+
166
+ function registerPostmortemTerminalRestore(): void {
167
+ if (terminalRestoreRegistered) return;
168
+ terminalRestoreRegistered = true;
169
+ postmortem.register("terminal-restore", () => {
170
+ emergencyTerminalRestore();
171
+ });
172
+ }
164
173
 
165
174
  /** Record alternate-screen state (called by the TUI on `?1049h`/`?1049l` writes). */
166
175
  export function setAltScreenActive(active: boolean): void {
@@ -523,6 +532,7 @@ export class ProcessTerminal implements Terminal {
523
532
  // escapes never reach the developer's terminal during `bun test`.
524
533
  this.#headless = isTerminalHeadless();
525
534
  if (this.#headless) return;
535
+ registerPostmortemTerminalRestore();
526
536
 
527
537
  // Register for emergency cleanup
528
538
  activeTerminal = this;