feat: delete fields tool
fix: field rename fix: list fields now scrolls
This commit is contained in:
parent
7a3b3f2161
commit
7c19ada88b
@ -1,7 +1,7 @@
|
||||
import { cliPrompt } from "./prompts.ts";
|
||||
import type { TerminalBlock } from "./TerminalLayout.ts";
|
||||
|
||||
type prompt = [string, (v?: string) => boolean] | string;
|
||||
type prompt = [string, (v?: string) => boolean | undefined] | string;
|
||||
|
||||
export async function forceArgs(
|
||||
args: string[],
|
||||
|
@ -13,6 +13,7 @@ const toolRegistry: [string, Promise<{ default: ITool }>][] = [
|
||||
["checkCode", import("../tools/checkCode.ts")],
|
||||
["fieldRename", import("../tools/fieldRename.ts")],
|
||||
["listFormFields", import("../tools/listFormFields.ts")],
|
||||
["deleteFields", import("../tools/deleteFields.ts")],
|
||||
];
|
||||
|
||||
export class PdfToolsCli {
|
||||
|
@ -1,4 +1,5 @@
|
||||
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";
|
||||
@ -127,13 +128,16 @@ export async function selectMenuInteractive(
|
||||
|
||||
export async function multiSelectMenuInteractive(
|
||||
q: string,
|
||||
options: string[] | [string, callback][],
|
||||
config?: ISelectMenuConfig,
|
||||
options: (string | [string, callback])[],
|
||||
config?: ISelectMenuConfig & { allOption?: boolean },
|
||||
): Promise<string[] | null> {
|
||||
Deno.stdin.setRaw(true);
|
||||
let selected = 0;
|
||||
let selectedOptions: number[] = config?.initialSelections || [];
|
||||
|
||||
if (config?.allOption) {
|
||||
options.unshift("Select All");
|
||||
}
|
||||
const rawValues = options.map((i) => typeof i === "string" ? i : i[0]);
|
||||
|
||||
if (rawValues.length !== options.length) {
|
||||
@ -145,6 +149,21 @@ export async function multiSelectMenuInteractive(
|
||||
terminalBlock.setRenderHeight(Deno.consoleSize().rows);
|
||||
}
|
||||
|
||||
const checkSelectAll = () => {
|
||||
if (selectedOptions.includes(0)) {
|
||||
log("yeet");
|
||||
selectedOptions = [];
|
||||
} else {
|
||||
log("neat");
|
||||
selectedOptions = Array.from(options).map((_, i) => i);
|
||||
}
|
||||
};
|
||||
|
||||
const validateSelectAll = () => {
|
||||
const allPresent = selectedOptions.length == options.length;
|
||||
if (!allPresent) selectedOptions = selectedOptions.filter((e) => e != 0);
|
||||
};
|
||||
|
||||
let range: [number, number] = [terminalBlock.lineCount, 1];
|
||||
function renderMenu() {
|
||||
const { rows } = Deno.consoleSize();
|
||||
@ -193,11 +212,14 @@ export async function multiSelectMenuInteractive(
|
||||
const onSpace = (e: CLICharEvent) => {
|
||||
if (e.detail.char !== " ") return;
|
||||
e.stopImmediatePropagation();
|
||||
if (selectedOptions.includes(selected)) {
|
||||
if (config?.allOption && selected === 0) {
|
||||
checkSelectAll();
|
||||
} else if (selectedOptions.includes(selected)) {
|
||||
selectedOptions = selectedOptions.filter((i) => i !== selected);
|
||||
} else {
|
||||
selectedOptions.push(selected);
|
||||
}
|
||||
validateSelectAll();
|
||||
renderMenu();
|
||||
};
|
||||
|
||||
@ -299,7 +321,7 @@ if (import.meta.main) {
|
||||
"ximenia",
|
||||
"yuzu",
|
||||
"zucchini",
|
||||
], { terminalBlock: block });
|
||||
], { terminalBlock: block, allOption: true });
|
||||
cliLog(val || "No value");
|
||||
|
||||
// Deno.stdout.writeSync(new TextEncoder().encode("\x07"));
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "@bearmetal/pdf-tools",
|
||||
"version": "1.0.8-l",
|
||||
"version": "1.0.8-n",
|
||||
"license": "GPL 3.0",
|
||||
"tasks": {
|
||||
"dev": "deno run -A --env-file=.env main.ts",
|
||||
"dev": "deno run -A main.ts",
|
||||
"compile": "deno compile -o pdf-tools.exe --target x86_64-pc-windows-msvc --include ./asciiart.txt -A ./main.ts",
|
||||
"install": "deno install -fgq --import-map ./deno.json -n checkfields -R ./main.ts",
|
||||
"debug": "deno run -A --env-file=.env --inspect-wait --watch main.ts"
|
||||
|
50
tools/deleteFields.ts
Normal file
50
tools/deleteFields.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { forceArgs } from "../cli/forceArgs.ts";
|
||||
import { cliPrompt } from "../cli/prompts.ts";
|
||||
import { multiSelectMenuInteractive } from "../cli/selectMenu.ts";
|
||||
import { TerminalBlock } from "../cli/TerminalLayout.ts";
|
||||
import type { callback, ITool } from "../types.ts";
|
||||
import { loadPdf, savePdf } from "util/saveLoadPdf.ts";
|
||||
import { log } from "util/logfile.ts";
|
||||
|
||||
export class DeleteFormFields implements ITool {
|
||||
name = "deleteFormFields";
|
||||
description = "delete multiple form fields from a PDF";
|
||||
block?: TerminalBlock;
|
||||
|
||||
async run(pdfPath: string = "") {
|
||||
if (!this.block) this.block = new TerminalBlock();
|
||||
[pdfPath] = await forceArgs([pdfPath], [[
|
||||
"Please provide path to PDF",
|
||||
(d) => d?.endsWith(".pdf"),
|
||||
]], this.block);
|
||||
|
||||
const pdf = await loadPdf(pdfPath);
|
||||
const form = pdf.getForm();
|
||||
const fields = form.getFields();
|
||||
let updatesMade = false;
|
||||
await multiSelectMenuInteractive(
|
||||
`${pdfPath}\nSelect fields to delete:`,
|
||||
fields.map<[string, callback]>((
|
||||
f,
|
||||
) => [f.getName(), () => {
|
||||
while (f.acroField.getWidgets().length) {
|
||||
f.acroField.removeWidget(0);
|
||||
}
|
||||
form.removeField(f);
|
||||
updatesMade = true;
|
||||
}]),
|
||||
);
|
||||
if (!updatesMade) return;
|
||||
const path = await cliPrompt(
|
||||
"Save to path (or hit enter to keep current):",
|
||||
this.block,
|
||||
) || pdfPath;
|
||||
await savePdf(pdf, path);
|
||||
}
|
||||
help?: (() => Promise<void> | void) | undefined;
|
||||
done?: (() => Promise<void> | void) | undefined;
|
||||
setBlock(block: TerminalBlock) {
|
||||
this.block = block;
|
||||
}
|
||||
}
|
||||
export default new DeleteFormFields();
|
@ -404,7 +404,7 @@ class RenameFields implements ITool {
|
||||
if (!this.block) {
|
||||
this.block = new TerminalBlock();
|
||||
}
|
||||
this.block.setPreserveHistory(true);
|
||||
this.block.setPreserveHistory(false);
|
||||
|
||||
[pdfPath, pattern, change] = await forceArgs(
|
||||
[pdfPath, pattern, change],
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { forceArgs } from "../cli/forceArgs.ts";
|
||||
import { cliAlert } from "../cli/prompts.ts";
|
||||
import { TerminalBlock } from "../cli/TerminalLayout.ts";
|
||||
import { loadPdfForm } from "util/saveLoadPdf.ts";
|
||||
import type { ITool } from "../types.ts";
|
||||
import { InputManager } from "../cli/InputManager.ts";
|
||||
|
||||
export class ListFormFields implements ITool {
|
||||
name = "listformfields";
|
||||
@ -12,21 +12,48 @@ export class ListFormFields implements ITool {
|
||||
if (!this.block) {
|
||||
this.block = new TerminalBlock();
|
||||
}
|
||||
this.block.setPreserveHistory(true);
|
||||
this.block.setPreserveHistory(false);
|
||||
[pdfPath] = await forceArgs([pdfPath], [[
|
||||
"Please provide path to PDF:",
|
||||
(p) => !!p && p.endsWith(".pdf"),
|
||||
]], this.block);
|
||||
const lines = [pdfPath];
|
||||
let rLines: string[] = [];
|
||||
|
||||
const form = await loadPdfForm(pdfPath);
|
||||
const fields = form.getFields();
|
||||
const fieldNames = fields.map((f) => f.getName());
|
||||
const lines = [];
|
||||
for (const fieldName of fieldNames) {
|
||||
lines.push(fieldName);
|
||||
}
|
||||
this.block.setLines(lines, [0, 1]);
|
||||
await cliAlert("", this.block);
|
||||
|
||||
let offset = 0;
|
||||
|
||||
const buildRLines = () => {
|
||||
rLines = fieldNames.slice(offset, this.block!.getRenderHeight());
|
||||
this.block!.setLines(lines.concat(rLines), [0, 1]);
|
||||
};
|
||||
buildRLines();
|
||||
|
||||
await new Promise<void>((res) => {
|
||||
const im = InputManager.getInstance();
|
||||
const up = () => {
|
||||
if (fieldNames.length < this.block!.getRenderHeight() - 1) return;
|
||||
offset = Math.max(0, offset - 1);
|
||||
buildRLines();
|
||||
};
|
||||
const down = () => {
|
||||
if (fieldNames.length < this.block!.getRenderHeight() - 1) return;
|
||||
offset = Math.min(fieldNames.length, offset + 1);
|
||||
buildRLines();
|
||||
};
|
||||
const enter = () => {
|
||||
res();
|
||||
im.removeEventListener("arrow-up", up);
|
||||
im.removeEventListener("arrow-down", down);
|
||||
im.removeEventListener("enter", enter);
|
||||
};
|
||||
im.addEventListener("arrow-up", up);
|
||||
im.addEventListener("arrow-down", down);
|
||||
im.addEventListener("enter", enter);
|
||||
});
|
||||
}
|
||||
setBlock(terminalBlock: TerminalBlock) {
|
||||
this.block = terminalBlock;
|
||||
|
Loading…
x
Reference in New Issue
Block a user