pi-copy-message 1.0.10 → 2.0.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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 2.0.0 - 2026-08-06
4
+
5
+ - Set pi 0.84.0 as the minimum supported version and refresh the local coding-agent and TUI development baseline.
6
+ - Honor configured `tui.select.pageUp` and `tui.select.pageDown` bindings, with Ctrl-modified page and jump controls that remain available in fullscreen mode.
7
+ - Decode Kitty CSI-u printable input so type-to-filter search works in terminals using Kitty keyboard disambiguation.
8
+ - Validate the package and its core copy flows with an isolated Pi 0.84.0 installation.
9
+
10
+ ## 1.0.11 - 2026-07-16
11
+
12
+ - Refresh the local Pi development lock and compatibility baseline to 0.80.9; the unified model runtime and additive Kimi/model-catalog changes do not affect the message-history picker.
4
13
 
5
14
  ## 1.0.10 - 2026-07-14
6
15
 
package/README.md CHANGED
@@ -93,8 +93,10 @@ Copy with role and timestamp metadata instead of raw text only:
93
93
  |---|---|
94
94
  | Configured `tui.select.up` (default: `↑`) | Move to older visible message |
95
95
  | Configured `tui.select.down` (default: `↓`) | Move to newer visible message |
96
- | `Home` | Jump to oldest visible message |
97
- | `End` | Jump to newest visible message |
96
+ | Configured `tui.select.pageUp` / `tui.select.pageDown` (default: `PageUp` / `PageDown`) | Move by one visible page in regular mode |
97
+ | `Ctrl+PageUp` / `Ctrl+PageDown` | Move by one visible page in fullscreen mode |
98
+ | `Home` / `End` | Jump to oldest/newest visible message in regular mode |
99
+ | `Ctrl+Home` / `Ctrl+End` | Jump to oldest/newest visible message in fullscreen mode |
98
100
  | Type text | Filter visible messages |
99
101
  | `time:<term>` | Search timestamps |
100
102
  | `Backspace` | Delete one search character |
@@ -122,8 +124,8 @@ Copy with role and timestamp metadata instead of raw text only:
122
124
 
123
125
  ## Compatibility
124
126
 
125
- - Tested with pi 0.80.7
126
- - Pi 0.80.7 includes `Ctrl+X` for copying the latest assistant response; this extension remains useful for searchable history, other roles, metadata, and direct selectors.
127
+ - Requires and is tested with Pi 0.84.0 or later
128
+ - Pi includes `Ctrl+X` for copying the latest assistant response; this extension remains useful for searchable history, other roles, metadata, and direct selectors.
127
129
  - Supported Node.js range for local repo tooling: `>=22.19.0`
128
130
  - `.nvmrc` pins Node 22.19.0 for local development
129
131
 
@@ -139,4 +141,4 @@ npm run check
139
141
  Key files:
140
142
 
141
143
  - `extensions/copy-message.ts` — publishable extension implementation
