@dreki-gg/pi-questionnaire 0.2.5 → 0.2.6

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,11 @@
1
1
  # @dreki-gg/pi-questionnaire
2
2
 
3
+ ## 0.2.6
4
+
5
+ ### Patch Changes
6
+
7
+ - Wrap long questionnaire prompts, options, descriptions, and review rows instead of truncating them at the terminal edge.
8
+
3
9
  ## 0.2.5
4
10
 
5
11
  ### Patch Changes
@@ -1,5 +1,13 @@
1
1
  import type { ExtensionContext } from '@earendil-works/pi-coding-agent';
2
- import { Editor, type EditorTheme, Key, matchesKey, truncateToWidth } from '@earendil-works/pi-tui';
2
+ import {
3
+ Editor,
4
+ type EditorTheme,
5
+ Key,
6
+ matchesKey,
7
+ truncateToWidth,
8
+ visibleWidth,
9
+ wrapTextWithAnsi,
10
+ } from '@earendil-works/pi-tui';
3
11
  import {
4
12
  areAllAnswersValid,
5
13
  createInitialQuestionStateById,
@@ -50,6 +58,35 @@ function getRenderOptions(question: NormalizedQuestion): RenderOption[] {
50
58
  return listed;
51
59
  }
52
60
 
61
+ function pushWrappedText(lines: string[], text: string, width: number) {
62
+ lines.push(...wrapTextWithAnsi(text, Math.max(1, width)));
63
+ }
64
+
65
+ function pushWrappedWithPrefix(
66
+ lines: string[],
67
+ prefix: string,
68
+ text: string,
69
+ width: number,
70
+ continuationPrefix = ' '.repeat(visibleWidth(prefix)),
71
+ ) {
72
+ const availableWidth = Math.max(1, width - visibleWidth(prefix));
73
+ const wrapped = wrapTextWithAnsi(text, availableWidth);
74
+
75
+ if (wrapped.length === 0) {
76
+ lines.push(truncateToWidth(prefix, width));
77
+ return;
78
+ }
79
+
80
+ lines.push(truncateToWidth(`${prefix}${wrapped[0]}`, width));
81
+
82
+ for (const line of wrapped.slice(1)) {
83
+ const continuationWidth = Math.max(1, width - visibleWidth(continuationPrefix));
84
+ for (const continuationLine of wrapTextWithAnsi(line, continuationWidth)) {
85
+ lines.push(truncateToWidth(`${continuationPrefix}${continuationLine}`, width));
86
+ }
87
+ }
88
+ }
89
+
53
90
  function ensureQuestionState(
54
91
  stateById: Record<string, QuestionSelectionState>,
55
92
  questionId: string,
@@ -357,7 +394,7 @@ export async function runQuestionnaireUI(
357
394
  options.length - 1,
358
395
  );
359
396
 
360
- lines.push(truncateToWidth(theme.fg('text', question.prompt), width));
397
+ pushWrappedText(lines, theme.fg('text', question.prompt), width);
361
398
  lines.push('');
362
399
 
363
400
  for (const [index, option] of options.entries()) {
@@ -373,19 +410,17 @@ export async function runQuestionnaireUI(
373
410
  : 'Other';
374
411
  const marker = selection.wasOtherSelected ? '[x]' : '[ ]';
375
412
  const color = selection.wasOtherSelected ? 'accent' : 'text';
376
- lines.push(truncateToWidth(`${prefix}${theme.fg(color, `${marker} ${label}`)}`, width));
413
+ pushWrappedWithPrefix(lines, prefix, theme.fg(color, `${marker} ${label}`), width);
377
414
  continue;
378
415
  }
379
416
 
380
417
  const selected = selection.listedSelectedValues.includes(option.value);
381
418
  const marker = selected ? '[x]' : '[ ]';
382
419
  const color = selected ? 'accent' : 'text';
383
- lines.push(
384
- truncateToWidth(`${prefix}${theme.fg(color, `${marker} ${option.label}`)}`, width),
385
- );
420
+ pushWrappedWithPrefix(lines, prefix, theme.fg(color, `${marker} ${option.label}`), width);
386
421
 
387
422
  if (option.description) {
388
- lines.push(truncateToWidth(` ${theme.fg('muted', option.description)}`, width));
423
+ pushWrappedWithPrefix(lines, ' ', theme.fg('muted', option.description), width);
389
424
  }
390
425
  }
391
426
 
@@ -402,7 +437,7 @@ export async function runQuestionnaireUI(
402
437
  const ready = areAllAnswersValid(questions, uiState.questionStateById);
403
438
  const normalizedAnswers = normalizeAnswers(questions, uiState.questionStateById);
404
439
 
405
- lines.push(truncateToWidth(theme.fg('accent', theme.bold('Review answers')), width));
440
+ pushWrappedText(lines, theme.fg('accent', theme.bold('Review answers')), width);
406
441
  lines.push('');
407
442
 
408
443
  for (const [index, question] of questions.entries()) {
@@ -415,22 +450,21 @@ export async function runQuestionnaireUI(
415
450
  const marker = valid ? theme.fg('success', '■') : theme.fg('warning', '□');
416
451
  const value = formatAnswerValue(answer);
417
452
  const color = valid ? 'text' : 'muted';
418
- lines.push(
419
- truncateToWidth(
420
- `${cursor}${marker} ${theme.fg('accent', `${question.label}:`)} ${theme.fg(color, value)}`,
421
- width,
422
- ),
453
+ pushWrappedWithPrefix(
454
+ lines,
455
+ cursor,
456
+ `${marker} ${theme.fg('accent', `${question.label}:`)} ${theme.fg(color, value)}`,
457
+ width,
423
458
  );
424
459
  }
425
460
 
426
461
  lines.push('');
427
- lines.push(
428
- truncateToWidth(
429
- ready
430
- ? theme.fg('success', 'Press Enter to submit')
431
- : theme.fg('warning', 'Complete all questions before submitting.'),
432
- width,
433
- ),
462
+ pushWrappedText(
463
+ lines,
464
+ ready
465
+ ? theme.fg('success', 'Press Enter to submit')
466
+ : theme.fg('warning', 'Complete all questions before submitting.'),
467
+ width,
434
468
  );
435
469
  }
436
470
 
@@ -448,7 +482,7 @@ export async function runQuestionnaireUI(
448
482
  }
449
483
 
450
484
  lines.push('');
451
- lines.push(truncateToWidth(theme.fg('dim', hint), width));
485
+ pushWrappedText(lines, theme.fg('dim', hint), width);
452
486
  }
453
487
 
454
488
  function render(width: number): string[] {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreki-gg/pi-questionnaire",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Tool-first questionnaire flow for pi with shared tabbed TUI and custom rendering",
5
5
  "keywords": [
6
6
  "pi-package"