@oh-my-pi/pi-utils 18.8.5 → 18.8.7

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,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.8.7] - 2026-10-09
6
+
7
+ ### Added
8
+
9
+ - Added `getNativeGrammarsDir()`, where the native addon loads downloaded tree-sitter grammars from (`~/.omp/natives/grammars`).
10
+
11
+ ### Fixed
12
+
13
+ - Fixed the virtual terminal misreading private CSI sequences such as kitty keyboard `CSI < u` and modifyOtherKeys `CSI > 4;1 m` as cursor restores or text styling, which garbled replayed PTY output.
14
+
5
15
  ## [18.8.1] - 2026-10-07
6
16
 
7
17
  ### Added
@@ -247,6 +247,12 @@ export declare function getAvatarCacheDir(): string;
247
247
  export declare function getFastembedCacheDir(): string;
248
248
  /** Get the on-demand fastembed runtime install root (~/.omp/cache/fastembed-runtime). */
249
249
  export declare function getFastembedRuntimeDir(): string;
250
+ /**
251
+ * Get the directory the native addon loads downloaded tree-sitter wasm grammars
252
+ * from (`<natives dir>/grammars`, ~/.omp/natives/grammars). The natives loader
253
+ * configures the addon with the same path.
254
+ */
255
+ export declare function getNativeGrammarsDir(): string;
250
256
  /** Get the natives directory. PI_NATIVES_DIR overrides the usual cache root; relative values are ignored. */
251
257
  export declare function getNativesDir(): string;
252
258
  /** Get the stats database path (~/.omp/stats.db). */
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Owned temporary directory, removed via `remove()` or `using`/`await using`.
3
+ *
4
+ * Prefixes follow `mkdtemp`: `"@name-"` creates under the OS temp dir, any other
5
+ * prefix is a path (a bare `"name-"` lands in the process cwd), and no prefix
6
+ * means `"@pi-temp-"`.
7
+ */
1
8
  export declare class TempDir {
2
9
  #private;
3
10
  private constructor();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-utils",
3
- "version": "18.8.5",
3
+ "version": "18.8.7",
4
4
  "description": "Shared utilities for pi packages",
5
5
  "keywords": [
6
6
  "cli",
@@ -57,7 +57,7 @@
57
57
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
58
58
  },
59
59
  "dependencies": {
60
- "@oh-my-pi/pi-natives": "18.8.5"
60
+ "@oh-my-pi/pi-natives": "18.8.7"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/bun": "^1.3.14"
package/src/dirs.ts CHANGED
@@ -865,6 +865,15 @@ export function getFastembedRuntimeDir(): string {
865
865
  return dirs.rootSubdir(path.join("cache", "fastembed-runtime"), "cache");
866
866
  }
867
867
 
868
+ /**
869
+ * Get the directory the native addon loads downloaded tree-sitter wasm grammars
870
+ * from (`<natives dir>/grammars`, ~/.omp/natives/grammars). The natives loader
871
+ * configures the addon with the same path.
872
+ */
873
+ export function getNativeGrammarsDir(): string {
874
+ return path.join(getNativesDir(), "grammars");
875
+ }
876
+
868
877
  /** Get the natives directory. PI_NATIVES_DIR overrides the usual cache root; relative values are ignored. */
869
878
  export function getNativesDir(): string {
870
879
  return resolveAbsoluteDir(process.env.PI_NATIVES_DIR) ?? dirs.rootSubdir("natives", "cache");
package/src/temp.ts CHANGED
@@ -3,6 +3,13 @@ import * as fsPromises from "node:fs/promises";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
5
 
6
+ /**
7
+ * Owned temporary directory, removed via `remove()` or `using`/`await using`.
8
+ *
9
+ * Prefixes follow `mkdtemp`: `"@name-"` creates under the OS temp dir, any other
10
+ * prefix is a path (a bare `"name-"` lands in the process cwd), and no prefix
11
+ * means `"@pi-temp-"`.
12
+ */
6
13
  export class TempDir {
7
14
  #path: string;
8
15
  private constructor(path: string) {
@@ -319,17 +319,38 @@ export class Terminal {
319
319
  }
320
320
  }
321
321
 
322
+ /**
323
+ * Dispatches a CSI sequence on its whole identifier (private prefix,
324
+ * intermediates, final), dropping the ones without a handler as xterm does:
325
+ * `CSI < u` pops the kitty keyboard stack rather than restoring the cursor,
326
+ * `CSI > 4;1 m` sets modifyOtherKeys rather than SGR, and `CSI 0 SP q` sets
327
+ * the cursor shape rather than anything this buffer models.
328
+ */
322
329
  #handleCsi(raw: string, final: string): void {
323
- const privateMode = raw.startsWith("?");
324
- const greater = raw.startsWith(">");
325
- const body = privateMode || greater ? raw.slice(1) : raw;
326
- const params =
327
- parseCsiParams(body) ??
328
- body
329
- .replaceAll(":", ";")
330
- .split(";")
331
- .map(value => (value === "" ? 0 : Number(value)));
330
+ const marker = raw.charCodeAt(0);
331
+ // `<`, `=`, `>`, `?` open a private sequence family.
332
+ const prefixed = marker >= 0x3c && marker <= 0x3f;
333
+ // Null when intermediates or a misplaced private marker follow the parameters.
334
+ const params = parseCsiParams(prefixed ? raw.slice(1) : raw);
335
+ if (!params) return;
332
336
  const first = params[0] ?? 0;
337
+ if (prefixed) {
338
+ if (marker !== 0x3f) return;
339
+ switch (final) {
340
+ case "h":
341
+ case "l":
342
+ this.#setModes(params, true, final === "h");
343
+ break;
344
+ // DECSED/DECSEL: no cell is ever protected, so selective erase is plain erase.
345
+ case "J":
346
+ this.#eraseDisplay(first);
347
+ break;
348
+ case "K":
349
+ this.#eraseLine(first);
350
+ break;
351
+ }
352
+ return;
353
+ }
333
354
  const amount = Math.max(1, first);
334
355
  switch (final) {
335
356
  case "A":
@@ -399,7 +420,7 @@ export class Terminal {
399
420
  this.#setRendition(params);
400
421
  break;
401
422
  case "r":
402
- if (!privateMode) this.#setScrollRegion(params);
423
+ this.#setScrollRegion(params);
403
424
  break;
404
425
  case "s":
405
426
  this.#saveCursor();
@@ -409,14 +430,13 @@ export class Terminal {
409
430
  break;
410
431
  case "h":
411
432
  case "l":
412
- this.#setModes(params, privateMode, final === "h");
433
+ this.#setModes(params, false, final === "h");
413
434
  break;
414
435
  case "n":
415
- if (!privateMode && first === 6)
416
- this.#emitData(`\x1b[${this.#active.cursorY + 1};${this.#active.cursorX + 1}R`);
436
+ if (first === 6) this.#emitData(`\x1b[${this.#active.cursorY + 1};${this.#active.cursorX + 1}R`);
417
437
  break;
418
438
  case "c":
419
- if (!greater) this.#emitData("\x1b[?1;2c");
439
+ this.#emitData("\x1b[?1;2c");
420
440
  break;
421
441
  }
422
442
  }