142
- - `tests/copy-message.test.ts` — regression tests for command wiring, filtering, search, jumps, and clipboard behavior
144
+ - `tests/copy-message.test.ts` — regression tests for command wiring, filtering, search, navigation, and copy formatting
@@ -1,7 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import type { ExtensionAPI, ExtensionCommandContext, KeybindingsManager } from "@earendil-works/pi-coding-agent";
3
- import type { AutocompleteItem } from "@earendil-works/pi-tui";
4
- import { matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
3
+ import type { AutocompleteItem, TuiMode } from "@earendil-works/pi-tui";
4
+ import { decodeKittyPrintable, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
5
5
 
6
6
  const MAX_VISIBLE_MESSAGES = 8;
7
7
  const MAX_PEEK_LINES = 16;
@@ -370,7 +370,7 @@ function bindingHint(keybindings: PickerKeybindings | undefined, action: "up" |
370
370
  return keys.reduce((shortest, key) => visibleWidth(key) < visibleWidth(shortest) ? key : shortest, keys[0] ?? "unbound");
371
371
  }
372
372
 
373
- function helpLines(width: number, keybindings?: PickerKeybindings): string[] {
373
+ function helpLines(width: number, keybindings?: PickerKeybindings, tuiMode: TuiMode = "regular"): string[] {
374
374
  const up = bindingHint(keybindings, "up");
375
375
  const down = bindingHint(keybindings, "down");
376
376
  const confirm = bindingHint(keybindings, "confirm");
@@ -379,6 +379,8 @@ function helpLines(width: number, keybindings?: PickerKeybindings): string[] {
379
379
  !keybindings ||
380
380
  !(keybindings.matches(data, "tui.select.up") ||
381
381
  keybindings.matches(data, "tui.select.down") ||
382
+ keybindings.matches(data, "tui.select.pageUp") ||
383
+ keybindings.matches(data, "tui.select.pageDown") ||
382
384
  keybindings.matches(data, "tui.select.confirm") ||
383
385
  keybindings.matches(data, "tui.select.cancel"));
384
386
  const peek = available("\t") ? "Tab peek" : undefined;
@@ -389,10 +391,15 @@ function helpLines(width: number, keybindings?: PickerKeybindings): string[] {
389
391
  ].filter(({ data }) => available(data));
390
392
  const filterHint = filters.length > 0 ? `Ctrl+${filters.map(({ hint }) => hint).join("/")} filters` : undefined;
391
393
  const meta = available("\x1bm") ? "Alt+M meta" : undefined;
392
- const jumps = [
393
- { hint: "Home", data: "\x1b[H" },
394
- { hint: "End", data: "\x1b[F" },
395
- ].filter(({ data }) => available(data));
394
+ const jumps = (tuiMode === "fullscreen"
395
+ ? [
396
+ { hint: "Ctrl+Home", data: "\x1b[1;5H" },
397
+ { hint: "Ctrl+End", data: "\x1b[1;5F" },
398
+ ]
399
+ : [
400
+ { hint: "Home", data: "\x1b[H" },
401
+ { hint: "End", data: "\x1b[F" },
402
+ ]).filter(({ data }) => available(data));
396
403
  const jumpHint = jumps.length > 0 ? `${jumps.map(({ hint }) => hint).join("/")} jump` : undefined;
397
404
  const join = (...hints: Array<string | undefined>) => hints.filter(Boolean).join(" · ");
398
405
  const core = width < 74 ? [`${up}/${down} nav`, `${confirm} copy`, `${cancel} cancel`] : [`${up} older`, `${down} newer`, `${confirm} copy`, `${cancel} cancel`];
@@ -445,7 +452,7 @@ export class CopyMessagePickerState {
445
452
  return selected ? formatMessageForCopy(selected, this.format) : undefined;
446
453
  }
447
454
 
448
- render(width: number, theme: CopyMessageTheme, keybindings?: PickerKeybindings): string[] {
455
+ render(width: number, theme: CopyMessageTheme, keybindings?: PickerKeybindings, tuiMode: TuiMode = "regular"): string[] {
449
456
  const maxVisible = Math.min(this.visibleMessages.length, MAX_VISIBLE_MESSAGES);
450
457
  const start = maxVisible === 0 ? 0 : Math.max(0, Math.min(this.selectedIndex - maxVisible + 1, this.visibleMessages.length - maxVisible));
451
458
  const end = Math.min(this.visibleMessages.length, start + maxVisible);
@@ -476,7 +483,7 @@ export class CopyMessagePickerState {
476
483
  const position = this.visibleMessages.length === 0 ? "0/0" : `${this.selectedIndex + 1}/${this.visibleMessages.length}`;
477
484
  lines.push(`${theme.fg("dim", `(${position})`)} · ${userState} · ${assistantState} · ${toolState} · ${formatState} · ${searchState}`);
478
485
  lines.push("");
479
- lines.push(...helpLines(width, keybindings).map((line) => hotkeyHint(theme, line)));
486
+ lines.push(...helpLines(width, keybindings, tuiMode).map((line) => hotkeyHint(theme, line)));
480
487
  lines.push("");
481
488
  return lines.map((line) => truncateToWidth(line, width, ""));
482
489
  }
@@ -490,6 +497,14 @@ export class CopyMessagePickerState {
490
497
  this.move(1);
491
498
  return "render";
492
499
  }
500
+ if (keybindings?.matches(data, "tui.select.pageUp")) {
501
+ this.move(-MAX_VISIBLE_MESSAGES);
502
+ return "render";
503
+ }
504
+ if (keybindings?.matches(data, "tui.select.pageDown")) {
505
+ this.move(MAX_VISIBLE_MESSAGES);
506
+ return "render";
507
+ }
493
508
  if (keybindings?.matches(data, "tui.select.confirm")) {
494
509
  return this.visibleMessages.length > 0 ? "copy" : "none";
495
510
  }
@@ -521,8 +536,9 @@ export class CopyMessagePickerState {
521
536
  this.setSearch(this.search.slice(0, -1));
522
537
  return "render";
523
538
  }
524
- if (isPrintableSearchInput(data)) {
525
- this.setSearch(this.search + data);
539
+ const printable = decodeKittyPrintable(data) ?? data;
540
+ if (isPrintableSearchInput(printable)) {
541
+ this.setSearch(this.search + printable);
526
542
  return "render";
527
543
  }
528
544
  if (!keybindings && matchesKey(data, "up")) {
@@ -533,6 +549,30 @@ export class CopyMessagePickerState {
533
549
  this.move(1);
534
550
  return "render";
535
551
  }
552
+ if (!keybindings && matchesKey(data, "pageUp")) {
553
+ this.move(-MAX_VISIBLE_MESSAGES);
554
+ return "render";
555
+ }
556
+ if (!keybindings && matchesKey(data, "pageDown")) {
557
+ this.move(MAX_VISIBLE_MESSAGES);
558
+ return "render";
559
+ }
560
+ if (matchesKey(data, "ctrl+pageUp")) {
561
+ this.move(-MAX_VISIBLE_MESSAGES);
562
+ return "render";
563
+ }
564
+ if (matchesKey(data, "ctrl+pageDown")) {
565
+ this.move(MAX_VISIBLE_MESSAGES);
566
+ return "render";
567
+ }
568
+ if (matchesKey(data, "ctrl+home")) {
569
+ this.jumpToTop();
570
+ return "render";
571
+ }
572
+ if (matchesKey(data, "ctrl+end")) {
573
+ this.jumpToBottom();
574
+ return "render";
575
+ }
536
576
  if (matchesKey(data, "home")) {
537
577
  this.jumpToTop();
538
578
  return "render";
@@ -631,7 +671,7 @@ async function pickMessage(ctx: ExtensionCommandContext, messages: CopyableMessa
631
671
  const state = new CopyMessagePickerState(messages, initialFormat);
632
672
  return {
633
673
  render(width: number) {
634
- return state.render(width, theme, keybindings);
674
+ return state.render(width, theme, keybindings, tui.mode);
635
675
  },
636
676
  invalidate() {},
637
677
  handleInput(data: string) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-copy-message",
3
- "version": "1.0.10",
3
+ "version": "2.0.0",
4
4
  "description": "Pi extension with a searchable TUI picker for copying raw session messages",
5
5
  "author": "Mitch Fultz (https://github.com/fitchmultz)",
6
6
  "license": "MIT",
@@ -44,8 +44,8 @@
44
44
  "LICENSE"
45
45
  ],
46
46
  "devDependencies": {
47
- "@earendil-works/pi-coding-agent": "^0.80.7",
48
- "@earendil-works/pi-tui": "^0.80.7",
47
+ "@earendil-works/pi-coding-agent": "0.84.0",
48
+ "@earendil-works/pi-tui": "0.84.0",
49
49
  "@types/node": "^25.9.1",
50
50
  "tsx": "^4.22.4",
51
51
  "typescript": "^6.0.3"
@@ -59,5 +59,13 @@
59
59
  "peerDependencies": {
60
60
  "@earendil-works/pi-coding-agent": "*",
61
61
  "@earendil-works/pi-tui": "*"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "@earendil-works/pi-coding-agent": {
65
+ "optional": true
66
+ },
67
+ "@earendil-works/pi-tui": {
68
+ "optional": true
69
+ }
62
70
  }
63
71
  }