feat: esc to cancel select menus

feat: fieldVisibility tool
This commit is contained in:
2025-06-06 12:54:44 -06:00
parent 7c19ada88b
commit 04d5044c43
5 changed files with 195 additions and 15 deletions

View File

@@ -6,12 +6,13 @@ interface EventMap {
exit: Event;
enter: Event;
backspace: Event;
escape: Event;
delete: Event;
"arrow-left": Event;
"arrow-right": Event;
"arrow-up": Event;
"arrow-down": Event;
[key: string]: Event;
// [key: string]: Event;
}
interface EventDetailMap {
@@ -146,6 +147,12 @@ export class InputManager extends ManagerEventTarget {
continue;
}
if (byte === 27 && i + 1 >= n) {
this.dispatchEvent(new Event("escape"));
i++;
continue;
}
// Escape sequences
if (byte === 27 && i + 1 < n && buf[i + 1] === 91) {
const code = buf[i + 2];
@@ -193,4 +200,23 @@ export class InputManager extends ManagerEventTarget {
if (raw) await Deno.stdin.setRaw(false);
}
dispatchKey(key: string) {
switch (key) {
case "enter":
case "backspace":
case "arrow-up":
case "arrow-down":
case "arrow-right":
case "arrow-left":
case "delete":
case "escape":
this.dispatchEvent(new Event(key));
break;
default:
this.dispatchEvent(
new CLICharEvent({ key: key.charCodeAt(0), char: key }),
);
}
}
}

View File

@@ -14,6 +14,7 @@ const toolRegistry: [string, Promise<{ default: ITool }>][] = [
["fieldRename", import("../tools/fieldRename.ts")],
["listFormFields", import("../tools/listFormFields.ts")],
["deleteFields", import("../tools/deleteFields.ts")],
["fieldVisibility", import("../tools/fieldVisibility.ts")],
];
export class PdfToolsCli {

View File

@@ -1,5 +1,4 @@
import type { callback } from "../types.ts";
import { log } from "util/logfile.ts";
import { type CLICharEvent, InputManager } from "./InputManager.ts";
import { cliLog } from "./prompts.ts";
import { colorize } from "./style.ts";
@@ -92,7 +91,17 @@ export async function selectMenuInteractive(
inputBuffer = inputBuffer.slice(0, -1);
};
let resolve: null | ((value: string) => void) = null;
let resolve: null | ((value: string | null) => void) = null;
const onEscape = () => {
im.removeEventListener("arrow-up", onUp);
im.removeEventListener("arrow-down", onDown);
im.removeEventListener("char", onKey);
im.removeEventListener("backspace", onBackspace);
im.removeEventListener("enter", onEnter);
im.removeEventListener("escape", onEscape);
resolve?.(null);
};
const onEnter = (e: Event) => {
e.stopImmediatePropagation();
@@ -108,22 +117,24 @@ export async function selectMenuInteractive(
im.removeEventListener("char", onKey);
im.removeEventListener("backspace", onBackspace);
im.removeEventListener("enter", onEnter);
im.removeEventListener("escape", onEscape);
resolve?.(options[selected]);
};
renderMenu();
await new Promise<string>((res) => {
const final = await new Promise<string | null>((res) => {
resolve = res;
im.addEventListener("char", onKey);
im.addEventListener("backspace", onBackspace);
im.addEventListener("enter", onEnter);
im.addEventListener("arrow-up", onUp);
im.addEventListener("arrow-down", onDown);
im.addEventListener("escape", onEscape);
});
terminalBlock.setLines(["Selected: " + options[selected]], range);
terminalBlock.setLines(["Selected: " + final], range);
return options[selected];
return final;
}
export async function multiSelectMenuInteractive(
@@ -151,10 +162,8 @@ export async function multiSelectMenuInteractive(
const checkSelectAll = () => {
if (selectedOptions.includes(0)) {
log("yeet");
selectedOptions = [];
} else {
log("neat");
selectedOptions = Array.from(options).map((_, i) => i);
}
};
@@ -195,7 +204,7 @@ export async function multiSelectMenuInteractive(
const im = InputManager.getInstance();
im.activate();
let resolve = null as null | ((value: number[]) => void);
let resolve = null as null | ((value: number[] | null) => void);
const onUp = (e: Event) => {
e.stopImmediatePropagation();
@@ -223,24 +232,36 @@ export async function multiSelectMenuInteractive(
renderMenu();
};
const onEnter = (e: Event) => {
e.stopImmediatePropagation();
resolve?.(selectedOptions);
const onEscape = () => {
im.removeEventListener("arrow-up", onUp);
im.removeEventListener("arrow-down", onDown);
im.removeEventListener("char", onSpace);
im.removeEventListener("enter", onEnter);
im.removeEventListener("escape", onEscape);
resolve?.(null);
};
const onEnter = (e: Event) => {
e.stopImmediatePropagation();
im.removeEventListener("arrow-up", onUp);
im.removeEventListener("arrow-down", onDown);
im.removeEventListener("char", onSpace);
im.removeEventListener("enter", onEnter);
im.removeEventListener("escape", onEscape);
resolve?.(selectedOptions);
};
renderMenu();
const selections = await new Promise<number[]>((res) => {
const selections = await new Promise<number[] | null>((res) => {
resolve = res;
im.addEventListener("arrow-up", onUp);
im.addEventListener("arrow-down", onDown);
im.addEventListener("char", onSpace);
im.addEventListener("enter", onEnter);
im.addEventListener("escape", onEscape);
});
if (!selections) return null;
for (const optionI of selections) {
const option = options[optionI];
if (Array.isArray(option)) {
@@ -322,7 +343,7 @@ if (import.meta.main) {
"yuzu",
"zucchini",
], { terminalBlock: block, allOption: true });
cliLog(val || "No value");
cliLog(val || "No value", block);
// Deno.stdout.writeSync(new TextEncoder().encode("\x07"));
}