document-cli 1.2.11 → 1.4.0

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.
@@ -1,8 +1,8 @@
1
- import { d as inferFormatFromExtension, i as formatOdbReportLines, l as loadProvidedFonts, n as describeOdbReport, o as readInput, r as formatOdbFormLines, t as describeOdbForm, u as formatToExtension } from "./odb-structure-CX_0zL8_.js";
1
+ import { d as formatToExtension, f as inferFormatFromExtension, i as formatOdbReportLines, n as describeOdbReport, o as formatDocxExtrasLines, r as formatOdbFormLines, s as readInput, t as describeOdbForm, u as loadProvidedFonts } from "./odb-structure-DkfNyHGR.js";
2
2
  import { readFile, writeFile } from "node:fs/promises";
3
- import { createDocx, createOdg, createOdp, createOds, createOdt, createPptx, decodeMarkdownText, docxToPdf, encodeMarkdownText, hsqldbCellDisplayText, markdownToPdf, odgToPdf, odpToPdf, odsToPdf, odtToPdf, openDocx, openOdg, openOdp, openOds, openOdt, openPptx, pptxToPdf, readOdbForms, readOdbReports, readOdbTables, readOdgContent, readOdsContent, readPdf, rgbHexToColor, xlsxToPdf } from "documents.js";
3
+ import { buildDocxPackage, buildOdtPackage, convertWordprocessingToLayout, createDocx, createFontMeasurer, createFontRegistry, createOdg, createOdp, createOds, createOdt, createPptx, decodeMarkdownText, docxToPdf, encodeMarkdownText, encodePackage, hsqldbCellDisplayText, markdownToPdf, odgToPdf, odpToPdf, odsToPdf, odtToPdf, openDocx, openOdg, openOdp, openOds, openOdt, openPptx, pptxToPdf, readDocxExtras, readOdbForms, readOdbReportContent, readOdbReports, readOdbTables, readOdgContent, readOdsContent, readPdf, rgbHexToColor, writePdf, xlsxToPdf } from "documents.js";
4
4
  import { basename, dirname, extname, join } from "node:path";
5
- import { cellReference, columnIndexToLetters, decodePackage } from "odf.js";
5
+ import { cellReference, columnIndexToLetters, decodePackage as decodePackage$1, encodePackage as encodePackage$1 } from "odf.js";
6
6
  import { readdirSync } from "node:fs";
7
7
  import { Box, Text, render, useApp, useInput, useWindowSize } from "ink";
8
8
  import { createContext, useContext, useEffect, useReducer, useState } from "react";
