// deno-lint-disable-must-await-calls import { Cursor } from "./cursor.ts"; import { colorize } from "./style.ts"; import { TerminalBlock, TerminalLayout } from "./TerminalLayout.ts"; import { type CLICharEvent, InputManager } from "./InputManager.ts"; export async function cliPrompt( message: string, block?: TerminalBlock, ): Promise { const encoder = new TextEncoder(); const input: string[] = []; let cursorPos = 0; Cursor.show(); const im = InputManager.getInstance(); im.activate(); const render = () => { const line = message + " " + input.join(""); const moveTo = `\x1b[${message.length + 2 + cursorPos}G`; if (block) { block.setLines([line]); } else { Deno.stdout.writeSync(encoder.encode("\r\x1b[K" + line + moveTo)); } }; render(); const cPos = block?.requestCursorAt(0, message.length + 1); if (cPos) { const [row, column] = cPos; im.setCursor(row, column); im.setBounds({ top: row, left: column, right: column, bottom: row }); } const exit = () => { im.removeEventListener("enter", onEnter); im.removeEventListener("backspace", onBackspace); im.removeEventListener("delete", onDelete); im.removeEventListener("arrow-left", onLeft); im.removeEventListener("arrow-right", onRight); im.removeEventListener("char", onKey); Cursor.hide(); }; let resolve: null | ((value: string) => void) = null; const onEnter = () => { exit(); resolve?.(input.join("")); }; const onBackspace = () => { if (cursorPos > 0) { input.splice(cursorPos - 1, 1); cursorPos--; render(); } }; const onDelete = () => { if (cursorPos < input.length) { input.splice(cursorPos, 1); render(); } }; const onLeft = () => { if (cursorPos > 0) { cursorPos--; render(); } }; const onRight = () => { if (cursorPos < input.length) { cursorPos++; render(); } }; const onKey = (e: Event) => { const ke = (e as CLICharEvent).detail; cursorPos = im.getRelativeCursor(); input.splice(cursorPos, 0, ke.char); im.updateBounds({ right: input.length + message.length + 1 }); render(); }; return await new Promise((res) => { resolve = res; im.addEventListener("enter", onEnter); im.addEventListener("backspace", onBackspace); im.addEventListener("delete", onDelete); im.addEventListener("arrow-left", onLeft); im.addEventListener("arrow-right", onRight); im.addEventListener("char", onKey); }); } export async function cliConfirm(message: string, block?: TerminalBlock) { const im = InputManager.getInstance(); let inpout = ""; function isValidInput(input: string) { switch (input) { case "y": case "n": return inpout.length === 0; case "e": return inpout === "y"; case "s": return inpout === "ye"; case "o": return inpout === "n"; default: return false; } } function onKey(e: CLICharEvent) { const ke = e.detail; const char = String.fromCharCode(ke.key); if (isValidInput(char)) { inpout += char; } else { e.stopImmediatePropagation(); } } im.addEventListener("char", onKey); const value = await cliPrompt(message + " (y/n)", block).then((v) => v.charAt(0).toLowerCase() === "y" ); im.removeEventListener("char", onKey); return value; } export async function cliAlert(message: string, block?: TerminalBlock) { const im = InputManager.getInstance(); const onKey = (e: CLICharEvent) => { e.stopImmediatePropagation(); }; im.addEventListener("char", onKey); await cliPrompt( message + colorize(" Press Enter to continue", "gray"), block, ); im.removeEventListener("char", onKey); } export function cliLog( message: string | object | Array, block?: TerminalBlock, ) { if (!block) { console.log(message); } else { if (typeof message === "object") message = Deno.inspect(message); block.setLines(message.split("\n")); } } if (import.meta.main) { Cursor.hide(); const layout = new TerminalLayout(); const title = new TerminalBlock(); const block = new TerminalBlock(); const footer = new TerminalBlock(); block.setPreserveHistory(true); // ScrollManager.enable(block); title.setLines(["Hello, World!"]); title.setFixedHeight(1); footer.setLines(["Press Ctrl+C to exit"]); footer.setFixedHeight(1); layout.register("title", title); layout.register("block", block); layout.register("footer", footer); InputManager.addEventListener("exit", () => { layout.clearAll(); // console.clear(); Deno.exit(0); }); Deno.addSignalListener("SIGINT", () => { layout.clearAll(); // console.clear(); // Deno.exit(0); }); const name = await cliPrompt("Enter your name:", block); cliLog(`Hello, ${name}!`, block); const single = await cliConfirm("Are you single?", block); cliLog(single ? "Do you want to go out with me?" : "Okay", block); // ScrollManager.enable(block); const loopingConvo = [ "No response?", "I guess that's okay", "Maybe I'll see you next week?", "Wow, really not going to say anything to me?", "Well, if that's how you feel", ]; let convo = 0; setInterval(() => { cliLog(loopingConvo[convo % loopingConvo.length], block); convo++; }, 2000); // setTimeout(async () => { // await cliAlert("Well, if that's that...", block); // Deno.exit(0); // }, 10000); }