document-cli 1.8.0 → 1.10.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/dist/cli.js +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{tui-BCl5_L4H.js → tui-BVeYd0ry.js} +1844 -285
- 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, 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, 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,7 +36,7 @@ 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
42
|
const pdfBytes = markdownToPdf(encodeMarkdownText(openDocument.source), pdfOptions);
|
|
@@ -120,11 +120,15 @@ async function openDocumentAtPath(path) {
|
|
|
120
120
|
editor: openOdg(bytes),
|
|
121
121
|
path
|
|
122
122
|
};
|
|
123
|
-
case "pdf":
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
case "pdf": {
|
|
124
|
+
const editor = openPdf(bytes);
|
|
125
|
+
return {
|
|
126
|
+
format,
|
|
127
|
+
editor,
|
|
128
|
+
layout: editor.toLayoutDocument(),
|
|
129
|
+
path
|
|
130
|
+
};
|
|
131
|
+
}
|
|
128
132
|
case "markdown": return {
|
|
129
133
|
format,
|
|
130
134
|
source: decodeMarkdownText(bytes),
|
|
@@ -171,10 +175,19 @@ function createNewDocument(format) {
|
|
|
171
175
|
editor: createOdg(),
|
|
172
176
|
path: void 0
|
|
173
177
|
};
|
|
178
|
+
case "pdf": {
|
|
179
|
+
const editor = createPdf();
|
|
180
|
+
return {
|
|
181
|
+
format,
|
|
182
|
+
editor,
|
|
183
|
+
layout: editor.toLayoutDocument(),
|
|
184
|
+
path: void 0
|
|
185
|
+
};
|
|
186
|
+
}
|
|
174
187
|
}
|
|
175
188
|
}
|
|
176
189
|
async function saveDocumentTo(openDocument, path) {
|
|
177
|
-
if (openDocument.format === "odb" || openDocument.format === "
|
|
190
|
+
if (openDocument.format === "odb" || openDocument.format === "xlsx") throw new Error(`A ${openDocument.format} document is opened read-only and cannot be written back`);
|
|
178
191
|
if (openDocument.format === "markdown") {
|
|
179
192
|
await writeFile(path, encodeMarkdownText(openDocument.source));
|
|
180
193
|
return;
|
|
@@ -189,7 +202,8 @@ const EDITABLE_FORMATS = {
|
|
|
189
202
|
odt: true,
|
|
190
203
|
odp: true,
|
|
191
204
|
ods: true,
|
|
192
|
-
odg: true
|
|
205
|
+
odg: true,
|
|
206
|
+
pdf: true
|
|
193
207
|
};
|
|
194
208
|
const WRITABLE_FORMATS = {
|
|
195
209
|
...EDITABLE_FORMATS,
|
|
@@ -368,6 +382,12 @@ function documentWithPath(doc, path) {
|
|
|
368
382
|
editor: doc.editor,
|
|
369
383
|
path
|
|
370
384
|
};
|
|
385
|
+
case "pdf": return {
|
|
386
|
+
format: "pdf",
|
|
387
|
+
editor: doc.editor,
|
|
388
|
+
layout: doc.layout,
|
|
389
|
+
path
|
|
390
|
+
};
|
|
371
391
|
case "odb": return {
|
|
372
392
|
format: "odb",
|
|
373
393
|
tables: doc.tables,
|
|
@@ -380,11 +400,6 @@ function documentWithPath(doc, path) {
|
|
|
380
400
|
source: doc.source,
|
|
381
401
|
path
|
|
382
402
|
};
|
|
383
|
-
case "pdf": return {
|
|
384
|
-
format: "pdf",
|
|
385
|
-
layout: doc.layout,
|
|
386
|
-
path
|
|
387
|
-
};
|
|
388
403
|
case "xlsx": return {
|
|
389
404
|
format: "xlsx",
|
|
390
405
|
layout: doc.layout,
|
|
@@ -425,6 +440,15 @@ function reopenEditable(doc, bytes) {
|
|
|
425
440
|
editor: openOdg(bytes),
|
|
426
441
|
path: doc.path
|
|
427
442
|
};
|
|
443
|
+
case "pdf": {
|
|
444
|
+
const editor = openPdf(bytes);
|
|
445
|
+
return {
|
|
446
|
+
format: "pdf",
|
|
447
|
+
editor,
|
|
448
|
+
layout: editor.toLayoutDocument(),
|
|
449
|
+
path: doc.path
|
|
450
|
+
};
|
|
451
|
+
}
|
|
428
452
|
}
|
|
429
453
|
}
|
|
430
454
|
function mutate(state, doc, apply) {
|
|
@@ -508,6 +532,45 @@ function drawingDocument(state) {
|
|
|
508
532
|
if (doc === void 0) return;
|
|
509
533
|
return doc.format === "odg" ? doc : void 0;
|
|
510
534
|
}
|
|
535
|
+
function pdfDocument(state) {
|
|
536
|
+
const doc = state.openDocument;
|
|
537
|
+
if (doc === void 0) return;
|
|
538
|
+
return doc.format === "pdf" ? doc : void 0;
|
|
539
|
+
}
|
|
540
|
+
function pdfItemAt(doc, pageIndex, itemIndex) {
|
|
541
|
+
return doc.editor.page(pageIndex)?.items()[itemIndex];
|
|
542
|
+
}
|
|
543
|
+
function withPdfPage(state, pageIndex, apply) {
|
|
544
|
+
const doc = pdfDocument(state);
|
|
545
|
+
if (doc === void 0) return wrongDocument(state, "a pdf document");
|
|
546
|
+
const page = doc.editor.page(pageIndex);
|
|
547
|
+
if (page === void 0) return withStatus(state, "warning", `There is no page at index ${pageIndex}`);
|
|
548
|
+
return mutate(state, doc, () => {
|
|
549
|
+
apply(page);
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
function withPdfItemMatching(state, pageIndex, itemIndex, guard, kindLabel, apply) {
|
|
553
|
+
const doc = pdfDocument(state);
|
|
554
|
+
if (doc === void 0) return wrongDocument(state, "a pdf document");
|
|
555
|
+
const item = pdfItemAt(doc, pageIndex, itemIndex);
|
|
556
|
+
if (item === void 0) return withStatus(state, "warning", `Page ${pageIndex} has no item at index ${itemIndex}`);
|
|
557
|
+
if (!guard(item)) return withStatus(state, "warning", `Item ${itemIndex} on page ${pageIndex} is a ${item.kind} item, not ${kindLabel}`);
|
|
558
|
+
return mutate(state, doc, () => {
|
|
559
|
+
apply(item);
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
const isPdfTextItem = (item) => item.kind === "text";
|
|
563
|
+
const isPdfRectItem = (item) => item.kind === "rect";
|
|
564
|
+
const isPdfEllipseItem = (item) => item.kind === "ellipse";
|
|
565
|
+
const isPdfLineItem = (item) => item.kind === "line";
|
|
566
|
+
const isPdfPathItem = (item) => item.kind === "path";
|
|
567
|
+
const isPdfImageItem = (item) => item.kind === "image";
|
|
568
|
+
const isPdfLinkItem = (item) => item.kind === "link";
|
|
569
|
+
function vectorHostDocument(state) {
|
|
570
|
+
const doc = state.openDocument;
|
|
571
|
+
if (doc === void 0) return;
|
|
572
|
+
return doc.format === "odg" || doc.format === "odp" ? doc : void 0;
|
|
573
|
+
}
|
|
511
574
|
function markdownDocument(state) {
|
|
512
575
|
const doc = state.openDocument;
|
|
513
576
|
if (doc === void 0) return;
|
|
@@ -740,6 +803,12 @@ function appReducer(state, action) {
|
|
|
740
803
|
case "SET_RUN_COLOR": return withRun(state, action.blockIndex, action.runIndex, (run) => {
|
|
741
804
|
run.color = action.color;
|
|
742
805
|
});
|
|
806
|
+
case "SET_RUN_FONT_FAMILY": return withRun(state, action.blockIndex, action.runIndex, (run) => {
|
|
807
|
+
run.fontFamily = action.fontFamily;
|
|
808
|
+
});
|
|
809
|
+
case "SET_RUN_FONT_SIZE": return withRun(state, action.blockIndex, action.runIndex, (run) => {
|
|
810
|
+
run.sizePt = action.sizePt;
|
|
811
|
+
});
|
|
743
812
|
case "APPEND_TABLE": {
|
|
744
813
|
const doc = wordprocessingDocument(state);
|
|
745
814
|
if (doc === void 0) return wrongDocument(state, "a docx or odt document");
|
|
@@ -800,6 +869,14 @@ function appReducer(state, action) {
|
|
|
800
869
|
setTextContainerText(item, action.text);
|
|
801
870
|
});
|
|
802
871
|
}
|
|
872
|
+
case "ADD_LIST": {
|
|
873
|
+
const doc = wordprocessingDocument(state);
|
|
874
|
+
if (doc === void 0) return wrongDocument(state, "a docx or odt document");
|
|
875
|
+
if (doc.format !== "odt") return wrongDocument(state, "an odt document (lists are an odt-only concept)");
|
|
876
|
+
return mutate(state, doc, () => {
|
|
877
|
+
doc.editor.body.appendList();
|
|
878
|
+
});
|
|
879
|
+
}
|
|
803
880
|
case "INSERT_PARAGRAPH_IMAGE": {
|
|
804
881
|
const doc = wordprocessingDocument(state);
|
|
805
882
|
if (doc === void 0) return wrongDocument(state, "a docx or odt document");
|
|
@@ -943,6 +1020,23 @@ function appReducer(state, action) {
|
|
|
943
1020
|
case "SET_CELL_VALUE": return withSheet(state, action.sheetIndex, (sheet) => {
|
|
944
1021
|
sheet.cell(action.row, action.column).value = action.value;
|
|
945
1022
|
});
|
|
1023
|
+
case "SET_CELL_FORMULA": return withSheet(state, action.sheetIndex, (sheet) => {
|
|
1024
|
+
sheet.cell(action.row, action.column).formula = action.formula;
|
|
1025
|
+
});
|
|
1026
|
+
case "ADD_SHEET_IMAGE": return withSheet(state, action.sheetIndex, (sheet) => {
|
|
1027
|
+
sheet.addImage({
|
|
1028
|
+
kind: "image",
|
|
1029
|
+
format: action.format,
|
|
1030
|
+
base64: bytesToBase64(action.bytes),
|
|
1031
|
+
widthPt: action.widthPt,
|
|
1032
|
+
heightPt: action.heightPt,
|
|
1033
|
+
altText: action.altText,
|
|
1034
|
+
anchorRow: action.anchorRow,
|
|
1035
|
+
anchorColumn: action.anchorColumn,
|
|
1036
|
+
offsetXPt: action.offsetXPt,
|
|
1037
|
+
offsetYPt: action.offsetYPt
|
|
1038
|
+
});
|
|
1039
|
+
});
|
|
946
1040
|
case "MERGE_CELLS": {
|
|
947
1041
|
const doc = spreadsheetDocument(state);
|
|
948
1042
|
if (doc === void 0) return wrongDocument(state, "an ods document");
|
|
@@ -966,23 +1060,64 @@ function appReducer(state, action) {
|
|
|
966
1060
|
case "ADD_ELLIPSE":
|
|
967
1061
|
case "ADD_LINE":
|
|
968
1062
|
case "ADD_PATH": {
|
|
969
|
-
const doc =
|
|
970
|
-
if (doc === void 0) return wrongDocument(state, "an odg document");
|
|
971
|
-
|
|
972
|
-
|
|
1063
|
+
const doc = vectorHostDocument(state);
|
|
1064
|
+
if (doc === void 0) return wrongDocument(state, "an odg or odp document");
|
|
1065
|
+
if (doc.format === "odg") {
|
|
1066
|
+
const page = doc.editor.pages()[action.containerIndex];
|
|
1067
|
+
if (page === void 0) return withStatus(state, "warning", `There is no page at index ${action.containerIndex}`);
|
|
1068
|
+
return mutate(state, doc, () => {
|
|
1069
|
+
switch (action.type) {
|
|
1070
|
+
case "ADD_RECT":
|
|
1071
|
+
page.addRect(action.init);
|
|
1072
|
+
return;
|
|
1073
|
+
case "ADD_ELLIPSE":
|
|
1074
|
+
page.addEllipse(action.init);
|
|
1075
|
+
return;
|
|
1076
|
+
case "ADD_LINE":
|
|
1077
|
+
page.addLine(action.init);
|
|
1078
|
+
return;
|
|
1079
|
+
case "ADD_PATH":
|
|
1080
|
+
page.addPath(action.init);
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
const slide = doc.editor.slides()[action.containerIndex];
|
|
1086
|
+
if (slide === void 0) return withStatus(state, "warning", `There is no slide at index ${action.containerIndex}`);
|
|
973
1087
|
return mutate(state, doc, () => {
|
|
974
1088
|
switch (action.type) {
|
|
975
1089
|
case "ADD_RECT":
|
|
976
|
-
|
|
1090
|
+
slide.addVector({
|
|
1091
|
+
kind: "rect",
|
|
1092
|
+
frame: action.init.frame,
|
|
1093
|
+
fill: action.init.fill,
|
|
1094
|
+
stroke: action.init.stroke
|
|
1095
|
+
});
|
|
977
1096
|
return;
|
|
978
1097
|
case "ADD_ELLIPSE":
|
|
979
|
-
|
|
1098
|
+
slide.addVector({
|
|
1099
|
+
kind: "ellipse",
|
|
1100
|
+
frame: action.init.frame,
|
|
1101
|
+
fill: action.init.fill,
|
|
1102
|
+
stroke: action.init.stroke
|
|
1103
|
+
});
|
|
980
1104
|
return;
|
|
981
1105
|
case "ADD_LINE":
|
|
982
|
-
|
|
1106
|
+
slide.addVector({
|
|
1107
|
+
kind: "line",
|
|
1108
|
+
from: action.init.from,
|
|
1109
|
+
to: action.init.to,
|
|
1110
|
+
stroke: action.init.stroke
|
|
1111
|
+
});
|
|
983
1112
|
return;
|
|
984
1113
|
case "ADD_PATH":
|
|
985
|
-
|
|
1114
|
+
slide.addVector({
|
|
1115
|
+
kind: "path",
|
|
1116
|
+
frame: action.init.frame,
|
|
1117
|
+
subpaths: [...action.init.subpaths],
|
|
1118
|
+
fill: action.init.fill,
|
|
1119
|
+
stroke: action.init.stroke
|
|
1120
|
+
});
|
|
986
1121
|
return;
|
|
987
1122
|
}
|
|
988
1123
|
});
|
|
@@ -1001,6 +1136,129 @@ function appReducer(state, action) {
|
|
|
1001
1136
|
action.vector.stroke = action.stroke;
|
|
1002
1137
|
});
|
|
1003
1138
|
}
|
|
1139
|
+
case "ADD_PDF_TEXT": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1140
|
+
page.appendText(action.init);
|
|
1141
|
+
});
|
|
1142
|
+
case "ADD_PDF_RECT": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1143
|
+
page.appendRect(action.init);
|
|
1144
|
+
});
|
|
1145
|
+
case "ADD_PDF_ELLIPSE": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1146
|
+
page.appendEllipse(action.init);
|
|
1147
|
+
});
|
|
1148
|
+
case "ADD_PDF_LINE": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1149
|
+
page.appendLine(action.init);
|
|
1150
|
+
});
|
|
1151
|
+
case "ADD_PDF_PATH": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1152
|
+
page.appendPath(action.init);
|
|
1153
|
+
});
|
|
1154
|
+
case "ADD_PDF_IMAGE": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1155
|
+
page.appendImage(action.init);
|
|
1156
|
+
});
|
|
1157
|
+
case "ADD_PDF_LINK": return withPdfPage(state, action.pageIndex, (page) => {
|
|
1158
|
+
page.appendLink(action.init);
|
|
1159
|
+
});
|
|
1160
|
+
case "REMOVE_PDF_ITEM": {
|
|
1161
|
+
const doc = pdfDocument(state);
|
|
1162
|
+
if (doc === void 0) return wrongDocument(state, "a pdf document");
|
|
1163
|
+
const item = pdfItemAt(doc, action.pageIndex, action.itemIndex);
|
|
1164
|
+
if (item === void 0) return withStatus(state, "warning", `Page ${action.pageIndex} has no item at index ${action.itemIndex}`);
|
|
1165
|
+
return mutate(state, doc, () => {
|
|
1166
|
+
item.remove();
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
case "SET_PDF_TEXT_TEXT": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1170
|
+
item.text = action.text;
|
|
1171
|
+
});
|
|
1172
|
+
case "SET_PDF_TEXT_POSITION": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1173
|
+
item.xPt = action.xPt;
|
|
1174
|
+
item.yPt = action.yPt;
|
|
1175
|
+
});
|
|
1176
|
+
case "SET_PDF_TEXT_FONT": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1177
|
+
item.font = action.font;
|
|
1178
|
+
});
|
|
1179
|
+
case "SET_PDF_TEXT_SIZE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1180
|
+
item.sizePt = action.sizePt;
|
|
1181
|
+
});
|
|
1182
|
+
case "SET_PDF_TEXT_COLOR": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1183
|
+
item.color = action.color;
|
|
1184
|
+
});
|
|
1185
|
+
case "SET_PDF_TEXT_ROTATION": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1186
|
+
item.rotationDeg = action.rotationDeg;
|
|
1187
|
+
});
|
|
1188
|
+
case "SET_PDF_TEXT_WIDTH": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1189
|
+
item.widthPt = action.widthPt;
|
|
1190
|
+
});
|
|
1191
|
+
case "TOGGLE_PDF_TEXT_UNDERLINE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfTextItem, "text", (item) => {
|
|
1192
|
+
item.underline = !(item.underline ?? false);
|
|
1193
|
+
});
|
|
1194
|
+
case "SET_PDF_RECT_FRAME": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfRectItem, "rect", (item) => {
|
|
1195
|
+
item.xPt = action.xPt;
|
|
1196
|
+
item.yPt = action.yPt;
|
|
1197
|
+
item.widthPt = action.widthPt;
|
|
1198
|
+
item.heightPt = action.heightPt;
|
|
1199
|
+
});
|
|
1200
|
+
case "SET_PDF_RECT_FILL": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfRectItem, "rect", (item) => {
|
|
1201
|
+
item.fill = action.fill;
|
|
1202
|
+
});
|
|
1203
|
+
case "SET_PDF_RECT_STROKE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfRectItem, "rect", (item) => {
|
|
1204
|
+
item.stroke = action.stroke;
|
|
1205
|
+
});
|
|
1206
|
+
case "SET_PDF_ELLIPSE_FRAME": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfEllipseItem, "ellipse", (item) => {
|
|
1207
|
+
item.xPt = action.xPt;
|
|
1208
|
+
item.yPt = action.yPt;
|
|
1209
|
+
item.widthPt = action.widthPt;
|
|
1210
|
+
item.heightPt = action.heightPt;
|
|
1211
|
+
});
|
|
1212
|
+
case "SET_PDF_ELLIPSE_FILL": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfEllipseItem, "ellipse", (item) => {
|
|
1213
|
+
item.fill = action.fill;
|
|
1214
|
+
});
|
|
1215
|
+
case "SET_PDF_ELLIPSE_STROKE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfEllipseItem, "ellipse", (item) => {
|
|
1216
|
+
item.stroke = action.stroke;
|
|
1217
|
+
});
|
|
1218
|
+
case "SET_PDF_LINE_FROM": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLineItem, "line", (item) => {
|
|
1219
|
+
item.x1Pt = action.x1Pt;
|
|
1220
|
+
item.y1Pt = action.y1Pt;
|
|
1221
|
+
});
|
|
1222
|
+
case "SET_PDF_LINE_TO": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLineItem, "line", (item) => {
|
|
1223
|
+
item.x2Pt = action.x2Pt;
|
|
1224
|
+
item.y2Pt = action.y2Pt;
|
|
1225
|
+
});
|
|
1226
|
+
case "SET_PDF_LINE_COLOR": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLineItem, "line", (item) => {
|
|
1227
|
+
item.color = action.color;
|
|
1228
|
+
});
|
|
1229
|
+
case "SET_PDF_LINE_WIDTH": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLineItem, "line", (item) => {
|
|
1230
|
+
item.widthPt = action.widthPt;
|
|
1231
|
+
});
|
|
1232
|
+
case "SET_PDF_PATH_FILL": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfPathItem, "path", (item) => {
|
|
1233
|
+
item.fill = action.fill;
|
|
1234
|
+
});
|
|
1235
|
+
case "SET_PDF_PATH_FILL_RULE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfPathItem, "path", (item) => {
|
|
1236
|
+
item.fillRule = action.fillRule;
|
|
1237
|
+
});
|
|
1238
|
+
case "SET_PDF_PATH_STROKE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfPathItem, "path", (item) => {
|
|
1239
|
+
item.stroke = action.stroke;
|
|
1240
|
+
});
|
|
1241
|
+
case "SET_PDF_IMAGE_FRAME": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfImageItem, "image", (item) => {
|
|
1242
|
+
item.xPt = action.xPt;
|
|
1243
|
+
item.yPt = action.yPt;
|
|
1244
|
+
item.widthPt = action.widthPt;
|
|
1245
|
+
item.heightPt = action.heightPt;
|
|
1246
|
+
});
|
|
1247
|
+
case "SET_PDF_IMAGE_ROTATION": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfImageItem, "image", (item) => {
|
|
1248
|
+
item.rotationDeg = action.rotationDeg;
|
|
1249
|
+
});
|
|
1250
|
+
case "SET_PDF_IMAGE_SOURCE": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfImageItem, "image", (item) => {
|
|
1251
|
+
item.setImage(action.bytes, action.format);
|
|
1252
|
+
});
|
|
1253
|
+
case "SET_PDF_LINK_URI": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLinkItem, "link", (item) => {
|
|
1254
|
+
item.uri = action.uri;
|
|
1255
|
+
});
|
|
1256
|
+
case "SET_PDF_LINK_FRAME": return withPdfItemMatching(state, action.pageIndex, action.itemIndex, isPdfLinkItem, "link", (item) => {
|
|
1257
|
+
item.xPt = action.xPt;
|
|
1258
|
+
item.yPt = action.yPt;
|
|
1259
|
+
item.widthPt = action.widthPt;
|
|
1260
|
+
item.heightPt = action.heightPt;
|
|
1261
|
+
});
|
|
1004
1262
|
case "SET_MARKDOWN_SOURCE": {
|
|
1005
1263
|
const doc = markdownDocument(state);
|
|
1006
1264
|
if (doc === void 0) return wrongDocument(state, "a markdown document");
|
|
@@ -1034,7 +1292,7 @@ function appReducer(state, action) {
|
|
|
1034
1292
|
case "UNDO": {
|
|
1035
1293
|
const doc = state.openDocument;
|
|
1036
1294
|
if (doc === void 0) return withStatus(state, "info", "There is nothing to undo");
|
|
1037
|
-
if (doc.format === "odb" || doc.format === "
|
|
1295
|
+
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`);
|
|
1038
1296
|
const snapshot = state.undoStack.at(-1);
|
|
1039
1297
|
if (snapshot === void 0) return withStatus(state, "info", "There is nothing to undo");
|
|
1040
1298
|
const restored = doc.format === "markdown" ? {
|
|
@@ -1118,7 +1376,7 @@ function TextField(props) {
|
|
|
1118
1376
|
}
|
|
1119
1377
|
//#endregion
|
|
1120
1378
|
//#region src/tui/components/command-palette.tsx
|
|
1121
|
-
const USAGE_COLUMN_WIDTH =
|
|
1379
|
+
const USAGE_COLUMN_WIDTH = 36;
|
|
1122
1380
|
const COMMANDS = [
|
|
1123
1381
|
{
|
|
1124
1382
|
name: "save",
|
|
@@ -1137,7 +1395,7 @@ const COMMANDS = [
|
|
|
1137
1395
|
},
|
|
1138
1396
|
{
|
|
1139
1397
|
name: "new",
|
|
1140
|
-
usage: ":new docx|pptx|odt|odp|ods|odg",
|
|
1398
|
+
usage: ":new docx|pptx|odt|odp|ods|odg|pdf",
|
|
1141
1399
|
description: "Create an empty document"
|
|
1142
1400
|
},
|
|
1143
1401
|
{
|
|
@@ -1254,7 +1512,7 @@ async function runCommand(line, state, dispatch) {
|
|
|
1254
1512
|
case "new": {
|
|
1255
1513
|
const format = args[0];
|
|
1256
1514
|
if (format === void 0 || !isEditableFormat(format)) {
|
|
1257
|
-
warn(dispatch, "Usage: :new docx|pptx|odt|odp|ods|odg");
|
|
1515
|
+
warn(dispatch, "Usage: :new docx|pptx|odt|odp|ods|odg|pdf");
|
|
1258
1516
|
return;
|
|
1259
1517
|
}
|
|
1260
1518
|
dispatch({
|
|
@@ -2132,6 +2390,19 @@ function ParagraphFamilyBodyList(props) {
|
|
|
2132
2390
|
setTableWizard("rows");
|
|
2133
2391
|
}
|
|
2134
2392
|
}, { isActive: !anyOverlayOpen(state) && !wizardOpen && !formulaFlowOpen });
|
|
2393
|
+
useInput((input) => {
|
|
2394
|
+
if (input === "L" && adapter.lists !== void 0) {
|
|
2395
|
+
const newIndex = adapter.lists().length;
|
|
2396
|
+
dispatch({ type: "ADD_LIST" });
|
|
2397
|
+
dispatch({
|
|
2398
|
+
type: "PUSH_SCREEN",
|
|
2399
|
+
screen: {
|
|
2400
|
+
kind: "listEditor",
|
|
2401
|
+
blockIndex: newIndex
|
|
2402
|
+
}
|
|
2403
|
+
});
|
|
2404
|
+
}
|
|
2405
|
+
}, { isActive: !anyOverlayOpen(state) && !wizardOpen && !formulaFlowOpen });
|
|
2135
2406
|
useInput((input) => {
|
|
2136
2407
|
if (input === "m" && adapter.formatLabel === "odt") setFormulaFlow("picking");
|
|
2137
2408
|
}, { isActive: !anyOverlayOpen(state) && !wizardOpen && !formulaFlowOpen });
|
|
@@ -2386,6 +2657,7 @@ function ParagraphFamilyBodyList(props) {
|
|
|
2386
2657
|
dimColor: true,
|
|
2387
2658
|
children: [
|
|
2388
2659
|
"Enter to open, a to append a paragraph, T to append a table",
|
|
2660
|
+
adapter.lists !== void 0 ? ", L to add a list" : "",
|
|
2389
2661
|
adapter.formatLabel === "odt" ? ", m to insert a formula" : "",
|
|
2390
2662
|
", Esc back"
|
|
2391
2663
|
]
|
|
@@ -2408,6 +2680,7 @@ function isValidHexColorInput(input) {
|
|
|
2408
2680
|
}
|
|
2409
2681
|
//#endregion
|
|
2410
2682
|
//#region src/tui/screens/editors/docx/paragraph-detail.tsx
|
|
2683
|
+
const DEFAULT_RUN_SIZE_PT = 12;
|
|
2411
2684
|
const IMAGE_FIELDS = [
|
|
2412
2685
|
{
|
|
2413
2686
|
key: "path",
|
|
@@ -2430,14 +2703,14 @@ const IMAGE_FIELDS = [
|
|
|
2430
2703
|
defaultValue: ""
|
|
2431
2704
|
}
|
|
2432
2705
|
];
|
|
2433
|
-
function inferImageFormat$
|
|
2706
|
+
function inferImageFormat$2(path) {
|
|
2434
2707
|
const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
|
|
2435
2708
|
if (extension === "png") return "png";
|
|
2436
2709
|
if (extension === "jpg" || extension === "jpeg") return "jpeg";
|
|
2437
2710
|
}
|
|
2438
2711
|
async function applyInsertImage(blockIndex, values, dispatch) {
|
|
2439
2712
|
const path = requireFieldValue(values, "path");
|
|
2440
|
-
const format = inferImageFormat$
|
|
2713
|
+
const format = inferImageFormat$2(path);
|
|
2441
2714
|
if (format === void 0) {
|
|
2442
2715
|
dispatch({
|
|
2443
2716
|
type: "SET_STATUS",
|
|
@@ -2487,6 +2760,8 @@ function ParagraphDetailScreen() {
|
|
|
2487
2760
|
const dispatch = useAppDispatch();
|
|
2488
2761
|
const [runIndex, setRunIndex] = useState(0);
|
|
2489
2762
|
const [colorInput, setColorInput] = useState(void 0);
|
|
2763
|
+
const [fontFamilyInput, setFontFamilyInput] = useState(void 0);
|
|
2764
|
+
const [fontSizeInput, setFontSizeInput] = useState(void 0);
|
|
2490
2765
|
const [imageWizardOpen, setImageWizardOpen] = useState(false);
|
|
2491
2766
|
const [formulaPickerOpen, setFormulaPickerOpen] = useState(false);
|
|
2492
2767
|
const screen = currentScreen(state);
|
|
@@ -2584,8 +2859,16 @@ function ParagraphDetailScreen() {
|
|
|
2584
2859
|
});
|
|
2585
2860
|
return;
|
|
2586
2861
|
}
|
|
2587
|
-
if (input === "c")
|
|
2588
|
-
|
|
2862
|
+
if (input === "c") {
|
|
2863
|
+
setColorInput(selectedRun.color === void 0 ? "" : layoutColorToHex(selectedRun.color).slice(1));
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2866
|
+
if (input === "f") {
|
|
2867
|
+
setFontFamilyInput(selectedRun.fontFamily ?? "");
|
|
2868
|
+
return;
|
|
2869
|
+
}
|
|
2870
|
+
if (input === "s") setFontSizeInput(selectedRun.sizePt === void 0 ? "" : String(selectedRun.sizePt));
|
|
2871
|
+
}, { isActive: !anyOverlayOpen(state) && colorInput === void 0 && fontFamilyInput === void 0 && fontSizeInput === void 0 && !imageWizardOpen && !formulaPickerOpen });
|
|
2589
2872
|
if (screen.kind !== "paragraphDetail") return /* @__PURE__ */ jsx(Text, {
|
|
2590
2873
|
color: "red",
|
|
2591
2874
|
children: "ParagraphDetailScreen rendered outside a paragraphDetail screen."
|
|
@@ -2643,6 +2926,60 @@ function ParagraphDetailScreen() {
|
|
|
2643
2926
|
setColorInput(void 0);
|
|
2644
2927
|
}
|
|
2645
2928
|
})] }),
|
|
2929
|
+
fontFamilyInput === void 0 ? void 0 : /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
2930
|
+
color: "cyan",
|
|
2931
|
+
children: "Font family: "
|
|
2932
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
2933
|
+
value: fontFamilyInput,
|
|
2934
|
+
isFocused: true,
|
|
2935
|
+
placeholder: "e.g. Calibri",
|
|
2936
|
+
onChange: setFontFamilyInput,
|
|
2937
|
+
onSubmit: (value) => {
|
|
2938
|
+
const trimmed = value.trim();
|
|
2939
|
+
if (trimmed.length === 0) dispatch({
|
|
2940
|
+
type: "SET_STATUS",
|
|
2941
|
+
severity: "warning",
|
|
2942
|
+
text: "A font family name cannot be blank"
|
|
2943
|
+
});
|
|
2944
|
+
else dispatch({
|
|
2945
|
+
type: "SET_RUN_FONT_FAMILY",
|
|
2946
|
+
blockIndex,
|
|
2947
|
+
runIndex: clampedRunIndex,
|
|
2948
|
+
fontFamily: trimmed
|
|
2949
|
+
});
|
|
2950
|
+
setFontFamilyInput(void 0);
|
|
2951
|
+
},
|
|
2952
|
+
onCancel: () => {
|
|
2953
|
+
setFontFamilyInput(void 0);
|
|
2954
|
+
}
|
|
2955
|
+
})] }),
|
|
2956
|
+
fontSizeInput === void 0 ? void 0 : /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
2957
|
+
color: "cyan",
|
|
2958
|
+
children: "Font size (pt): "
|
|
2959
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
2960
|
+
value: fontSizeInput,
|
|
2961
|
+
isFocused: true,
|
|
2962
|
+
placeholder: "e.g. 12",
|
|
2963
|
+
onChange: setFontSizeInput,
|
|
2964
|
+
onSubmit: (value) => {
|
|
2965
|
+
const sizePt = parseNumberField(value, selectedRun?.sizePt ?? DEFAULT_RUN_SIZE_PT);
|
|
2966
|
+
if (sizePt <= 0) dispatch({
|
|
2967
|
+
type: "SET_STATUS",
|
|
2968
|
+
severity: "warning",
|
|
2969
|
+
text: `"${value}" is not a positive font size`
|
|
2970
|
+
});
|
|
2971
|
+
else dispatch({
|
|
2972
|
+
type: "SET_RUN_FONT_SIZE",
|
|
2973
|
+
blockIndex,
|
|
2974
|
+
runIndex: clampedRunIndex,
|
|
2975
|
+
sizePt
|
|
2976
|
+
});
|
|
2977
|
+
setFontSizeInput(void 0);
|
|
2978
|
+
},
|
|
2979
|
+
onCancel: () => {
|
|
2980
|
+
setFontSizeInput(void 0);
|
|
2981
|
+
}
|
|
2982
|
+
})] }),
|
|
2646
2983
|
imageWizardOpen ? /* @__PURE__ */ jsx(FieldWizard, {
|
|
2647
2984
|
fields: IMAGE_FIELDS,
|
|
2648
2985
|
onCancel: () => {
|
|
@@ -2678,7 +3015,7 @@ function ParagraphDetailScreen() {
|
|
|
2678
3015
|
/* @__PURE__ */ jsxs(Text, {
|
|
2679
3016
|
dimColor: true,
|
|
2680
3017
|
children: [
|
|
2681
|
-
"<- / -> move, Enter edit text, b/i/u toggle, c colour, a append run, I image",
|
|
3018
|
+
"<- / -> move, Enter edit text, b/i/u toggle, c colour, f font, s size, a append run, I image",
|
|
2682
3019
|
doc.format === "docx" ? ", m formula" : "",
|
|
2683
3020
|
", Esc back"
|
|
2684
3021
|
]
|
|
@@ -3747,6 +4084,64 @@ function OdbTableRowsScreen() {
|
|
|
3747
4084
|
});
|
|
3748
4085
|
}
|
|
3749
4086
|
//#endregion
|
|
4087
|
+
//#region src/tui/screens/shared/vector-fields.ts
|
|
4088
|
+
function parseColorField(raw) {
|
|
4089
|
+
const trimmed = raw.trim();
|
|
4090
|
+
if (trimmed.length === 0) return;
|
|
4091
|
+
const [r, g, b] = trimmed.split(/\s+/).map((part) => Number.parseFloat(part));
|
|
4092
|
+
if (r === void 0 || g === void 0 || b === void 0 || ![
|
|
4093
|
+
r,
|
|
4094
|
+
g,
|
|
4095
|
+
b
|
|
4096
|
+
].every((value) => Number.isFinite(value))) return;
|
|
4097
|
+
return {
|
|
4098
|
+
r,
|
|
4099
|
+
g,
|
|
4100
|
+
b
|
|
4101
|
+
};
|
|
4102
|
+
}
|
|
4103
|
+
function parseStrokeField(raw) {
|
|
4104
|
+
const trimmed = raw.trim();
|
|
4105
|
+
if (trimmed.length === 0) return;
|
|
4106
|
+
const [r, g, b, widthPt] = trimmed.split(/\s+/).map((part) => Number.parseFloat(part));
|
|
4107
|
+
if (r === void 0 || g === void 0 || b === void 0 || widthPt === void 0 || ![
|
|
4108
|
+
r,
|
|
4109
|
+
g,
|
|
4110
|
+
b,
|
|
4111
|
+
widthPt
|
|
4112
|
+
].every((value) => Number.isFinite(value))) return;
|
|
4113
|
+
return {
|
|
4114
|
+
color: {
|
|
4115
|
+
r,
|
|
4116
|
+
g,
|
|
4117
|
+
b
|
|
4118
|
+
},
|
|
4119
|
+
widthPt
|
|
4120
|
+
};
|
|
4121
|
+
}
|
|
4122
|
+
function defaultTriangleSubpaths(widthPt, heightPt) {
|
|
4123
|
+
return [{
|
|
4124
|
+
start: {
|
|
4125
|
+
xPt: 0,
|
|
4126
|
+
yPt: heightPt
|
|
4127
|
+
},
|
|
4128
|
+
segments: [{
|
|
4129
|
+
kind: "line",
|
|
4130
|
+
to: {
|
|
4131
|
+
xPt: widthPt / 2,
|
|
4132
|
+
yPt: 0
|
|
4133
|
+
}
|
|
4134
|
+
}, {
|
|
4135
|
+
kind: "line",
|
|
4136
|
+
to: {
|
|
4137
|
+
xPt: widthPt,
|
|
4138
|
+
yPt: heightPt
|
|
4139
|
+
}
|
|
4140
|
+
}],
|
|
4141
|
+
closed: true
|
|
4142
|
+
}];
|
|
4143
|
+
}
|
|
4144
|
+
//#endregion
|
|
3750
4145
|
//#region src/tui/screens/editors/odg/shared.ts
|
|
3751
4146
|
function requireOdgDocument(state) {
|
|
3752
4147
|
const doc = state.openDocument;
|
|
@@ -3798,14 +4193,14 @@ function vectorKindLabel(kind) {
|
|
|
3798
4193
|
case "path": return "Path";
|
|
3799
4194
|
}
|
|
3800
4195
|
}
|
|
3801
|
-
function formatPt(value) {
|
|
4196
|
+
function formatPt$1(value) {
|
|
3802
4197
|
return value.toFixed(1);
|
|
3803
4198
|
}
|
|
3804
4199
|
function formatFrame(box) {
|
|
3805
|
-
return `${formatPt(box.xPt)},${formatPt(box.yPt)} ${formatPt(box.widthPt)}x${formatPt(box.heightPt)}pt`;
|
|
4200
|
+
return `${formatPt$1(box.xPt)},${formatPt$1(box.yPt)} ${formatPt$1(box.widthPt)}x${formatPt$1(box.heightPt)}pt`;
|
|
3806
4201
|
}
|
|
3807
4202
|
function formatPoint$1(point) {
|
|
3808
|
-
return `${formatPt(point.xPt)},${formatPt(point.yPt)}`;
|
|
4203
|
+
return `${formatPt$1(point.xPt)},${formatPt$1(point.yPt)}`;
|
|
3809
4204
|
}
|
|
3810
4205
|
function formatColor$1(color) {
|
|
3811
4206
|
return `rgb(${color.r.toFixed(2)}, ${color.g.toFixed(2)}, ${color.b.toFixed(2)})`;
|
|
@@ -3817,78 +4212,22 @@ function describeVectorGeometry(vector) {
|
|
|
3817
4212
|
function describeFillStroke(vector) {
|
|
3818
4213
|
const parts = [];
|
|
3819
4214
|
if (vector.kind !== "line" && vector.fill !== void 0) parts.push(`fill ${formatColor$1(vector.fill)}`);
|
|
3820
|
-
if (vector.stroke !== void 0) parts.push(`stroke ${formatColor$1(vector.stroke.color)} ${formatPt(vector.stroke.widthPt)}pt`);
|
|
4215
|
+
if (vector.stroke !== void 0) parts.push(`stroke ${formatColor$1(vector.stroke.color)} ${formatPt$1(vector.stroke.widthPt)}pt`);
|
|
3821
4216
|
return parts.length === 0 ? "no fill or stroke" : parts.join(", ");
|
|
3822
4217
|
}
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
const
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
};
|
|
3837
|
-
}
|
|
3838
|
-
function parseStrokeField(raw) {
|
|
3839
|
-
const trimmed = raw.trim();
|
|
3840
|
-
if (trimmed.length === 0) return;
|
|
3841
|
-
const [r, g, b, widthPt] = trimmed.split(/\s+/).map((part) => Number.parseFloat(part));
|
|
3842
|
-
if (r === void 0 || g === void 0 || b === void 0 || widthPt === void 0 || ![
|
|
3843
|
-
r,
|
|
3844
|
-
g,
|
|
3845
|
-
b,
|
|
3846
|
-
widthPt
|
|
3847
|
-
].every((value) => Number.isFinite(value))) return;
|
|
3848
|
-
return {
|
|
3849
|
-
color: {
|
|
3850
|
-
r,
|
|
3851
|
-
g,
|
|
3852
|
-
b
|
|
3853
|
-
},
|
|
3854
|
-
widthPt
|
|
3855
|
-
};
|
|
3856
|
-
}
|
|
3857
|
-
function defaultTriangleSubpaths(widthPt, heightPt) {
|
|
3858
|
-
return [{
|
|
3859
|
-
start: {
|
|
3860
|
-
xPt: 0,
|
|
3861
|
-
yPt: heightPt
|
|
3862
|
-
},
|
|
3863
|
-
segments: [{
|
|
3864
|
-
kind: "line",
|
|
3865
|
-
to: {
|
|
3866
|
-
xPt: widthPt / 2,
|
|
3867
|
-
yPt: 0
|
|
3868
|
-
}
|
|
3869
|
-
}, {
|
|
3870
|
-
kind: "line",
|
|
3871
|
-
to: {
|
|
3872
|
-
xPt: widthPt,
|
|
3873
|
-
yPt: heightPt
|
|
3874
|
-
}
|
|
3875
|
-
}],
|
|
3876
|
-
closed: true
|
|
3877
|
-
}];
|
|
3878
|
-
}
|
|
3879
|
-
//#endregion
|
|
3880
|
-
//#region src/tui/screens/editors/odg/page-list.tsx
|
|
3881
|
-
function OdgPageListScreen() {
|
|
3882
|
-
const state = useAppState();
|
|
3883
|
-
const dispatch = useAppDispatch();
|
|
3884
|
-
const pages = requireOdgDocument(state).editor.pages();
|
|
3885
|
-
const query = state.searchQuery.trim().toLowerCase();
|
|
3886
|
-
const pageIndices = pages.map((_, index) => index).filter((index) => query.length === 0 || `page ${index + 1}`.includes(query));
|
|
3887
|
-
const { selectedIndex } = useNavigationInput({
|
|
3888
|
-
itemCount: pageIndices.length,
|
|
3889
|
-
isActive: !anyOverlayOpen(state),
|
|
3890
|
-
onBack: () => {
|
|
3891
|
-
dispatch({ type: "POP_SCREEN" });
|
|
4218
|
+
//#endregion
|
|
4219
|
+
//#region src/tui/screens/editors/odg/page-list.tsx
|
|
4220
|
+
function OdgPageListScreen() {
|
|
4221
|
+
const state = useAppState();
|
|
4222
|
+
const dispatch = useAppDispatch();
|
|
4223
|
+
const pages = requireOdgDocument(state).editor.pages();
|
|
4224
|
+
const query = state.searchQuery.trim().toLowerCase();
|
|
4225
|
+
const pageIndices = pages.map((_, index) => index).filter((index) => query.length === 0 || `page ${index + 1}`.includes(query));
|
|
4226
|
+
const { selectedIndex } = useNavigationInput({
|
|
4227
|
+
itemCount: pageIndices.length,
|
|
4228
|
+
isActive: !anyOverlayOpen(state),
|
|
4229
|
+
onBack: () => {
|
|
4230
|
+
dispatch({ type: "POP_SCREEN" });
|
|
3892
4231
|
},
|
|
3893
4232
|
onSelect: (index) => {
|
|
3894
4233
|
const pageIndex = pageIndices[index];
|
|
@@ -3940,7 +4279,7 @@ function OdgPageListScreen() {
|
|
|
3940
4279
|
}
|
|
3941
4280
|
//#endregion
|
|
3942
4281
|
//#region src/tui/screens/editors/odg/page-detail.tsx
|
|
3943
|
-
const ADD_KIND_OPTIONS = [
|
|
4282
|
+
const ADD_KIND_OPTIONS$1 = [
|
|
3944
4283
|
{
|
|
3945
4284
|
kind: "rect",
|
|
3946
4285
|
label: "Rectangle"
|
|
@@ -3966,7 +4305,7 @@ const ADD_KIND_OPTIONS = [
|
|
|
3966
4305
|
label: "Image"
|
|
3967
4306
|
}
|
|
3968
4307
|
];
|
|
3969
|
-
const GEOMETRY_FIELDS = [
|
|
4308
|
+
const GEOMETRY_FIELDS$1 = [
|
|
3970
4309
|
{
|
|
3971
4310
|
key: "xPt",
|
|
3972
4311
|
label: "X (pt)",
|
|
@@ -3988,24 +4327,24 @@ const GEOMETRY_FIELDS = [
|
|
|
3988
4327
|
defaultValue: "100"
|
|
3989
4328
|
}
|
|
3990
4329
|
];
|
|
3991
|
-
const FILL_FIELD = {
|
|
4330
|
+
const FILL_FIELD$1 = {
|
|
3992
4331
|
key: "fill",
|
|
3993
4332
|
label: "Fill \"r g b\" (0-1 each), blank for none",
|
|
3994
4333
|
defaultValue: "0.8 0.8 0.8"
|
|
3995
4334
|
};
|
|
3996
|
-
const STROKE_FIELD = {
|
|
4335
|
+
const STROKE_FIELD$1 = {
|
|
3997
4336
|
key: "stroke",
|
|
3998
4337
|
label: "Stroke \"r g b widthPt\" (0-1 colour, pt width), blank for none",
|
|
3999
4338
|
defaultValue: "0 0 0 1"
|
|
4000
4339
|
};
|
|
4001
|
-
function fieldsForAddKind(kind) {
|
|
4340
|
+
function fieldsForAddKind$1(kind) {
|
|
4002
4341
|
switch (kind) {
|
|
4003
4342
|
case "rect":
|
|
4004
4343
|
case "ellipse":
|
|
4005
4344
|
case "path": return [
|
|
4006
|
-
...GEOMETRY_FIELDS,
|
|
4007
|
-
FILL_FIELD,
|
|
4008
|
-
STROKE_FIELD
|
|
4345
|
+
...GEOMETRY_FIELDS$1,
|
|
4346
|
+
FILL_FIELD$1,
|
|
4347
|
+
STROKE_FIELD$1
|
|
4009
4348
|
];
|
|
4010
4349
|
case "line": return [
|
|
4011
4350
|
{
|
|
@@ -4028,15 +4367,15 @@ function fieldsForAddKind(kind) {
|
|
|
4028
4367
|
label: "To Y (pt)",
|
|
4029
4368
|
defaultValue: "40"
|
|
4030
4369
|
},
|
|
4031
|
-
STROKE_FIELD
|
|
4370
|
+
STROKE_FIELD$1
|
|
4032
4371
|
];
|
|
4033
|
-
case "textbox": return [...GEOMETRY_FIELDS, {
|
|
4372
|
+
case "textbox": return [...GEOMETRY_FIELDS$1, {
|
|
4034
4373
|
key: "text",
|
|
4035
4374
|
label: "Text",
|
|
4036
4375
|
defaultValue: "Text"
|
|
4037
4376
|
}];
|
|
4038
4377
|
case "image": return [
|
|
4039
|
-
...GEOMETRY_FIELDS,
|
|
4378
|
+
...GEOMETRY_FIELDS$1,
|
|
4040
4379
|
{
|
|
4041
4380
|
key: "path",
|
|
4042
4381
|
label: "Image file path (.png/.jpg/.jpeg)",
|
|
@@ -4050,7 +4389,7 @@ function fieldsForAddKind(kind) {
|
|
|
4050
4389
|
];
|
|
4051
4390
|
}
|
|
4052
4391
|
}
|
|
4053
|
-
function readFrame(values) {
|
|
4392
|
+
function readFrame$1(values) {
|
|
4054
4393
|
return {
|
|
4055
4394
|
xPt: parseNumberField(requireFieldValue(values, "xPt"), 0),
|
|
4056
4395
|
yPt: parseNumberField(requireFieldValue(values, "yPt"), 0),
|
|
@@ -4069,19 +4408,19 @@ function warnIfVectorAddedReadOnly(doc, pageIndex, dispatch, label) {
|
|
|
4069
4408
|
const added = buildPageItems(doc, pageIndex).filter((item) => item.kind === "vector").at(-1);
|
|
4070
4409
|
if (added !== void 0 && added.liveVector === void 0) warnVectorIsViewOnly(dispatch, label);
|
|
4071
4410
|
}
|
|
4072
|
-
function inferImageFormat(path) {
|
|
4411
|
+
function inferImageFormat$1(path) {
|
|
4073
4412
|
const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
|
|
4074
4413
|
if (extension === "png") return "png";
|
|
4075
4414
|
if (extension === "jpg" || extension === "jpeg") return "jpeg";
|
|
4076
4415
|
}
|
|
4077
|
-
async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
4416
|
+
async function applyAddKind$1(kind, pageIndex, doc, values, dispatch) {
|
|
4078
4417
|
switch (kind) {
|
|
4079
4418
|
case "rect":
|
|
4080
4419
|
dispatch({
|
|
4081
4420
|
type: "ADD_RECT",
|
|
4082
|
-
pageIndex,
|
|
4421
|
+
containerIndex: pageIndex,
|
|
4083
4422
|
init: {
|
|
4084
|
-
frame: readFrame(values),
|
|
4423
|
+
frame: readFrame$1(values),
|
|
4085
4424
|
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
4086
4425
|
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
4087
4426
|
}
|
|
@@ -4091,9 +4430,9 @@ async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
|
4091
4430
|
case "ellipse":
|
|
4092
4431
|
dispatch({
|
|
4093
4432
|
type: "ADD_ELLIPSE",
|
|
4094
|
-
pageIndex,
|
|
4433
|
+
containerIndex: pageIndex,
|
|
4095
4434
|
init: {
|
|
4096
|
-
frame: readFrame(values),
|
|
4435
|
+
frame: readFrame$1(values),
|
|
4097
4436
|
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
4098
4437
|
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
4099
4438
|
}
|
|
@@ -4103,7 +4442,7 @@ async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
|
4103
4442
|
case "line":
|
|
4104
4443
|
dispatch({
|
|
4105
4444
|
type: "ADD_LINE",
|
|
4106
|
-
pageIndex,
|
|
4445
|
+
containerIndex: pageIndex,
|
|
4107
4446
|
init: {
|
|
4108
4447
|
from: {
|
|
4109
4448
|
xPt: parseNumberField(requireFieldValue(values, "fromXPt"), 0),
|
|
@@ -4126,10 +4465,10 @@ async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
|
4126
4465
|
warnIfVectorAddedReadOnly(doc, pageIndex, dispatch, "Line");
|
|
4127
4466
|
return;
|
|
4128
4467
|
case "path": {
|
|
4129
|
-
const frame = readFrame(values);
|
|
4468
|
+
const frame = readFrame$1(values);
|
|
4130
4469
|
dispatch({
|
|
4131
4470
|
type: "ADD_PATH",
|
|
4132
|
-
pageIndex,
|
|
4471
|
+
containerIndex: pageIndex,
|
|
4133
4472
|
init: {
|
|
4134
4473
|
frame,
|
|
4135
4474
|
subpaths: defaultTriangleSubpaths(frame.widthPt, frame.heightPt),
|
|
@@ -4144,14 +4483,14 @@ async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
|
4144
4483
|
dispatch({
|
|
4145
4484
|
type: "ADD_TEXTBOX",
|
|
4146
4485
|
containerIndex: pageIndex,
|
|
4147
|
-
frame: readFrame(values),
|
|
4486
|
+
frame: readFrame$1(values),
|
|
4148
4487
|
text: requireFieldValue(values, "text")
|
|
4149
4488
|
});
|
|
4150
4489
|
return;
|
|
4151
4490
|
case "image": {
|
|
4152
|
-
const frame = readFrame(values);
|
|
4491
|
+
const frame = readFrame$1(values);
|
|
4153
4492
|
const path = requireFieldValue(values, "path");
|
|
4154
|
-
const format = inferImageFormat(path);
|
|
4493
|
+
const format = inferImageFormat$1(path);
|
|
4155
4494
|
if (format === void 0) {
|
|
4156
4495
|
dispatch({
|
|
4157
4496
|
type: "SET_STATUS",
|
|
@@ -4182,15 +4521,15 @@ async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
|
4182
4521
|
}
|
|
4183
4522
|
}
|
|
4184
4523
|
}
|
|
4185
|
-
function AddItemFlow(props) {
|
|
4524
|
+
function AddItemFlow$1(props) {
|
|
4186
4525
|
const dispatch = useAppDispatch();
|
|
4187
4526
|
const [kind, setKind] = useState(void 0);
|
|
4188
4527
|
const { selectedIndex } = useNavigationInput({
|
|
4189
|
-
itemCount: ADD_KIND_OPTIONS.length,
|
|
4528
|
+
itemCount: ADD_KIND_OPTIONS$1.length,
|
|
4190
4529
|
isActive: props.isActive && kind === void 0,
|
|
4191
4530
|
onBack: props.onCancel,
|
|
4192
4531
|
onSelect: (index) => {
|
|
4193
|
-
const option = ADD_KIND_OPTIONS[index];
|
|
4532
|
+
const option = ADD_KIND_OPTIONS$1[index];
|
|
4194
4533
|
if (option === void 0) return;
|
|
4195
4534
|
setKind(option.kind);
|
|
4196
4535
|
}
|
|
@@ -4203,7 +4542,7 @@ function AddItemFlow(props) {
|
|
|
4203
4542
|
bold: true,
|
|
4204
4543
|
children: "Add item -- choose a kind"
|
|
4205
4544
|
}), /* @__PURE__ */ jsx(ListView, {
|
|
4206
|
-
items: ADD_KIND_OPTIONS,
|
|
4545
|
+
items: ADD_KIND_OPTIONS$1,
|
|
4207
4546
|
selectedIndex,
|
|
4208
4547
|
reservedRows: 6,
|
|
4209
4548
|
renderItem: (option, isSelected) => /* @__PURE__ */ jsxs(Text, {
|
|
@@ -4213,10 +4552,10 @@ function AddItemFlow(props) {
|
|
|
4213
4552
|
})]
|
|
4214
4553
|
});
|
|
4215
4554
|
return /* @__PURE__ */ jsx(FieldWizard, {
|
|
4216
|
-
fields: fieldsForAddKind(kind),
|
|
4555
|
+
fields: fieldsForAddKind$1(kind),
|
|
4217
4556
|
onCancel: props.onCancel,
|
|
4218
4557
|
onComplete: (values) => {
|
|
4219
|
-
applyAddKind(kind, props.pageIndex, props.doc, values, dispatch).then(props.onCreated);
|
|
4558
|
+
applyAddKind$1(kind, props.pageIndex, props.doc, values, dispatch).then(props.onCreated);
|
|
4220
4559
|
}
|
|
4221
4560
|
});
|
|
4222
4561
|
}
|
|
@@ -4268,7 +4607,7 @@ function OdgPageDetailScreen() {
|
|
|
4268
4607
|
setIsAdding(true);
|
|
4269
4608
|
}
|
|
4270
4609
|
});
|
|
4271
|
-
if (isAdding) return /* @__PURE__ */ jsx(AddItemFlow, {
|
|
4610
|
+
if (isAdding) return /* @__PURE__ */ jsx(AddItemFlow$1, {
|
|
4272
4611
|
pageIndex,
|
|
4273
4612
|
doc,
|
|
4274
4613
|
isActive: !overlayOpen,
|
|
@@ -4321,7 +4660,7 @@ function buildVectorRows(vector, liveVector, dispatch) {
|
|
|
4321
4660
|
});
|
|
4322
4661
|
}
|
|
4323
4662
|
rows.push({
|
|
4324
|
-
label: `Stroke: ${vector.stroke === void 0 ? "none" : `${formatColor$1(vector.stroke.color)} ${formatPt(vector.stroke.widthPt)}pt`}`,
|
|
4663
|
+
label: `Stroke: ${vector.stroke === void 0 ? "none" : `${formatColor$1(vector.stroke.color)} ${formatPt$1(vector.stroke.widthPt)}pt`}`,
|
|
4325
4664
|
currentValue: vector.stroke === void 0 ? "" : `${vector.stroke.color.r} ${vector.stroke.color.g} ${vector.stroke.color.b} ${vector.stroke.widthPt}`,
|
|
4326
4665
|
commit: (raw) => {
|
|
4327
4666
|
const stroke = parseStrokeField(raw);
|
|
@@ -4453,7 +4792,7 @@ function ShapeDetail(props) {
|
|
|
4453
4792
|
}
|
|
4454
4793
|
},
|
|
4455
4794
|
{
|
|
4456
|
-
label: `X: ${formatPt(frame.xPt)}pt`,
|
|
4795
|
+
label: `X: ${formatPt$1(frame.xPt)}pt`,
|
|
4457
4796
|
currentValue: String(frame.xPt),
|
|
4458
4797
|
commit: (raw) => {
|
|
4459
4798
|
dispatch({
|
|
@@ -4468,7 +4807,7 @@ function ShapeDetail(props) {
|
|
|
4468
4807
|
}
|
|
4469
4808
|
},
|
|
4470
4809
|
{
|
|
4471
|
-
label: `Y: ${formatPt(frame.yPt)}pt`,
|
|
4810
|
+
label: `Y: ${formatPt$1(frame.yPt)}pt`,
|
|
4472
4811
|
currentValue: String(frame.yPt),
|
|
4473
4812
|
commit: (raw) => {
|
|
4474
4813
|
dispatch({
|
|
@@ -4483,7 +4822,7 @@ function ShapeDetail(props) {
|
|
|
4483
4822
|
}
|
|
4484
4823
|
},
|
|
4485
4824
|
{
|
|
4486
|
-
label: `Width: ${formatPt(frame.widthPt)}pt`,
|
|
4825
|
+
label: `Width: ${formatPt$1(frame.widthPt)}pt`,
|
|
4487
4826
|
currentValue: String(frame.widthPt),
|
|
4488
4827
|
commit: (raw) => {
|
|
4489
4828
|
dispatch({
|
|
@@ -4498,7 +4837,7 @@ function ShapeDetail(props) {
|
|
|
4498
4837
|
}
|
|
4499
4838
|
},
|
|
4500
4839
|
{
|
|
4501
|
-
label: `Height: ${formatPt(frame.heightPt)}pt`,
|
|
4840
|
+
label: `Height: ${formatPt$1(frame.heightPt)}pt`,
|
|
4502
4841
|
currentValue: String(frame.heightPt),
|
|
4503
4842
|
commit: (raw) => {
|
|
4504
4843
|
dispatch({
|
|
@@ -4513,7 +4852,7 @@ function ShapeDetail(props) {
|
|
|
4513
4852
|
}
|
|
4514
4853
|
},
|
|
4515
4854
|
{
|
|
4516
|
-
label: `Rotation: ${shape.rotationDeg === void 0 ? "none" : `${formatPt(shape.rotationDeg)} deg`}`,
|
|
4855
|
+
label: `Rotation: ${shape.rotationDeg === void 0 ? "none" : `${formatPt$1(shape.rotationDeg)} deg`}`,
|
|
4517
4856
|
currentValue: shape.rotationDeg === void 0 ? "" : String(shape.rotationDeg),
|
|
4518
4857
|
commit: (raw) => {
|
|
4519
4858
|
const trimmed = raw.trim();
|
|
@@ -5061,17 +5400,149 @@ function summarizeSlideTables(doc, slideIndex) {
|
|
|
5061
5400
|
}
|
|
5062
5401
|
//#endregion
|
|
5063
5402
|
//#region src/tui/screens/editors/pptx/slide-detail.tsx
|
|
5064
|
-
const IMAGE_EXTENSION_TO_FORMAT = {
|
|
5403
|
+
const IMAGE_EXTENSION_TO_FORMAT$1 = {
|
|
5065
5404
|
png: "png",
|
|
5066
5405
|
jpg: "jpeg",
|
|
5067
5406
|
jpeg: "jpeg"
|
|
5068
5407
|
};
|
|
5069
5408
|
const DEFAULT_TABLE_ROWS = 2;
|
|
5070
5409
|
const DEFAULT_TABLE_COLUMNS = 2;
|
|
5410
|
+
const VECTOR_GEOMETRY_FIELDS = [
|
|
5411
|
+
{
|
|
5412
|
+
key: "xPt",
|
|
5413
|
+
label: "X (pt)",
|
|
5414
|
+
defaultValue: "40"
|
|
5415
|
+
},
|
|
5416
|
+
{
|
|
5417
|
+
key: "yPt",
|
|
5418
|
+
label: "Y (pt)",
|
|
5419
|
+
defaultValue: "40"
|
|
5420
|
+
},
|
|
5421
|
+
{
|
|
5422
|
+
key: "widthPt",
|
|
5423
|
+
label: "Width (pt)",
|
|
5424
|
+
defaultValue: "160"
|
|
5425
|
+
},
|
|
5426
|
+
{
|
|
5427
|
+
key: "heightPt",
|
|
5428
|
+
label: "Height (pt)",
|
|
5429
|
+
defaultValue: "100"
|
|
5430
|
+
}
|
|
5431
|
+
];
|
|
5432
|
+
const VECTOR_FILL_FIELD = {
|
|
5433
|
+
key: "fill",
|
|
5434
|
+
label: "Fill \"r g b\" (0-1 each), blank for none",
|
|
5435
|
+
defaultValue: "0.8 0.8 0.8"
|
|
5436
|
+
};
|
|
5437
|
+
const VECTOR_STROKE_FIELD = {
|
|
5438
|
+
key: "stroke",
|
|
5439
|
+
label: "Stroke \"r g b widthPt\" (0-1 colour, pt width), blank for none",
|
|
5440
|
+
defaultValue: "0 0 0 1"
|
|
5441
|
+
};
|
|
5442
|
+
const VECTOR_LINE_FIELDS = [
|
|
5443
|
+
{
|
|
5444
|
+
key: "fromXPt",
|
|
5445
|
+
label: "From X (pt)",
|
|
5446
|
+
defaultValue: "40"
|
|
5447
|
+
},
|
|
5448
|
+
{
|
|
5449
|
+
key: "fromYPt",
|
|
5450
|
+
label: "From Y (pt)",
|
|
5451
|
+
defaultValue: "40"
|
|
5452
|
+
},
|
|
5453
|
+
{
|
|
5454
|
+
key: "toXPt",
|
|
5455
|
+
label: "To X (pt)",
|
|
5456
|
+
defaultValue: "200"
|
|
5457
|
+
},
|
|
5458
|
+
{
|
|
5459
|
+
key: "toYPt",
|
|
5460
|
+
label: "To Y (pt)",
|
|
5461
|
+
defaultValue: "40"
|
|
5462
|
+
},
|
|
5463
|
+
VECTOR_STROKE_FIELD
|
|
5464
|
+
];
|
|
5465
|
+
function fieldsForVectorKind(kind) {
|
|
5466
|
+
switch (kind) {
|
|
5467
|
+
case "rect":
|
|
5468
|
+
case "ellipse":
|
|
5469
|
+
case "path": return [
|
|
5470
|
+
...VECTOR_GEOMETRY_FIELDS,
|
|
5471
|
+
VECTOR_FILL_FIELD,
|
|
5472
|
+
VECTOR_STROKE_FIELD
|
|
5473
|
+
];
|
|
5474
|
+
case "line": return VECTOR_LINE_FIELDS;
|
|
5475
|
+
}
|
|
5476
|
+
}
|
|
5477
|
+
function readVectorFrame(values) {
|
|
5478
|
+
return {
|
|
5479
|
+
xPt: parseNumberField(requireFieldValue(values, "xPt"), 0),
|
|
5480
|
+
yPt: parseNumberField(requireFieldValue(values, "yPt"), 0),
|
|
5481
|
+
widthPt: parseNumberField(requireFieldValue(values, "widthPt"), 160),
|
|
5482
|
+
heightPt: parseNumberField(requireFieldValue(values, "heightPt"), 100)
|
|
5483
|
+
};
|
|
5484
|
+
}
|
|
5485
|
+
function buildVectorAction(kind, slideIndex, values) {
|
|
5486
|
+
switch (kind) {
|
|
5487
|
+
case "rect": return {
|
|
5488
|
+
type: "ADD_RECT",
|
|
5489
|
+
containerIndex: slideIndex,
|
|
5490
|
+
init: {
|
|
5491
|
+
frame: readVectorFrame(values),
|
|
5492
|
+
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
5493
|
+
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
5494
|
+
}
|
|
5495
|
+
};
|
|
5496
|
+
case "ellipse": return {
|
|
5497
|
+
type: "ADD_ELLIPSE",
|
|
5498
|
+
containerIndex: slideIndex,
|
|
5499
|
+
init: {
|
|
5500
|
+
frame: readVectorFrame(values),
|
|
5501
|
+
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
5502
|
+
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
5503
|
+
}
|
|
5504
|
+
};
|
|
5505
|
+
case "line": return {
|
|
5506
|
+
type: "ADD_LINE",
|
|
5507
|
+
containerIndex: slideIndex,
|
|
5508
|
+
init: {
|
|
5509
|
+
from: {
|
|
5510
|
+
xPt: parseNumberField(requireFieldValue(values, "fromXPt"), 0),
|
|
5511
|
+
yPt: parseNumberField(requireFieldValue(values, "fromYPt"), 0)
|
|
5512
|
+
},
|
|
5513
|
+
to: {
|
|
5514
|
+
xPt: parseNumberField(requireFieldValue(values, "toXPt"), 100),
|
|
5515
|
+
yPt: parseNumberField(requireFieldValue(values, "toYPt"), 0)
|
|
5516
|
+
},
|
|
5517
|
+
stroke: parseStrokeField(requireFieldValue(values, "stroke")) ?? {
|
|
5518
|
+
color: {
|
|
5519
|
+
r: 0,
|
|
5520
|
+
g: 0,
|
|
5521
|
+
b: 0
|
|
5522
|
+
},
|
|
5523
|
+
widthPt: 1
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5526
|
+
};
|
|
5527
|
+
case "path": {
|
|
5528
|
+
const frame = readVectorFrame(values);
|
|
5529
|
+
return {
|
|
5530
|
+
type: "ADD_PATH",
|
|
5531
|
+
containerIndex: slideIndex,
|
|
5532
|
+
init: {
|
|
5533
|
+
frame,
|
|
5534
|
+
subpaths: defaultTriangleSubpaths(frame.widthPt, frame.heightPt),
|
|
5535
|
+
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
5536
|
+
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
5537
|
+
}
|
|
5538
|
+
};
|
|
5539
|
+
}
|
|
5540
|
+
}
|
|
5541
|
+
}
|
|
5071
5542
|
function imageFormatFromPath(path) {
|
|
5072
5543
|
const dotIndex = path.lastIndexOf(".");
|
|
5073
5544
|
if (dotIndex < 0) return;
|
|
5074
|
-
return IMAGE_EXTENSION_TO_FORMAT[path.slice(dotIndex + 1).toLowerCase()];
|
|
5545
|
+
return IMAGE_EXTENSION_TO_FORMAT$1[path.slice(dotIndex + 1).toLowerCase()];
|
|
5075
5546
|
}
|
|
5076
5547
|
async function readImageForShape(path) {
|
|
5077
5548
|
const format = imageFormatFromPath(path);
|
|
@@ -5112,6 +5583,7 @@ function SlideDetailScreen(props) {
|
|
|
5112
5583
|
const [draft, setDraft] = useState("");
|
|
5113
5584
|
const [imageError, setImageError] = useState(void 0);
|
|
5114
5585
|
const [tableRows, setTableRows] = useState(DEFAULT_TABLE_ROWS);
|
|
5586
|
+
const [vectorKind, setVectorKind] = useState(void 0);
|
|
5115
5587
|
const formIsOpen = addMode !== "closed";
|
|
5116
5588
|
const { selectedIndex } = useNavigationInput({
|
|
5117
5589
|
itemCount: selectableRowIndices.length,
|
|
@@ -5172,6 +5644,27 @@ function SlideDetailScreen(props) {
|
|
|
5172
5644
|
if (input === "b") {
|
|
5173
5645
|
setDraft(String(DEFAULT_TABLE_ROWS));
|
|
5174
5646
|
setAddMode("tableRows");
|
|
5647
|
+
return;
|
|
5648
|
+
}
|
|
5649
|
+
if (doc.format !== "odp") return;
|
|
5650
|
+
if (input === "r") {
|
|
5651
|
+
setVectorKind("rect");
|
|
5652
|
+
setAddMode("vector");
|
|
5653
|
+
return;
|
|
5654
|
+
}
|
|
5655
|
+
if (input === "e") {
|
|
5656
|
+
setVectorKind("ellipse");
|
|
5657
|
+
setAddMode("vector");
|
|
5658
|
+
return;
|
|
5659
|
+
}
|
|
5660
|
+
if (input === "n") {
|
|
5661
|
+
setVectorKind("line");
|
|
5662
|
+
setAddMode("vector");
|
|
5663
|
+
return;
|
|
5664
|
+
}
|
|
5665
|
+
if (input === "p") {
|
|
5666
|
+
setVectorKind("path");
|
|
5667
|
+
setAddMode("vector");
|
|
5175
5668
|
}
|
|
5176
5669
|
}, { isActive: !overlayOpen && addMode === "chooseKind" });
|
|
5177
5670
|
useInput((input) => {
|
|
@@ -5285,9 +5778,25 @@ function SlideDetailScreen(props) {
|
|
|
5285
5778
|
});
|
|
5286
5779
|
}
|
|
5287
5780
|
}),
|
|
5288
|
-
addMode === "chooseKind" ? /* @__PURE__ */
|
|
5781
|
+
addMode === "chooseKind" ? /* @__PURE__ */ jsxs(Text, {
|
|
5289
5782
|
color: "cyan",
|
|
5290
|
-
children:
|
|
5783
|
+
children: [
|
|
5784
|
+
"Add shape: t textbox, i image, b table",
|
|
5785
|
+
doc.format === "odp" ? ", r rect, e ellipse, n line, p path" : "",
|
|
5786
|
+
", Esc cancel"
|
|
5787
|
+
]
|
|
5788
|
+
}) : void 0,
|
|
5789
|
+
addMode === "vector" && vectorKind !== void 0 ? /* @__PURE__ */ jsx(FieldWizard, {
|
|
5790
|
+
fields: fieldsForVectorKind(vectorKind),
|
|
5791
|
+
onCancel: () => {
|
|
5792
|
+
setAddMode("closed");
|
|
5793
|
+
setVectorKind(void 0);
|
|
5794
|
+
},
|
|
5795
|
+
onComplete: (values) => {
|
|
5796
|
+
dispatch(buildVectorAction(vectorKind, slideIndex, values));
|
|
5797
|
+
setAddMode("closed");
|
|
5798
|
+
setVectorKind(void 0);
|
|
5799
|
+
}
|
|
5291
5800
|
}) : void 0,
|
|
5292
5801
|
addMode === "textbox" ? /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
5293
5802
|
color: "cyan",
|
|
@@ -6110,8 +6619,90 @@ const RESERVED_LETTERS = /* @__PURE__ */ new Set([
|
|
|
6110
6619
|
"l",
|
|
6111
6620
|
"p",
|
|
6112
6621
|
"t",
|
|
6113
|
-
"m"
|
|
6622
|
+
"m",
|
|
6623
|
+
"f",
|
|
6624
|
+
"i"
|
|
6114
6625
|
]);
|
|
6626
|
+
const IMAGE_EXTENSION_TO_FORMAT = {
|
|
6627
|
+
png: "png",
|
|
6628
|
+
jpg: "jpeg",
|
|
6629
|
+
jpeg: "jpeg"
|
|
6630
|
+
};
|
|
6631
|
+
function inferSheetImageFormat(path) {
|
|
6632
|
+
const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
|
|
6633
|
+
return IMAGE_EXTENSION_TO_FORMAT[extension];
|
|
6634
|
+
}
|
|
6635
|
+
const SHEET_IMAGE_FIELDS = [
|
|
6636
|
+
{
|
|
6637
|
+
key: "path",
|
|
6638
|
+
label: "Image file path (.png/.jpg/.jpeg)",
|
|
6639
|
+
defaultValue: ""
|
|
6640
|
+
},
|
|
6641
|
+
{
|
|
6642
|
+
key: "widthPt",
|
|
6643
|
+
label: "Width (pt)",
|
|
6644
|
+
defaultValue: "100"
|
|
6645
|
+
},
|
|
6646
|
+
{
|
|
6647
|
+
key: "heightPt",
|
|
6648
|
+
label: "Height (pt)",
|
|
6649
|
+
defaultValue: "60"
|
|
6650
|
+
},
|
|
6651
|
+
{
|
|
6652
|
+
key: "offsetXPt",
|
|
6653
|
+
label: "Offset X from anchor cell (pt)",
|
|
6654
|
+
defaultValue: "0"
|
|
6655
|
+
},
|
|
6656
|
+
{
|
|
6657
|
+
key: "offsetYPt",
|
|
6658
|
+
label: "Offset Y from anchor cell (pt)",
|
|
6659
|
+
defaultValue: "0"
|
|
6660
|
+
},
|
|
6661
|
+
{
|
|
6662
|
+
key: "altText",
|
|
6663
|
+
label: "Alt text, blank for none",
|
|
6664
|
+
defaultValue: ""
|
|
6665
|
+
}
|
|
6666
|
+
];
|
|
6667
|
+
async function applyAddSheetImage(sheetIndex, anchorRow, anchorColumn, values, dispatch) {
|
|
6668
|
+
const path = requireFieldValue(values, "path");
|
|
6669
|
+
const format = inferSheetImageFormat(path);
|
|
6670
|
+
if (format === void 0) {
|
|
6671
|
+
dispatch({
|
|
6672
|
+
type: "SET_STATUS",
|
|
6673
|
+
severity: "warning",
|
|
6674
|
+
text: `${path} is not a .png or .jpg/.jpeg file -- image not added`
|
|
6675
|
+
});
|
|
6676
|
+
return;
|
|
6677
|
+
}
|
|
6678
|
+
try {
|
|
6679
|
+
const bytes = new Uint8Array(await readInput(path));
|
|
6680
|
+
const widthPt = parseNumberField(requireFieldValue(values, "widthPt"), 100);
|
|
6681
|
+
const heightPt = parseNumberField(requireFieldValue(values, "heightPt"), 60);
|
|
6682
|
+
const offsetXPt = parseNumberField(requireFieldValue(values, "offsetXPt"), 0);
|
|
6683
|
+
const offsetYPt = parseNumberField(requireFieldValue(values, "offsetYPt"), 0);
|
|
6684
|
+
const altTextRaw = requireFieldValue(values, "altText").trim();
|
|
6685
|
+
dispatch({
|
|
6686
|
+
type: "ADD_SHEET_IMAGE",
|
|
6687
|
+
sheetIndex,
|
|
6688
|
+
anchorRow,
|
|
6689
|
+
anchorColumn,
|
|
6690
|
+
offsetXPt,
|
|
6691
|
+
offsetYPt,
|
|
6692
|
+
format,
|
|
6693
|
+
bytes,
|
|
6694
|
+
widthPt,
|
|
6695
|
+
heightPt,
|
|
6696
|
+
altText: altTextRaw.length === 0 ? void 0 : altTextRaw
|
|
6697
|
+
});
|
|
6698
|
+
} catch (error) {
|
|
6699
|
+
dispatch({
|
|
6700
|
+
type: "SET_STATUS",
|
|
6701
|
+
severity: "error",
|
|
6702
|
+
text: `Could not read ${path}: ${describeError(error)}`
|
|
6703
|
+
});
|
|
6704
|
+
}
|
|
6705
|
+
}
|
|
6115
6706
|
function windowStart(cursor, total, viewport) {
|
|
6116
6707
|
const maxStart = Math.max(0, total - viewport);
|
|
6117
6708
|
return Math.min(Math.max(cursor - Math.floor(viewport / 2), 0), maxStart);
|
|
@@ -6134,6 +6725,9 @@ function OdsSpreadsheetGridScreen() {
|
|
|
6134
6725
|
const [viewMode, setViewMode] = useState("grid");
|
|
6135
6726
|
const [editSession, setEditSession] = useState(void 0);
|
|
6136
6727
|
const [mergeAnchor, setMergeAnchor] = useState(void 0);
|
|
6728
|
+
const [formulaEditing, setFormulaEditing] = useState(false);
|
|
6729
|
+
const [formulaDraft, setFormulaDraft] = useState("");
|
|
6730
|
+
const [imageWizardOpen, setImageWizardOpen] = useState(false);
|
|
6137
6731
|
const { columns: terminalColumns, rows: terminalRows } = useWindowSize();
|
|
6138
6732
|
const sheet = resolveSheet(doc.editor, sheetIndex);
|
|
6139
6733
|
const { rowCount, columnCount } = sheetExtent(sheet);
|
|
@@ -6142,6 +6736,7 @@ function OdsSpreadsheetGridScreen() {
|
|
|
6142
6736
|
const clampedColumn = Math.min(cursorColumn, columnCount - 1);
|
|
6143
6737
|
const overlayOpen = anyOverlayOpen(state);
|
|
6144
6738
|
const editing = editSession !== void 0;
|
|
6739
|
+
const editingAnything = editing || formulaEditing || imageWizardOpen;
|
|
6145
6740
|
function moveCursor(deltaRow, deltaColumn) {
|
|
6146
6741
|
setCursorRow((row) => Math.min(Math.max(row + deltaRow, 0), rowCount - 1));
|
|
6147
6742
|
setCursorColumn((column) => Math.min(Math.max(column + deltaColumn, 0), columnCount - 1));
|
|
@@ -6179,7 +6774,7 @@ function OdsSpreadsheetGridScreen() {
|
|
|
6179
6774
|
sheetIndex
|
|
6180
6775
|
}
|
|
6181
6776
|
});
|
|
6182
|
-
}, { isActive: !overlayOpen && !
|
|
6777
|
+
}, { isActive: !overlayOpen && !editingAnything });
|
|
6183
6778
|
useInput((input, key) => {
|
|
6184
6779
|
if (key.upArrow || input === "k") {
|
|
6185
6780
|
moveCursor(-1, 0);
|
|
@@ -6229,6 +6824,16 @@ function OdsSpreadsheetGridScreen() {
|
|
|
6229
6824
|
else commitMerge(mergeAnchor);
|
|
6230
6825
|
return;
|
|
6231
6826
|
}
|
|
6827
|
+
if (input === "f") {
|
|
6828
|
+
const cell = cells.get(cellKey(clampedRow, clampedColumn));
|
|
6829
|
+
setFormulaDraft(cell?.formula ?? "");
|
|
6830
|
+
setFormulaEditing(true);
|
|
6831
|
+
return;
|
|
6832
|
+
}
|
|
6833
|
+
if (input === "i") {
|
|
6834
|
+
setImageWizardOpen(true);
|
|
6835
|
+
return;
|
|
6836
|
+
}
|
|
6232
6837
|
if (key.return) {
|
|
6233
6838
|
if (mergeAnchor !== void 0) {
|
|
6234
6839
|
commitMerge(mergeAnchor);
|
|
@@ -6240,7 +6845,7 @@ function OdsSpreadsheetGridScreen() {
|
|
|
6240
6845
|
return;
|
|
6241
6846
|
}
|
|
6242
6847
|
if (input.length === 1 && !key.ctrl && !key.meta && !RESERVED_LETTERS.has(input)) beginEdit(input, inferKind(input));
|
|
6243
|
-
}, { isActive: !overlayOpen && !
|
|
6848
|
+
}, { isActive: !overlayOpen && !editingAnything && viewMode === "grid" });
|
|
6244
6849
|
const compactRows = sheet === void 0 ? [] : sheet.cells.filter((cell) => cell.value.kind !== "empty").map((cell) => ({
|
|
6245
6850
|
row: cell.row,
|
|
6246
6851
|
column: cell.column,
|
|
@@ -6358,25 +6963,59 @@ function OdsSpreadsheetGridScreen() {
|
|
|
6358
6963
|
setEditSession(void 0);
|
|
6359
6964
|
}
|
|
6360
6965
|
}),
|
|
6361
|
-
/* @__PURE__ */
|
|
6362
|
-
|
|
6363
|
-
children:
|
|
6364
|
-
})
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6966
|
+
formulaEditing ? /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsxs(Text, {
|
|
6967
|
+
color: "cyan",
|
|
6968
|
+
children: [cursorAddress, " formula: "]
|
|
6969
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
6970
|
+
value: formulaDraft,
|
|
6971
|
+
isFocused: !overlayOpen,
|
|
6972
|
+
placeholder: "e.g. of:=[.A1]+[.A2]",
|
|
6973
|
+
onChange: setFormulaDraft,
|
|
6974
|
+
onSubmit: (value) => {
|
|
6975
|
+
const trimmed = value.trim();
|
|
6976
|
+
dispatch({
|
|
6977
|
+
type: "SET_CELL_FORMULA",
|
|
6978
|
+
sheetIndex,
|
|
6979
|
+
row: clampedRow,
|
|
6980
|
+
column: clampedColumn,
|
|
6981
|
+
formula: trimmed.length === 0 ? void 0 : trimmed
|
|
6982
|
+
});
|
|
6983
|
+
setFormulaEditing(false);
|
|
6984
|
+
},
|
|
6985
|
+
onCancel: () => {
|
|
6986
|
+
setFormulaEditing(false);
|
|
6987
|
+
}
|
|
6988
|
+
})] }) : void 0,
|
|
6989
|
+
imageWizardOpen ? /* @__PURE__ */ jsx(FieldWizard, {
|
|
6990
|
+
fields: SHEET_IMAGE_FIELDS,
|
|
6991
|
+
onCancel: () => {
|
|
6992
|
+
setImageWizardOpen(false);
|
|
6993
|
+
},
|
|
6994
|
+
onComplete: (values) => {
|
|
6995
|
+
applyAddSheetImage(sheetIndex, clampedRow, clampedColumn, values, dispatch).then(() => {
|
|
6996
|
+
setImageWizardOpen(false);
|
|
6997
|
+
});
|
|
6998
|
+
}
|
|
6999
|
+
}) : void 0,
|
|
7000
|
+
/* @__PURE__ */ jsx(Text, {
|
|
7001
|
+
dimColor: true,
|
|
7002
|
+
children: mergeAnchor === void 0 ? `hjkl/arrows move, Enter/type to edit, m to anchor a merge, p print settings, t ${viewMode === "grid" ? "compact list" : "grid"} view, f formula, i image, Esc back` : "hjkl/arrows to the opposite corner, m/Enter to merge, Esc to cancel"
|
|
7003
|
+
})
|
|
7004
|
+
]
|
|
7005
|
+
});
|
|
7006
|
+
}
|
|
7007
|
+
//#endregion
|
|
7008
|
+
//#region src/tui/screens/editors/odt/list-editor.tsx
|
|
7009
|
+
function ListEditorScreen() {
|
|
7010
|
+
const state = useAppState();
|
|
7011
|
+
const dispatch = useAppDispatch();
|
|
7012
|
+
const overlayOpen = anyOverlayOpen(state);
|
|
7013
|
+
const [isAdding, setIsAdding] = useState(false);
|
|
7014
|
+
const [newItemText, setNewItemText] = useState("");
|
|
7015
|
+
const [editingIndex, setEditingIndex] = useState(void 0);
|
|
7016
|
+
const screen = currentScreen(state);
|
|
7017
|
+
const doc = paragraphFamilyDocument(state.openDocument);
|
|
7018
|
+
const list = screen.kind === "listEditor" && doc?.format === "odt" ? doc.editor.lists()[screen.blockIndex] : void 0;
|
|
6380
7019
|
const items = list === void 0 ? [] : list.items();
|
|
6381
7020
|
const rows = items.map((item, index) => ({
|
|
6382
7021
|
item,
|
|
@@ -6539,9 +7178,58 @@ function requirePdfDocument(openDocument) {
|
|
|
6539
7178
|
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.");
|
|
6540
7179
|
return openDocument;
|
|
6541
7180
|
}
|
|
7181
|
+
function isEditablePdfDocument(doc) {
|
|
7182
|
+
return doc.format === "pdf";
|
|
7183
|
+
}
|
|
6542
7184
|
function formatSize(widthPt, heightPt) {
|
|
6543
7185
|
return `${widthPt.toFixed(0)}×${heightPt.toFixed(0)}pt`;
|
|
6544
7186
|
}
|
|
7187
|
+
function formatPt(value) {
|
|
7188
|
+
return value.toFixed(1);
|
|
7189
|
+
}
|
|
7190
|
+
function formatColor(color) {
|
|
7191
|
+
const byte = (component) => Math.round(component * 255).toString(16).padStart(2, "0");
|
|
7192
|
+
return `#${byte(color.r)}${byte(color.g)}${byte(color.b)}`;
|
|
7193
|
+
}
|
|
7194
|
+
function formatStroke(stroke) {
|
|
7195
|
+
return `${formatColor(stroke.color)} @ ${stroke.widthPt.toFixed(1)}pt`;
|
|
7196
|
+
}
|
|
7197
|
+
function parseRequiredColorField(raw, fallback) {
|
|
7198
|
+
return parseColorField(raw) ?? fallback;
|
|
7199
|
+
}
|
|
7200
|
+
function parseFontWeight(raw) {
|
|
7201
|
+
return raw.trim().toLowerCase() === "bold" ? "bold" : "normal";
|
|
7202
|
+
}
|
|
7203
|
+
function parseFontStyle(raw) {
|
|
7204
|
+
return raw.trim().toLowerCase() === "italic" ? "italic" : "normal";
|
|
7205
|
+
}
|
|
7206
|
+
function parseOptionalNumberField(raw) {
|
|
7207
|
+
const trimmed = raw.trim();
|
|
7208
|
+
if (trimmed.length === 0) return;
|
|
7209
|
+
const parsed = Number.parseFloat(trimmed);
|
|
7210
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
7211
|
+
}
|
|
7212
|
+
function defaultTriangleLayoutSubpaths(widthPt, heightPt) {
|
|
7213
|
+
return [{
|
|
7214
|
+
startXPt: 0,
|
|
7215
|
+
startYPt: heightPt,
|
|
7216
|
+
segments: [{
|
|
7217
|
+
kind: "line",
|
|
7218
|
+
xPt: widthPt / 2,
|
|
7219
|
+
yPt: 0
|
|
7220
|
+
}, {
|
|
7221
|
+
kind: "line",
|
|
7222
|
+
xPt: widthPt,
|
|
7223
|
+
yPt: heightPt
|
|
7224
|
+
}],
|
|
7225
|
+
closed: true
|
|
7226
|
+
}];
|
|
7227
|
+
}
|
|
7228
|
+
function inferImageFormat(path) {
|
|
7229
|
+
const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
|
|
7230
|
+
if (extension === "png") return "png";
|
|
7231
|
+
if (extension === "jpg" || extension === "jpeg") return "jpeg";
|
|
7232
|
+
}
|
|
6545
7233
|
//#endregion
|
|
6546
7234
|
//#region src/tui/screens/editors/pdf/page-list.tsx
|
|
6547
7235
|
const LAYOUT_ITEM_KIND_ORDER = [
|
|
@@ -6630,13 +7318,6 @@ function PdfPageListScreen() {
|
|
|
6630
7318
|
function formatPoint(xPt, yPt) {
|
|
6631
7319
|
return `(${xPt.toFixed(1)}, ${yPt.toFixed(1)})pt`;
|
|
6632
7320
|
}
|
|
6633
|
-
function formatColor(color) {
|
|
6634
|
-
const byte = (component) => Math.round(component * 255).toString(16).padStart(2, "0");
|
|
6635
|
-
return `#${byte(color.r)}${byte(color.g)}${byte(color.b)}`;
|
|
6636
|
-
}
|
|
6637
|
-
function formatStroke(stroke) {
|
|
6638
|
-
return `${formatColor(stroke.color)} @ ${stroke.widthPt.toFixed(1)}pt`;
|
|
6639
|
-
}
|
|
6640
7321
|
function fieldsFor(item) {
|
|
6641
7322
|
const fields = [["Kind", item.kind]];
|
|
6642
7323
|
switch (item.kind) {
|
|
@@ -6688,19 +7369,10 @@ function fieldsFor(item) {
|
|
|
6688
7369
|
if (item.sourcePath !== void 0) fields.push(["Source path", item.sourcePath]);
|
|
6689
7370
|
return fields;
|
|
6690
7371
|
}
|
|
6691
|
-
function
|
|
6692
|
-
const state = useAppState();
|
|
6693
|
-
const dispatch = useAppDispatch();
|
|
6694
|
-
const doc = requirePdfDocument(state.openDocument);
|
|
6695
|
-
const screen = currentScreen(state);
|
|
6696
|
-
if (screen.kind !== "pdfItemDetail") throw new Error(`PdfItemDetailScreen rendered while the current screen is "${screen.kind}", not "pdfItemDetail".`);
|
|
6697
|
-
const page = doc.layout.pages[screen.pageIndex];
|
|
6698
|
-
if (page === void 0) throw new Error(`pdfItemDetail was pushed for page ${screen.pageIndex}, but the open PDF has no page at that index.`);
|
|
6699
|
-
const item = page.items[screen.itemIndex];
|
|
6700
|
-
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.`);
|
|
7372
|
+
function ReadOnlyItemDetail(props) {
|
|
6701
7373
|
useInput((input, key) => {
|
|
6702
|
-
if (key.escape || key.leftArrow || input === "h")
|
|
6703
|
-
}, { isActive:
|
|
7374
|
+
if (key.escape || key.leftArrow || input === "h") props.onBack();
|
|
7375
|
+
}, { isActive: props.isActive });
|
|
6704
7376
|
return /* @__PURE__ */ jsxs(Box, {
|
|
6705
7377
|
flexDirection: "column",
|
|
6706
7378
|
children: [
|
|
@@ -6708,12 +7380,12 @@ function PdfItemDetailScreen() {
|
|
|
6708
7380
|
bold: true,
|
|
6709
7381
|
children: [
|
|
6710
7382
|
"Page ",
|
|
6711
|
-
|
|
7383
|
+
props.pageIndex + 1,
|
|
6712
7384
|
", item ",
|
|
6713
|
-
|
|
7385
|
+
props.itemIndex + 1
|
|
6714
7386
|
]
|
|
6715
7387
|
}),
|
|
6716
|
-
fieldsFor(item).map(([label, value]) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
7388
|
+
fieldsFor(props.item).map(([label, value]) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
6717
7389
|
label,
|
|
6718
7390
|
": ",
|
|
6719
7391
|
value
|
|
@@ -6725,109 +7397,996 @@ function PdfItemDetailScreen() {
|
|
|
6725
7397
|
]
|
|
6726
7398
|
});
|
|
6727
7399
|
}
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
7400
|
+
function buildFrameRows(frame, onFrameChange) {
|
|
7401
|
+
return [
|
|
7402
|
+
{
|
|
7403
|
+
label: `X: ${formatPt(frame.xPt)}pt`,
|
|
7404
|
+
currentValue: String(frame.xPt),
|
|
7405
|
+
commit: (raw) => onFrameChange({
|
|
7406
|
+
...frame,
|
|
7407
|
+
xPt: parseNumberField(raw, frame.xPt)
|
|
7408
|
+
})
|
|
7409
|
+
},
|
|
7410
|
+
{
|
|
7411
|
+
label: `Y: ${formatPt(frame.yPt)}pt`,
|
|
7412
|
+
currentValue: String(frame.yPt),
|
|
7413
|
+
commit: (raw) => onFrameChange({
|
|
7414
|
+
...frame,
|
|
7415
|
+
yPt: parseNumberField(raw, frame.yPt)
|
|
7416
|
+
})
|
|
7417
|
+
},
|
|
7418
|
+
{
|
|
7419
|
+
label: `Width: ${formatPt(frame.widthPt)}pt`,
|
|
7420
|
+
currentValue: String(frame.widthPt),
|
|
7421
|
+
commit: (raw) => onFrameChange({
|
|
7422
|
+
...frame,
|
|
7423
|
+
widthPt: parseNumberField(raw, frame.widthPt)
|
|
7424
|
+
})
|
|
7425
|
+
},
|
|
7426
|
+
{
|
|
7427
|
+
label: `Height: ${formatPt(frame.heightPt)}pt`,
|
|
7428
|
+
currentValue: String(frame.heightPt),
|
|
7429
|
+
commit: (raw) => onFrameChange({
|
|
7430
|
+
...frame,
|
|
7431
|
+
heightPt: parseNumberField(raw, frame.heightPt)
|
|
7432
|
+
})
|
|
6753
7433
|
}
|
|
6754
|
-
|
|
6755
|
-
if (minX > maxX) return "empty path";
|
|
6756
|
-
return formatSize(maxX - minX, maxY - minY);
|
|
7434
|
+
];
|
|
6757
7435
|
}
|
|
6758
|
-
function
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
}
|
|
7436
|
+
function buildFillStrokeRows(fill, stroke, onFillChange, onStrokeChange) {
|
|
7437
|
+
return [{
|
|
7438
|
+
label: `Fill: ${fill === void 0 ? "none" : formatColor(fill)}`,
|
|
7439
|
+
currentValue: fill === void 0 ? "" : `${fill.r} ${fill.g} ${fill.b}`,
|
|
7440
|
+
commit: (raw) => onFillChange(parseColorField(raw))
|
|
7441
|
+
}, {
|
|
7442
|
+
label: `Stroke: ${stroke === void 0 ? "none" : formatStroke(stroke)}`,
|
|
7443
|
+
currentValue: stroke === void 0 ? "" : `${stroke.color.r} ${stroke.color.g} ${stroke.color.b} ${stroke.widthPt}`,
|
|
7444
|
+
commit: (raw) => onStrokeChange(parseStrokeField(raw))
|
|
7445
|
+
}];
|
|
6768
7446
|
}
|
|
6769
|
-
function
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
7447
|
+
function buildTextRows(item, pageIndex, itemIndex, dispatch) {
|
|
7448
|
+
return [
|
|
7449
|
+
{
|
|
7450
|
+
label: `Text: ${item.text}`,
|
|
7451
|
+
currentValue: item.text,
|
|
7452
|
+
commit: (raw) => dispatch({
|
|
7453
|
+
type: "SET_PDF_TEXT_TEXT",
|
|
7454
|
+
pageIndex,
|
|
7455
|
+
itemIndex,
|
|
7456
|
+
text: raw
|
|
7457
|
+
})
|
|
7458
|
+
},
|
|
7459
|
+
{
|
|
7460
|
+
label: `X: ${formatPt(item.xPt)}pt`,
|
|
7461
|
+
currentValue: String(item.xPt),
|
|
7462
|
+
commit: (raw) => dispatch({
|
|
7463
|
+
type: "SET_PDF_TEXT_POSITION",
|
|
7464
|
+
pageIndex,
|
|
7465
|
+
itemIndex,
|
|
7466
|
+
xPt: parseNumberField(raw, item.xPt),
|
|
7467
|
+
yPt: item.yPt
|
|
7468
|
+
})
|
|
7469
|
+
},
|
|
7470
|
+
{
|
|
7471
|
+
label: `Y: ${formatPt(item.yPt)}pt`,
|
|
7472
|
+
currentValue: String(item.yPt),
|
|
7473
|
+
commit: (raw) => dispatch({
|
|
7474
|
+
type: "SET_PDF_TEXT_POSITION",
|
|
7475
|
+
pageIndex,
|
|
7476
|
+
itemIndex,
|
|
7477
|
+
xPt: item.xPt,
|
|
7478
|
+
yPt: parseNumberField(raw, item.yPt)
|
|
7479
|
+
})
|
|
7480
|
+
},
|
|
7481
|
+
{
|
|
7482
|
+
label: `Font family: ${item.font.family}`,
|
|
7483
|
+
currentValue: item.font.family,
|
|
7484
|
+
commit: (raw) => {
|
|
7485
|
+
const family = raw.trim();
|
|
7486
|
+
dispatch({
|
|
7487
|
+
type: "SET_PDF_TEXT_FONT",
|
|
7488
|
+
pageIndex,
|
|
7489
|
+
itemIndex,
|
|
7490
|
+
font: {
|
|
7491
|
+
...item.font,
|
|
7492
|
+
family: family.length === 0 ? item.font.family : family
|
|
7493
|
+
}
|
|
7494
|
+
});
|
|
7495
|
+
}
|
|
7496
|
+
},
|
|
7497
|
+
{
|
|
7498
|
+
label: `Font weight: ${item.font.weight} (Enter to toggle)`,
|
|
7499
|
+
currentValue: "",
|
|
7500
|
+
activate: () => dispatch({
|
|
7501
|
+
type: "SET_PDF_TEXT_FONT",
|
|
7502
|
+
pageIndex,
|
|
7503
|
+
itemIndex,
|
|
7504
|
+
font: {
|
|
7505
|
+
...item.font,
|
|
7506
|
+
weight: item.font.weight === "bold" ? "normal" : "bold"
|
|
6794
7507
|
}
|
|
6795
|
-
})
|
|
7508
|
+
})
|
|
6796
7509
|
},
|
|
6797
|
-
|
|
6798
|
-
|
|
7510
|
+
{
|
|
7511
|
+
label: `Font style: ${item.font.style} (Enter to toggle)`,
|
|
7512
|
+
currentValue: "",
|
|
7513
|
+
activate: () => dispatch({
|
|
7514
|
+
type: "SET_PDF_TEXT_FONT",
|
|
7515
|
+
pageIndex,
|
|
7516
|
+
itemIndex,
|
|
7517
|
+
font: {
|
|
7518
|
+
...item.font,
|
|
7519
|
+
style: item.font.style === "italic" ? "normal" : "italic"
|
|
7520
|
+
}
|
|
7521
|
+
})
|
|
6799
7522
|
},
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
screen.pageIndex + 1,
|
|
6809
|
-
" items (",
|
|
6810
|
-
items.length,
|
|
6811
|
-
" of ",
|
|
6812
|
-
page.items.length,
|
|
6813
|
-
")"
|
|
6814
|
-
]
|
|
6815
|
-
}), /* @__PURE__ */ jsx(ListView, {
|
|
6816
|
-
items,
|
|
6817
|
-
selectedIndex,
|
|
6818
|
-
emptyMessage: query === "" ? "This page has no items." : `No items match "${state.searchQuery}".`,
|
|
6819
|
-
renderItem: ({ item, itemIndex }, isSelected) => /* @__PURE__ */ jsxs(Text, {
|
|
6820
|
-
color: isSelected ? "cyan" : void 0,
|
|
6821
|
-
inverse: isSelected,
|
|
6822
|
-
children: [
|
|
6823
|
-
itemIndex + 1,
|
|
6824
|
-
". ",
|
|
6825
|
-
item.kind,
|
|
6826
|
-
" -- ",
|
|
6827
|
-
previewFor(item)
|
|
6828
|
-
]
|
|
7523
|
+
{
|
|
7524
|
+
label: `Size: ${item.sizePt}pt`,
|
|
7525
|
+
currentValue: String(item.sizePt),
|
|
7526
|
+
commit: (raw) => dispatch({
|
|
7527
|
+
type: "SET_PDF_TEXT_SIZE",
|
|
7528
|
+
pageIndex,
|
|
7529
|
+
itemIndex,
|
|
7530
|
+
sizePt: parseNumberField(raw, item.sizePt)
|
|
6829
7531
|
})
|
|
6830
|
-
}
|
|
7532
|
+
},
|
|
7533
|
+
{
|
|
7534
|
+
label: `Colour: ${formatColor(item.color)}`,
|
|
7535
|
+
currentValue: `${item.color.r} ${item.color.g} ${item.color.b}`,
|
|
7536
|
+
commit: (raw) => dispatch({
|
|
7537
|
+
type: "SET_PDF_TEXT_COLOR",
|
|
7538
|
+
pageIndex,
|
|
7539
|
+
itemIndex,
|
|
7540
|
+
color: parseRequiredColorField(raw, item.color)
|
|
7541
|
+
})
|
|
7542
|
+
},
|
|
7543
|
+
{
|
|
7544
|
+
label: `Width: ${item.widthPt === void 0 ? "unset" : `${item.widthPt}pt`}`,
|
|
7545
|
+
currentValue: item.widthPt === void 0 ? "" : String(item.widthPt),
|
|
7546
|
+
commit: (raw) => dispatch({
|
|
7547
|
+
type: "SET_PDF_TEXT_WIDTH",
|
|
7548
|
+
pageIndex,
|
|
7549
|
+
itemIndex,
|
|
7550
|
+
widthPt: parseOptionalNumberField(raw)
|
|
7551
|
+
})
|
|
7552
|
+
},
|
|
7553
|
+
{
|
|
7554
|
+
label: `Rotation: ${item.rotationDeg === void 0 ? "unset" : `${item.rotationDeg}°`}`,
|
|
7555
|
+
currentValue: item.rotationDeg === void 0 ? "" : String(item.rotationDeg),
|
|
7556
|
+
commit: (raw) => dispatch({
|
|
7557
|
+
type: "SET_PDF_TEXT_ROTATION",
|
|
7558
|
+
pageIndex,
|
|
7559
|
+
itemIndex,
|
|
7560
|
+
rotationDeg: parseOptionalNumberField(raw)
|
|
7561
|
+
})
|
|
7562
|
+
},
|
|
7563
|
+
{
|
|
7564
|
+
label: `Underline: ${item.underline === true ? "yes" : "no"} (Enter to toggle)`,
|
|
7565
|
+
currentValue: "",
|
|
7566
|
+
activate: () => dispatch({
|
|
7567
|
+
type: "TOGGLE_PDF_TEXT_UNDERLINE",
|
|
7568
|
+
pageIndex,
|
|
7569
|
+
itemIndex
|
|
7570
|
+
})
|
|
7571
|
+
}
|
|
7572
|
+
];
|
|
7573
|
+
}
|
|
7574
|
+
function buildRectRows(item, pageIndex, itemIndex, dispatch) {
|
|
7575
|
+
return [...buildFrameRows(item, (frame) => dispatch({
|
|
7576
|
+
type: "SET_PDF_RECT_FRAME",
|
|
7577
|
+
pageIndex,
|
|
7578
|
+
itemIndex,
|
|
7579
|
+
...frame
|
|
7580
|
+
})), ...buildFillStrokeRows(item.fill, item.stroke, (fill) => dispatch({
|
|
7581
|
+
type: "SET_PDF_RECT_FILL",
|
|
7582
|
+
pageIndex,
|
|
7583
|
+
itemIndex,
|
|
7584
|
+
fill
|
|
7585
|
+
}), (stroke) => dispatch({
|
|
7586
|
+
type: "SET_PDF_RECT_STROKE",
|
|
7587
|
+
pageIndex,
|
|
7588
|
+
itemIndex,
|
|
7589
|
+
stroke
|
|
7590
|
+
}))];
|
|
7591
|
+
}
|
|
7592
|
+
function buildEllipseRows(item, pageIndex, itemIndex, dispatch) {
|
|
7593
|
+
return [...buildFrameRows(item, (frame) => dispatch({
|
|
7594
|
+
type: "SET_PDF_ELLIPSE_FRAME",
|
|
7595
|
+
pageIndex,
|
|
7596
|
+
itemIndex,
|
|
7597
|
+
...frame
|
|
7598
|
+
})), ...buildFillStrokeRows(item.fill, item.stroke, (fill) => dispatch({
|
|
7599
|
+
type: "SET_PDF_ELLIPSE_FILL",
|
|
7600
|
+
pageIndex,
|
|
7601
|
+
itemIndex,
|
|
7602
|
+
fill
|
|
7603
|
+
}), (stroke) => dispatch({
|
|
7604
|
+
type: "SET_PDF_ELLIPSE_STROKE",
|
|
7605
|
+
pageIndex,
|
|
7606
|
+
itemIndex,
|
|
7607
|
+
stroke
|
|
7608
|
+
}))];
|
|
7609
|
+
}
|
|
7610
|
+
function buildLineRows(item, pageIndex, itemIndex, dispatch) {
|
|
7611
|
+
return [
|
|
7612
|
+
{
|
|
7613
|
+
label: `From X: ${formatPt(item.x1Pt)}pt`,
|
|
7614
|
+
currentValue: String(item.x1Pt),
|
|
7615
|
+
commit: (raw) => dispatch({
|
|
7616
|
+
type: "SET_PDF_LINE_FROM",
|
|
7617
|
+
pageIndex,
|
|
7618
|
+
itemIndex,
|
|
7619
|
+
x1Pt: parseNumberField(raw, item.x1Pt),
|
|
7620
|
+
y1Pt: item.y1Pt
|
|
7621
|
+
})
|
|
7622
|
+
},
|
|
7623
|
+
{
|
|
7624
|
+
label: `From Y: ${formatPt(item.y1Pt)}pt`,
|
|
7625
|
+
currentValue: String(item.y1Pt),
|
|
7626
|
+
commit: (raw) => dispatch({
|
|
7627
|
+
type: "SET_PDF_LINE_FROM",
|
|
7628
|
+
pageIndex,
|
|
7629
|
+
itemIndex,
|
|
7630
|
+
x1Pt: item.x1Pt,
|
|
7631
|
+
y1Pt: parseNumberField(raw, item.y1Pt)
|
|
7632
|
+
})
|
|
7633
|
+
},
|
|
7634
|
+
{
|
|
7635
|
+
label: `To X: ${formatPt(item.x2Pt)}pt`,
|
|
7636
|
+
currentValue: String(item.x2Pt),
|
|
7637
|
+
commit: (raw) => dispatch({
|
|
7638
|
+
type: "SET_PDF_LINE_TO",
|
|
7639
|
+
pageIndex,
|
|
7640
|
+
itemIndex,
|
|
7641
|
+
x2Pt: parseNumberField(raw, item.x2Pt),
|
|
7642
|
+
y2Pt: item.y2Pt
|
|
7643
|
+
})
|
|
7644
|
+
},
|
|
7645
|
+
{
|
|
7646
|
+
label: `To Y: ${formatPt(item.y2Pt)}pt`,
|
|
7647
|
+
currentValue: String(item.y2Pt),
|
|
7648
|
+
commit: (raw) => dispatch({
|
|
7649
|
+
type: "SET_PDF_LINE_TO",
|
|
7650
|
+
pageIndex,
|
|
7651
|
+
itemIndex,
|
|
7652
|
+
x2Pt: item.x2Pt,
|
|
7653
|
+
y2Pt: parseNumberField(raw, item.y2Pt)
|
|
7654
|
+
})
|
|
7655
|
+
},
|
|
7656
|
+
{
|
|
7657
|
+
label: `Colour: ${formatColor(item.color)}`,
|
|
7658
|
+
currentValue: `${item.color.r} ${item.color.g} ${item.color.b}`,
|
|
7659
|
+
commit: (raw) => dispatch({
|
|
7660
|
+
type: "SET_PDF_LINE_COLOR",
|
|
7661
|
+
pageIndex,
|
|
7662
|
+
itemIndex,
|
|
7663
|
+
color: parseRequiredColorField(raw, item.color)
|
|
7664
|
+
})
|
|
7665
|
+
},
|
|
7666
|
+
{
|
|
7667
|
+
label: `Width: ${item.widthPt}pt`,
|
|
7668
|
+
currentValue: String(item.widthPt),
|
|
7669
|
+
commit: (raw) => dispatch({
|
|
7670
|
+
type: "SET_PDF_LINE_WIDTH",
|
|
7671
|
+
pageIndex,
|
|
7672
|
+
itemIndex,
|
|
7673
|
+
widthPt: Math.max(parseNumberField(raw, item.widthPt), Number.EPSILON)
|
|
7674
|
+
})
|
|
7675
|
+
}
|
|
7676
|
+
];
|
|
7677
|
+
}
|
|
7678
|
+
function buildPathRows(item, pageIndex, itemIndex, dispatch) {
|
|
7679
|
+
return [{
|
|
7680
|
+
label: `Fill rule: ${item.fillRule ?? "nonzero (default)"} (Enter to cycle)`,
|
|
7681
|
+
currentValue: "",
|
|
7682
|
+
activate: () => dispatch({
|
|
7683
|
+
type: "SET_PDF_PATH_FILL_RULE",
|
|
7684
|
+
pageIndex,
|
|
7685
|
+
itemIndex,
|
|
7686
|
+
fillRule: item.fillRule === "evenodd" ? void 0 : item.fillRule === "nonzero" ? "evenodd" : "nonzero"
|
|
7687
|
+
})
|
|
7688
|
+
}, ...buildFillStrokeRows(item.fill, item.stroke, (fill) => dispatch({
|
|
7689
|
+
type: "SET_PDF_PATH_FILL",
|
|
7690
|
+
pageIndex,
|
|
7691
|
+
itemIndex,
|
|
7692
|
+
fill
|
|
7693
|
+
}), (stroke) => dispatch({
|
|
7694
|
+
type: "SET_PDF_PATH_STROKE",
|
|
7695
|
+
pageIndex,
|
|
7696
|
+
itemIndex,
|
|
7697
|
+
stroke
|
|
7698
|
+
}))];
|
|
7699
|
+
}
|
|
7700
|
+
function buildImageRows(item, pageIndex, itemIndex, dispatch, onReplaceImage) {
|
|
7701
|
+
return [
|
|
7702
|
+
...buildFrameRows(item, (frame) => dispatch({
|
|
7703
|
+
type: "SET_PDF_IMAGE_FRAME",
|
|
7704
|
+
pageIndex,
|
|
7705
|
+
itemIndex,
|
|
7706
|
+
...frame
|
|
7707
|
+
})),
|
|
7708
|
+
{
|
|
7709
|
+
label: `Rotation: ${item.rotationDeg === void 0 ? "unset" : `${item.rotationDeg}°`}`,
|
|
7710
|
+
currentValue: item.rotationDeg === void 0 ? "" : String(item.rotationDeg),
|
|
7711
|
+
commit: (raw) => dispatch({
|
|
7712
|
+
type: "SET_PDF_IMAGE_ROTATION",
|
|
7713
|
+
pageIndex,
|
|
7714
|
+
itemIndex,
|
|
7715
|
+
rotationDeg: parseOptionalNumberField(raw)
|
|
7716
|
+
})
|
|
7717
|
+
},
|
|
7718
|
+
{
|
|
7719
|
+
label: "Replace image...",
|
|
7720
|
+
currentValue: "",
|
|
7721
|
+
activate: onReplaceImage
|
|
7722
|
+
}
|
|
7723
|
+
];
|
|
7724
|
+
}
|
|
7725
|
+
function buildLinkRows(item, pageIndex, itemIndex, dispatch) {
|
|
7726
|
+
return [{
|
|
7727
|
+
label: `URI: ${item.uri}`,
|
|
7728
|
+
currentValue: item.uri,
|
|
7729
|
+
commit: (raw) => dispatch({
|
|
7730
|
+
type: "SET_PDF_LINK_URI",
|
|
7731
|
+
pageIndex,
|
|
7732
|
+
itemIndex,
|
|
7733
|
+
uri: raw
|
|
7734
|
+
})
|
|
7735
|
+
}, ...buildFrameRows(item, (frame) => dispatch({
|
|
7736
|
+
type: "SET_PDF_LINK_FRAME",
|
|
7737
|
+
pageIndex,
|
|
7738
|
+
itemIndex,
|
|
7739
|
+
...frame
|
|
7740
|
+
}))];
|
|
7741
|
+
}
|
|
7742
|
+
function buildRowsFor(item, pageIndex, itemIndex, dispatch, onReplaceImage) {
|
|
7743
|
+
switch (item.kind) {
|
|
7744
|
+
case "text": return buildTextRows(item, pageIndex, itemIndex, dispatch);
|
|
7745
|
+
case "rect": return buildRectRows(item, pageIndex, itemIndex, dispatch);
|
|
7746
|
+
case "ellipse": return buildEllipseRows(item, pageIndex, itemIndex, dispatch);
|
|
7747
|
+
case "line": return buildLineRows(item, pageIndex, itemIndex, dispatch);
|
|
7748
|
+
case "path": return buildPathRows(item, pageIndex, itemIndex, dispatch);
|
|
7749
|
+
case "image": return buildImageRows(item, pageIndex, itemIndex, dispatch, onReplaceImage);
|
|
7750
|
+
case "link": return buildLinkRows(item, pageIndex, itemIndex, dispatch);
|
|
7751
|
+
}
|
|
7752
|
+
}
|
|
7753
|
+
function pathSummary(item) {
|
|
7754
|
+
const segmentCount = item.subpaths.reduce((total, subpath) => total + subpath.segments.length, 0);
|
|
7755
|
+
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)`;
|
|
7756
|
+
}
|
|
7757
|
+
async function applyImageReplace(pageIndex, itemIndex, path, dispatch) {
|
|
7758
|
+
const format = inferImageFormat(path);
|
|
7759
|
+
if (format === void 0) {
|
|
7760
|
+
dispatch({
|
|
7761
|
+
type: "SET_STATUS",
|
|
7762
|
+
severity: "warning",
|
|
7763
|
+
text: `${path} is not a .png or .jpg/.jpeg file -- image not replaced`
|
|
7764
|
+
});
|
|
7765
|
+
return;
|
|
7766
|
+
}
|
|
7767
|
+
try {
|
|
7768
|
+
dispatch({
|
|
7769
|
+
type: "SET_PDF_IMAGE_SOURCE",
|
|
7770
|
+
pageIndex,
|
|
7771
|
+
itemIndex,
|
|
7772
|
+
format,
|
|
7773
|
+
bytes: new Uint8Array(await readInput(path))
|
|
7774
|
+
});
|
|
7775
|
+
} catch (error) {
|
|
7776
|
+
dispatch({
|
|
7777
|
+
type: "SET_STATUS",
|
|
7778
|
+
severity: "error",
|
|
7779
|
+
text: `Could not read ${path}: ${describeError(error)}`
|
|
7780
|
+
});
|
|
7781
|
+
}
|
|
7782
|
+
}
|
|
7783
|
+
function EditableItemDetail(props) {
|
|
7784
|
+
const dispatch = useAppDispatch();
|
|
7785
|
+
const { doc, pageIndex, itemIndex } = props;
|
|
7786
|
+
const [editingField, setEditingField] = useState(void 0);
|
|
7787
|
+
const [draft, setDraft] = useState("");
|
|
7788
|
+
const [replacingImage, setReplacingImage] = useState(false);
|
|
7789
|
+
const item = doc.editor.page(pageIndex)?.items()[itemIndex];
|
|
7790
|
+
const rows = item === void 0 ? [] : buildRowsFor(item, pageIndex, itemIndex, dispatch, () => setReplacingImage(true));
|
|
7791
|
+
const { selectedIndex } = useNavigationInput({
|
|
7792
|
+
itemCount: rows.length,
|
|
7793
|
+
isActive: props.isActive && editingField === void 0 && !replacingImage,
|
|
7794
|
+
onBack: () => {
|
|
7795
|
+
dispatch({ type: "POP_SCREEN" });
|
|
7796
|
+
},
|
|
7797
|
+
onSelect: (index) => {
|
|
7798
|
+
const row = rows[index];
|
|
7799
|
+
if (row === void 0) return;
|
|
7800
|
+
if (row.activate !== void 0) {
|
|
7801
|
+
row.activate();
|
|
7802
|
+
return;
|
|
7803
|
+
}
|
|
7804
|
+
setDraft(row.currentValue);
|
|
7805
|
+
setEditingField(index);
|
|
7806
|
+
}
|
|
7807
|
+
});
|
|
7808
|
+
if (item === void 0) return /* @__PURE__ */ jsxs(Box, {
|
|
7809
|
+
flexDirection: "column",
|
|
7810
|
+
children: [/* @__PURE__ */ jsxs(Text, {
|
|
7811
|
+
color: "yellow",
|
|
7812
|
+
children: [
|
|
7813
|
+
"There is no item ",
|
|
7814
|
+
itemIndex + 1,
|
|
7815
|
+
" on page ",
|
|
7816
|
+
pageIndex + 1,
|
|
7817
|
+
" any more."
|
|
7818
|
+
]
|
|
7819
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
7820
|
+
dimColor: true,
|
|
7821
|
+
children: "Esc to go back"
|
|
7822
|
+
})]
|
|
7823
|
+
});
|
|
7824
|
+
if (replacingImage) return /* @__PURE__ */ jsx(FieldWizard, {
|
|
7825
|
+
fields: [{
|
|
7826
|
+
key: "path",
|
|
7827
|
+
label: "Image file path (.png/.jpg/.jpeg)",
|
|
7828
|
+
defaultValue: ""
|
|
7829
|
+
}],
|
|
7830
|
+
onCancel: () => {
|
|
7831
|
+
setReplacingImage(false);
|
|
7832
|
+
},
|
|
7833
|
+
onComplete: (values) => {
|
|
7834
|
+
applyImageReplace(pageIndex, itemIndex, requireFieldValue(values, "path"), dispatch).then(() => {
|
|
7835
|
+
setReplacingImage(false);
|
|
7836
|
+
});
|
|
7837
|
+
}
|
|
7838
|
+
});
|
|
7839
|
+
if (editingField !== void 0) {
|
|
7840
|
+
const row = rows[editingField];
|
|
7841
|
+
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.`);
|
|
7842
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
7843
|
+
flexDirection: "column",
|
|
7844
|
+
borderStyle: "round",
|
|
7845
|
+
paddingX: 1,
|
|
7846
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
7847
|
+
bold: true,
|
|
7848
|
+
children: row.label
|
|
7849
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
7850
|
+
value: draft,
|
|
7851
|
+
isFocused: true,
|
|
7852
|
+
onChange: setDraft,
|
|
7853
|
+
onCancel: () => {
|
|
7854
|
+
setEditingField(void 0);
|
|
7855
|
+
},
|
|
7856
|
+
onSubmit: (value) => {
|
|
7857
|
+
row.commit?.(value);
|
|
7858
|
+
setEditingField(void 0);
|
|
7859
|
+
}
|
|
7860
|
+
})]
|
|
7861
|
+
});
|
|
7862
|
+
}
|
|
7863
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
7864
|
+
flexDirection: "column",
|
|
7865
|
+
children: [
|
|
7866
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
7867
|
+
bold: true,
|
|
7868
|
+
children: [
|
|
7869
|
+
"Page ",
|
|
7870
|
+
pageIndex + 1,
|
|
7871
|
+
", item ",
|
|
7872
|
+
itemIndex + 1,
|
|
7873
|
+
" -- ",
|
|
7874
|
+
item.kind
|
|
7875
|
+
]
|
|
7876
|
+
}),
|
|
7877
|
+
item.kind === "path" && /* @__PURE__ */ jsx(Text, {
|
|
7878
|
+
dimColor: true,
|
|
7879
|
+
children: pathSummary(item)
|
|
7880
|
+
}),
|
|
7881
|
+
item.kind === "image" && /* @__PURE__ */ jsxs(Text, {
|
|
7882
|
+
dimColor: true,
|
|
7883
|
+
children: ["Image ID: ", item.imageId]
|
|
7884
|
+
}),
|
|
7885
|
+
/* @__PURE__ */ jsx(ListView, {
|
|
7886
|
+
items: rows,
|
|
7887
|
+
selectedIndex,
|
|
7888
|
+
reservedRows: 5,
|
|
7889
|
+
renderItem: (row, isSelected) => /* @__PURE__ */ jsx(Text, {
|
|
7890
|
+
color: isSelected ? "cyan" : void 0,
|
|
7891
|
+
inverse: isSelected,
|
|
7892
|
+
children: row.label
|
|
7893
|
+
})
|
|
7894
|
+
}),
|
|
7895
|
+
/* @__PURE__ */ jsx(Text, {
|
|
7896
|
+
dimColor: true,
|
|
7897
|
+
children: "Enter to edit a field, Esc to go back"
|
|
7898
|
+
})
|
|
7899
|
+
]
|
|
7900
|
+
});
|
|
7901
|
+
}
|
|
7902
|
+
function PdfItemDetailScreen() {
|
|
7903
|
+
const state = useAppState();
|
|
7904
|
+
const dispatch = useAppDispatch();
|
|
7905
|
+
const doc = requirePdfDocument(state.openDocument);
|
|
7906
|
+
const screen = currentScreen(state);
|
|
7907
|
+
if (screen.kind !== "pdfItemDetail") throw new Error(`PdfItemDetailScreen rendered while the current screen is "${screen.kind}", not "pdfItemDetail".`);
|
|
7908
|
+
const isActive = !anyOverlayOpen(state);
|
|
7909
|
+
if (!isEditablePdfDocument(doc)) {
|
|
7910
|
+
const page = doc.layout.pages[screen.pageIndex];
|
|
7911
|
+
if (page === void 0) throw new Error(`pdfItemDetail was pushed for page ${screen.pageIndex}, but the open PDF has no page at that index.`);
|
|
7912
|
+
const item = page.items[screen.itemIndex];
|
|
7913
|
+
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.`);
|
|
7914
|
+
return /* @__PURE__ */ jsx(ReadOnlyItemDetail, {
|
|
7915
|
+
item,
|
|
7916
|
+
pageIndex: screen.pageIndex,
|
|
7917
|
+
itemIndex: screen.itemIndex,
|
|
7918
|
+
isActive,
|
|
7919
|
+
onBack: () => {
|
|
7920
|
+
dispatch({ type: "POP_SCREEN" });
|
|
7921
|
+
}
|
|
7922
|
+
});
|
|
7923
|
+
}
|
|
7924
|
+
return /* @__PURE__ */ jsx(EditableItemDetail, {
|
|
7925
|
+
doc,
|
|
7926
|
+
pageIndex: screen.pageIndex,
|
|
7927
|
+
itemIndex: screen.itemIndex,
|
|
7928
|
+
isActive
|
|
7929
|
+
});
|
|
7930
|
+
}
|
|
7931
|
+
//#endregion
|
|
7932
|
+
//#region src/tui/screens/editors/pdf/page-items.tsx
|
|
7933
|
+
const TEXT_PREVIEW_MAX_CHARS = 48;
|
|
7934
|
+
function truncate(text, maxChars) {
|
|
7935
|
+
return text.length > maxChars ? `${text.slice(0, maxChars)}…` : text;
|
|
7936
|
+
}
|
|
7937
|
+
function pathDimensions(item) {
|
|
7938
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
7939
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
7940
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
7941
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
7942
|
+
const consider = (xPt, yPt) => {
|
|
7943
|
+
minX = Math.min(minX, xPt);
|
|
7944
|
+
minY = Math.min(minY, yPt);
|
|
7945
|
+
maxX = Math.max(maxX, xPt);
|
|
7946
|
+
maxY = Math.max(maxY, yPt);
|
|
7947
|
+
};
|
|
7948
|
+
for (const subpath of item.subpaths) {
|
|
7949
|
+
consider(subpath.startXPt, subpath.startYPt);
|
|
7950
|
+
for (const segment of subpath.segments) {
|
|
7951
|
+
if (segment.kind === "cubic") {
|
|
7952
|
+
consider(segment.c1xPt, segment.c1yPt);
|
|
7953
|
+
consider(segment.c2xPt, segment.c2yPt);
|
|
7954
|
+
}
|
|
7955
|
+
consider(segment.xPt, segment.yPt);
|
|
7956
|
+
}
|
|
7957
|
+
}
|
|
7958
|
+
if (minX > maxX) return "empty path";
|
|
7959
|
+
return formatSize(maxX - minX, maxY - minY);
|
|
7960
|
+
}
|
|
7961
|
+
function previewFor(item) {
|
|
7962
|
+
switch (item.kind) {
|
|
7963
|
+
case "text": return truncate(item.text, TEXT_PREVIEW_MAX_CHARS);
|
|
7964
|
+
case "link": return item.uri;
|
|
7965
|
+
case "image":
|
|
7966
|
+
case "rect":
|
|
7967
|
+
case "ellipse": return formatSize(item.widthPt, item.heightPt);
|
|
7968
|
+
case "line": return formatSize(Math.abs(item.x2Pt - item.x1Pt), Math.abs(item.y2Pt - item.y1Pt));
|
|
7969
|
+
case "path": return pathDimensions(item);
|
|
7970
|
+
}
|
|
7971
|
+
}
|
|
7972
|
+
const ADD_KIND_OPTIONS = [
|
|
7973
|
+
{
|
|
7974
|
+
kind: "text",
|
|
7975
|
+
label: "Text"
|
|
7976
|
+
},
|
|
7977
|
+
{
|
|
7978
|
+
kind: "rect",
|
|
7979
|
+
label: "Rectangle"
|
|
7980
|
+
},
|
|
7981
|
+
{
|
|
7982
|
+
kind: "ellipse",
|
|
7983
|
+
label: "Ellipse"
|
|
7984
|
+
},
|
|
7985
|
+
{
|
|
7986
|
+
kind: "line",
|
|
7987
|
+
label: "Line"
|
|
7988
|
+
},
|
|
7989
|
+
{
|
|
7990
|
+
kind: "path",
|
|
7991
|
+
label: "Path (fixed triangle shape)"
|
|
7992
|
+
},
|
|
7993
|
+
{
|
|
7994
|
+
kind: "image",
|
|
7995
|
+
label: "Image"
|
|
7996
|
+
},
|
|
7997
|
+
{
|
|
7998
|
+
kind: "link",
|
|
7999
|
+
label: "Link"
|
|
8000
|
+
}
|
|
8001
|
+
];
|
|
8002
|
+
const GEOMETRY_FIELDS = [
|
|
8003
|
+
{
|
|
8004
|
+
key: "xPt",
|
|
8005
|
+
label: "X (pt)",
|
|
8006
|
+
defaultValue: "40"
|
|
8007
|
+
},
|
|
8008
|
+
{
|
|
8009
|
+
key: "yPt",
|
|
8010
|
+
label: "Y (pt)",
|
|
8011
|
+
defaultValue: "40"
|
|
8012
|
+
},
|
|
8013
|
+
{
|
|
8014
|
+
key: "widthPt",
|
|
8015
|
+
label: "Width (pt)",
|
|
8016
|
+
defaultValue: "160"
|
|
8017
|
+
},
|
|
8018
|
+
{
|
|
8019
|
+
key: "heightPt",
|
|
8020
|
+
label: "Height (pt)",
|
|
8021
|
+
defaultValue: "100"
|
|
8022
|
+
}
|
|
8023
|
+
];
|
|
8024
|
+
const FILL_FIELD = {
|
|
8025
|
+
key: "fill",
|
|
8026
|
+
label: "Fill \"r g b\" (0-1 each), blank for none",
|
|
8027
|
+
defaultValue: "0.8 0.8 0.8"
|
|
8028
|
+
};
|
|
8029
|
+
const STROKE_FIELD = {
|
|
8030
|
+
key: "stroke",
|
|
8031
|
+
label: "Stroke \"r g b widthPt\" (0-1 colour, pt width), blank for none",
|
|
8032
|
+
defaultValue: "0 0 0 1"
|
|
8033
|
+
};
|
|
8034
|
+
const REQUIRED_COLOR_FIELD = {
|
|
8035
|
+
key: "color",
|
|
8036
|
+
label: "Colour \"r g b\" (0-1 each)",
|
|
8037
|
+
defaultValue: "0 0 0"
|
|
8038
|
+
};
|
|
8039
|
+
function fieldsForAddKind(kind) {
|
|
8040
|
+
switch (kind) {
|
|
8041
|
+
case "text": return [
|
|
8042
|
+
{
|
|
8043
|
+
key: "xPt",
|
|
8044
|
+
label: "X (pt)",
|
|
8045
|
+
defaultValue: "40"
|
|
8046
|
+
},
|
|
8047
|
+
{
|
|
8048
|
+
key: "yPt",
|
|
8049
|
+
label: "Y (pt)",
|
|
8050
|
+
defaultValue: "40"
|
|
8051
|
+
},
|
|
8052
|
+
{
|
|
8053
|
+
key: "text",
|
|
8054
|
+
label: "Text",
|
|
8055
|
+
defaultValue: "Text"
|
|
8056
|
+
},
|
|
8057
|
+
{
|
|
8058
|
+
key: "fontFamily",
|
|
8059
|
+
label: "Font family",
|
|
8060
|
+
defaultValue: "Helvetica"
|
|
8061
|
+
},
|
|
8062
|
+
{
|
|
8063
|
+
key: "fontWeight",
|
|
8064
|
+
label: "Font weight (normal/bold)",
|
|
8065
|
+
defaultValue: "normal"
|
|
8066
|
+
},
|
|
8067
|
+
{
|
|
8068
|
+
key: "fontStyle",
|
|
8069
|
+
label: "Font style (normal/italic)",
|
|
8070
|
+
defaultValue: "normal"
|
|
8071
|
+
},
|
|
8072
|
+
{
|
|
8073
|
+
key: "sizePt",
|
|
8074
|
+
label: "Size (pt)",
|
|
8075
|
+
defaultValue: "12"
|
|
8076
|
+
},
|
|
8077
|
+
REQUIRED_COLOR_FIELD
|
|
8078
|
+
];
|
|
8079
|
+
case "rect":
|
|
8080
|
+
case "ellipse":
|
|
8081
|
+
case "path": return [
|
|
8082
|
+
...GEOMETRY_FIELDS,
|
|
8083
|
+
FILL_FIELD,
|
|
8084
|
+
STROKE_FIELD
|
|
8085
|
+
];
|
|
8086
|
+
case "line": return [
|
|
8087
|
+
{
|
|
8088
|
+
key: "fromXPt",
|
|
8089
|
+
label: "From X (pt)",
|
|
8090
|
+
defaultValue: "40"
|
|
8091
|
+
},
|
|
8092
|
+
{
|
|
8093
|
+
key: "fromYPt",
|
|
8094
|
+
label: "From Y (pt)",
|
|
8095
|
+
defaultValue: "40"
|
|
8096
|
+
},
|
|
8097
|
+
{
|
|
8098
|
+
key: "toXPt",
|
|
8099
|
+
label: "To X (pt)",
|
|
8100
|
+
defaultValue: "200"
|
|
8101
|
+
},
|
|
8102
|
+
{
|
|
8103
|
+
key: "toYPt",
|
|
8104
|
+
label: "To Y (pt)",
|
|
8105
|
+
defaultValue: "40"
|
|
8106
|
+
},
|
|
8107
|
+
REQUIRED_COLOR_FIELD,
|
|
8108
|
+
{
|
|
8109
|
+
key: "widthPt",
|
|
8110
|
+
label: "Width (pt)",
|
|
8111
|
+
defaultValue: "1"
|
|
8112
|
+
}
|
|
8113
|
+
];
|
|
8114
|
+
case "image": return [...GEOMETRY_FIELDS, {
|
|
8115
|
+
key: "path",
|
|
8116
|
+
label: "Image file path (.png/.jpg/.jpeg)",
|
|
8117
|
+
defaultValue: ""
|
|
8118
|
+
}];
|
|
8119
|
+
case "link": return [{
|
|
8120
|
+
key: "uri",
|
|
8121
|
+
label: "URI",
|
|
8122
|
+
defaultValue: "https://example.com"
|
|
8123
|
+
}, ...GEOMETRY_FIELDS];
|
|
8124
|
+
}
|
|
8125
|
+
}
|
|
8126
|
+
function readFrame(values) {
|
|
8127
|
+
return {
|
|
8128
|
+
xPt: parseNumberField(requireFieldValue(values, "xPt"), 0),
|
|
8129
|
+
yPt: parseNumberField(requireFieldValue(values, "yPt"), 0),
|
|
8130
|
+
widthPt: parseNumberField(requireFieldValue(values, "widthPt"), 100),
|
|
8131
|
+
heightPt: parseNumberField(requireFieldValue(values, "heightPt"), 60)
|
|
8132
|
+
};
|
|
8133
|
+
}
|
|
8134
|
+
async function applyAddKind(kind, pageIndex, values, dispatch) {
|
|
8135
|
+
switch (kind) {
|
|
8136
|
+
case "text":
|
|
8137
|
+
dispatch({
|
|
8138
|
+
type: "ADD_PDF_TEXT",
|
|
8139
|
+
pageIndex,
|
|
8140
|
+
init: {
|
|
8141
|
+
xPt: parseNumberField(requireFieldValue(values, "xPt"), 0),
|
|
8142
|
+
yPt: parseNumberField(requireFieldValue(values, "yPt"), 0),
|
|
8143
|
+
text: requireFieldValue(values, "text"),
|
|
8144
|
+
font: {
|
|
8145
|
+
family: requireFieldValue(values, "fontFamily").trim() || "Helvetica",
|
|
8146
|
+
weight: parseFontWeight(requireFieldValue(values, "fontWeight")),
|
|
8147
|
+
style: parseFontStyle(requireFieldValue(values, "fontStyle"))
|
|
8148
|
+
},
|
|
8149
|
+
sizePt: Math.max(parseNumberField(requireFieldValue(values, "sizePt"), 12), Number.EPSILON),
|
|
8150
|
+
color: parseColorField(requireFieldValue(values, "color")) ?? {
|
|
8151
|
+
r: 0,
|
|
8152
|
+
g: 0,
|
|
8153
|
+
b: 0
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
});
|
|
8157
|
+
return;
|
|
8158
|
+
case "rect":
|
|
8159
|
+
dispatch({
|
|
8160
|
+
type: "ADD_PDF_RECT",
|
|
8161
|
+
pageIndex,
|
|
8162
|
+
init: {
|
|
8163
|
+
...readFrame(values),
|
|
8164
|
+
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
8165
|
+
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
8166
|
+
}
|
|
8167
|
+
});
|
|
8168
|
+
return;
|
|
8169
|
+
case "ellipse":
|
|
8170
|
+
dispatch({
|
|
8171
|
+
type: "ADD_PDF_ELLIPSE",
|
|
8172
|
+
pageIndex,
|
|
8173
|
+
init: {
|
|
8174
|
+
...readFrame(values),
|
|
8175
|
+
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
8176
|
+
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
8177
|
+
}
|
|
8178
|
+
});
|
|
8179
|
+
return;
|
|
8180
|
+
case "line":
|
|
8181
|
+
dispatch({
|
|
8182
|
+
type: "ADD_PDF_LINE",
|
|
8183
|
+
pageIndex,
|
|
8184
|
+
init: {
|
|
8185
|
+
x1Pt: parseNumberField(requireFieldValue(values, "fromXPt"), 0),
|
|
8186
|
+
y1Pt: parseNumberField(requireFieldValue(values, "fromYPt"), 0),
|
|
8187
|
+
x2Pt: parseNumberField(requireFieldValue(values, "toXPt"), 100),
|
|
8188
|
+
y2Pt: parseNumberField(requireFieldValue(values, "toYPt"), 0),
|
|
8189
|
+
color: parseColorField(requireFieldValue(values, "color")) ?? {
|
|
8190
|
+
r: 0,
|
|
8191
|
+
g: 0,
|
|
8192
|
+
b: 0
|
|
8193
|
+
},
|
|
8194
|
+
widthPt: Math.max(parseNumberField(requireFieldValue(values, "widthPt"), 1), Number.EPSILON)
|
|
8195
|
+
}
|
|
8196
|
+
});
|
|
8197
|
+
return;
|
|
8198
|
+
case "path": {
|
|
8199
|
+
const frame = readFrame(values);
|
|
8200
|
+
dispatch({
|
|
8201
|
+
type: "ADD_PDF_PATH",
|
|
8202
|
+
pageIndex,
|
|
8203
|
+
init: {
|
|
8204
|
+
subpaths: defaultTriangleLayoutSubpaths(frame.widthPt, frame.heightPt),
|
|
8205
|
+
fill: parseColorField(requireFieldValue(values, "fill")),
|
|
8206
|
+
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
8207
|
+
}
|
|
8208
|
+
});
|
|
8209
|
+
return;
|
|
8210
|
+
}
|
|
8211
|
+
case "image": {
|
|
8212
|
+
const frame = readFrame(values);
|
|
8213
|
+
const path = requireFieldValue(values, "path");
|
|
8214
|
+
const format = inferImageFormat(path);
|
|
8215
|
+
if (format === void 0) {
|
|
8216
|
+
dispatch({
|
|
8217
|
+
type: "SET_STATUS",
|
|
8218
|
+
severity: "warning",
|
|
8219
|
+
text: `${path} is not a .png or .jpg/.jpeg file -- image not added`
|
|
8220
|
+
});
|
|
8221
|
+
return;
|
|
8222
|
+
}
|
|
8223
|
+
try {
|
|
8224
|
+
const bytes = new Uint8Array(await readInput(path));
|
|
8225
|
+
dispatch({
|
|
8226
|
+
type: "ADD_PDF_IMAGE",
|
|
8227
|
+
pageIndex,
|
|
8228
|
+
init: {
|
|
8229
|
+
...frame,
|
|
8230
|
+
format,
|
|
8231
|
+
bytes
|
|
8232
|
+
}
|
|
8233
|
+
});
|
|
8234
|
+
} catch (error) {
|
|
8235
|
+
dispatch({
|
|
8236
|
+
type: "SET_STATUS",
|
|
8237
|
+
severity: "error",
|
|
8238
|
+
text: `Could not read ${path}: ${describeError(error)}`
|
|
8239
|
+
});
|
|
8240
|
+
}
|
|
8241
|
+
return;
|
|
8242
|
+
}
|
|
8243
|
+
case "link": {
|
|
8244
|
+
const frame = readFrame(values);
|
|
8245
|
+
dispatch({
|
|
8246
|
+
type: "ADD_PDF_LINK",
|
|
8247
|
+
pageIndex,
|
|
8248
|
+
init: {
|
|
8249
|
+
uri: requireFieldValue(values, "uri"),
|
|
8250
|
+
...frame
|
|
8251
|
+
}
|
|
8252
|
+
});
|
|
8253
|
+
return;
|
|
8254
|
+
}
|
|
8255
|
+
}
|
|
8256
|
+
}
|
|
8257
|
+
function AddItemFlow(props) {
|
|
8258
|
+
const dispatch = useAppDispatch();
|
|
8259
|
+
const [kind, setKind] = useState(void 0);
|
|
8260
|
+
const { selectedIndex } = useNavigationInput({
|
|
8261
|
+
itemCount: ADD_KIND_OPTIONS.length,
|
|
8262
|
+
isActive: props.isActive && kind === void 0,
|
|
8263
|
+
onBack: props.onCancel,
|
|
8264
|
+
onSelect: (index) => {
|
|
8265
|
+
const option = ADD_KIND_OPTIONS[index];
|
|
8266
|
+
if (option === void 0) return;
|
|
8267
|
+
setKind(option.kind);
|
|
8268
|
+
}
|
|
8269
|
+
});
|
|
8270
|
+
if (kind === void 0) return /* @__PURE__ */ jsxs(Box, {
|
|
8271
|
+
flexDirection: "column",
|
|
8272
|
+
borderStyle: "round",
|
|
8273
|
+
paddingX: 1,
|
|
8274
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
8275
|
+
bold: true,
|
|
8276
|
+
children: "Add item -- choose a kind"
|
|
8277
|
+
}), /* @__PURE__ */ jsx(ListView, {
|
|
8278
|
+
items: ADD_KIND_OPTIONS,
|
|
8279
|
+
selectedIndex,
|
|
8280
|
+
reservedRows: 6,
|
|
8281
|
+
renderItem: (option, isSelected) => /* @__PURE__ */ jsxs(Text, {
|
|
8282
|
+
color: isSelected ? "cyan" : void 0,
|
|
8283
|
+
children: [isSelected ? "> " : " ", option.label]
|
|
8284
|
+
})
|
|
8285
|
+
})]
|
|
8286
|
+
});
|
|
8287
|
+
return /* @__PURE__ */ jsx(FieldWizard, {
|
|
8288
|
+
fields: fieldsForAddKind(kind),
|
|
8289
|
+
onCancel: props.onCancel,
|
|
8290
|
+
onComplete: (values) => {
|
|
8291
|
+
applyAddKind(kind, props.pageIndex, values, dispatch).then(props.onCreated);
|
|
8292
|
+
}
|
|
8293
|
+
});
|
|
8294
|
+
}
|
|
8295
|
+
function PdfPageItemsScreen() {
|
|
8296
|
+
const state = useAppState();
|
|
8297
|
+
const dispatch = useAppDispatch();
|
|
8298
|
+
const doc = requirePdfDocument(state.openDocument);
|
|
8299
|
+
const editable = isEditablePdfDocument(doc);
|
|
8300
|
+
const screen = currentScreen(state);
|
|
8301
|
+
if (screen.kind !== "pdfPageItems") throw new Error(`PdfPageItemsScreen rendered while the current screen is "${screen.kind}", not "pdfPageItems".`);
|
|
8302
|
+
const page = doc.layout.pages[screen.pageIndex];
|
|
8303
|
+
if (page === void 0) throw new Error(`pdfPageItems was pushed for page ${screen.pageIndex}, but the open PDF has no page at that index.`);
|
|
8304
|
+
const [isAdding, setIsAdding] = useState(false);
|
|
8305
|
+
const overlayOpen = anyOverlayOpen(state);
|
|
8306
|
+
const query = state.searchQuery.trim().toLowerCase();
|
|
8307
|
+
const indexed = page.items.map((item, itemIndex) => ({
|
|
8308
|
+
item,
|
|
8309
|
+
itemIndex
|
|
8310
|
+
}));
|
|
8311
|
+
const items = query === "" ? indexed : indexed.filter((entry) => `${entry.item.kind} ${previewFor(entry.item)}`.toLowerCase().includes(query));
|
|
8312
|
+
const { selectedIndex } = useNavigationInput({
|
|
8313
|
+
itemCount: items.length,
|
|
8314
|
+
isActive: !overlayOpen && !isAdding,
|
|
8315
|
+
onSelect: (index) => {
|
|
8316
|
+
const entry = items[index];
|
|
8317
|
+
if (entry === void 0) return;
|
|
8318
|
+
dispatch({
|
|
8319
|
+
type: "PUSH_SCREEN",
|
|
8320
|
+
screen: {
|
|
8321
|
+
kind: "pdfItemDetail",
|
|
8322
|
+
pageIndex: screen.pageIndex,
|
|
8323
|
+
itemIndex: entry.itemIndex
|
|
8324
|
+
}
|
|
8325
|
+
});
|
|
8326
|
+
},
|
|
8327
|
+
onBack: () => {
|
|
8328
|
+
dispatch({ type: "POP_SCREEN" });
|
|
8329
|
+
},
|
|
8330
|
+
onAppend: editable ? () => {
|
|
8331
|
+
setIsAdding(true);
|
|
8332
|
+
} : void 0
|
|
8333
|
+
});
|
|
8334
|
+
useInput((input) => {
|
|
8335
|
+
if (input !== "d") return;
|
|
8336
|
+
const entry = items[selectedIndex];
|
|
8337
|
+
if (entry === void 0) return;
|
|
8338
|
+
dispatch({
|
|
8339
|
+
type: "REMOVE_PDF_ITEM",
|
|
8340
|
+
pageIndex: screen.pageIndex,
|
|
8341
|
+
itemIndex: entry.itemIndex
|
|
8342
|
+
});
|
|
8343
|
+
}, { isActive: editable && !overlayOpen && !isAdding && items.length > 0 });
|
|
8344
|
+
if (isAdding) return /* @__PURE__ */ jsx(AddItemFlow, {
|
|
8345
|
+
pageIndex: screen.pageIndex,
|
|
8346
|
+
isActive: !overlayOpen,
|
|
8347
|
+
onCancel: () => {
|
|
8348
|
+
setIsAdding(false);
|
|
8349
|
+
},
|
|
8350
|
+
onCreated: () => {
|
|
8351
|
+
setIsAdding(false);
|
|
8352
|
+
}
|
|
8353
|
+
});
|
|
8354
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
8355
|
+
flexDirection: "column",
|
|
8356
|
+
children: [
|
|
8357
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
8358
|
+
bold: true,
|
|
8359
|
+
children: [
|
|
8360
|
+
"Page ",
|
|
8361
|
+
screen.pageIndex + 1,
|
|
8362
|
+
" items (",
|
|
8363
|
+
items.length,
|
|
8364
|
+
" of ",
|
|
8365
|
+
page.items.length,
|
|
8366
|
+
")"
|
|
8367
|
+
]
|
|
8368
|
+
}),
|
|
8369
|
+
/* @__PURE__ */ jsx(ListView, {
|
|
8370
|
+
items,
|
|
8371
|
+
selectedIndex,
|
|
8372
|
+
emptyMessage: query === "" ? editable ? "This page has no items -- press 'a' to add one" : "This page has no items." : `No items match "${state.searchQuery}".`,
|
|
8373
|
+
renderItem: ({ item, itemIndex }, isSelected) => /* @__PURE__ */ jsxs(Text, {
|
|
8374
|
+
color: isSelected ? "cyan" : void 0,
|
|
8375
|
+
inverse: isSelected,
|
|
8376
|
+
children: [
|
|
8377
|
+
itemIndex + 1,
|
|
8378
|
+
". ",
|
|
8379
|
+
item.kind,
|
|
8380
|
+
" -- ",
|
|
8381
|
+
previewFor(item)
|
|
8382
|
+
]
|
|
8383
|
+
})
|
|
8384
|
+
}),
|
|
8385
|
+
editable && /* @__PURE__ */ jsx(Text, {
|
|
8386
|
+
dimColor: true,
|
|
8387
|
+
children: "a to add an item, d to delete the selected item"
|
|
8388
|
+
})
|
|
8389
|
+
]
|
|
6831
8390
|
});
|
|
6832
8391
|
}
|
|
6833
8392
|
//#endregion
|