@hank-warren/pi-ask-user-question 0.5.0 → 0.5.2

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,5 +1,29 @@
1
1
  # @hank-warren/pi-ask-user-question
2
2
 
3
+ ## 0.5.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 3fbf632: Make backspace delete a whole character rather than half a surrogate pair. Both text-entry surfaces sliced UTF-16 code units, so one backspace after an emoji left a lone surrogate — an ill-formed string that was then submitted as an approval note or a free-text answer.
8
+
9
+ `pi-permission-selector` adds a code-point-aware `removeLastCharacter` export (additive; the frozen shared surface gains a name and loses none), and `pi-ask-user-question` uses it for the custom-answer field.
10
+
11
+ - Updated dependencies [3fbf632]
12
+ - @hank-warren/pi-permission-selector@1.3.0
13
+
14
+ ## 0.5.1
15
+
16
+ ### Patch Changes
17
+
18
+ - 464fe1e: fix: pasting into the ask-user-question custom-answer field was silently dropped
19
+
20
+ pi-tui re-wraps pastes in bracketed-paste markers (`\x1b[200~ … \x1b[201~`) before they reach `handleInput`. The questionnaire dialog's free-text field only accepted printable chunks, so a paste — which contains ESC — fell through every key predicate into the inert branch and vanished. The note editor was unaffected because `handleCommentKey` buffers its own pastes.
21
+
22
+ The bracketed-paste state machine inside `handleCommentKey` is now extracted as a new shared export, `consumePasteChunk` (pi-permission-selector minor), and the custom-answer field routes input through it: single-chunk and chunk-spanning pastes insert, multi-line pastes flatten to one line, and input trailing the end marker is re-dispatched as ordinary keys.
23
+
24
+ - Updated dependencies [464fe1e]
25
+ - @hank-warren/pi-permission-selector@1.2.0
26
+
3
27
  ## 0.5.0
4
28
 
5
29
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hank-warren/pi-ask-user-question",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Structured questionnaire tool for Pi with numbered options, digit hotkeys and Tab-to-comment, composed from the shared permission-selector component.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -46,7 +46,7 @@
46
46
  "CHANGELOG.md"
47
47
  ],
48
48
  "dependencies": {
49
- "@hank-warren/pi-permission-selector": "^1.1.0"
49
+ "@hank-warren/pi-permission-selector": "^1.3.0"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@earendil-works/pi-coding-agent": "*",
package/view/dialog.ts CHANGED
@@ -39,6 +39,7 @@
39
39
  */
40
40
 
41
41
  import {
42
+ consumePasteChunk,
42
43
  isBackspaceKey,
43
44
  isCharKey,
44
45
  isEnterKey,
@@ -48,6 +49,7 @@ import {
48
49
  isRightKey,
49
50
  isShiftTabKey,
50
51
  isTabKey,
52
+ removeLastCharacter,
51
53
  } from "@hank-warren/pi-permission-selector/keys.ts";
52
54
  import { OptionSelector, type SelectorOption } from "@hank-warren/pi-permission-selector/selector.ts";
53
55
  import {
@@ -99,6 +101,14 @@ const PREVIEW_MAX_ROWS = 12;
99
101
  interface TabState {
100
102
  selector: OptionSelector;
101
103
  customText?: string;
104
+ /**
105
+ * In-progress bracketed paste aimed at the custom-answer field. pi-tui wraps
106
+ * pastes in `\x1b[200~ … \x1b[201~` and the markers can span input chunks;
107
+ * a paste chunk fails every key predicate, so without this buffer pasting
108
+ * into the field was silently dropped (the note editor was unaffected —
109
+ * handleCommentKey buffers its own pastes).
110
+ */
111
+ pasteBuffer?: string;
102
112
  pendingNotes?: string;
103
113
  /**
104
114
  * Labels checked alongside the sentinel row on a multi-select question, held
@@ -371,10 +381,26 @@ export class QuestionnaireDialog {
371
381
  // Free-text mode. Every check below MUST use a shared predicate — see
372
382
  // rule 1. Esc unwinds to the option list rather than cancelling the
373
383
  // dialog: one Esc never discards more than one layer.
384
+ //
385
+ // Bracketed pastes come first: a paste chunk contains ESC, so it would
386
+ // otherwise fall through every predicate into the inert branch and vanish.
387
+ const paste = consumePasteChunk(tab.pasteBuffer, keyData);
388
+ if (paste !== undefined) {
389
+ tab.pasteBuffer = paste.buffer;
390
+ if (paste.text !== undefined) {
391
+ tab.customText += paste.text;
392
+ this.repaint();
393
+ }
394
+ // Ordinary input after the end marker (e.g. a trailing Enter) goes back
395
+ // through normal key handling.
396
+ if (paste.rest) this.handleInput(paste.rest);
397
+ return;
398
+ }
374
399
  if (isEscapeKey(keyData)) {
375
400
  // Back to the option list. The selector still holds every tick, so a
376
401
  // multi-select question is exactly as the user left it.
377
402
  tab.customText = undefined;
403
+ tab.pasteBuffer = undefined;
378
404
  tab.pendingNotes = undefined;
379
405
  tab.pendingSelected = undefined;
380
406
  this.repaint();
@@ -394,13 +420,16 @@ export class QuestionnaireDialog {
394
420
  this.session.recordAnswer(text, { custom: true, notes: tab.pendingNotes });
395
421
  }
396
422
  tab.customText = undefined;
423
+ tab.pasteBuffer = undefined;
397
424
  tab.pendingNotes = undefined;
398
425
  tab.pendingSelected = undefined;
399
426
  this.afterAnswer();
400
427
  return;
401
428
  }
402
429
  if (isBackspaceKey(keyData)) {
403
- tab.customText = tab.customText.slice(0, -1);
430
+ // Code-point aware: a UTF-16 slice would split a surrogate pair and
431
+ // leave a lone surrogate in the answer that reaches the model.
432
+ tab.customText = removeLastCharacter(tab.customText);
404
433
  this.repaint();
405
434
  return;
406
435
  }