document-cli 1.4.0 → 1.6.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 +204 -23
- package/dist/index.cjs +225 -20
- package/dist/index.js +226 -21
- package/dist/{odb-structure-DkfNyHGR.js → odb-structure-Du1w_hOU.js} +25 -1
- package/dist/{tui-CQCLalYe.js → tui-CqYlr4nN.js} +336 -79
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
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, pptxToPdf, readDocxExtras, readOdbForms, readOdbReportContent, readOdbReports, readOdbTables, readOdgContent, readOdsContent, readPdf, rgbHexToColor, writePdf, xlsxToPdf } from "documents.js";
|
|
3
|
+
import { buildDocxPackage, buildOdtPackage, convertWordprocessingToLayout, createDocx, createFontMeasurer, createFontRegistry, createOdg, createOdp, createOds, createOdt, createPptx, decodeMarkdownText, docxToPdf, encodeMarkdownText, encodePackage, hsqldbCellDisplayText, markdownToPdf, odgToPdf, odpToPdf, odsToPdf, odtToPdf, openDocx, openOdg, openOdp, openOds, openOdt, openPptx, pptxToPdf, 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";
|
|
@@ -219,7 +219,8 @@ function selectionKeyFor(screen) {
|
|
|
219
219
|
case "pdfPageList":
|
|
220
220
|
case "exportOptions":
|
|
221
221
|
case "saveAsPrompt":
|
|
222
|
-
case "markdownLineList":
|
|
222
|
+
case "markdownLineList":
|
|
223
|
+
case "metadata": return screen.kind;
|
|
223
224
|
case "filePicker": return `filePicker:${screen.purpose}:${screen.cwd}`;
|
|
224
225
|
case "paragraphDetail":
|
|
225
226
|
case "tableView":
|
|
@@ -489,10 +490,6 @@ function shapeAt(doc, containerIndex, shapeIndex) {
|
|
|
489
490
|
if (doc.format === "odg") return doc.editor.pages()[containerIndex]?.shapes()[shapeIndex];
|
|
490
491
|
return doc.editor.slides()[containerIndex]?.shapes()[shapeIndex];
|
|
491
492
|
}
|
|
492
|
-
function rotatableShapeAt(doc, containerIndex, shapeIndex) {
|
|
493
|
-
if (doc.format === "odg") return doc.editor.pages()[containerIndex]?.shapes()[shapeIndex];
|
|
494
|
-
return doc.editor.slides()[containerIndex]?.shapes()[shapeIndex];
|
|
495
|
-
}
|
|
496
493
|
function sheetAt(doc, sheetIndex) {
|
|
497
494
|
return doc.editor.sheets()[sheetIndex];
|
|
498
495
|
}
|
|
@@ -525,8 +522,8 @@ function withSheet(state, sheetIndex, apply) {
|
|
|
525
522
|
apply(sheet);
|
|
526
523
|
});
|
|
527
524
|
}
|
|
528
|
-
function
|
|
529
|
-
const paragraph =
|
|
525
|
+
function setTextContainerText(container, text) {
|
|
526
|
+
const paragraph = container.paragraphs()[0] ?? container.appendParagraph();
|
|
530
527
|
const runs = paragraph.runs();
|
|
531
528
|
const firstRun = runs[0];
|
|
532
529
|
if (firstRun === void 0) {
|
|
@@ -536,6 +533,9 @@ function setCellText(cell, text) {
|
|
|
536
533
|
firstRun.text = text;
|
|
537
534
|
for (const extra of runs.slice(1)) extra.remove();
|
|
538
535
|
}
|
|
536
|
+
function setCellText(cell, text) {
|
|
537
|
+
setTextContainerText(cell, text);
|
|
538
|
+
}
|
|
539
539
|
function appReducer(state, action) {
|
|
540
540
|
switch (action.type) {
|
|
541
541
|
case "PUSH_SCREEN": return {
|
|
@@ -720,6 +720,18 @@ function appReducer(state, action) {
|
|
|
720
720
|
appended.list = membership;
|
|
721
721
|
});
|
|
722
722
|
}
|
|
723
|
+
case "SET_LIST_ITEM_TEXT": {
|
|
724
|
+
const doc = wordprocessingDocument(state);
|
|
725
|
+
if (doc === void 0) return wrongDocument(state, "a docx or odt document");
|
|
726
|
+
if (doc.format !== "odt") return wrongDocument(state, "an odt document (lists are an odt-only concept)");
|
|
727
|
+
const list = doc.editor.lists()[action.blockIndex];
|
|
728
|
+
if (list === void 0) return withStatus(state, "warning", `There is no list at index ${action.blockIndex}`);
|
|
729
|
+
const item = list.items()[action.itemIndex];
|
|
730
|
+
if (item === void 0) return withStatus(state, "warning", `List ${action.blockIndex} has no item at index ${action.itemIndex}`);
|
|
731
|
+
return mutate(state, doc, () => {
|
|
732
|
+
setTextContainerText(item, action.text);
|
|
733
|
+
});
|
|
734
|
+
}
|
|
723
735
|
case "ADD_SLIDE": {
|
|
724
736
|
const doc = presentationDocument(state);
|
|
725
737
|
if (doc === void 0) return wrongDocument(state, "a pptx or odp document");
|
|
@@ -792,16 +804,9 @@ function appReducer(state, action) {
|
|
|
792
804
|
case "SET_SHAPE_FRAME": return withShape(state, action.containerIndex, action.shapeIndex, (shape) => {
|
|
793
805
|
shape.frame = action.frame;
|
|
794
806
|
});
|
|
795
|
-
case "SET_SHAPE_ROTATION": {
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
if (doc.format === "pptx") return withStatus(state, "warning", "documents.js has no rotation setter for a pptx shape; rotate the shape in odp instead");
|
|
799
|
-
const shape = rotatableShapeAt(doc, action.containerIndex, action.shapeIndex);
|
|
800
|
-
if (shape === void 0) return withStatus(state, "warning", `There is no shape ${action.shapeIndex} at index ${action.containerIndex}`);
|
|
801
|
-
return mutate(state, doc, () => {
|
|
802
|
-
shape.rotationDeg = action.rotationDeg;
|
|
803
|
-
});
|
|
804
|
-
}
|
|
807
|
+
case "SET_SHAPE_ROTATION": return withShape(state, action.containerIndex, action.shapeIndex, (shape) => {
|
|
808
|
+
shape.rotationDeg = action.rotationDeg;
|
|
809
|
+
});
|
|
805
810
|
case "SET_SLIDE_NOTES": {
|
|
806
811
|
const doc = presentationDocument(state);
|
|
807
812
|
if (doc === void 0) return wrongDocument(state, "a pptx or odp document");
|
|
@@ -1019,6 +1024,11 @@ const COMMANDS = [
|
|
|
1019
1024
|
usage: ":close",
|
|
1020
1025
|
description: "Close the open document"
|
|
1021
1026
|
},
|
|
1027
|
+
{
|
|
1028
|
+
name: "undo",
|
|
1029
|
+
usage: ":undo",
|
|
1030
|
+
description: "Undo the last change"
|
|
1031
|
+
},
|
|
1022
1032
|
{
|
|
1023
1033
|
name: "help",
|
|
1024
1034
|
usage: ":help",
|
|
@@ -1151,6 +1161,9 @@ async function runCommand(line, state, dispatch) {
|
|
|
1151
1161
|
case "close":
|
|
1152
1162
|
dispatch({ type: "REQUEST_CLOSE" });
|
|
1153
1163
|
return;
|
|
1164
|
+
case "undo":
|
|
1165
|
+
dispatch({ type: "UNDO" });
|
|
1166
|
+
return;
|
|
1154
1167
|
case "help":
|
|
1155
1168
|
dispatch({
|
|
1156
1169
|
type: "OPEN_OVERLAY",
|
|
@@ -1417,6 +1430,10 @@ const GLOBAL_KEYS = [
|
|
|
1417
1430
|
keys: "a",
|
|
1418
1431
|
description: "Append a new item to the current list"
|
|
1419
1432
|
},
|
|
1433
|
+
{
|
|
1434
|
+
keys: "m",
|
|
1435
|
+
description: "Show the open document's metadata (read-only)"
|
|
1436
|
+
},
|
|
1420
1437
|
{
|
|
1421
1438
|
keys: "Ctrl+S",
|
|
1422
1439
|
description: "Save the open document"
|
|
@@ -1425,6 +1442,10 @@ const GLOBAL_KEYS = [
|
|
|
1425
1442
|
keys: "Ctrl+W",
|
|
1426
1443
|
description: "Close the open document"
|
|
1427
1444
|
},
|
|
1445
|
+
{
|
|
1446
|
+
keys: "Ctrl+Z",
|
|
1447
|
+
description: "Undo the last change"
|
|
1448
|
+
},
|
|
1428
1449
|
{
|
|
1429
1450
|
keys: "q / Ctrl+C",
|
|
1430
1451
|
description: "Quit"
|
|
@@ -3014,6 +3035,10 @@ function requireShapeOrVectorDetailScreen(state) {
|
|
|
3014
3035
|
if (screen.kind !== "shapeOrVectorDetail") throw new Error(`OdgShapeOrVectorDetailScreen rendered while the top of the stack is '${screen.kind}', not 'shapeOrVectorDetail' -- app.tsx's ScreenBody switch only mounts this component for that screen kind, so this cannot happen without a routing bug.`);
|
|
3015
3036
|
return screen;
|
|
3016
3037
|
}
|
|
3038
|
+
function vectorsParityMatch(liveVectors, contentVectors) {
|
|
3039
|
+
if (liveVectors.length !== contentVectors.length) return false;
|
|
3040
|
+
return liveVectors.every((live, index) => live.kind === contentVectors[index]?.kind);
|
|
3041
|
+
}
|
|
3017
3042
|
function buildPageItems(doc, pageIndex) {
|
|
3018
3043
|
const page = doc.editor.pages()[pageIndex];
|
|
3019
3044
|
if (page === void 0) return [];
|
|
@@ -3021,9 +3046,12 @@ function buildPageItems(doc, pageIndex) {
|
|
|
3021
3046
|
if (content.kind !== "drawing") throw new Error(`readOdgContent(doc.editor.toPackage()) returned a '${content.kind}' ContentDocument for an odg-format open document -- an odg package should always read back as the 'drawing' variant, so this indicates a real inconsistency in documents.js's own reader, not a state this screen should paper over.`);
|
|
3022
3047
|
const contentPage = content.pages[pageIndex];
|
|
3023
3048
|
if (contentPage === void 0) throw new Error(`readOdgContent found no page at index ${pageIndex}, but doc.editor.pages() has a page there -- the two read the same live package, so they should always agree on page count.`);
|
|
3024
|
-
const
|
|
3049
|
+
const liveVectors = page.vectors();
|
|
3050
|
+
const parityOk = vectorsParityMatch(liveVectors, contentPage.vectors);
|
|
3051
|
+
const vectorItems = contentPage.vectors.map((vector, index) => ({
|
|
3025
3052
|
kind: "vector",
|
|
3026
|
-
vector
|
|
3053
|
+
vector,
|
|
3054
|
+
liveVector: parityOk ? liveVectors[index] : void 0
|
|
3027
3055
|
}));
|
|
3028
3056
|
const shapeItems = page.shapes().map((shape, index) => {
|
|
3029
3057
|
return {
|
|
@@ -3315,15 +3343,19 @@ function warnVectorIsViewOnly(dispatch, label) {
|
|
|
3315
3343
|
dispatch({
|
|
3316
3344
|
type: "SET_STATUS",
|
|
3317
3345
|
severity: "info",
|
|
3318
|
-
text: `${label} added --
|
|
3346
|
+
text: `${label} added -- but this page also has a vector element documents.js's OdgPage.vectors() cannot wrap, so every vector on it (including this one) shows read-only in this list rather than risk pairing the wrong live handle to the wrong row.`
|
|
3319
3347
|
});
|
|
3320
3348
|
}
|
|
3349
|
+
function warnIfVectorAddedReadOnly(doc, pageIndex, dispatch, label) {
|
|
3350
|
+
const added = buildPageItems(doc, pageIndex).filter((item) => item.kind === "vector").at(-1);
|
|
3351
|
+
if (added !== void 0 && added.liveVector === void 0) warnVectorIsViewOnly(dispatch, label);
|
|
3352
|
+
}
|
|
3321
3353
|
function inferImageFormat(path) {
|
|
3322
3354
|
const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
|
|
3323
3355
|
if (extension === "png") return "png";
|
|
3324
3356
|
if (extension === "jpg" || extension === "jpeg") return "jpeg";
|
|
3325
3357
|
}
|
|
3326
|
-
async function applyAddKind(kind, pageIndex, values, dispatch) {
|
|
3358
|
+
async function applyAddKind(kind, pageIndex, doc, values, dispatch) {
|
|
3327
3359
|
switch (kind) {
|
|
3328
3360
|
case "rect":
|
|
3329
3361
|
dispatch({
|
|
@@ -3335,7 +3367,7 @@ async function applyAddKind(kind, pageIndex, values, dispatch) {
|
|
|
3335
3367
|
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
3336
3368
|
}
|
|
3337
3369
|
});
|
|
3338
|
-
|
|
3370
|
+
warnIfVectorAddedReadOnly(doc, pageIndex, dispatch, "Rectangle");
|
|
3339
3371
|
return;
|
|
3340
3372
|
case "ellipse":
|
|
3341
3373
|
dispatch({
|
|
@@ -3347,7 +3379,7 @@ async function applyAddKind(kind, pageIndex, values, dispatch) {
|
|
|
3347
3379
|
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
3348
3380
|
}
|
|
3349
3381
|
});
|
|
3350
|
-
|
|
3382
|
+
warnIfVectorAddedReadOnly(doc, pageIndex, dispatch, "Ellipse");
|
|
3351
3383
|
return;
|
|
3352
3384
|
case "line":
|
|
3353
3385
|
dispatch({
|
|
@@ -3372,7 +3404,7 @@ async function applyAddKind(kind, pageIndex, values, dispatch) {
|
|
|
3372
3404
|
}
|
|
3373
3405
|
}
|
|
3374
3406
|
});
|
|
3375
|
-
|
|
3407
|
+
warnIfVectorAddedReadOnly(doc, pageIndex, dispatch, "Line");
|
|
3376
3408
|
return;
|
|
3377
3409
|
case "path": {
|
|
3378
3410
|
const frame = readFrame(values);
|
|
@@ -3386,7 +3418,7 @@ async function applyAddKind(kind, pageIndex, values, dispatch) {
|
|
|
3386
3418
|
stroke: parseStrokeField(requireFieldValue(values, "stroke"))
|
|
3387
3419
|
}
|
|
3388
3420
|
});
|
|
3389
|
-
|
|
3421
|
+
warnIfVectorAddedReadOnly(doc, pageIndex, dispatch, "Path");
|
|
3390
3422
|
return;
|
|
3391
3423
|
}
|
|
3392
3424
|
case "textbox":
|
|
@@ -3515,7 +3547,7 @@ function AddItemFlow(props) {
|
|
|
3515
3547
|
fields: fieldsForAddKind(kind),
|
|
3516
3548
|
onCancel: props.onCancel,
|
|
3517
3549
|
onComplete: (values) => {
|
|
3518
|
-
applyAddKind(kind, props.pageIndex, values, dispatch).then(props.onCreated);
|
|
3550
|
+
applyAddKind(kind, props.pageIndex, props.doc, values, dispatch).then(props.onCreated);
|
|
3519
3551
|
}
|
|
3520
3552
|
});
|
|
3521
3553
|
}
|
|
@@ -3569,6 +3601,7 @@ function OdgPageDetailScreen() {
|
|
|
3569
3601
|
});
|
|
3570
3602
|
if (isAdding) return /* @__PURE__ */ jsx(AddItemFlow, {
|
|
3571
3603
|
pageIndex,
|
|
3604
|
+
doc,
|
|
3572
3605
|
isActive: !overlayOpen,
|
|
3573
3606
|
onCancel: () => {
|
|
3574
3607
|
setIsAdding(false);
|
|
@@ -3602,19 +3635,75 @@ function OdgPageDetailScreen() {
|
|
|
3602
3635
|
}
|
|
3603
3636
|
//#endregion
|
|
3604
3637
|
//#region src/tui/screens/editors/odg/shape-or-vector-detail.tsx
|
|
3638
|
+
function buildVectorRows(vector, liveVector, dispatch) {
|
|
3639
|
+
const rows = [];
|
|
3640
|
+
if (vector.kind !== "line" && liveVector.kind !== "line") {
|
|
3641
|
+
const fillTarget = liveVector;
|
|
3642
|
+
rows.push({
|
|
3643
|
+
label: `Fill: ${vector.fill === void 0 ? "none" : formatColor$1(vector.fill)}`,
|
|
3644
|
+
currentValue: vector.fill === void 0 ? "" : `${vector.fill.r} ${vector.fill.g} ${vector.fill.b}`,
|
|
3645
|
+
commit: (raw) => {
|
|
3646
|
+
dispatch({
|
|
3647
|
+
type: "SET_VECTOR_FILL",
|
|
3648
|
+
vector: fillTarget,
|
|
3649
|
+
fill: parseColorField(raw)
|
|
3650
|
+
});
|
|
3651
|
+
}
|
|
3652
|
+
});
|
|
3653
|
+
}
|
|
3654
|
+
rows.push({
|
|
3655
|
+
label: `Stroke: ${vector.stroke === void 0 ? "none" : `${formatColor$1(vector.stroke.color)} ${formatPt(vector.stroke.widthPt)}pt`}`,
|
|
3656
|
+
currentValue: vector.stroke === void 0 ? "" : `${vector.stroke.color.r} ${vector.stroke.color.g} ${vector.stroke.color.b} ${vector.stroke.widthPt}`,
|
|
3657
|
+
commit: (raw) => {
|
|
3658
|
+
const stroke = parseStrokeField(raw);
|
|
3659
|
+
if (stroke === void 0) {
|
|
3660
|
+
dispatch({
|
|
3661
|
+
type: "SET_STATUS",
|
|
3662
|
+
severity: "warning",
|
|
3663
|
+
text: "A vector stroke cannot be cleared to none through this editor -- enter \"r g b widthPt\" (0-1 colour, pt width) instead"
|
|
3664
|
+
});
|
|
3665
|
+
return;
|
|
3666
|
+
}
|
|
3667
|
+
dispatch({
|
|
3668
|
+
type: "SET_VECTOR_STROKE",
|
|
3669
|
+
vector: liveVector,
|
|
3670
|
+
stroke
|
|
3671
|
+
});
|
|
3672
|
+
}
|
|
3673
|
+
});
|
|
3674
|
+
return rows;
|
|
3675
|
+
}
|
|
3605
3676
|
function VectorDetail(props) {
|
|
3606
|
-
|
|
3677
|
+
const dispatch = useAppDispatch();
|
|
3678
|
+
const [editingField, setEditingField] = useState(void 0);
|
|
3679
|
+
const [draft, setDraft] = useState("");
|
|
3680
|
+
const { vector, liveVector, isActive } = props;
|
|
3681
|
+
const rows = liveVector === void 0 ? [] : buildVectorRows(vector, liveVector, dispatch);
|
|
3682
|
+
const { selectedIndex } = useNavigationInput({
|
|
3683
|
+
itemCount: rows.length,
|
|
3684
|
+
isActive: isActive && editingField === void 0,
|
|
3685
|
+
onBack: () => {
|
|
3686
|
+
dispatch({ type: "POP_SCREEN" });
|
|
3687
|
+
},
|
|
3688
|
+
onSelect: (index) => {
|
|
3689
|
+
const row = rows[index];
|
|
3690
|
+
if (row === void 0) return;
|
|
3691
|
+
setDraft(row.currentValue);
|
|
3692
|
+
setEditingField(index);
|
|
3693
|
+
}
|
|
3694
|
+
});
|
|
3695
|
+
if (liveVector === void 0) return /* @__PURE__ */ jsxs(Box, {
|
|
3607
3696
|
flexDirection: "column",
|
|
3608
3697
|
children: [
|
|
3609
3698
|
/* @__PURE__ */ jsxs(Text, {
|
|
3610
3699
|
bold: true,
|
|
3611
|
-
children: [vectorKindLabel(
|
|
3700
|
+
children: [vectorKindLabel(vector.kind), " (view-only)"]
|
|
3612
3701
|
}),
|
|
3613
|
-
/* @__PURE__ */ jsxs(Text, { children: ["Geometry: ", describeVectorGeometry(
|
|
3614
|
-
/* @__PURE__ */ jsx(Text, { children: describeFillStroke(
|
|
3702
|
+
/* @__PURE__ */ jsxs(Text, { children: ["Geometry: ", describeVectorGeometry(vector)] }),
|
|
3703
|
+
/* @__PURE__ */ jsx(Text, { children: describeFillStroke(vector) }),
|
|
3615
3704
|
/* @__PURE__ */ jsx(Text, {
|
|
3616
3705
|
color: "yellow",
|
|
3617
|
-
children: "documents.js's OdgPage has no
|
|
3706
|
+
children: "This page's live vectors (documents.js's `OdgPage.vectors()`) don't line up one-to-one with what odf.js's own reader found here -- likely a `draw:circle`/`polygon`/`polyline`/`custom-shape` element the live accessor has no wrapper for, sitting alongside a plain rect/ellipse/line/path it does. Rather than risk pairing the wrong live handle to this row, every vector on this page is shown read-only until that mismatch is resolved outside this TUI. Open the file in a real ODF editor to change it."
|
|
3618
3707
|
}),
|
|
3619
3708
|
/* @__PURE__ */ jsx(Text, {
|
|
3620
3709
|
dimColor: true,
|
|
@@ -3622,6 +3711,53 @@ function VectorDetail(props) {
|
|
|
3622
3711
|
})
|
|
3623
3712
|
]
|
|
3624
3713
|
});
|
|
3714
|
+
if (editingField !== void 0) {
|
|
3715
|
+
const row = rows[editingField];
|
|
3716
|
+
if (row === void 0) throw new Error(`VectorDetail 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.`);
|
|
3717
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
3718
|
+
flexDirection: "column",
|
|
3719
|
+
borderStyle: "round",
|
|
3720
|
+
paddingX: 1,
|
|
3721
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
3722
|
+
bold: true,
|
|
3723
|
+
children: row.label
|
|
3724
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
3725
|
+
value: draft,
|
|
3726
|
+
isFocused: true,
|
|
3727
|
+
onChange: setDraft,
|
|
3728
|
+
onCancel: () => {
|
|
3729
|
+
setEditingField(void 0);
|
|
3730
|
+
},
|
|
3731
|
+
onSubmit: (value) => {
|
|
3732
|
+
row.commit(value);
|
|
3733
|
+
setEditingField(void 0);
|
|
3734
|
+
}
|
|
3735
|
+
})]
|
|
3736
|
+
});
|
|
3737
|
+
}
|
|
3738
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
3739
|
+
flexDirection: "column",
|
|
3740
|
+
children: [
|
|
3741
|
+
/* @__PURE__ */ jsx(Text, {
|
|
3742
|
+
bold: true,
|
|
3743
|
+
children: vectorKindLabel(vector.kind)
|
|
3744
|
+
}),
|
|
3745
|
+
/* @__PURE__ */ jsxs(Text, { children: ["Geometry: ", describeVectorGeometry(vector)] }),
|
|
3746
|
+
/* @__PURE__ */ jsx(ListView, {
|
|
3747
|
+
items: rows,
|
|
3748
|
+
selectedIndex,
|
|
3749
|
+
reservedRows: 5,
|
|
3750
|
+
renderItem: (row, isSelected) => /* @__PURE__ */ jsxs(Text, {
|
|
3751
|
+
color: isSelected ? "cyan" : void 0,
|
|
3752
|
+
children: [isSelected ? "> " : " ", row.label]
|
|
3753
|
+
})
|
|
3754
|
+
}),
|
|
3755
|
+
/* @__PURE__ */ jsx(Text, {
|
|
3756
|
+
dimColor: true,
|
|
3757
|
+
children: "Enter to edit a field, Esc to go back"
|
|
3758
|
+
})
|
|
3759
|
+
]
|
|
3760
|
+
});
|
|
3625
3761
|
}
|
|
3626
3762
|
function ShapeDetail(props) {
|
|
3627
3763
|
const dispatch = useAppDispatch();
|
|
@@ -3791,7 +3927,7 @@ function OdgShapeOrVectorDetailScreen() {
|
|
|
3791
3927
|
const item = items[itemIndex];
|
|
3792
3928
|
useInput((input, key) => {
|
|
3793
3929
|
if (key.escape || key.leftArrow || input === "h") dispatch({ type: "POP_SCREEN" });
|
|
3794
|
-
}, { isActive: !overlayOpen && item
|
|
3930
|
+
}, { isActive: !overlayOpen && item === void 0 });
|
|
3795
3931
|
if (item === void 0) return /* @__PURE__ */ jsxs(Box, {
|
|
3796
3932
|
flexDirection: "column",
|
|
3797
3933
|
children: [/* @__PURE__ */ jsxs(Text, {
|
|
@@ -3808,7 +3944,11 @@ function OdgShapeOrVectorDetailScreen() {
|
|
|
3808
3944
|
children: "Esc to go back"
|
|
3809
3945
|
})]
|
|
3810
3946
|
});
|
|
3811
|
-
if (item.kind === "vector") return /* @__PURE__ */ jsx(VectorDetail, {
|
|
3947
|
+
if (item.kind === "vector") return /* @__PURE__ */ jsx(VectorDetail, {
|
|
3948
|
+
vector: item.vector,
|
|
3949
|
+
liveVector: item.liveVector,
|
|
3950
|
+
isActive: !overlayOpen
|
|
3951
|
+
});
|
|
3812
3952
|
const vectorCount = items.filter((entry) => entry.kind === "vector").length;
|
|
3813
3953
|
return /* @__PURE__ */ jsx(ShapeDetail, {
|
|
3814
3954
|
pageIndex,
|
|
@@ -3950,10 +4090,6 @@ function formatRotationDeg(value) {
|
|
|
3950
4090
|
return value === void 0 ? "(unset)" : `${Math.round(value * ROTATION_DISPLAY_PRECISION) / ROTATION_DISPLAY_PRECISION}°`;
|
|
3951
4091
|
}
|
|
3952
4092
|
function RotationField(props) {
|
|
3953
|
-
if (!props.isEditable) return /* @__PURE__ */ jsx(Text, {
|
|
3954
|
-
dimColor: true,
|
|
3955
|
-
children: "[R] Rotation: not available for pptx shapes"
|
|
3956
|
-
});
|
|
3957
4093
|
if (props.isEditing) return /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
3958
4094
|
color: "cyan",
|
|
3959
4095
|
children: "[R] Rotation (deg, blank to unset): "
|
|
@@ -3987,7 +4123,6 @@ const FIELD_LABELS = {
|
|
|
3987
4123
|
width: "Width (pt)",
|
|
3988
4124
|
height: "Height (pt)"
|
|
3989
4125
|
};
|
|
3990
|
-
const ROTATION_UNAVAILABLE_MESSAGE = "documents.js has no rotation setter for a pptx shape; rotate the shape in odp instead";
|
|
3991
4126
|
const POINT_DISPLAY_PRECISION = 100;
|
|
3992
4127
|
function formatPoints(value) {
|
|
3993
4128
|
return value === void 0 ? "(unset)" : `${Math.round(value * POINT_DISPLAY_PRECISION) / POINT_DISPLAY_PRECISION}pt`;
|
|
@@ -4007,9 +4142,9 @@ function describeFieldValue(key, shape) {
|
|
|
4007
4142
|
function roundForDisplay(value) {
|
|
4008
4143
|
return Math.round(value * POINT_DISPLAY_PRECISION) / POINT_DISPLAY_PRECISION;
|
|
4009
4144
|
}
|
|
4010
|
-
function initialDraftFor(key, shape
|
|
4145
|
+
function initialDraftFor(key, shape) {
|
|
4011
4146
|
if (key === "rotation") {
|
|
4012
|
-
const value =
|
|
4147
|
+
const value = shape.rotationDeg;
|
|
4013
4148
|
return value === void 0 ? "" : String(roundForDisplay(value));
|
|
4014
4149
|
}
|
|
4015
4150
|
if (key === "text") return shape.text;
|
|
@@ -4022,10 +4157,9 @@ function initialDraftFor(key, shape, rotatableShape) {
|
|
|
4022
4157
|
}
|
|
4023
4158
|
}
|
|
4024
4159
|
function FieldRow(props) {
|
|
4025
|
-
const { fieldKey, shape,
|
|
4160
|
+
const { fieldKey, shape, isSelected, isEditing, draft, onDraftChange, onSubmit, onCancel } = props;
|
|
4026
4161
|
if (fieldKey === "rotation") return /* @__PURE__ */ jsx(RotationField, {
|
|
4027
|
-
rotationDeg:
|
|
4028
|
-
isEditable: isOdp,
|
|
4162
|
+
rotationDeg: shape.rotationDeg,
|
|
4029
4163
|
isSelected,
|
|
4030
4164
|
isEditing,
|
|
4031
4165
|
draftValue: draft,
|
|
@@ -4074,9 +4208,7 @@ function ShapeEditorScreen(props) {
|
|
|
4074
4208
|
const overlayOpen = anyOverlayOpen(state);
|
|
4075
4209
|
const doc = assertPresentationDocument(state.openDocument);
|
|
4076
4210
|
const { slideIndex, shapeIndex } = props.screen;
|
|
4077
|
-
const isOdp = doc.format === "odp";
|
|
4078
4211
|
const shape = doc.editor.slides()[slideIndex]?.shapes()[shapeIndex];
|
|
4079
|
-
const rotatableShape = doc.format === "odp" ? doc.editor.slides()[slideIndex]?.shapes()[shapeIndex] : void 0;
|
|
4080
4212
|
const [editingField, setEditingField] = useState(void 0);
|
|
4081
4213
|
const [draft, setDraft] = useState("");
|
|
4082
4214
|
const { selectedIndex } = useNavigationInput({
|
|
@@ -4089,20 +4221,12 @@ function ShapeEditorScreen(props) {
|
|
|
4089
4221
|
if (shape === void 0) return;
|
|
4090
4222
|
const key = FIELD_KEYS[index];
|
|
4091
4223
|
if (key === void 0) return;
|
|
4092
|
-
if (key === "rotation" && !isOdp) {
|
|
4093
|
-
dispatch({
|
|
4094
|
-
type: "SET_STATUS",
|
|
4095
|
-
severity: "warning",
|
|
4096
|
-
text: ROTATION_UNAVAILABLE_MESSAGE
|
|
4097
|
-
});
|
|
4098
|
-
return;
|
|
4099
|
-
}
|
|
4100
4224
|
dispatch({
|
|
4101
4225
|
type: "SET_SELECTION",
|
|
4102
4226
|
key: selectionKeyFor(props.screen),
|
|
4103
4227
|
index
|
|
4104
4228
|
});
|
|
4105
|
-
setDraft(initialDraftFor(key, shape
|
|
4229
|
+
setDraft(initialDraftFor(key, shape));
|
|
4106
4230
|
setEditingField(key);
|
|
4107
4231
|
}
|
|
4108
4232
|
});
|
|
@@ -4227,8 +4351,6 @@ function ShapeEditorScreen(props) {
|
|
|
4227
4351
|
renderItem: (key, isSelected) => /* @__PURE__ */ jsx(FieldRow, {
|
|
4228
4352
|
fieldKey: key,
|
|
4229
4353
|
shape,
|
|
4230
|
-
rotatableShape,
|
|
4231
|
-
isOdp,
|
|
4232
4354
|
isSelected,
|
|
4233
4355
|
isEditing: editingField === key,
|
|
4234
4356
|
draft,
|
|
@@ -5334,28 +5456,40 @@ function OdsSpreadsheetGridScreen() {
|
|
|
5334
5456
|
function ListEditorScreen() {
|
|
5335
5457
|
const state = useAppState();
|
|
5336
5458
|
const dispatch = useAppDispatch();
|
|
5459
|
+
const overlayOpen = anyOverlayOpen(state);
|
|
5337
5460
|
const [isAdding, setIsAdding] = useState(false);
|
|
5338
5461
|
const [newItemText, setNewItemText] = useState("");
|
|
5462
|
+
const [editingIndex, setEditingIndex] = useState(void 0);
|
|
5339
5463
|
const screen = currentScreen(state);
|
|
5340
5464
|
const doc = paragraphFamilyDocument(state.openDocument);
|
|
5341
5465
|
const list = screen.kind === "listEditor" && doc?.format === "odt" ? doc.editor.lists()[screen.blockIndex] : void 0;
|
|
5342
|
-
const
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5466
|
+
const items = list === void 0 ? [] : list.items();
|
|
5467
|
+
const rows = items.map((item, index) => ({
|
|
5468
|
+
item,
|
|
5469
|
+
index
|
|
5470
|
+
}));
|
|
5471
|
+
const itemCount = items.length;
|
|
5472
|
+
const isNavigationActive = !overlayOpen && !isAdding && editingIndex === void 0;
|
|
5473
|
+
const { selectedIndex } = useNavigationInput({
|
|
5474
|
+
itemCount,
|
|
5475
|
+
isActive: isNavigationActive,
|
|
5476
|
+
onBack: () => {
|
|
5346
5477
|
dispatch({ type: "POP_SCREEN" });
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5478
|
+
},
|
|
5479
|
+
onSelect: (index) => {
|
|
5480
|
+
setEditingIndex(index);
|
|
5481
|
+
},
|
|
5482
|
+
onAppend: () => {
|
|
5350
5483
|
setIsAdding(true);
|
|
5351
|
-
return;
|
|
5352
5484
|
}
|
|
5485
|
+
});
|
|
5486
|
+
useInput((input, key) => {
|
|
5353
5487
|
if (key.tab || input === ">" || input === "<") dispatch({
|
|
5354
5488
|
type: "SET_STATUS",
|
|
5355
5489
|
severity: "warning",
|
|
5356
5490
|
text: "Indenting a list item needs a new reducer action this pass didn't add -- OdtListItem.addNestedList() has no wiring yet"
|
|
5357
5491
|
});
|
|
5358
|
-
}, { isActive:
|
|
5492
|
+
}, { isActive: isNavigationActive });
|
|
5359
5493
|
if (screen.kind !== "listEditor") return /* @__PURE__ */ jsx(Text, {
|
|
5360
5494
|
color: "red",
|
|
5361
5495
|
children: "ListEditorScreen rendered outside a listEditor screen."
|
|
@@ -5372,6 +5506,36 @@ function ListEditorScreen() {
|
|
|
5372
5506
|
"."
|
|
5373
5507
|
]
|
|
5374
5508
|
});
|
|
5509
|
+
if (editingIndex !== void 0) {
|
|
5510
|
+
const item = items[editingIndex];
|
|
5511
|
+
if (item === void 0) throw new Error(`ListEditorScreen is editing item index ${editingIndex}, but list ${screen.blockIndex} only has ${items.length} items -- selecting a row always sets editingIndex to a valid index from that same items array, so this indicates a bug in that selection.`);
|
|
5512
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
5513
|
+
flexDirection: "column",
|
|
5514
|
+
children: [/* @__PURE__ */ jsxs(Text, {
|
|
5515
|
+
bold: true,
|
|
5516
|
+
children: [
|
|
5517
|
+
"List ",
|
|
5518
|
+
screen.blockIndex,
|
|
5519
|
+
", item ",
|
|
5520
|
+
editingIndex + 1
|
|
5521
|
+
]
|
|
5522
|
+
}), /* @__PURE__ */ jsx(RunTextEditor, {
|
|
5523
|
+
initialText: item.text,
|
|
5524
|
+
onCommit: (text) => {
|
|
5525
|
+
dispatch({
|
|
5526
|
+
type: "SET_LIST_ITEM_TEXT",
|
|
5527
|
+
blockIndex: screen.blockIndex,
|
|
5528
|
+
itemIndex: editingIndex,
|
|
5529
|
+
text
|
|
5530
|
+
});
|
|
5531
|
+
setEditingIndex(void 0);
|
|
5532
|
+
},
|
|
5533
|
+
onCancel: () => {
|
|
5534
|
+
setEditingIndex(void 0);
|
|
5535
|
+
}
|
|
5536
|
+
})]
|
|
5537
|
+
});
|
|
5538
|
+
}
|
|
5375
5539
|
return /* @__PURE__ */ jsxs(Box, {
|
|
5376
5540
|
flexDirection: "column",
|
|
5377
5541
|
children: [
|
|
@@ -5387,13 +5551,23 @@ function ListEditorScreen() {
|
|
|
5387
5551
|
")"
|
|
5388
5552
|
]
|
|
5389
5553
|
}),
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5554
|
+
/* @__PURE__ */ jsx(ListView, {
|
|
5555
|
+
items: rows,
|
|
5556
|
+
selectedIndex,
|
|
5557
|
+
emptyMessage: "This list has no items yet -- press 'a' to add one.",
|
|
5558
|
+
renderItem: (row, isSelected) => {
|
|
5559
|
+
const trimmed = row.item.text.trim();
|
|
5560
|
+
return /* @__PURE__ */ jsxs(Text, {
|
|
5561
|
+
color: isSelected ? "cyan" : void 0,
|
|
5562
|
+
inverse: isSelected,
|
|
5563
|
+
children: [
|
|
5564
|
+
row.index + 1,
|
|
5565
|
+
". ",
|
|
5566
|
+
trimmed.length === 0 ? "(empty)" : row.item.text
|
|
5567
|
+
]
|
|
5568
|
+
});
|
|
5569
|
+
}
|
|
5570
|
+
}),
|
|
5397
5571
|
isAdding ? /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
5398
5572
|
color: "cyan",
|
|
5399
5573
|
children: "+ "
|
|
@@ -5417,7 +5591,7 @@ function ListEditorScreen() {
|
|
|
5417
5591
|
}
|
|
5418
5592
|
})] }) : /* @__PURE__ */ jsx(Text, {
|
|
5419
5593
|
dimColor: true,
|
|
5420
|
-
children: "
|
|
5594
|
+
children: "Enter to edit an item, a to add, Esc back"
|
|
5421
5595
|
})
|
|
5422
5596
|
]
|
|
5423
5597
|
});
|
|
@@ -6240,6 +6414,67 @@ function SaveAsPromptScreen() {
|
|
|
6240
6414
|
});
|
|
6241
6415
|
}
|
|
6242
6416
|
//#endregion
|
|
6417
|
+
//#region src/tui/format/read-metadata.ts
|
|
6418
|
+
function metadataFor(doc) {
|
|
6419
|
+
switch (doc.format) {
|
|
6420
|
+
case "docx": return readDocxContent(doc.editor.toPackage()).metadata;
|
|
6421
|
+
case "pptx": return readPptxContent(doc.editor.toPackage()).metadata;
|
|
6422
|
+
case "odt": return readOdtContent(doc.editor.toPackage()).metadata;
|
|
6423
|
+
case "odp": return readOdpContent(doc.editor.toPackage()).metadata;
|
|
6424
|
+
case "ods": return readOdsContent(doc.editor.toPackage()).metadata;
|
|
6425
|
+
case "odg": return readOdgContent(doc.editor.toPackage()).metadata;
|
|
6426
|
+
case "markdown": return readMarkdownContent(doc.source).metadata;
|
|
6427
|
+
case "pdf":
|
|
6428
|
+
case "xlsx": return doc.layout.metadata;
|
|
6429
|
+
case "odb": throw new Error("A .odb database has no document-level metadata -- it is a table/form/report container, not a single document with its own title/author/etc.");
|
|
6430
|
+
}
|
|
6431
|
+
}
|
|
6432
|
+
//#endregion
|
|
6433
|
+
//#region src/tui/screens/shared/metadata.tsx
|
|
6434
|
+
function MetadataScreen() {
|
|
6435
|
+
const state = useAppState();
|
|
6436
|
+
const dispatch = useAppDispatch();
|
|
6437
|
+
const doc = state.openDocument;
|
|
6438
|
+
useInput((input, key) => {
|
|
6439
|
+
if (key.escape || key.leftArrow || input === "h") dispatch({ type: "POP_SCREEN" });
|
|
6440
|
+
}, { isActive: doc !== void 0 && !anyOverlayOpen(state) });
|
|
6441
|
+
if (doc === void 0) return /* @__PURE__ */ jsx(Text, {
|
|
6442
|
+
color: "red",
|
|
6443
|
+
children: "MetadataScreen requires an open document."
|
|
6444
|
+
});
|
|
6445
|
+
let lines;
|
|
6446
|
+
let errorMessage;
|
|
6447
|
+
try {
|
|
6448
|
+
lines = formatMetadataLines(metadataFor(doc));
|
|
6449
|
+
} catch (error) {
|
|
6450
|
+
errorMessage = error instanceof Error ? error.message : String(error);
|
|
6451
|
+
}
|
|
6452
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
6453
|
+
flexDirection: "column",
|
|
6454
|
+
children: [
|
|
6455
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
6456
|
+
bold: true,
|
|
6457
|
+
children: [
|
|
6458
|
+
"Document metadata (",
|
|
6459
|
+
doc.format,
|
|
6460
|
+
")"
|
|
6461
|
+
]
|
|
6462
|
+
}),
|
|
6463
|
+
errorMessage !== void 0 ? /* @__PURE__ */ jsx(Text, {
|
|
6464
|
+
color: "yellow",
|
|
6465
|
+
children: errorMessage
|
|
6466
|
+
}) : lines !== void 0 && lines.length > 0 ? lines.map((line) => /* @__PURE__ */ jsx(Text, { children: line }, line)) : /* @__PURE__ */ jsx(Text, {
|
|
6467
|
+
dimColor: true,
|
|
6468
|
+
children: "This document carries no metadata."
|
|
6469
|
+
}),
|
|
6470
|
+
/* @__PURE__ */ jsx(Text, {
|
|
6471
|
+
dimColor: true,
|
|
6472
|
+
children: "Esc / ← / h to go back"
|
|
6473
|
+
})
|
|
6474
|
+
]
|
|
6475
|
+
});
|
|
6476
|
+
}
|
|
6477
|
+
//#endregion
|
|
6243
6478
|
//#region src/tui/app.tsx
|
|
6244
6479
|
function App(props) {
|
|
6245
6480
|
return /* @__PURE__ */ jsx(AppStateProvider, {
|
|
@@ -6285,6 +6520,7 @@ function ScreenBody({ screen }) {
|
|
|
6285
6520
|
case "pdfItemDetail": return /* @__PURE__ */ jsx(PdfItemDetailScreen, {});
|
|
6286
6521
|
case "exportOptions": return /* @__PURE__ */ jsx(ExportOptionsScreen, {});
|
|
6287
6522
|
case "saveAsPrompt": return /* @__PURE__ */ jsx(SaveAsPromptScreen, {});
|
|
6523
|
+
case "metadata": return /* @__PURE__ */ jsx(MetadataScreen, {});
|
|
6288
6524
|
}
|
|
6289
6525
|
}
|
|
6290
6526
|
function Overlay({ state }) {
|
|
@@ -6374,6 +6610,10 @@ function AppShell({ startPath }) {
|
|
|
6374
6610
|
dispatch({ type: "REQUEST_CLOSE" });
|
|
6375
6611
|
return;
|
|
6376
6612
|
}
|
|
6613
|
+
if (key.ctrl && input === "z") {
|
|
6614
|
+
dispatch({ type: "UNDO" });
|
|
6615
|
+
return;
|
|
6616
|
+
}
|
|
6377
6617
|
if (input === ":") {
|
|
6378
6618
|
dispatch({
|
|
6379
6619
|
type: "OPEN_OVERLAY",
|
|
@@ -6395,10 +6635,27 @@ function AppShell({ startPath }) {
|
|
|
6395
6635
|
});
|
|
6396
6636
|
return;
|
|
6397
6637
|
}
|
|
6398
|
-
if (key.ctrl && input === "d")
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6638
|
+
if (key.ctrl && input === "d") {
|
|
6639
|
+
dispatch({
|
|
6640
|
+
type: "OPEN_OVERLAY",
|
|
6641
|
+
overlay: "diagnosticsPanel"
|
|
6642
|
+
});
|
|
6643
|
+
return;
|
|
6644
|
+
}
|
|
6645
|
+
if (input === "m") {
|
|
6646
|
+
if (state.openDocument === void 0) {
|
|
6647
|
+
dispatch({
|
|
6648
|
+
type: "SET_STATUS",
|
|
6649
|
+
severity: "warning",
|
|
6650
|
+
text: "There is no open document to show metadata for"
|
|
6651
|
+
});
|
|
6652
|
+
return;
|
|
6653
|
+
}
|
|
6654
|
+
dispatch({
|
|
6655
|
+
type: "PUSH_SCREEN",
|
|
6656
|
+
screen: { kind: "metadata" }
|
|
6657
|
+
});
|
|
6658
|
+
}
|
|
6402
6659
|
}, { isActive: !overlayOpen });
|
|
6403
6660
|
return /* @__PURE__ */ jsxs(Box, {
|
|
6404
6661
|
flexDirection: "column",
|