Compare commits

...

2 Commits

4 changed files with 84 additions and 24 deletions

View File

@ -44,7 +44,9 @@ export class TerminalLayout {
clearTimeout(this.debounceTimer); clearTimeout(this.debounceTimer);
} }
this.debounceTimer = setTimeout( this.debounceTimer = setTimeout(
() => this.renderLayout(), () => {
this.renderLayout();
},
this.debounceDelay, this.debounceDelay,
); );
} }
@ -76,6 +78,10 @@ export class TerminalLayout {
block.renderInternal(usedLines + 1); block.renderInternal(usedLines + 1);
usedLines += lines.length; usedLines += lines.length;
} }
for (const name of this.layoutOrder) {
const block = this.blocks[name];
block.runPostRenderAction?.();
}
} }
clearAll() { clearAll() {
@ -217,7 +223,9 @@ export class TerminalBlock {
const baseRow = startRow ?? this.lastRenderRow; const baseRow = startRow ?? this.lastRenderRow;
const excessLines = this.renderHeight - this.renderLines.length; const excessLines = this.renderHeight - this.renderLines.length;
for (let i = 0; i < excessLines; i++) { for (let i = 0; i < excessLines; i++) {
const moveToLine = `[${baseRow + this.renderLines.length + i};1H`; const moveToLine = `\x1b[${
baseRow + this.renderLines.length + i
};1H\x1b[2K`;
Deno.stdout.writeSync(new TextEncoder().encode(moveToLine)); Deno.stdout.writeSync(new TextEncoder().encode(moveToLine));
} }
@ -254,6 +262,17 @@ export class TerminalBlock {
return this.fixedHeight ?? 0; return this.fixedHeight ?? 0;
} }
private _postRenderAction?: () => void;
setPostRenderAction(action: (this: TerminalBlock) => void) {
this._postRenderAction = action;
}
runPostRenderAction() {
if (this._postRenderAction) {
this._postRenderAction.call(this);
this._postRenderAction = undefined;
}
}
get lineCount() { get lineCount() {
return this.renderLines.length; return this.renderLines.length;
} }

View File

@ -1,4 +1,5 @@
// deno-lint-disable-must-await-calls // deno-lint-disable-must-await-calls
import { log } from "util/logfile.ts";
import { Cursor } from "./cursor.ts"; import { Cursor } from "./cursor.ts";
import { colorize } from "./style.ts"; import { colorize } from "./style.ts";
import { TerminalBlock, TerminalLayout } from "./TerminalLayout.ts"; import { TerminalBlock, TerminalLayout } from "./TerminalLayout.ts";
@ -9,6 +10,7 @@ export async function cliPrompt(
): Promise<string> { ): Promise<string> {
const encoder = new TextEncoder(); const encoder = new TextEncoder();
const input: string[] = []; const input: string[] = [];
let cursorPos = 0;
await Deno.stdin.setRaw(true); await Deno.stdin.setRaw(true);
@ -22,33 +24,61 @@ export async function cliPrompt(
Deno.stdout.writeSync(encoder.encode(message + " ")); Deno.stdout.writeSync(encoder.encode(message + " "));
} }
const buf = new Uint8Array(1); const render = () => {
const line = message + " " + input.join("");
const moveTo = `\x1b[${message.length + 2 + cursorPos}G`;
if (block) {
block.setPostRenderAction(function () {
Deno.stdout.writeSync(
encoder.encode(`\x1b[${this["lastRenderRow"]};1H`),
);
Deno.stdout.writeSync(encoder.encode(moveTo));
});
range = block.setLines([line], range);
} else {
Deno.stdout.writeSync(encoder.encode("\x1b[K" + line + moveTo));
}
};
render();
const buf = new Uint8Array(6); // 6 bytes is enough for all the keys
while (true) { while (true) {
const n = await Deno.stdin.read(buf); const n = await Deno.stdin.read(buf);
if (n === null) break; if (n === null) break;
const byte = buf[0]; const [a, b, c] = buf;
if (byte === 3) { // Ctrl+C if (a === 3) { // Ctrl+C
Deno.stdin.setRaw(false);
block?.["layout"]?.clearAll();
block?.clear(); block?.clear();
block?.["layout"]?.clearAll();
Deno.stdin.setRaw(false);
Deno.exit(130); Deno.exit(130);
} }
if (byte === 13) { // Enter if (a === 13) { // Enter
break; break;
} else if (byte === 127 || byte === 8) { // Backspace } else if (a === 127 || a === 8) { // Backspace
input.pop(); if (cursorPos > 0) {
} else if (byte >= 32 && byte <= 126) { // Printable chars input.splice(cursorPos - 1, 1);
input.push(String.fromCharCode(byte)); cursorPos--;
}
} else if (a === 46) { // Delete
if (cursorPos < input.length) {
input.splice(cursorPos, 1);
}
} else if (a === 27 && b === 91) { // Arrow keys
if (c === 51 && cursorPos < input.length) { // delete
input.splice(cursorPos, 1);
}
if (c === 68 && cursorPos > 0) cursorPos--; // Left
if (c === 67 && cursorPos < input.length) cursorPos++; // Right
} else if (a >= 32 && a <= 126) { // Printable ASCII
input.splice(cursorPos, 0, String.fromCharCode(a));
cursorPos++;
} }
const line = message + " " + input.join(""); render();
if (block) {
range = block.setLines([line], range);
} else {
Deno.stdout.writeSync(encoder.encode("\r\x1b[K" + line));
}
} }
await Deno.stdin.setRaw(false); await Deno.stdin.setRaw(false);
@ -92,13 +122,18 @@ if (import.meta.main) {
const layout = new TerminalLayout(); const layout = new TerminalLayout();
const title = new TerminalBlock(); const title = new TerminalBlock();
const block = new TerminalBlock(); const block = new TerminalBlock();
const footer = new TerminalBlock();
block.setPreserveHistory(true); block.setPreserveHistory(true);
// ScrollManager.enable(block); // ScrollManager.enable(block);
title.setLines(["Hello, World!"]); title.setLines(["Hello, World!"]);
title.setFixedHeight(1); title.setFixedHeight(1);
footer.setLines(["Press Ctrl+C to exit"]);
footer.setFixedHeight(1);
layout.register("title", title); layout.register("title", title);
layout.register("block", block); layout.register("block", block);
layout.register("footer", footer);
Deno.addSignalListener("SIGINT", () => { Deno.addSignalListener("SIGINT", () => {
layout.clearAll(); layout.clearAll();

View File

@ -1,6 +1,6 @@
{ {
"name": "@bearmetal/pdf-tools", "name": "@bearmetal/pdf-tools",
"version": "1.0.3", "version": "1.0.4",
"license": "GPL 3.0", "license": "GPL 3.0",
"tasks": { "tasks": {
"dev": "deno run -A --env-file=.env main.ts", "dev": "deno run -A --env-file=.env main.ts",

View File

@ -153,6 +153,7 @@ class RenameFields implements ITool {
const fields = form.getFields(); const fields = form.getFields();
const foundUpdates: [string, callback][] = []; const foundUpdates: [string, callback][] = [];
let changesMade = false;
for (const field of fields) { for (const field of fields) {
const name = field.getName(); const name = field.getName();
@ -164,6 +165,7 @@ class RenameFields implements ITool {
`${colorize(name, "red")} -> ${colorize(preview, "green")}`, `${colorize(name, "red")} -> ${colorize(preview, "green")}`,
() => { () => {
applyRename(field, name, patternRegex, toChange); applyRename(field, name, patternRegex, toChange);
changesMade = true;
}, },
]); ]);
} }
@ -178,11 +180,15 @@ class RenameFields implements ITool {
); );
} }
if (changesMade) {
const path = await cliPrompt( const path = await cliPrompt(
"Save to path (or hit enter to keep current):", "Save to path (or hit enter to keep current):",
this.block, this.block,
); );
await savePdf(pdf, path || pdfPath); await savePdf(pdf, path || pdfPath);
} else {
cliLog("No changes made, skipping", this.block);
}
} }
} }
} }