@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 +24 -0
- package/package.json +2 -2
- package/view/dialog.ts +30 -1
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.
|
|
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.
|
|
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
|
-
|
|
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
|
}
|