document-cli 1.10.0 → 1.11.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.
package/README.md CHANGED
@@ -41,7 +41,7 @@ graph TD
41
41
 
42
42
  ## Why
43
43
 
44
- `documents.js` is a library, not a tool — everything it does happens through function calls from TypeScript/JavaScript. Most people who want to convert a docx to a PDF, extract an `.odb` table to CSV, or poke at a PDF's structure from a terminal don't want to write a script to do it. `document-cli` is that missing entry point: every one of documents.js's 27 direct conversion pairs, its generic converter, its `.odm`/`.odb` extraction functions, and its PDF inspector become a single command-line invocation, and its six live-view editors (docx/pptx/odt/odp/ods/odg) plus its line-based markdown editor become a keyboard-driven terminal app that never needs a code editor open at all.
44
+ `documents.js` is a library, not a tool — everything it does happens through function calls from TypeScript/JavaScript. Most people who want to convert a docx to a PDF, extract an `.odb` table to CSV, or poke at a PDF's structure from a terminal don't want to write a script to do it. `document-cli` is that missing entry point: every one of documents.js's 27 direct conversion pairs, its generic converter, its `.odm`/`.odb` extraction functions, and its PDF inspector become a single command-line invocation, and its seven live-view editors (docx/pptx/odt/odp/ods/odg/markdown) become a keyboard-driven terminal app that never needs a code editor open at all.
45
45
 
46
46
  The CLI and the TUI are deliberately not two separate implementations of the same logic. The TUI's own document-opening, saving, and PDF-export code (`src/tui/format/`) calls the identical `documents.js` functions the CLI commands call — `openDocx`/`createDocx`/`docxToPdf` and their five siblings per format, plus `readOdbTables`/`readPdf` for the two read-only sources — so there is exactly one place either surface can drift from what documents.js itself does: nowhere.
47
47
 
@@ -165,9 +165,9 @@ Diagnostics and the summary line always go to stderr; stdout is reserved for the
165
165
 
166
166
  Launch it either bare (`document-cli`, with no arguments) or explicitly with `document-cli tui [file]` — both open the same app; the explicit form additionally opens `file` immediately, skipping the launcher screen. The TUI needs an interactive terminal: a bare invocation with redirected stdout prints help text instead, and an explicit `tui` invocation with redirected stdout fails outright, since there's no terminal for Ink to draw into.
167
167
 
168
- It supports the same six formats documents.js's live-view editors cover — docx, pptx, odt, odp, ods, odg — each with a full navigate/edit/save experience built on that format's editor (paragraphs and runs for docx/odt, slides and shapes for pptx/odp, sheets and cells for ods, pages and vectors/shapes for odg), plus undo (whole-document snapshots taken before each committed mutation), search, a command palette, and PDF export straight from the open document. On a pptx or odp slide, `a` from the shape list also adds a real table (rows then columns, a two-step prompt) alongside the existing textbox/image choices, and `n` opens the slide's own speaker notes for either format — `PptxSlide` and `OdpSlide` both carry a real `.notes` getter/setter, so notes editing was never odp-specific, only gated that way until this phase removed the gate.
168
+ It supports the same seven formats documents.js's live-view editors cover — docx, pptx, odt, odp, ods, odg, markdown — each with a full navigate/edit/save experience built on that format's editor (paragraphs and runs for docx/odt/markdown, slides and shapes for pptx/odp, sheets and cells for ods, pages and vectors/shapes for odg), plus undo (whole-document snapshots taken before each committed mutation), search, a command palette, and PDF export straight from the open document. On a pptx or odp slide, `a` from the shape list also adds a real table (rows then columns, a two-step prompt) alongside the existing textbox/image choices, and `n` opens the slide's own speaker notes for either format — `PptxSlide` and `OdpSlide` both carry a real `.notes` getter/setter, so notes editing was never odp-specific, only gated that way until this phase removed the gate.
169
169
 
170
- Markdown (`.md`/`.markdown`) is also a fully supported TUI format, but a structurally different one: documents.js has no `MarkdownEditor` the way it has a `DocxEditor`/`OdtEditor` (its own markdown support is a thin read/write pair over a plain string, not an `XmlElement` tree to hold a live view into), so the TUI edits a markdown document as its own raw source text rather than through a live-view editor object. Opening a `.md` file loads its bytes verbatim as a source string (`decodeMarkdownText`); the root screen lists that source split into lines, one row each, with search filtering by line content; selecting a line opens a line editor that replaces its text in place; `Ctrl+S` writes the (possibly edited) source straight back to disk (`encodeMarkdownText`), the identical byte↔text boundary opening it went through in reverse never through `readMarkdownContent`/`buildMarkdownText`. PDF export is the one place the `ContentDocument` pivot enters at all: exporting calls `markdownToPdf` directly on the current source text, run fresh on every export rather than kept in sync with in-progress edits. There is no "create a new markdown document" flow (documents.js has no `createMarkdown()`), so a markdown document can only be opened from an existing file, never created fresh from the TUI's new-document screen the way docx/pptx/odt/odp/ods/odg can.
170
+ Markdown (`.md`/`.markdown`) shares the same paragraph/run/table body-list screens docx and odt already use, through documents.js's own `MarkdownEditor` (`openMarkdown`/`createMarkdownEditor`) — a genuine live view over a mutable `ContentDocument`, the same live-view contract every other editor here follows, even though there is no `XmlElement` tree underneath it the way there is for docx/odt (`MarkdownEditor.toMarkdownText()` re-serialises the whole document fresh on every call, rather than exposing a `toBytes()`). Appending a paragraph, appending a run, and toggling bold/italic all go through the identical reducer actions docx/odt use; a markdown run has no underline, colour, font family, or font size at all (CommonMark/GFM has no construct for any of the four), so those keys along with image insertion, which `MarkdownParagraph` has no counterpart for — are simply absent from a markdown paragraph's own key hints rather than opening a prompt that could only end in a warning. A markdown table can be created and its cells edited through the same 'T' wizard and table-view screens docx/odt use, but GFM tables have no cell-merge concept, so a merge requested alongside table creation still creates the table (unmerged) and reports why the merge itself didn't happen. `:view-source` (markdown documents only) shows the literal text the document was opened with side by side with what a save would write right now — these can genuinely differ even with no edits made this session, from a heading-style, bullet-marker, or line-ending choice the writer normalises. Every save re-serialises the whole document fresh through `buildMarkdownText`, a deliberate, permanent consequence of structured editing rather than something to work around. Diagnostics from the read side (a clamped heading level, a dropped front-matter key, a fenced code block's own info string with nowhere to go, …) now surface into the same diagnostics panel a PDF export's own substitutions already populate, the moment a `.md` file is opened, not only on export. documents.js's own `createMarkdownEditor()` exists now, but this TUI does not yet wire a "new markdown document" flow into `:new`/the new-document picker, so a markdown document can still only be opened from an existing file.
171
171
 
