document-cli 1.6.0 → 1.7.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-CqYlr4nN.js → tui-BzdX4QQg.js} +572 -53
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1194,7 +1194,7 @@ function registerSetMetadataCommand(program) {
|
|
|
1194
1194
|
}
|
|
1195
1195
|
//#endregion
|
|
1196
1196
|
//#region package.json
|
|
1197
|
-
var version = "1.
|
|
1197
|
+
var version = "1.7.0";
|
|
1198
1198
|
//#endregion
|
|
1199
1199
|
//#region src/program.ts
|
|
1200
1200
|
function createProgram() {
|
|
@@ -1221,7 +1221,7 @@ function createProgram() {
|
|
|
1221
1221
|
//#region src/cli.ts
|
|
1222
1222
|
async function launchTui(startPath, signal) {
|
|
1223
1223
|
try {
|
|
1224
|
-
const { runTui } = await import("./tui-
|
|
1224
|
+
const { runTui } = await import("./tui-BzdX4QQg.js");
|
|
1225
1225
|
await runTui({
|
|
1226
1226
|
startPath,
|
|
1227
1227
|
signal
|
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
|
@@ -230,6 +230,7 @@ function selectionKeyFor(screen) {
|
|
|
230
230
|
case "slideDetail":
|
|
231
231
|
case "notesEditor": return `${screen.kind}:${screen.slideIndex}`;
|
|
232
232
|
case "shapeEditor": return `shapeEditor:${screen.slideIndex}:${screen.shapeIndex}`;
|
|
233
|
+
case "slideTableDetail": return `slideTableDetail:${screen.slideIndex}:${screen.tableIndex}`;
|
|
233
234
|
case "spreadsheetGrid":
|
|
234
235
|
case "printSettingsEditor": return `${screen.kind}:${screen.sheetIndex}`;
|
|
235
236
|
case "cellDetail": return `cellDetail:${screen.sheetIndex}:${screen.row}:${screen.col}`;
|
|
@@ -435,6 +436,38 @@ function mutate(state, doc, apply) {
|
|
|
435
436
|
undoStack: pushSnapshot(state.undoStack, snapshot)
|
|
436
437
|
};
|
|
437
438
|
}
|
|
439
|
+
function mutateGuarded(state, doc, apply) {
|
|
440
|
+
try {
|
|
441
|
+
return mutate(state, doc, apply);
|
|
442
|
+
} catch (error) {
|
|
443
|
+
return withStatus(state, "warning", error instanceof Error ? error.message : String(error));
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function mergePptxTableCells(table, startRow, startColumn, rowSpan, colSpan) {
|
|
447
|
+
if (!Number.isInteger(rowSpan) || rowSpan < 1 || !Number.isInteger(colSpan) || colSpan < 1) throw new Error(`mergeSlideTableCells: rowSpan and colSpan must be positive integers, got rowSpan=${rowSpan}, colSpan=${colSpan}`);
|
|
448
|
+
const rows = table.rows();
|
|
449
|
+
if (startRow + rowSpan > rows.length) throw new Error(`mergeSlideTableCells: rowSpan ${rowSpan} starting at row ${startRow} exceeds this table's own ${rows.length} rows`);
|
|
450
|
+
const anchorRow = rows[startRow];
|
|
451
|
+
if (anchorRow === void 0) throw new Error(`mergeSlideTableCells: row ${startRow} does not exist in this table`);
|
|
452
|
+
const columnCount = anchorRow.cells().length;
|
|
453
|
+
if (startColumn + colSpan > columnCount) throw new Error(`mergeSlideTableCells: colSpan ${colSpan} starting at column ${startColumn} exceeds this table's own ${columnCount} columns`);
|
|
454
|
+
for (let rowOffset = 0; rowOffset < rowSpan; rowOffset++) {
|
|
455
|
+
const row = rows[startRow + rowOffset];
|
|
456
|
+
if (row === void 0) throw new Error(`mergeSlideTableCells: row ${startRow + rowOffset} does not exist in this table`);
|
|
457
|
+
const cells = row.cells();
|
|
458
|
+
for (let columnOffset = 0; columnOffset < colSpan; columnOffset++) {
|
|
459
|
+
const cell = cells[startColumn + columnOffset];
|
|
460
|
+
if (cell === void 0) throw new Error(`mergeSlideTableCells: column ${startColumn + columnOffset} does not exist in row ${startRow + rowOffset}`);
|
|
461
|
+
if (rowOffset === 0 && columnOffset === 0) {
|
|
462
|
+
cell.colSpan = colSpan;
|
|
463
|
+
cell.rowSpan = rowSpan;
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
if (columnOffset > 0) cell.horizontalMerge = true;
|
|
467
|
+
if (rowOffset > 0) cell.verticalMerge = true;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
438
471
|
function mutateMarkdown(state, doc, source) {
|
|
439
472
|
const snapshot = encodeMarkdownText(doc.source);
|
|
440
473
|
return {
|
|
@@ -685,11 +718,21 @@ function appReducer(state, action) {
|
|
|
685
718
|
case "APPEND_TABLE": {
|
|
686
719
|
const doc = wordprocessingDocument(state);
|
|
687
720
|
if (doc === void 0) return wrongDocument(state, "a docx or odt document");
|
|
688
|
-
return
|
|
689
|
-
doc.editor.body.appendTable({
|
|
721
|
+
return mutateGuarded(state, doc, () => {
|
|
722
|
+
const table = doc.editor.body.appendTable({
|
|
690
723
|
rows: action.rows,
|
|
691
724
|
columns: action.columns
|
|
692
725
|
});
|
|
726
|
+
if (action.merge !== void 0) table.mergeCells(action.merge.startRow, action.merge.startColumn, action.merge.rowSpan, action.merge.colSpan);
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
case "MERGE_TABLE_CELLS": {
|
|
730
|
+
const doc = wordprocessingDocument(state);
|
|
731
|
+
if (doc === void 0) return wrongDocument(state, "a docx or odt document");
|
|
732
|
+
const table = tableAt(doc, action.tableIndex);
|
|
733
|
+
if (table === void 0) return withStatus(state, "warning", `There is no table at index ${action.tableIndex}`);
|
|
734
|
+
return mutateGuarded(state, doc, () => {
|
|
735
|
+
table.mergeCells(action.startRow, action.startColumn, action.rowSpan, action.colSpan);
|
|
693
736
|
});
|
|
694
737
|
}
|
|
695
738
|
case "SET_TABLE_CELL_TEXT": {
|
|
@@ -754,6 +797,22 @@ function appReducer(state, action) {
|
|
|
754
797
|
});
|
|
755
798
|
});
|
|
756
799
|
}
|
|
800
|
+
case "MERGE_SLIDE_TABLE_CELLS": {
|
|
801
|
+
const doc = presentationDocument(state);
|
|
802
|
+
if (doc === void 0) return wrongDocument(state, "a pptx or odp document");
|
|
803
|
+
if (doc.format === "odp") {
|
|
804
|
+
const entry = doc.editor.slides()[action.slideIndex]?.tables()[action.tableIndex];
|
|
805
|
+
if (entry === void 0) return withStatus(state, "warning", `There is no table at index ${action.tableIndex} on slide ${action.slideIndex}`);
|
|
806
|
+
return mutateGuarded(state, doc, () => {
|
|
807
|
+
entry.table.mergeCells(action.startRow, action.startColumn, action.rowSpan, action.colSpan);
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
const table = doc.editor.slides()[action.slideIndex]?.tables()[action.tableIndex];
|
|
811
|
+
if (table === void 0) return withStatus(state, "warning", `There is no table at index ${action.tableIndex} on slide ${action.slideIndex}`);
|
|
812
|
+
return mutateGuarded(state, doc, () => {
|
|
813
|
+
mergePptxTableCells(table, action.startRow, action.startColumn, action.rowSpan, action.colSpan);
|
|
814
|
+
});
|
|
815
|
+
}
|
|
757
816
|
case "ADD_TEXTBOX": {
|
|
758
817
|
const doc = shapeHostDocument(state);
|
|
759
818
|
if (doc === void 0) return wrongDocument(state, "a pptx, odp or odg document");
|
|
@@ -826,6 +885,15 @@ function appReducer(state, action) {
|
|
|
826
885
|
case "SET_CELL_VALUE": return withSheet(state, action.sheetIndex, (sheet) => {
|
|
827
886
|
sheet.cell(action.row, action.column).value = action.value;
|
|
828
887
|
});
|
|
888
|
+
case "MERGE_CELLS": {
|
|
889
|
+
const doc = spreadsheetDocument(state);
|
|
890
|
+
if (doc === void 0) return wrongDocument(state, "an ods document");
|
|
891
|
+
const sheet = sheetAt(doc, action.sheetIndex);
|
|
892
|
+
if (sheet === void 0) return withStatus(state, "warning", `There is no sheet at index ${action.sheetIndex}`);
|
|
893
|
+
return mutateGuarded(state, doc, () => {
|
|
894
|
+
sheet.mergeCells(action.startRow, action.startColumn, action.rowSpan, action.colSpan);
|
|
895
|
+
});
|
|
896
|
+
}
|
|
829
897
|
case "SET_SHEET_PRINT_SETTINGS": return withSheet(state, action.sheetIndex, (sheet) => {
|
|
830
898
|
sheet.printSettings = action.printSettings;
|
|
831
899
|
});
|
|
@@ -1606,6 +1674,14 @@ function truncatePreview(text, maxLength) {
|
|
|
1606
1674
|
if (singleLine.length === 0) return "(empty)";
|
|
1607
1675
|
return singleLine.length > maxLength ? `${singleLine.slice(0, Math.max(0, maxLength - 1))}…` : singleLine;
|
|
1608
1676
|
}
|
|
1677
|
+
function parsePositiveIntField(raw, fallback) {
|
|
1678
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1679
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
1680
|
+
}
|
|
1681
|
+
function parseNonNegativeIntField(raw, fallback) {
|
|
1682
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1683
|
+
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
|
1684
|
+
}
|
|
1609
1685
|
//#endregion
|
|
1610
1686
|
//#region src/tui/screens/shared/paragraph-family.tsx
|
|
1611
1687
|
function paragraphFamilyDocument(openDocument) {
|
|
@@ -1669,6 +1745,8 @@ function tableSummary(table) {
|
|
|
1669
1745
|
function listSummary(list, index) {
|
|
1670
1746
|
return `List ${index} (${list.itemCount} item${list.itemCount === 1 ? "" : "s"})`;
|
|
1671
1747
|
}
|
|
1748
|
+
const DEFAULT_TABLE_ROWS$1 = 2;
|
|
1749
|
+
const DEFAULT_TABLE_COLUMNS$1 = 2;
|
|
1672
1750
|
function ParagraphFamilyBodyList(props) {
|
|
1673
1751
|
const { adapter } = props;
|
|
1674
1752
|
const state = useAppState();
|
|
@@ -1711,9 +1789,88 @@ function ParagraphFamilyBodyList(props) {
|
|
|
1711
1789
|
if (row.kind !== "header") acc.push(rowIndex);
|
|
1712
1790
|
return acc;
|
|
1713
1791
|
}, []);
|
|
1792
|
+
const [tableWizard, setTableWizard] = useState("closed");
|
|
1793
|
+
const [wizardDraft, setWizardDraft] = useState("");
|
|
1794
|
+
const [wizardRows, setWizardRows] = useState(DEFAULT_TABLE_ROWS$1);
|
|
1795
|
+
const [wizardColumns, setWizardColumns] = useState(DEFAULT_TABLE_COLUMNS$1);
|
|
1796
|
+
const [wizardStartRow, setWizardStartRow] = useState(0);
|
|
1797
|
+
const [wizardStartColumn, setWizardStartColumn] = useState(0);
|
|
1798
|
+
const [wizardRowSpan, setWizardRowSpan] = useState(1);
|
|
1799
|
+
const wizardOpen = tableWizard !== "closed";
|
|
1800
|
+
const closeWizard = () => {
|
|
1801
|
+
setTableWizard("closed");
|
|
1802
|
+
};
|
|
1803
|
+
const commitAppendTable = (merge) => {
|
|
1804
|
+
const newIndex = tables.length;
|
|
1805
|
+
dispatch({
|
|
1806
|
+
type: "APPEND_TABLE",
|
|
1807
|
+
rows: wizardRows,
|
|
1808
|
+
columns: wizardColumns,
|
|
1809
|
+
merge
|
|
1810
|
+
});
|
|
1811
|
+
closeWizard();
|
|
1812
|
+
dispatch({
|
|
1813
|
+
type: "PUSH_SCREEN",
|
|
1814
|
+
screen: {
|
|
1815
|
+
kind: "tableView",
|
|
1816
|
+
blockIndex: newIndex
|
|
1817
|
+
}
|
|
1818
|
+
});
|
|
1819
|
+
};
|
|
1820
|
+
useInput((input) => {
|
|
1821
|
+
if (input === "T") {
|
|
1822
|
+
setWizardDraft(String(DEFAULT_TABLE_ROWS$1));
|
|
1823
|
+
setTableWizard("rows");
|
|
1824
|
+
}
|
|
1825
|
+
}, { isActive: !anyOverlayOpen(state) && !wizardOpen });
|
|
1826
|
+
useInput((input, key) => {
|
|
1827
|
+
if (key.escape) {
|
|
1828
|
+
closeWizard();
|
|
1829
|
+
return;
|
|
1830
|
+
}
|
|
1831
|
+
if (input === "y" || input === "Y") {
|
|
1832
|
+
setWizardDraft("0");
|
|
1833
|
+
setTableWizard("mergeStartRow");
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
if (input === "n" || input === "N" || key.return) commitAppendTable(void 0);
|
|
1837
|
+
}, { isActive: !anyOverlayOpen(state) && tableWizard === "mergePrompt" });
|
|
1838
|
+
const submitWizardRows = (raw) => {
|
|
1839
|
+
setWizardRows(parsePositiveIntField(raw, DEFAULT_TABLE_ROWS$1));
|
|
1840
|
+
setWizardDraft(String(DEFAULT_TABLE_COLUMNS$1));
|
|
1841
|
+
setTableWizard("columns");
|
|
1842
|
+
};
|
|
1843
|
+
const submitWizardColumns = (raw) => {
|
|
1844
|
+
setWizardColumns(parsePositiveIntField(raw, DEFAULT_TABLE_COLUMNS$1));
|
|
1845
|
+
setTableWizard("mergePrompt");
|
|
1846
|
+
};
|
|
1847
|
+
const submitWizardMergeStartRow = (raw) => {
|
|
1848
|
+
setWizardStartRow(Math.min(parseNonNegativeIntField(raw, 0), Math.max(0, wizardRows - 1)));
|
|
1849
|
+
setWizardDraft("0");
|
|
1850
|
+
setTableWizard("mergeStartColumn");
|
|
1851
|
+
};
|
|
1852
|
+
const submitWizardMergeStartColumn = (raw) => {
|
|
1853
|
+
setWizardStartColumn(Math.min(parseNonNegativeIntField(raw, 0), Math.max(0, wizardColumns - 1)));
|
|
1854
|
+
setWizardDraft("1");
|
|
1855
|
+
setTableWizard("mergeRowSpan");
|
|
1856
|
+
};
|
|
1857
|
+
const submitWizardMergeRowSpan = (raw) => {
|
|
1858
|
+
setWizardRowSpan(Math.min(parsePositiveIntField(raw, 1), Math.max(1, wizardRows - wizardStartRow)));
|
|
1859
|
+
setWizardDraft("1");
|
|
1860
|
+
setTableWizard("mergeColSpan");
|
|
1861
|
+
};
|
|
1862
|
+
const submitWizardMergeColSpan = (raw) => {
|
|
1863
|
+
const colSpan = Math.min(parsePositiveIntField(raw, 1), Math.max(1, wizardColumns - wizardStartColumn));
|
|
1864
|
+
commitAppendTable({
|
|
1865
|
+
startRow: wizardStartRow,
|
|
1866
|
+
startColumn: wizardStartColumn,
|
|
1867
|
+
rowSpan: wizardRowSpan,
|
|
1868
|
+
colSpan
|
|
1869
|
+
});
|
|
1870
|
+
};
|
|
1714
1871
|
const { selectedIndex } = usePersistedSelection(selectionKeyFor({ kind: "bodyList" }), {
|
|
1715
1872
|
itemCount: selectableRowIndices.length,
|
|
1716
|
-
isActive: !anyOverlayOpen(state),
|
|
1873
|
+
isActive: !anyOverlayOpen(state) && !wizardOpen,
|
|
1717
1874
|
onBack: () => {
|
|
1718
1875
|
dispatch({ type: "POP_SCREEN" });
|
|
1719
1876
|
},
|
|
@@ -1800,9 +1957,89 @@ function ParagraphFamilyBodyList(props) {
|
|
|
1800
1957
|
});
|
|
1801
1958
|
}
|
|
1802
1959
|
}),
|
|
1960
|
+
tableWizard === "rows" ? /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
1961
|
+
color: "cyan",
|
|
1962
|
+
children: "Rows: "
|
|
1963
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
1964
|
+
value: wizardDraft,
|
|
1965
|
+
isFocused: true,
|
|
1966
|
+
onChange: setWizardDraft,
|
|
1967
|
+
onSubmit: submitWizardRows,
|
|
1968
|
+
onCancel: closeWizard
|
|
1969
|
+
})] }) : void 0,
|
|
1970
|
+
tableWizard === "columns" ? /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
1971
|
+
color: "cyan",
|
|
1972
|
+
children: "Columns: "
|
|
1973
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
1974
|
+
value: wizardDraft,
|
|
1975
|
+
isFocused: true,
|
|
1976
|
+
onChange: setWizardDraft,
|
|
1977
|
+
onSubmit: submitWizardColumns,
|
|
1978
|
+
onCancel: closeWizard
|
|
1979
|
+
})] }) : void 0,
|
|
1980
|
+
tableWizard === "mergePrompt" ? /* @__PURE__ */ jsx(Text, {
|
|
1981
|
+
color: "cyan",
|
|
1982
|
+
children: "Merge cells now? y/N"
|
|
1983
|
+
}) : void 0,
|
|
1984
|
+
tableWizard === "mergeStartRow" ? /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsxs(Text, {
|
|
1985
|
+
color: "cyan",
|
|
1986
|
+
children: [
|
|
1987
|
+
"Merge start row (0-",
|
|
1988
|
+
Math.max(0, wizardRows - 1),
|
|
1989
|
+
"): "
|
|
1990
|
+
]
|
|
1991
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
1992
|
+
value: wizardDraft,
|
|
1993
|
+
isFocused: true,
|
|
1994
|
+
onChange: setWizardDraft,
|
|
1995
|
+
onSubmit: submitWizardMergeStartRow,
|
|
1996
|
+
onCancel: closeWizard
|
|
1997
|
+
})] }) : void 0,
|
|
1998
|
+
tableWizard === "mergeStartColumn" ? /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsxs(Text, {
|
|
1999
|
+
color: "cyan",
|
|
2000
|
+
children: [
|
|
2001
|
+
"Merge start column (0-",
|
|
2002
|
+
Math.max(0, wizardColumns - 1),
|
|
2003
|
+
"): "
|
|
2004
|
+
]
|
|
2005
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
2006
|
+
value: wizardDraft,
|
|
2007
|
+
isFocused: true,
|
|
2008
|
+
onChange: setWizardDraft,
|
|
2009
|
+
onSubmit: submitWizardMergeStartColumn,
|
|
2010
|
+
onCancel: closeWizard
|
|
2011
|
+
})] }) : void 0,
|
|
2012
|
+
tableWizard === "mergeRowSpan" ? /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsxs(Text, {
|
|
2013
|
+
color: "cyan",
|
|
2014
|
+
children: [
|
|
2015
|
+
"Merge row span (1-",
|
|
2016
|
+
Math.max(1, wizardRows - wizardStartRow),
|
|
2017
|
+
"): "
|
|
2018
|
+
]
|
|
2019
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
2020
|
+
value: wizardDraft,
|
|
2021
|
+
isFocused: true,
|
|
2022
|
+
onChange: setWizardDraft,
|
|
2023
|
+
onSubmit: submitWizardMergeRowSpan,
|
|
2024
|
+
onCancel: closeWizard
|
|
2025
|
+
})] }) : void 0,
|
|
2026
|
+
tableWizard === "mergeColSpan" ? /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsxs(Text, {
|
|
2027
|
+
color: "cyan",
|
|
2028
|
+
children: [
|
|
2029
|
+
"Merge column span (1-",
|
|
2030
|
+
Math.max(1, wizardColumns - wizardStartColumn),
|
|
2031
|
+
"): "
|
|
2032
|
+
]
|
|
2033
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
2034
|
+
value: wizardDraft,
|
|
2035
|
+
isFocused: true,
|
|
2036
|
+
onChange: setWizardDraft,
|
|
2037
|
+
onSubmit: submitWizardMergeColSpan,
|
|
2038
|
+
onCancel: closeWizard
|
|
2039
|
+
})] }) : void 0,
|
|
1803
2040
|
/* @__PURE__ */ jsx(Text, {
|
|
1804
2041
|
dimColor: true,
|
|
1805
|
-
children: "Enter to open, a to append a paragraph, Esc back"
|
|
2042
|
+
children: "Enter to open, a to append a paragraph, T to append a table, Esc back"
|
|
1806
2043
|
})
|
|
1807
2044
|
]
|
|
1808
2045
|
});
|
|
@@ -2079,7 +2316,7 @@ function RunEditorScreen() {
|
|
|
2079
2316
|
}
|
|
2080
2317
|
//#endregion
|
|
2081
2318
|
//#region src/tui/screens/editors/docx/table-view.tsx
|
|
2082
|
-
const CELL_WIDTH$
|
|
2319
|
+
const CELL_WIDTH$2 = 16;
|
|
2083
2320
|
function TableViewScreen() {
|
|
2084
2321
|
const state = useAppState();
|
|
2085
2322
|
const dispatch = useAppDispatch();
|
|
@@ -2087,6 +2324,7 @@ function TableViewScreen() {
|
|
|
2087
2324
|
row: 0,
|
|
2088
2325
|
column: 0
|
|
2089
2326
|
});
|
|
2327
|
+
const [mergeAnchor, setMergeAnchor] = useState(void 0);
|
|
2090
2328
|
const screen = currentScreen(state);
|
|
2091
2329
|
const doc = paragraphFamilyDocument(state.openDocument);
|
|
2092
2330
|
const table = screen.kind === "tableView" && doc !== void 0 ? liveTableAt(doc, screen.blockIndex) : void 0;
|
|
@@ -2095,6 +2333,22 @@ function TableViewScreen() {
|
|
|
2095
2333
|
const columnCount = rows[0]?.cells().length ?? 0;
|
|
2096
2334
|
const clampedRow = rowCount === 0 ? 0 : Math.min(cursor.row, rowCount - 1);
|
|
2097
2335
|
const clampedColumn = columnCount === 0 ? 0 : Math.min(cursor.column, columnCount - 1);
|
|
2336
|
+
const commitMerge = (anchor) => {
|
|
2337
|
+
if (screen.kind !== "tableView") return;
|
|
2338
|
+
const startRow = Math.min(anchor.row, clampedRow);
|
|
2339
|
+
const startColumn = Math.min(anchor.column, clampedColumn);
|
|
2340
|
+
const rowSpan = Math.abs(clampedRow - anchor.row) + 1;
|
|
2341
|
+
const colSpan = Math.abs(clampedColumn - anchor.column) + 1;
|
|
2342
|
+
dispatch({
|
|
2343
|
+
type: "MERGE_TABLE_CELLS",
|
|
2344
|
+
tableIndex: screen.blockIndex,
|
|
2345
|
+
startRow,
|
|
2346
|
+
startColumn,
|
|
2347
|
+
rowSpan,
|
|
2348
|
+
colSpan
|
|
2349
|
+
});
|
|
2350
|
+
setMergeAnchor(void 0);
|
|
2351
|
+
};
|
|
2098
2352
|
useInput((input, key) => {
|
|
2099
2353
|
if (table === void 0 || screen.kind !== "tableView") return;
|
|
2100
2354
|
if (key.upArrow || input === "k") {
|
|
@@ -2126,18 +2380,36 @@ function TableViewScreen() {
|
|
|
2126
2380
|
return;
|
|
2127
2381
|
}
|
|
2128
2382
|
if (key.escape) {
|
|
2383
|
+
if (mergeAnchor !== void 0) {
|
|
2384
|
+
setMergeAnchor(void 0);
|
|
2385
|
+
return;
|
|
2386
|
+
}
|
|
2129
2387
|
dispatch({ type: "POP_SCREEN" });
|
|
2130
2388
|
return;
|
|
2131
2389
|
}
|
|
2132
|
-
if (
|
|
2133
|
-
|
|
2134
|
-
screen: {
|
|
2135
|
-
kind: "tableCellDetail",
|
|
2136
|
-
blockIndex: screen.blockIndex,
|
|
2390
|
+
if (input === "m" && rowCount > 0 && columnCount > 0) {
|
|
2391
|
+
if (mergeAnchor === void 0) setMergeAnchor({
|
|
2137
2392
|
row: clampedRow,
|
|
2138
|
-
|
|
2393
|
+
column: clampedColumn
|
|
2394
|
+
});
|
|
2395
|
+
else commitMerge(mergeAnchor);
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
if (key.return && rowCount > 0 && columnCount > 0) {
|
|
2399
|
+
if (mergeAnchor !== void 0) {
|
|
2400
|
+
commitMerge(mergeAnchor);
|
|
2401
|
+
return;
|
|
2139
2402
|
}
|
|
2140
|
-
|
|
2403
|
+
dispatch({
|
|
2404
|
+
type: "PUSH_SCREEN",
|
|
2405
|
+
screen: {
|
|
2406
|
+
kind: "tableCellDetail",
|
|
2407
|
+
blockIndex: screen.blockIndex,
|
|
2408
|
+
row: clampedRow,
|
|
2409
|
+
col: clampedColumn
|
|
2410
|
+
}
|
|
2411
|
+
});
|
|
2412
|
+
}
|
|
2141
2413
|
}, { isActive: !anyOverlayOpen(state) });
|
|
2142
2414
|
if (screen.kind !== "tableView") return /* @__PURE__ */ jsx(Text, {
|
|
2143
2415
|
color: "red",
|
|
@@ -2175,10 +2447,11 @@ function TableViewScreen() {
|
|
|
2175
2447
|
children: "This table has no rows."
|
|
2176
2448
|
}) : rows.map((row, rowIndex) => /* @__PURE__ */ jsx(Box, { children: row.cells().map((cell, columnIndex) => {
|
|
2177
2449
|
const isSelected = rowIndex === clampedRow && columnIndex === clampedColumn;
|
|
2450
|
+
const isAnchor = rowIndex === mergeAnchor?.row && columnIndex === mergeAnchor?.column;
|
|
2178
2451
|
return /* @__PURE__ */ jsx(Box, {
|
|
2179
|
-
width: CELL_WIDTH$
|
|
2452
|
+
width: CELL_WIDTH$2,
|
|
2180
2453
|
borderStyle: "single",
|
|
2181
|
-
borderColor: isSelected ? "cyan" : "gray",
|
|
2454
|
+
borderColor: isSelected ? "cyan" : isAnchor ? "yellow" : "gray",
|
|
2182
2455
|
children: /* @__PURE__ */ jsx(Text, {
|
|
2183
2456
|
color: isSelected ? "cyan" : void 0,
|
|
2184
2457
|
inverse: isSelected,
|
|
@@ -2188,7 +2461,7 @@ function TableViewScreen() {
|
|
|
2188
2461
|
}) }, rowIndex)),
|
|
2189
2462
|
/* @__PURE__ */ jsx(Text, {
|
|
2190
2463
|
dimColor: true,
|
|
2191
|
-
children: "Arrows/hjkl move, Enter to edit a cell, Esc back"
|
|
2464
|
+
children: mergeAnchor === void 0 ? "Arrows/hjkl move, Enter to edit a cell, m to anchor a merge, Esc back" : "Arrows/hjkl to the opposite corner, m/Enter to merge, Esc to cancel"
|
|
2192
2465
|
})
|
|
2193
2466
|
]
|
|
2194
2467
|
});
|
|
@@ -4367,6 +4640,30 @@ function ShapeEditorScreen(props) {
|
|
|
4367
4640
|
});
|
|
4368
4641
|
}
|
|
4369
4642
|
//#endregion
|
|
4643
|
+
//#region src/tui/screens/shared/slide-table.ts
|
|
4644
|
+
function resolveSlideTable(doc, slideIndex, tableIndex) {
|
|
4645
|
+
const content = doc.format === "odp" ? readOdpContent(doc.editor.toPackage()) : readPptxContent(doc.editor.toPackage());
|
|
4646
|
+
if (content.kind !== "presentation") throw new Error("readPptxContent/readOdpContent always resolve a presentation package to the presentation ContentDocument variant.");
|
|
4647
|
+
const slide = content.slides[slideIndex];
|
|
4648
|
+
if (slide === void 0) return;
|
|
4649
|
+
return slide.shapes.flatMap((shape) => shape.blocks.filter((block) => block.kind === "table"))[tableIndex];
|
|
4650
|
+
}
|
|
4651
|
+
function slideTableCellText(cell) {
|
|
4652
|
+
return cell.blocks.filter((block) => block.kind === "paragraph").map((paragraph) => paragraph.runs.map((run) => run.text).join("")).join("\n");
|
|
4653
|
+
}
|
|
4654
|
+
function summarizeGridTable(table, index) {
|
|
4655
|
+
const rows = table.rows();
|
|
4656
|
+
return {
|
|
4657
|
+
index,
|
|
4658
|
+
rowCount: rows.length,
|
|
4659
|
+
columnCount: rows[0]?.cells().length ?? 0
|
|
4660
|
+
};
|
|
4661
|
+
}
|
|
4662
|
+
function summarizeSlideTables(doc, slideIndex) {
|
|
4663
|
+
if (doc.format === "odp") return (doc.editor.slides()[slideIndex]?.tables() ?? []).map((entry, index) => summarizeGridTable(entry.table, index));
|
|
4664
|
+
return (doc.editor.slides()[slideIndex]?.tables() ?? []).map((table, index) => summarizeGridTable(table, index));
|
|
4665
|
+
}
|
|
4666
|
+
//#endregion
|
|
4370
4667
|
//#region src/tui/screens/editors/pptx/slide-detail.tsx
|
|
4371
4668
|
const IMAGE_EXTENSION_TO_FORMAT = {
|
|
4372
4669
|
png: "png",
|
|
@@ -4375,10 +4672,6 @@ const IMAGE_EXTENSION_TO_FORMAT = {
|
|
|
4375
4672
|
};
|
|
4376
4673
|
const DEFAULT_TABLE_ROWS = 2;
|
|
4377
4674
|
const DEFAULT_TABLE_COLUMNS = 2;
|
|
4378
|
-
function parsePositiveIntField(raw, fallback) {
|
|
4379
|
-
const parsed = Number.parseInt(raw, 10);
|
|
4380
|
-
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
4381
|
-
}
|
|
4382
4675
|
function imageFormatFromPath(path) {
|
|
4383
4676
|
const dotIndex = path.lastIndexOf(".");
|
|
4384
4677
|
if (dotIndex < 0) return;
|
|
@@ -4399,34 +4692,63 @@ function SlideDetailScreen(props) {
|
|
|
4399
4692
|
const doc = assertPresentationDocument(state.openDocument);
|
|
4400
4693
|
const { slideIndex } = props.screen;
|
|
4401
4694
|
const slide = doc.editor.slides()[slideIndex];
|
|
4402
|
-
const
|
|
4695
|
+
const shapes = slide === void 0 ? [] : slide.shapes();
|
|
4696
|
+
const tableSummaries = slide === void 0 ? [] : summarizeSlideTables(doc, slideIndex);
|
|
4697
|
+
const rows = [...shapes.map((shape, index) => ({
|
|
4698
|
+
kind: "shape",
|
|
4403
4699
|
index,
|
|
4404
4700
|
text: shape.text,
|
|
4405
4701
|
frame: shape.frame
|
|
4406
|
-
}))
|
|
4702
|
+
})), ...tableSummaries.length > 0 ? [{
|
|
4703
|
+
kind: "tablesHeader",
|
|
4704
|
+
count: tableSummaries.length
|
|
4705
|
+
}, ...tableSummaries.map((summary) => ({
|
|
4706
|
+
kind: "table",
|
|
4707
|
+
index: summary.index,
|
|
4708
|
+
rowCount: summary.rowCount,
|
|
4709
|
+
columnCount: summary.columnCount
|
|
4710
|
+
}))] : []];
|
|
4711
|
+
const selectableRowIndices = rows.reduce((acc, row, index) => {
|
|
4712
|
+
if (row.kind !== "tablesHeader") acc.push(index);
|
|
4713
|
+
return acc;
|
|
4714
|
+
}, []);
|
|
4407
4715
|
const [addMode, setAddMode] = useState("closed");
|
|
4408
4716
|
const [draft, setDraft] = useState("");
|
|
4409
4717
|
const [imageError, setImageError] = useState(void 0);
|
|
4410
4718
|
const [tableRows, setTableRows] = useState(DEFAULT_TABLE_ROWS);
|
|
4411
4719
|
const formIsOpen = addMode !== "closed";
|
|
4412
4720
|
const { selectedIndex } = useNavigationInput({
|
|
4413
|
-
itemCount:
|
|
4721
|
+
itemCount: selectableRowIndices.length,
|
|
4414
4722
|
isActive: !overlayOpen && !formIsOpen,
|
|
4415
4723
|
onBack: () => {
|
|
4416
4724
|
dispatch({ type: "POP_SCREEN" });
|
|
4417
4725
|
},
|
|
4418
4726
|
onSelect: (index) => {
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4727
|
+
const rowIndex = selectableRowIndices[index];
|
|
4728
|
+
const row = rowIndex === void 0 ? void 0 : rows[rowIndex];
|
|
4729
|
+
if (row === void 0) return;
|
|
4730
|
+
if (row.kind === "shape") {
|
|
4731
|
+
dispatch({
|
|
4732
|
+
type: "SET_SELECTION",
|
|
4733
|
+
key: selectionKeyFor(props.screen),
|
|
4734
|
+
index
|
|
4735
|
+
});
|
|
4736
|
+
dispatch({
|
|
4737
|
+
type: "PUSH_SCREEN",
|
|
4738
|
+
screen: {
|
|
4739
|
+
kind: "shapeEditor",
|
|
4740
|
+
slideIndex,
|
|
4741
|
+
shapeIndex: row.index
|
|
4742
|
+
}
|
|
4743
|
+
});
|
|
4744
|
+
return;
|
|
4745
|
+
}
|
|
4746
|
+
if (row.kind === "table") dispatch({
|
|
4425
4747
|
type: "PUSH_SCREEN",
|
|
4426
4748
|
screen: {
|
|
4427
|
-
kind: "
|
|
4749
|
+
kind: "slideTableDetail",
|
|
4428
4750
|
slideIndex,
|
|
4429
|
-
|
|
4751
|
+
tableIndex: row.index
|
|
4430
4752
|
}
|
|
4431
4753
|
});
|
|
4432
4754
|
},
|
|
@@ -4434,6 +4756,7 @@ function SlideDetailScreen(props) {
|
|
|
4434
4756
|
setAddMode("chooseKind");
|
|
4435
4757
|
}
|
|
4436
4758
|
});
|
|
4759
|
+
const listSelectedIndex = selectableRowIndices[selectedIndex] ?? -1;
|
|
4437
4760
|
useInput((input, key) => {
|
|
4438
4761
|
if (key.escape) {
|
|
4439
4762
|
setAddMode("closed");
|
|
@@ -4516,9 +4839,9 @@ function SlideDetailScreen(props) {
|
|
|
4516
4839
|
"Slide ",
|
|
4517
4840
|
slideIndex + 1,
|
|
4518
4841
|
" -- ",
|
|
4519
|
-
|
|
4842
|
+
shapes.length,
|
|
4520
4843
|
" shape",
|
|
4521
|
-
|
|
4844
|
+
shapes.length === 1 ? "" : "s"
|
|
4522
4845
|
]
|
|
4523
4846
|
}),
|
|
4524
4847
|
slide === void 0 ? /* @__PURE__ */ jsx(Text, {
|
|
@@ -4526,20 +4849,45 @@ function SlideDetailScreen(props) {
|
|
|
4526
4849
|
children: "This slide no longer exists -- press Esc to go back"
|
|
4527
4850
|
}) : /* @__PURE__ */ jsx(ListView, {
|
|
4528
4851
|
items: rows,
|
|
4529
|
-
selectedIndex,
|
|
4852
|
+
selectedIndex: listSelectedIndex,
|
|
4530
4853
|
emptyMessage: "No shapes yet -- press 'a' to add one",
|
|
4531
|
-
renderItem: (row, isSelected) =>
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4854
|
+
renderItem: (row, isSelected) => {
|
|
4855
|
+
if (row.kind === "tablesHeader") return /* @__PURE__ */ jsxs(Text, {
|
|
4856
|
+
bold: true,
|
|
4857
|
+
dimColor: true,
|
|
4858
|
+
children: [
|
|
4859
|
+
"Tables (",
|
|
4860
|
+
row.count,
|
|
4861
|
+
")"
|
|
4862
|
+
]
|
|
4863
|
+
});
|
|
4864
|
+
if (row.kind === "table") return /* @__PURE__ */ jsxs(Text, {
|
|
4865
|
+
color: isSelected ? "cyan" : void 0,
|
|
4866
|
+
inverse: isSelected,
|
|
4867
|
+
children: [
|
|
4868
|
+
" ",
|
|
4869
|
+
"Table ",
|
|
4870
|
+
row.index + 1,
|
|
4871
|
+
" (",
|
|
4872
|
+
row.rowCount,
|
|
4873
|
+
"x",
|
|
4874
|
+
row.columnCount,
|
|
4875
|
+
")"
|
|
4876
|
+
]
|
|
4877
|
+
});
|
|
4878
|
+
return /* @__PURE__ */ jsxs(Text, {
|
|
4879
|
+
color: isSelected ? "cyan" : void 0,
|
|
4880
|
+
inverse: isSelected,
|
|
4881
|
+
children: [
|
|
4882
|
+
row.index + 1,
|
|
4883
|
+
". ",
|
|
4884
|
+
describeSlideFamilyShape({
|
|
4885
|
+
text: row.text,
|
|
4886
|
+
frame: row.frame
|
|
4887
|
+
})
|
|
4888
|
+
]
|
|
4889
|
+
});
|
|
4890
|
+
}
|
|
4543
4891
|
}),
|
|
4544
4892
|
addMode === "chooseKind" ? /* @__PURE__ */ jsx(Text, {
|
|
4545
4893
|
color: "cyan",
|
|
@@ -4607,6 +4955,146 @@ function SlideDetailScreen(props) {
|
|
|
4607
4955
|
});
|
|
4608
4956
|
}
|
|
4609
4957
|
//#endregion
|
|
4958
|
+
//#region src/tui/screens/editors/pptx/slide-table-detail.tsx
|
|
4959
|
+
const CELL_WIDTH$1 = 16;
|
|
4960
|
+
function SlideTableDetailScreen(props) {
|
|
4961
|
+
const state = useAppState();
|
|
4962
|
+
const dispatch = useAppDispatch();
|
|
4963
|
+
const doc = assertPresentationDocument(state.openDocument);
|
|
4964
|
+
const { slideIndex, tableIndex } = props.screen;
|
|
4965
|
+
const [cursor, setCursor] = useState({
|
|
4966
|
+
row: 0,
|
|
4967
|
+
column: 0
|
|
4968
|
+
});
|
|
4969
|
+
const [mergeAnchor, setMergeAnchor] = useState(void 0);
|
|
4970
|
+
const table = resolveSlideTable(doc, slideIndex, tableIndex);
|
|
4971
|
+
const rows = table?.rows ?? [];
|
|
4972
|
+
const rowCount = rows.length;
|
|
4973
|
+
const columnCount = rows[0]?.cells.length ?? 0;
|
|
4974
|
+
const clampedRow = rowCount === 0 ? 0 : Math.min(cursor.row, rowCount - 1);
|
|
4975
|
+
const clampedColumn = columnCount === 0 ? 0 : Math.min(cursor.column, columnCount - 1);
|
|
4976
|
+
const commitMerge = (anchor) => {
|
|
4977
|
+
const startRow = Math.min(anchor.row, clampedRow);
|
|
4978
|
+
const startColumn = Math.min(anchor.column, clampedColumn);
|
|
4979
|
+
const rowSpan = Math.abs(clampedRow - anchor.row) + 1;
|
|
4980
|
+
const colSpan = Math.abs(clampedColumn - anchor.column) + 1;
|
|
4981
|
+
dispatch({
|
|
4982
|
+
type: "MERGE_SLIDE_TABLE_CELLS",
|
|
4983
|
+
slideIndex,
|
|
4984
|
+
tableIndex,
|
|
4985
|
+
startRow,
|
|
4986
|
+
startColumn,
|
|
4987
|
+
rowSpan,
|
|
4988
|
+
colSpan
|
|
4989
|
+
});
|
|
4990
|
+
setMergeAnchor(void 0);
|
|
4991
|
+
};
|
|
4992
|
+
useInput((input, key) => {
|
|
4993
|
+
if (table === void 0) return;
|
|
4994
|
+
if (key.upArrow || input === "k") {
|
|
4995
|
+
setCursor({
|
|
4996
|
+
row: Math.max(0, clampedRow - 1),
|
|
4997
|
+
column: clampedColumn
|
|
4998
|
+
});
|
|
4999
|
+
return;
|
|
5000
|
+
}
|
|
5001
|
+
if (key.downArrow || input === "j") {
|
|
5002
|
+
setCursor({
|
|
5003
|
+
row: rowCount === 0 ? 0 : Math.min(rowCount - 1, clampedRow + 1),
|
|
5004
|
+
column: clampedColumn
|
|
5005
|
+
});
|
|
5006
|
+
return;
|
|
5007
|
+
}
|
|
5008
|
+
if (key.leftArrow || input === "h") {
|
|
5009
|
+
setCursor({
|
|
5010
|
+
row: clampedRow,
|
|
5011
|
+
column: Math.max(0, clampedColumn - 1)
|
|
5012
|
+
});
|
|
5013
|
+
return;
|
|
5014
|
+
}
|
|
5015
|
+
if (key.rightArrow || input === "l") {
|
|
5016
|
+
setCursor({
|
|
5017
|
+
row: clampedRow,
|
|
5018
|
+
column: columnCount === 0 ? 0 : Math.min(columnCount - 1, clampedColumn + 1)
|
|
5019
|
+
});
|
|
5020
|
+
return;
|
|
5021
|
+
}
|
|
5022
|
+
if (key.escape) {
|
|
5023
|
+
if (mergeAnchor !== void 0) {
|
|
5024
|
+
setMergeAnchor(void 0);
|
|
5025
|
+
return;
|
|
5026
|
+
}
|
|
5027
|
+
dispatch({ type: "POP_SCREEN" });
|
|
5028
|
+
return;
|
|
5029
|
+
}
|
|
5030
|
+
if (input === "m" || key.return) {
|
|
5031
|
+
if (mergeAnchor === void 0) {
|
|
5032
|
+
setMergeAnchor({
|
|
5033
|
+
row: clampedRow,
|
|
5034
|
+
column: clampedColumn
|
|
5035
|
+
});
|
|
5036
|
+
return;
|
|
5037
|
+
}
|
|
5038
|
+
commitMerge(mergeAnchor);
|
|
5039
|
+
}
|
|
5040
|
+
}, { isActive: !anyOverlayOpen(state) });
|
|
5041
|
+
if (table === void 0) return /* @__PURE__ */ jsxs(Box, {
|
|
5042
|
+
flexDirection: "column",
|
|
5043
|
+
children: [/* @__PURE__ */ jsxs(Text, {
|
|
5044
|
+
bold: true,
|
|
5045
|
+
children: [
|
|
5046
|
+
"Slide ",
|
|
5047
|
+
slideIndex + 1,
|
|
5048
|
+
", table ",
|
|
5049
|
+
tableIndex + 1
|
|
5050
|
+
]
|
|
5051
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
5052
|
+
color: "yellow",
|
|
5053
|
+
children: "This table no longer exists -- press Esc to go back"
|
|
5054
|
+
})]
|
|
5055
|
+
});
|
|
5056
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
5057
|
+
flexDirection: "column",
|
|
5058
|
+
children: [
|
|
5059
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
5060
|
+
bold: true,
|
|
5061
|
+
children: [
|
|
5062
|
+
"Slide ",
|
|
5063
|
+
slideIndex + 1,
|
|
5064
|
+
", table ",
|
|
5065
|
+
tableIndex + 1,
|
|
5066
|
+
" (",
|
|
5067
|
+
rowCount,
|
|
5068
|
+
"x",
|
|
5069
|
+
columnCount,
|
|
5070
|
+
")"
|
|
5071
|
+
]
|
|
5072
|
+
}),
|
|
5073
|
+
rows.length === 0 ? /* @__PURE__ */ jsx(Text, {
|
|
5074
|
+
dimColor: true,
|
|
5075
|
+
children: "This table has no rows."
|
|
5076
|
+
}) : rows.map((row, rowIndex) => /* @__PURE__ */ jsx(Box, { children: row.cells.map((cell, columnIndex) => {
|
|
5077
|
+
const isCursor = rowIndex === clampedRow && columnIndex === clampedColumn;
|
|
5078
|
+
const isAnchor = rowIndex === mergeAnchor?.row && columnIndex === mergeAnchor?.column;
|
|
5079
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
5080
|
+
width: CELL_WIDTH$1,
|
|
5081
|
+
borderStyle: "single",
|
|
5082
|
+
borderColor: isCursor ? "cyan" : isAnchor ? "yellow" : "gray",
|
|
5083
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
5084
|
+
color: isCursor ? "cyan" : void 0,
|
|
5085
|
+
inverse: isCursor,
|
|
5086
|
+
children: truncatePreview(slideTableCellText(cell), 14)
|
|
5087
|
+
})
|
|
5088
|
+
}, columnIndex);
|
|
5089
|
+
}) }, rowIndex)),
|
|
5090
|
+
/* @__PURE__ */ jsx(Text, {
|
|
5091
|
+
dimColor: true,
|
|
5092
|
+
children: mergeAnchor === void 0 ? "Arrows/hjkl move, m to anchor a merge, Esc back" : "Arrows/hjkl to the opposite corner, m/Enter to merge, Esc to cancel"
|
|
5093
|
+
})
|
|
5094
|
+
]
|
|
5095
|
+
});
|
|
5096
|
+
}
|
|
5097
|
+
//#endregion
|
|
4610
5098
|
//#region src/tui/screens/editors/pptx/index.tsx
|
|
4611
5099
|
function PptxSlideListScreen() {
|
|
4612
5100
|
const doc = useAppState().openDocument;
|
|
@@ -5225,7 +5713,8 @@ const RESERVED_LETTERS = /* @__PURE__ */ new Set([
|
|
|
5225
5713
|
"k",
|
|
5226
5714
|
"l",
|
|
5227
5715
|
"p",
|
|
5228
|
-
"t"
|
|
5716
|
+
"t",
|
|
5717
|
+
"m"
|
|
5229
5718
|
]);
|
|
5230
5719
|
function windowStart(cursor, total, viewport) {
|
|
5231
5720
|
const maxStart = Math.max(0, total - viewport);
|
|
@@ -5248,6 +5737,7 @@ function OdsSpreadsheetGridScreen() {
|
|
|
5248
5737
|
const [cursorColumn, setCursorColumn] = useState(0);
|
|
5249
5738
|
const [viewMode, setViewMode] = useState("grid");
|
|
5250
5739
|
const [editSession, setEditSession] = useState(void 0);
|
|
5740
|
+
const [mergeAnchor, setMergeAnchor] = useState(void 0);
|
|
5251
5741
|
const { columns: terminalColumns, rows: terminalRows } = useWindowSize();
|
|
5252
5742
|
const sheet = resolveSheet(doc.editor, sheetIndex);
|
|
5253
5743
|
const { rowCount, columnCount } = sheetExtent(sheet);
|
|
@@ -5266,6 +5756,21 @@ function OdsSpreadsheetGridScreen() {
|
|
|
5266
5756
|
seedKind
|
|
5267
5757
|
});
|
|
5268
5758
|
}
|
|
5759
|
+
function commitMerge(anchor) {
|
|
5760
|
+
const startRow = Math.min(anchor.row, clampedRow);
|
|
5761
|
+
const startColumn = Math.min(anchor.column, clampedColumn);
|
|
5762
|
+
const rowSpan = Math.abs(clampedRow - anchor.row) + 1;
|
|
5763
|
+
const colSpan = Math.abs(clampedColumn - anchor.column) + 1;
|
|
5764
|
+
dispatch({
|
|
5765
|
+
type: "MERGE_CELLS",
|
|
5766
|
+
sheetIndex,
|
|
5767
|
+
startRow,
|
|
5768
|
+
startColumn,
|
|
5769
|
+
rowSpan,
|
|
5770
|
+
colSpan
|
|
5771
|
+
});
|
|
5772
|
+
setMergeAnchor(void 0);
|
|
5773
|
+
}
|
|
5269
5774
|
useInput((input) => {
|
|
5270
5775
|
if (input === "t") {
|
|
5271
5776
|
setViewMode((mode) => mode === "grid" ? "compact" : "grid");
|
|
@@ -5313,10 +5818,26 @@ function OdsSpreadsheetGridScreen() {
|
|
|
5313
5818
|
return;
|
|
5314
5819
|
}
|
|
5315
5820
|
if (key.escape) {
|
|
5821
|
+
if (mergeAnchor !== void 0) {
|
|
5822
|
+
setMergeAnchor(void 0);
|
|
5823
|
+
return;
|
|
5824
|
+
}
|
|
5316
5825
|
dispatch({ type: "POP_SCREEN" });
|
|
5317
5826
|
return;
|
|
5318
5827
|
}
|
|
5828
|
+
if (input === "m") {
|
|
5829
|
+
if (mergeAnchor === void 0) setMergeAnchor({
|
|
5830
|
+
row: clampedRow,
|
|
5831
|
+
column: clampedColumn
|
|
5832
|
+
});
|
|
5833
|
+
else commitMerge(mergeAnchor);
|
|
5834
|
+
return;
|
|
5835
|
+
}
|
|
5319
5836
|
if (key.return) {
|
|
5837
|
+
if (mergeAnchor !== void 0) {
|
|
5838
|
+
commitMerge(mergeAnchor);
|
|
5839
|
+
return;
|
|
5840
|
+
}
|
|
5320
5841
|
const cell = cells.get(cellKey(clampedRow, clampedColumn));
|
|
5321
5842
|
const seedKind = cell === void 0 || cell.value.kind === "empty" ? "string" : cell.value.kind;
|
|
5322
5843
|
beginEdit(cell === void 0 ? "" : rawEditableText(cell.value), seedKind);
|
|
@@ -5399,10 +5920,11 @@ function OdsSpreadsheetGridScreen() {
|
|
|
5399
5920
|
children: [`${row + 1}`.padStart(4), " "]
|
|
5400
5921
|
}), visibleColumns.map((column) => {
|
|
5401
5922
|
const isCursor = row === clampedRow && column === clampedColumn;
|
|
5923
|
+
const isAnchor = row === mergeAnchor?.row && column === mergeAnchor?.column;
|
|
5402
5924
|
const cell = cells.get(cellKey(row, column));
|
|
5403
5925
|
return /* @__PURE__ */ jsx(Text, {
|
|
5404
5926
|
inverse: isCursor,
|
|
5405
|
-
color: isCursor ? "cyan" : void 0,
|
|
5927
|
+
color: isCursor ? "cyan" : isAnchor ? "yellow" : void 0,
|
|
5406
5928
|
children: padCell(cell === void 0 ? "" : cell.displayText, CELL_WIDTH)
|
|
5407
5929
|
}, column);
|
|
5408
5930
|
})] }, row))]
|
|
@@ -5440,13 +5962,9 @@ function OdsSpreadsheetGridScreen() {
|
|
|
5440
5962
|
setEditSession(void 0);
|
|
5441
5963
|
}
|
|
5442
5964
|
}),
|
|
5443
|
-
/* @__PURE__ */
|
|
5965
|
+
/* @__PURE__ */ jsx(Text, {
|
|
5444
5966
|
dimColor: true,
|
|
5445
|
-
children:
|
|
5446
|
-
"hjkl/arrows move, Enter/type to edit, p print settings, t ",
|
|
5447
|
-
viewMode === "grid" ? "compact list" : "grid",
|
|
5448
|
-
" view, Esc back"
|
|
5449
|
-
]
|
|
5967
|
+
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, Esc back` : "hjkl/arrows to the opposite corner, m/Enter to merge, Esc to cancel"
|
|
5450
5968
|
})
|
|
5451
5969
|
]
|
|
5452
5970
|
});
|
|
@@ -6498,6 +7016,7 @@ function ScreenBody({ screen }) {
|
|
|
6498
7016
|
case "slideList": return format === "odp" ? /* @__PURE__ */ jsx(OdpSlideListScreen, {}) : /* @__PURE__ */ jsx(PptxSlideListScreen, {});
|
|
6499
7017
|
case "slideDetail": return /* @__PURE__ */ jsx(SlideDetailScreen, { screen });
|
|
6500
7018
|
case "shapeEditor": return /* @__PURE__ */ jsx(ShapeEditorScreen, { screen });
|
|
7019
|
+
case "slideTableDetail": return /* @__PURE__ */ jsx(SlideTableDetailScreen, { screen });
|
|
6501
7020
|
case "notesEditor": return /* @__PURE__ */ jsx(NotesEditorScreen, { screen });
|
|
6502
7021
|
case "sheetList": return /* @__PURE__ */ jsx(OdsSheetListScreen, {});
|
|
6503
7022
|
case "spreadsheetGrid": return /* @__PURE__ */ jsx(OdsSpreadsheetGridScreen, {});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "document-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "CLI and interactive Ink TUI for documents.js: every docx/pptx/odt/odp/ods/odg/odf/pdf/odm/odb/xlsx/markdown conversion, bridge, and editor as a scriptable command or a terminal app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|