document-cli 1.9.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 +3 -3
- package/dist/cli.js +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{tui-D9SLI7oT.js → tui-DWXPty2k.js} +1554 -440
- package/package.json +1 -1
|
@@ -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, createPptx, decodeMarkdownText, docxToPdf, encodeMarkdownText, encodePackage, hsqldbCellDisplayText, markdownToPdf, odgToPdf, odpToPdf, odsToPdf, odtToPdf, openDocx, openOdg, openOdp, openOds, openOdt, 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";
|
|
@@ -36,10 +36,10 @@ function toPdfOptions(options, fonts) {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
async function exportToPdf(openDocument, destinationPath, options) {
|
|
39
|
-
if (openDocument.format === "odb" || openDocument.format === "pdf") throw new Error(`A ${openDocument.format} document is
|
|
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.
|
|
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);
|
|
@@ -120,16 +120,29 @@ async function openDocumentAtPath(path) {
|
|
|
120
120
|
editor: openOdg(bytes),
|
|
121
121
|
path
|
|
122
122
|
};
|
|
123
|
-
case "pdf":
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
123
|
+
case "pdf": {
|
|
124
|
+
const editor = openPdf(bytes);
|
|
125
|
+
return {
|
|
126
|
+
format,
|
|
127
|
+
editor,
|
|
128
|
+
layout: editor.toLayoutDocument(),
|
|
129
|
+
path
|
|
130
|
+
};
|
|
131
|
+
}
|
|
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
|
+
}
|
|
133
146
|
case "xlsx": return {
|
|
134
147
|
format,
|
|
135
148
|
layout: readPdf(xlsxToPdf(bytes)),
|
|
@@ -171,12 +184,21 @@ function createNewDocument(format) {
|
|
|
171
184
|
editor: createOdg(),
|
|
172
185
|
path: void 0
|
|
173
186
|
};
|
|
187
|
+
case "pdf": {
|
|
188
|
+
const editor = createPdf();
|
|
189
|
+
return {
|
|
190
|
+
format,
|
|
191
|
+
editor,
|
|
192
|
+
layout: editor.toLayoutDocument(),
|
|
193
|
+
path: void 0
|
|
194
|
+
};
|
|
195
|
+
}
|
|
174
196
|
}
|
|
175
197
|
}
|
|
176
198
|
async function saveDocumentTo(openDocument, path) {
|
|
177
|
-
if (openDocument.format === "odb" || openDocument.format === "
|
|
199
|
+
if (openDocument.format === "odb" || openDocument.format === "xlsx") throw new Error(`A ${openDocument.format} document is opened read-only and cannot be written back`);
|
|
178
200
|
if (openDocument.format === "markdown") {
|
|
179
|
-
await writeFile(path, encodeMarkdownText(openDocument.
|
|
201
|
+
await writeFile(path, encodeMarkdownText(openDocument.editor.toMarkdownText()));
|
|
180
202
|
return;
|
|
181
203
|
}
|
|
182
204
|
await writeFile(path, openDocument.editor.toBytes());
|
|
@@ -189,7 +211,8 @@ const EDITABLE_FORMATS = {
|
|
|
189
211
|
odt: true,
|
|
190
212
|
odp: true,
|
|
191
213
|
ods: true,
|
|
192
|
-
odg: true
|
|
214
|
+
odg: true,
|
|
215
|
+
pdf: true
|
|
193
216
|
};
|
|
194
217
|
const WRITABLE_FORMATS = {
|
|
195
218
|
...EDITABLE_FORMATS,
|
|
@@ -219,7 +242,7 @@ function selectionKeyFor(screen) {
|
|
|
219
242
|
case "pdfPageList":
|
|
220
243
|
case "exportOptions":
|
|
221
244
|
case "saveAsPrompt":
|
|
222
|
-
case "
|
|
245
|
+
case "viewSource":
|
|
223
246
|
case "metadata": return screen.kind;
|
|
224
247
|
case "filePicker": return `filePicker:${screen.purpose}:${screen.cwd}`;
|
|
225
248
|
case "paragraphDetail":
|
|
@@ -242,19 +265,18 @@ function selectionKeyFor(screen) {
|
|
|
242
265
|
case "odbFormDetail": return `odbFormDetail:${screen.formName}`;
|
|
243
266
|
case "odbReportDetail": return `odbReportDetail:${screen.reportName}`;
|
|
244
267
|
case "odbReportRender": return `odbReportRender:${screen.reportName}`;
|
|
245
|
-
case "markdownLineEditor": return `markdownLineEditor:${screen.lineIndex}`;
|
|
246
268
|
}
|
|
247
269
|
}
|
|
248
270
|
function rootScreenForFormat(format) {
|
|
249
271
|
switch (format) {
|
|
250
272
|
case "docx":
|
|
251
|
-
case "odt":
|
|
273
|
+
case "odt":
|
|
274
|
+
case "markdown": return { kind: "bodyList" };
|
|
252
275
|
case "pptx":
|
|
253
276
|
case "odp": return { kind: "slideList" };
|
|
254
277
|
case "ods": return { kind: "sheetList" };
|
|
255
278
|
case "odg": return { kind: "pageList" };
|
|
256
279
|
case "odb": return { kind: "odbTableList" };
|
|
257
|
-
case "markdown": return { kind: "markdownLineList" };
|
|
258
280
|
case "pdf":
|
|
259
281
|
case "xlsx": return { kind: "pdfPageList" };
|
|
260
282
|
}
|
|
@@ -368,6 +390,12 @@ function documentWithPath(doc, path) {
|
|
|
368
390
|
editor: doc.editor,
|
|
369
391
|
path
|
|
370
392
|
};
|
|
393
|
+
case "pdf": return {
|
|
394
|
+
format: "pdf",
|
|
395
|
+
editor: doc.editor,
|
|
396
|
+
layout: doc.layout,
|
|
397
|
+
path
|
|
398
|
+
};
|
|
371
399
|
case "odb": return {
|
|
372
400
|
format: "odb",
|
|
373
401
|
tables: doc.tables,
|
|
@@ -377,12 +405,8 @@ function documentWithPath(doc, path) {
|
|
|
377
405
|
};
|
|
378
406
|
case "markdown": return {
|
|
379
407
|
format: "markdown",
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
};
|
|
383
|
-
case "pdf": return {
|
|
384
|
-
format: "pdf",
|
|
385
|
-
layout: doc.layout,
|
|
408
|
+
editor: doc.editor,
|
|
409
|
+
originalText: doc.originalText,
|
|
386
410
|
path
|
|
387
411
|
};
|
|
388
412
|
case "xlsx": return {
|
|
@@ -425,10 +449,22 @@ function reopenEditable(doc, bytes) {
|
|
|
425
449
|
editor: openOdg(bytes),
|
|
426
450
|
path: doc.path
|
|
427
451
|
};
|
|
452
|
+
case "pdf": {
|
|
453
|
+
const editor = openPdf(bytes);
|
|
454
|
+
return {
|
|
455
|
+
format: "pdf",
|
|
456
|
+
editor,
|
|
457
|
+
layout: editor.toLayoutDocument(),
|
|
458
|
+
path: doc.path
|
|
459
|
+
};
|
|
460
|
+
}
|
|
428
461
|
}
|
|
429
462
|
}
|
|
463
|
+
function toUndoSnapshot(doc) {
|
|
464
|
+
return doc.format === "markdown" ? encodeMarkdownText(doc.editor.toMarkdownText()) : doc.editor.toBytes();
|
|
465
|
+
}
|
|
430
466
|
function mutate(state, doc, apply) {
|
|
431
|
-
const snapshot = doc
|
|
467
|
+
const snapshot = toUndoSnapshot(doc);
|
|
432
468
|
apply();
|
|
433
469
|
return {
|
|
434
470
|
...state,
|
|
@@ -468,22 +504,15 @@ function mergePptxTableCells(table, startRow, startColumn, rowSpan, colSpan) {
|
|
|
468
504
|
}
|
|
469
505
|
}
|
|
470
506
|
}
|
|
471
|
-
function mutateMarkdown(state, doc, source) {
|
|
472
|
-
const snapshot = encodeMarkdownText(doc.source);
|
|
473
|
-
return {
|
|
474
|
-
...state,
|
|
475
|
-
openDocument: {
|
|
476
|
-
...doc,
|
|
477
|
-
source
|
|
478
|
-
},
|
|
479
|
-
hasUnsavedChanges: true,
|
|
480
|
-
undoStack: pushSnapshot(state.undoStack, snapshot)
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
507
|
function wrongDocument(state, expected) {
|
|
484
508
|
return withStatus(state, "warning", `That action needs ${expected}; the open document is ${state.openDocument === void 0 ? "no document" : state.openDocument.format}`);
|
|
485
509
|
}
|
|
486
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) {
|
|
487
516
|
const doc = state.openDocument;
|
|
488
517
|
if (doc === void 0) return;
|
|
489
518
|
return doc.format === "docx" || doc.format === "odt" ? doc : void 0;
|
|
@@ -508,15 +537,44 @@ function drawingDocument(state) {
|
|
|
508
537
|
if (doc === void 0) return;
|
|
509
538
|
return doc.format === "odg" ? doc : void 0;
|
|
510
539
|
}
|
|
511
|
-
function
|
|
540
|
+
function pdfDocument(state) {
|
|
512
541
|
const doc = state.openDocument;
|
|
513
542
|
if (doc === void 0) return;
|
|
514
|
-
return doc.format === "
|
|
543
|
+
return doc.format === "pdf" ? doc : void 0;
|
|
544
|
+
}
|
|
545
|
+
function pdfItemAt(doc, pageIndex, itemIndex) {
|
|
546
|
+
return doc.editor.page(pageIndex)?.items()[itemIndex];
|
|
515
547
|
}
|
|
516
|
-
function
|
|
548
|
+
function withPdfPage(state, pageIndex, apply) {
|
|
549
|
+
const doc = pdfDocument(state);
|
|
550
|
+
if (doc === void 0) return wrongDocument(state, "a pdf document");
|
|
551
|
+
const page = doc.editor.page(pageIndex);
|
|
552
|
+
if (page === void 0) return withStatus(state, "warning", `There is no page at index ${pageIndex}`);
|
|
553
|
+
return mutate(state, doc, () => {
|
|
554
|
+
apply(page);
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
function withPdfItemMatching(state, pageIndex, itemIndex, guard, kindLabel, apply) {
|
|
558
|
+
const doc = pdfDocument(state);
|
|
559
|
+
if (doc === void 0) return wrongDocument(state, "a pdf document");
|
|
560
|
+
const item = pdfItemAt(doc, pageIndex, itemIndex);
|
|
561
|
+
if (item === void 0) return withStatus(state, "warning", `Page ${pageIndex} has no item at index ${itemIndex}`);
|
|
562
|
+
if (!guard(item)) return withStatus(state, "warning", `Item ${itemIndex} on page ${pageIndex} is a ${item.kind} item, not ${kindLabel}`);
|
|
563
|
+
return mutate(state, doc, () => {
|
|
564
|
+
apply(item);
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
const isPdfTextItem = (item) => item.kind === "text";
|
|
568
|
+
const isPdfRectItem = (item) => item.kind === "rect";
|
|
569
|
+
const isPdfEllipseItem = (item) => item.kind === "ellipse";
|
|
570
|
+
const isPdfLineItem = (item) => item.kind === "line";
|
|
571
|
+
const isPdfPathItem = (item) => item.kind === "path";
|
|
572
|
+
const isPdfImageItem = (item) => item.kind === "image";
|
|
573
|
+
const isPdfLinkItem = (item) => item.kind === "link";
|
|
574
|
+
function vectorHostDocument(state) {
|
|
517
575
|
const doc = state.openDocument;
|
|
518
576
|
if (doc === void 0) return;
|
|
519
|
-
return doc.format === "
|
|
577
|
+
return doc.format === "odg" || doc.format === "odp" ? doc : void 0;
|
|
520
578
|
}
|
|
521
579
|
function paragraphAt(doc, blockIndex) {
|
|
522
580
|
return doc.editor.paragraphs()[blockIndex];
|
|
@@ -524,6 +582,9 @@ function paragraphAt(doc, blockIndex) {
|
|
|
524
582
|
function tableAt(doc, tableIndex) {
|
|
525
583
|
return doc.editor.tables()[tableIndex];
|
|
526
584
|
}
|
|
585
|
+
function tableCellAt(table, row, column) {
|
|
586
|
+
return table.rows()[row]?.cells()[column];
|
|
587
|
+
}
|
|
527
588
|
function shapeAt(doc, containerIndex, shapeIndex) {
|
|
528
589
|
if (doc.format === "odg") return doc.editor.pages()[containerIndex]?.shapes()[shapeIndex];
|
|
529
590
|
return doc.editor.slides()[containerIndex]?.shapes()[shapeIndex];
|
|
@@ -533,7 +594,7 @@ function sheetAt(doc, sheetIndex) {
|
|
|
533
594
|
}
|
|
534
595
|
function withRun(state, blockIndex, runIndex, apply) {
|
|
535
596
|
const doc = wordprocessingDocument(state);
|
|
536
|
-
if (doc === void 0) return wrongDocument(state, "a docx or
|
|
597
|
+
if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
|
|
537
598
|
const paragraph = paragraphAt(doc, blockIndex);
|
|
538
599
|
if (paragraph === void 0) return withStatus(state, "warning", `There is no paragraph at index ${blockIndex}`);
|
|
539
600
|
const run = paragraph.runs()[runIndex];
|
|
@@ -542,6 +603,17 @@ function withRun(state, blockIndex, runIndex, apply) {
|
|
|
542
603
|
apply(run);
|
|
543
604
|
});
|
|
544
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
|
+
}
|
|
545
617
|
function withShape(state, containerIndex, shapeIndex, apply) {
|
|
546
618
|
const doc = shapeHostDocument(state);
|
|
547
619
|
if (doc === void 0) return wrongDocument(state, "a pptx, odp or odg document");
|
|
@@ -703,8 +775,15 @@ function appReducer(state, action) {
|
|
|
703
775
|
};
|
|
704
776
|
case "APPEND_PARAGRAPH": {
|
|
705
777
|
const doc = wordprocessingDocument(state);
|
|
706
|
-
if (doc === void 0) return wrongDocument(state, "a docx or
|
|
778
|
+
if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
|
|
707
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
|
+
}
|
|
708
787
|
doc.editor.body.appendParagraph({
|
|
709
788
|
text: action.text,
|
|
710
789
|
styleId: action.styleId,
|
|
@@ -713,9 +792,9 @@ function appReducer(state, action) {
|
|
|
713
792
|
});
|
|
714
793
|
}
|
|
715
794
|
case "SET_PARAGRAPH_ALIGNMENT": {
|
|
716
|
-
const doc =
|
|
795
|
+
const doc = styledWordprocessingDocument(state);
|
|
717
796
|
if (doc === void 0) return wrongDocument(state, "a docx or odt document");
|
|
718
|
-
const paragraph =
|
|
797
|
+
const paragraph = doc.editor.paragraphs()[action.blockIndex];
|
|
719
798
|
if (paragraph === void 0) return withStatus(state, "warning", `There is no paragraph at index ${action.blockIndex}`);
|
|
720
799
|
return mutate(state, doc, () => {
|
|
721
800
|
paragraph.alignment = action.alignment;
|
|
@@ -723,7 +802,7 @@ function appReducer(state, action) {
|
|
|
723
802
|
}
|
|
724
803
|
case "APPEND_RUN": {
|
|
725
804
|
const doc = wordprocessingDocument(state);
|
|
726
|
-
if (doc === void 0) return wrongDocument(state, "a docx or
|
|
805
|
+
if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
|
|
727
806
|
const paragraph = paragraphAt(doc, action.blockIndex);
|
|
728
807
|
if (paragraph === void 0) return withStatus(state, "warning", `There is no paragraph at index ${action.blockIndex}`);
|
|
729
808
|
return mutate(state, doc, () => {
|
|
@@ -739,50 +818,60 @@ function appReducer(state, action) {
|
|
|
739
818
|
case "TOGGLE_RUN_ITALIC": return withRun(state, action.blockIndex, action.runIndex, (run) => {
|
|
740
819
|
run.italic = !run.italic;
|
|
741
820
|
});
|
|
742
|
-
case "TOGGLE_RUN_UNDERLINE": return
|
|
821
|
+
case "TOGGLE_RUN_UNDERLINE": return withStyledRun(state, action.blockIndex, action.runIndex, (run) => {
|
|
743
822
|
run.underline = !run.underline;
|
|
744
823
|
});
|
|
745
|
-
case "SET_RUN_COLOR": return
|
|
824
|
+
case "SET_RUN_COLOR": return withStyledRun(state, action.blockIndex, action.runIndex, (run) => {
|
|
746
825
|
run.color = action.color;
|
|
747
826
|
});
|
|
748
|
-
case "SET_RUN_FONT_FAMILY": return
|
|
827
|
+
case "SET_RUN_FONT_FAMILY": return withStyledRun(state, action.blockIndex, action.runIndex, (run) => {
|
|
749
828
|
run.fontFamily = action.fontFamily;
|
|
750
829
|
});
|
|
751
|
-
case "SET_RUN_FONT_SIZE": return
|
|
830
|
+
case "SET_RUN_FONT_SIZE": return withStyledRun(state, action.blockIndex, action.runIndex, (run) => {
|
|
752
831
|
run.sizePt = action.sizePt;
|
|
753
832
|
});
|
|
754
833
|
case "APPEND_TABLE": {
|
|
755
834
|
const doc = wordprocessingDocument(state);
|
|
756
|
-
if (doc === void 0) return wrongDocument(state, "a docx or
|
|
757
|
-
|
|
835
|
+
if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
|
|
836
|
+
let mergeUnsupported = false;
|
|
837
|
+
const nextState = mutateGuarded(state, doc, () => {
|
|
758
838
|
const table = doc.editor.body.appendTable({
|
|
759
839
|
rows: action.rows,
|
|
760
840
|
columns: action.columns
|
|
761
841
|
});
|
|
762
|
-
if (action.merge
|
|
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);
|
|
763
848
|
});
|
|
849
|
+
return mergeUnsupported ? withStatus(nextState, "warning", "Markdown tables do not support merged cells -- the table was created without merging") : nextState;
|
|
764
850
|
}
|
|
765
851
|
case "MERGE_TABLE_CELLS": {
|
|
766
852
|
const doc = wordprocessingDocument(state);
|
|
767
|
-
if (doc === void 0) return wrongDocument(state, "a docx or
|
|
853
|
+
if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
|
|
768
854
|
const table = tableAt(doc, action.tableIndex);
|
|
769
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");
|
|
770
857
|
return mutateGuarded(state, doc, () => {
|
|
771
858
|
table.mergeCells(action.startRow, action.startColumn, action.rowSpan, action.colSpan);
|
|
772
859
|
});
|
|
773
860
|
}
|
|
774
861
|
case "SET_TABLE_CELL_TEXT": {
|
|
775
862
|
const doc = wordprocessingDocument(state);
|
|
776
|
-
if (doc === void 0) return wrongDocument(state, "a docx or
|
|
863
|
+
if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
|
|
777
864
|
const table = tableAt(doc, action.tableIndex);
|
|
778
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}`);
|
|
779
868
|
return mutate(state, doc, () => {
|
|
780
|
-
setCellText(
|
|
869
|
+
setCellText(cell, action.text);
|
|
781
870
|
});
|
|
782
871
|
}
|
|
783
872
|
case "ADD_LIST_ITEM": {
|
|
784
873
|
const doc = wordprocessingDocument(state);
|
|
785
|
-
if (doc === void 0) return wrongDocument(state, "a docx or
|
|
874
|
+
if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
|
|
786
875
|
if (doc.format === "odt") {
|
|
787
876
|
const list = doc.editor.lists()[action.blockIndex];
|
|
788
877
|
if (list === void 0) return withStatus(state, "warning", `There is no list at index ${action.blockIndex}`);
|
|
@@ -801,7 +890,7 @@ function appReducer(state, action) {
|
|
|
801
890
|
}
|
|
802
891
|
case "SET_LIST_ITEM_TEXT": {
|
|
803
892
|
const doc = wordprocessingDocument(state);
|
|
804
|
-
if (doc === void 0) return wrongDocument(state, "a docx or
|
|
893
|
+
if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
|
|
805
894
|
if (doc.format !== "odt") return wrongDocument(state, "an odt document (lists are an odt-only concept)");
|
|
806
895
|
const list = doc.editor.lists()[action.blockIndex];
|
|
807
896
|
if (list === void 0) return withStatus(state, "warning", `There is no list at index ${action.blockIndex}`);
|
|
@@ -813,16 +902,16 @@ function appReducer(state, action) {
|
|
|
813
902
|
}
|
|
814
903
|
case "ADD_LIST": {
|
|
815
904
|
const doc = wordprocessingDocument(state);
|
|
816
|
-
if (doc === void 0) return wrongDocument(state, "a docx or
|
|
905
|
+
if (doc === void 0) return wrongDocument(state, "a docx, odt or markdown document");
|
|
817
906
|
if (doc.format !== "odt") return wrongDocument(state, "an odt document (lists are an odt-only concept)");
|
|
818
907
|
return mutate(state, doc, () => {
|
|
819
908
|
doc.editor.body.appendList();
|
|
820
909
|
});
|
|
821
910
|
}
|
|
822
911
|
case "INSERT_PARAGRAPH_IMAGE": {
|
|
823
|
-
const doc =
|
|
912
|
+
const doc = styledWordprocessingDocument(state);
|
|
824
913
|
if (doc === void 0) return wrongDocument(state, "a docx or odt document");
|
|
825
|
-
const paragraph =
|
|
914
|
+
const paragraph = doc.editor.paragraphs()[action.blockIndex];
|
|
826
915
|
if (paragraph === void 0) return withStatus(state, "warning", `There is no paragraph at index ${action.blockIndex}`);
|
|
827
916
|
return mutate(state, doc, () => {
|
|
828
917
|
paragraph.insertImageAfter({
|
|
@@ -1078,11 +1167,129 @@ function appReducer(state, action) {
|
|
|
1078
1167
|
action.vector.stroke = action.stroke;
|
|
1079
1168
|
});
|
|
1080
1169
|
}
|
|
1081
|
-
case "
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1170
|
+
case "ADD_PDF_TEXT": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1171
|
+
page.appendText(action.init);
|
|
1172
|
+
});
|
|
1173
|
+
case "ADD_PDF_RECT": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1174
|
+
page.appendRect(action.init);
|
|
1175
|
+
});
|
|
1176
|
+
case "ADD_PDF_ELLIPSE": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1177
|
+
page.appendEllipse(action.init);
|
|
1178
|
+
});
|
|
1179
|
+
case "ADD_PDF_LINE": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1180
|
+
page.appendLine(action.init);
|
|
1181
|
+
});
|
|
1182
|
+
case "ADD_PDF_PATH": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1183
|
+
page.appendPath(action.init);
|
|
1184
|
+
});
|
|
1185
|
+
case "ADD_PDF_IMAGE": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1186
|
+
page.appendImage(action.init);
|
|
1187
|
+
});
|
|
1188
|
+
case "ADD_PDF_LINK": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1189
|
+
page.appendLink(action.init);
|
|
1190
|
+
});
|
|
1191
|
+
case "REMOVE_PDF_ITEM": {
|
|
1192
|
+
const doc = pdfDocument(state);
|
|
1193
|
+
if (doc === void 0) return wrongDocument(state, "a pdf document");
|
|
1194
|
+
const item = pdfItemAt(doc, action.pageIndex, action.itemIndex);
|
|
1195
|
+
if (item === void 0) return withStatus(state, "warning", `Page ${action.pageIndex} has no item at index ${action.itemIndex}`);
|
|
1196
|
+
return mutate(state, doc, () => {
|
|
1197
|
+
item.remove();
|
|
1198
|
+
});
|
|
1085
1199
|
}
|
|
1200
|
+
case "SET_PDF_TEXT_TEXT": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1201
|
+
item.text = action.text;
|
|
1202
|
+
});
|
|
1203
|
+
case "SET_PDF_TEXT_POSITION": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1204
|
+
item.xPt = action.xPt;
|
|
1205
|
+
item.yPt = action.yPt;
|
|
1206
|
+
});
|
|
1207
|
+
case "SET_PDF_TEXT_FONT": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1208
|
+
item.font = action.font;
|
|
1209
|
+
});
|
|
1210
|
+
case "SET_PDF_TEXT_SIZE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1211
|
+
item.sizePt = action.sizePt;
|
|
1212
|
+
});
|
|
1213
|
+
case "SET_PDF_TEXT_COLOR": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1214
|
+
item.color = action.color;
|
|
1215
|
+
});
|
|
1216
|
+
case "SET_PDF_TEXT_ROTATION": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1217
|
+
item.rotationDeg = action.rotationDeg;
|
|
1218
|
+
});
|
|
1219
|
+
case "SET_PDF_TEXT_WIDTH": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1220
|
+
item.widthPt = action.widthPt;
|
|
1221
|
+
});
|
|
1222
|
+
case "TOGGLE_PDF_TEXT_UNDERLINE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1223
|
+
item.underline = !(item.underline ?? false);
|
|
1224
|
+
});
|
|
1225
|
+
case "SET_PDF_RECT_FRAME": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfRectItem, "rect", (item) => {
|
|
1226
|
+
item.xPt = action.xPt;
|
|
1227
|
+
item.yPt = action.yPt;
|
|
1228
|
+
item.widthPt = action.widthPt;
|
|
1229
|
+
item.heightPt = action.heightPt;
|
|
1230
|
+
});
|
|
1231
|
+
case "SET_PDF_RECT_FILL": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfRectItem, "rect", (item) => {
|
|
1232
|
+
item.fill = action.fill;
|
|
1233
|
+
});
|
|
1234
|
+
case "SET_PDF_RECT_STROKE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfRectItem, "rect", (item) => {
|
|
1235
|
+
item.stroke = action.stroke;
|
|
1236
|
+
});
|
|
1237
|
+
case "SET_PDF_ELLIPSE_FRAME": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfEllipseItem, "ellipse", (item) => {
|
|
1238
|
+
item.xPt = action.xPt;
|
|
1239
|
+
item.yPt = action.yPt;
|
|
1240
|
+
item.widthPt = action.widthPt;
|
|
1241
|
+
item.heightPt = action.heightPt;
|
|
1242
|
+
});
|
|
1243
|
+
case "SET_PDF_ELLIPSE_FILL": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfEllipseItem, "ellipse", (item) => {
|
|
1244
|
+
item.fill = action.fill;
|
|
1245
|
+
});
|
|
1246
|
+
case "SET_PDF_ELLIPSE_STROKE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfEllipseItem, "ellipse", (item) => {
|
|
1247
|
+
item.stroke = action.stroke;
|
|
1248
|
+
});
|
|
1249
|
+
case "SET_PDF_LINE_FROM": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLineItem, "line", (item) => {
|
|
1250
|
+
item.x1Pt = action.x1Pt;
|
|
1251
|
+
item.y1Pt = action.y1Pt;
|
|
1252
|
+
});
|
|
1253
|
+
case "SET_PDF_LINE_TO": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLineItem, "line", (item) => {
|
|
1254
|
+
item.x2Pt = action.x2Pt;
|
|
1255
|
+
item.y2Pt = action.y2Pt;
|
|
1256
|
+
});
|
|
1257
|
+
case "SET_PDF_LINE_COLOR": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLineItem, "line", (item) => {
|
|
1258
|
+
item.color = action.color;
|
|
1259
|
+
});
|
|
1260
|
+
case "SET_PDF_LINE_WIDTH": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLineItem, "line", (item) => {
|
|
1261
|
+
item.widthPt = action.widthPt;
|
|
1262
|
+
});
|
|
1263
|
+
case "SET_PDF_PATH_FILL": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfPathItem, "path", (item) => {
|
|
1264
|
+
item.fill = action.fill;
|
|
1265
|
+
});
|
|
1266
|
+
case "SET_PDF_PATH_FILL_RULE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfPathItem, "path", (item) => {
|
|
1267
|
+
item.fillRule = action.fillRule;
|
|
1268
|
+
});
|
|
1269
|
+
case "SET_PDF_PATH_STROKE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfPathItem, "path", (item) => {
|
|
1270
|
+
item.stroke = action.stroke;
|
|
1271
|
+
});
|
|
1272
|
+
case "SET_PDF_IMAGE_FRAME": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfImageItem, "image", (item) => {
|
|
1273
|
+
item.xPt = action.xPt;
|
|
1274
|
+
item.yPt = action.yPt;
|
|
1275
|
+
item.widthPt = action.widthPt;
|
|
1276
|
+
item.heightPt = action.heightPt;
|
|
1277
|
+
});
|
|
1278
|
+
case "SET_PDF_IMAGE_ROTATION": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfImageItem, "image", (item) => {
|
|
1279
|
+
item.rotationDeg = action.rotationDeg;
|
|
1280
|
+
});
|
|
1281
|
+
case "SET_PDF_IMAGE_SOURCE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfImageItem, "image", (item) => {
|
|
1282
|
+
item.setImage(action.bytes, action.format);
|
|
1283
|
+
});
|
|
1284
|
+
case "SET_PDF_LINK_URI": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLinkItem, "link", (item) => {
|
|
1285
|
+
item.uri = action.uri;
|
|
1286
|
+
});
|
|
1287
|
+
case "SET_PDF_LINK_FRAME": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLinkItem, "link", (item) => {
|
|
1288
|
+
item.xPt = action.xPt;
|
|
1289
|
+
item.yPt = action.yPt;
|
|
1290
|
+
item.widthPt = action.widthPt;
|
|
1291
|
+
item.heightPt = action.heightPt;
|
|
1292
|
+
});
|
|
1086
1293
|
case "APPEND_DIAGNOSTIC": return {
|
|
1087
1294
|
...state,
|
|
1088
1295
|
diagnostics: [...state.diagnostics, action.diagnostic]
|
|
@@ -1111,12 +1318,12 @@ function appReducer(state, action) {
|
|
|
1111
1318
|
case "UNDO": {
|
|
1112
1319
|
const doc = state.openDocument;
|
|
1113
1320
|
if (doc === void 0) return withStatus(state, "info", "There is nothing to undo");
|
|
1114
|
-
if (doc.format === "odb" || doc.format === "
|
|
1321
|
+
if (doc.format === "odb" || doc.format === "xlsx") return withStatus(state, "warning", `A ${doc.format} document is read-only, so it has no history to undo`);
|
|
1115
1322
|
const snapshot = state.undoStack.at(-1);
|
|
1116
1323
|
if (snapshot === void 0) return withStatus(state, "info", "There is nothing to undo");
|
|
1117
1324
|
const restored = doc.format === "markdown" ? {
|
|
1118
1325
|
...doc,
|
|
1119
|
-
|
|
1326
|
+
editor: openMarkdown(decodeMarkdownText(snapshot))
|
|
1120
1327
|
} : reopenEditable(doc, snapshot);
|
|
1121
1328
|
return withStatus({
|
|
1122
1329
|
...state,
|
|
@@ -1195,7 +1402,7 @@ function TextField(props) {
|
|
|
1195
1402
|
}
|
|
1196
1403
|
//#endregion
|
|
1197
1404
|
//#region src/tui/components/command-palette.tsx
|
|
1198
|
-
const USAGE_COLUMN_WIDTH =
|
|
1405
|
+
const USAGE_COLUMN_WIDTH = 36;
|
|
1199
1406
|
const COMMANDS = [
|
|
1200
1407
|
{
|
|
1201
1408
|
name: "save",
|
|
@@ -1214,7 +1421,7 @@ const COMMANDS = [
|
|
|
1214
1421
|
},
|
|
1215
1422
|
{
|
|
1216
1423
|
name: "new",
|
|
1217
|
-
usage: ":new docx|pptx|odt|odp|ods|odg",
|
|
1424
|
+
usage: ":new docx|pptx|odt|odp|ods|odg|pdf",
|
|
1218
1425
|
description: "Create an empty document"
|
|
1219
1426
|
},
|
|
1220
1427
|
{
|
|
@@ -1232,6 +1439,11 @@ const COMMANDS = [
|
|
|
1232
1439
|
usage: ":undo",
|
|
1233
1440
|
description: "Undo the last change"
|
|
1234
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
|
+
},
|
|
1235
1447
|
{
|
|
1236
1448
|
name: "help",
|
|
1237
1449
|
usage: ":help",
|
|
@@ -1331,7 +1543,7 @@ async function runCommand(line, state, dispatch) {
|
|
|
1331
1543
|
case "new": {
|
|
1332
1544
|
const format = args[0];
|
|
1333
1545
|
if (format === void 0 || !isEditableFormat(format)) {
|
|
1334
|
-
warn(dispatch, "Usage: :new docx|pptx|odt|odp|ods|odg");
|
|
1546
|
+
warn(dispatch, "Usage: :new docx|pptx|odt|odp|ods|odg|pdf");
|
|
1335
1547
|
return;
|
|
1336
1548
|
}
|
|
1337
1549
|
dispatch({
|
|
@@ -1350,7 +1562,12 @@ async function runCommand(line, state, dispatch) {
|
|
|
1350
1562
|
dispatch({
|
|
1351
1563
|
type: "OPEN_FILE_SUCCESS",
|
|
1352
1564
|
path,
|
|
1353
|
-
doc: await openDocumentAtPath(path)
|
|
1565
|
+
doc: await openDocumentAtPath(path, { onDiagnostic: (diagnostic) => {
|
|
1566
|
+
dispatch({
|
|
1567
|
+
type: "APPEND_DIAGNOSTIC",
|
|
1568
|
+
diagnostic
|
|
1569
|
+
});
|
|
1570
|
+
} })
|
|
1354
1571
|
});
|
|
1355
1572
|
} catch (error) {
|
|
1356
1573
|
dispatch({
|
|
@@ -1367,6 +1584,16 @@ async function runCommand(line, state, dispatch) {
|
|
|
1367
1584
|
case "undo":
|
|
1368
1585
|
dispatch({ type: "UNDO" });
|
|
1369
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;
|
|
1370
1597
|
case "help":
|
|
1371
1598
|
dispatch({
|
|
1372
1599
|
type: "OPEN_OVERLAY",
|
|
@@ -2037,9 +2264,12 @@ function parseNumberField(raw, fallback) {
|
|
|
2037
2264
|
}
|
|
2038
2265
|
//#endregion
|
|
2039
2266
|
//#region src/tui/screens/shared/paragraph-family.tsx
|
|
2267
|
+
function supportsRunStyleExtras(run) {
|
|
2268
|
+
return "underline" in run;
|
|
2269
|
+
}
|
|
2040
2270
|
function paragraphFamilyDocument(openDocument) {
|
|
2041
2271
|
if (openDocument === void 0) return;
|
|
2042
|
-
return openDocument.format === "docx" || openDocument.format === "odt" ? openDocument : void 0;
|
|
2272
|
+
return openDocument.format === "docx" || openDocument.format === "odt" || openDocument.format === "markdown" ? openDocument : void 0;
|
|
2043
2273
|
}
|
|
2044
2274
|
function liveParagraphAt(doc, blockIndex) {
|
|
2045
2275
|
return doc.editor.paragraphs()[blockIndex];
|
|
@@ -2522,14 +2752,14 @@ const IMAGE_FIELDS = [
|
|
|
2522
2752
|
defaultValue: ""
|
|
2523
2753
|
}
|
|
2524
2754
|
];
|
|
2525
|
-
function inferImageFormat$
|
|
2755
|
+
function inferImageFormat$2(path) {
|
|
2526
2756
|
const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
|
|
2527
2757
|
if (extension === "png") return "png";
|
|
2528
2758
|
if (extension === "jpg" || extension === "jpeg") return "jpeg";
|
|
2529
2759
|
}
|
|
2530
2760
|
async function applyInsertImage(blockIndex, values, dispatch) {
|
|
2531
2761
|
const path = requireFieldValue(values, "path");
|
|
2532
|
-
const format = inferImageFormat$
|
|
2762
|
+
const format = inferImageFormat$2(path);
|
|
2533
2763
|
if (format === void 0) {
|
|
2534
2764
|
dispatch({
|
|
2535
2765
|
type: "SET_STATUS",
|
|
@@ -2565,14 +2795,17 @@ function ParagraphRunsView(props) {
|
|
|
2565
2795
|
dimColor: true,
|
|
2566
2796
|
children: "(no runs -- press 'a' to append one)"
|
|
2567
2797
|
});
|
|
2568
|
-
return /* @__PURE__ */ jsx(Box, { children: props.runs.map((run, index) =>
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
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
|
+
}) });
|
|
2576
2809
|
}
|
|
2577
2810
|
function ParagraphDetailScreen() {
|
|
2578
2811
|
const state = useAppState();
|
|
@@ -2645,7 +2878,7 @@ function ParagraphDetailScreen() {
|
|
|
2645
2878
|
});
|
|
2646
2879
|
return;
|
|
2647
2880
|
}
|
|
2648
|
-
if (input === "I") {
|
|
2881
|
+
if (input === "I" && doc?.format !== "markdown") {
|
|
2649
2882
|
setImageWizardOpen(true);
|
|
2650
2883
|
return;
|
|
2651
2884
|
}
|
|
@@ -2670,6 +2903,7 @@ function ParagraphDetailScreen() {
|
|
|
2670
2903
|
});
|
|
2671
2904
|
return;
|
|
2672
2905
|
}
|
|
2906
|
+
if (!supportsRunStyleExtras(selectedRun)) return;
|
|
2673
2907
|
if (input === "u") {
|
|
2674
2908
|
dispatch({
|
|
2675
2909
|
type: "TOGGLE_RUN_UNDERLINE",
|
|
@@ -2694,7 +2928,7 @@ function ParagraphDetailScreen() {
|
|
|
2694
2928
|
});
|
|
2695
2929
|
if (doc === void 0) return /* @__PURE__ */ jsx(Text, {
|
|
2696
2930
|
color: "red",
|
|
2697
|
-
children: "ParagraphDetailScreen requires an open docx or
|
|
2931
|
+
children: "ParagraphDetailScreen requires an open docx, odt or markdown document."
|
|
2698
2932
|
});
|
|
2699
2933
|
if (paragraph === void 0) return /* @__PURE__ */ jsxs(Text, {
|
|
2700
2934
|
color: "red",
|
|
@@ -2781,7 +3015,7 @@ function ParagraphDetailScreen() {
|
|
|
2781
3015
|
placeholder: "e.g. 12",
|
|
2782
3016
|
onChange: setFontSizeInput,
|
|
2783
3017
|
onSubmit: (value) => {
|
|
2784
|
-
const sizePt = parseNumberField(value, selectedRun
|
|
3018
|
+
const sizePt = parseNumberField(value, selectedRun !== void 0 && supportsRunStyleExtras(selectedRun) ? selectedRun.sizePt ?? DEFAULT_RUN_SIZE_PT : DEFAULT_RUN_SIZE_PT);
|
|
2785
3019
|
if (sizePt <= 0) dispatch({
|
|
2786
3020
|
type: "SET_STATUS",
|
|
2787
3021
|
severity: "warning",
|
|
@@ -2834,7 +3068,12 @@ function ParagraphDetailScreen() {
|
|
|
2834
3068
|
/* @__PURE__ */ jsxs(Text, {
|
|
2835
3069
|
dimColor: true,
|
|
2836
3070
|
children: [
|
|
2837
|
-
"<- / -> move, Enter edit text, b/i
|
|
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",
|
|
2838
3077
|
doc.format === "docx" ? ", m formula" : "",
|
|
2839
3078
|
", Esc back"
|
|
2840
3079
|
]
|
|
@@ -2869,7 +3108,7 @@ function RunEditorScreen() {
|
|
|
2869
3108
|
});
|
|
2870
3109
|
if (doc === void 0) return /* @__PURE__ */ jsx(Text, {
|
|
2871
3110
|
color: "red",
|
|
2872
|
-
children: "RunEditorScreen requires an open docx or
|
|
3111
|
+
children: "RunEditorScreen requires an open docx, odt or markdown document."
|
|
2873
3112
|
});
|
|
2874
3113
|
const paragraph = liveParagraphAt(doc, screen.blockIndex);
|
|
2875
3114
|
if (paragraph === void 0) return /* @__PURE__ */ jsxs(Text, {
|
|
@@ -3028,7 +3267,7 @@ function TableViewScreen() {
|
|
|
3028
3267
|
});
|
|
3029
3268
|
if (doc === void 0) return /* @__PURE__ */ jsx(Text, {
|
|
3030
3269
|
color: "red",
|
|
3031
|
-
children: "TableViewScreen requires an open docx or
|
|
3270
|
+
children: "TableViewScreen requires an open docx, odt or markdown document."
|
|
3032
3271
|
});
|
|
3033
3272
|
if (table === void 0) return /* @__PURE__ */ jsxs(Text, {
|
|
3034
3273
|
color: "red",
|
|
@@ -3102,7 +3341,7 @@ function TableCellDetailScreen() {
|
|
|
3102
3341
|
});
|
|
3103
3342
|
if (doc === void 0) return /* @__PURE__ */ jsx(Text, {
|
|
3104
3343
|
color: "red",
|
|
3105
|
-
children: "TableCellDetailScreen requires an open docx or
|
|
3344
|
+
children: "TableCellDetailScreen requires an open docx, odt or markdown document."
|
|
3106
3345
|
});
|
|
3107
3346
|
if (cell === void 0) return /* @__PURE__ */ jsxs(Text, {
|
|
3108
3347
|
color: "red",
|
|
@@ -3249,123 +3488,67 @@ function DocxExtrasScreen() {
|
|
|
3249
3488
|
});
|
|
3250
3489
|
}
|
|
3251
3490
|
//#endregion
|
|
3252
|
-
//#region src/tui/screens/editors/markdown/
|
|
3253
|
-
function
|
|
3254
|
-
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.");
|
|
3255
|
-
return openDocument;
|
|
3256
|
-
}
|
|
3257
|
-
//#endregion
|
|
3258
|
-
//#region src/tui/screens/editors/markdown/line-list.tsx
|
|
3259
|
-
function MarkdownLineListScreen() {
|
|
3260
|
-
const state = useAppState();
|
|
3261
|
-
const dispatch = useAppDispatch();
|
|
3262
|
-
const doc = requireMarkdownDocument(state.openDocument);
|
|
3263
|
-
const query = state.searchQuery.trim().toLowerCase();
|
|
3264
|
-
const allLines = doc.source.split("\n").map((line, lineIndex) => ({
|
|
3265
|
-
line,
|
|
3266
|
-
lineIndex
|
|
3267
|
-
}));
|
|
3268
|
-
const lines = query === "" ? allLines : allLines.filter((entry) => entry.line.toLowerCase().includes(query));
|
|
3269
|
-
const { selectedIndex } = useNavigationInput({
|
|
3270
|
-
itemCount: lines.length,
|
|
3271
|
-
onSelect: (index) => {
|
|
3272
|
-
const entry = lines[index];
|
|
3273
|
-
if (entry === void 0) return;
|
|
3274
|
-
dispatch({
|
|
3275
|
-
type: "PUSH_SCREEN",
|
|
3276
|
-
screen: {
|
|
3277
|
-
kind: "markdownLineEditor",
|
|
3278
|
-
lineIndex: entry.lineIndex
|
|
3279
|
-
}
|
|
3280
|
-
});
|
|
3281
|
-
},
|
|
3282
|
-
onBack: () => {
|
|
3283
|
-
dispatch({ type: "POP_SCREEN" });
|
|
3284
|
-
},
|
|
3285
|
-
isActive: !anyOverlayOpen(state)
|
|
3286
|
-
});
|
|
3287
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
3288
|
-
flexDirection: "column",
|
|
3289
|
-
children: [/* @__PURE__ */ jsxs(Text, {
|
|
3290
|
-
bold: true,
|
|
3291
|
-
children: [
|
|
3292
|
-
"Lines (",
|
|
3293
|
-
lines.length,
|
|
3294
|
-
" of ",
|
|
3295
|
-
allLines.length,
|
|
3296
|
-
")"
|
|
3297
|
-
]
|
|
3298
|
-
}), /* @__PURE__ */ jsx(ListView, {
|
|
3299
|
-
items: lines,
|
|
3300
|
-
selectedIndex,
|
|
3301
|
-
emptyMessage: query === "" ? "This document has no lines." : `No lines match "${state.searchQuery}".`,
|
|
3302
|
-
renderItem: ({ line, lineIndex }, isSelected) => /* @__PURE__ */ jsxs(Text, {
|
|
3303
|
-
color: isSelected ? "cyan" : void 0,
|
|
3304
|
-
inverse: isSelected,
|
|
3305
|
-
children: [
|
|
3306
|
-
lineIndex + 1,
|
|
3307
|
-
": ",
|
|
3308
|
-
line === "" ? "(blank)" : line
|
|
3309
|
-
]
|
|
3310
|
-
})
|
|
3311
|
-
})]
|
|
3312
|
-
});
|
|
3313
|
-
}
|
|
3314
|
-
//#endregion
|
|
3315
|
-
//#region src/tui/screens/editors/markdown/line-editor.tsx
|
|
3316
|
-
function MarkdownLineEditorScreen() {
|
|
3491
|
+
//#region src/tui/screens/editors/markdown/view-source.tsx
|
|
3492
|
+
function MarkdownViewSourceScreen() {
|
|
3317
3493
|
const state = useAppState();
|
|
3318
3494
|
const dispatch = useAppDispatch();
|
|
3319
3495
|
const screen = currentScreen(state);
|
|
3320
3496
|
const doc = state.openDocument;
|
|
3321
|
-
|
|
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, {
|
|
3322
3501
|
color: "red",
|
|
3323
|
-
children: "
|
|
3502
|
+
children: "MarkdownViewSourceScreen rendered outside a viewSource screen."
|
|
3324
3503
|
});
|
|
3325
3504
|
if (doc?.format !== "markdown") return /* @__PURE__ */ jsx(Text, {
|
|
3326
3505
|
color: "red",
|
|
3327
|
-
children: "
|
|
3328
|
-
});
|
|
3329
|
-
const lines = doc.source.split("\n");
|
|
3330
|
-
const line = lines[screen.lineIndex];
|
|
3331
|
-
if (line === void 0) return /* @__PURE__ */ jsxs(Text, {
|
|
3332
|
-
color: "red",
|
|
3333
|
-
children: [
|
|
3334
|
-
"There is no line at index ",
|
|
3335
|
-
screen.lineIndex,
|
|
3336
|
-
"."
|
|
3337
|
-
]
|
|
3506
|
+
children: "view-source requires an open markdown document."
|
|
3338
3507
|
});
|
|
3508
|
+
const asItWillSaveNow = doc.editor.toMarkdownText();
|
|
3339
3509
|
return /* @__PURE__ */ jsxs(Box, {
|
|
3340
3510
|
flexDirection: "column",
|
|
3341
3511
|
children: [
|
|
3342
|
-
/* @__PURE__ */
|
|
3512
|
+
/* @__PURE__ */ jsx(Text, {
|
|
3343
3513
|
bold: true,
|
|
3344
|
-
children:
|
|
3514
|
+
children: "As opened"
|
|
3345
3515
|
}),
|
|
3346
|
-
/* @__PURE__ */ jsx(
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
nextLines[screen.lineIndex] = text;
|
|
3351
|
-
dispatch({
|
|
3352
|
-
type: "SET_MARKDOWN_SOURCE",
|
|
3353
|
-
source: nextLines.join("\n")
|
|
3354
|
-
});
|
|
3355
|
-
dispatch({ type: "POP_SCREEN" });
|
|
3356
|
-
},
|
|
3357
|
-
onCancel: () => {
|
|
3358
|
-
dispatch({ type: "POP_SCREEN" });
|
|
3359
|
-
}
|
|
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"
|
|
3360
3520
|
}),
|
|
3521
|
+
/* @__PURE__ */ jsx(Text, { children: asItWillSaveNow }),
|
|
3361
3522
|
/* @__PURE__ */ jsx(Text, {
|
|
3362
3523
|
dimColor: true,
|
|
3363
|
-
children: "
|
|
3524
|
+
children: "Esc back"
|
|
3364
3525
|
})
|
|
3365
3526
|
]
|
|
3366
3527
|
});
|
|
3367
3528
|
}
|
|
3368
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
|
|
3369
3552
|
//#region src/tui/screens/editors/odb/shared.ts
|
|
3370
3553
|
function requireOdbDocument(openDocument) {
|
|
3371
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.");
|
|
@@ -4012,14 +4195,14 @@ function vectorKindLabel(kind) {
|
|
|
4012
4195
|
case "path": return "Path";
|
|
4013
4196
|
}
|
|
4014
4197
|
}
|
|
4015
|
-
function formatPt(value) {
|
|
4198
|
+
function formatPt$1(value) {
|
|
4016
4199
|
return value.toFixed(1);
|
|
4017
4200
|
}
|
|
4018
4201
|
function formatFrame(box) {
|
|
4019
|
-
return `${formatPt(box.xPt)},${formatPt(box.yPt)} ${formatPt(box.widthPt)}x${formatPt(box.heightPt)}pt`;
|
|
4202
|
+
return `${formatPt$1(box.xPt)},${formatPt$1(box.yPt)} ${formatPt$1(box.widthPt)}x${formatPt$1(box.heightPt)}pt`;
|
|
4020
4203
|
}
|
|
4021
4204
|
function formatPoint$1(point) {
|
|
4022
|
-
return `${formatPt(point.xPt)},${formatPt(point.yPt)}`;
|
|
4205
|
+
return `${formatPt$1(point.xPt)},${formatPt$1(point.yPt)}`;
|
|
4023
4206
|
}
|
|
4024
4207
|
function formatColor$1(color) {
|
|
4025
4208
|
return `rgb(${color.r.toFixed(2)}, ${color.g.toFixed(2)}, ${color.b.toFixed(2)})`;
|
|
@@ -4031,7 +4214,7 @@ function describeVectorGeometry(vector) {
|
|
|
4031
4214
|
function describeFillStroke(vector) {
|
|
4032
4215
|
const parts = [];
|
|
4033
4216
|
if (vector.kind !== "line" && vector.fill !== void 0) parts.push(`fill ${formatColor$1(vector.fill)}`);
|
|
4034
|
-
if (vector.stroke !== void 0) parts.push(`stroke ${formatColor$1(vector.stroke.color)} ${formatPt(vector.stroke.widthPt)}pt`);
|
|
4217
|
+
if (vector.stroke !== void 0) parts.push(`stroke ${formatColor$1(vector.stroke.color)} ${formatPt$1(vector.stroke.widthPt)}pt`);
|
|
4035
4218
|
return parts.length === 0 ? "no fill or stroke" : parts.join(", ");
|
|
4036
4219
|
}
|
|
4037
4220
|
//#endregion
|
|
@@ -4098,7 +4281,7 @@ function OdgPageListScreen() {
|
|
|
4098
4281
|
}
|
|
4099
4282
|
//#endregion
|
|
4100
4283
|
//#region src/tui/screens/editors/odg/page-detail.tsx
|
|
4101
|
-
const ADD_KIND_OPTIONS = [
|
|
4284
|
+
const ADD_KIND_OPTIONS$1 = [
|
|
4102
4285
|
{
|
|
4103
4286
|
kind: "rect",
|
|
4104
4287
|
label: "Rectangle"
|
|
@@ -4124,7 +4307,7 @@ const ADD_KIND_OPTIONS = [
|
|
|
4124
4307
|
label: "Image"
|
|
4125
4308
|
}
|
|
4126
4309
|
];
|
|
4127
|
-
const GEOMETRY_FIELDS = [
|
|
4310
|
+
const GEOMETRY_FIELDS$1 = [
|
|
4128
4311
|
{
|
|
4129
4312
|
key: "xPt",
|
|
4130
4313
|
label: "X (pt)",
|
|
@@ -4146,24 +4329,24 @@ const GEOMETRY_FIELDS = [
|
|
|
4146
4329
|
defaultValue: "100"
|
|
4147
4330
|
}
|
|
4148
4331
|
];
|
|
4149
|
-
const FILL_FIELD = {
|
|
4332
|
+
const FILL_FIELD$1 = {
|
|
4150
4333
|
key: "fill",
|
|
4151
4334
|
label: "Fill \"r g b\" (0-1 each), blank for none",
|
|
4152
4335
|
defaultValue: "0.8 0.8 0.8"
|
|
4153
4336
|
};
|
|
4154
|
-
const STROKE_FIELD = {
|
|
4337
|
+
const STROKE_FIELD$1 = {
|
|
4155
4338
|
key: "stroke",
|
|
4156
4339
|
label: "Stroke \"r g b widthPt\" (0-1 colour, pt width), blank for none",
|
|
4157
4340
|
defaultValue: "0 0 0 1"
|
|
4158
4341
|
};
|
|
4159
|
-
function fieldsForAddKind(kind) {
|
|
4342
|
+
function fieldsForAddKind$1(kind) {
|
|
4160
4343
|
switch (kind) {
|
|
4161
4344
|
case "rect":
|
|
4162
4345
|
case "ellipse":
|
|
4163
4346
|
case "path": return [
|
|
4164
|
-
...GEOMETRY_FIELDS,
|
|
4165
|
-
FILL_FIELD,
|
|
4166
|
-
STROKE_FIELD
|
|
4347
|
+
...GEOMETRY_FIELDS$1,
|
|
4348
|
+
FILL_FIELD$1,
|
|
4349
|
+
STROKE_FIELD$1
|
|
4167
4350
|
];
|
|
4168
4351
|
case "line": return [
|
|
4169
4352
|
{
|
|
@@ -4186,15 +4369,15 @@ function fieldsForAddKind(kind) {
|
|
|
4186
4369
|
label: "To Y (pt)",
|
|
4187
4370
|
defaultValue: "40"
|
|
4188
4371
|
},
|
|
4189
|
-
STROKE_FIELD
|
|
4372
|
+
STROKE_FIELD$1
|
|
4190
4373
|
];
|
|
4191
|
-
case "textbox": return [...GEOMETRY_FIELDS, {
|
|
4374
|
+
case "textbox": return [...GEOMETRY_FIELDS$1, {
|
|
4192
4375
|
key: "text",
|
|
4193
4376
|
label: "Text",
|
|
4194
4377
|
defaultValue: "Text"
|
|
4195
4378
|
}];
|
|
4196
4379
|
case "image": return [
|
|
4197
|
-
...GEOMETRY_FIELDS,
|
|
4380
|
+
...GEOMETRY_FIELDS$1,
|
|
4198
4381
|
{
|
|
4199
4382
|
key: "path",
|
|
4200
4383
|
label: "Image file path (.png/.jpg/.jpeg)",
|
|
@@ -4208,7 +4391,7 @@ function fieldsForAddKind(kind) {
|
|
|
4208
4391
|
];
|
|
4209
4392
|
}
|
|
4210
4393
|
}
|
|
4211
|
-
function readFrame(values) {
|
|
4394
|
+
function readFrame$1(values) {
|
|
4212
4395
|
return {
|
|
4213
4396
|
xPt: parseNumberField(requireFieldValue(values, "xPt"), 0),
|
|
4214
4397
|
yPt: parseNumberField(requireFieldValue(values, "yPt"), 0),
|
|
@@ -4227,19 +4410,19 @@ function warnIfVectorAddedReadOnly(doc, pageIndex, dispatch, label) {
|
|
|
4227
4410
|
const added = buildPageItems(doc, pageIndex).filter((item) => item.kind === "vector").at(-1);
|
|
4228
4411
|
if (added !== void 0 && added.liveVector === void 0) warnVectorIsViewOnly(dispatch, label);
|
|
4229
4412
|
}
|
|
4230
|
-
function inferImageFormat(path) {
|
|
4413
|
+
function inferImageFormat$1(path) {
|
|
4231
4414
|
const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
|
|
4232
4415
|
if (extension === "png") return "png";
|
|
4233
4416
|
if (extension === "jpg" || extension === "jpeg") return "jpeg";
|
|
4234
4417
|
}
|
|
4235
|
-
async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
4418
|
+
async function applyAddKind$1(kind, pageIndex, doc, values, dispatch) {
|
|
4236
4419
|
switch (kind) {
|
|
4237
4420
|
case "rect":
|
|
4238
4421
|
dispatch({
|
|
4239
4422
|
type: "ADD_RECT",
|
|
4240
4423
|
containerIndex: pageIndex,
|
|
4241
4424
|
init: {
|
|
4242
|
-
frame: readFrame(values),
|
|
4425
|
+
frame: readFrame$1(values),
|
|
4243
4426
|
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
4244
4427
|
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
4245
4428
|
}
|
|
@@ -4251,7 +4434,7 @@ async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
|
4251
4434
|
type: "ADD_ELLIPSE",
|
|
4252
4435
|
containerIndex: pageIndex,
|
|
4253
4436
|
init: {
|
|
4254
|
-
frame: readFrame(values),
|
|
4437
|
+
frame: readFrame$1(values),
|
|
4255
4438
|
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
4256
4439
|
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
4257
4440
|
}
|
|
@@ -4284,7 +4467,7 @@ async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
|
4284
4467
|
warnIfVectorAddedReadOnly(doc, pageIndex, dispatch, "Line");
|
|
4285
4468
|
return;
|
|
4286
4469
|
case "path": {
|
|
4287
|
-
const frame = readFrame(values);
|
|
4470
|
+
const frame = readFrame$1(values);
|
|
4288
4471
|
dispatch({
|
|
4289
4472
|
type: "ADD_PATH",
|
|
4290
4473
|
containerIndex: pageIndex,
|
|
@@ -4302,14 +4485,14 @@ async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
|
4302
4485
|
dispatch({
|
|
4303
4486
|
type: "ADD_TEXTBOX",
|
|
4304
4487
|
containerIndex: pageIndex,
|
|
4305
|
-
frame: readFrame(values),
|
|
4488
|
+
frame: readFrame$1(values),
|
|
4306
4489
|
text: requireFieldValue(values, "text")
|
|
4307
4490
|
});
|
|
4308
4491
|
return;
|
|
4309
4492
|
case "image": {
|
|
4310
|
-
const frame = readFrame(values);
|
|
4493
|
+
const frame = readFrame$1(values);
|
|
4311
4494
|
const path = requireFieldValue(values, "path");
|
|
4312
|
-
const format = inferImageFormat(path);
|
|
4495
|
+
const format = inferImageFormat$1(path);
|
|
4313
4496
|
if (format === void 0) {
|
|
4314
4497
|
dispatch({
|
|
4315
4498
|
type: "SET_STATUS",
|
|
@@ -4340,15 +4523,15 @@ async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
|
4340
4523
|
}
|
|
4341
4524
|
}
|
|
4342
4525
|
}
|
|
4343
|
-
function AddItemFlow(props) {
|
|
4526
|
+
function AddItemFlow$1(props) {
|
|
4344
4527
|
const dispatch = useAppDispatch();
|
|
4345
4528
|
const [kind, setKind] = useState(void 0);
|
|
4346
4529
|
const { selectedIndex } = useNavigationInput({
|
|
4347
|
-
itemCount: ADD_KIND_OPTIONS.length,
|
|
4530
|
+
itemCount: ADD_KIND_OPTIONS$1.length,
|
|
4348
4531
|
isActive: props.isActive && kind === void 0,
|
|
4349
4532
|
onBack: props.onCancel,
|
|
4350
4533
|
onSelect: (index) => {
|
|
4351
|
-
const option = ADD_KIND_OPTIONS[index];
|
|
4534
|
+
const option = ADD_KIND_OPTIONS$1[index];
|
|
4352
4535
|
if (option === void 0) return;
|
|
4353
4536
|
setKind(option.kind);
|
|
4354
4537
|
}
|
|
@@ -4361,7 +4544,7 @@ function AddItemFlow(props) {
|
|
|
4361
4544
|
bold: true,
|
|
4362
4545
|
children: "Add item -- choose a kind"
|
|
4363
4546
|
}), /* @__PURE__ */ jsx(ListView, {
|
|
4364
|
-
items: ADD_KIND_OPTIONS,
|
|
4547
|
+
items: ADD_KIND_OPTIONS$1,
|
|
4365
4548
|
selectedIndex,
|
|
4366
4549
|
reservedRows: 6,
|
|
4367
4550
|
renderItem: (option, isSelected) => /* @__PURE__ */ jsxs(Text, {
|
|
@@ -4371,10 +4554,10 @@ function AddItemFlow(props) {
|
|
|
4371
4554
|
})]
|
|
4372
4555
|
});
|
|
4373
4556
|
return /* @__PURE__ */ jsx(FieldWizard, {
|
|
4374
|
-
fields: fieldsForAddKind(kind),
|
|
4557
|
+
fields: fieldsForAddKind$1(kind),
|
|
4375
4558
|
onCancel: props.onCancel,
|
|
4376
4559
|
onComplete: (values) => {
|
|
4377
|
-
applyAddKind(kind, props.pageIndex, props.doc, values, dispatch).then(props.onCreated);
|
|
4560
|
+
applyAddKind$1(kind, props.pageIndex, props.doc, values, dispatch).then(props.onCreated);
|
|
4378
4561
|
}
|
|
4379
4562
|
});
|
|
4380
4563
|
}
|
|
@@ -4426,7 +4609,7 @@ function OdgPageDetailScreen() {
|
|
|
4426
4609
|
setIsAdding(true);
|
|
4427
4610
|
}
|
|
4428
4611
|
});
|
|
4429
|
-
if (isAdding) return /* @__PURE__ */ jsx(AddItemFlow, {
|
|
4612
|
+
if (isAdding) return /* @__PURE__ */ jsx(AddItemFlow$1, {
|
|
4430
4613
|
pageIndex,
|
|
4431
4614
|
doc,
|
|
4432
4615
|
isActive: !overlayOpen,
|
|
@@ -4479,7 +4662,7 @@ function buildVectorRows(vector, liveVector, dispatch) {
|
|
|
4479
4662
|
});
|
|
4480
4663
|
}
|
|
4481
4664
|
rows.push({
|
|
4482
|
-
label: `Stroke: ${vector.stroke === void 0 ? "none" : `${formatColor$1(vector.stroke.color)} ${formatPt(vector.stroke.widthPt)}pt`}`,
|
|
4665
|
+
label: `Stroke: ${vector.stroke === void 0 ? "none" : `${formatColor$1(vector.stroke.color)} ${formatPt$1(vector.stroke.widthPt)}pt`}`,
|
|
4483
4666
|
currentValue: vector.stroke === void 0 ? "" : `${vector.stroke.color.r} ${vector.stroke.color.g} ${vector.stroke.color.b} ${vector.stroke.widthPt}`,
|
|
4484
4667
|
commit: (raw) => {
|
|
4485
4668
|
const stroke = parseStrokeField(raw);
|
|
@@ -4611,7 +4794,7 @@ function ShapeDetail(props) {
|
|
|
4611
4794
|
}
|
|
4612
4795
|
},
|
|
4613
4796
|
{
|
|
4614
|
-
label: `X: ${formatPt(frame.xPt)}pt`,
|
|
4797
|
+
label: `X: ${formatPt$1(frame.xPt)}pt`,
|
|
4615
4798
|
currentValue: String(frame.xPt),
|
|
4616
4799
|
commit: (raw) => {
|
|
4617
4800
|
dispatch({
|
|
@@ -4626,7 +4809,7 @@ function ShapeDetail(props) {
|
|
|
4626
4809
|
}
|
|
4627
4810
|
},
|
|
4628
4811
|
{
|
|
4629
|
-
label: `Y: ${formatPt(frame.yPt)}pt`,
|
|
4812
|
+
label: `Y: ${formatPt$1(frame.yPt)}pt`,
|
|
4630
4813
|
currentValue: String(frame.yPt),
|
|
4631
4814
|
commit: (raw) => {
|
|
4632
4815
|
dispatch({
|
|
@@ -4641,7 +4824,7 @@ function ShapeDetail(props) {
|
|
|
4641
4824
|
}
|
|
4642
4825
|
},
|
|
4643
4826
|
{
|
|
4644
|
-
label: `Width: ${formatPt(frame.widthPt)}pt`,
|
|
4827
|
+
label: `Width: ${formatPt$1(frame.widthPt)}pt`,
|
|
4645
4828
|
currentValue: String(frame.widthPt),
|
|
4646
4829
|
commit: (raw) => {
|
|
4647
4830
|
dispatch({
|
|
@@ -4656,7 +4839,7 @@ function ShapeDetail(props) {
|
|
|
4656
4839
|
}
|
|
4657
4840
|
},
|
|
4658
4841
|
{
|
|
4659
|
-
label: `Height: ${formatPt(frame.heightPt)}pt`,
|
|
4842
|
+
label: `Height: ${formatPt$1(frame.heightPt)}pt`,
|
|
4660
4843
|
currentValue: String(frame.heightPt),
|
|
4661
4844
|
commit: (raw) => {
|
|
4662
4845
|
dispatch({
|
|
@@ -4671,7 +4854,7 @@ function ShapeDetail(props) {
|
|
|
4671
4854
|
}
|
|
4672
4855
|
},
|
|
4673
4856
|
{
|
|
4674
|
-
label: `Rotation: ${shape.rotationDeg === void 0 ? "none" : `${formatPt(shape.rotationDeg)} deg`}`,
|
|
4857
|
+
label: `Rotation: ${shape.rotationDeg === void 0 ? "none" : `${formatPt$1(shape.rotationDeg)} deg`}`,
|
|
4675
4858
|
currentValue: shape.rotationDeg === void 0 ? "" : String(shape.rotationDeg),
|
|
4676
4859
|
commit: (raw) => {
|
|
4677
4860
|
const trimmed = raw.trim();
|
|
@@ -6997,26 +7180,75 @@ function requirePdfDocument(openDocument) {
|
|
|
6997
7180
|
if (openDocument?.format !== "pdf" && openDocument?.format !== "xlsx") throw new Error("A PDF inspection screen rendered without an open PDF or xlsx document; the app router only reaches this screen group from pdfPageList, which is only ever the root screen of one of those two formats.");
|
|
6998
7181
|
return openDocument;
|
|
6999
7182
|
}
|
|
7183
|
+
function isEditablePdfDocument(doc) {
|
|
7184
|
+
return doc.format === "pdf";
|
|
7185
|
+
}
|
|
7000
7186
|
function formatSize(widthPt, heightPt) {
|
|
7001
7187
|
return `${widthPt.toFixed(0)}×${heightPt.toFixed(0)}pt`;
|
|
7002
7188
|
}
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
"
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7189
|
+
function formatPt(value) {
|
|
7190
|
+
return value.toFixed(1);
|
|
7191
|
+
}
|
|
7192
|
+
function formatColor(color) {
|
|
7193
|
+
const byte = (component) => Math.round(component * 255).toString(16).padStart(2, "0");
|
|
7194
|
+
return `#${byte(color.r)}${byte(color.g)}${byte(color.b)}`;
|
|
7195
|
+
}
|
|
7196
|
+
function formatStroke(stroke) {
|
|
7197
|
+
return `${formatColor(stroke.color)} @ ${stroke.widthPt.toFixed(1)}pt`;
|
|
7198
|
+
}
|
|
7199
|
+
function parseRequiredColorField(raw, fallback) {
|
|
7200
|
+
return parseColorField(raw) ?? fallback;
|
|
7201
|
+
}
|
|
7202
|
+
function parseFontWeight(raw) {
|
|
7203
|
+
return raw.trim().toLowerCase() === "bold" ? "bold" : "normal";
|
|
7204
|
+
}
|
|
7205
|
+
function parseFontStyle(raw) {
|
|
7206
|
+
return raw.trim().toLowerCase() === "italic" ? "italic" : "normal";
|
|
7207
|
+
}
|
|
7208
|
+
function parseOptionalNumberField(raw) {
|
|
7209
|
+
const trimmed = raw.trim();
|
|
7210
|
+
if (trimmed.length === 0) return;
|
|
7211
|
+
const parsed = Number.parseFloat(trimmed);
|
|
7212
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
7213
|
+
}
|
|
7214
|
+
function defaultTriangleLayoutSubpaths(widthPt, heightPt) {
|
|
7215
|
+
return [{
|
|
7216
|
+
startXPt: 0,
|
|
7217
|
+
startYPt: heightPt,
|
|
7218
|
+
segments: [{
|
|
7219
|
+
kind: "line",
|
|
7220
|
+
xPt: widthPt / 2,
|
|
7221
|
+
yPt: 0
|
|
7222
|
+
}, {
|
|
7223
|
+
kind: "line",
|
|
7224
|
+
xPt: widthPt,
|
|
7225
|
+
yPt: heightPt
|
|
7226
|
+
}],
|
|
7227
|
+
closed: true
|
|
7228
|
+
}];
|
|
7229
|
+
}
|
|
7230
|
+
function inferImageFormat(path) {
|
|
7231
|
+
const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
|
|
7232
|
+
if (extension === "png") return "png";
|
|
7233
|
+
if (extension === "jpg" || extension === "jpeg") return "jpeg";
|
|
7234
|
+
}
|
|
7235
|
+
//#endregion
|
|
7236
|
+
//#region src/tui/screens/editors/pdf/page-list.tsx
|
|
7237
|
+
const LAYOUT_ITEM_KIND_ORDER = [
|
|
7238
|
+
"text",
|
|
7239
|
+
"image",
|
|
7240
|
+
"rect",
|
|
7241
|
+
"ellipse",
|
|
7242
|
+
"line",
|
|
7243
|
+
"path",
|
|
7244
|
+
"link"
|
|
7245
|
+
];
|
|
7246
|
+
function summariseItemKinds(items) {
|
|
7247
|
+
const counts = /* @__PURE__ */ new Map();
|
|
7248
|
+
for (const item of items) {
|
|
7249
|
+
const current = counts.get(item.kind);
|
|
7250
|
+
counts.set(item.kind, current === void 0 ? 1 : current + 1);
|
|
7251
|
+
}
|
|
7020
7252
|
const parts = [];
|
|
7021
7253
|
for (const kind of LAYOUT_ITEM_KIND_ORDER) {
|
|
7022
7254
|
const count = counts.get(kind);
|
|
@@ -7043,195 +7275,1036 @@ function PdfPageListScreen() {
|
|
|
7043
7275
|
const entry = pages[index];
|
|
7044
7276
|
if (entry === void 0) return;
|
|
7045
7277
|
dispatch({
|
|
7046
|
-
type: "PUSH_SCREEN",
|
|
7047
|
-
screen: {
|
|
7048
|
-
kind: "pdfPageItems",
|
|
7049
|
-
pageIndex: entry.pageIndex
|
|
7278
|
+
type: "PUSH_SCREEN",
|
|
7279
|
+
screen: {
|
|
7280
|
+
kind: "pdfPageItems",
|
|
7281
|
+
pageIndex: entry.pageIndex
|
|
7282
|
+
}
|
|
7283
|
+
});
|
|
7284
|
+
},
|
|
7285
|
+
onBack: () => {
|
|
7286
|
+
dispatch({ type: "POP_SCREEN" });
|
|
7287
|
+
},
|
|
7288
|
+
isActive: !anyOverlayOpen(state)
|
|
7289
|
+
});
|
|
7290
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
7291
|
+
flexDirection: "column",
|
|
7292
|
+
children: [/* @__PURE__ */ jsxs(Text, {
|
|
7293
|
+
bold: true,
|
|
7294
|
+
children: [
|
|
7295
|
+
"Pages (",
|
|
7296
|
+
pages.length,
|
|
7297
|
+
" of ",
|
|
7298
|
+
doc.layout.pages.length,
|
|
7299
|
+
")"
|
|
7300
|
+
]
|
|
7301
|
+
}), /* @__PURE__ */ jsx(ListView, {
|
|
7302
|
+
items: pages,
|
|
7303
|
+
selectedIndex,
|
|
7304
|
+
emptyMessage: query === "" ? "This PDF has no pages." : `No pages match "${state.searchQuery}".`,
|
|
7305
|
+
renderItem: ({ page, pageIndex }, isSelected) => /* @__PURE__ */ jsxs(Text, {
|
|
7306
|
+
color: isSelected ? "cyan" : void 0,
|
|
7307
|
+
inverse: isSelected,
|
|
7308
|
+
children: [
|
|
7309
|
+
"Page ",
|
|
7310
|
+
pageIndex + 1,
|
|
7311
|
+
" -- ",
|
|
7312
|
+
pageSummaryText(page)
|
|
7313
|
+
]
|
|
7314
|
+
})
|
|
7315
|
+
})]
|
|
7316
|
+
});
|
|
7317
|
+
}
|
|
7318
|
+
//#endregion
|
|
7319
|
+
//#region src/tui/screens/editors/pdf/item-detail.tsx
|
|
7320
|
+
function formatPoint(xPt, yPt) {
|
|
7321
|
+
return `(${xPt.toFixed(1)}, ${yPt.toFixed(1)})pt`;
|
|
7322
|
+
}
|
|
7323
|
+
function fieldsFor(item) {
|
|
7324
|
+
const fields = [["Kind", item.kind]];
|
|
7325
|
+
switch (item.kind) {
|
|
7326
|
+
case "text":
|
|
7327
|
+
fields.push(["Text", item.text]);
|
|
7328
|
+
fields.push(["Position", formatPoint(item.xPt, item.yPt)]);
|
|
7329
|
+
fields.push(["Font family", item.font.family]);
|
|
7330
|
+
fields.push(["Font weight", item.font.weight]);
|
|
7331
|
+
fields.push(["Font style", item.font.style]);
|
|
7332
|
+
fields.push(["Size", `${item.sizePt}pt`]);
|
|
7333
|
+
fields.push(["Colour", formatColor(item.color)]);
|
|
7334
|
+
if (item.widthPt !== void 0) fields.push(["Width", `${item.widthPt}pt`]);
|
|
7335
|
+
if (item.rotationDeg !== void 0) fields.push(["Rotation", `${item.rotationDeg}°`]);
|
|
7336
|
+
if (item.underline !== void 0) fields.push(["Underline", item.underline ? "yes" : "no"]);
|
|
7337
|
+
break;
|
|
7338
|
+
case "image":
|
|
7339
|
+
fields.push(["Image ID", item.imageId]);
|
|
7340
|
+
fields.push(["Position", formatPoint(item.xPt, item.yPt)]);
|
|
7341
|
+
fields.push(["Size", formatSize(item.widthPt, item.heightPt)]);
|
|
7342
|
+
if (item.rotationDeg !== void 0) fields.push(["Rotation", `${item.rotationDeg}°`]);
|
|
7343
|
+
break;
|
|
7344
|
+
case "rect":
|
|
7345
|
+
case "ellipse":
|
|
7346
|
+
fields.push(["Position", formatPoint(item.xPt, item.yPt)]);
|
|
7347
|
+
fields.push(["Size", formatSize(item.widthPt, item.heightPt)]);
|
|
7348
|
+
if (item.fill !== void 0) fields.push(["Fill", formatColor(item.fill)]);
|
|
7349
|
+
if (item.stroke !== void 0) fields.push(["Stroke", formatStroke(item.stroke)]);
|
|
7350
|
+
break;
|
|
7351
|
+
case "line":
|
|
7352
|
+
fields.push(["From", formatPoint(item.x1Pt, item.y1Pt)]);
|
|
7353
|
+
fields.push(["To", formatPoint(item.x2Pt, item.y2Pt)]);
|
|
7354
|
+
fields.push(["Colour", formatColor(item.color)]);
|
|
7355
|
+
fields.push(["Width", `${item.widthPt}pt`]);
|
|
7356
|
+
break;
|
|
7357
|
+
case "path":
|
|
7358
|
+
fields.push(["Subpaths", `${item.subpaths.length}`]);
|
|
7359
|
+
fields.push(["Segments", `${item.subpaths.reduce((total, subpath) => total + subpath.segments.length, 0)}`]);
|
|
7360
|
+
if (item.fill !== void 0) fields.push(["Fill", formatColor(item.fill)]);
|
|
7361
|
+
if (item.fillRule !== void 0) fields.push(["Fill rule", item.fillRule]);
|
|
7362
|
+
if (item.stroke !== void 0) fields.push(["Stroke", formatStroke(item.stroke)]);
|
|
7363
|
+
break;
|
|
7364
|
+
case "link":
|
|
7365
|
+
fields.push(["URI", item.uri]);
|
|
7366
|
+
fields.push(["Position", formatPoint(item.xPt, item.yPt)]);
|
|
7367
|
+
fields.push(["Size", formatSize(item.widthPt, item.heightPt)]);
|
|
7368
|
+
break;
|
|
7369
|
+
default: return item;
|
|
7370
|
+
}
|
|
7371
|
+
if (item.sourcePath !== void 0) fields.push(["Source path", item.sourcePath]);
|
|
7372
|
+
return fields;
|
|
7373
|
+
}
|
|
7374
|
+
function ReadOnlyItemDetail(props) {
|
|
7375
|
+
useInput((input, key) => {
|
|
7376
|
+
if (key.escape || key.leftArrow || input === "h") props.onBack();
|
|
7377
|
+
}, { isActive: props.isActive });
|
|
7378
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
7379
|
+
flexDirection: "column",
|
|
7380
|
+
children: [
|
|
7381
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
7382
|
+
bold: true,
|
|
7383
|
+
children: [
|
|
7384
|
+
"Page ",
|
|
7385
|
+
props.pageIndex + 1,
|
|
7386
|
+
", item ",
|
|
7387
|
+
props.itemIndex + 1
|
|
7388
|
+
]
|
|
7389
|
+
}),
|
|
7390
|
+
fieldsFor(props.item).map(([label, value]) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
7391
|
+
label,
|
|
7392
|
+
": ",
|
|
7393
|
+
value
|
|
7394
|
+
] }, label)),
|
|
7395
|
+
/* @__PURE__ */ jsx(Text, {
|
|
7396
|
+
dimColor: true,
|
|
7397
|
+
children: "Esc / ← / h to go back"
|
|
7398
|
+
})
|
|
7399
|
+
]
|
|
7400
|
+
});
|
|
7401
|
+
}
|
|
7402
|
+
function buildFrameRows(frame, onFrameChange) {
|
|
7403
|
+
return [
|
|
7404
|
+
{
|
|
7405
|
+
label: `X: ${formatPt(frame.xPt)}pt`,
|
|
7406
|
+
currentValue: String(frame.xPt),
|
|
7407
|
+
commit: (raw) => onFrameChange({
|
|
7408
|
+
...frame,
|
|
7409
|
+
xPt: parseNumberField(raw, frame.xPt)
|
|
7410
|
+
})
|
|
7411
|
+
},
|
|
7412
|
+
{
|
|
7413
|
+
label: `Y: ${formatPt(frame.yPt)}pt`,
|
|
7414
|
+
currentValue: String(frame.yPt),
|
|
7415
|
+
commit: (raw) => onFrameChange({
|
|
7416
|
+
...frame,
|
|
7417
|
+
yPt: parseNumberField(raw, frame.yPt)
|
|
7418
|
+
})
|
|
7419
|
+
},
|
|
7420
|
+
{
|
|
7421
|
+
label: `Width: ${formatPt(frame.widthPt)}pt`,
|
|
7422
|
+
currentValue: String(frame.widthPt),
|
|
7423
|
+
commit: (raw) => onFrameChange({
|
|
7424
|
+
...frame,
|
|
7425
|
+
widthPt: parseNumberField(raw, frame.widthPt)
|
|
7426
|
+
})
|
|
7427
|
+
},
|
|
7428
|
+
{
|
|
7429
|
+
label: `Height: ${formatPt(frame.heightPt)}pt`,
|
|
7430
|
+
currentValue: String(frame.heightPt),
|
|
7431
|
+
commit: (raw) => onFrameChange({
|
|
7432
|
+
...frame,
|
|
7433
|
+
heightPt: parseNumberField(raw, frame.heightPt)
|
|
7434
|
+
})
|
|
7435
|
+
}
|
|
7436
|
+
];
|
|
7437
|
+
}
|
|
7438
|
+
function buildFillStrokeRows(fill, stroke, onFillChange, onStrokeChange) {
|
|
7439
|
+
return [{
|
|
7440
|
+
label: `Fill: ${fill === void 0 ? "none" : formatColor(fill)}`,
|
|
7441
|
+
currentValue: fill === void 0 ? "" : `${fill.r} ${fill.g} ${fill.b}`,
|
|
7442
|
+
commit: (raw) => onFillChange(parseColorField(raw))
|
|
7443
|
+
}, {
|
|
7444
|
+
label: `Stroke: ${stroke === void 0 ? "none" : formatStroke(stroke)}`,
|
|
7445
|
+
currentValue: stroke === void 0 ? "" : `${stroke.color.r} ${stroke.color.g} ${stroke.color.b} ${stroke.widthPt}`,
|
|
7446
|
+
commit: (raw) => onStrokeChange(parseStrokeField(raw))
|
|
7447
|
+
}];
|
|
7448
|
+
}
|
|
7449
|
+
function buildTextRows(item, pageIndex, itemIndex, dispatch) {
|
|
7450
|
+
return [
|
|
7451
|
+
{
|
|
7452
|
+
label: `Text: ${item.text}`,
|
|
7453
|
+
currentValue: item.text,
|
|
7454
|
+
commit: (raw) => dispatch({
|
|
7455
|
+
type: "SET_PDF_TEXT_TEXT",
|
|
7456
|
+
pageIndex,
|
|
7457
|
+
itemIndex,
|
|
7458
|
+
text: raw
|
|
7459
|
+
})
|
|
7460
|
+
},
|
|
7461
|
+
{
|
|
7462
|
+
label: `X: ${formatPt(item.xPt)}pt`,
|
|
7463
|
+
currentValue: String(item.xPt),
|
|
7464
|
+
commit: (raw) => dispatch({
|
|
7465
|
+
type: "SET_PDF_TEXT_POSITION",
|
|
7466
|
+
pageIndex,
|
|
7467
|
+
itemIndex,
|
|
7468
|
+
xPt: parseNumberField(raw, item.xPt),
|
|
7469
|
+
yPt: item.yPt
|
|
7470
|
+
})
|
|
7471
|
+
},
|
|
7472
|
+
{
|
|
7473
|
+
label: `Y: ${formatPt(item.yPt)}pt`,
|
|
7474
|
+
currentValue: String(item.yPt),
|
|
7475
|
+
commit: (raw) => dispatch({
|
|
7476
|
+
type: "SET_PDF_TEXT_POSITION",
|
|
7477
|
+
pageIndex,
|
|
7478
|
+
itemIndex,
|
|
7479
|
+
xPt: item.xPt,
|
|
7480
|
+
yPt: parseNumberField(raw, item.yPt)
|
|
7481
|
+
})
|
|
7482
|
+
},
|
|
7483
|
+
{
|
|
7484
|
+
label: `Font family: ${item.font.family}`,
|
|
7485
|
+
currentValue: item.font.family,
|
|
7486
|
+
commit: (raw) => {
|
|
7487
|
+
const family = raw.trim();
|
|
7488
|
+
dispatch({
|
|
7489
|
+
type: "SET_PDF_TEXT_FONT",
|
|
7490
|
+
pageIndex,
|
|
7491
|
+
itemIndex,
|
|
7492
|
+
font: {
|
|
7493
|
+
...item.font,
|
|
7494
|
+
family: family.length === 0 ? item.font.family : family
|
|
7495
|
+
}
|
|
7496
|
+
});
|
|
7497
|
+
}
|
|
7498
|
+
},
|
|
7499
|
+
{
|
|
7500
|
+
label: `Font weight: ${item.font.weight} (Enter to toggle)`,
|
|
7501
|
+
currentValue: "",
|
|
7502
|
+
activate: () => dispatch({
|
|
7503
|
+
type: "SET_PDF_TEXT_FONT",
|
|
7504
|
+
pageIndex,
|
|
7505
|
+
itemIndex,
|
|
7506
|
+
font: {
|
|
7507
|
+
...item.font,
|
|
7508
|
+
weight: item.font.weight === "bold" ? "normal" : "bold"
|
|
7509
|
+
}
|
|
7510
|
+
})
|
|
7511
|
+
},
|
|
7512
|
+
{
|
|
7513
|
+
label: `Font style: ${item.font.style} (Enter to toggle)`,
|
|
7514
|
+
currentValue: "",
|
|
7515
|
+
activate: () => dispatch({
|
|
7516
|
+
type: "SET_PDF_TEXT_FONT",
|
|
7517
|
+
pageIndex,
|
|
7518
|
+
itemIndex,
|
|
7519
|
+
font: {
|
|
7520
|
+
...item.font,
|
|
7521
|
+
style: item.font.style === "italic" ? "normal" : "italic"
|
|
7522
|
+
}
|
|
7523
|
+
})
|
|
7524
|
+
},
|
|
7525
|
+
{
|
|
7526
|
+
label: `Size: ${item.sizePt}pt`,
|
|
7527
|
+
currentValue: String(item.sizePt),
|
|
7528
|
+
commit: (raw) => dispatch({
|
|
7529
|
+
type: "SET_PDF_TEXT_SIZE",
|
|
7530
|
+
pageIndex,
|
|
7531
|
+
itemIndex,
|
|
7532
|
+
sizePt: parseNumberField(raw, item.sizePt)
|
|
7533
|
+
})
|
|
7534
|
+
},
|
|
7535
|
+
{
|
|
7536
|
+
label: `Colour: ${formatColor(item.color)}`,
|
|
7537
|
+
currentValue: `${item.color.r} ${item.color.g} ${item.color.b}`,
|
|
7538
|
+
commit: (raw) => dispatch({
|
|
7539
|
+
type: "SET_PDF_TEXT_COLOR",
|
|
7540
|
+
pageIndex,
|
|
7541
|
+
itemIndex,
|
|
7542
|
+
color: parseRequiredColorField(raw, item.color)
|
|
7543
|
+
})
|
|
7544
|
+
},
|
|
7545
|
+
{
|
|
7546
|
+
label: `Width: ${item.widthPt === void 0 ? "unset" : `${item.widthPt}pt`}`,
|
|
7547
|
+
currentValue: item.widthPt === void 0 ? "" : String(item.widthPt),
|
|
7548
|
+
commit: (raw) => dispatch({
|
|
7549
|
+
type: "SET_PDF_TEXT_WIDTH",
|
|
7550
|
+
pageIndex,
|
|
7551
|
+
itemIndex,
|
|
7552
|
+
widthPt: parseOptionalNumberField(raw)
|
|
7553
|
+
})
|
|
7554
|
+
},
|
|
7555
|
+
{
|
|
7556
|
+
label: `Rotation: ${item.rotationDeg === void 0 ? "unset" : `${item.rotationDeg}°`}`,
|
|
7557
|
+
currentValue: item.rotationDeg === void 0 ? "" : String(item.rotationDeg),
|
|
7558
|
+
commit: (raw) => dispatch({
|
|
7559
|
+
type: "SET_PDF_TEXT_ROTATION",
|
|
7560
|
+
pageIndex,
|
|
7561
|
+
itemIndex,
|
|
7562
|
+
rotationDeg: parseOptionalNumberField(raw)
|
|
7563
|
+
})
|
|
7564
|
+
},
|
|
7565
|
+
{
|
|
7566
|
+
label: `Underline: ${item.underline === true ? "yes" : "no"} (Enter to toggle)`,
|
|
7567
|
+
currentValue: "",
|
|
7568
|
+
activate: () => dispatch({
|
|
7569
|
+
type: "TOGGLE_PDF_TEXT_UNDERLINE",
|
|
7570
|
+
pageIndex,
|
|
7571
|
+
itemIndex
|
|
7572
|
+
})
|
|
7573
|
+
}
|
|
7574
|
+
];
|
|
7575
|
+
}
|
|
7576
|
+
function buildRectRows(item, pageIndex, itemIndex, dispatch) {
|
|
7577
|
+
return [...buildFrameRows(item, (frame) => dispatch({
|
|
7578
|
+
type: "SET_PDF_RECT_FRAME",
|
|
7579
|
+
pageIndex,
|
|
7580
|
+
itemIndex,
|
|
7581
|
+
...frame
|
|
7582
|
+
})), ...buildFillStrokeRows(item.fill, item.stroke, (fill) => dispatch({
|
|
7583
|
+
type: "SET_PDF_RECT_FILL",
|
|
7584
|
+
pageIndex,
|
|
7585
|
+
itemIndex,
|
|
7586
|
+
fill
|
|
7587
|
+
}), (stroke) => dispatch({
|
|
7588
|
+
type: "SET_PDF_RECT_STROKE",
|
|
7589
|
+
pageIndex,
|
|
7590
|
+
itemIndex,
|
|
7591
|
+
stroke
|
|
7592
|
+
}))];
|
|
7593
|
+
}
|
|
7594
|
+
function buildEllipseRows(item, pageIndex, itemIndex, dispatch) {
|
|
7595
|
+
return [...buildFrameRows(item, (frame) => dispatch({
|
|
7596
|
+
type: "SET_PDF_ELLIPSE_FRAME",
|
|
7597
|
+
pageIndex,
|
|
7598
|
+
itemIndex,
|
|
7599
|
+
...frame
|
|
7600
|
+
})), ...buildFillStrokeRows(item.fill, item.stroke, (fill) => dispatch({
|
|
7601
|
+
type: "SET_PDF_ELLIPSE_FILL",
|
|
7602
|
+
pageIndex,
|
|
7603
|
+
itemIndex,
|
|
7604
|
+
fill
|
|
7605
|
+
}), (stroke) => dispatch({
|
|
7606
|
+
type: "SET_PDF_ELLIPSE_STROKE",
|
|
7607
|
+
pageIndex,
|
|
7608
|
+
itemIndex,
|
|
7609
|
+
stroke
|
|
7610
|
+
}))];
|
|
7611
|
+
}
|
|
7612
|
+
function buildLineRows(item, pageIndex, itemIndex, dispatch) {
|
|
7613
|
+
return [
|
|
7614
|
+
{
|
|
7615
|
+
label: `From X: ${formatPt(item.x1Pt)}pt`,
|
|
7616
|
+
currentValue: String(item.x1Pt),
|
|
7617
|
+
commit: (raw) => dispatch({
|
|
7618
|
+
type: "SET_PDF_LINE_FROM",
|
|
7619
|
+
pageIndex,
|
|
7620
|
+
itemIndex,
|
|
7621
|
+
x1Pt: parseNumberField(raw, item.x1Pt),
|
|
7622
|
+
y1Pt: item.y1Pt
|
|
7623
|
+
})
|
|
7624
|
+
},
|
|
7625
|
+
{
|
|
7626
|
+
label: `From Y: ${formatPt(item.y1Pt)}pt`,
|
|
7627
|
+
currentValue: String(item.y1Pt),
|
|
7628
|
+
commit: (raw) => dispatch({
|
|
7629
|
+
type: "SET_PDF_LINE_FROM",
|
|
7630
|
+
pageIndex,
|
|
7631
|
+
itemIndex,
|
|
7632
|
+
x1Pt: item.x1Pt,
|
|
7633
|
+
y1Pt: parseNumberField(raw, item.y1Pt)
|
|
7634
|
+
})
|
|
7635
|
+
},
|
|
7636
|
+
{
|
|
7637
|
+
label: `To X: ${formatPt(item.x2Pt)}pt`,
|
|
7638
|
+
currentValue: String(item.x2Pt),
|
|
7639
|
+
commit: (raw) => dispatch({
|
|
7640
|
+
type: "SET_PDF_LINE_TO",
|
|
7641
|
+
pageIndex,
|
|
7642
|
+
itemIndex,
|
|
7643
|
+
x2Pt: parseNumberField(raw, item.x2Pt),
|
|
7644
|
+
y2Pt: item.y2Pt
|
|
7645
|
+
})
|
|
7646
|
+
},
|
|
7647
|
+
{
|
|
7648
|
+
label: `To Y: ${formatPt(item.y2Pt)}pt`,
|
|
7649
|
+
currentValue: String(item.y2Pt),
|
|
7650
|
+
commit: (raw) => dispatch({
|
|
7651
|
+
type: "SET_PDF_LINE_TO",
|
|
7652
|
+
pageIndex,
|
|
7653
|
+
itemIndex,
|
|
7654
|
+
x2Pt: item.x2Pt,
|
|
7655
|
+
y2Pt: parseNumberField(raw, item.y2Pt)
|
|
7656
|
+
})
|
|
7657
|
+
},
|
|
7658
|
+
{
|
|
7659
|
+
label: `Colour: ${formatColor(item.color)}`,
|
|
7660
|
+
currentValue: `${item.color.r} ${item.color.g} ${item.color.b}`,
|
|
7661
|
+
commit: (raw) => dispatch({
|
|
7662
|
+
type: "SET_PDF_LINE_COLOR",
|
|
7663
|
+
pageIndex,
|
|
7664
|
+
itemIndex,
|
|
7665
|
+
color: parseRequiredColorField(raw, item.color)
|
|
7666
|
+
})
|
|
7667
|
+
},
|
|
7668
|
+
{
|
|
7669
|
+
label: `Width: ${item.widthPt}pt`,
|
|
7670
|
+
currentValue: String(item.widthPt),
|
|
7671
|
+
commit: (raw) => dispatch({
|
|
7672
|
+
type: "SET_PDF_LINE_WIDTH",
|
|
7673
|
+
pageIndex,
|
|
7674
|
+
itemIndex,
|
|
7675
|
+
widthPt: Math.max(parseNumberField(raw, item.widthPt), Number.EPSILON)
|
|
7676
|
+
})
|
|
7677
|
+
}
|
|
7678
|
+
];
|
|
7679
|
+
}
|
|
7680
|
+
function buildPathRows(item, pageIndex, itemIndex, dispatch) {
|
|
7681
|
+
return [{
|
|
7682
|
+
label: `Fill rule: ${item.fillRule ?? "nonzero (default)"} (Enter to cycle)`,
|
|
7683
|
+
currentValue: "",
|
|
7684
|
+
activate: () => dispatch({
|
|
7685
|
+
type: "SET_PDF_PATH_FILL_RULE",
|
|
7686
|
+
pageIndex,
|
|
7687
|
+
itemIndex,
|
|
7688
|
+
fillRule: item.fillRule === "evenodd" ? void 0 : item.fillRule === "nonzero" ? "evenodd" : "nonzero"
|
|
7689
|
+
})
|
|
7690
|
+
}, ...buildFillStrokeRows(item.fill, item.stroke, (fill) => dispatch({
|
|
7691
|
+
type: "SET_PDF_PATH_FILL",
|
|
7692
|
+
pageIndex,
|
|
7693
|
+
itemIndex,
|
|
7694
|
+
fill
|
|
7695
|
+
}), (stroke) => dispatch({
|
|
7696
|
+
type: "SET_PDF_PATH_STROKE",
|
|
7697
|
+
pageIndex,
|
|
7698
|
+
itemIndex,
|
|
7699
|
+
stroke
|
|
7700
|
+
}))];
|
|
7701
|
+
}
|
|
7702
|
+
function buildImageRows(item, pageIndex, itemIndex, dispatch, onReplaceImage) {
|
|
7703
|
+
return [
|
|
7704
|
+
...buildFrameRows(item, (frame) => dispatch({
|
|
7705
|
+
type: "SET_PDF_IMAGE_FRAME",
|
|
7706
|
+
pageIndex,
|
|
7707
|
+
itemIndex,
|
|
7708
|
+
...frame
|
|
7709
|
+
})),
|
|
7710
|
+
{
|
|
7711
|
+
label: `Rotation: ${item.rotationDeg === void 0 ? "unset" : `${item.rotationDeg}°`}`,
|
|
7712
|
+
currentValue: item.rotationDeg === void 0 ? "" : String(item.rotationDeg),
|
|
7713
|
+
commit: (raw) => dispatch({
|
|
7714
|
+
type: "SET_PDF_IMAGE_ROTATION",
|
|
7715
|
+
pageIndex,
|
|
7716
|
+
itemIndex,
|
|
7717
|
+
rotationDeg: parseOptionalNumberField(raw)
|
|
7718
|
+
})
|
|
7719
|
+
},
|
|
7720
|
+
{
|
|
7721
|
+
label: "Replace image...",
|
|
7722
|
+
currentValue: "",
|
|
7723
|
+
activate: onReplaceImage
|
|
7724
|
+
}
|
|
7725
|
+
];
|
|
7726
|
+
}
|
|
7727
|
+
function buildLinkRows(item, pageIndex, itemIndex, dispatch) {
|
|
7728
|
+
return [{
|
|
7729
|
+
label: `URI: ${item.uri}`,
|
|
7730
|
+
currentValue: item.uri,
|
|
7731
|
+
commit: (raw) => dispatch({
|
|
7732
|
+
type: "SET_PDF_LINK_URI",
|
|
7733
|
+
pageIndex,
|
|
7734
|
+
itemIndex,
|
|
7735
|
+
uri: raw
|
|
7736
|
+
})
|
|
7737
|
+
}, ...buildFrameRows(item, (frame) => dispatch({
|
|
7738
|
+
type: "SET_PDF_LINK_FRAME",
|
|
7739
|
+
pageIndex,
|
|
7740
|
+
itemIndex,
|
|
7741
|
+
...frame
|
|
7742
|
+
}))];
|
|
7743
|
+
}
|
|
7744
|
+
function buildRowsFor(item, pageIndex, itemIndex, dispatch, onReplaceImage) {
|
|
7745
|
+
switch (item.kind) {
|
|
7746
|
+
case "text": return buildTextRows(item, pageIndex, itemIndex, dispatch);
|
|
7747
|
+
case "rect": return buildRectRows(item, pageIndex, itemIndex, dispatch);
|
|
7748
|
+
case "ellipse": return buildEllipseRows(item, pageIndex, itemIndex, dispatch);
|
|
7749
|
+
case "line": return buildLineRows(item, pageIndex, itemIndex, dispatch);
|
|
7750
|
+
case "path": return buildPathRows(item, pageIndex, itemIndex, dispatch);
|
|
7751
|
+
case "image": return buildImageRows(item, pageIndex, itemIndex, dispatch, onReplaceImage);
|
|
7752
|
+
case "link": return buildLinkRows(item, pageIndex, itemIndex, dispatch);
|
|
7753
|
+
}
|
|
7754
|
+
}
|
|
7755
|
+
function pathSummary(item) {
|
|
7756
|
+
const segmentCount = item.subpaths.reduce((total, subpath) => total + subpath.segments.length, 0);
|
|
7757
|
+
return `${item.subpaths.length} subpath${item.subpaths.length === 1 ? "" : "s"}, ${segmentCount} segment${segmentCount === 1 ? "" : "s"} (not editable here -- see documents.js's own PdfPathItem doc comment)`;
|
|
7758
|
+
}
|
|
7759
|
+
async function applyImageReplace(pageIndex, itemIndex, path, dispatch) {
|
|
7760
|
+
const format = inferImageFormat(path);
|
|
7761
|
+
if (format === void 0) {
|
|
7762
|
+
dispatch({
|
|
7763
|
+
type: "SET_STATUS",
|
|
7764
|
+
severity: "warning",
|
|
7765
|
+
text: `${path} is not a .png or .jpg/.jpeg file -- image not replaced`
|
|
7766
|
+
});
|
|
7767
|
+
return;
|
|
7768
|
+
}
|
|
7769
|
+
try {
|
|
7770
|
+
dispatch({
|
|
7771
|
+
type: "SET_PDF_IMAGE_SOURCE",
|
|
7772
|
+
pageIndex,
|
|
7773
|
+
itemIndex,
|
|
7774
|
+
format,
|
|
7775
|
+
bytes: new Uint8Array(await readInput(path))
|
|
7776
|
+
});
|
|
7777
|
+
} catch (error) {
|
|
7778
|
+
dispatch({
|
|
7779
|
+
type: "SET_STATUS",
|
|
7780
|
+
severity: "error",
|
|
7781
|
+
text: `Could not read ${path}: ${describeError(error)}`
|
|
7782
|
+
});
|
|
7783
|
+
}
|
|
7784
|
+
}
|
|
7785
|
+
function EditableItemDetail(props) {
|
|
7786
|
+
const dispatch = useAppDispatch();
|
|
7787
|
+
const { doc, pageIndex, itemIndex } = props;
|
|
7788
|
+
const [editingField, setEditingField] = useState(void 0);
|
|
7789
|
+
const [draft, setDraft] = useState("");
|
|
7790
|
+
const [replacingImage, setReplacingImage] = useState(false);
|
|
7791
|
+
const item = doc.editor.page(pageIndex)?.items()[itemIndex];
|
|
7792
|
+
const rows = item === void 0 ? [] : buildRowsFor(item, pageIndex, itemIndex, dispatch, () => setReplacingImage(true));
|
|
7793
|
+
const { selectedIndex } = useNavigationInput({
|
|
7794
|
+
itemCount: rows.length,
|
|
7795
|
+
isActive: props.isActive && editingField === void 0 && !replacingImage,
|
|
7796
|
+
onBack: () => {
|
|
7797
|
+
dispatch({ type: "POP_SCREEN" });
|
|
7798
|
+
},
|
|
7799
|
+
onSelect: (index) => {
|
|
7800
|
+
const row = rows[index];
|
|
7801
|
+
if (row === void 0) return;
|
|
7802
|
+
if (row.activate !== void 0) {
|
|
7803
|
+
row.activate();
|
|
7804
|
+
return;
|
|
7805
|
+
}
|
|
7806
|
+
setDraft(row.currentValue);
|
|
7807
|
+
setEditingField(index);
|
|
7808
|
+
}
|
|
7809
|
+
});
|
|
7810
|
+
if (item === void 0) return /* @__PURE__ */ jsxs(Box, {
|
|
7811
|
+
flexDirection: "column",
|
|
7812
|
+
children: [/* @__PURE__ */ jsxs(Text, {
|
|
7813
|
+
color: "yellow",
|
|
7814
|
+
children: [
|
|
7815
|
+
"There is no item ",
|
|
7816
|
+
itemIndex + 1,
|
|
7817
|
+
" on page ",
|
|
7818
|
+
pageIndex + 1,
|
|
7819
|
+
" any more."
|
|
7820
|
+
]
|
|
7821
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
7822
|
+
dimColor: true,
|
|
7823
|
+
children: "Esc to go back"
|
|
7824
|
+
})]
|
|
7825
|
+
});
|
|
7826
|
+
if (replacingImage) return /* @__PURE__ */ jsx(FieldWizard, {
|
|
7827
|
+
fields: [{
|
|
7828
|
+
key: "path",
|
|
7829
|
+
label: "Image file path (.png/.jpg/.jpeg)",
|
|
7830
|
+
defaultValue: ""
|
|
7831
|
+
}],
|
|
7832
|
+
onCancel: () => {
|
|
7833
|
+
setReplacingImage(false);
|
|
7834
|
+
},
|
|
7835
|
+
onComplete: (values) => {
|
|
7836
|
+
applyImageReplace(pageIndex, itemIndex, requireFieldValue(values, "path"), dispatch).then(() => {
|
|
7837
|
+
setReplacingImage(false);
|
|
7838
|
+
});
|
|
7839
|
+
}
|
|
7840
|
+
});
|
|
7841
|
+
if (editingField !== void 0) {
|
|
7842
|
+
const row = rows[editingField];
|
|
7843
|
+
if (row === void 0) throw new Error(`EditableItemDetail is editing field index ${editingField}, but there are only ${rows.length} rows -- selecting a row always sets editingField to a valid index from that same rows array, so this indicates a bug in that selection.`);
|
|
7844
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
7845
|
+
flexDirection: "column",
|
|
7846
|
+
borderStyle: "round",
|
|
7847
|
+
paddingX: 1,
|
|
7848
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
7849
|
+
bold: true,
|
|
7850
|
+
children: row.label
|
|
7851
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
7852
|
+
value: draft,
|
|
7853
|
+
isFocused: true,
|
|
7854
|
+
onChange: setDraft,
|
|
7855
|
+
onCancel: () => {
|
|
7856
|
+
setEditingField(void 0);
|
|
7857
|
+
},
|
|
7858
|
+
onSubmit: (value) => {
|
|
7859
|
+
row.commit?.(value);
|
|
7860
|
+
setEditingField(void 0);
|
|
7861
|
+
}
|
|
7862
|
+
})]
|
|
7863
|
+
});
|
|
7864
|
+
}
|
|
7865
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
7866
|
+
flexDirection: "column",
|
|
7867
|
+
children: [
|
|
7868
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
7869
|
+
bold: true,
|
|
7870
|
+
children: [
|
|
7871
|
+
"Page ",
|
|
7872
|
+
pageIndex + 1,
|
|
7873
|
+
", item ",
|
|
7874
|
+
itemIndex + 1,
|
|
7875
|
+
" -- ",
|
|
7876
|
+
item.kind
|
|
7877
|
+
]
|
|
7878
|
+
}),
|
|
7879
|
+
item.kind === "path" && /* @__PURE__ */ jsx(Text, {
|
|
7880
|
+
dimColor: true,
|
|
7881
|
+
children: pathSummary(item)
|
|
7882
|
+
}),
|
|
7883
|
+
item.kind === "image" && /* @__PURE__ */ jsxs(Text, {
|
|
7884
|
+
dimColor: true,
|
|
7885
|
+
children: ["Image ID: ", item.imageId]
|
|
7886
|
+
}),
|
|
7887
|
+
/* @__PURE__ */ jsx(ListView, {
|
|
7888
|
+
items: rows,
|
|
7889
|
+
selectedIndex,
|
|
7890
|
+
reservedRows: 5,
|
|
7891
|
+
renderItem: (row, isSelected) => /* @__PURE__ */ jsx(Text, {
|
|
7892
|
+
color: isSelected ? "cyan" : void 0,
|
|
7893
|
+
inverse: isSelected,
|
|
7894
|
+
children: row.label
|
|
7895
|
+
})
|
|
7896
|
+
}),
|
|
7897
|
+
/* @__PURE__ */ jsx(Text, {
|
|
7898
|
+
dimColor: true,
|
|
7899
|
+
children: "Enter to edit a field, Esc to go back"
|
|
7900
|
+
})
|
|
7901
|
+
]
|
|
7902
|
+
});
|
|
7903
|
+
}
|
|
7904
|
+
function PdfItemDetailScreen() {
|
|
7905
|
+
const state = useAppState();
|
|
7906
|
+
const dispatch = useAppDispatch();
|
|
7907
|
+
const doc = requirePdfDocument(state.openDocument);
|
|
7908
|
+
const screen = currentScreen(state);
|
|
7909
|
+
if (screen.kind !== "pdfItemDetail") throw new Error(`PdfItemDetailScreen rendered while the current screen is "${screen.kind}", not "pdfItemDetail".`);
|
|
7910
|
+
const isActive = !anyOverlayOpen(state);
|
|
7911
|
+
if (!isEditablePdfDocument(doc)) {
|
|
7912
|
+
const page = doc.layout.pages[screen.pageIndex];
|
|
7913
|
+
if (page === void 0) throw new Error(`pdfItemDetail was pushed for page ${screen.pageIndex}, but the open PDF has no page at that index.`);
|
|
7914
|
+
const item = page.items[screen.itemIndex];
|
|
7915
|
+
if (item === void 0) throw new Error(`pdfItemDetail was pushed for item ${screen.itemIndex} on page ${screen.pageIndex}, but that page has no item at that index.`);
|
|
7916
|
+
return /* @__PURE__ */ jsx(ReadOnlyItemDetail, {
|
|
7917
|
+
item,
|
|
7918
|
+
pageIndex: screen.pageIndex,
|
|
7919
|
+
itemIndex: screen.itemIndex,
|
|
7920
|
+
isActive,
|
|
7921
|
+
onBack: () => {
|
|
7922
|
+
dispatch({ type: "POP_SCREEN" });
|
|
7923
|
+
}
|
|
7924
|
+
});
|
|
7925
|
+
}
|
|
7926
|
+
return /* @__PURE__ */ jsx(EditableItemDetail, {
|
|
7927
|
+
doc,
|
|
7928
|
+
pageIndex: screen.pageIndex,
|
|
7929
|
+
itemIndex: screen.itemIndex,
|
|
7930
|
+
isActive
|
|
7931
|
+
});
|
|
7932
|
+
}
|
|
7933
|
+
//#endregion
|
|
7934
|
+
//#region src/tui/screens/editors/pdf/page-items.tsx
|
|
7935
|
+
const TEXT_PREVIEW_MAX_CHARS = 48;
|
|
7936
|
+
function truncate(text, maxChars) {
|
|
7937
|
+
return text.length > maxChars ? `${text.slice(0, maxChars)}…` : text;
|
|
7938
|
+
}
|
|
7939
|
+
function pathDimensions(item) {
|
|
7940
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
7941
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
7942
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
7943
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
7944
|
+
const consider = (xPt, yPt) => {
|
|
7945
|
+
minX = Math.min(minX, xPt);
|
|
7946
|
+
minY = Math.min(minY, yPt);
|
|
7947
|
+
maxX = Math.max(maxX, xPt);
|
|
7948
|
+
maxY = Math.max(maxY, yPt);
|
|
7949
|
+
};
|
|
7950
|
+
for (const subpath of item.subpaths) {
|
|
7951
|
+
consider(subpath.startXPt, subpath.startYPt);
|
|
7952
|
+
for (const segment of subpath.segments) {
|
|
7953
|
+
if (segment.kind === "cubic") {
|
|
7954
|
+
consider(segment.c1xPt, segment.c1yPt);
|
|
7955
|
+
consider(segment.c2xPt, segment.c2yPt);
|
|
7956
|
+
}
|
|
7957
|
+
consider(segment.xPt, segment.yPt);
|
|
7958
|
+
}
|
|
7959
|
+
}
|
|
7960
|
+
if (minX > maxX) return "empty path";
|
|
7961
|
+
return formatSize(maxX - minX, maxY - minY);
|
|
7962
|
+
}
|
|
7963
|
+
function previewFor(item) {
|
|
7964
|
+
switch (item.kind) {
|
|
7965
|
+
case "text": return truncate(item.text, TEXT_PREVIEW_MAX_CHARS);
|
|
7966
|
+
case "link": return item.uri;
|
|
7967
|
+
case "image":
|
|
7968
|
+
case "rect":
|
|
7969
|
+
case "ellipse": return formatSize(item.widthPt, item.heightPt);
|
|
7970
|
+
case "line": return formatSize(Math.abs(item.x2Pt - item.x1Pt), Math.abs(item.y2Pt - item.y1Pt));
|
|
7971
|
+
case "path": return pathDimensions(item);
|
|
7972
|
+
}
|
|
7973
|
+
}
|
|
7974
|
+
const ADD_KIND_OPTIONS = [
|
|
7975
|
+
{
|
|
7976
|
+
kind: "text",
|
|
7977
|
+
label: "Text"
|
|
7978
|
+
},
|
|
7979
|
+
{
|
|
7980
|
+
kind: "rect",
|
|
7981
|
+
label: "Rectangle"
|
|
7982
|
+
},
|
|
7983
|
+
{
|
|
7984
|
+
kind: "ellipse",
|
|
7985
|
+
label: "Ellipse"
|
|
7986
|
+
},
|
|
7987
|
+
{
|
|
7988
|
+
kind: "line",
|
|
7989
|
+
label: "Line"
|
|
7990
|
+
},
|
|
7991
|
+
{
|
|
7992
|
+
kind: "path",
|
|
7993
|
+
label: "Path (fixed triangle shape)"
|
|
7994
|
+
},
|
|
7995
|
+
{
|
|
7996
|
+
kind: "image",
|
|
7997
|
+
label: "Image"
|
|
7998
|
+
},
|
|
7999
|
+
{
|
|
8000
|
+
kind: "link",
|
|
8001
|
+
label: "Link"
|
|
8002
|
+
}
|
|
8003
|
+
];
|
|
8004
|
+
const GEOMETRY_FIELDS = [
|
|
8005
|
+
{
|
|
8006
|
+
key: "xPt",
|
|
8007
|
+
label: "X (pt)",
|
|
8008
|
+
defaultValue: "40"
|
|
8009
|
+
},
|
|
8010
|
+
{
|
|
8011
|
+
key: "yPt",
|
|
8012
|
+
label: "Y (pt)",
|
|
8013
|
+
defaultValue: "40"
|
|
8014
|
+
},
|
|
8015
|
+
{
|
|
8016
|
+
key: "widthPt",
|
|
8017
|
+
label: "Width (pt)",
|
|
8018
|
+
defaultValue: "160"
|
|
8019
|
+
},
|
|
8020
|
+
{
|
|
8021
|
+
key: "heightPt",
|
|
8022
|
+
label: "Height (pt)",
|
|
8023
|
+
defaultValue: "100"
|
|
8024
|
+
}
|
|
8025
|
+
];
|
|
8026
|
+
const FILL_FIELD = {
|
|
8027
|
+
key: "fill",
|
|
8028
|
+
label: "Fill \"r g b\" (0-1 each), blank for none",
|
|
8029
|
+
defaultValue: "0.8 0.8 0.8"
|
|
8030
|
+
};
|
|
8031
|
+
const STROKE_FIELD = {
|
|
8032
|
+
key: "stroke",
|
|
8033
|
+
label: "Stroke \"r g b widthPt\" (0-1 colour, pt width), blank for none",
|
|
8034
|
+
defaultValue: "0 0 0 1"
|
|
8035
|
+
};
|
|
8036
|
+
const REQUIRED_COLOR_FIELD = {
|
|
8037
|
+
key: "color",
|
|
8038
|
+
label: "Colour \"r g b\" (0-1 each)",
|
|
8039
|
+
defaultValue: "0 0 0"
|
|
8040
|
+
};
|
|
8041
|
+
function fieldsForAddKind(kind) {
|
|
8042
|
+
switch (kind) {
|
|
8043
|
+
case "text": return [
|
|
8044
|
+
{
|
|
8045
|
+
key: "xPt",
|
|
8046
|
+
label: "X (pt)",
|
|
8047
|
+
defaultValue: "40"
|
|
8048
|
+
},
|
|
8049
|
+
{
|
|
8050
|
+
key: "yPt",
|
|
8051
|
+
label: "Y (pt)",
|
|
8052
|
+
defaultValue: "40"
|
|
8053
|
+
},
|
|
8054
|
+
{
|
|
8055
|
+
key: "text",
|
|
8056
|
+
label: "Text",
|
|
8057
|
+
defaultValue: "Text"
|
|
8058
|
+
},
|
|
8059
|
+
{
|
|
8060
|
+
key: "fontFamily",
|
|
8061
|
+
label: "Font family",
|
|
8062
|
+
defaultValue: "Helvetica"
|
|
8063
|
+
},
|
|
8064
|
+
{
|
|
8065
|
+
key: "fontWeight",
|
|
8066
|
+
label: "Font weight (normal/bold)",
|
|
8067
|
+
defaultValue: "normal"
|
|
8068
|
+
},
|
|
8069
|
+
{
|
|
8070
|
+
key: "fontStyle",
|
|
8071
|
+
label: "Font style (normal/italic)",
|
|
8072
|
+
defaultValue: "normal"
|
|
8073
|
+
},
|
|
8074
|
+
{
|
|
8075
|
+
key: "sizePt",
|
|
8076
|
+
label: "Size (pt)",
|
|
8077
|
+
defaultValue: "12"
|
|
8078
|
+
},
|
|
8079
|
+
REQUIRED_COLOR_FIELD
|
|
8080
|
+
];
|
|
8081
|
+
case "rect":
|
|
8082
|
+
case "ellipse":
|
|
8083
|
+
case "path": return [
|
|
8084
|
+
...GEOMETRY_FIELDS,
|
|
8085
|
+
FILL_FIELD,
|
|
8086
|
+
STROKE_FIELD
|
|
8087
|
+
];
|
|
8088
|
+
case "line": return [
|
|
8089
|
+
{
|
|
8090
|
+
key: "fromXPt",
|
|
8091
|
+
label: "From X (pt)",
|
|
8092
|
+
defaultValue: "40"
|
|
8093
|
+
},
|
|
8094
|
+
{
|
|
8095
|
+
key: "fromYPt",
|
|
8096
|
+
label: "From Y (pt)",
|
|
8097
|
+
defaultValue: "40"
|
|
8098
|
+
},
|
|
8099
|
+
{
|
|
8100
|
+
key: "toXPt",
|
|
8101
|
+
label: "To X (pt)",
|
|
8102
|
+
defaultValue: "200"
|
|
8103
|
+
},
|
|
8104
|
+
{
|
|
8105
|
+
key: "toYPt",
|
|
8106
|
+
label: "To Y (pt)",
|
|
8107
|
+
defaultValue: "40"
|
|
8108
|
+
},
|
|
8109
|
+
REQUIRED_COLOR_FIELD,
|
|
8110
|
+
{
|
|
8111
|
+
key: "widthPt",
|
|
8112
|
+
label: "Width (pt)",
|
|
8113
|
+
defaultValue: "1"
|
|
8114
|
+
}
|
|
8115
|
+
];
|
|
8116
|
+
case "image": return [...GEOMETRY_FIELDS, {
|
|
8117
|
+
key: "path",
|
|
8118
|
+
label: "Image file path (.png/.jpg/.jpeg)",
|
|
8119
|
+
defaultValue: ""
|
|
8120
|
+
}];
|
|
8121
|
+
case "link": return [{
|
|
8122
|
+
key: "uri",
|
|
8123
|
+
label: "URI",
|
|
8124
|
+
defaultValue: "https://example.com"
|
|
8125
|
+
}, ...GEOMETRY_FIELDS];
|
|
8126
|
+
}
|
|
8127
|
+
}
|
|
8128
|
+
function readFrame(values) {
|
|
8129
|
+
return {
|
|
8130
|
+
xPt: parseNumberField(requireFieldValue(values, "xPt"), 0),
|
|
8131
|
+
yPt: parseNumberField(requireFieldValue(values, "yPt"), 0),
|
|
8132
|
+
widthPt: parseNumberField(requireFieldValue(values, "widthPt"), 100),
|
|
8133
|
+
heightPt: parseNumberField(requireFieldValue(values, "heightPt"), 60)
|
|
8134
|
+
};
|
|
8135
|
+
}
|
|
8136
|
+
async function applyAddKind(kind, pageIndex, values, dispatch) {
|
|
8137
|
+
switch (kind) {
|
|
8138
|
+
case "text":
|
|
8139
|
+
dispatch({
|
|
8140
|
+
type: "ADD_PDF_TEXT",
|
|
8141
|
+
pageIndex,
|
|
8142
|
+
init: {
|
|
8143
|
+
xPt: parseNumberField(requireFieldValue(values, "xPt"), 0),
|
|
8144
|
+
yPt: parseNumberField(requireFieldValue(values, "yPt"), 0),
|
|
8145
|
+
text: requireFieldValue(values, "text"),
|
|
8146
|
+
font: {
|
|
8147
|
+
family: requireFieldValue(values, "fontFamily").trim() || "Helvetica",
|
|
8148
|
+
weight: parseFontWeight(requireFieldValue(values, "fontWeight")),
|
|
8149
|
+
style: parseFontStyle(requireFieldValue(values, "fontStyle"))
|
|
8150
|
+
},
|
|
8151
|
+
sizePt: Math.max(parseNumberField(requireFieldValue(values, "sizePt"), 12), Number.EPSILON),
|
|
8152
|
+
color: parseColorField(requireFieldValue(values, "color")) ?? {
|
|
8153
|
+
r: 0,
|
|
8154
|
+
g: 0,
|
|
8155
|
+
b: 0
|
|
8156
|
+
}
|
|
8157
|
+
}
|
|
8158
|
+
});
|
|
8159
|
+
return;
|
|
8160
|
+
case "rect":
|
|
8161
|
+
dispatch({
|
|
8162
|
+
type: "ADD_PDF_RECT",
|
|
8163
|
+
pageIndex,
|
|
8164
|
+
init: {
|
|
8165
|
+
...readFrame(values),
|
|
8166
|
+
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
8167
|
+
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
8168
|
+
}
|
|
8169
|
+
});
|
|
8170
|
+
return;
|
|
8171
|
+
case "ellipse":
|
|
8172
|
+
dispatch({
|
|
8173
|
+
type: "ADD_PDF_ELLIPSE",
|
|
8174
|
+
pageIndex,
|
|
8175
|
+
init: {
|
|
8176
|
+
...readFrame(values),
|
|
8177
|
+
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
8178
|
+
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
8179
|
+
}
|
|
8180
|
+
});
|
|
8181
|
+
return;
|
|
8182
|
+
case "line":
|
|
8183
|
+
dispatch({
|
|
8184
|
+
type: "ADD_PDF_LINE",
|
|
8185
|
+
pageIndex,
|
|
8186
|
+
init: {
|
|
8187
|
+
x1Pt: parseNumberField(requireFieldValue(values, "fromXPt"), 0),
|
|
8188
|
+
y1Pt: parseNumberField(requireFieldValue(values, "fromYPt"), 0),
|
|
8189
|
+
x2Pt: parseNumberField(requireFieldValue(values, "toXPt"), 100),
|
|
8190
|
+
y2Pt: parseNumberField(requireFieldValue(values, "toYPt"), 0),
|
|
8191
|
+
color: parseColorField(requireFieldValue(values, "color")) ?? {
|
|
8192
|
+
r: 0,
|
|
8193
|
+
g: 0,
|
|
8194
|
+
b: 0
|
|
8195
|
+
},
|
|
8196
|
+
widthPt: Math.max(parseNumberField(requireFieldValue(values, "widthPt"), 1), Number.EPSILON)
|
|
8197
|
+
}
|
|
8198
|
+
});
|
|
8199
|
+
return;
|
|
8200
|
+
case "path": {
|
|
8201
|
+
const frame = readFrame(values);
|
|
8202
|
+
dispatch({
|
|
8203
|
+
type: "ADD_PDF_PATH",
|
|
8204
|
+
pageIndex,
|
|
8205
|
+
init: {
|
|
8206
|
+
subpaths: defaultTriangleLayoutSubpaths(frame.widthPt, frame.heightPt),
|
|
8207
|
+
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
8208
|
+
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
8209
|
+
}
|
|
8210
|
+
});
|
|
8211
|
+
return;
|
|
8212
|
+
}
|
|
8213
|
+
case "image": {
|
|
8214
|
+
const frame = readFrame(values);
|
|
8215
|
+
const path = requireFieldValue(values, "path");
|
|
8216
|
+
const format = inferImageFormat(path);
|
|
8217
|
+
if (format === void 0) {
|
|
8218
|
+
dispatch({
|
|
8219
|
+
type: "SET_STATUS",
|
|
8220
|
+
severity: "warning",
|
|
8221
|
+
text: `${path} is not a .png or .jpg/.jpeg file -- image not added`
|
|
8222
|
+
});
|
|
8223
|
+
return;
|
|
8224
|
+
}
|
|
8225
|
+
try {
|
|
8226
|
+
const bytes = new Uint8Array(await readInput(path));
|
|
8227
|
+
dispatch({
|
|
8228
|
+
type: "ADD_PDF_IMAGE",
|
|
8229
|
+
pageIndex,
|
|
8230
|
+
init: {
|
|
8231
|
+
...frame,
|
|
8232
|
+
format,
|
|
8233
|
+
bytes
|
|
8234
|
+
}
|
|
8235
|
+
});
|
|
8236
|
+
} catch (error) {
|
|
8237
|
+
dispatch({
|
|
8238
|
+
type: "SET_STATUS",
|
|
8239
|
+
severity: "error",
|
|
8240
|
+
text: `Could not read ${path}: ${describeError(error)}`
|
|
8241
|
+
});
|
|
8242
|
+
}
|
|
8243
|
+
return;
|
|
8244
|
+
}
|
|
8245
|
+
case "link": {
|
|
8246
|
+
const frame = readFrame(values);
|
|
8247
|
+
dispatch({
|
|
8248
|
+
type: "ADD_PDF_LINK",
|
|
8249
|
+
pageIndex,
|
|
8250
|
+
init: {
|
|
8251
|
+
uri: requireFieldValue(values, "uri"),
|
|
8252
|
+
...frame
|
|
7050
8253
|
}
|
|
7051
8254
|
});
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
8255
|
+
return;
|
|
8256
|
+
}
|
|
8257
|
+
}
|
|
8258
|
+
}
|
|
8259
|
+
function AddItemFlow(props) {
|
|
8260
|
+
const dispatch = useAppDispatch();
|
|
8261
|
+
const [kind, setKind] = useState(void 0);
|
|
8262
|
+
const { selectedIndex } = useNavigationInput({
|
|
8263
|
+
itemCount: ADD_KIND_OPTIONS.length,
|
|
8264
|
+
isActive: props.isActive && kind === void 0,
|
|
8265
|
+
onBack: props.onCancel,
|
|
8266
|
+
onSelect: (index) => {
|
|
8267
|
+
const option = ADD_KIND_OPTIONS[index];
|
|
8268
|
+
if (option === void 0) return;
|
|
8269
|
+
setKind(option.kind);
|
|
8270
|
+
}
|
|
7057
8271
|
});
|
|
7058
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
8272
|
+
if (kind === void 0) return /* @__PURE__ */ jsxs(Box, {
|
|
7059
8273
|
flexDirection: "column",
|
|
7060
|
-
|
|
8274
|
+
borderStyle: "round",
|
|
8275
|
+
paddingX: 1,
|
|
8276
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
7061
8277
|
bold: true,
|
|
7062
|
-
children:
|
|
7063
|
-
"Pages (",
|
|
7064
|
-
pages.length,
|
|
7065
|
-
" of ",
|
|
7066
|
-
doc.layout.pages.length,
|
|
7067
|
-
")"
|
|
7068
|
-
]
|
|
8278
|
+
children: "Add item -- choose a kind"
|
|
7069
8279
|
}), /* @__PURE__ */ jsx(ListView, {
|
|
7070
|
-
items:
|
|
8280
|
+
items: ADD_KIND_OPTIONS,
|
|
7071
8281
|
selectedIndex,
|
|
7072
|
-
|
|
7073
|
-
renderItem: (
|
|
8282
|
+
reservedRows: 6,
|
|
8283
|
+
renderItem: (option, isSelected) => /* @__PURE__ */ jsxs(Text, {
|
|
7074
8284
|
color: isSelected ? "cyan" : void 0,
|
|
7075
|
-
|
|
7076
|
-
children: [
|
|
7077
|
-
"Page ",
|
|
7078
|
-
pageIndex + 1,
|
|
7079
|
-
" -- ",
|
|
7080
|
-
pageSummaryText(page)
|
|
7081
|
-
]
|
|
8285
|
+
children: [isSelected ? "> " : " ", option.label]
|
|
7082
8286
|
})
|
|
7083
8287
|
})]
|
|
7084
8288
|
});
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
}
|
|
7091
|
-
function formatColor(color) {
|
|
7092
|
-
const byte = (component) => Math.round(component * 255).toString(16).padStart(2, "0");
|
|
7093
|
-
return `#${byte(color.r)}${byte(color.g)}${byte(color.b)}`;
|
|
7094
|
-
}
|
|
7095
|
-
function formatStroke(stroke) {
|
|
7096
|
-
return `${formatColor(stroke.color)} @ ${stroke.widthPt.toFixed(1)}pt`;
|
|
7097
|
-
}
|
|
7098
|
-
function fieldsFor(item) {
|
|
7099
|
-
const fields = [["Kind", item.kind]];
|
|
7100
|
-
switch (item.kind) {
|
|
7101
|
-
case "text":
|
|
7102
|
-
fields.push(["Text", item.text]);
|
|
7103
|
-
fields.push(["Position", formatPoint(item.xPt, item.yPt)]);
|
|
7104
|
-
fields.push(["Font family", item.font.family]);
|
|
7105
|
-
fields.push(["Font weight", item.font.weight]);
|
|
7106
|
-
fields.push(["Font style", item.font.style]);
|
|
7107
|
-
fields.push(["Size", `${item.sizePt}pt`]);
|
|
7108
|
-
fields.push(["Colour", formatColor(item.color)]);
|
|
7109
|
-
if (item.widthPt !== void 0) fields.push(["Width", `${item.widthPt}pt`]);
|
|
7110
|
-
if (item.rotationDeg !== void 0) fields.push(["Rotation", `${item.rotationDeg}°`]);
|
|
7111
|
-
if (item.underline !== void 0) fields.push(["Underline", item.underline ? "yes" : "no"]);
|
|
7112
|
-
break;
|
|
7113
|
-
case "image":
|
|
7114
|
-
fields.push(["Image ID", item.imageId]);
|
|
7115
|
-
fields.push(["Position", formatPoint(item.xPt, item.yPt)]);
|
|
7116
|
-
fields.push(["Size", formatSize(item.widthPt, item.heightPt)]);
|
|
7117
|
-
if (item.rotationDeg !== void 0) fields.push(["Rotation", `${item.rotationDeg}°`]);
|
|
7118
|
-
break;
|
|
7119
|
-
case "rect":
|
|
7120
|
-
case "ellipse":
|
|
7121
|
-
fields.push(["Position", formatPoint(item.xPt, item.yPt)]);
|
|
7122
|
-
fields.push(["Size", formatSize(item.widthPt, item.heightPt)]);
|
|
7123
|
-
if (item.fill !== void 0) fields.push(["Fill", formatColor(item.fill)]);
|
|
7124
|
-
if (item.stroke !== void 0) fields.push(["Stroke", formatStroke(item.stroke)]);
|
|
7125
|
-
break;
|
|
7126
|
-
case "line":
|
|
7127
|
-
fields.push(["From", formatPoint(item.x1Pt, item.y1Pt)]);
|
|
7128
|
-
fields.push(["To", formatPoint(item.x2Pt, item.y2Pt)]);
|
|
7129
|
-
fields.push(["Colour", formatColor(item.color)]);
|
|
7130
|
-
fields.push(["Width", `${item.widthPt}pt`]);
|
|
7131
|
-
break;
|
|
7132
|
-
case "path":
|
|
7133
|
-
fields.push(["Subpaths", `${item.subpaths.length}`]);
|
|
7134
|
-
fields.push(["Segments", `${item.subpaths.reduce((total, subpath) => total + subpath.segments.length, 0)}`]);
|
|
7135
|
-
if (item.fill !== void 0) fields.push(["Fill", formatColor(item.fill)]);
|
|
7136
|
-
if (item.fillRule !== void 0) fields.push(["Fill rule", item.fillRule]);
|
|
7137
|
-
if (item.stroke !== void 0) fields.push(["Stroke", formatStroke(item.stroke)]);
|
|
7138
|
-
break;
|
|
7139
|
-
case "link":
|
|
7140
|
-
fields.push(["URI", item.uri]);
|
|
7141
|
-
fields.push(["Position", formatPoint(item.xPt, item.yPt)]);
|
|
7142
|
-
fields.push(["Size", formatSize(item.widthPt, item.heightPt)]);
|
|
7143
|
-
break;
|
|
7144
|
-
default: return item;
|
|
7145
|
-
}
|
|
7146
|
-
if (item.sourcePath !== void 0) fields.push(["Source path", item.sourcePath]);
|
|
7147
|
-
return fields;
|
|
7148
|
-
}
|
|
7149
|
-
function PdfItemDetailScreen() {
|
|
7150
|
-
const state = useAppState();
|
|
7151
|
-
const dispatch = useAppDispatch();
|
|
7152
|
-
const doc = requirePdfDocument(state.openDocument);
|
|
7153
|
-
const screen = currentScreen(state);
|
|
7154
|
-
if (screen.kind !== "pdfItemDetail") throw new Error(`PdfItemDetailScreen rendered while the current screen is "${screen.kind}", not "pdfItemDetail".`);
|
|
7155
|
-
const page = doc.layout.pages[screen.pageIndex];
|
|
7156
|
-
if (page === void 0) throw new Error(`pdfItemDetail was pushed for page ${screen.pageIndex}, but the open PDF has no page at that index.`);
|
|
7157
|
-
const item = page.items[screen.itemIndex];
|
|
7158
|
-
if (item === void 0) throw new Error(`pdfItemDetail was pushed for item ${screen.itemIndex} on page ${screen.pageIndex}, but that page has no item at that index.`);
|
|
7159
|
-
useInput((input, key) => {
|
|
7160
|
-
if (key.escape || key.leftArrow || input === "h") dispatch({ type: "POP_SCREEN" });
|
|
7161
|
-
}, { isActive: !anyOverlayOpen(state) });
|
|
7162
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
7163
|
-
flexDirection: "column",
|
|
7164
|
-
children: [
|
|
7165
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
7166
|
-
bold: true,
|
|
7167
|
-
children: [
|
|
7168
|
-
"Page ",
|
|
7169
|
-
screen.pageIndex + 1,
|
|
7170
|
-
", item ",
|
|
7171
|
-
screen.itemIndex + 1
|
|
7172
|
-
]
|
|
7173
|
-
}),
|
|
7174
|
-
fieldsFor(item).map(([label, value]) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
7175
|
-
label,
|
|
7176
|
-
": ",
|
|
7177
|
-
value
|
|
7178
|
-
] }, label)),
|
|
7179
|
-
/* @__PURE__ */ jsx(Text, {
|
|
7180
|
-
dimColor: true,
|
|
7181
|
-
children: "Esc / ← / h to go back"
|
|
7182
|
-
})
|
|
7183
|
-
]
|
|
7184
|
-
});
|
|
7185
|
-
}
|
|
7186
|
-
//#endregion
|
|
7187
|
-
//#region src/tui/screens/editors/pdf/page-items.tsx
|
|
7188
|
-
const TEXT_PREVIEW_MAX_CHARS = 48;
|
|
7189
|
-
function truncate(text, maxChars) {
|
|
7190
|
-
return text.length > maxChars ? `${text.slice(0, maxChars)}…` : text;
|
|
7191
|
-
}
|
|
7192
|
-
function pathDimensions(item) {
|
|
7193
|
-
let minX = Number.POSITIVE_INFINITY;
|
|
7194
|
-
let minY = Number.POSITIVE_INFINITY;
|
|
7195
|
-
let maxX = Number.NEGATIVE_INFINITY;
|
|
7196
|
-
let maxY = Number.NEGATIVE_INFINITY;
|
|
7197
|
-
const consider = (xPt, yPt) => {
|
|
7198
|
-
minX = Math.min(minX, xPt);
|
|
7199
|
-
minY = Math.min(minY, yPt);
|
|
7200
|
-
maxX = Math.max(maxX, xPt);
|
|
7201
|
-
maxY = Math.max(maxY, yPt);
|
|
7202
|
-
};
|
|
7203
|
-
for (const subpath of item.subpaths) {
|
|
7204
|
-
consider(subpath.startXPt, subpath.startYPt);
|
|
7205
|
-
for (const segment of subpath.segments) {
|
|
7206
|
-
if (segment.kind === "cubic") {
|
|
7207
|
-
consider(segment.c1xPt, segment.c1yPt);
|
|
7208
|
-
consider(segment.c2xPt, segment.c2yPt);
|
|
7209
|
-
}
|
|
7210
|
-
consider(segment.xPt, segment.yPt);
|
|
8289
|
+
return /* @__PURE__ */ jsx(FieldWizard, {
|
|
8290
|
+
fields: fieldsForAddKind(kind),
|
|
8291
|
+
onCancel: props.onCancel,
|
|
8292
|
+
onComplete: (values) => {
|
|
8293
|
+
applyAddKind(kind, props.pageIndex, values, dispatch).then(props.onCreated);
|
|
7211
8294
|
}
|
|
7212
|
-
}
|
|
7213
|
-
if (minX > maxX) return "empty path";
|
|
7214
|
-
return formatSize(maxX - minX, maxY - minY);
|
|
7215
|
-
}
|
|
7216
|
-
function previewFor(item) {
|
|
7217
|
-
switch (item.kind) {
|
|
7218
|
-
case "text": return truncate(item.text, TEXT_PREVIEW_MAX_CHARS);
|
|
7219
|
-
case "link": return item.uri;
|
|
7220
|
-
case "image":
|
|
7221
|
-
case "rect":
|
|
7222
|
-
case "ellipse": return formatSize(item.widthPt, item.heightPt);
|
|
7223
|
-
case "line": return formatSize(Math.abs(item.x2Pt - item.x1Pt), Math.abs(item.y2Pt - item.y1Pt));
|
|
7224
|
-
case "path": return pathDimensions(item);
|
|
7225
|
-
}
|
|
8295
|
+
});
|
|
7226
8296
|
}
|
|
7227
8297
|
function PdfPageItemsScreen() {
|
|
7228
8298
|
const state = useAppState();
|
|
7229
8299
|
const dispatch = useAppDispatch();
|
|
7230
8300
|
const doc = requirePdfDocument(state.openDocument);
|
|
8301
|
+
const editable = isEditablePdfDocument(doc);
|
|
7231
8302
|
const screen = currentScreen(state);
|
|
7232
8303
|
if (screen.kind !== "pdfPageItems") throw new Error(`PdfPageItemsScreen rendered while the current screen is "${screen.kind}", not "pdfPageItems".`);
|
|
7233
8304
|
const page = doc.layout.pages[screen.pageIndex];
|
|
7234
8305
|
if (page === void 0) throw new Error(`pdfPageItems was pushed for page ${screen.pageIndex}, but the open PDF has no page at that index.`);
|
|
8306
|
+
const [isAdding, setIsAdding] = useState(false);
|
|
8307
|
+
const overlayOpen = anyOverlayOpen(state);
|
|
7235
8308
|
const query = state.searchQuery.trim().toLowerCase();
|
|
7236
8309
|
const indexed = page.items.map((item, itemIndex) => ({
|
|
7237
8310
|
item,
|
|
@@ -7240,6 +8313,7 @@ function PdfPageItemsScreen() {
|
|
|
7240
8313
|
const items = query === "" ? indexed : indexed.filter((entry) => `${entry.item.kind} ${previewFor(entry.item)}`.toLowerCase().includes(query));
|
|
7241
8314
|
const { selectedIndex } = useNavigationInput({
|
|
7242
8315
|
itemCount: items.length,
|
|
8316
|
+
isActive: !overlayOpen && !isAdding,
|
|
7243
8317
|
onSelect: (index) => {
|
|
7244
8318
|
const entry = items[index];
|
|
7245
8319
|
if (entry === void 0) return;
|
|
@@ -7255,37 +8329,66 @@ function PdfPageItemsScreen() {
|
|
|
7255
8329
|
onBack: () => {
|
|
7256
8330
|
dispatch({ type: "POP_SCREEN" });
|
|
7257
8331
|
},
|
|
7258
|
-
|
|
8332
|
+
onAppend: editable ? () => {
|
|
8333
|
+
setIsAdding(true);
|
|
8334
|
+
} : void 0
|
|
8335
|
+
});
|
|
8336
|
+
useInput((input) => {
|
|
8337
|
+
if (input !== "d") return;
|
|
8338
|
+
const entry = items[selectedIndex];
|
|
8339
|
+
if (entry === void 0) return;
|
|
8340
|
+
dispatch({
|
|
8341
|
+
type: "REMOVE_PDF_ITEM",
|
|
8342
|
+
pageIndex: screen.pageIndex,
|
|
8343
|
+
itemIndex: entry.itemIndex
|
|
8344
|
+
});
|
|
8345
|
+
}, { isActive: editable && !overlayOpen && !isAdding && items.length > 0 });
|
|
8346
|
+
if (isAdding) return /* @__PURE__ */ jsx(AddItemFlow, {
|
|
8347
|
+
pageIndex: screen.pageIndex,
|
|
8348
|
+
isActive: !overlayOpen,
|
|
8349
|
+
onCancel: () => {
|
|
8350
|
+
setIsAdding(false);
|
|
8351
|
+
},
|
|
8352
|
+
onCreated: () => {
|
|
8353
|
+
setIsAdding(false);
|
|
8354
|
+
}
|
|
7259
8355
|
});
|
|
7260
8356
|
return /* @__PURE__ */ jsxs(Box, {
|
|
7261
8357
|
flexDirection: "column",
|
|
7262
|
-
children: [
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
"Page ",
|
|
7266
|
-
screen.pageIndex + 1,
|
|
7267
|
-
" items (",
|
|
7268
|
-
items.length,
|
|
7269
|
-
" of ",
|
|
7270
|
-
page.items.length,
|
|
7271
|
-
")"
|
|
7272
|
-
]
|
|
7273
|
-
}), /* @__PURE__ */ jsx(ListView, {
|
|
7274
|
-
items,
|
|
7275
|
-
selectedIndex,
|
|
7276
|
-
emptyMessage: query === "" ? "This page has no items." : `No items match "${state.searchQuery}".`,
|
|
7277
|
-
renderItem: ({ item, itemIndex }, isSelected) => /* @__PURE__ */ jsxs(Text, {
|
|
7278
|
-
color: isSelected ? "cyan" : void 0,
|
|
7279
|
-
inverse: isSelected,
|
|
8358
|
+
children: [
|
|
8359
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
8360
|
+
bold: true,
|
|
7280
8361
|
children: [
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
8362
|
+
"Page ",
|
|
8363
|
+
screen.pageIndex + 1,
|
|
8364
|
+
" items (",
|
|
8365
|
+
items.length,
|
|
8366
|
+
" of ",
|
|
8367
|
+
page.items.length,
|
|
8368
|
+
")"
|
|
7286
8369
|
]
|
|
8370
|
+
}),
|
|
8371
|
+
/* @__PURE__ */ jsx(ListView, {
|
|
8372
|
+
items,
|
|
8373
|
+
selectedIndex,
|
|
8374
|
+
emptyMessage: query === "" ? editable ? "This page has no items -- press 'a' to add one" : "This page has no items." : `No items match "${state.searchQuery}".`,
|
|
8375
|
+
renderItem: ({ item, itemIndex }, isSelected) => /* @__PURE__ */ jsxs(Text, {
|
|
8376
|
+
color: isSelected ? "cyan" : void 0,
|
|
8377
|
+
inverse: isSelected,
|
|
8378
|
+
children: [
|
|
8379
|
+
itemIndex + 1,
|
|
8380
|
+
". ",
|
|
8381
|
+
item.kind,
|
|
8382
|
+
" -- ",
|
|
8383
|
+
previewFor(item)
|
|
8384
|
+
]
|
|
8385
|
+
})
|
|
8386
|
+
}),
|
|
8387
|
+
editable && /* @__PURE__ */ jsx(Text, {
|
|
8388
|
+
dimColor: true,
|
|
8389
|
+
children: "a to add an item, d to delete the selected item"
|
|
7287
8390
|
})
|
|
7288
|
-
|
|
8391
|
+
]
|
|
7289
8392
|
});
|
|
7290
8393
|
}
|
|
7291
8394
|
//#endregion
|
|
@@ -7444,7 +8547,12 @@ function FilePickerScreen() {
|
|
|
7444
8547
|
function openAtPath(path) {
|
|
7445
8548
|
(async () => {
|
|
7446
8549
|
try {
|
|
7447
|
-
const doc = await openDocumentAtPath(path)
|
|
8550
|
+
const doc = await openDocumentAtPath(path, { onDiagnostic: (diagnostic) => {
|
|
8551
|
+
dispatch({
|
|
8552
|
+
type: "APPEND_DIAGNOSTIC",
|
|
8553
|
+
diagnostic
|
|
8554
|
+
});
|
|
8555
|
+
} });
|
|
7448
8556
|
dispatch({
|
|
7449
8557
|
type: "OPEN_FILE_SUCCESS",
|
|
7450
8558
|
path,
|
|
@@ -7795,7 +8903,7 @@ function metadataFor(doc) {
|
|
|
7795
8903
|
case "odp": return readOdpContent(doc.editor.toPackage()).metadata;
|
|
7796
8904
|
case "ods": return readOdsContent(doc.editor.toPackage()).metadata;
|
|
7797
8905
|
case "odg": return readOdgContent(doc.editor.toPackage()).metadata;
|
|
7798
|
-
case "markdown": return readMarkdownContent(doc.
|
|
8906
|
+
case "markdown": return readMarkdownContent(doc.editor.toMarkdownText()).metadata;
|
|
7799
8907
|
case "pdf":
|
|
7800
8908
|
case "xlsx": return doc.layout.metadata;
|
|
7801
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.");
|
|
@@ -7860,7 +8968,9 @@ function ScreenBody({ screen }) {
|
|
|
7860
8968
|
case "launcher": return /* @__PURE__ */ jsx(LauncherScreen, {});
|
|
7861
8969
|
case "filePicker": return /* @__PURE__ */ jsx(FilePickerScreen, {});
|
|
7862
8970
|
case "newDocumentPicker": return /* @__PURE__ */ jsx(NewDocumentPickerScreen, {});
|
|
7863
|
-
case "bodyList":
|
|
8971
|
+
case "bodyList":
|
|
8972
|
+
if (format === "odt") return /* @__PURE__ */ jsx(OdtBodyListScreen, {});
|
|
8973
|
+
return format === "markdown" ? /* @__PURE__ */ jsx(MarkdownBodyListScreen, {}) : /* @__PURE__ */ jsx(DocxBodyListScreen, {});
|
|
7864
8974
|
case "docxExtras": return /* @__PURE__ */ jsx(DocxExtrasScreen, {});
|
|
7865
8975
|
case "paragraphDetail": return /* @__PURE__ */ jsx(ParagraphDetailScreen, {});
|
|
7866
8976
|
case "runEditor": return /* @__PURE__ */ jsx(RunEditorScreen, {});
|
|
@@ -7886,8 +8996,7 @@ function ScreenBody({ screen }) {
|
|
|
7886
8996
|
case "odbReportList": return /* @__PURE__ */ jsx(OdbReportListScreen, {});
|
|
7887
8997
|
case "odbReportDetail": return /* @__PURE__ */ jsx(OdbReportDetailScreen, {});
|
|
7888
8998
|
case "odbReportRender": return /* @__PURE__ */ jsx(OdbReportRenderScreen, {});
|
|
7889
|
-
case "
|
|
7890
|
-
case "markdownLineEditor": return /* @__PURE__ */ jsx(MarkdownLineEditorScreen, {});
|
|
8999
|
+
case "viewSource": return /* @__PURE__ */ jsx(MarkdownViewSourceScreen, {});
|
|
7891
9000
|
case "pdfPageList": return /* @__PURE__ */ jsx(PdfPageListScreen, {});
|
|
7892
9001
|
case "pdfPageItems": return /* @__PURE__ */ jsx(PdfPageItemsScreen, {});
|
|
7893
9002
|
case "pdfItemDetail": return /* @__PURE__ */ jsx(PdfItemDetailScreen, {});
|
|
@@ -7933,7 +9042,12 @@ function AppShell({ startPath }) {
|
|
|
7933
9042
|
let cancelled = false;
|
|
7934
9043
|
(async () => {
|
|
7935
9044
|
try {
|
|
7936
|
-
const doc = await openDocumentAtPath(startPath)
|
|
9045
|
+
const doc = await openDocumentAtPath(startPath, { onDiagnostic: (diagnostic) => {
|
|
9046
|
+
dispatch({
|
|
9047
|
+
type: "APPEND_DIAGNOSTIC",
|
|
9048
|
+
diagnostic
|
|
9049
|
+
});
|
|
9050
|
+
} });
|
|
7937
9051
|
if (!cancelled) dispatch({
|
|
7938
9052
|
type: "OPEN_FILE_SUCCESS",
|
|
7939
9053
|
path: startPath,
|