172
172
  Three further formats open read-only: a `.odb` browses its tables and rows with no write path at all (documents.js's own `.odb` support has no write direction to offer), a `.pdf` browses its pages and positioned items rather than being edited in place, and a `.xlsx` opens as a converted PDF preview — documents.js has no xlsx editor to hold a live view into, so opening one runs `xlsxToPdf` once at open time and browses the result through the identical page-list/page-items/item-detail screens a real `.pdf` uses, with the original bytes kept alongside so a later export re-runs `xlsxToPdf` with the caller's own fonts and diagnostics rather than reusing the fixed preview conversion. A `.odb` additionally browses its *structure* alongside its data: `f` from the table list opens the form browser and `r` the report browser, each listing what the database declares and opening one to show it in full — a form's own data source and field-bound controls (sub-forms nested under their parent), a report's data-source command, band and group structure, and every `rpt:` formula. Both are rendered through the same `src/odb-structure.ts` the `odb-forms`/`odb-reports` commands print, so the two views cannot drift apart, and search filters by line (`/SUM` narrows a long report to its aggregate expressions). A standalone `.odf` formula document has no TUI editor either — nothing to edit interactively, only a PDF conversion.
173
173
 
package/dist/cli.js CHANGED
@@ -1194,7 +1194,7 @@ function registerSetMetadataCommand(program) {
1194
1194
  }
1195
1195
  //#endregion
1196
1196
  //#region package.json
1197
- var version = "1.10.0";
1197
+ var version = "1.11.0";
1198
1198
  //#endregion
1199
1199
  //#region src/program.ts
1200
1200
  function createProgram() {
@@ -1221,7 +1221,7 @@ function createProgram() {
1221
1221
  //#region src/cli.ts
1222
1222
  async function launchTui(startPath, signal) {
1223
1223
  try {
1224
- const { runTui } = await import("./tui-BVeYd0ry.js");
1224
+ const { runTui } = await import("./tui-DWXPty2k.js");
1225
1225
  await runTui({
1226
1226
  startPath,
1227
1227
  signal
package/dist/index.cjs CHANGED
@@ -1661,7 +1661,7 @@ function registerSetMetadataCommand(program) {
1661
1661
  }
1662
1662
  //#endregion
1663
1663
  //#region package.json
1664
- var version = "1.10.0";
1664
+ var version = "1.11.0";
1665
1665
  //#endregion
1666
1666
  //#region src/program.ts
1667
1667
  function createProgram() {
package/dist/index.js CHANGED
@@ -1660,7 +1660,7 @@ function registerSetMetadataCommand(program) {
1660
1660
  }
1661
1661
  //#endregion
1662
1662
  //#region package.json
1663
- var version = "1.10.0";
1663
+ var version = "1.11.0";
1664
1664
  //#endregion
1665
1665
  //#region src/program.ts
1666
1666
  function createProgram() {
@@ -1,6 +1,6 @@
1
1
  import { c as formatDocxExtrasLines, f as loadProvidedFonts, i as formatOdbReportLines, l as readInput, m as inferFormatFromExtension, n as describeOdbReport, o as formatMetadataLines, p as formatToExtension, r as formatOdbFormLines, t as describeOdbForm } from "./odb-structure-Du1w_hOU.js";
2
2
  import { readFile, writeFile } from "node:fs/promises";
3
- import { buildDocxPackage, buildOdtPackage, bytesToBase64, convertWordprocessingToLayout, createDocx, createFontMeasurer, createFontRegistry, createOdg, createOdp, createOds, createOdt, createPdf, createPptx, decodeMarkdownText, docxToPdf, encodeMarkdownText, encodePackage, hsqldbCellDisplayText, markdownToPdf, odgToPdf, odpToPdf, odsToPdf, odtToPdf, openDocx, openOdg, openOdp, openOds, openOdt, openPdf, openPptx, parseXml, pptxToPdf, readDocxContent, readDocxExtras, readMarkdownContent, readOdbForms, readOdbReportContent, readOdbReports, readOdbTables, readOdgContent, readOdpContent, readOdsContent, readOdtContent, readPdf, readPptxContent, rgbHexToColor, writePdf, xlsxToPdf } from "documents.js";
3
+ import { buildDocxPackage, buildOdtPackage, bytesToBase64, convertWordprocessingToLayout, createDocx, createFontMeasurer, createFontRegistry, createOdg, createOdp, createOds, createOdt, createPdf, createPptx, decodeMarkdownText, docxToPdf, encodeMarkdownText, encodePackage, hsqldbCellDisplayText, markdownToPdf, odgToPdf, odpToPdf, odsToPdf, odtToPdf, openDocx, openMarkdown, openOdg, openOdp, openOds, openOdt, openPdf, openPptx, parseXml, pptxToPdf, readDocxContent, readDocxExtras, readMarkdownContent, readOdbForms, readOdbReportContent, readOdbReports, readOdbTables, readOdgContent, readOdpContent, readOdsContent, readOdtContent, readPdf, readPptxContent, rgbHexToColor, writePdf, xlsxToPdf } from "documents.js";
4
4
  import { basename, dirname, extname, join } from "node:path";
5
5
  import { cellReference, columnIndexToLetters, decodePackage as decodePackage$1, encodePackage as encodePackage$1 } from "odf.js";
6
6
  import { readdirSync } from "node:fs";
@@ -39,7 +39,7 @@ async function exportToPdf(openDocument, destinationPath, options) {
39
39
  if (openDocument.format === "odb" || openDocument.format === "pdf") throw new Error(`A ${openDocument.format} document has no export-to-PDF path -- ${openDocument.format === "pdf" ? "save it directly instead" : "it is a read-only source with nothing to convert"}`);
40
40
  const pdfOptions = toPdfOptions(options, await loadProvidedFonts(options.fontFiles ?? [], { signal: options.signal }));
41
41
  if (openDocument.format === "markdown") {
42
- const pdfBytes = markdownToPdf(encodeMarkdownText(openDocument.source), pdfOptions);
42
+ const pdfBytes = markdownToPdf(encodeMarkdownText(openDocument.editor.toMarkdownText()), pdfOptions);
43
43
  await writeFile(destinationPath, pdfBytes);
44
44
  return;
45
45
  }
@@ -75,7 +75,7 @@ function detectFormat(path) {
75
75
  //#endregion
76
76
  //#region src/tui/format/open-document.ts
77
77
  const ODB_EXTENSION = ".odb";
78
- async function openDocumentAtPath(path) {
78
+ async function openDocumentAtPath(path, options = {}) {
79
79
  const bytes = new Uint8Array(await readFile(path));
80
80
  if (path.toLowerCase().endsWith(ODB_EXTENSION)) {
81
81
  const pkg = decodePackage$1(bytes);
@@ -129,11 +129,20 @@ async function openDocumentAtPath(path) {
129
129
  path
130
130
  };
131
131
  }
132
- case "markdown": return {
133
- format,
134
- source: decodeMarkdownText(bytes),
135
- path
136
- };
132
+ case "markdown": {
133
+ const text = decodeMarkdownText(bytes);
134
+ return {
135
+ format,
136
+ editor: openMarkdown(text, { sink: (diagnostic) => {
137
+ options.onDiagnostic?.({
138
+ severity: diagnostic.severity,
139
+ message: diagnostic.message
140
+ });
141
+ } }),
142
+ originalText: text,
143
+ path
144
+ };
145
+ }
137
146
  case "xlsx": return {
138
147
  format,
139
148
  layout: readPdf(xlsxToPdf(bytes)),
@@ -189,7 +198,7 @@ function createNewDocument(format) {
189
198
  async function saveDocumentTo(openDocument, path) {
190
199
  if (openDocument.format === "odb" || openDocument.format === "xlsx") throw new Error(`A ${openDocument.format} document is opened read-only and cannot be written back`);
191
200
  if (openDocument.format === "markdown") {
192
- await writeFile(path, encodeMarkdownText(openDocument.source));
201
+ await writeFile(path, encodeMarkdownText(openDocument.editor.toMarkdownText()));
193
202
  return;
194
203
  }
195
204
  await writeFile(path, openDocument.editor.toBytes());
@@ -233,7 +242,7 @@ function selectionKeyFor(screen) {
233
242
  case "pdfPageList":
234
243
  case "exportOptions":
235
244
  case "saveAsPrompt":
236
- case "markdownLineList":
245
+ case "viewSource":
237
246
  case "metadata": return screen.kind;
238
247
  case "filePicker": return `filePicker:${screen.purpose}:${screen.cwd}`;
239
248
  case "paragraphDetail":
@@ -256,19 +265,18 @@ function selectionKeyFor(screen) {
256
265
  case "odbFormDetail": return `odbFormDetail:${screen.formName}`;
257
266
  case "odbReportDetail": return `odbReportDetail:${screen.reportName}`;
258
267
  case "odbReportRender": return `odbReportRender:${screen.reportName}`;
259
- case "markdownLineEditor": return `markdownLineEditor:${screen.lineIndex}`;
260
268
  }
261
269
  }
262
270
  function rootScreenForFormat(format) {
263
271
  switch (format) {
264
272
  case "docx":
265
- case "odt": return { kind: "bodyList" };
273
+ case "odt":
274
+ case "markdown": return { kind: "bodyList" };
266
275
  case "pptx":
267
276
  case "odp": return { kind: "slideList" };
268
277
  case "ods": return { kind: "sheetList" };
269
278
  case "odg": return { kind: "pageList" };
270
279
  case "odb": return { kind: "odbTableList" };
271
- case "markdown": return { kind: "markdownLineList" };
272
280
  case "pdf":
273
281
  case "xlsx": return { kind: "pdfPageList" };
274
282
  }
@@ -397,7 +405,8 @@ function documentWithPath(doc, path) {
397
405
  };
398
406
  case "markdown": return {
399
407
  format: "markdown",
400
- source: doc.source,
408
+ editor: doc.editor,
409
+ originalText: doc.originalText,
401
410
  path
402
411
  };
403
412
  case "xlsx": return {
@@ -451,8 +460,11 @@ function reopenEditable(doc, bytes) {
451
460
  }
452
461
  }
453
462
  }
463
+ function toUndoSnapshot(doc) {
464
+ return doc.format === "markdown" ? encodeMarkdownText(doc.editor.toMarkdownText()) : doc.editor.toBytes();
465
+ }
454
466
  function mutate(state, doc, apply) {
455
- const snapshot = doc.editor.toBytes();
467
+ const snapshot = toUndoSnapshot(doc);
456
468
  apply();
457
469
  return {
458
470
  ...state,
@@ -492,22 +504,15 @@ function mergePptxTableCells(table, startRow, startColumn, rowSpan, colSpan) {
492
504
  }
493
505
  }
494
506
  }
495
- function mutateMarkdown(state, doc, source) {
496
- const snapshot = encodeMarkdownText(doc.source);
497
- return {
498
- ...state,
499
- openDocument: {
500
- ...doc,
501
- source
502
- },
503
- hasUnsavedChanges: true,
504
- undoStack: pushSnapshot(state.undoStack, snapshot)
505
- };
506
- }
507
507
  function wrongDocument(state, expected) {
508
508
  return withStatus(state, "warning", `That action needs ${expected}; the open document is ${state.openDocument === void 0 ? "no document" : state.openDocument.format}`);
509
509
  }
510
510
  function wordprocessingDocument(state) {
511
+ const doc = state.openDocument;
512
+ if (doc === void 0) return;
513
+ return doc.format === "docx" || doc.format === "odt" || doc.format === "markdown" ? doc : void 0;
514
+ }
515
+ function styledWordprocessingDocument(state) {
511
516
  const doc = state.openDocument;
512
517
  if (doc === void 0) return;
513
518
  return doc.format === "docx" || doc.format === "odt" ? doc : void 0;
@@ -571,17 +576,15 @@ function vectorHostDocument(state) {
571
576
  if (doc === void 0) return;
572
577
  return doc.format === "odg" || doc.format === "odp" ? doc : void 0;
573
578
  }
574
- function markdownDocument(state) {
575
- const doc = state.openDocument;
576
- if (doc === void 0) return;
577
- return doc.format === "markdown" ? doc : void 0;
578
- }
579
579
  function paragraphAt(doc, blockIndex) {
580
580
  return doc.editor.paragraphs()[blockIndex];
581
581
  }
582
582
  function tableAt(doc, tableIndex) {
583
583
  return doc.editor.tables()[tableIndex];
584
584
  }
585
+ function tableCellAt(table, row, column) {
586
+ return table.rows()[row]?.cells()[column];
587
+ }
585
588
  function shapeAt(doc, containerIndex, shapeIndex) {
586
589
  if (doc.format === "odg") return doc.editor.pages()[containerIndex]?.shapes()[shapeIndex];
587
590
  return doc.editor.slides()[containerIndex]?.shapes()[shapeIndex];
@@ -591,7 +594,7 @@ function sheetAt(doc, sheetIndex) {
591
594
  }
592
595
  function withRun(state, blockIndex, runIndex, apply) {
593
596
  const doc = wordprocessingDocument(state);
594
- if (doc === void 0) return wrongDocument(state, "a docx or odt document");
597
+ if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
595
598
  const paragraph = paragraphAt(doc, blockIndex);
596
599
  if (paragraph === void 0) return withStatus(state, "warning", `There is no paragraph at index ${blockIndex}`);
597
600
  const run = paragraph.runs()[runIndex];
@@ -600,6 +603,17 @@ function withRun(state, blockIndex, runIndex, apply) {
600
603
  apply(run);
601
604
  });
602
605
  }
606
+ function withStyledRun(state, blockIndex, runIndex, apply) {
607
+ const doc = styledWordprocessingDocument(state);
608
+ if (doc === void 0) return wrongDocument(state, "a docx or odt document");
609
+ const paragraph = doc.editor.paragraphs()[blockIndex];
610
+ if (paragraph === void 0) return withStatus(state, "warning", `There is no paragraph at index ${blockIndex}`);
611
+ const run = paragraph.runs()[runIndex];
612
+ if (run === void 0) return withStatus(state, "warning", `Paragraph ${blockIndex} has no run at index ${runIndex}`);
613
+ return mutate(state, doc, () => {
614
+ apply(run);
615
+ });
616
+ }
603
617
  function withShape(state, containerIndex, shapeIndex, apply) {
604
618
  const doc = shapeHostDocument(state);
605
619
  if (doc === void 0) return wrongDocument(state, "a pptx, odp or odg document");
@@ -761,8 +775,15 @@ function appReducer(state, action) {
761
775
  };
762
776
  case "APPEND_PARAGRAPH": {
763
777
  const doc = wordprocessingDocument(state);
764
- if (doc === void 0) return wrongDocument(state, "a docx or odt document");
778
+ if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
765
779
  return mutate(state, doc, () => {
780
+ if (doc.format === "markdown") {
781
+ doc.editor.body.appendParagraph({
782
+ text: action.text,
783
+ styleId: action.styleId
784
+ });
785
+ return;
786
+ }
766
787
  doc.editor.body.appendParagraph({
767
788
  text: action.text,
768
789
  styleId: action.styleId,
@@ -771,9 +792,9 @@ function appReducer(state, action) {
771
792
  });
772
793
  }
773
794
  case "SET_PARAGRAPH_ALIGNMENT": {
774
- const doc = wordprocessingDocument(state);
795
+ const doc = styledWordprocessingDocument(state);
775
796
  if (doc === void 0) return wrongDocument(state, "a docx or odt document");
776
- const paragraph = paragraphAt(doc, action.blockIndex);
797
+ const paragraph = doc.editor.paragraphs()[action.blockIndex];
777
798
  if (paragraph === void 0) return withStatus(state, "warning", `There is no paragraph at index ${action.blockIndex}`);
778
799
  return mutate(state, doc, () => {
779
800
  paragraph.alignment = action.alignment;
@@ -781,7 +802,7 @@ function appReducer(state, action) {
781
802
  }
782
803
  case "APPEND_RUN": {
783
804
  const doc = wordprocessingDocument(state);
784
- if (doc === void 0) return wrongDocument(state, "a docx or odt document");
805
+ if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
785
806
  const paragraph = paragraphAt(doc, action.blockIndex);
786
807
  if (paragraph === void 0) return withStatus(state, "warning", `There is no paragraph at index ${action.blockIndex}`);
787
808
  return mutate(state, doc, () => {
@@ -797,50 +818,60 @@ function appReducer(state, action) {
797
818
  case "TOGGLE_RUN_ITALIC": return withRun(state, action.blockIndex, action.runIndex, (run) => {
798
819
  run.italic = !run.italic;
799
820
  });
800
- case "TOGGLE_RUN_UNDERLINE": return withRun(state, action.blockIndex, action.runIndex, (run) => {
821
+ case "TOGGLE_RUN_UNDERLINE": return withStyledRun(state, action.blockIndex, action.runIndex, (run) => {
801
822
  run.underline = !run.underline;
802
823
  });
803
- case "SET_RUN_COLOR": return withRun(state, action.blockIndex, action.runIndex, (run) => {
824
+ case "SET_RUN_COLOR": return withStyledRun(state, action.blockIndex, action.runIndex, (run) => {
804
825
  run.color = action.color;
805
826
  });
806
- case "SET_RUN_FONT_FAMILY": return withRun(state, action.blockIndex, action.runIndex, (run) => {
827
+ case "SET_RUN_FONT_FAMILY": return withStyledRun(state, action.blockIndex, action.runIndex, (run) => {
807
828
  run.fontFamily = action.fontFamily;
808
829
  });
809
- case "SET_RUN_FONT_SIZE": return withRun(state, action.blockIndex, action.runIndex, (run) => {
830
+ case "SET_RUN_FONT_SIZE": return withStyledRun(state, action.blockIndex, action.runIndex, (run) => {
810
831
  run.sizePt = action.sizePt;
811
832
  });
812
833
  case "APPEND_TABLE": {
813
834
  const doc = wordprocessingDocument(state);
814
- if (doc === void 0) return wrongDocument(state, "a docx or odt document");
815
- return mutateGuarded(state, doc, () => {
835
+ if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
836
+ let mergeUnsupported = false;
837
+ const nextState = mutateGuarded(state, doc, () => {
816
838
  const table = doc.editor.body.appendTable({
817
839
  rows: action.rows,
818
840
  columns: action.columns
819
841
  });
820
- if (action.merge !== void 0) table.mergeCells(action.merge.startRow, action.merge.startColumn, action.merge.rowSpan, action.merge.colSpan);
842
+ if (action.merge === void 0) return;
843
+ if (!("mergeCells" in table)) {
844
+ mergeUnsupported = true;
845
+ return;
846
+ }
847
+ table.mergeCells(action.merge.startRow, action.merge.startColumn, action.merge.rowSpan, action.merge.colSpan);
821
848
  });
849
+ return mergeUnsupported ? withStatus(nextState, "warning", "Markdown tables do not support merged cells -- the table was created without merging") : nextState;
822
850
  }
823
851
  case "MERGE_TABLE_CELLS": {
824
852
  const doc = wordprocessingDocument(state);
825
- if (doc === void 0) return wrongDocument(state, "a docx or odt document");
853
+ if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
826
854
  const table = tableAt(doc, action.tableIndex);
827
855
  if (table === void 0) return withStatus(state, "warning", `There is no table at index ${action.tableIndex}`);
856
+ if (!("mergeCells" in table)) return withStatus(state, "warning", "Markdown tables do not support merged cells");
828
857
  return mutateGuarded(state, doc, () => {
829
858
  table.mergeCells(action.startRow, action.startColumn, action.rowSpan, action.colSpan);
830
859
  });
831
860
  }
832
861
  case "SET_TABLE_CELL_TEXT": {
833
862
  const doc = wordprocessingDocument(state);
834
- if (doc === void 0) return wrongDocument(state, "a docx or odt document");
863
+ if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
835
864
  const table = tableAt(doc, action.tableIndex);
836
865
  if (table === void 0) return withStatus(state, "warning", `There is no table at index ${action.tableIndex}`);
866
+ const cell = tableCellAt(table, action.row, action.column);
867
+ if (cell === void 0) return withStatus(state, "warning", `There is no cell at row ${action.row}, column ${action.column} of table ${action.tableIndex}`);
837
868
  return mutate(state, doc, () => {
838
- setCellText(table.cell(action.row, action.column), action.text);
869
+ setCellText(cell, action.text);
839
870
  });
840
871
  }
841
872
  case "ADD_LIST_ITEM": {
842
873
  const doc = wordprocessingDocument(state);
843
- if (doc === void 0) return wrongDocument(state, "a docx or odt document");
874
+ if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
844
875
  if (doc.format === "odt") {
845
876
  const list = doc.editor.lists()[action.blockIndex];
846
877
  if (list === void 0) return withStatus(state, "warning", `There is no list at index ${action.blockIndex}`);
@@ -859,7 +890,7 @@ function appReducer(state, action) {
859
890
  }
860
891
  case "SET_LIST_ITEM_TEXT": {
861
892
  const doc = wordprocessingDocument(state);
862
- if (doc === void 0) return wrongDocument(state, "a docx or odt document");
893
+ if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
863
894
  if (doc.format !== "odt") return wrongDocument(state, "an odt document (lists are an odt-only concept)");
864
895
  const list = doc.editor.lists()[action.blockIndex];
865
896
  if (list === void 0) return withStatus(state, "warning", `There is no list at index ${action.blockIndex}`);
@@ -871,16 +902,16 @@ function appReducer(state, action) {
871
902
  }
872
903
  case "ADD_LIST": {
873
904
  const doc = wordprocessingDocument(state);
874
- if (doc === void 0) return wrongDocument(state, "a docx or odt document");
905
+ if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
875
906
  if (doc.format !== "odt") return wrongDocument(state, "an odt document (lists are an odt-only concept)");
876
907
  return mutate(state, doc, () => {
877
908
  doc.editor.body.appendList();
878
909
  });
879
910
  }
880
911
  case "INSERT_PARAGRAPH_IMAGE": {
881
- const doc = wordprocessingDocument(state);
912
+ const doc = styledWordprocessingDocument(state);
882
913
  if (doc === void 0) return wrongDocument(state, "a docx or odt document");
883
- const paragraph = paragraphAt(doc, action.blockIndex);
914
+ const paragraph = doc.editor.paragraphs()[action.blockIndex];
884
915
  if (paragraph === void 0) return withStatus(state, "warning", `There is no paragraph at index ${action.blockIndex}`);
885
916
  return mutate(state, doc, () => {
886
917
  paragraph.insertImageAfter({
@@ -1259,11 +1290,6 @@ function appReducer(state, action) {
1259
1290
  item.widthPt = action.widthPt;
1260
1291
  item.heightPt = action.heightPt;
1261
1292
  });
1262
- case "SET_MARKDOWN_SOURCE": {
1263
- const doc = markdownDocument(state);
1264
- if (doc === void 0) return wrongDocument(state, "a markdown document");
1265
- return mutateMarkdown(state, doc, action.source);
1266
- }
1267
1293
  case "APPEND_DIAGNOSTIC": return {
1268
1294
  ...state,
1269
1295
  diagnostics: [...state.diagnostics, action.diagnostic]
@@ -1297,7 +1323,7 @@ function appReducer(state, action) {
1297
1323
  if (snapshot === void 0) return withStatus(state, "info", "There is nothing to undo");
1298
1324
  const restored = doc.format === "markdown" ? {
1299
1325
  ...doc,
1300
- source: decodeMarkdownText(snapshot)
1326
+ editor: openMarkdown(decodeMarkdownText(snapshot))
1301
1327
  } : reopenEditable(doc, snapshot);
1302
1328
  return withStatus({
1303
1329
  ...state,
@@ -1413,6 +1439,11 @@ const COMMANDS = [
1413
1439
  usage: ":undo",
1414
1440
  description: "Undo the last change"
1415
1441
  },
1442
+ {
1443
+ name: "view-source",
1444
+ usage: ":view-source",
1445
+ description: "Compare a markdown document as opened vs. as it will save now"
1446
+ },
1416
1447
  {
1417
1448
  name: "help",
1418
1449
  usage: ":help",
@@ -1531,7 +1562,12 @@ async function runCommand(line, state, dispatch) {
1531
1562
  dispatch({
1532
1563
  type: "OPEN_FILE_SUCCESS",
1533
1564
  path,
1534
- doc: await openDocumentAtPath(path)
1565
+ doc: await openDocumentAtPath(path, { onDiagnostic: (diagnostic) => {
1566
+ dispatch({
1567
+ type: "APPEND_DIAGNOSTIC",
1568
+ diagnostic
1569
+ });
1570
+ } })
1535
1571
  });
1536
1572
  } catch (error) {
1537
1573
  dispatch({
@@ -1548,6 +1584,16 @@ async function runCommand(line, state, dispatch) {
1548
1584
  case "undo":
1549
1585
  dispatch({ type: "UNDO" });
1550
1586
  return;
1587
+ case "view-source":
1588
+ if (doc?.format !== "markdown") {
1589
+ warn(dispatch, ":view-source only applies to an open markdown document");
1590
+ return;
1591
+ }
1592
+ dispatch({
1593
+ type: "PUSH_SCREEN",
1594
+ screen: { kind: "viewSource" }
1595
+ });
1596
+ return;
1551
1597
  case "help":
1552
1598
  dispatch({
1553
1599
  type: "OPEN_OVERLAY",
@@ -2218,9 +2264,12 @@ function parseNumberField(raw, fallback) {
2218
2264
  }
2219
2265
  //#endregion
2220
2266
  //#region src/tui/screens/shared/paragraph-family.tsx
2267
+ function supportsRunStyleExtras(run) {
2268
+ return "underline" in run;
2269
+ }
2221
2270
  function paragraphFamilyDocument(openDocument) {
2222
2271
  if (openDocument === void 0) return;
2223
- return openDocument.format === "docx" || openDocument.format === "odt" ? openDocument : void 0;
2272
+ return openDocument.format === "docx" || openDocument.format === "odt" || openDocument.format === "markdown" ? openDocument : void 0;
2224
2273
  }
2225
2274
  function liveParagraphAt(doc, blockIndex) {
2226
2275
  return doc.editor.paragraphs()[blockIndex];
@@ -2746,14 +2795,17 @@ function ParagraphRunsView(props) {
2746
2795
  dimColor: true,
2747
2796
  children: "(no runs -- press 'a' to append one)"
2748
2797
  });
2749
- return /* @__PURE__ */ jsx(Box, { children: props.runs.map((run, index) => /* @__PURE__ */ jsx(Text, {
2750
- bold: run.bold,
2751
- italic: run.italic,
2752
- underline: run.underline,
2753
- color: run.color === void 0 ? void 0 : layoutColorToHex(run.color),
2754
- inverse: index === props.selectedRunIndex,
2755
- children: run.text.length === 0 ? "<empty run>" : run.text
2756
- }, index)) });
2798
+ return /* @__PURE__ */ jsx(Box, { children: props.runs.map((run, index) => {
2799
+ const styled = supportsRunStyleExtras(run);
2800
+ return /* @__PURE__ */ jsx(Text, {
2801
+ bold: run.bold,
2802
+ italic: run.italic,
2803
+ underline: styled && run.underline,
2804
+ color: styled && run.color !== void 0 ? layoutColorToHex(run.color) : void 0,
2805
+ inverse: index === props.selectedRunIndex,
2806
+ children: run.text.length === 0 ? "<empty run>" : run.text
2807
+ }, index);
2808
+ }) });
2757
2809
  }
2758
2810
  function ParagraphDetailScreen() {
2759
2811
  const state = useAppState();
@@ -2826,7 +2878,7 @@ function ParagraphDetailScreen() {
2826
2878
  });
2827
2879
  return;
2828
2880
  }
2829
- if (input === "I") {
2881
+ if (input === "I" && doc?.format !== "markdown") {
2830
2882
  setImageWizardOpen(true);
2831
2883
  return;
2832
2884
  }
@@ -2851,6 +2903,7 @@ function ParagraphDetailScreen() {
2851
2903
  });
2852
2904
  return;
2853
2905
  }
2906
+ if (!supportsRunStyleExtras(selectedRun)) return;
2854
2907
  if (input === "u") {
2855
2908
  dispatch({
2856
2909
  type: "TOGGLE_RUN_UNDERLINE",
@@ -2875,7 +2928,7 @@ function ParagraphDetailScreen() {
2875
2928
  });
2876
2929
  if (doc === void 0) return /* @__PURE__ */ jsx(Text, {
2877
2930
  color: "red",
2878
- children: "ParagraphDetailScreen requires an open docx or odt document."
2931
+ children: "ParagraphDetailScreen requires an open docx, odt or markdown document."
2879
2932
  });
2880
2933
  if (paragraph === void 0) return /* @__PURE__ */ jsxs(Text, {
2881
2934
  color: "red",
@@ -2962,7 +3015,7 @@ function ParagraphDetailScreen() {
2962
3015
  placeholder: "e.g. 12",
2963
3016
  onChange: setFontSizeInput,
2964
3017
  onSubmit: (value) => {
2965
- const sizePt = parseNumberField(value, selectedRun?.sizePt ?? DEFAULT_RUN_SIZE_PT);
3018
+ const sizePt = parseNumberField(value, selectedRun !== void 0 && supportsRunStyleExtras(selectedRun) ? selectedRun.sizePt ?? DEFAULT_RUN_SIZE_PT : DEFAULT_RUN_SIZE_PT);
2966
3019
  if (sizePt <= 0) dispatch({
2967
3020
  type: "SET_STATUS",
2968
3021
  severity: "warning",
@@ -3015,7 +3068,12 @@ function ParagraphDetailScreen() {
3015
3068
  /* @__PURE__ */ jsxs(Text, {
3016
3069
  dimColor: true,
3017
3070
  children: [
3018
- "<- / -> move, Enter edit text, b/i/u toggle, c colour, f font, s size, a append run, I image",
3071
+ "<- / -> move, Enter edit text, b/i",
3072
+ doc.format === "markdown" ? "" : "/u",
3073
+ " toggle",
3074
+ doc.format === "markdown" ? "" : ", c colour, f font, s size",
3075
+ ", a append run",
3076
+ doc.format === "markdown" ? "" : ", I image",
3019
3077
  doc.format === "docx" ? ", m formula" : "",
3020
3078
  ", Esc back"
3021
3079
  ]
@@ -3050,7 +3108,7 @@ function RunEditorScreen() {
3050
3108
  });
3051
3109
  if (doc === void 0) return /* @__PURE__ */ jsx(Text, {
3052
3110
  color: "red",
3053
- children: "RunEditorScreen requires an open docx or odt document."
3111
+ children: "RunEditorScreen requires an open docx, odt or markdown document."
3054
3112
  });
3055
3113
  const paragraph = liveParagraphAt(doc, screen.blockIndex);
3056
3114
  if (paragraph === void 0) return /* @__PURE__ */ jsxs(Text, {
@@ -3209,7 +3267,7 @@ function TableViewScreen() {
3209
3267
  });
3210
3268
  if (doc === void 0) return /* @__PURE__ */ jsx(Text, {
3211
3269
  color: "red",
3212
- children: "TableViewScreen requires an open docx or odt document."
3270
+ children: "TableViewScreen requires an open docx, odt or markdown document."
3213
3271
  });
3214
3272
  if (table === void 0) return /* @__PURE__ */ jsxs(Text, {
3215
3273
  color: "red",
@@ -3283,7 +3341,7 @@ function TableCellDetailScreen() {
3283
3341
  });
3284
3342
  if (doc === void 0) return /* @__PURE__ */ jsx(Text, {
3285
3343
  color: "red",
3286
- children: "TableCellDetailScreen requires an open docx or odt document."
3344
+ children: "TableCellDetailScreen requires an open docx, odt or markdown document."
3287
3345
  });
3288
3346
  if (cell === void 0) return /* @__PURE__ */ jsxs(Text, {
3289
3347
  color: "red",
@@ -3430,123 +3488,67 @@ function DocxExtrasScreen() {
3430
3488
  });
3431
3489
  }
3432
3490
  //#endregion
3433
- //#region src/tui/screens/editors/markdown/shared.ts
3434
- function requireMarkdownDocument(openDocument) {
3435
- 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.");
3436
- return openDocument;
3437
- }
3438
- //#endregion
3439
- //#region src/tui/screens/editors/markdown/line-list.tsx
3440
- function MarkdownLineListScreen() {
3441
- const state = useAppState();
3442
- const dispatch = useAppDispatch();
3443
- const doc = requireMarkdownDocument(state.openDocument);
3444
- const query = state.searchQuery.trim().toLowerCase();
3445
- const allLines = doc.source.split("\n").map((line, lineIndex) => ({
3446
- line,
3447
- lineIndex
3448
- }));
3449
- const lines = query === "" ? allLines : allLines.filter((entry) => entry.line.toLowerCase().includes(query));
3450
- const { selectedIndex } = useNavigationInput({
3451
- itemCount: lines.length,
3452
- onSelect: (index) => {
3453
- const entry = lines[index];
3454
- if (entry === void 0) return;
3455
- dispatch({
3456
- type: "PUSH_SCREEN",
3457
- screen: {
3458
- kind: "markdownLineEditor",
3459
- lineIndex: entry.lineIndex
3460
- }
3461
- });
3462
- },
3463
- onBack: () => {
3464
- dispatch({ type: "POP_SCREEN" });
3465
- },
3466
- isActive: !anyOverlayOpen(state)
3467
- });
3468
- return /* @__PURE__ */ jsxs(Box, {
3469
- flexDirection: "column",
3470
- children: [/* @__PURE__ */ jsxs(Text, {
3471
- bold: true,
3472
- children: [
3473
- "Lines (",
3474
- lines.length,
3475
- " of ",
3476
- allLines.length,
3477
- ")"
3478
- ]
3479
- }), /* @__PURE__ */ jsx(ListView, {
3480
- items: lines,
3481
- selectedIndex,
3482
- emptyMessage: query === "" ? "This document has no lines." : `No lines match "${state.searchQuery}".`,
3483
- renderItem: ({ line, lineIndex }, isSelected) => /* @__PURE__ */ jsxs(Text, {
3484
- color: isSelected ? "cyan" : void 0,
3485
- inverse: isSelected,
3486
- children: [
3487
- lineIndex + 1,
3488
- ": ",
3489
- line === "" ? "(blank)" : line
3490
- ]
3491
- })
3492
- })]
3493
- });
3494
- }
3495
- //#endregion
3496
- //#region src/tui/screens/editors/markdown/line-editor.tsx
3497
- function MarkdownLineEditorScreen() {
3491
+ //#region src/tui/screens/editors/markdown/view-source.tsx
3492
+ function MarkdownViewSourceScreen() {
3498
3493
  const state = useAppState();
3499
3494
  const dispatch = useAppDispatch();
3500
3495
  const screen = currentScreen(state);
3501
3496
  const doc = state.openDocument;
3502
- if (screen.kind !== "markdownLineEditor") return /* @__PURE__ */ jsx(Text, {
3497
+ useInput((_input, key) => {
3498
+ if (key.escape) dispatch({ type: "POP_SCREEN" });
3499
+ }, { isActive: !anyOverlayOpen(state) && screen.kind === "viewSource" });
3500
+ if (screen.kind !== "viewSource") return /* @__PURE__ */ jsx(Text, {
3503
3501
  color: "red",
3504
- children: "MarkdownLineEditorScreen rendered outside a markdownLineEditor screen."
3502
+ children: "MarkdownViewSourceScreen rendered outside a viewSource screen."
3505
3503
  });
3506
3504
  if (doc?.format !== "markdown") return /* @__PURE__ */ jsx(Text, {
3507
3505
  color: "red",
3508
- children: "MarkdownLineEditorScreen requires an open markdown document."
3509
- });
3510
- const lines = doc.source.split("\n");
3511
- const line = lines[screen.lineIndex];
3512
- if (line === void 0) return /* @__PURE__ */ jsxs(Text, {
3513
- color: "red",
3514
- children: [
3515
- "There is no line at index ",
3516
- screen.lineIndex,
3517
- "."
3518
- ]
3506
+ children: "view-source requires an open markdown document."
3519
3507
  });
3508
+ const asItWillSaveNow = doc.editor.toMarkdownText();
3520
3509
  return /* @__PURE__ */ jsxs(Box, {
3521
3510
  flexDirection: "column",
3522
3511
  children: [
3523
- /* @__PURE__ */ jsxs(Text, {
3512
+ /* @__PURE__ */ jsx(Text, {
3524
3513
  bold: true,
3525
- children: ["Edit line ", screen.lineIndex + 1]
3514
+ children: "As opened"
3526
3515
  }),
3527
- /* @__PURE__ */ jsx(RunTextEditor, {
3528
- initialText: line,
3529
- onCommit: (text) => {
3530
- const nextLines = [...lines];
3531
- nextLines[screen.lineIndex] = text;
3532
- dispatch({
3533
- type: "SET_MARKDOWN_SOURCE",
3534
- source: nextLines.join("\n")
3535
- });
3536
- dispatch({ type: "POP_SCREEN" });
3537
- },
3538
- onCancel: () => {
3539
- dispatch({ type: "POP_SCREEN" });
3540
- }
3516
+ /* @__PURE__ */ jsx(Text, { children: doc.originalText ?? "(this document was created fresh, with no original text to compare against)" }),
3517
+ /* @__PURE__ */ jsx(Text, {
3518
+ bold: true,
3519
+ children: "As it will save right now"
3541
3520
  }),
3521
+ /* @__PURE__ */ jsx(Text, { children: asItWillSaveNow }),
3542
3522
  /* @__PURE__ */ jsx(Text, {
3543
3523
  dimColor: true,
3544
- children: "Enter to commit, Esc to discard"
3524
+ children: "Esc back"
3545
3525
  })
3546
3526
  ]
3547
3527
  });
3548
3528
  }
3549
3529
  //#endregion
3530
+ //#region src/tui/screens/editors/markdown/index.tsx
3531
+ function MarkdownBodyListScreen() {
3532
+ const state = useAppState();
3533
+ const dispatch = useAppDispatch();
3534
+ const doc = state.openDocument;
3535
+ if (doc?.format !== "markdown") return /* @__PURE__ */ jsxs(Text, {
3536
+ color: "red",
3537
+ children: [
3538
+ "MarkdownBodyListScreen requires an open markdown document, found ",
3539
+ doc === void 0 ? "no open document" : doc.format,
3540
+ "."
3541
+ ]
3542
+ });
3543
+ const adapter = createParagraphFamilyAdapter({
3544
+ formatLabel: "markdown",
3545
+ paragraphs: () => doc.editor.paragraphs(),
3546
+ tables: () => doc.editor.tables(),
3547
+ dispatch
3548
+ });
3549
+ return /* @__PURE__ */ jsx(ParagraphFamilyBodyList, { adapter });
3550
+ }
3551
+ //#endregion
3550
3552
  //#region src/tui/screens/editors/odb/shared.ts
3551
3553
  function requireOdbDocument(openDocument) {
3552
3554
  if (openDocument?.format !== "odb") throw new Error("An .odb browsing screen rendered without an open .odb document; the app router only reaches this screen group from odbTableList, which is only ever the root screen of an open .odb document.");
@@ -8545,7 +8547,12 @@ function FilePickerScreen() {
8545
8547
  function openAtPath(path) {
8546
8548
  (async () => {
8547
8549
  try {
8548
- const doc = await openDocumentAtPath(path);
8550
+ const doc = await openDocumentAtPath(path, { onDiagnostic: (diagnostic) => {
8551
+ dispatch({
8552
+ type: "APPEND_DIAGNOSTIC",
8553
+ diagnostic
8554
+ });
8555
+ } });
8549
8556
  dispatch({
8550
8557
  type: "OPEN_FILE_SUCCESS",
8551
8558
  path,
@@ -8896,7 +8903,7 @@ function metadataFor(doc) {
8896
8903
  case "odp": return readOdpContent(doc.editor.toPackage()).metadata;
8897
8904
  case "ods": return readOdsContent(doc.editor.toPackage()).metadata;
8898
8905
  case "odg": return readOdgContent(doc.editor.toPackage()).metadata;
8899
- case "markdown": return readMarkdownContent(doc.source).metadata;
8906
+ case "markdown": return readMarkdownContent(doc.editor.toMarkdownText()).metadata;
8900
8907
  case "pdf":
8901
8908
  case "xlsx": return doc.layout.metadata;
8902
8909
  case "odb": throw new Error("A .odb database has no document-level metadata -- it is a table/form/report container, not a single document with its own title/author/etc.");
@@ -8961,7 +8968,9 @@ function ScreenBody({ screen }) {
8961
8968
  case "launcher": return /* @__PURE__ */ jsx(LauncherScreen, {});
8962
8969
  case "filePicker": return /* @__PURE__ */ jsx(FilePickerScreen, {});
8963
8970
  case "newDocumentPicker": return /* @__PURE__ */ jsx(NewDocumentPickerScreen, {});
8964
- case "bodyList": return format === "odt" ? /* @__PURE__ */ jsx(OdtBodyListScreen, {}) : /* @__PURE__ */ jsx(DocxBodyListScreen, {});
8971
+ case "bodyList":
8972
+ if (format === "odt") return /* @__PURE__ */ jsx(OdtBodyListScreen, {});
8973
+ return format === "markdown" ? /* @__PURE__ */ jsx(MarkdownBodyListScreen, {}) : /* @__PURE__ */ jsx(DocxBodyListScreen, {});
8965
8974
  case "docxExtras": return /* @__PURE__ */ jsx(DocxExtrasScreen, {});
8966
8975
  case "paragraphDetail": return /* @__PURE__ */ jsx(ParagraphDetailScreen, {});
8967
8976
  case "runEditor": return /* @__PURE__ */ jsx(RunEditorScreen, {});
@@ -8987,8 +8996,7 @@ function ScreenBody({ screen }) {
8987
8996
  case "odbReportList": return /* @__PURE__ */ jsx(OdbReportListScreen, {});
8988
8997
  case "odbReportDetail": return /* @__PURE__ */ jsx(OdbReportDetailScreen, {});
8989
8998
  case "odbReportRender": return /* @__PURE__ */ jsx(OdbReportRenderScreen, {});
8990
- case "markdownLineList": return /* @__PURE__ */ jsx(MarkdownLineListScreen, {});
8991
- case "markdownLineEditor": return /* @__PURE__ */ jsx(MarkdownLineEditorScreen, {});
8999
+ case "viewSource": return /* @__PURE__ */ jsx(MarkdownViewSourceScreen, {});
8992
9000
  case "pdfPageList": return /* @__PURE__ */ jsx(PdfPageListScreen, {});
8993
9001
  case "pdfPageItems": return /* @__PURE__ */ jsx(PdfPageItemsScreen, {});
8994
9002
  case "pdfItemDetail": return /* @__PURE__ */ jsx(PdfItemDetailScreen, {});
@@ -9034,7 +9042,12 @@ function AppShell({ startPath }) {
9034
9042
  let cancelled = false;
9035
9043
  (async () => {
9036
9044
  try {
9037
- const doc = await openDocumentAtPath(startPath);
9045
+ const doc = await openDocumentAtPath(startPath, { onDiagnostic: (diagnostic) => {
9046
+ dispatch({
9047
+ type: "APPEND_DIAGNOSTIC",
9048
+ diagnostic
9049
+ });
9050
+ } });
9038
9051
  if (!cancelled) dispatch({
9039
9052
  type: "OPEN_FILE_SUCCESS",
9040
9053
  path: startPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document-cli",
3
- "version": "1.10.0",
3
+ "version": "1.11.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": {