pi-ask-user 0.5.0 → 0.5.1
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/index.ts +57 -37
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -27,7 +27,11 @@ import {
|
|
|
27
27
|
truncateToWidth,
|
|
28
28
|
wrapTextWithAnsi,
|
|
29
29
|
} from "@mariozechner/pi-tui";
|
|
30
|
-
import { renderSingleSelectRows
|
|
30
|
+
import { renderSingleSelectRows } from "./single-select-layout";
|
|
31
|
+
|
|
32
|
+
import { createRequire } from "node:module";
|
|
33
|
+
const _require = createRequire(import.meta.url);
|
|
34
|
+
const ASK_USER_VERSION: string = (_require("./package.json") as { version: string }).version;
|
|
31
35
|
|
|
32
36
|
type AskOptionInput = QuestionOption | string;
|
|
33
37
|
|
|
@@ -124,13 +128,25 @@ class BoxBorderTop implements Component {
|
|
|
124
128
|
|
|
125
129
|
class BoxBorderBottom implements Component {
|
|
126
130
|
private color: (s: string) => string;
|
|
127
|
-
|
|
131
|
+
private label?: string;
|
|
132
|
+
private labelColor?: (s: string) => string;
|
|
133
|
+
constructor(color: (s: string) => string, label?: string, labelColor?: (s: string) => string) {
|
|
128
134
|
this.color = color;
|
|
135
|
+
this.label = label;
|
|
136
|
+
this.labelColor = labelColor;
|
|
129
137
|
}
|
|
130
138
|
invalidate(): void {}
|
|
131
139
|
render(width: number): string[] {
|
|
132
140
|
const inner = Math.max(0, width - 2);
|
|
133
|
-
|
|
141
|
+
if (!this.label || inner < this.label.length + 4) {
|
|
142
|
+
return [this.color(`╰${"─".repeat(inner)}╯`)];
|
|
143
|
+
}
|
|
144
|
+
const tag = ` ${this.label} `;
|
|
145
|
+
const leftDashes = inner - tag.length - 1;
|
|
146
|
+
const style = this.labelColor ?? this.color;
|
|
147
|
+
return [
|
|
148
|
+
this.color("╰" + "─".repeat(Math.max(0, leftDashes))) + style(tag) + this.color("─╯"),
|
|
149
|
+
];
|
|
134
150
|
}
|
|
135
151
|
}
|
|
136
152
|
|
|
@@ -481,53 +497,53 @@ class WrappedSingleSelectList implements Component {
|
|
|
481
497
|
private buildPreviewLines(width: number, filteredOptions: QuestionOption[], maxLines: number): string[] {
|
|
482
498
|
if (maxLines <= 0) return [];
|
|
483
499
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
489
|
-
};
|
|
490
|
-
const pushBlank = (): void => {
|
|
491
|
-
if (lines.length === 0 || lines[lines.length - 1] !== "") {
|
|
492
|
-
lines.push("");
|
|
493
|
-
}
|
|
494
|
-
};
|
|
500
|
+
let mdTheme: MarkdownTheme | undefined;
|
|
501
|
+
try {
|
|
502
|
+
mdTheme = getMarkdownTheme();
|
|
503
|
+
} catch {}
|
|
495
504
|
|
|
496
|
-
|
|
497
|
-
|
|
505
|
+
// Build a markdown string for the preview content
|
|
506
|
+
let md = "";
|
|
498
507
|
|
|
499
508
|
if (this.isFreeformRow(this.selectedIndex, filteredOptions)) {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
pushBlank();
|
|
504
|
-
pushWrapped("Use this when none of the listed options fit.", (line) => this.theme.fg("muted", line));
|
|
509
|
+
md += "## Custom response\n\n";
|
|
510
|
+
md += "Open the editor to write **any** answer.\n\n";
|
|
511
|
+
md += "*Use this when none of the listed options fit.*\n";
|
|
505
512
|
if (this.searchQuery) {
|
|
506
|
-
|
|
507
|
-
pushWrapped(`Current filter: ${this.searchQuery}`, (line) => this.theme.fg("dim", line));
|
|
513
|
+
md += `\n> Current filter: \`${this.searchQuery}\`\n`;
|
|
508
514
|
}
|
|
509
515
|
} else {
|
|
510
516
|
const selected = filteredOptions[this.selectedIndex];
|
|
511
517
|
if (!selected) {
|
|
512
|
-
|
|
518
|
+
md += "*No option selected*\n";
|
|
513
519
|
} else {
|
|
514
|
-
|
|
515
|
-
pushBlank();
|
|
520
|
+
md += `## ${selected.title}\n\n`;
|
|
516
521
|
if (selected.description?.trim()) {
|
|
517
|
-
|
|
522
|
+
md += `${selected.description}\n`;
|
|
518
523
|
} else {
|
|
519
|
-
|
|
524
|
+
md += "*No additional details provided for this option.*\n";
|
|
520
525
|
}
|
|
521
|
-
|
|
522
|
-
pushWrapped("Press Enter to select this option.", (line) => this.theme.fg("dim", line));
|
|
526
|
+
md += `\n---\n\nPress \`Enter\` to select this option.\n`;
|
|
523
527
|
if (this.searchQuery) {
|
|
524
|
-
|
|
525
|
-
pushWrapped(`Filter: ${this.searchQuery}`, (line) => this.theme.fg("dim", line));
|
|
528
|
+
md += `\n> Filter: \`${this.searchQuery}\`\n`;
|
|
526
529
|
}
|
|
527
530
|
}
|
|
528
531
|
}
|
|
529
532
|
|
|
530
|
-
|
|
533
|
+
// Render via Markdown component if theme is available, otherwise fall back to plain text
|
|
534
|
+
let lines: string[];
|
|
535
|
+
if (mdTheme) {
|
|
536
|
+
const mdComponent = new Markdown(md.trim(), 0, 0, mdTheme);
|
|
537
|
+
lines = mdComponent.render(width);
|
|
538
|
+
} else {
|
|
539
|
+
lines = [];
|
|
540
|
+
for (const line of wrapTextWithAnsi(md.trim(), Math.max(10, width))) {
|
|
541
|
+
lines.push(truncateToWidth(line, width, ""));
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Trim trailing blanks
|
|
546
|
+
while (lines.length > 0 && lines[lines.length - 1]?.trim() === "") {
|
|
531
547
|
lines.pop();
|
|
532
548
|
}
|
|
533
549
|
|
|
@@ -733,7 +749,11 @@ class AskComponent extends Container {
|
|
|
733
749
|
this.addChild(this.helpText);
|
|
734
750
|
|
|
735
751
|
this.addChild(new Spacer(1));
|
|
736
|
-
this.addChild(new BoxBorderBottom(
|
|
752
|
+
this.addChild(new BoxBorderBottom(
|
|
753
|
+
(s: string) => theme.fg("accent", s),
|
|
754
|
+
`v${ASK_USER_VERSION}`,
|
|
755
|
+
(s: string) => theme.fg("dim", s),
|
|
756
|
+
));
|
|
737
757
|
|
|
738
758
|
this.updateStaticText();
|
|
739
759
|
this.showSelectMode();
|
|
@@ -766,7 +786,7 @@ class AskComponent extends Container {
|
|
|
766
786
|
if (index === 0 || index === rawLines.length - 1) {
|
|
767
787
|
// Box top/bottom borders already rendered at innerWidth — re-render at full width
|
|
768
788
|
if (index === 0) return new BoxBorderTop(borderColor, "ask_user", titleColor).render(width)[0];
|
|
769
|
-
return new BoxBorderBottom(borderColor).render(width)[0];
|
|
789
|
+
return new BoxBorderBottom(borderColor, `v${ASK_USER_VERSION}`, (s: string) => this.theme.fg("dim", s)).render(width)[0];
|
|
770
790
|
}
|
|
771
791
|
const padded = truncateToWidth(line, innerWidth, "", true);
|
|
772
792
|
return `${borderColor(BOX_BORDER_LEFT)}${padded}${borderColor(BOX_BORDER_RIGHT)}`;
|
|
@@ -951,11 +971,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
951
971
|
name: "ask_user",
|
|
952
972
|
label: "Ask User",
|
|
953
973
|
description:
|
|
954
|
-
"Ask the user a question with optional multiple-choice answers. Use this to gather information interactively. Before calling, gather context with tools (read/
|
|
974
|
+
"Ask the user a question with optional multiple-choice answers. Use this to gather information interactively. Before calling, gather context with tools (read/web/ref) and pass a short summary via the context field.",
|
|
955
975
|
promptSnippet:
|
|
956
976
|
"Ask the user a question with optional multiple-choice answers to gather information interactively",
|
|
957
977
|
promptGuidelines: [
|
|
958
|
-
"Before calling ask_user, gather context with tools (read/
|
|
978
|
+
"Before calling ask_user, gather context with tools (read/web/ref) and pass a short summary via the context field.",
|
|
959
979
|
"Use ask_user when the user's intent is ambiguous, when a decision requires explicit user input, or when multiple valid options exist.",
|
|
960
980
|
],
|
|
961
981
|
parameters: Type.Object({
|
package/package.json
CHANGED