@@ -78,7 +78,7 @@ const ODB_EXTENSION = ".odb";
78
78
  async function openDocumentAtPath(path) {
79
79
  const bytes = new Uint8Array(await readFile(path));
80
80
  if (path.toLowerCase().endsWith(ODB_EXTENSION)) {
81
- const pkg = decodePackage(bytes);
81
+ const pkg = decodePackage$1(bytes);
82
82
  return {
83
83
  format: "odb",
84
84
  tables: readOdbTables(pkg),
@@ -209,6 +209,7 @@ function selectionKeyFor(screen) {
209
209
  case "launcher":
210
210
  case "newDocumentPicker":
211
211
  case "bodyList":
212
+ case "docxExtras":
212
213
  case "slideList":
213
214
  case "sheetList":
214
215
  case "pageList":
@@ -238,6 +239,7 @@ function selectionKeyFor(screen) {
238
239
  case "odbTableRows": return `odbTableRows:${screen.tableName}`;
239
240
  case "odbFormDetail": return `odbFormDetail:${screen.formName}`;
240
241
  case "odbReportDetail": return `odbReportDetail:${screen.reportName}`;
242
+ case "odbReportRender": return `odbReportRender:${screen.reportName}`;
241
243
  case "markdownLineEditor": return `markdownLineEditor:${screen.lineIndex}`;
242
244
  }
243
245
  }
@@ -2260,6 +2262,12 @@ function DocxBodyListScreen() {
2260
2262
  const state = useAppState();
2261
2263
  const dispatch = useAppDispatch();
2262
2264
  const doc = state.openDocument;
2265
+ useInput((input) => {
2266
+ if (input === "x" && doc?.format === "docx") dispatch({
2267
+ type: "PUSH_SCREEN",
2268
+ screen: { kind: "docxExtras" }
2269
+ });
2270
+ }, { isActive: doc?.format === "docx" && !anyOverlayOpen(state) });
2263
2271
  if (doc?.format !== "docx") return /* @__PURE__ */ jsxs(Text, {
2264
2272
  color: "red",
2265
2273
  children: [
@@ -2277,6 +2285,65 @@ function DocxBodyListScreen() {
2277
2285
  return /* @__PURE__ */ jsx(ParagraphFamilyBodyList, { adapter });
2278
2286
  }
2279
2287
  //#endregion
2288
+ //#region src/tui/screens/editors/docx/extras.tsx
2289
+ const DOCX_EXTRAS_RESERVED_ROWS = 5;
2290
+ function DocxExtrasScreen() {
2291
+ const state = useAppState();
2292
+ const dispatch = useAppDispatch();
2293
+ const doc = state.openDocument;
2294
+ const isDocx = doc?.format === "docx";
2295
+ const extras = doc?.format === "docx" ? readDocxExtras(doc.editor.toPackage()) : void 0;
2296
+ const allLines = extras === void 0 ? [] : formatDocxExtrasLines(extras);
2297
+ const query = state.searchQuery.trim().toLowerCase();
2298
+ const lines = query === "" ? allLines : allLines.filter((line) => line.toLowerCase().includes(query));
2299
+ const { selectedIndex } = useNavigationInput({
2300
+ itemCount: lines.length,
2301
+ onSelect: () => {},
2302
+ onBack: () => {
2303
+ dispatch({ type: "POP_SCREEN" });
2304
+ },
2305
+ isActive: isDocx && !anyOverlayOpen(state)
2306
+ });
2307
+ if (!isDocx) return /* @__PURE__ */ jsxs(Text, {
2308
+ color: "red",
2309
+ children: [
2310
+ "DocxExtrasScreen requires an open docx document, found ",
2311
+ doc === void 0 ? "no open document" : doc.format,
2312
+ "."
2313
+ ]
2314
+ });
2315
+ return /* @__PURE__ */ jsxs(Box, {
2316
+ flexDirection: "column",
2317
+ children: [
2318
+ /* @__PURE__ */ jsxs(Text, {
2319
+ bold: true,
2320
+ children: [
2321
+ "Comments, footnotes, headers, footers, numbering (",
2322
+ lines.length,
2323
+ " of ",
2324
+ allLines.length,
2325
+ " lines)"
2326
+ ]
2327
+ }),
2328
+ /* @__PURE__ */ jsx(ListView, {
2329
+ items: lines,
2330
+ selectedIndex,
2331
+ reservedRows: DOCX_EXTRAS_RESERVED_ROWS,
2332
+ emptyMessage: query === "" ? "This document carries no comments, footnotes, headers, footers, or numbering definitions." : `No lines match "${state.searchQuery}".`,
2333
+ renderItem: (line, isSelected) => /* @__PURE__ */ jsx(Text, {
2334
+ color: isSelected ? "cyan" : void 0,
2335
+ inverse: isSelected,
2336
+ children: line
2337
+ })
2338
+ }),
2339
+ /* @__PURE__ */ jsx(Text, {
2340
+ dimColor: true,
2341
+ children: "Esc to go back to the body list"
2342
+ })
2343
+ ]
2344
+ });
2345
+ }
2346
+ //#endregion
2280
2347
  //#region src/tui/screens/editors/markdown/shared.ts
2281
2348
  function requireMarkdownDocument(openDocument) {
2282
2349
  if (openDocument?.format !== "markdown") throw new Error("A markdown editing screen rendered without an open markdown document; the app router only reaches this screen group from markdownLineList, which is only ever the root screen of an open markdown document.");
@@ -2537,7 +2604,15 @@ function OdbReportDetailScreen() {
2537
2604
  const lines = query === "" ? allLines : allLines.filter((line) => line.toLowerCase().includes(query));
2538
2605
  const { selectedIndex } = useNavigationInput({
2539
2606
  itemCount: lines.length,
2540
- onSelect: () => {},
2607
+ onSelect: () => {
2608
+ dispatch({
2609
+ type: "PUSH_SCREEN",
2610
+ screen: {
2611
+ kind: "odbReportRender",
2612
+ reportName: report.name
2613
+ }
2614
+ });
2615
+ },
2541
2616
  onBack: () => {
2542
2617
  dispatch({ type: "POP_SCREEN" });
2543
2618
  },
@@ -2574,7 +2649,7 @@ function OdbReportDetailScreen() {
2574
2649
  }),
2575
2650
  /* @__PURE__ */ jsx(Text, {
2576
2651
  dimColor: true,
2577
- children: "Esc to go back to the report list"
2652
+ children: "Enter to render this report, Esc to go back to the report list"
2578
2653
  })
2579
2654
  ]
2580
2655
  });
@@ -2636,6 +2711,146 @@ function OdbReportListScreen() {
2636
2711
  });
2637
2712
  }
2638
2713
  //#endregion
2714
+ //#region src/tui/format/render-odb-report.ts
2715
+ const REPORT_RENDER_TARGET_FORMATS = {
2716
+ docx: true,
2717
+ odt: true,
2718
+ pdf: true
2719
+ };
2720
+ function isReportRenderTargetFormat(format) {
2721
+ return format !== void 0 && format in REPORT_RENDER_TARGET_FORMATS;
2722
+ }
2723
+ function renderReportBytes(content, format, fonts, options) {
2724
+ if (format === "docx") return encodePackage(buildDocxPackage(content));
2725
+ if (format === "odt") return encodePackage$1(buildOdtPackage(content));
2726
+ if (content.kind !== "wordprocessing") throw new Error("readOdbReportContent returned a non-wordprocessing ContentDocument");
2727
+ const registry = createFontRegistry({
2728
+ fonts,
2729
+ onSubstitution: (substitution) => {
2730
+ const requested = `${substitution.requestedFamily}${substitution.requestedBold ? " bold" : ""}${substitution.requestedItalic ? " italic" : ""}`;
2731
+ options.onDiagnostic({
2732
+ severity: "info",
2733
+ message: `No "${requested}" face available; drew it in "${substitution.resolvedFamily}"`
2734
+ });
2735
+ }
2736
+ });
2737
+ const { document: layout, formulas } = convertWordprocessingToLayout(content, { measurer: createFontMeasurer(registry) });
2738
+ return writePdf(layout, {
2739
+ signal: options.signal,
2740
+ onSubstitution: (substitution, context) => {
2741
+ options.onDiagnostic({
2742
+ severity: "info",
2743
+ message: `Substituted "${substitution.to}" for "${substitution.from}"`,
2744
+ pageIndex: context.pageIndex
2745
+ });
2746
+ },
2747
+ formulas,
2748
+ fonts: registry
2749
+ });
2750
+ }
2751
+ async function renderOdbReportTo(doc, destinationPath, options) {
2752
+ const format = detectFormat(destinationPath);
2753
+ if (!isReportRenderTargetFormat(format)) throw new Error(`Cannot tell whether to render "${options.reportName}" as docx, odt, or pdf from '${destinationPath}' -- give the destination one of those three extensions`);
2754
+ const fonts = await loadProvidedFonts(options.fontFiles ?? [], { signal: options.signal });
2755
+ const pkg = decodePackage$1(new Uint8Array(await readFile(doc.path)));
2756
+ const bytes = renderReportBytes(readOdbReportContent(pkg, { report: options.reportName }), format, fonts, options);
2757
+ await writeFile(destinationPath, bytes);
2758
+ }
2759
+ //#endregion
2760
+ //#region src/tui/screens/editors/odb/report-render.tsx
2761
+ function defaultReportRenderDestination(odbPath, reportName) {
2762
+ return join(dirname(odbPath), `${reportName}.pdf`);
2763
+ }
2764
+ function parseFontFileField$1(value) {
2765
+ return value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
2766
+ }
2767
+ function OdbReportRenderScreen() {
2768
+ const state = useAppState();
2769
+ const dispatch = useAppDispatch();
2770
+ const isActive = !anyOverlayOpen(state);
2771
+ const doc = requireOdbDocument(state.openDocument);
2772
+ const screen = currentScreen(state);
2773
+ if (screen.kind !== "odbReportRender") throw new Error(`OdbReportRenderScreen rendered while the current screen is "${screen.kind}", not "odbReportRender".`);
2774
+ const reportName = screen.reportName;
2775
+ const [destination, setDestination] = useState(() => defaultReportRenderDestination(doc.path, reportName));
2776
+ const [fontFiles, setFontFiles] = useState("");
2777
+ const [field, setField] = useState("destination");
2778
+ const cancel = () => {
2779
+ dispatch({ type: "POP_SCREEN" });
2780
+ };
2781
+ const submit = () => {
2782
+ (async () => {
2783
+ let diagnosticCount = 0;
2784
+ try {
2785
+ await renderOdbReportTo(doc, destination, {
2786
+ reportName,
2787
+ fontFiles: parseFontFileField$1(fontFiles),
2788
+ onDiagnostic: (diagnostic) => {
2789
+ diagnosticCount += 1;
2790
+ dispatch({
2791
+ type: "APPEND_DIAGNOSTIC",
2792
+ diagnostic
2793
+ });
2794
+ }
2795
+ });
2796
+ dispatch({
2797
+ type: "SET_STATUS",
2798
+ severity: "info",
2799
+ text: `Rendered "${reportName}" to ${destination}`
2800
+ });
2801
+ if (diagnosticCount > 0) dispatch({
2802
+ type: "OPEN_OVERLAY",
2803
+ overlay: "diagnosticsPanel"
2804
+ });
2805
+ dispatch({ type: "POP_SCREEN" });
2806
+ } catch (error) {
2807
+ dispatch({
2808
+ type: "OPEN_FILE_ERROR",
2809
+ message: `Could not render "${reportName}" to ${destination}`,
2810
+ detail: describeError(error)
2811
+ });
2812
+ }
2813
+ })();
2814
+ };
2815
+ return /* @__PURE__ */ jsxs(Box, {
2816
+ flexDirection: "column",
2817
+ children: [
2818
+ /* @__PURE__ */ jsxs(Text, {
2819
+ bold: true,
2820
+ children: ["Render report: ", reportName]
2821
+ }),
2822
+ /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
2823
+ color: "cyan",
2824
+ children: "Path: "
2825
+ }), /* @__PURE__ */ jsx(TextField, {
2826
+ value: destination,
2827
+ isFocused: isActive && field === "destination",
2828
+ placeholder: "destination path (.docx, .odt, or .pdf)",
2829
+ onChange: setDestination,
2830
+ onSubmit: () => {
2831
+ setField("fonts");
2832
+ },
2833
+ onCancel: cancel
2834
+ })] }),
2835
+ /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
2836
+ color: "cyan",
2837
+ children: "Fonts: "
2838
+ }), /* @__PURE__ */ jsx(TextField, {
2839
+ value: fontFiles,
2840
+ isFocused: isActive && field === "fonts",
2841
+ placeholder: "optional .ttf/.otf paths, comma-separated -- pdf only",
2842
+ onChange: setFontFiles,
2843
+ onSubmit: submit,
2844
+ onCancel: cancel
2845
+ })] }),
2846
+ /* @__PURE__ */ jsx(Text, {
2847
+ dimColor: true,
2848
+ children: field === "destination" ? "Enter for fonts, Esc to cancel" : "Enter to render, Esc to cancel. The destination's own extension picks docx/odt/pdf"
2849
+ })
2850
+ ]
2851
+ });
2852
+ }
2853
+ //#endregion
2639
2854
  //#region src/tui/screens/editors/odb/table-list.tsx
