@arbidocs/blocks 0.4.98 → 0.4.100
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/agents/index.cjs +207 -207
- package/dist/agents/index.js +2 -2
- package/dist/{chunk-LZ5AFAWC.js → chunk-6HZNFOHW.js} +3 -3
- package/dist/{chunk-LZ5AFAWC.js.map → chunk-6HZNFOHW.js.map} +1 -1
- package/dist/{chunk-FIEAWH4K.js → chunk-72PQ42YI.js} +1040 -88
- package/dist/chunk-72PQ42YI.js.map +1 -0
- package/dist/{chunk-MIMVRMKC.cjs → chunk-B7P73VHX.cjs} +1040 -83
- package/dist/chunk-B7P73VHX.cjs.map +1 -0
- package/dist/{chunk-6B2AWE3I.js → chunk-E2QWWLK5.js} +3 -3
- package/dist/{chunk-6B2AWE3I.js.map → chunk-E2QWWLK5.js.map} +1 -1
- package/dist/{chunk-MUYZP5FB.cjs → chunk-IE55TWIG.cjs} +5 -5
- package/dist/{chunk-MUYZP5FB.cjs.map → chunk-IE55TWIG.cjs.map} +1 -1
- package/dist/{chunk-5NQIBJPP.js → chunk-NJX774HB.js} +3 -3
- package/dist/{chunk-5NQIBJPP.js.map → chunk-NJX774HB.js.map} +1 -1
- package/dist/{chunk-DDYXFALG.cjs → chunk-NW3LFDZL.cjs} +3 -3
- package/dist/{chunk-DDYXFALG.cjs.map → chunk-NW3LFDZL.cjs.map} +1 -1
- package/dist/{chunk-VJO66VVF.cjs → chunk-O24WPHLD.cjs} +5 -5
- package/dist/{chunk-VJO66VVF.cjs.map → chunk-O24WPHLD.cjs.map} +1 -1
- package/dist/connect/docx.cjs +6 -6
- package/dist/connect/docx.js +4 -4
- package/dist/connect/index.cjs +3 -3
- package/dist/connect/index.js +2 -2
- package/dist/docx/NativeDocxEditor.d.ts +6 -1
- package/dist/docx/definitions/fragment.d.ts +3 -1
- package/dist/docx/definitions/port.d.ts +6 -2
- package/dist/docx/index.cjs +51 -31
- package/dist/docx/index.d.ts +3 -0
- package/dist/docx/index.js +3 -3
- package/dist/docx/layout/GenerateImage.d.ts +50 -0
- package/dist/docx/layout/LayoutRibbon.d.ts +32 -0
- package/dist/docx/layout/LayoutTabs.d.ts +20 -0
- package/dist/docx/layout/PictureRibbon.d.ts +23 -0
- package/dist/docx/layout/TableKeys.d.ts +25 -0
- package/dist/docx/layout/TableRibbon.d.ts +11 -0
- package/dist/docx/layout/caption.d.ts +38 -0
- package/dist/docx/layout/presets.d.ts +40 -0
- package/dist/docx/structural/trackedOrNoted.d.ts +51 -0
- package/dist/docx/structural/useTrackedOrNoted.d.ts +5 -0
- package/dist/facts/index.cjs +1 -1
- package/dist/facts/index.js +1 -1
- package/dist/files/index.cjs +1 -1
- package/dist/files/index.js +1 -1
- package/dist/home/index.cjs +1 -1
- package/dist/home/index.js +1 -1
- package/dist/index.cjs +253 -253
- package/dist/index.js +3 -3
- package/dist/shell/index.cjs +1 -1
- package/dist/shell/index.js +1 -1
- package/dist/studio/index.cjs +1 -1
- package/dist/studio/index.js +1 -1
- package/dist/workflow-canvas/index.cjs +9 -9
- package/dist/workflow-canvas/index.js +2 -2
- package/package.json +5 -4
- package/src/docx/README.md +22 -0
- package/src/docx/layout/layout.css +120 -0
- package/src/docx/styles.css +2 -0
- package/dist/chunk-FIEAWH4K.js.map +0 -1
- package/dist/chunk-MIMVRMKC.cjs.map +0 -1
|
@@ -3,13 +3,14 @@ import { stylesXml, definitionBookmark, DEFINITION_BOOKMARK_PREFIX, compareTerms
|
|
|
3
3
|
import { filterMentionCandidates, MentionMenu, findMentionQuery, listOptionId, extractMentions, useListKeyNav } from './chunk-AA3PT6KR.js';
|
|
4
4
|
import { unzipSync, strFromU8, strToU8, zipSync } from 'fflate';
|
|
5
5
|
import { collectReviewItems, revisionItemsOf } from '@docx-editor.dev/core/store';
|
|
6
|
-
import React3, { createContext, useContext, useState, useRef, useCallback, useEffect, useLayoutEffect, useMemo } from 'react';
|
|
7
|
-
import { useDocxEditor, ReviewRailContext, useEditorSnapshot, useChromeTranslate, DocxEditor, createFontSource, useParagraphStyle, useEditorCommand } from '@docx-editor.dev/react';
|
|
6
|
+
import React3, { createContext, useContext, useState, useRef, useCallback, useEffect, useLayoutEffect, useMemo, useSyncExternalStore } from 'react';
|
|
7
|
+
import { useDocxEditor, ReviewRailContext, useEditorSnapshot, useEditorState, useChromeTranslate, DocxEditor, createFontSource, useParagraphStyle, normalizeImageBytes, usePageSetup, useEditorCommand } from '@docx-editor.dev/react';
|
|
8
8
|
import { AiMarkdown, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Button, DialogFooter, cn, splitMarkdownBlocks } from '@arbidocs/react/ui';
|
|
9
|
-
import { MessageSquarePlus, MessageSquare, PencilLine, ChevronLeft, ChevronRight, X, ChevronUp, ChevronDown, CheckCheck, RotateCcw, CircleCheck, Trash2, Check, SendHorizontal, Type, Ellipsis, RefreshCw, WifiOff, Download, Save, BookA, ListRestart, Blocks, Superscript, NotebookText } from 'lucide-react';
|
|
9
|
+
import { MessageSquarePlus, MessageSquare, PencilLine, ChevronLeft, ChevronRight, X, ChevronUp, ChevronDown, CheckCheck, RotateCcw, CircleCheck, Trash2, Check, SendHorizontal, Loader2, Sparkles, Type, Ellipsis, RefreshCw, WifiOff, Download, Save, BookA, ListRestart, Blocks, Superscript, NotebookText, Columns2, Settings2, BetweenHorizontalStart, BetweenHorizontalEnd, BetweenVerticalStart, BetweenVerticalEnd, Merge, Split, Heading, AlignVerticalJustifyStart, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, RotateCw, FlipHorizontal2, FlipVertical2, Scaling, RectangleVertical, RectangleHorizontal, FileText, SeparatorHorizontal, Captions } from 'lucide-react';
|
|
10
10
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
11
11
|
import { createPortal } from 'react-dom';
|
|
12
|
-
import { defaultLegalTemplate, listBuildingBlocks, attachedTemplateOf, linkedLevelStyles, styleDivergence, buildingBlockFragment, fragmentClipboardHtml, newFromTemplate } from '@arbidocs/coedit/template';
|
|
12
|
+
import { defaultLegalTemplate, listBuildingBlocks, attachedTemplateOf, linkedLevelStyles, styleDivergence, buildingBlockFragment, fragmentClipboardHtml, trackInsertion, newFromTemplate } from '@arbidocs/coedit/template';
|
|
13
|
+
import { executeImageCommand } from '@docx-editor.dev/core/editor';
|
|
13
14
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
14
15
|
import * as Y from 'yjs';
|
|
15
16
|
import { DOCUMENT_COLLABORATION_VERSIONS, collaborationModule } from '@arbidocs/coedit';
|
|
@@ -1260,8 +1261,6 @@ function DocxMentions({
|
|
|
1260
1261
|
}
|
|
1261
1262
|
) });
|
|
1262
1263
|
}
|
|
1263
|
-
|
|
1264
|
-
// src/docx/definitions/fragment.ts
|
|
1265
1264
|
var DEFINED_TERM_STYLE = "DefinedTerm";
|
|
1266
1265
|
var FRAGMENT_STYLES = stylesXml(
|
|
1267
1266
|
"",
|
|
@@ -1294,12 +1293,13 @@ function base64(bytes) {
|
|
|
1294
1293
|
binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
|
|
1295
1294
|
return btoa(binary);
|
|
1296
1295
|
}
|
|
1297
|
-
function fragmentHtml(body, covered2) {
|
|
1298
|
-
const
|
|
1296
|
+
function fragmentHtml(body, covered2, revision) {
|
|
1297
|
+
const untracked = docxPackage({
|
|
1299
1298
|
document: documentXml(body),
|
|
1300
1299
|
styles: FRAGMENT_STYLES,
|
|
1301
1300
|
title: "Defined terms"
|
|
1302
1301
|
});
|
|
1302
|
+
const bytes = revision ? trackInsertion(untracked, revision, covered2) : untracked;
|
|
1303
1303
|
return `<div data-docx-fragment="${base64(bytes)}" data-docx-fragment-end="${covered2 ? "covered" : "open"}"></div>`;
|
|
1304
1304
|
}
|
|
1305
1305
|
|
|
@@ -1556,7 +1556,7 @@ function readParagraph(node, topLevel, bookmarks) {
|
|
|
1556
1556
|
links
|
|
1557
1557
|
};
|
|
1558
1558
|
}
|
|
1559
|
-
function editorPort(editor) {
|
|
1559
|
+
function editorPort(editor, revision) {
|
|
1560
1560
|
const surface = () => {
|
|
1561
1561
|
const s = editor.surface;
|
|
1562
1562
|
if (!s) throw new Error("The document is not open.");
|
|
@@ -1575,7 +1575,7 @@ function editorPort(editor) {
|
|
|
1575
1575
|
surface().setSelection({ anchor: from, head: to });
|
|
1576
1576
|
surface().deleteSelection();
|
|
1577
1577
|
},
|
|
1578
|
-
paste: (body, text, covered2) => editor.exec({ type: "paste", text, html: fragmentHtml(body, covered2) }).ok,
|
|
1578
|
+
paste: (body, text, covered2) => editor.exec({ type: "paste", text, html: fragmentHtml(body, covered2, revision) }).ok,
|
|
1579
1579
|
splitParagraph: () => surface().splitParagraph()
|
|
1580
1580
|
};
|
|
1581
1581
|
}
|
|
@@ -1618,6 +1618,70 @@ function holdCaret(editor, timeoutMs = 5e3) {
|
|
|
1618
1618
|
const offSelection = editor.on("selectionChange", restore);
|
|
1619
1619
|
const timer = setTimeout(stop, timeoutMs);
|
|
1620
1620
|
}
|
|
1621
|
+
|
|
1622
|
+
// src/docx/structural/trackedOrNoted.ts
|
|
1623
|
+
var revisionDate = (at = /* @__PURE__ */ new Date()) => at.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
1624
|
+
function trackedOrNoted(port, { label, author, insert, inserted = Boolean }) {
|
|
1625
|
+
const mode = port.mode();
|
|
1626
|
+
if (mode === "viewing") throw new Error("This document is read only.");
|
|
1627
|
+
if (mode !== "suggesting") return { result: insert(null), how: "direct" };
|
|
1628
|
+
const revision = { author, date: revisionDate() };
|
|
1629
|
+
const before = port.caret();
|
|
1630
|
+
const tracked = port.trackedBy(revision);
|
|
1631
|
+
port.setMode("editing");
|
|
1632
|
+
let result;
|
|
1633
|
+
try {
|
|
1634
|
+
result = insert(revision);
|
|
1635
|
+
} finally {
|
|
1636
|
+
port.setMode("suggesting");
|
|
1637
|
+
}
|
|
1638
|
+
if (!inserted(result) || port.trackedBy(revision) > tracked)
|
|
1639
|
+
return { result, how: inserted(result) ? "tracked" : "direct" };
|
|
1640
|
+
const after = port.caret();
|
|
1641
|
+
if (before && after && port.comment(before, after, label)) return { result, how: "noted" };
|
|
1642
|
+
return { result, how: "direct" };
|
|
1643
|
+
}
|
|
1644
|
+
var attr2 = (node, name) => node.attributes?.find((a) => a.localName === name)?.value;
|
|
1645
|
+
function structuralPort(editor) {
|
|
1646
|
+
return {
|
|
1647
|
+
mode: () => editor.getEditingMode(),
|
|
1648
|
+
setMode: (mode) => void editor.setEditingMode(mode),
|
|
1649
|
+
caret: () => {
|
|
1650
|
+
const head = editor.surface?.state().selection?.head;
|
|
1651
|
+
return head ? { paragraphId: head.paragraphId, offset: head.offset } : null;
|
|
1652
|
+
},
|
|
1653
|
+
// Read from the story's tree: the review pane merges adjacent insertions by one author into
|
|
1654
|
+
// one card, so a card's date need not be this insert's.
|
|
1655
|
+
trackedBy: ({ author, date }) => {
|
|
1656
|
+
const root = editor.surface?.session.part().root;
|
|
1657
|
+
let count = 0;
|
|
1658
|
+
const visit = (node) => {
|
|
1659
|
+
if (node.localName === "ins" && attr2(node, "author") === author && attr2(node, "date") === date)
|
|
1660
|
+
count++;
|
|
1661
|
+
node.children?.forEach(visit);
|
|
1662
|
+
};
|
|
1663
|
+
if (root) visit(root);
|
|
1664
|
+
return count;
|
|
1665
|
+
},
|
|
1666
|
+
comment: (from, to, text) => {
|
|
1667
|
+
const surface = editor.surface;
|
|
1668
|
+
if (!surface) return false;
|
|
1669
|
+
surface.setSelection({ anchor: from, head: to });
|
|
1670
|
+
const ok2 = editor.addComment(text).ok;
|
|
1671
|
+
surface.setSelection({ anchor: to, head: to });
|
|
1672
|
+
return ok2;
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
var DocxAuthorContext = createContext("You");
|
|
1677
|
+
function useTrackedOrNoted() {
|
|
1678
|
+
const editor = useDocxEditor();
|
|
1679
|
+
const author = useContext(DocxAuthorContext);
|
|
1680
|
+
return useCallback(
|
|
1681
|
+
(spec) => editor ? trackedOrNoted(structuralPort(editor), { ...spec, author }) : void 0,
|
|
1682
|
+
[editor, author]
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1621
1685
|
var DEFINED_TERM_TRIGGER = "@@";
|
|
1622
1686
|
var TRIGGER_QUERY = /(?:^|[\s([{"“‘'])@@([^@\n\t]{0,40})$/;
|
|
1623
1687
|
var MAX_GROUPS = 8;
|
|
@@ -1661,6 +1725,7 @@ function DefinedTermsProvider({
|
|
|
1661
1725
|
labels
|
|
1662
1726
|
}) {
|
|
1663
1727
|
const editor = useDocxEditor();
|
|
1728
|
+
const track = useTrackedOrNoted();
|
|
1664
1729
|
const l = useMemo(() => ({ ...DEFAULT_DEFINED_TERMS_LABELS, ...labels }), [labels]);
|
|
1665
1730
|
const [sources, setSources] = useState(null);
|
|
1666
1731
|
const loadedAt = useRef(0);
|
|
@@ -1717,35 +1782,46 @@ function DefinedTermsProvider({
|
|
|
1717
1782
|
refreshRef.current = refresh;
|
|
1718
1783
|
const firstHover = useCallback(() => void refreshRef.current(), []);
|
|
1719
1784
|
const caretRect = () => container().querySelector(".docx-editor-one-surface__caret")?.getBoundingClientRect();
|
|
1720
|
-
const
|
|
1785
|
+
const attempt = useCallback(
|
|
1721
1786
|
(fn) => {
|
|
1722
|
-
if (!editor) return void 0;
|
|
1723
1787
|
try {
|
|
1724
|
-
return
|
|
1788
|
+
return fn();
|
|
1725
1789
|
} catch (error) {
|
|
1726
1790
|
notify?.(error instanceof Error ? error.message : "Could not change the document.", "error");
|
|
1727
1791
|
return void 0;
|
|
1728
1792
|
}
|
|
1729
1793
|
},
|
|
1730
|
-
[
|
|
1794
|
+
[notify]
|
|
1795
|
+
);
|
|
1796
|
+
const run = useCallback(
|
|
1797
|
+
(fn) => editor ? attempt(() => withDirectEditing(editor, fn)) : void 0,
|
|
1798
|
+
[editor, attempt]
|
|
1731
1799
|
);
|
|
1732
1800
|
const pick = useCallback(
|
|
1733
1801
|
(row) => {
|
|
1734
1802
|
if (!editor || !picker || !row) return;
|
|
1735
1803
|
const port = editorPort(editor);
|
|
1736
|
-
const
|
|
1804
|
+
const placed = run(() => {
|
|
1737
1805
|
if (picker.mode === "search" && picker.caret) port.setCaret(picker.caret);
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1806
|
+
const caret = port.caret();
|
|
1807
|
+
if (!caret) throw new Error("Place the cursor where the term should go.");
|
|
1808
|
+
const typed = picker.mode === "typing" ? DEFINED_TERM_TRIGGER.length + picker.query.length : 0;
|
|
1809
|
+
if (typed) port.deleteRange({ ...caret, offset: Math.max(0, caret.offset - typed) }, caret);
|
|
1810
|
+
return true;
|
|
1741
1811
|
});
|
|
1812
|
+
const result = placed ? attempt(
|
|
1813
|
+
() => track({
|
|
1814
|
+
label: `Inserted defined term: ${row.definition.term}`,
|
|
1815
|
+
insert: (revision) => insertDefinedTerm(editorPort(editor, revision), row.definition)
|
|
1816
|
+
})?.result
|
|
1817
|
+
) : void 0;
|
|
1742
1818
|
setPicker(null);
|
|
1743
1819
|
editor.surface?.focus();
|
|
1744
1820
|
if (result) holdCaret(editor);
|
|
1745
1821
|
if (result?.addedDefinition)
|
|
1746
1822
|
notify?.(`\u201C${row.definition.term}\u201D added to the Definitions section.`, "success");
|
|
1747
1823
|
},
|
|
1748
|
-
[editor, picker, run, notify]
|
|
1824
|
+
[editor, picker, run, attempt, track, notify]
|
|
1749
1825
|
);
|
|
1750
1826
|
const openPicker = useCallback(
|
|
1751
1827
|
(anchor) => {
|
|
@@ -2149,6 +2225,7 @@ function HouseTemplateProvider({
|
|
|
2149
2225
|
children
|
|
2150
2226
|
}) {
|
|
2151
2227
|
const editor = useDocxEditor();
|
|
2228
|
+
const track = useTrackedOrNoted();
|
|
2152
2229
|
const [template, setTemplate] = useState(builtinHouseTemplate);
|
|
2153
2230
|
const [divergence, setDivergence] = useState(null);
|
|
2154
2231
|
const [basedOnTemplate, setBasedOnTemplate] = useState(false);
|
|
@@ -2189,11 +2266,18 @@ function HouseTemplateProvider({
|
|
|
2189
2266
|
const fragment = buildingBlockFragment(template.bytes, name);
|
|
2190
2267
|
if (!fragment) return false;
|
|
2191
2268
|
try {
|
|
2192
|
-
const result =
|
|
2193
|
-
|
|
2194
|
-
() => editor.exec({
|
|
2195
|
-
|
|
2196
|
-
|
|
2269
|
+
const result = track({
|
|
2270
|
+
label: `Inserted Quick Part: ${name}`,
|
|
2271
|
+
insert: (revision) => editor.exec({
|
|
2272
|
+
type: "paste",
|
|
2273
|
+
text: "",
|
|
2274
|
+
html: fragmentClipboardHtml(
|
|
2275
|
+
revision ? trackInsertion(fragment, revision, true) : fragment
|
|
2276
|
+
)
|
|
2277
|
+
}),
|
|
2278
|
+
inserted: (r) => r.ok
|
|
2279
|
+
})?.result;
|
|
2280
|
+
if (!result?.ok) throw new Error(result?.reason ?? "The block could not be inserted here.");
|
|
2197
2281
|
refresh();
|
|
2198
2282
|
return true;
|
|
2199
2283
|
} catch (failure) {
|
|
@@ -2204,7 +2288,7 @@ function HouseTemplateProvider({
|
|
|
2204
2288
|
return false;
|
|
2205
2289
|
}
|
|
2206
2290
|
},
|
|
2207
|
-
[editor, editable, template, notify, refresh]
|
|
2291
|
+
[editor, editable, template, notify, refresh, track]
|
|
2208
2292
|
);
|
|
2209
2293
|
const setAsHouseTemplate = useMemo(
|
|
2210
2294
|
() => host?.setFromDocument && documentId ? async () => {
|
|
@@ -2247,6 +2331,241 @@ function HouseTemplateProvider({
|
|
|
2247
2331
|
);
|
|
2248
2332
|
return /* @__PURE__ */ jsx(HouseTemplateContext.Provider, { value: enabled ? value : null, children });
|
|
2249
2333
|
}
|
|
2334
|
+
function RibbonMenu({
|
|
2335
|
+
label,
|
|
2336
|
+
icon,
|
|
2337
|
+
children,
|
|
2338
|
+
className,
|
|
2339
|
+
testId,
|
|
2340
|
+
title
|
|
2341
|
+
}) {
|
|
2342
|
+
const [open, setOpen] = useState(false);
|
|
2343
|
+
const element = useRef(null);
|
|
2344
|
+
useEffect(() => {
|
|
2345
|
+
if (!open) return;
|
|
2346
|
+
const dismiss = (event) => {
|
|
2347
|
+
if (!element.current?.contains(event.target)) setOpen(false);
|
|
2348
|
+
};
|
|
2349
|
+
const escape = (event) => {
|
|
2350
|
+
if (event.key === "Escape") setOpen(false);
|
|
2351
|
+
};
|
|
2352
|
+
document.addEventListener("pointerdown", dismiss);
|
|
2353
|
+
document.addEventListener("keydown", escape);
|
|
2354
|
+
return () => {
|
|
2355
|
+
document.removeEventListener("pointerdown", dismiss);
|
|
2356
|
+
document.removeEventListener("keydown", escape);
|
|
2357
|
+
};
|
|
2358
|
+
}, [open]);
|
|
2359
|
+
return /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon-menu", ref: element, children: [
|
|
2360
|
+
/* @__PURE__ */ jsx(
|
|
2361
|
+
"button",
|
|
2362
|
+
{
|
|
2363
|
+
type: "button",
|
|
2364
|
+
className: ["native-docx-ribbon-trigger", className].filter(Boolean).join(" "),
|
|
2365
|
+
"aria-label": label,
|
|
2366
|
+
title: title ?? label,
|
|
2367
|
+
"aria-expanded": open,
|
|
2368
|
+
"data-testid": testId,
|
|
2369
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
2370
|
+
onClick: () => setOpen((value) => !value),
|
|
2371
|
+
children: icon
|
|
2372
|
+
}
|
|
2373
|
+
),
|
|
2374
|
+
open && /* @__PURE__ */ jsx(
|
|
2375
|
+
"div",
|
|
2376
|
+
{
|
|
2377
|
+
className: "native-docx-ribbon-popover",
|
|
2378
|
+
role: "group",
|
|
2379
|
+
"aria-label": label,
|
|
2380
|
+
onMouseDown: (event) => {
|
|
2381
|
+
if (!(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement))
|
|
2382
|
+
event.preventDefault();
|
|
2383
|
+
},
|
|
2384
|
+
children: typeof children === "function" ? children(() => setOpen(false)) : children
|
|
2385
|
+
}
|
|
2386
|
+
)
|
|
2387
|
+
] });
|
|
2388
|
+
}
|
|
2389
|
+
var titleOf = (prompt) => {
|
|
2390
|
+
const line = prompt.trim().split("\n")[0];
|
|
2391
|
+
return line.length > 80 ? `${line.slice(0, 79)}\u2026` : line;
|
|
2392
|
+
};
|
|
2393
|
+
function rememberCaret(editor) {
|
|
2394
|
+
const surface = editor.surface;
|
|
2395
|
+
const head = surface?.state().selection?.head;
|
|
2396
|
+
if (!surface || !head) return null;
|
|
2397
|
+
return { paraId: surface.session.paraIdOf(head.paragraphId), ...head };
|
|
2398
|
+
}
|
|
2399
|
+
var mounted = [];
|
|
2400
|
+
var running = 0;
|
|
2401
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
2402
|
+
var setRunning = (delta) => {
|
|
2403
|
+
running += delta;
|
|
2404
|
+
listeners.forEach((listener) => listener());
|
|
2405
|
+
};
|
|
2406
|
+
var subscribe = (listener) => {
|
|
2407
|
+
listeners.add(listener);
|
|
2408
|
+
return () => void listeners.delete(listener);
|
|
2409
|
+
};
|
|
2410
|
+
function insertionTarget(origin, caret, editors = mounted) {
|
|
2411
|
+
if (editors.includes(origin) && origin.surface) return origin;
|
|
2412
|
+
const paraId = caret?.paraId;
|
|
2413
|
+
if (!paraId) return null;
|
|
2414
|
+
for (let i = editors.length - 1; i >= 0; i--) {
|
|
2415
|
+
const session = editors[i].surface?.session;
|
|
2416
|
+
if (session?.nodeIdOf(paraId)) return editors[i];
|
|
2417
|
+
}
|
|
2418
|
+
return null;
|
|
2419
|
+
}
|
|
2420
|
+
function restoreCaret(editor, caret) {
|
|
2421
|
+
const surface = editor.surface;
|
|
2422
|
+
if (!surface || !caret) return;
|
|
2423
|
+
const paragraphId = caret.paraId && surface.session.nodeIdOf(caret.paraId) || caret.paragraphId;
|
|
2424
|
+
if (!surface.session.paragraphIds().includes(paragraphId)) return;
|
|
2425
|
+
const point = { paragraphId, offset: caret.offset };
|
|
2426
|
+
surface.setSelection({ anchor: point, head: point });
|
|
2427
|
+
}
|
|
2428
|
+
async function insertGeneratedImage(editor, bytes, prompt, caret) {
|
|
2429
|
+
const image = normalizeImageBytes(bytes);
|
|
2430
|
+
if (!image.ok) throw new Error("The generated image is not in a format Word can embed.");
|
|
2431
|
+
let result;
|
|
2432
|
+
for (let attempt = 0; attempt < 3 && !result?.ok; attempt++) {
|
|
2433
|
+
editor.focus();
|
|
2434
|
+
restoreCaret(editor, caret);
|
|
2435
|
+
result = await executeImageCommand(editor, {
|
|
2436
|
+
type: "insertImage",
|
|
2437
|
+
data: image.bytes,
|
|
2438
|
+
mime: image.mime,
|
|
2439
|
+
widthPoints: image.widthPoints,
|
|
2440
|
+
heightPoints: image.heightPoints,
|
|
2441
|
+
description: prompt.trim(),
|
|
2442
|
+
title: titleOf(prompt)
|
|
2443
|
+
});
|
|
2444
|
+
if (!result.ok && result.reason !== "stale drawing selection") break;
|
|
2445
|
+
}
|
|
2446
|
+
if (!result?.ok) throw new Error(result?.reason || "The picture could not be inserted here.");
|
|
2447
|
+
}
|
|
2448
|
+
var AUTO = "";
|
|
2449
|
+
function useGenerateImageTarget() {
|
|
2450
|
+
const editor = useDocxEditor();
|
|
2451
|
+
const loaded2 = useEditorState((snapshot) => !snapshot.isLoading);
|
|
2452
|
+
useEffect(() => {
|
|
2453
|
+
if (!editor || !loaded2) return;
|
|
2454
|
+
mounted.push(editor);
|
|
2455
|
+
return () => void mounted.splice(mounted.indexOf(editor), 1);
|
|
2456
|
+
}, [editor, loaded2]);
|
|
2457
|
+
}
|
|
2458
|
+
function GenerateImageButton({ generation }) {
|
|
2459
|
+
const editor = useDocxEditor();
|
|
2460
|
+
const [prompt, setPrompt] = useState("");
|
|
2461
|
+
const [aspectRatio, setAspectRatio] = useState(AUTO);
|
|
2462
|
+
const [busy, setBusy] = useState(false);
|
|
2463
|
+
const [error, setError] = useState("");
|
|
2464
|
+
const abort = useRef(null);
|
|
2465
|
+
const close = useRef(() => {
|
|
2466
|
+
});
|
|
2467
|
+
const editable = useEditorState((snapshot) => snapshot.editable);
|
|
2468
|
+
const generating = useSyncExternalStore(subscribe, () => running > 0);
|
|
2469
|
+
async function run() {
|
|
2470
|
+
if (!editor || !prompt.trim() || busy) return;
|
|
2471
|
+
const caret = rememberCaret(editor);
|
|
2472
|
+
const controller = new AbortController();
|
|
2473
|
+
abort.current = controller;
|
|
2474
|
+
setBusy(true);
|
|
2475
|
+
setRunning(1);
|
|
2476
|
+
setError("");
|
|
2477
|
+
try {
|
|
2478
|
+
const bytes = await generation.generate(prompt.trim(), {
|
|
2479
|
+
aspectRatio: aspectRatio || void 0,
|
|
2480
|
+
signal: controller.signal
|
|
2481
|
+
});
|
|
2482
|
+
if (controller.signal.aborted) return;
|
|
2483
|
+
const target = insertionTarget(editor, caret);
|
|
2484
|
+
if (!target) throw new Error("The document was closed before the generated image was ready.");
|
|
2485
|
+
await insertGeneratedImage(target, bytes, prompt, caret);
|
|
2486
|
+
setPrompt("");
|
|
2487
|
+
close.current();
|
|
2488
|
+
generation.notify?.("Generated image inserted", "success");
|
|
2489
|
+
} catch (failure) {
|
|
2490
|
+
if (controller.signal.aborted) return;
|
|
2491
|
+
const message = failure instanceof Error ? failure.message : String(failure);
|
|
2492
|
+
setError(message);
|
|
2493
|
+
generation.notify?.(message, "error");
|
|
2494
|
+
} finally {
|
|
2495
|
+
abort.current = null;
|
|
2496
|
+
setRunning(-1);
|
|
2497
|
+
setBusy(false);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
if (!editable && !generating) return null;
|
|
2501
|
+
return /* @__PURE__ */ jsx(
|
|
2502
|
+
RibbonMenu,
|
|
2503
|
+
{
|
|
2504
|
+
label: generating ? "Generating image\u2026" : "Generate image",
|
|
2505
|
+
icon: generating ? /* @__PURE__ */ jsx(Loader2, { size: 16, className: "native-docx-spin" }) : /* @__PURE__ */ jsx(Sparkles, { size: 16 }),
|
|
2506
|
+
testId: "docx-generate-image",
|
|
2507
|
+
children: (dismiss) => {
|
|
2508
|
+
close.current = dismiss;
|
|
2509
|
+
return /* @__PURE__ */ jsxs(
|
|
2510
|
+
"form",
|
|
2511
|
+
{
|
|
2512
|
+
className: "native-docx-form native-docx-generate-image",
|
|
2513
|
+
onSubmit: (event) => {
|
|
2514
|
+
event.preventDefault();
|
|
2515
|
+
void run();
|
|
2516
|
+
},
|
|
2517
|
+
children: [
|
|
2518
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
2519
|
+
"Describe the image",
|
|
2520
|
+
/* @__PURE__ */ jsx(
|
|
2521
|
+
"textarea",
|
|
2522
|
+
{
|
|
2523
|
+
autoFocus: true,
|
|
2524
|
+
rows: 3,
|
|
2525
|
+
value: prompt,
|
|
2526
|
+
disabled: busy,
|
|
2527
|
+
placeholder: "A watercolour of a lighthouse at dawn",
|
|
2528
|
+
"data-testid": "docx-generate-image-prompt",
|
|
2529
|
+
onChange: (event) => setPrompt(event.target.value),
|
|
2530
|
+
onKeyDown: (event) => {
|
|
2531
|
+
if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) void run();
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
)
|
|
2535
|
+
] }),
|
|
2536
|
+
generation.aspectRatios && generation.aspectRatios.length > 0 && /* @__PURE__ */ jsxs("label", { children: [
|
|
2537
|
+
"Aspect ratio",
|
|
2538
|
+
/* @__PURE__ */ jsxs(
|
|
2539
|
+
"select",
|
|
2540
|
+
{
|
|
2541
|
+
value: aspectRatio,
|
|
2542
|
+
disabled: busy,
|
|
2543
|
+
onChange: (event) => setAspectRatio(event.target.value),
|
|
2544
|
+
children: [
|
|
2545
|
+
/* @__PURE__ */ jsx("option", { value: AUTO, children: "Auto" }),
|
|
2546
|
+
generation.aspectRatios.map((ratio) => /* @__PURE__ */ jsx("option", { value: ratio.value, children: ratio.label }, ratio.value))
|
|
2547
|
+
]
|
|
2548
|
+
}
|
|
2549
|
+
)
|
|
2550
|
+
] }),
|
|
2551
|
+
busy && /* @__PURE__ */ jsx("p", { role: "status", "data-testid": "docx-generate-image-status", children: "Generating\u2026 the picture is inserted where your cursor was. You can keep writing." }),
|
|
2552
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", children: error }),
|
|
2553
|
+
/* @__PURE__ */ jsx("div", { className: "native-docx-form-actions", children: busy ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => abort.current?.abort(), children: "Cancel" }) : /* @__PURE__ */ jsx(
|
|
2554
|
+
"button",
|
|
2555
|
+
{
|
|
2556
|
+
type: "submit",
|
|
2557
|
+
disabled: !prompt.trim(),
|
|
2558
|
+
"data-testid": "docx-generate-image-submit",
|
|
2559
|
+
children: "Generate"
|
|
2560
|
+
}
|
|
2561
|
+
) })
|
|
2562
|
+
]
|
|
2563
|
+
}
|
|
2564
|
+
);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
2250
2569
|
function wordDraftHtml(markdown) {
|
|
2251
2570
|
const template = document.createElement("template");
|
|
2252
2571
|
template.innerHTML = renderToStaticMarkup(/* @__PURE__ */ jsx(AiMarkdown, { children: markdown }));
|
|
@@ -2334,61 +2653,6 @@ var WordDraftStream = class {
|
|
|
2334
2653
|
}
|
|
2335
2654
|
}
|
|
2336
2655
|
};
|
|
2337
|
-
function RibbonMenu({
|
|
2338
|
-
label,
|
|
2339
|
-
icon,
|
|
2340
|
-
children,
|
|
2341
|
-
className,
|
|
2342
|
-
testId,
|
|
2343
|
-
title
|
|
2344
|
-
}) {
|
|
2345
|
-
const [open, setOpen] = useState(false);
|
|
2346
|
-
const element = useRef(null);
|
|
2347
|
-
useEffect(() => {
|
|
2348
|
-
if (!open) return;
|
|
2349
|
-
const dismiss = (event) => {
|
|
2350
|
-
if (!element.current?.contains(event.target)) setOpen(false);
|
|
2351
|
-
};
|
|
2352
|
-
const escape = (event) => {
|
|
2353
|
-
if (event.key === "Escape") setOpen(false);
|
|
2354
|
-
};
|
|
2355
|
-
document.addEventListener("pointerdown", dismiss);
|
|
2356
|
-
document.addEventListener("keydown", escape);
|
|
2357
|
-
return () => {
|
|
2358
|
-
document.removeEventListener("pointerdown", dismiss);
|
|
2359
|
-
document.removeEventListener("keydown", escape);
|
|
2360
|
-
};
|
|
2361
|
-
}, [open]);
|
|
2362
|
-
return /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon-menu", ref: element, children: [
|
|
2363
|
-
/* @__PURE__ */ jsx(
|
|
2364
|
-
"button",
|
|
2365
|
-
{
|
|
2366
|
-
type: "button",
|
|
2367
|
-
className: ["native-docx-ribbon-trigger", className].filter(Boolean).join(" "),
|
|
2368
|
-
"aria-label": label,
|
|
2369
|
-
title: title ?? label,
|
|
2370
|
-
"aria-expanded": open,
|
|
2371
|
-
"data-testid": testId,
|
|
2372
|
-
onMouseDown: (event) => event.preventDefault(),
|
|
2373
|
-
onClick: () => setOpen((value) => !value),
|
|
2374
|
-
children: icon
|
|
2375
|
-
}
|
|
2376
|
-
),
|
|
2377
|
-
open && /* @__PURE__ */ jsx(
|
|
2378
|
-
"div",
|
|
2379
|
-
{
|
|
2380
|
-
className: "native-docx-ribbon-popover",
|
|
2381
|
-
role: "group",
|
|
2382
|
-
"aria-label": label,
|
|
2383
|
-
onMouseDown: (event) => {
|
|
2384
|
-
if (!(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement))
|
|
2385
|
-
event.preventDefault();
|
|
2386
|
-
},
|
|
2387
|
-
children: typeof children === "function" ? children(() => setOpen(false)) : children
|
|
2388
|
-
}
|
|
2389
|
-
)
|
|
2390
|
-
] });
|
|
2391
|
-
}
|
|
2392
2656
|
|
|
2393
2657
|
// src/docx/wordStyles.ts
|
|
2394
2658
|
var QUICK_STYLES = ["Normal", "Title", "heading 1", "heading 2", "heading 3"];
|
|
@@ -2678,6 +2942,679 @@ function WordShortcuts({ scope }) {
|
|
|
2678
2942
|
}, [scope]);
|
|
2679
2943
|
return null;
|
|
2680
2944
|
}
|
|
2945
|
+
|
|
2946
|
+
// src/docx/layout/presets.ts
|
|
2947
|
+
var TWIPS_PER_INCH = 1440;
|
|
2948
|
+
var MARGIN_PRESETS = [
|
|
2949
|
+
{ id: "normal", label: "Normal", top: 1440, right: 1440, bottom: 1440, left: 1440 },
|
|
2950
|
+
{ id: "narrow", label: "Narrow", top: 720, right: 720, bottom: 720, left: 720 },
|
|
2951
|
+
{ id: "moderate", label: "Moderate", top: 1440, right: 1080, bottom: 1440, left: 1080 },
|
|
2952
|
+
{ id: "wide", label: "Wide", top: 1440, right: 2880, bottom: 1440, left: 2880 }
|
|
2953
|
+
];
|
|
2954
|
+
var PAPER_SIZES = [
|
|
2955
|
+
{ id: "letter", label: "Letter", width: 12240, height: 15840 },
|
|
2956
|
+
{ id: "legal", label: "Legal", width: 12240, height: 20160 },
|
|
2957
|
+
{ id: "a4", label: "A4", width: 11906, height: 16838 },
|
|
2958
|
+
{ id: "a5", label: "A5", width: 8391, height: 11906 },
|
|
2959
|
+
{ id: "executive", label: "Executive", width: 10440, height: 15120 }
|
|
2960
|
+
];
|
|
2961
|
+
var TOLERANCE = 20;
|
|
2962
|
+
var near = (a, b) => Math.abs(a - b) <= TOLERANCE;
|
|
2963
|
+
function matchMargins(setup) {
|
|
2964
|
+
if (!setup) return null;
|
|
2965
|
+
const m = setup.marginsTwips;
|
|
2966
|
+
return MARGIN_PRESETS.find(
|
|
2967
|
+
(p) => near(p.top, m.top) && near(p.right, m.right) && near(p.bottom, m.bottom) && near(p.left, m.left)
|
|
2968
|
+
)?.id ?? null;
|
|
2969
|
+
}
|
|
2970
|
+
function matchPaper(setup) {
|
|
2971
|
+
if (!setup) return null;
|
|
2972
|
+
const short = Math.min(setup.pageWidthTwips, setup.pageHeightTwips);
|
|
2973
|
+
const long = Math.max(setup.pageWidthTwips, setup.pageHeightTwips);
|
|
2974
|
+
return PAPER_SIZES.find((p) => near(p.width, short) && near(p.height, long))?.id ?? null;
|
|
2975
|
+
}
|
|
2976
|
+
function orientedSize(paper, orientation) {
|
|
2977
|
+
return orientation === "landscape" ? { pageWidthTwips: paper.height, pageHeightTwips: paper.width } : { pageWidthTwips: paper.width, pageHeightTwips: paper.height };
|
|
2978
|
+
}
|
|
2979
|
+
function inches(twips) {
|
|
2980
|
+
return `${Number((twips / TWIPS_PER_INCH).toFixed(2))}"`;
|
|
2981
|
+
}
|
|
2982
|
+
function paperDescription(paper) {
|
|
2983
|
+
return `${inches(paper.width)} \xD7 ${inches(paper.height)}`;
|
|
2984
|
+
}
|
|
2985
|
+
var COLUMNS_UNSUPPORTED = "Text columns are not available in this editor yet. Documents that already use columns display and save them unchanged.";
|
|
2986
|
+
function RibbonGallery({
|
|
2987
|
+
label,
|
|
2988
|
+
choices,
|
|
2989
|
+
current,
|
|
2990
|
+
onPick,
|
|
2991
|
+
close,
|
|
2992
|
+
testId
|
|
2993
|
+
}) {
|
|
2994
|
+
return /* @__PURE__ */ jsx("div", { className: "native-docx-gallery", role: "menu", "aria-label": label, "data-testid": testId, children: choices.map((choice) => /* @__PURE__ */ jsxs(
|
|
2995
|
+
"button",
|
|
2996
|
+
{
|
|
2997
|
+
type: "button",
|
|
2998
|
+
role: "menuitemradio",
|
|
2999
|
+
"aria-checked": current === choice.id,
|
|
3000
|
+
disabled: choice.disabled,
|
|
3001
|
+
title: choice.title,
|
|
3002
|
+
"data-choice": choice.id,
|
|
3003
|
+
onClick: () => {
|
|
3004
|
+
onPick(choice.id);
|
|
3005
|
+
close();
|
|
3006
|
+
},
|
|
3007
|
+
children: [
|
|
3008
|
+
choice.icon,
|
|
3009
|
+
/* @__PURE__ */ jsxs("span", { className: "native-docx-gallery-text", children: [
|
|
3010
|
+
/* @__PURE__ */ jsx("span", { className: "native-docx-gallery-label", children: choice.label }),
|
|
3011
|
+
choice.detail && /* @__PURE__ */ jsx("span", { className: "native-docx-gallery-detail", children: choice.detail })
|
|
3012
|
+
] })
|
|
3013
|
+
]
|
|
3014
|
+
},
|
|
3015
|
+
choice.id
|
|
3016
|
+
)) });
|
|
3017
|
+
}
|
|
3018
|
+
var marginDetail = (p) => `Top ${inches(p.top)} \xB7 Bottom ${inches(p.bottom)} \xB7 Left ${inches(p.left)} \xB7 Right ${inches(p.right)}`;
|
|
3019
|
+
function Margins({ setup, apply, openDialog }) {
|
|
3020
|
+
return /* @__PURE__ */ jsx(RibbonMenu, { label: "Margins", icon: /* @__PURE__ */ jsx(Scaling, { size: 16 }), testId: "docx-layout-margins", children: (close) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3021
|
+
/* @__PURE__ */ jsx(
|
|
3022
|
+
RibbonGallery,
|
|
3023
|
+
{
|
|
3024
|
+
label: "Margins",
|
|
3025
|
+
current: matchMargins(setup),
|
|
3026
|
+
close,
|
|
3027
|
+
choices: MARGIN_PRESETS.map((p) => ({
|
|
3028
|
+
id: p.id,
|
|
3029
|
+
label: p.label,
|
|
3030
|
+
detail: marginDetail(p)
|
|
3031
|
+
})),
|
|
3032
|
+
onPick: (id) => {
|
|
3033
|
+
const p = MARGIN_PRESETS.find((m) => m.id === id);
|
|
3034
|
+
apply({
|
|
3035
|
+
marginTopTwips: p.top,
|
|
3036
|
+
marginRightTwips: p.right,
|
|
3037
|
+
marginBottomTwips: p.bottom,
|
|
3038
|
+
marginLeftTwips: p.left,
|
|
3039
|
+
scope: "section"
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
),
|
|
3044
|
+
/* @__PURE__ */ jsx(
|
|
3045
|
+
"button",
|
|
3046
|
+
{
|
|
3047
|
+
type: "button",
|
|
3048
|
+
className: "native-docx-gallery-more",
|
|
3049
|
+
onClick: () => {
|
|
3050
|
+
close();
|
|
3051
|
+
openDialog();
|
|
3052
|
+
},
|
|
3053
|
+
children: "Custom margins\u2026"
|
|
3054
|
+
}
|
|
3055
|
+
)
|
|
3056
|
+
] }) });
|
|
3057
|
+
}
|
|
3058
|
+
function Orientation({ setup, apply }) {
|
|
3059
|
+
return /* @__PURE__ */ jsx(
|
|
3060
|
+
RibbonMenu,
|
|
3061
|
+
{
|
|
3062
|
+
label: "Orientation",
|
|
3063
|
+
icon: setup?.orientation === "landscape" ? /* @__PURE__ */ jsx(RectangleHorizontal, { size: 16 }) : /* @__PURE__ */ jsx(RectangleVertical, { size: 16 }),
|
|
3064
|
+
testId: "docx-layout-orientation",
|
|
3065
|
+
children: (close) => /* @__PURE__ */ jsx(
|
|
3066
|
+
RibbonGallery,
|
|
3067
|
+
{
|
|
3068
|
+
label: "Orientation",
|
|
3069
|
+
current: setup?.orientation,
|
|
3070
|
+
close,
|
|
3071
|
+
choices: [
|
|
3072
|
+
{ id: "portrait", label: "Portrait", icon: /* @__PURE__ */ jsx(RectangleVertical, { size: 16 }) },
|
|
3073
|
+
{ id: "landscape", label: "Landscape", icon: /* @__PURE__ */ jsx(RectangleHorizontal, { size: 16 }) }
|
|
3074
|
+
],
|
|
3075
|
+
onPick: (id) => apply({ orientation: id, scope: "section" })
|
|
3076
|
+
}
|
|
3077
|
+
)
|
|
3078
|
+
}
|
|
3079
|
+
);
|
|
3080
|
+
}
|
|
3081
|
+
function Size({ setup, apply, openDialog }) {
|
|
3082
|
+
return /* @__PURE__ */ jsx(RibbonMenu, { label: "Size", icon: /* @__PURE__ */ jsx(FileText, { size: 16 }), testId: "docx-layout-size", children: (close) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3083
|
+
/* @__PURE__ */ jsx(
|
|
3084
|
+
RibbonGallery,
|
|
3085
|
+
{
|
|
3086
|
+
label: "Size",
|
|
3087
|
+
current: matchPaper(setup),
|
|
3088
|
+
close,
|
|
3089
|
+
choices: PAPER_SIZES.map((p) => ({
|
|
3090
|
+
id: p.id,
|
|
3091
|
+
label: p.label,
|
|
3092
|
+
detail: paperDescription(p)
|
|
3093
|
+
})),
|
|
3094
|
+
onPick: (id) => apply({
|
|
3095
|
+
...orientedSize(
|
|
3096
|
+
PAPER_SIZES.find((p) => p.id === id),
|
|
3097
|
+
setup?.orientation
|
|
3098
|
+
),
|
|
3099
|
+
scope: "section"
|
|
3100
|
+
})
|
|
3101
|
+
}
|
|
3102
|
+
),
|
|
3103
|
+
/* @__PURE__ */ jsx(
|
|
3104
|
+
"button",
|
|
3105
|
+
{
|
|
3106
|
+
type: "button",
|
|
3107
|
+
className: "native-docx-gallery-more",
|
|
3108
|
+
onClick: () => {
|
|
3109
|
+
close();
|
|
3110
|
+
openDialog();
|
|
3111
|
+
},
|
|
3112
|
+
children: "More paper sizes\u2026"
|
|
3113
|
+
}
|
|
3114
|
+
)
|
|
3115
|
+
] }) });
|
|
3116
|
+
}
|
|
3117
|
+
var COLUMN_BREAK = { type: "insertBreak", kind: "column" };
|
|
3118
|
+
function Breaks() {
|
|
3119
|
+
const page = useEditorCommand("insert.pageBreak");
|
|
3120
|
+
const nextPage = useEditorCommand("insert.sectionBreakNextPage");
|
|
3121
|
+
const continuous = useEditorCommand("insert.sectionBreakContinuous");
|
|
3122
|
+
const column = useEditorCommand(COLUMN_BREAK);
|
|
3123
|
+
const rows = [
|
|
3124
|
+
{ id: "page", label: "Page", detail: "Start the next page here", command: page },
|
|
3125
|
+
{ id: "column", label: "Column", detail: "Start the next column here", command: column },
|
|
3126
|
+
{
|
|
3127
|
+
id: "nextPage",
|
|
3128
|
+
label: "Next Page",
|
|
3129
|
+
detail: "Section break: new section on the next page",
|
|
3130
|
+
command: nextPage
|
|
3131
|
+
},
|
|
3132
|
+
{
|
|
3133
|
+
id: "continuous",
|
|
3134
|
+
label: "Continuous",
|
|
3135
|
+
detail: "Section break: new section on the same page",
|
|
3136
|
+
command: continuous
|
|
3137
|
+
}
|
|
3138
|
+
];
|
|
3139
|
+
return /* @__PURE__ */ jsx(RibbonMenu, { label: "Breaks", icon: /* @__PURE__ */ jsx(SeparatorHorizontal, { size: 16 }), testId: "docx-layout-breaks", children: (close) => /* @__PURE__ */ jsx(
|
|
3140
|
+
RibbonGallery,
|
|
3141
|
+
{
|
|
3142
|
+
label: "Breaks",
|
|
3143
|
+
close,
|
|
3144
|
+
choices: rows.map((row) => ({
|
|
3145
|
+
id: row.id,
|
|
3146
|
+
label: row.label,
|
|
3147
|
+
detail: row.detail,
|
|
3148
|
+
disabled: !row.command.isEnabled,
|
|
3149
|
+
title: row.command.disabledReason ?? void 0
|
|
3150
|
+
})),
|
|
3151
|
+
onPick: (id) => void rows.find((row) => row.id === id)?.command.execute()
|
|
3152
|
+
}
|
|
3153
|
+
) });
|
|
3154
|
+
}
|
|
3155
|
+
function LayoutButtons() {
|
|
3156
|
+
const { pageSetup, apply } = usePageSetup();
|
|
3157
|
+
const [dialog, setDialog] = useState(false);
|
|
3158
|
+
const props = { setup: pageSetup, apply, openDialog: () => setDialog(true) };
|
|
3159
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3160
|
+
/* @__PURE__ */ jsx(Margins, { ...props }),
|
|
3161
|
+
/* @__PURE__ */ jsx(Orientation, { ...props }),
|
|
3162
|
+
/* @__PURE__ */ jsx(Size, { ...props }),
|
|
3163
|
+
/* @__PURE__ */ jsx(
|
|
3164
|
+
DocxEditor.Toolbar.Action,
|
|
3165
|
+
{
|
|
3166
|
+
label: "Columns",
|
|
3167
|
+
icon: /* @__PURE__ */ jsx(Columns2, { size: 16 }),
|
|
3168
|
+
disabled: true,
|
|
3169
|
+
disabledReason: COLUMNS_UNSUPPORTED,
|
|
3170
|
+
className: "native-docx-layout-columns"
|
|
3171
|
+
}
|
|
3172
|
+
),
|
|
3173
|
+
/* @__PURE__ */ jsx(Breaks, {}),
|
|
3174
|
+
/* @__PURE__ */ jsx(
|
|
3175
|
+
DocxEditor.Toolbar.Action,
|
|
3176
|
+
{
|
|
3177
|
+
label: "Page Setup\u2026",
|
|
3178
|
+
icon: /* @__PURE__ */ jsx(Settings2, { size: 16 }),
|
|
3179
|
+
onSelect: () => setDialog(true),
|
|
3180
|
+
className: "native-docx-layout-page-setup"
|
|
3181
|
+
}
|
|
3182
|
+
),
|
|
3183
|
+
/* @__PURE__ */ jsx(DocxEditor.PageSetupDialog, { open: dialog, onClose: () => setDialog(false) })
|
|
3184
|
+
] });
|
|
3185
|
+
}
|
|
3186
|
+
var ROW_ABOVE = { type: "insertRow", where: "above" };
|
|
3187
|
+
var ROW_BELOW = { type: "insertRow", where: "below" };
|
|
3188
|
+
var COLUMN_LEFT = { type: "insertColumn", where: "left" };
|
|
3189
|
+
var COLUMN_RIGHT = { type: "insertColumn", where: "right" };
|
|
3190
|
+
var MERGE = { type: "mergeCells" };
|
|
3191
|
+
var SPLIT = { type: "splitCell", rows: 1, cols: 2 };
|
|
3192
|
+
var HEADER_ROW = { type: "toggleHeaderRow" };
|
|
3193
|
+
var DELETE_ROW = { type: "deleteRow" };
|
|
3194
|
+
var DELETE_COLUMN = { type: "deleteColumn" };
|
|
3195
|
+
var DELETE_TABLE = { type: "deleteTable" };
|
|
3196
|
+
var ALIGN_TOP = { type: "setTableCellVerticalAlignment", alignment: "top" };
|
|
3197
|
+
var ALIGN_CENTER = { type: "setTableCellVerticalAlignment", alignment: "center" };
|
|
3198
|
+
var ALIGN_BOTTOM = { type: "setTableCellVerticalAlignment", alignment: "bottom" };
|
|
3199
|
+
function useRows(rows) {
|
|
3200
|
+
return rows.map((row) => ({ ...row, state: useEditorCommand(row.command) }));
|
|
3201
|
+
}
|
|
3202
|
+
function CommandGallery({
|
|
3203
|
+
label,
|
|
3204
|
+
icon,
|
|
3205
|
+
testId,
|
|
3206
|
+
rows
|
|
3207
|
+
}) {
|
|
3208
|
+
const bound = useRows(rows);
|
|
3209
|
+
return /* @__PURE__ */ jsx(RibbonMenu, { label, icon, testId, children: (close) => /* @__PURE__ */ jsx(
|
|
3210
|
+
RibbonGallery,
|
|
3211
|
+
{
|
|
3212
|
+
label,
|
|
3213
|
+
close,
|
|
3214
|
+
choices: bound.map((row) => ({
|
|
3215
|
+
id: row.id,
|
|
3216
|
+
label: row.label,
|
|
3217
|
+
icon: row.icon,
|
|
3218
|
+
disabled: !row.state.isEnabled,
|
|
3219
|
+
title: row.state.disabledReason ?? void 0
|
|
3220
|
+
})),
|
|
3221
|
+
onPick: (id) => void bound.find((row) => row.id === id)?.state.execute()
|
|
3222
|
+
}
|
|
3223
|
+
) });
|
|
3224
|
+
}
|
|
3225
|
+
function CommandAction({
|
|
3226
|
+
command,
|
|
3227
|
+
label,
|
|
3228
|
+
icon,
|
|
3229
|
+
testId
|
|
3230
|
+
}) {
|
|
3231
|
+
const state = useEditorCommand(command);
|
|
3232
|
+
return /* @__PURE__ */ jsx(
|
|
3233
|
+
DocxEditor.Toolbar.Action,
|
|
3234
|
+
{
|
|
3235
|
+
label,
|
|
3236
|
+
icon,
|
|
3237
|
+
active: state.isActive,
|
|
3238
|
+
disabled: !state.isEnabled,
|
|
3239
|
+
disabledReason: state.disabledReason ?? void 0,
|
|
3240
|
+
onSelect: () => void state.execute(),
|
|
3241
|
+
className: `native-docx-table-${testId}`
|
|
3242
|
+
}
|
|
3243
|
+
);
|
|
3244
|
+
}
|
|
3245
|
+
function TableButtons() {
|
|
3246
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3247
|
+
/* @__PURE__ */ jsx(
|
|
3248
|
+
CommandAction,
|
|
3249
|
+
{
|
|
3250
|
+
command: ROW_ABOVE,
|
|
3251
|
+
label: "Insert row above",
|
|
3252
|
+
icon: /* @__PURE__ */ jsx(BetweenHorizontalStart, { size: 16 }),
|
|
3253
|
+
testId: "row-above"
|
|
3254
|
+
}
|
|
3255
|
+
),
|
|
3256
|
+
/* @__PURE__ */ jsx(
|
|
3257
|
+
CommandAction,
|
|
3258
|
+
{
|
|
3259
|
+
command: ROW_BELOW,
|
|
3260
|
+
label: "Insert row below",
|
|
3261
|
+
icon: /* @__PURE__ */ jsx(BetweenHorizontalEnd, { size: 16 }),
|
|
3262
|
+
testId: "row-below"
|
|
3263
|
+
}
|
|
3264
|
+
),
|
|
3265
|
+
/* @__PURE__ */ jsx(
|
|
3266
|
+
CommandAction,
|
|
3267
|
+
{
|
|
3268
|
+
command: COLUMN_LEFT,
|
|
3269
|
+
label: "Insert column left",
|
|
3270
|
+
icon: /* @__PURE__ */ jsx(BetweenVerticalStart, { size: 16 }),
|
|
3271
|
+
testId: "column-left"
|
|
3272
|
+
}
|
|
3273
|
+
),
|
|
3274
|
+
/* @__PURE__ */ jsx(
|
|
3275
|
+
CommandAction,
|
|
3276
|
+
{
|
|
3277
|
+
command: COLUMN_RIGHT,
|
|
3278
|
+
label: "Insert column right",
|
|
3279
|
+
icon: /* @__PURE__ */ jsx(BetweenVerticalEnd, { size: 16 }),
|
|
3280
|
+
testId: "column-right"
|
|
3281
|
+
}
|
|
3282
|
+
),
|
|
3283
|
+
/* @__PURE__ */ jsx(
|
|
3284
|
+
CommandGallery,
|
|
3285
|
+
{
|
|
3286
|
+
label: "Delete",
|
|
3287
|
+
icon: /* @__PURE__ */ jsx(Trash2, { size: 16 }),
|
|
3288
|
+
testId: "docx-table-delete",
|
|
3289
|
+
rows: [
|
|
3290
|
+
{ id: "row", label: "Delete row", command: DELETE_ROW },
|
|
3291
|
+
{ id: "column", label: "Delete column", command: DELETE_COLUMN },
|
|
3292
|
+
{ id: "table", label: "Delete table", command: DELETE_TABLE }
|
|
3293
|
+
]
|
|
3294
|
+
}
|
|
3295
|
+
),
|
|
3296
|
+
/* @__PURE__ */ jsx(
|
|
3297
|
+
CommandAction,
|
|
3298
|
+
{
|
|
3299
|
+
command: MERGE,
|
|
3300
|
+
label: "Merge cells",
|
|
3301
|
+
icon: /* @__PURE__ */ jsx(Merge, { size: 16 }),
|
|
3302
|
+
testId: "merge"
|
|
3303
|
+
}
|
|
3304
|
+
),
|
|
3305
|
+
/* @__PURE__ */ jsx(CommandAction, { command: SPLIT, label: "Split cell", icon: /* @__PURE__ */ jsx(Split, { size: 16 }), testId: "split" }),
|
|
3306
|
+
/* @__PURE__ */ jsx(
|
|
3307
|
+
CommandAction,
|
|
3308
|
+
{
|
|
3309
|
+
command: HEADER_ROW,
|
|
3310
|
+
label: "Repeat header row",
|
|
3311
|
+
icon: /* @__PURE__ */ jsx(Heading, { size: 16 }),
|
|
3312
|
+
testId: "header-row"
|
|
3313
|
+
}
|
|
3314
|
+
),
|
|
3315
|
+
/* @__PURE__ */ jsx(
|
|
3316
|
+
CommandGallery,
|
|
3317
|
+
{
|
|
3318
|
+
label: "Cell alignment",
|
|
3319
|
+
icon: /* @__PURE__ */ jsx(AlignVerticalJustifyCenter, { size: 16 }),
|
|
3320
|
+
testId: "docx-table-align",
|
|
3321
|
+
rows: [
|
|
3322
|
+
{
|
|
3323
|
+
id: "top",
|
|
3324
|
+
label: "Top",
|
|
3325
|
+
icon: /* @__PURE__ */ jsx(AlignVerticalJustifyStart, { size: 16 }),
|
|
3326
|
+
command: ALIGN_TOP
|
|
3327
|
+
},
|
|
3328
|
+
{
|
|
3329
|
+
id: "center",
|
|
3330
|
+
label: "Center",
|
|
3331
|
+
icon: /* @__PURE__ */ jsx(AlignVerticalJustifyCenter, { size: 16 }),
|
|
3332
|
+
command: ALIGN_CENTER
|
|
3333
|
+
},
|
|
3334
|
+
{
|
|
3335
|
+
id: "bottom",
|
|
3336
|
+
label: "Bottom",
|
|
3337
|
+
icon: /* @__PURE__ */ jsx(AlignVerticalJustifyEnd, { size: 16 }),
|
|
3338
|
+
command: ALIGN_BOTTOM
|
|
3339
|
+
}
|
|
3340
|
+
]
|
|
3341
|
+
}
|
|
3342
|
+
),
|
|
3343
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.TableBorderTarget, {}),
|
|
3344
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.TableBorderStyle, {}),
|
|
3345
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.TableBorderWidth, {}),
|
|
3346
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.TableBorderColor, {}),
|
|
3347
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.TableCellFill, {})
|
|
3348
|
+
] });
|
|
3349
|
+
}
|
|
3350
|
+
var CAPTION_LABELS = ["Figure", "Table", "Equation"];
|
|
3351
|
+
var CAPTION_STYLES = stylesXml(
|
|
3352
|
+
"",
|
|
3353
|
+
'<w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/></w:style><w:style w:type="paragraph" w:styleId="Caption"><w:name w:val="caption"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:uiPriority w:val="35"/><w:unhideWhenUsed/><w:qFormat/><w:pPr><w:spacing w:after="200" w:line="240" w:lineRule="auto"/></w:pPr><w:rPr><w:i/><w:iCs/><w:color w:val="44546A"/><w:sz w:val="18"/><w:szCs w:val="18"/></w:rPr></w:style>'
|
|
3354
|
+
);
|
|
3355
|
+
function seqIdentifier(instruction) {
|
|
3356
|
+
const match = /^\s*SEQ\s+("[^"]+"|\S+)/i.exec(instruction);
|
|
3357
|
+
return match ? match[1].replace(/"/g, "") : null;
|
|
3358
|
+
}
|
|
3359
|
+
function countSeqFields(root, label, throughId) {
|
|
3360
|
+
let count = 0;
|
|
3361
|
+
let done = false;
|
|
3362
|
+
const target = label.toLowerCase();
|
|
3363
|
+
const visit = (node) => {
|
|
3364
|
+
if (done) return;
|
|
3365
|
+
if (node.localName === "fldSimple") {
|
|
3366
|
+
const instr = node.attributes?.find((a) => a.localName === "instr")?.value ?? "";
|
|
3367
|
+
if (seqIdentifier(instr)?.toLowerCase() === target) count++;
|
|
3368
|
+
} else if (node.localName === "instrText") {
|
|
3369
|
+
const text = (node.children ?? []).map((c) => c.value ?? "").join("");
|
|
3370
|
+
if (seqIdentifier(text)?.toLowerCase() === target) count++;
|
|
3371
|
+
}
|
|
3372
|
+
node.children?.forEach(visit);
|
|
3373
|
+
if (throughId && node.id === throughId) done = true;
|
|
3374
|
+
};
|
|
3375
|
+
visit(root);
|
|
3376
|
+
return count;
|
|
3377
|
+
}
|
|
3378
|
+
function captionParagraphXml(label, number, text) {
|
|
3379
|
+
const run = (value) => `<w:r><w:t xml:space="preserve">${esc(value)}</w:t></w:r>`;
|
|
3380
|
+
const rest = text.trim() ? `: ${text.trim()}` : "";
|
|
3381
|
+
return `<w:p><w:pPr><w:pStyle w:val="Caption"/></w:pPr>${run(`${label} `)}<w:fldSimple w:instr=" SEQ ${esc(label)} \\* ARABIC "><w:r><w:t>${number}</w:t></w:r></w:fldSimple>${rest ? run(rest) : ""}</w:p>`;
|
|
3382
|
+
}
|
|
3383
|
+
function base642(bytes) {
|
|
3384
|
+
let binary = "";
|
|
3385
|
+
for (let i = 0; i < bytes.length; i += 32768)
|
|
3386
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
|
|
3387
|
+
return btoa(binary);
|
|
3388
|
+
}
|
|
3389
|
+
function captionFragmentHtml(label, number, text, revision) {
|
|
3390
|
+
const untracked = docxPackage({
|
|
3391
|
+
document: documentXml(captionParagraphXml(label, number, text)),
|
|
3392
|
+
styles: CAPTION_STYLES,
|
|
3393
|
+
title: "Caption"
|
|
3394
|
+
});
|
|
3395
|
+
const bytes = revision ? trackInsertion(untracked, revision, true) : untracked;
|
|
3396
|
+
return `<div data-docx-fragment="${base642(bytes)}" data-docx-fragment-end="covered"></div>`;
|
|
3397
|
+
}
|
|
3398
|
+
var TRANSFORMS = Object.fromEntries(
|
|
3399
|
+
["rotateCW", "rotateCCW", "flipH", "flipV"].map((action) => [
|
|
3400
|
+
action,
|
|
3401
|
+
{ type: "transformImage", action }
|
|
3402
|
+
])
|
|
3403
|
+
);
|
|
3404
|
+
function TransformAction({
|
|
3405
|
+
action,
|
|
3406
|
+
label,
|
|
3407
|
+
icon
|
|
3408
|
+
}) {
|
|
3409
|
+
const state = useEditorCommand(TRANSFORMS[action]);
|
|
3410
|
+
return /* @__PURE__ */ jsx(
|
|
3411
|
+
DocxEditor.Toolbar.Action,
|
|
3412
|
+
{
|
|
3413
|
+
label,
|
|
3414
|
+
icon,
|
|
3415
|
+
disabled: !state.isEnabled,
|
|
3416
|
+
disabledReason: state.disabledReason ?? void 0,
|
|
3417
|
+
onSelect: () => void state.execute(),
|
|
3418
|
+
className: `native-docx-picture-${action}`
|
|
3419
|
+
}
|
|
3420
|
+
);
|
|
3421
|
+
}
|
|
3422
|
+
function insertCaption(editor, label, text, track) {
|
|
3423
|
+
const surface = editor.surface;
|
|
3424
|
+
const selection = surface?.state().selection;
|
|
3425
|
+
if (!surface || !selection) return null;
|
|
3426
|
+
const { paragraphId } = selection.head;
|
|
3427
|
+
const number = countSeqFields(
|
|
3428
|
+
surface.session.part().root,
|
|
3429
|
+
label,
|
|
3430
|
+
paragraphId
|
|
3431
|
+
) + 1;
|
|
3432
|
+
const ids = surface.session.paragraphIds();
|
|
3433
|
+
const next = ids[ids.indexOf(paragraphId) + 1];
|
|
3434
|
+
const caption = `${label} ${number}${text.trim() ? `: ${text.trim()}` : ""}`;
|
|
3435
|
+
const outcome = track({
|
|
3436
|
+
label: `Inserted caption: ${caption}`,
|
|
3437
|
+
inserted: (inserted) => inserted !== null,
|
|
3438
|
+
insert: (revision) => {
|
|
3439
|
+
if (next) {
|
|
3440
|
+
surface.setSelection({
|
|
3441
|
+
anchor: { paragraphId: next, offset: 0 },
|
|
3442
|
+
head: { paragraphId: next, offset: 0 }
|
|
3443
|
+
});
|
|
3444
|
+
} else {
|
|
3445
|
+
editor.exec({ type: "selectAll" });
|
|
3446
|
+
const end = surface.state().selection?.head;
|
|
3447
|
+
if (!end) return null;
|
|
3448
|
+
surface.setSelection({ anchor: end, head: end });
|
|
3449
|
+
surface.splitParagraph();
|
|
3450
|
+
}
|
|
3451
|
+
const ok2 = editor.exec({
|
|
3452
|
+
type: "paste",
|
|
3453
|
+
text: caption,
|
|
3454
|
+
html: captionFragmentHtml(label, number, text, revision)
|
|
3455
|
+
}).ok;
|
|
3456
|
+
return ok2 ? number : null;
|
|
3457
|
+
}
|
|
3458
|
+
});
|
|
3459
|
+
return outcome?.result ?? null;
|
|
3460
|
+
}
|
|
3461
|
+
function CaptionButton({ defaultLabel = "Figure" }) {
|
|
3462
|
+
const editor = useDocxEditor();
|
|
3463
|
+
const track = useTrackedOrNoted();
|
|
3464
|
+
const [label, setLabel] = useState(defaultLabel);
|
|
3465
|
+
const [text, setText] = useState("");
|
|
3466
|
+
const [error, setError] = useState("");
|
|
3467
|
+
return /* @__PURE__ */ jsx(RibbonMenu, { label: "Insert caption", icon: /* @__PURE__ */ jsx(Captions, { size: 16 }), testId: "docx-insert-caption", children: (close) => /* @__PURE__ */ jsxs(
|
|
3468
|
+
"form",
|
|
3469
|
+
{
|
|
3470
|
+
className: "native-docx-form",
|
|
3471
|
+
onSubmit: (event) => {
|
|
3472
|
+
event.preventDefault();
|
|
3473
|
+
if (!editor) return;
|
|
3474
|
+
try {
|
|
3475
|
+
if (insertCaption(editor, label, text, track) === null)
|
|
3476
|
+
return setError("The caption could not be inserted here.");
|
|
3477
|
+
setText("");
|
|
3478
|
+
setError("");
|
|
3479
|
+
close();
|
|
3480
|
+
editor.focus();
|
|
3481
|
+
} catch (failure) {
|
|
3482
|
+
setError(failure instanceof Error ? failure.message : String(failure));
|
|
3483
|
+
}
|
|
3484
|
+
},
|
|
3485
|
+
children: [
|
|
3486
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
3487
|
+
"Label",
|
|
3488
|
+
/* @__PURE__ */ jsx(
|
|
3489
|
+
"select",
|
|
3490
|
+
{
|
|
3491
|
+
value: label,
|
|
3492
|
+
onChange: (event) => setLabel(event.target.value),
|
|
3493
|
+
children: CAPTION_LABELS.map((value) => /* @__PURE__ */ jsx("option", { children: value }, value))
|
|
3494
|
+
}
|
|
3495
|
+
)
|
|
3496
|
+
] }),
|
|
3497
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
3498
|
+
"Caption",
|
|
3499
|
+
/* @__PURE__ */ jsx(
|
|
3500
|
+
"input",
|
|
3501
|
+
{
|
|
3502
|
+
autoFocus: true,
|
|
3503
|
+
value: text,
|
|
3504
|
+
placeholder: "Optional text after the number",
|
|
3505
|
+
"data-testid": "docx-caption-text",
|
|
3506
|
+
onChange: (event) => setText(event.target.value)
|
|
3507
|
+
}
|
|
3508
|
+
)
|
|
3509
|
+
] }),
|
|
3510
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", children: error }),
|
|
3511
|
+
/* @__PURE__ */ jsx("button", { type: "submit", "data-testid": "docx-caption-insert", children: "Insert" })
|
|
3512
|
+
]
|
|
3513
|
+
}
|
|
3514
|
+
) });
|
|
3515
|
+
}
|
|
3516
|
+
function PictureButtons() {
|
|
3517
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3518
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.ImageWrap, {}),
|
|
3519
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.ImageProperties, {}),
|
|
3520
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.ImageAltText, {}),
|
|
3521
|
+
/* @__PURE__ */ jsx(TransformAction, { action: "rotateCW", label: "Rotate right 90\xB0", icon: /* @__PURE__ */ jsx(RotateCw, { size: 16 }) }),
|
|
3522
|
+
/* @__PURE__ */ jsx(TransformAction, { action: "rotateCCW", label: "Rotate left 90\xB0", icon: /* @__PURE__ */ jsx(RotateCcw, { size: 16 }) }),
|
|
3523
|
+
/* @__PURE__ */ jsx(
|
|
3524
|
+
TransformAction,
|
|
3525
|
+
{
|
|
3526
|
+
action: "flipH",
|
|
3527
|
+
label: "Flip horizontal",
|
|
3528
|
+
icon: /* @__PURE__ */ jsx(FlipHorizontal2, { size: 16 })
|
|
3529
|
+
}
|
|
3530
|
+
),
|
|
3531
|
+
/* @__PURE__ */ jsx(TransformAction, { action: "flipV", label: "Flip vertical", icon: /* @__PURE__ */ jsx(FlipVertical2, { size: 16 }) }),
|
|
3532
|
+
/* @__PURE__ */ jsx(CaptionButton, {})
|
|
3533
|
+
] });
|
|
3534
|
+
}
|
|
3535
|
+
var LAYOUT_TAB = "Layout";
|
|
3536
|
+
var TABLE_TAB = "Table";
|
|
3537
|
+
var PICTURE_TAB = "Picture";
|
|
3538
|
+
function useContextualTabs(tab) {
|
|
3539
|
+
const inTable = useEditorState((snapshot) => snapshot.table !== null);
|
|
3540
|
+
const onPicture = useEditorState((snapshot) => snapshot.image !== null);
|
|
3541
|
+
const tabs = [...inTable ? [TABLE_TAB] : [], ...onPicture ? [PICTURE_TAB] : []];
|
|
3542
|
+
const contextual = tab === TABLE_TAB || tab === PICTURE_TAB;
|
|
3543
|
+
return { tabs, shown: contextual && !tabs.includes(tab) ? "Home" : tab };
|
|
3544
|
+
}
|
|
3545
|
+
function LayoutTabContent({ tab }) {
|
|
3546
|
+
if (tab === LAYOUT_TAB) return /* @__PURE__ */ jsx(LayoutButtons, {});
|
|
3547
|
+
if (tab === TABLE_TAB) return /* @__PURE__ */ jsx(TableButtons, {});
|
|
3548
|
+
if (tab === PICTURE_TAB) return /* @__PURE__ */ jsx(PictureButtons, {});
|
|
3549
|
+
return null;
|
|
3550
|
+
}
|
|
3551
|
+
var contains = (node, id) => node.id === id || (node.children ?? []).some((child) => contains(child, id));
|
|
3552
|
+
function firstParagraph(node) {
|
|
3553
|
+
for (const child of node.children ?? []) {
|
|
3554
|
+
if (child.kind === "paragraph" && child.id) return child.id;
|
|
3555
|
+
if (child.localName === "tbl") continue;
|
|
3556
|
+
const nested = firstParagraph(child);
|
|
3557
|
+
if (nested) return nested;
|
|
3558
|
+
}
|
|
3559
|
+
return null;
|
|
3560
|
+
}
|
|
3561
|
+
function tableCells(root, paragraphId) {
|
|
3562
|
+
let table = null;
|
|
3563
|
+
const find = (node) => {
|
|
3564
|
+
if (node.localName === "tbl" && contains(node, paragraphId)) table = node;
|
|
3565
|
+
for (const child of node.children ?? []) if (contains(child, paragraphId)) find(child);
|
|
3566
|
+
};
|
|
3567
|
+
find(root);
|
|
3568
|
+
if (!table) return null;
|
|
3569
|
+
const cells = [];
|
|
3570
|
+
let index = -1;
|
|
3571
|
+
const rows = (node) => (node.children ?? []).flatMap(
|
|
3572
|
+
(child) => child.localName === "tr" ? [child] : child.localName === "tbl" ? [] : rows(child)
|
|
3573
|
+
);
|
|
3574
|
+
for (const row of rows(table))
|
|
3575
|
+
for (const cell of (row.children ?? []).filter((child) => child.localName === "tc")) {
|
|
3576
|
+
const first = firstParagraph(cell);
|
|
3577
|
+
if (!first) continue;
|
|
3578
|
+
if (contains(cell, paragraphId)) index = cells.length;
|
|
3579
|
+
cells.push(first);
|
|
3580
|
+
}
|
|
3581
|
+
return index < 0 ? null : { cells, index };
|
|
3582
|
+
}
|
|
3583
|
+
function TableTabKeys({ scope }) {
|
|
3584
|
+
const editor = useDocxEditor();
|
|
3585
|
+
const latest = useRef(editor);
|
|
3586
|
+
latest.current = editor;
|
|
3587
|
+
useEffect(() => {
|
|
3588
|
+
const element = scope.current;
|
|
3589
|
+
if (!element) return;
|
|
3590
|
+
const onKeyDown = (event) => {
|
|
3591
|
+
if (event.key !== "Tab" || event.ctrlKey || event.metaKey || event.altKey) return;
|
|
3592
|
+
const editor2 = latest.current;
|
|
3593
|
+
const surface = editor2?.surface;
|
|
3594
|
+
const head = surface?.state().selection?.head;
|
|
3595
|
+
if (!editor2 || !surface || !head) return;
|
|
3596
|
+
const root = () => surface.session.part().root;
|
|
3597
|
+
const table = tableCells(root(), head.paragraphId);
|
|
3598
|
+
if (!table) return;
|
|
3599
|
+
event.preventDefault();
|
|
3600
|
+
event.stopPropagation();
|
|
3601
|
+
let target = table.cells[table.index + (event.shiftKey ? -1 : 1)];
|
|
3602
|
+
if (!target && !event.shiftKey && editor2.exec({ type: "insertRow", where: "below" }).ok) {
|
|
3603
|
+
const grown = tableCells(
|
|
3604
|
+
root(),
|
|
3605
|
+
surface.state().selection?.head.paragraphId ?? head.paragraphId
|
|
3606
|
+
);
|
|
3607
|
+
target = grown?.cells[table.cells.length] ?? "";
|
|
3608
|
+
}
|
|
3609
|
+
if (!target) return;
|
|
3610
|
+
const point = { paragraphId: target, offset: 0 };
|
|
3611
|
+
surface.setSelection({ anchor: point, head: point });
|
|
3612
|
+
};
|
|
3613
|
+
element.addEventListener("keydown", onKeyDown, true);
|
|
3614
|
+
return () => element.removeEventListener("keydown", onKeyDown, true);
|
|
3615
|
+
}, [scope]);
|
|
3616
|
+
return null;
|
|
3617
|
+
}
|
|
2681
3618
|
function QuickParts() {
|
|
2682
3619
|
const house = useHouseTemplate();
|
|
2683
3620
|
if (!house || house.blocks.length === 0) return null;
|
|
@@ -2848,17 +3785,23 @@ function DefinedTermButtons() {
|
|
|
2848
3785
|
}
|
|
2849
3786
|
function DocxRibbon({
|
|
2850
3787
|
onSave,
|
|
2851
|
-
badge
|
|
3788
|
+
badge,
|
|
3789
|
+
imageGeneration
|
|
2852
3790
|
}) {
|
|
2853
|
-
const [
|
|
3791
|
+
const [selected, setTab] = useState("Home");
|
|
3792
|
+
const contextual = useContextualTabs(selected);
|
|
3793
|
+
useGenerateImageTarget();
|
|
3794
|
+
const tab = contextual.shown;
|
|
2854
3795
|
const t = useChromeTranslate();
|
|
2855
3796
|
return /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon", children: [
|
|
2856
3797
|
/* @__PURE__ */ jsxs("select", { "aria-label": "Ribbon tab", value: tab, onChange: (event) => setTab(event.target.value), children: [
|
|
2857
3798
|
/* @__PURE__ */ jsx("option", { children: "Home" }),
|
|
2858
3799
|
/* @__PURE__ */ jsx("option", { children: "Insert" }),
|
|
2859
3800
|
/* @__PURE__ */ jsx("option", { children: "Design" }),
|
|
3801
|
+
/* @__PURE__ */ jsx("option", { children: LAYOUT_TAB }),
|
|
2860
3802
|
/* @__PURE__ */ jsx("option", { children: "References" }),
|
|
2861
|
-
/* @__PURE__ */ jsx("option", { children: "Review" })
|
|
3803
|
+
/* @__PURE__ */ jsx("option", { children: "Review" }),
|
|
3804
|
+
contextual.tabs.map((name) => /* @__PURE__ */ jsx("option", { children: name }, name))
|
|
2862
3805
|
] }),
|
|
2863
3806
|
/* @__PURE__ */ jsxs(DocxEditor.Toolbar, { t, preset: false, onSave, children: [
|
|
2864
3807
|
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Undo, {}),
|
|
@@ -2889,10 +3832,14 @@ function DocxRibbon({
|
|
|
2889
3832
|
tab === "Insert" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2890
3833
|
/* @__PURE__ */ jsx(DocxEditor.Toolbar.TableInsert, {}),
|
|
2891
3834
|
/* @__PURE__ */ jsx(DocxEditor.Toolbar.ImageInsert, {}),
|
|
3835
|
+
imageGeneration && /* @__PURE__ */ jsx(GenerateImageButton, { generation: imageGeneration }),
|
|
3836
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Button, { slotId: "insert.pageBreak" }),
|
|
2892
3837
|
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Link, {}),
|
|
2893
3838
|
/* @__PURE__ */ jsx(DefinedTermButtons, {}),
|
|
2894
3839
|
/* @__PURE__ */ jsx(QuickParts, {})
|
|
2895
3840
|
] }),
|
|
3841
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.TableCellFill, { hidden: true }),
|
|
3842
|
+
/* @__PURE__ */ jsx(LayoutTabContent, { tab }),
|
|
2896
3843
|
tab === "Design" && /* @__PURE__ */ jsx(HouseTemplatePanel, {}),
|
|
2897
3844
|
tab === "References" && /* @__PURE__ */ jsx(NoteButtons, {}),
|
|
2898
3845
|
tab === "Review" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -3239,7 +4186,7 @@ function NativeDocxEditor({
|
|
|
3239
4186
|
if (change.source !== "load") changed();
|
|
3240
4187
|
},
|
|
3241
4188
|
onFontError: (e) => setError(e.message),
|
|
3242
|
-
children: /* @__PURE__ */ jsx(
|
|
4189
|
+
children: /* @__PURE__ */ jsx(DocxAuthorContext.Provider, { value: ready.author, children: /* @__PURE__ */ jsx(
|
|
3243
4190
|
DefinedTermsProvider,
|
|
3244
4191
|
{
|
|
3245
4192
|
load: adapter.definitions,
|
|
@@ -3268,6 +4215,10 @@ function NativeDocxEditor({
|
|
|
3268
4215
|
DocxRibbon,
|
|
3269
4216
|
{
|
|
3270
4217
|
onSave: () => void saveVersion(),
|
|
4218
|
+
imageGeneration: adapter.imageGeneration && {
|
|
4219
|
+
notify: adapter.notify,
|
|
4220
|
+
...adapter.imageGeneration
|
|
4221
|
+
},
|
|
3271
4222
|
badge: (
|
|
3272
4223
|
// Says the document is shared live, in place of a title row above the ribbon.
|
|
3273
4224
|
ready.room && ready.canEdit && !snapshot ? /* @__PURE__ */ jsx("span", { title: "Edits sync live with everyone who has this document open", children: "Co-edit mode" }) : void 0
|
|
@@ -3275,6 +4226,7 @@ function NativeDocxEditor({
|
|
|
3275
4226
|
}
|
|
3276
4227
|
),
|
|
3277
4228
|
!stream && /* @__PURE__ */ jsx(WordShortcuts, { scope: stageRef }),
|
|
4229
|
+
!stream && /* @__PURE__ */ jsx(TableTabKeys, { scope: stageRef }),
|
|
3278
4230
|
/* @__PURE__ */ jsxs(
|
|
3279
4231
|
"div",
|
|
3280
4232
|
{
|
|
@@ -3312,7 +4264,7 @@ function NativeDocxEditor({
|
|
|
3312
4264
|
}
|
|
3313
4265
|
)
|
|
3314
4266
|
}
|
|
3315
|
-
)
|
|
4267
|
+
) })
|
|
3316
4268
|
}
|
|
3317
4269
|
) }, retry) }),
|
|
3318
4270
|
/* @__PURE__ */ jsxs("div", { className: "native-docx-actions", children: [
|
|
@@ -3407,6 +4359,6 @@ async function loadFonts(baseUrl) {
|
|
|
3407
4359
|
};
|
|
3408
4360
|
}
|
|
3409
4361
|
|
|
3410
|
-
export { BUILTIN_TEMPLATE_NAME, DEFAULT_DEFINED_TERMS_LABELS, DEFINED_TERM_TRIGGER, DefinedTermsProvider, DocxCommentActivity, DocxEditorReview, DocxMentions, DocxParticipants, DocxRibbon, HouseTemplateProvider, NativeDocxEditor, builtinHouseTemplate, contentPath, editorPort, findDefinitionsSection, insertDefinedTerm, loadDocxFonts, normalizeDocxExport, resolveHouseTemplate, reviewModule, templateFileName, updateDefinitions, useDefinedTerms, useHouseTemplate, useReview, useReviewItem, withDirectEditing };
|
|
3411
|
-
//# sourceMappingURL=chunk-
|
|
3412
|
-
//# sourceMappingURL=chunk-
|
|
4362
|
+
export { BUILTIN_TEMPLATE_NAME, DEFAULT_DEFINED_TERMS_LABELS, DEFINED_TERM_TRIGGER, DefinedTermsProvider, DocxAuthorContext, DocxCommentActivity, DocxEditorReview, DocxMentions, DocxParticipants, DocxRibbon, GenerateImageButton, HouseTemplateProvider, NativeDocxEditor, builtinHouseTemplate, contentPath, editorPort, findDefinitionsSection, insertDefinedTerm, loadDocxFonts, normalizeDocxExport, resolveHouseTemplate, reviewModule, structuralPort, templateFileName, trackedOrNoted, updateDefinitions, useDefinedTerms, useHouseTemplate, useReview, useReviewItem, useTrackedOrNoted, withDirectEditing };
|
|
4363
|
+
//# sourceMappingURL=chunk-72PQ42YI.js.map
|
|
4364
|
+
//# sourceMappingURL=chunk-72PQ42YI.js.map
|