2640
2855
  const TABLE_LIST_RESERVED_ROWS = 5;
2641
2856
  function OdbTableListScreen() {
@@ -6039,6 +6254,7 @@ function ScreenBody({ screen }) {
6039
6254
  case "filePicker": return /* @__PURE__ */ jsx(FilePickerScreen, {});
6040
6255
  case "newDocumentPicker": return /* @__PURE__ */ jsx(NewDocumentPickerScreen, {});
6041
6256
  case "bodyList": return format === "odt" ? /* @__PURE__ */ jsx(OdtBodyListScreen, {}) : /* @__PURE__ */ jsx(DocxBodyListScreen, {});
6257
+ case "docxExtras": return /* @__PURE__ */ jsx(DocxExtrasScreen, {});
6042
6258
  case "paragraphDetail": return /* @__PURE__ */ jsx(ParagraphDetailScreen, {});
6043
6259
  case "runEditor": return /* @__PURE__ */ jsx(RunEditorScreen, {});
6044
6260
  case "tableView": return /* @__PURE__ */ jsx(TableViewScreen, {});
@@ -6061,6 +6277,7 @@ function ScreenBody({ screen }) {
6061
6277
  case "odbFormDetail": return /* @__PURE__ */ jsx(OdbFormDetailScreen, {});
6062
6278
  case "odbReportList": return /* @__PURE__ */ jsx(OdbReportListScreen, {});
6063
6279
  case "odbReportDetail": return /* @__PURE__ */ jsx(OdbReportDetailScreen, {});
6280
+ case "odbReportRender": return /* @__PURE__ */ jsx(OdbReportRenderScreen, {});
6064
6281
  case "markdownLineList": return /* @__PURE__ */ jsx(MarkdownLineListScreen, {});
6065
6282
  case "markdownLineEditor": return /* @__PURE__ */ jsx(MarkdownLineEditorScreen, {});
6066
6283
  case "pdfPageList": return /* @__PURE__ */ jsx(PdfPageListScreen, {});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document-cli",
3
- "version": "1.2.11",
3
+ "version": "1.4.0",
4
4
  "description": "CLI and interactive Ink TUI for documents.js: every docx/pptx/odt/odp/ods/odg/odf/pdf/odm/odb/xlsx/markdown conversion, bridge, and editor as a scriptable command or a terminal app.",
5
5
  "type": "module",
6
6
  "repository": {