@arbidocs/blocks 0.4.105 → 0.4.107
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/bin/arbidocs-render-take.mjs +27 -0
- package/dist/NativeDocxEditor-3VKJT43D.js +10 -0
- package/dist/NativeDocxEditor-3VKJT43D.js.map +1 -0
- package/dist/NativeDocxEditor-GNG5QV7V.cjs +31 -0
- package/dist/NativeDocxEditor-GNG5QV7V.cjs.map +1 -0
- package/dist/agents/index.cjs +210 -210
- package/dist/agents/index.js +5 -5
- package/dist/chat/index.cjs +125 -125
- package/dist/chat/index.js +3 -3
- package/dist/{chunk-PIT2YZCK.js → chunk-2PYENCW5.js} +3 -3
- package/dist/{chunk-PIT2YZCK.js.map → chunk-2PYENCW5.js.map} +1 -1
- package/dist/{chunk-W2RTZOK3.js → chunk-2SJTXYRM.js} +1749 -1778
- package/dist/chunk-2SJTXYRM.js.map +1 -0
- package/dist/{chunk-S7RHJI5Q.js → chunk-7NLPKJV5.js} +3 -3
- package/dist/{chunk-S7RHJI5Q.js.map → chunk-7NLPKJV5.js.map} +1 -1
- package/dist/{chunk-ZWWN4CUL.cjs → chunk-BHKFENKT.cjs} +1748 -1778
- package/dist/chunk-BHKFENKT.cjs.map +1 -0
- package/dist/{chunk-3R6XBYL7.cjs → chunk-DTBSGG36.cjs} +7 -2
- package/dist/chunk-DTBSGG36.cjs.map +1 -0
- package/dist/{chunk-IU542FPZ.js → chunk-EB7QAZLH.js} +6 -6
- package/dist/{chunk-IU542FPZ.js.map → chunk-EB7QAZLH.js.map} +1 -1
- package/dist/{chunk-TP3FAP5J.cjs → chunk-FZGMY6NQ.cjs} +5 -5
- package/dist/{chunk-TP3FAP5J.cjs.map → chunk-FZGMY6NQ.cjs.map} +1 -1
- package/dist/{chunk-W7NZRZ54.js → chunk-GFYGYME3.js} +3 -3
- package/dist/{chunk-W7NZRZ54.js.map → chunk-GFYGYME3.js.map} +1 -1
- package/dist/chunk-GYAUJ4KT.js +71 -0
- package/dist/chunk-GYAUJ4KT.js.map +1 -0
- package/dist/{chunk-XMBYDLZQ.cjs → chunk-HUJLKDWX.cjs} +4 -4
- package/dist/{chunk-XMBYDLZQ.cjs.map → chunk-HUJLKDWX.cjs.map} +1 -1
- package/dist/{chunk-4GFB5U3K.js → chunk-LHBJJ2GE.js} +4 -3
- package/dist/chunk-LHBJJ2GE.js.map +1 -0
- package/dist/{chunk-MP6FK43E.cjs → chunk-O5NDG4EA.cjs} +3 -3
- package/dist/{chunk-MP6FK43E.cjs.map → chunk-O5NDG4EA.cjs.map} +1 -1
- package/dist/{chunk-5YXG342J.cjs → chunk-OR4CO743.cjs} +3 -3
- package/dist/{chunk-5YXG342J.cjs.map → chunk-OR4CO743.cjs.map} +1 -1
- package/dist/{chunk-LGX6UFYG.js → chunk-P2KPDPFF.js} +8 -3
- package/dist/chunk-P2KPDPFF.js.map +1 -0
- package/dist/{chunk-DDF5GPIJ.js → chunk-P5ST47VY.js} +3 -3
- package/dist/{chunk-DDF5GPIJ.js.map → chunk-P5ST47VY.js.map} +1 -1
- package/dist/{chunk-EWSEQEPL.js → chunk-PFA43QNX.js} +3 -3
- package/dist/{chunk-EWSEQEPL.js.map → chunk-PFA43QNX.js.map} +1 -1
- package/dist/{chunk-7LLLQUM7.cjs → chunk-Q57MEBCK.cjs} +6 -5
- package/dist/chunk-Q57MEBCK.cjs.map +1 -0
- package/dist/{chunk-3PCHGUL5.cjs → chunk-R6APX23R.cjs} +12 -12
- package/dist/{chunk-3PCHGUL5.cjs.map → chunk-R6APX23R.cjs.map} +1 -1
- package/dist/{chunk-24ND4S6Y.cjs → chunk-RILMICOP.cjs} +5 -5
- package/dist/{chunk-24ND4S6Y.cjs.map → chunk-RILMICOP.cjs.map} +1 -1
- package/dist/chunk-S24XEVPS.cjs +73 -0
- package/dist/chunk-S24XEVPS.cjs.map +1 -0
- package/dist/{chunk-XQDM6T6C.js → chunk-WDD255XQ.js} +3 -3
- package/dist/{chunk-XQDM6T6C.js.map → chunk-WDD255XQ.js.map} +1 -1
- package/dist/{chunk-Z5VUZ6YD.cjs → chunk-ZV3BKLFY.cjs} +3 -3
- package/dist/{chunk-Z5VUZ6YD.cjs.map → chunk-ZV3BKLFY.cjs.map} +1 -1
- package/dist/collaboration/index.cjs +15 -15
- package/dist/collaboration/index.js +1 -1
- package/dist/collaboration/mail/MailMessageView.d.ts +3 -1
- package/dist/connect/docx.cjs +7 -6
- package/dist/connect/docx.cjs.map +1 -1
- package/dist/connect/docx.js +5 -4
- package/dist/connect/docx.js.map +1 -1
- package/dist/connect/index.cjs +409 -93
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.js +363 -48
- package/dist/connect/index.js.map +1 -1
- package/dist/connection/mail/ConnectedMailComposer.d.ts +12 -2
- package/dist/connection/mail/ConnectedMailMessage.d.ts +19 -0
- package/dist/connection/mail/ConnectedMailbox.d.ts +7 -3
- package/dist/connection/mail/MailComposerForm.d.ts +12 -2
- package/dist/connection/mail/index.d.ts +2 -1
- package/dist/connection/mail/types.d.ts +15 -0
- package/dist/connection/mail/useWordBody.d.ts +48 -0
- package/dist/docx/NativeDocxEditor.d.ts +9 -1
- package/dist/docx/index.cjs +51 -50
- package/dist/docx/index.cjs.map +1 -1
- package/dist/docx/index.js +4 -3
- package/dist/docx/index.js.map +1 -1
- package/dist/facts/index.cjs +2 -2
- package/dist/facts/index.js +2 -2
- package/dist/files/index.cjs +2 -2
- package/dist/files/index.js +2 -2
- package/dist/home/index.cjs +17 -17
- package/dist/home/index.js +3 -3
- package/dist/index.cjs +451 -451
- package/dist/index.js +9 -9
- package/dist/record/index.cjs +307 -66
- package/dist/record/index.cjs.map +1 -1
- package/dist/record/index.d.ts +145 -26
- package/dist/record/index.js +301 -67
- package/dist/record/index.js.map +1 -1
- package/dist/retrieval/index.cjs +1 -1
- package/dist/retrieval/index.js +1 -1
- package/dist/settings/index.cjs +36 -36
- package/dist/settings/index.js +3 -3
- package/dist/shell/index.cjs +1 -1
- package/dist/shell/index.js +1 -1
- package/dist/studio/index.cjs +4 -4
- package/dist/studio/index.js +3 -3
- package/dist/workflow-canvas/index.cjs +12 -12
- package/dist/workflow-canvas/index.js +5 -5
- package/dist/workspaces/index.cjs +2 -2
- package/dist/workspaces/index.js +2 -2
- package/node_modules/@arbidocs/coedit/package.json +1 -1
- package/package.json +8 -5
- package/record/render-overlays.mjs +152 -0
- package/record/render-take.py +985 -0
- package/src/docx/styles.css +22 -0
- package/dist/chunk-3R6XBYL7.cjs.map +0 -1
- package/dist/chunk-4GFB5U3K.js.map +0 -1
- package/dist/chunk-7LLLQUM7.cjs.map +0 -1
- package/dist/chunk-LGX6UFYG.js.map +0 -1
- package/dist/chunk-W2RTZOK3.js.map +0 -1
- package/dist/chunk-ZWWN4CUL.cjs.map +0 -1
|
@@ -3,20 +3,20 @@ import { FactPassageItem } from './chunk-HFZ45FD3.js';
|
|
|
3
3
|
import { stylesXml, definitionBookmark, groupDefinitions, definitionLabels, filterTermGroups, esc, hyperlinkRelationship, docxPackage, documentXml, definitionIdOfBookmark, compareTerms, parseDefinition } from './chunk-EPHGD4YV.js';
|
|
4
4
|
import { filterMentionCandidates, MentionMenu, findMentionQuery, listOptionId, extractMentions, useListKeyNav } from './chunk-AA3PT6KR.js';
|
|
5
5
|
import { unzipSync, strFromU8, strToU8, zipSync } from 'fflate';
|
|
6
|
-
import {
|
|
7
|
-
import React4, { createContext, useContext, useState, useRef, useCallback, useEffect, useLayoutEffect, useMemo, useSyncExternalStore } from 'react';
|
|
8
|
-
import { useDocxEditor, ReviewRailContext, useEditorSnapshot, useEditorState, useChromeTranslate, DocxEditor, createFontSource, useParagraphStyle, normalizeImageBytes, usePageSetup, useEditorCommand } from '@docx-editor.dev/react';
|
|
6
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
9
7
|
import { AiMarkdown, Dialog, DialogContent, DialogHeader, DialogTitle, cn, DialogDescription, Button, DialogFooter, Tabs, TabsList, TabsTrigger, TabsContent, Input, splitMarkdownBlocks } from '@arbidocs/react/ui';
|
|
10
|
-
import { MessageSquarePlus, MessageSquare, PencilLine, ChevronLeft, ChevronRight, X, ChevronUp, ChevronDown, CheckCheck, RotateCcw, CircleCheck, Trash2, Check, SendHorizontal, Loader2, Sparkles, Type, Pilcrow, UsersRound, RefreshCw, WifiOff, Download, Save, Ellipsis, BookA, Blocks, Link2, Superscript, NotebookText, RefreshCcw, Columns2, Settings2, BetweenHorizontalStart, BetweenHorizontalEnd, BetweenVerticalStart, BetweenVerticalEnd, Merge, Split, Heading, AlignVerticalJustifyStart, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, RotateCw, FlipHorizontal2, FlipVertical2, Scaling, RectangleVertical, RectangleHorizontal, FileText, SeparatorHorizontal, Captions } from 'lucide-react';
|
|
11
8
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
import React4, { createContext, useContext, useState, useRef, useCallback, useEffect, useLayoutEffect, useMemo, useSyncExternalStore } from 'react';
|
|
12
10
|
import { createPortal } from 'react-dom';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { renderToStaticMarkup } from 'react-dom/server';
|
|
11
|
+
import { collectReviewItems, revisionItemsOf } from '@docx-editor.dev/core/store';
|
|
12
|
+
import { useDocxEditor, ReviewRailContext, useEditorSnapshot, useEditorState, useChromeTranslate, DocxEditor, useParagraphStyle, normalizeImageBytes, usePageSetup, useEditorCommand } from '@docx-editor.dev/react';
|
|
13
|
+
import { MessageSquarePlus, MessageSquare, PencilLine, ChevronLeft, ChevronRight, X, ChevronUp, ChevronDown, CheckCheck, RotateCcw, CircleCheck, Trash2, Check, SendHorizontal, Loader2, Sparkles, Type, Pilcrow, UsersRound, RefreshCw, WifiOff, Download, Save, Ellipsis, BookA, Blocks, Link2, Superscript, NotebookText, RefreshCcw, Columns2, Settings2, BetweenHorizontalStart, BetweenHorizontalEnd, BetweenVerticalStart, BetweenVerticalEnd, Merge, Split, Heading, AlignVerticalJustifyStart, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, RotateCw, FlipHorizontal2, FlipVertical2, Scaling, RectangleVertical, RectangleHorizontal, FileText, SeparatorHorizontal, Captions } from 'lucide-react';
|
|
17
14
|
import * as Y from 'yjs';
|
|
18
15
|
import { DOCUMENT_COLLABORATION_VERSIONS, collaborationModule } from '@arbidocs/coedit';
|
|
19
16
|
import { createHocuspocusCollaboration } from '@arbidocs/coedit/hocuspocus';
|
|
17
|
+
import { defaultLegalTemplate, listBuildingBlocks, attachedTemplateOf, linkedLevelStyles, styleDivergence, buildingBlockFragment, fragmentClipboardHtml, trackInsertion, newFromTemplate } from '@arbidocs/coedit/template';
|
|
18
|
+
import { resolveNumberingLevel, resolveStoryListItems, buildStyleCascadeTable, withNumberingStyleLinks, buildNumberingIndex } from '@docx-editor.dev/core/layout';
|
|
19
|
+
import { executeImageCommand } from '@docx-editor.dev/core/editor';
|
|
20
20
|
|
|
21
21
|
function normalizeDocxExport(data) {
|
|
22
22
|
const files = unzipSync(new Uint8Array(data));
|
|
@@ -82,6 +82,93 @@ function normalizeDocxExport(data) {
|
|
|
82
82
|
if (!changed) return data;
|
|
83
83
|
return zipSync(files).slice().buffer;
|
|
84
84
|
}
|
|
85
|
+
function wordDraftHtml(markdown) {
|
|
86
|
+
const template = document.createElement("template");
|
|
87
|
+
template.innerHTML = renderToStaticMarkup(/* @__PURE__ */ jsx(AiMarkdown, { children: markdown }));
|
|
88
|
+
template.content.querySelectorAll('button, svg, [data-testid="code-block-header"]').forEach((node) => node.remove());
|
|
89
|
+
template.content.querySelectorAll('[data-testid="markdown-code-block"]').forEach((node) => {
|
|
90
|
+
const pre = node.querySelector("pre");
|
|
91
|
+
if (pre) node.replaceWith(pre);
|
|
92
|
+
});
|
|
93
|
+
return template.innerHTML;
|
|
94
|
+
}
|
|
95
|
+
function requireEdit(result) {
|
|
96
|
+
if (!result.ok) throw new Error(result.reason);
|
|
97
|
+
}
|
|
98
|
+
var WordDraftStream = class {
|
|
99
|
+
constructor(editor) {
|
|
100
|
+
this.editor = editor;
|
|
101
|
+
}
|
|
102
|
+
editor;
|
|
103
|
+
committed = [];
|
|
104
|
+
pending;
|
|
105
|
+
resetAfterFailure = false;
|
|
106
|
+
update(content, complete) {
|
|
107
|
+
const blocks = splitMarkdownBlocks(content);
|
|
108
|
+
const completeCount = complete ? blocks.length : Math.max(0, blocks.length - 1);
|
|
109
|
+
requireEdit(this.editor.setEditingMode("editing"));
|
|
110
|
+
try {
|
|
111
|
+
if (this.resetAfterFailure || this.committed.some((block, index) => blocks[index] !== block)) {
|
|
112
|
+
requireEdit(this.editor.exec({ type: "selectAll" }));
|
|
113
|
+
requireEdit(this.editor.exec({ type: "deleteText" }));
|
|
114
|
+
this.committed = [];
|
|
115
|
+
this.pending = void 0;
|
|
116
|
+
this.resetAfterFailure = false;
|
|
117
|
+
}
|
|
118
|
+
for (let index = this.committed.length; index < blocks.length; index++) {
|
|
119
|
+
const tail = this.editor.query({ type: "paragraphs" }).at(-1);
|
|
120
|
+
const start = this.pending?.start || tail?.paraId;
|
|
121
|
+
if (!start) throw new Error("The draft insertion paragraph is unavailable.");
|
|
122
|
+
requireEdit(
|
|
123
|
+
this.editor.exec(
|
|
124
|
+
this.pending ? {
|
|
125
|
+
type: "setSelection",
|
|
126
|
+
range: { from: { paraId: start }, to: { paraId: this.pending.end } }
|
|
127
|
+
} : { type: "setSelection", anchor: { paraId: start } }
|
|
128
|
+
)
|
|
129
|
+
);
|
|
130
|
+
const html = wordDraftHtml(blocks[index]) + "<p></p>";
|
|
131
|
+
const element = document.createElement("div");
|
|
132
|
+
element.innerHTML = html;
|
|
133
|
+
const startIndex = this.editor.query({ type: "paragraphs" }).findIndex((p) => p.paraId === start);
|
|
134
|
+
requireEdit(this.editor.exec({ type: "paste", html, text: element.textContent || "" }));
|
|
135
|
+
const inserted = this.editor.query({ type: "paragraphs" }).slice(startIndex);
|
|
136
|
+
let headingIndex = 0;
|
|
137
|
+
for (const heading of Array.from(element.querySelectorAll("h1,h2,h3,h4,h5,h6"))) {
|
|
138
|
+
const match = inserted.findIndex(
|
|
139
|
+
(p, i) => i >= headingIndex && p.text === heading.textContent
|
|
140
|
+
);
|
|
141
|
+
if (match >= 0 && inserted[match].paraId) {
|
|
142
|
+
requireEdit(
|
|
143
|
+
this.editor.exec({
|
|
144
|
+
type: "setSelection",
|
|
145
|
+
anchor: { paraId: inserted[match].paraId }
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
requireEdit(
|
|
149
|
+
this.editor.exec({
|
|
150
|
+
type: "setParagraphStyle",
|
|
151
|
+
styleId: `Heading${heading.tagName.slice(1)}`
|
|
152
|
+
})
|
|
153
|
+
);
|
|
154
|
+
headingIndex = match + 1;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const end = this.editor.query({ type: "paragraphs" }).at(-1)?.paraId;
|
|
158
|
+
if (!end) throw new Error("The draft continuation paragraph is unavailable.");
|
|
159
|
+
if (index < completeCount) {
|
|
160
|
+
this.committed.push(blocks[index]);
|
|
161
|
+
this.pending = void 0;
|
|
162
|
+
} else this.pending = { start, end };
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
this.resetAfterFailure = true;
|
|
166
|
+
throw error;
|
|
167
|
+
} finally {
|
|
168
|
+
requireEdit(this.editor.setEditingMode("viewing"));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
85
172
|
function findNode(node, id) {
|
|
86
173
|
if (node.id === id) return node;
|
|
87
174
|
for (const child of node.children ?? []) {
|
|
@@ -105,6 +192,22 @@ function reviewModule() {
|
|
|
105
192
|
}
|
|
106
193
|
};
|
|
107
194
|
}
|
|
195
|
+
|
|
196
|
+
// src/docx/review/commentText.ts
|
|
197
|
+
var SKIP = /* @__PURE__ */ new Set(["delText", "instrText", "delInstrText", "pPr", "rPr"]);
|
|
198
|
+
function inline(node) {
|
|
199
|
+
if (node.kind === "textValue") return node.value ?? "";
|
|
200
|
+
if (node.localName && SKIP.has(node.localName)) return "";
|
|
201
|
+
if (node.localName === "br" || node.localName === "cr") return "\n";
|
|
202
|
+
if (node.localName === "tab") return " ";
|
|
203
|
+
return (node.children ?? []).map(inline).join("");
|
|
204
|
+
}
|
|
205
|
+
function commentText(entry) {
|
|
206
|
+
if (entry.kind !== "comment") return entry.text;
|
|
207
|
+
const blocks = entry.item?.comment?.blocks ?? [];
|
|
208
|
+
const text = blocks.map(inline).join("\n").trim();
|
|
209
|
+
return text || entry.text;
|
|
210
|
+
}
|
|
108
211
|
var NONE = [];
|
|
109
212
|
var ok = (result) => result?.ok === true;
|
|
110
213
|
function useReview() {
|
|
@@ -140,22 +243,6 @@ var ReviewItemContext = createContext(null);
|
|
|
140
243
|
function useReviewItem() {
|
|
141
244
|
return useContext(ReviewItemContext);
|
|
142
245
|
}
|
|
143
|
-
|
|
144
|
-
// src/docx/review/commentText.ts
|
|
145
|
-
var SKIP = /* @__PURE__ */ new Set(["delText", "instrText", "delInstrText", "pPr", "rPr"]);
|
|
146
|
-
function inline(node) {
|
|
147
|
-
if (node.kind === "textValue") return node.value ?? "";
|
|
148
|
-
if (node.localName && SKIP.has(node.localName)) return "";
|
|
149
|
-
if (node.localName === "br" || node.localName === "cr") return "\n";
|
|
150
|
-
if (node.localName === "tab") return " ";
|
|
151
|
-
return (node.children ?? []).map(inline).join("");
|
|
152
|
-
}
|
|
153
|
-
function commentText(entry) {
|
|
154
|
-
if (entry.kind !== "comment") return entry.text;
|
|
155
|
-
const blocks = entry.item?.comment?.blocks ?? [];
|
|
156
|
-
const text = blocks.map(inline).join("\n").trim();
|
|
157
|
-
return text || entry.text;
|
|
158
|
-
}
|
|
159
246
|
var CARD_GAP = 8;
|
|
160
247
|
var CLUSTER_GAP = 24;
|
|
161
248
|
var REVIEW_MARGIN_MIN_WIDTH = 900;
|
|
@@ -1793,6 +1880,8 @@ function structuralPort(editor) {
|
|
|
1793
1880
|
}
|
|
1794
1881
|
};
|
|
1795
1882
|
}
|
|
1883
|
+
|
|
1884
|
+
// src/docx/structural/useTrackedOrNoted.ts
|
|
1796
1885
|
var DocxAuthorContext = createContext("You");
|
|
1797
1886
|
function useTrackedOrNoted() {
|
|
1798
1887
|
const editor = useDocxEditor();
|
|
@@ -2176,1796 +2265,1529 @@ function DefinedTermMenu({
|
|
|
2176
2265
|
}
|
|
2177
2266
|
);
|
|
2178
2267
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
try {
|
|
2189
|
-
return await host.load();
|
|
2190
|
-
} catch {
|
|
2191
|
-
return builtinHouseTemplate();
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
|
-
var HouseTemplateContext = createContext(null);
|
|
2195
|
-
var useHouseTemplate = () => useContext(HouseTemplateContext);
|
|
2196
|
-
function HouseTemplateProvider({
|
|
2197
|
-
host,
|
|
2198
|
-
documentId,
|
|
2199
|
-
editable,
|
|
2200
|
-
notify,
|
|
2201
|
-
enabled = true,
|
|
2202
|
-
children
|
|
2268
|
+
function RibbonMenu({
|
|
2269
|
+
label,
|
|
2270
|
+
icon,
|
|
2271
|
+
children,
|
|
2272
|
+
className,
|
|
2273
|
+
testId,
|
|
2274
|
+
title,
|
|
2275
|
+
keepMounted = false,
|
|
2276
|
+
popoverClassName
|
|
2203
2277
|
}) {
|
|
2204
|
-
const
|
|
2205
|
-
const
|
|
2206
|
-
const [template, setTemplate] = useState(builtinHouseTemplate);
|
|
2207
|
-
const [divergence, setDivergence] = useState(null);
|
|
2208
|
-
const [basedOnTemplate, setBasedOnTemplate] = useState(false);
|
|
2209
|
-
const [levels, setLevels] = useState({});
|
|
2210
|
-
const [generation, setGeneration] = useState(0);
|
|
2278
|
+
const [open, setOpen] = useState(false);
|
|
2279
|
+
const element = useRef(null);
|
|
2211
2280
|
useEffect(() => {
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
live = false;
|
|
2281
|
+
if (!open) return;
|
|
2282
|
+
const dismiss = (event) => {
|
|
2283
|
+
if (!element.current?.contains(event.target)) setOpen(false);
|
|
2216
2284
|
};
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
return [];
|
|
2223
|
-
}
|
|
2224
|
-
}, [template]);
|
|
2225
|
-
useEffect(() => {
|
|
2226
|
-
if (!editor || !enabled) return;
|
|
2227
|
-
let live = true;
|
|
2228
|
-
void editor.save().then((saved) => {
|
|
2229
|
-
if (!live || !saved) return;
|
|
2230
|
-
const bytes = new Uint8Array(saved);
|
|
2231
|
-
setBasedOnTemplate(attachedTemplateOf(bytes) === templateFileName(template));
|
|
2232
|
-
setLevels(linkedLevelStyles(bytes));
|
|
2233
|
-
setDivergence(styleDivergence(bytes, template.bytes));
|
|
2234
|
-
}).catch(() => live && setDivergence(null));
|
|
2285
|
+
const escape = (event) => {
|
|
2286
|
+
if (event.key === "Escape") setOpen(false);
|
|
2287
|
+
};
|
|
2288
|
+
document.addEventListener("pointerdown", dismiss);
|
|
2289
|
+
document.addEventListener("keydown", escape);
|
|
2235
2290
|
return () => {
|
|
2236
|
-
|
|
2291
|
+
document.removeEventListener("pointerdown", dismiss);
|
|
2292
|
+
document.removeEventListener("keydown", escape);
|
|
2237
2293
|
};
|
|
2238
|
-
}, [
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
revision ? trackInsertion(fragment, revision, true) : fragment
|
|
2253
|
-
)
|
|
2254
|
-
}),
|
|
2255
|
-
inserted: (r) => r.ok
|
|
2256
|
-
})?.result;
|
|
2257
|
-
if (!result?.ok) throw new Error(result?.reason ?? "The block could not be inserted here.");
|
|
2258
|
-
refresh();
|
|
2259
|
-
return true;
|
|
2260
|
-
} catch (failure) {
|
|
2261
|
-
notify?.(
|
|
2262
|
-
failure instanceof Error ? failure.message : "Could not insert the block.",
|
|
2263
|
-
"error"
|
|
2264
|
-
);
|
|
2265
|
-
return false;
|
|
2294
|
+
}, [open]);
|
|
2295
|
+
return /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon-menu", ref: element, children: [
|
|
2296
|
+
/* @__PURE__ */ jsx(
|
|
2297
|
+
"button",
|
|
2298
|
+
{
|
|
2299
|
+
type: "button",
|
|
2300
|
+
className: ["native-docx-ribbon-trigger", className].filter(Boolean).join(" "),
|
|
2301
|
+
"aria-label": label,
|
|
2302
|
+
title: title ?? label,
|
|
2303
|
+
"aria-expanded": open,
|
|
2304
|
+
"data-testid": testId,
|
|
2305
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
2306
|
+
onClick: () => setOpen((value) => !value),
|
|
2307
|
+
children: icon
|
|
2266
2308
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
);
|
|
2309
|
+
),
|
|
2310
|
+
(open || keepMounted) && /* @__PURE__ */ jsx(
|
|
2311
|
+
"div",
|
|
2312
|
+
{
|
|
2313
|
+
className: ["native-docx-ribbon-popover", popoverClassName].filter(Boolean).join(" "),
|
|
2314
|
+
role: "group",
|
|
2315
|
+
"aria-label": label,
|
|
2316
|
+
hidden: !open,
|
|
2317
|
+
onMouseDown: (event) => {
|
|
2318
|
+
if (!(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement))
|
|
2319
|
+
event.preventDefault();
|
|
2320
|
+
},
|
|
2321
|
+
children: typeof children === "function" ? children(() => setOpen(false)) : children
|
|
2281
2322
|
}
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
);
|
|
2285
|
-
const value = useMemo(
|
|
2286
|
-
() => ({
|
|
2287
|
-
template,
|
|
2288
|
-
blocks,
|
|
2289
|
-
divergence,
|
|
2290
|
-
basedOnTemplate,
|
|
2291
|
-
levels,
|
|
2292
|
-
insertBlock,
|
|
2293
|
-
refresh,
|
|
2294
|
-
setAsHouseTemplate,
|
|
2295
|
-
editable
|
|
2296
|
-
}),
|
|
2297
|
-
[
|
|
2298
|
-
template,
|
|
2299
|
-
blocks,
|
|
2300
|
-
divergence,
|
|
2301
|
-
basedOnTemplate,
|
|
2302
|
-
levels,
|
|
2303
|
-
insertBlock,
|
|
2304
|
-
refresh,
|
|
2305
|
-
setAsHouseTemplate,
|
|
2306
|
-
editable
|
|
2307
|
-
]
|
|
2308
|
-
);
|
|
2309
|
-
return /* @__PURE__ */ jsx(HouseTemplateContext.Provider, { value: enabled ? value : null, children });
|
|
2323
|
+
)
|
|
2324
|
+
] });
|
|
2310
2325
|
}
|
|
2311
2326
|
|
|
2312
|
-
// src/docx/
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
if (
|
|
2317
|
-
|
|
2318
|
-
const
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
}
|
|
2324
|
-
return null;
|
|
2325
|
-
};
|
|
2326
|
-
const chain = [];
|
|
2327
|
-
const texts = [];
|
|
2328
|
-
for (let u = item.ilvl; u >= 0; u--) {
|
|
2329
|
-
const level = levelOf(item.numId, u);
|
|
2330
|
-
if (!level || !numbers(level)) continue;
|
|
2331
|
-
if (u !== item.ilvl && texts.some((t) => t.includes(`%${u + 1}`))) continue;
|
|
2332
|
-
chain.push(u);
|
|
2333
|
-
texts.push(level.lvlText);
|
|
2334
|
-
}
|
|
2335
|
-
const value = chain.reverse().map((u) => markerAt(u) ?? "").join("");
|
|
2336
|
-
if (!value) return null;
|
|
2337
|
-
return value.length > 1 && value.endsWith(".") ? value.slice(0, -1) : value;
|
|
2338
|
-
}
|
|
2339
|
-
function listTargets(paragraphs, items, levelOf) {
|
|
2340
|
-
const order = paragraphs.map((p) => p.id);
|
|
2341
|
-
const out = [];
|
|
2342
|
-
for (const p of paragraphs) {
|
|
2343
|
-
const text = p.text.replace(//g, "").trim();
|
|
2344
|
-
if (!text) continue;
|
|
2345
|
-
const number = paragraphNumber(order, items, levelOf, p.id);
|
|
2346
|
-
const item = items.get(p.id);
|
|
2347
|
-
if (number !== null)
|
|
2348
|
-
out.push({
|
|
2349
|
-
paragraphId: p.id,
|
|
2350
|
-
kind: "numbered",
|
|
2351
|
-
number,
|
|
2352
|
-
marker: item?.markerText ?? "",
|
|
2353
|
-
text,
|
|
2354
|
-
heading: p.heading,
|
|
2355
|
-
level: item?.ilvl ?? 0
|
|
2356
|
-
});
|
|
2357
|
-
else if (p.heading)
|
|
2358
|
-
out.push({
|
|
2359
|
-
paragraphId: p.id,
|
|
2360
|
-
kind: "heading",
|
|
2361
|
-
number: null,
|
|
2362
|
-
marker: "",
|
|
2363
|
-
text,
|
|
2364
|
-
heading: true,
|
|
2365
|
-
level: 0
|
|
2366
|
-
});
|
|
2327
|
+
// src/docx/overflowPlan.ts
|
|
2328
|
+
function planOverflow(items, available, moreWidth) {
|
|
2329
|
+
const collapsed = /* @__PURE__ */ new Set();
|
|
2330
|
+
let used = items.reduce((sum, item) => sum + item.width, 0);
|
|
2331
|
+
if (used <= available) return collapsed;
|
|
2332
|
+
used += moreWidth;
|
|
2333
|
+
const order = items.map((item, index) => ({ item, index })).sort((a, b) => a.item.priority - b.item.priority || b.index - a.index);
|
|
2334
|
+
for (const { item } of order) {
|
|
2335
|
+
if (used <= available) break;
|
|
2336
|
+
collapsed.add(item.id);
|
|
2337
|
+
used -= item.width;
|
|
2367
2338
|
}
|
|
2368
|
-
return
|
|
2339
|
+
return collapsed;
|
|
2369
2340
|
}
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2341
|
+
var Overflow = createContext(null);
|
|
2342
|
+
var MORE_WIDTH = 32;
|
|
2343
|
+
var sameSet = (a, b) => a.size === b.size && [...a].every((id) => b.has(id));
|
|
2344
|
+
function RibbonOverflow({ tab, children }) {
|
|
2345
|
+
const row = useRef(null);
|
|
2346
|
+
const items = useRef(/* @__PURE__ */ new Map());
|
|
2347
|
+
const widths = useRef(/* @__PURE__ */ new Map());
|
|
2348
|
+
const [version, setVersion] = useState(0);
|
|
2349
|
+
const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
|
|
2350
|
+
const [slots, setSlots] = useState({});
|
|
2351
|
+
const [menu, setMenu] = useState([]);
|
|
2352
|
+
const register = useCallback((id, item) => {
|
|
2353
|
+
items.current.set(id, item);
|
|
2354
|
+
setVersion((v) => v + 1);
|
|
2355
|
+
return () => {
|
|
2356
|
+
if (items.current.get(id) === item) items.current.delete(id);
|
|
2357
|
+
setVersion((v) => v + 1);
|
|
2358
|
+
};
|
|
2359
|
+
}, []);
|
|
2360
|
+
const plan = useCallback(() => {
|
|
2361
|
+
const element = row.current;
|
|
2362
|
+
if (!element) return;
|
|
2363
|
+
const gap = parseFloat(getComputedStyle(element).columnGap) || 0;
|
|
2364
|
+
const ordered = Array.from(element.querySelectorAll("[data-ribbon-item]")).map((node) => node.dataset.ribbonItem).filter((id) => items.current.has(id));
|
|
2365
|
+
const measured = ordered.map((id) => {
|
|
2366
|
+
const { element: node, priority } = items.current.get(id);
|
|
2367
|
+
if (!node.hidden) {
|
|
2368
|
+
const width = node.getBoundingClientRect().width;
|
|
2369
|
+
widths.current.set(id, width ? width + gap : 0);
|
|
2370
|
+
}
|
|
2371
|
+
return { id, priority, width: widths.current.get(id) ?? 0 };
|
|
2372
|
+
});
|
|
2373
|
+
const next = planOverflow(measured, element.clientWidth, MORE_WIDTH);
|
|
2374
|
+
setCollapsed((current) => sameSet(current, next) ? current : next);
|
|
2375
|
+
const entries = ordered.filter((id) => next.has(id)).map((id) => ({ id, label: items.current.get(id).label }));
|
|
2376
|
+
setMenu(
|
|
2377
|
+
(current) => current.length === entries.length && current.every((entry, i) => entry.id === entries[i].id && entry.label === entries[i].label) ? current : entries
|
|
2378
|
+
);
|
|
2379
|
+
}, []);
|
|
2380
|
+
useLayoutEffect(plan, [plan, version, tab, collapsed]);
|
|
2381
|
+
useLayoutEffect(() => {
|
|
2382
|
+
const element = row.current;
|
|
2383
|
+
if (!element) return;
|
|
2384
|
+
const observer = new ResizeObserver(() => plan());
|
|
2385
|
+
observer.observe(element);
|
|
2386
|
+
for (const item of items.current.values()) observer.observe(item.element);
|
|
2387
|
+
return () => observer.disconnect();
|
|
2388
|
+
}, [plan, version]);
|
|
2389
|
+
const slotRef = useCallback(
|
|
2390
|
+
(id) => (node) => setSlots((current) => {
|
|
2391
|
+
if (node ? current[id] === node : !(id in current)) return current;
|
|
2392
|
+
const next = { ...current };
|
|
2393
|
+
if (node) next[id] = node;
|
|
2394
|
+
else delete next[id];
|
|
2395
|
+
return next;
|
|
2396
|
+
}),
|
|
2397
|
+
[]
|
|
2375
2398
|
);
|
|
2399
|
+
const context = useMemo(() => ({ collapsed, slots, register }), [collapsed, slots, register]);
|
|
2400
|
+
const shown = menu.filter((entry) => collapsed.has(entry.id));
|
|
2401
|
+
return /* @__PURE__ */ jsx(Overflow.Provider, { value: context, children: /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon-row", ref: row, "data-testid": "docx-ribbon-row", children: [
|
|
2402
|
+
children,
|
|
2403
|
+
shown.length > 0 && /* @__PURE__ */ jsx(
|
|
2404
|
+
RibbonMenu,
|
|
2405
|
+
{
|
|
2406
|
+
label: "More",
|
|
2407
|
+
title: "More commands",
|
|
2408
|
+
icon: /* @__PURE__ */ jsx(Ellipsis, { size: 16 }),
|
|
2409
|
+
testId: "docx-ribbon-more",
|
|
2410
|
+
keepMounted: true,
|
|
2411
|
+
popoverClassName: "native-docx-ribbon-more",
|
|
2412
|
+
children: (close) => /* @__PURE__ */ jsx(MoreRows, { entries: shown, slotRef, close })
|
|
2413
|
+
},
|
|
2414
|
+
tab
|
|
2415
|
+
)
|
|
2416
|
+
] }) });
|
|
2376
2417
|
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
const
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2418
|
+
function MoreRows({
|
|
2419
|
+
entries,
|
|
2420
|
+
slotRef,
|
|
2421
|
+
close
|
|
2422
|
+
}) {
|
|
2423
|
+
const refs = useRef(/* @__PURE__ */ new Map());
|
|
2424
|
+
const refFor = (id) => {
|
|
2425
|
+
let ref = refs.current.get(id);
|
|
2426
|
+
if (!ref) refs.current.set(id, ref = slotRef(id));
|
|
2427
|
+
return ref;
|
|
2428
|
+
};
|
|
2429
|
+
const list = useRef(null);
|
|
2430
|
+
const latest = useRef(close);
|
|
2431
|
+
latest.current = close;
|
|
2432
|
+
useEffect(() => {
|
|
2433
|
+
const element = list.current;
|
|
2434
|
+
if (!element) return;
|
|
2435
|
+
const onClick = (event) => {
|
|
2436
|
+
const target = event.target;
|
|
2437
|
+
const row = target.closest(".native-docx-ribbon-more-row");
|
|
2438
|
+
if (!row) return;
|
|
2439
|
+
const control = row.querySelector(".native-docx-ribbon-more-control");
|
|
2440
|
+
if (!control.contains(target)) {
|
|
2441
|
+
control.querySelector("button:not([disabled])")?.click();
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2444
|
+
const button = target.closest("button");
|
|
2445
|
+
if (button && !button.disabled && !control.querySelector(".native-docx-ribbon-popover")?.contains(button) && !button.hasAttribute("aria-expanded") && !button.hasAttribute("aria-haspopup"))
|
|
2446
|
+
latest.current();
|
|
2447
|
+
};
|
|
2448
|
+
element.addEventListener("click", onClick, true);
|
|
2449
|
+
return () => element.removeEventListener("click", onClick, true);
|
|
2450
|
+
}, []);
|
|
2451
|
+
return /* @__PURE__ */ jsx("div", { className: "native-docx-ribbon-more-list", ref: list, children: entries.map((entry) => /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon-more-row", "data-ribbon-more": entry.id, children: [
|
|
2452
|
+
/* @__PURE__ */ jsx("span", { className: "native-docx-ribbon-more-control", ref: refFor(entry.id) }),
|
|
2453
|
+
/* @__PURE__ */ jsx("span", { className: "native-docx-ribbon-more-label", children: entry.label })
|
|
2454
|
+
] }, entry.id)) });
|
|
2395
2455
|
}
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
}
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
if (
|
|
2410
|
-
const
|
|
2411
|
-
const
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
if (!runs) throw new Error("This heading cannot be referred to by its text; use its number.");
|
|
2421
|
-
port.deleteRange(
|
|
2422
|
-
{ paragraphId: paragraph.id, offset: 0 },
|
|
2423
|
-
{ paragraphId: paragraph.id, offset: paragraph.text.length }
|
|
2424
|
-
);
|
|
2425
|
-
if (!port.paste(`<w:p>${start}${runs}${end}</w:p>`, paragraph.text, false))
|
|
2426
|
-
throw new Error("The cross-reference target could not be marked.");
|
|
2427
|
-
return name;
|
|
2428
|
-
}
|
|
2429
|
-
function pastePoint(port, caret) {
|
|
2430
|
-
const paragraph = port.paragraphs().find((p) => p.id === caret.paragraphId);
|
|
2431
|
-
if (paragraph?.text[caret.offset - 1] !== FIELD_ATOM) return caret;
|
|
2432
|
-
port.setCaret(caret);
|
|
2433
|
-
port.typeText(" ");
|
|
2434
|
-
return { paragraphId: caret.paragraphId, offset: caret.offset + 1 };
|
|
2435
|
-
}
|
|
2436
|
-
function markCrossReferenceTarget(port, target, mode) {
|
|
2437
|
-
if (mode === "number" && !target.number) mode = "text";
|
|
2438
|
-
const bookmark = ensureTargetBookmark(port, target, mode);
|
|
2439
|
-
return { bookmark, mode, value: refResult(target, mode) };
|
|
2440
|
-
}
|
|
2441
|
-
function insertCrossReferenceField(port, { bookmark, mode, value }, caret) {
|
|
2442
|
-
caret = pastePoint(port, caret);
|
|
2443
|
-
port.setCaret(caret);
|
|
2444
|
-
if (!port.paste(`<w:p>${crossReferenceXml(bookmark, mode, value)}</w:p>`, value, false))
|
|
2445
|
-
throw new Error("The cross-reference could not be inserted.");
|
|
2446
|
-
port.setCaret({ paragraphId: caret.paragraphId, offset: caret.offset + 1 });
|
|
2447
|
-
return { bookmark };
|
|
2448
|
-
}
|
|
2449
|
-
function insertCrossReference(port, target, mode, caret) {
|
|
2450
|
-
return insertCrossReferenceField(port, markCrossReferenceTarget(port, target, mode), caret);
|
|
2451
|
-
}
|
|
2452
|
-
var documentReferenceTooltip = (text, appName = "ARBI") => `Cross-reference: ${text} \u2014 opens in ${appName}`;
|
|
2453
|
-
function insertDocumentReference(port, reference, caret) {
|
|
2454
|
-
const text = reference.text.trim();
|
|
2455
|
-
if (!text) throw new Error("Enter the reference text.");
|
|
2456
|
-
const tooltip = documentReferenceTooltip(text, reference.appName).slice(0, 255);
|
|
2457
|
-
caret = pastePoint(port, caret);
|
|
2458
|
-
port.setCaret(caret);
|
|
2459
|
-
const link = `<w:p><w:hyperlink r:id="rIdRef1"${tooltip ? ` w:tooltip="${esc(tooltip)}"` : ""} w:history="1">${run(text, CROSS_REFERENCE_STYLE)}</w:hyperlink></w:p>`;
|
|
2460
|
-
if (!port.paste(link, text, false, hyperlinkRelationship("rIdRef1", reference.url)))
|
|
2461
|
-
throw new Error("The reference could not be inserted.");
|
|
2462
|
-
port.setCaret({ paragraphId: caret.paragraphId, offset: caret.offset + text.length });
|
|
2463
|
-
}
|
|
2464
|
-
var REF_FIELD = /^REF\s+(\S+)((?:\s+\\[A-Za-z*]+(?:\s+MERGEFORMAT)?)*)\s*$/i;
|
|
2465
|
-
function staleCrossReferences(port) {
|
|
2466
|
-
const paragraphs = port.paragraphs();
|
|
2467
|
-
if (!paragraphs.some((p) => p.fields?.length)) return [];
|
|
2468
|
-
const items = port.listItems();
|
|
2469
|
-
const order = paragraphs.map((p) => p.id);
|
|
2470
|
-
const owner = /* @__PURE__ */ new Map();
|
|
2471
|
-
for (const p of paragraphs) for (const b of p.bookmarks) if (!owner.has(b)) owner.set(b, p.id);
|
|
2472
|
-
const out = [];
|
|
2473
|
-
for (const p of paragraphs)
|
|
2474
|
-
for (const field of p.fields ?? []) {
|
|
2475
|
-
const match = REF_FIELD.exec(field.instruction);
|
|
2476
|
-
if (!match || !match[1].startsWith(REF_BOOKMARK_PREFIX)) continue;
|
|
2477
|
-
const switches = match[2].toLowerCase();
|
|
2478
|
-
const mode = /\\[rwn]\b/.test(switches) ? "number" : "text";
|
|
2479
|
-
const target = owner.get(match[1]);
|
|
2480
|
-
const base = {
|
|
2481
|
-
paragraphId: p.id,
|
|
2482
|
-
offset: field.offset,
|
|
2483
|
-
instruction: field.instruction,
|
|
2484
|
-
cached: field.result,
|
|
2485
|
-
bookmark: match[1],
|
|
2486
|
-
mode
|
|
2487
|
-
};
|
|
2488
|
-
if (!target) {
|
|
2489
|
-
out.push({ ...base, value: null });
|
|
2490
|
-
continue;
|
|
2491
|
-
}
|
|
2492
|
-
const value = /\\[rwn]\b/.test(switches) ? switches.includes("\\r") ? paragraphNumber(order, items, port.levelOf, target) : null : (paragraphs.find((x) => x.id === target)?.text ?? "").replace(//g, "").trim();
|
|
2493
|
-
if (value && value !== field.result.trim()) out.push({ ...base, value });
|
|
2456
|
+
function RibbonItem({
|
|
2457
|
+
id,
|
|
2458
|
+
label,
|
|
2459
|
+
priority = 0,
|
|
2460
|
+
children
|
|
2461
|
+
}) {
|
|
2462
|
+
const context = useContext(Overflow);
|
|
2463
|
+
const element = useRef(null);
|
|
2464
|
+
const register = context?.register;
|
|
2465
|
+
useLayoutEffect(() => {
|
|
2466
|
+
if (!register || !element.current) return;
|
|
2467
|
+
return register(id, { label, priority, element: element.current });
|
|
2468
|
+
}, [register, id, label, priority]);
|
|
2469
|
+
if (!context) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
2470
|
+
const collapsed = context.collapsed.has(id);
|
|
2471
|
+
const slot = context.slots[id];
|
|
2472
|
+
return /* @__PURE__ */ jsx(
|
|
2473
|
+
"span",
|
|
2474
|
+
{
|
|
2475
|
+
ref: element,
|
|
2476
|
+
className: "native-docx-ribbon-item",
|
|
2477
|
+
"data-ribbon-item": id,
|
|
2478
|
+
hidden: collapsed || void 0,
|
|
2479
|
+
children: collapsed ? slot ? createPortal(children, slot) : null : children
|
|
2494
2480
|
}
|
|
2495
|
-
return out;
|
|
2496
|
-
}
|
|
2497
|
-
function brokenCrossReferences(port) {
|
|
2498
|
-
return port.paragraphs().flatMap(
|
|
2499
|
-
(p) => (p.marks ?? []).filter((m) => m.styleId === BROKEN_CROSS_REFERENCE_STYLE).map((m) => ({ paragraphId: p.id, offset: m.offset, text: m.text }))
|
|
2500
|
-
);
|
|
2501
|
-
}
|
|
2502
|
-
function refreshCrossReferences(port) {
|
|
2503
|
-
const stale = staleCrossReferences(port).sort(
|
|
2504
|
-
(a, b) => a.paragraphId === b.paragraphId ? b.offset - a.offset : 0
|
|
2505
2481
|
);
|
|
2506
|
-
let refreshed = 0;
|
|
2507
|
-
for (const ref of stale) {
|
|
2508
|
-
const paragraph = port.paragraphs().find((p) => p.id === ref.paragraphId);
|
|
2509
|
-
if (!paragraph || paragraph.text[ref.offset - 1] === FIELD_ATOM) continue;
|
|
2510
|
-
const at = { paragraphId: ref.paragraphId, offset: ref.offset };
|
|
2511
|
-
port.deleteRange(at, { paragraphId: ref.paragraphId, offset: ref.offset + 1 });
|
|
2512
|
-
port.setCaret(at);
|
|
2513
|
-
const text = ref.value ?? (ref.cached.trim() || ref.bookmark);
|
|
2514
|
-
const body = ref.value === null ? run(text, BROKEN_CROSS_REFERENCE_STYLE) : crossReferenceXml(ref.bookmark, ref.mode, ref.value);
|
|
2515
|
-
if (port.paste(`<w:p>${body}</w:p>`, text, false) && ref.value !== null) refreshed++;
|
|
2516
|
-
}
|
|
2517
|
-
return { refreshed, broken: brokenCrossReferences(port) };
|
|
2518
2482
|
}
|
|
2519
2483
|
|
|
2520
|
-
// src/docx/
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
const
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
function passageClause(text) {
|
|
2527
|
-
const first = text.trim().split("\n")[0] ?? "";
|
|
2528
|
-
const match = /^(?:#+\s*)?(?:\*\*)?(?:clause|section|article)?\s*(\d+(?:\.\d+)*)[.)]?(?:\*\*)?\s+\S/i.exec(
|
|
2529
|
-
first
|
|
2530
|
-
);
|
|
2531
|
-
return match ? match[1] : null;
|
|
2532
|
-
}
|
|
2533
|
-
function referenceText(title, passage) {
|
|
2534
|
-
const name = shortTitle(title);
|
|
2535
|
-
const clause = passageClause(passage.text);
|
|
2536
|
-
if (clause) return `${name}, clause ${clause}`;
|
|
2537
|
-
if (passage.page) return `${name}, p. ${passage.page}`;
|
|
2538
|
-
return name;
|
|
2484
|
+
// src/docx/wordStyles.ts
|
|
2485
|
+
var QUICK_STYLES = ["Normal", "Title", "heading 1", "heading 2", "heading 3"];
|
|
2486
|
+
function findStyle(options, name) {
|
|
2487
|
+
const wanted = name.toLowerCase();
|
|
2488
|
+
const id = name.replace(/(^|\s)(\w)/g, (_, _s, c) => c.toUpperCase());
|
|
2489
|
+
return options.find((o) => o.name.toLowerCase() === wanted) ?? options.find((o) => o.styleId.toLowerCase() === id.toLowerCase());
|
|
2539
2490
|
}
|
|
2540
|
-
function
|
|
2541
|
-
|
|
2542
|
-
return plain.length > max ? `${plain.slice(0, max - 1)}\u2026` : plain;
|
|
2491
|
+
function styleLabel(name) {
|
|
2492
|
+
return name === name.toLowerCase() ? name.replace(/(^|\s)(\w)/g, (m) => m.toUpperCase()) : name;
|
|
2543
2493
|
}
|
|
2544
|
-
function
|
|
2545
|
-
const
|
|
2546
|
-
|
|
2494
|
+
function wordShortcut(event) {
|
|
2495
|
+
const mod = event.ctrlKey || event.metaKey;
|
|
2496
|
+
if (!mod) return null;
|
|
2497
|
+
if (event.altKey && !event.shiftKey) {
|
|
2498
|
+
switch (event.code) {
|
|
2499
|
+
case "KeyF":
|
|
2500
|
+
return "footnote";
|
|
2501
|
+
case "KeyD":
|
|
2502
|
+
return "endnote";
|
|
2503
|
+
case "Digit1":
|
|
2504
|
+
case "Numpad1":
|
|
2505
|
+
return "heading 1";
|
|
2506
|
+
case "Digit2":
|
|
2507
|
+
case "Numpad2":
|
|
2508
|
+
return "heading 2";
|
|
2509
|
+
case "Digit3":
|
|
2510
|
+
case "Numpad3":
|
|
2511
|
+
return "heading 3";
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
if (event.shiftKey && !event.altKey && event.code === "KeyN") return "Normal";
|
|
2515
|
+
return null;
|
|
2547
2516
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
const
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2517
|
+
function divergenceSummary(divergence) {
|
|
2518
|
+
if (!divergence) return null;
|
|
2519
|
+
const changed = divergence.styles.filter((s) => s.kind === "changed").map((s) => styleLabel(s.name));
|
|
2520
|
+
const missing = divergence.styles.filter((s) => s.kind === "missing").map((s) => styleLabel(s.name));
|
|
2521
|
+
const parts = [
|
|
2522
|
+
divergence.docDefaults ? "default text formatting" : "",
|
|
2523
|
+
changed.length ? `${changed.length === 1 ? "style" : "styles"} ${changed.slice(0, 6).join(", ")}${changed.length > 6 ? "\u2026" : ""}` : "",
|
|
2524
|
+
missing.length ? `${missing.length} house ${missing.length === 1 ? "style" : "styles"} missing` : ""
|
|
2525
|
+
].filter(Boolean);
|
|
2526
|
+
return parts.length ? `Differs from the house template: ${parts.join("; ")}.` : null;
|
|
2556
2527
|
}
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
runs.push(serialize(child));
|
|
2564
|
-
}
|
|
2565
|
-
return runs.length ? runs.join("") : null;
|
|
2528
|
+
var BUILTIN_TEMPLATE_NAME = "ARBI legal template";
|
|
2529
|
+
var templateFileName = (t) => /\.(dotx|dotm|docx|docm)$/i.test(t.name) ? t.name : `${t.name}.dotx`;
|
|
2530
|
+
var builtin;
|
|
2531
|
+
function builtinHouseTemplate() {
|
|
2532
|
+
builtin ??= { bytes: defaultLegalTemplate(), name: BUILTIN_TEMPLATE_NAME, source: "builtin" };
|
|
2533
|
+
return builtin;
|
|
2566
2534
|
}
|
|
2567
|
-
function
|
|
2568
|
-
if (
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2535
|
+
async function resolveHouseTemplate(host) {
|
|
2536
|
+
if (!host) return builtinHouseTemplate();
|
|
2537
|
+
try {
|
|
2538
|
+
return await host.load();
|
|
2539
|
+
} catch {
|
|
2540
|
+
return builtinHouseTemplate();
|
|
2572
2541
|
}
|
|
2573
|
-
return void 0;
|
|
2574
2542
|
}
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
if (!s) throw new Error("The document is not open.");
|
|
2580
|
-
return s;
|
|
2581
|
-
};
|
|
2582
|
-
const root = () => session().part().root;
|
|
2583
|
-
let numbering;
|
|
2584
|
-
let cascade;
|
|
2585
|
-
const index = () => {
|
|
2586
|
-
if (!numbering) {
|
|
2587
|
-
const s = session();
|
|
2588
|
-
cascade = buildStyleCascadeTable(s.stylesRoot(), void 0, s.settingsRoot());
|
|
2589
|
-
numbering = withNumberingStyleLinks(buildNumberingIndex(s.numberingRoot()), cascade);
|
|
2590
|
-
}
|
|
2591
|
-
return numbering;
|
|
2592
|
-
};
|
|
2593
|
-
return {
|
|
2594
|
-
...base,
|
|
2595
|
-
listItems: () => {
|
|
2596
|
-
const body = root().children?.find((n) => n.kind === "body");
|
|
2597
|
-
const items = resolveStoryListItems(
|
|
2598
|
-
body?.children ?? [],
|
|
2599
|
-
index(),
|
|
2600
|
-
cascade
|
|
2601
|
-
);
|
|
2602
|
-
const out = /* @__PURE__ */ new Map();
|
|
2603
|
-
for (const [id, item] of items)
|
|
2604
|
-
out.set(id, {
|
|
2605
|
-
numId: item.numId,
|
|
2606
|
-
ilvl: item.ilvl,
|
|
2607
|
-
markerText: item.markerText,
|
|
2608
|
-
numFmt: item.numFmt
|
|
2609
|
-
});
|
|
2610
|
-
return out;
|
|
2611
|
-
},
|
|
2612
|
-
levelOf: (numId, ilvl) => {
|
|
2613
|
-
const level = resolveNumberingLevel(index(), numId, ilvl)?.level;
|
|
2614
|
-
return level ? { lvlText: level.lvlText, numFmt: level.numFmt } : null;
|
|
2615
|
-
},
|
|
2616
|
-
runsXml: (paragraphId) => {
|
|
2617
|
-
const node = findNode2(root(), paragraphId);
|
|
2618
|
-
return node ? paragraphRunsXml(node) : null;
|
|
2619
|
-
}
|
|
2620
|
-
};
|
|
2621
|
-
}
|
|
2622
|
-
|
|
2623
|
-
// src/docx/beforeSave.ts
|
|
2624
|
-
var hooks = /* @__PURE__ */ new WeakMap();
|
|
2625
|
-
function onBeforeSave(editor, fn) {
|
|
2626
|
-
let set = hooks.get(editor);
|
|
2627
|
-
if (!set) hooks.set(editor, set = /* @__PURE__ */ new Set());
|
|
2628
|
-
set.add(fn);
|
|
2629
|
-
return () => set.delete(fn);
|
|
2630
|
-
}
|
|
2631
|
-
function runBeforeSave(editor) {
|
|
2632
|
-
if (!editor) return;
|
|
2633
|
-
for (const fn of hooks.get(editor) ?? [])
|
|
2634
|
-
try {
|
|
2635
|
-
fn();
|
|
2636
|
-
} catch {
|
|
2637
|
-
}
|
|
2638
|
-
}
|
|
2639
|
-
var DEFAULT_CROSS_REFERENCES_LABELS = {
|
|
2640
|
-
insert: "Insert cross-reference",
|
|
2641
|
-
title: "Insert cross-reference",
|
|
2642
|
-
description: "Refer to a clause of this document, or to a passage of another document.",
|
|
2643
|
-
thisDocument: "This document",
|
|
2644
|
-
otherDocument: "Another document",
|
|
2645
|
-
filterTargets: "Find a clause or heading",
|
|
2646
|
-
noTargets: "This document has no numbered clauses or headings yet.",
|
|
2647
|
-
number: "Paragraph number",
|
|
2648
|
-
text: "Heading text",
|
|
2649
|
-
insertAs: "Insert",
|
|
2650
|
-
chooseDocument: "Choose a document",
|
|
2651
|
-
filterPassages: "Find a passage",
|
|
2652
|
-
loadingPassages: "Loading passages\u2026",
|
|
2653
|
-
noPassages: "No passage matches.",
|
|
2654
|
-
referenceText: "Reference text",
|
|
2655
|
-
insertButton: "Insert",
|
|
2656
|
-
cancel: "Cancel",
|
|
2657
|
-
clause: (number) => `Clause ${number}`,
|
|
2658
|
-
heading: "Heading",
|
|
2659
|
-
update: "Update cross-references",
|
|
2660
|
-
updated: "Cross-references updated",
|
|
2661
|
-
updateSummary: (refreshed) => refreshed ? `${refreshed} cross-reference${refreshed === 1 ? "" : "s"} brought up to date.` : "Every cross-reference is up to date.",
|
|
2662
|
-
brokenTitle: "Broken cross-references",
|
|
2663
|
-
brokenNone: "No cross-reference points at a deleted target.",
|
|
2664
|
-
brokenHelp: "Their target was deleted: the last text is kept and marked. Insert the reference again to repair it.",
|
|
2665
|
-
brokenCard: "Broken cross-reference",
|
|
2666
|
-
brokenCardBody: "Its target was deleted. Update cross-references to mark it.",
|
|
2667
|
-
show: "Show",
|
|
2668
|
-
close: "Close"
|
|
2669
|
-
};
|
|
2670
|
-
var CrossReferencesContext = createContext(null);
|
|
2671
|
-
var useCrossReferences = () => useContext(CrossReferencesContext);
|
|
2672
|
-
function CrossReferencesProvider({
|
|
2673
|
-
children,
|
|
2674
|
-
editable,
|
|
2675
|
-
references,
|
|
2543
|
+
var HouseTemplateContext = createContext(null);
|
|
2544
|
+
var useHouseTemplate = () => useContext(HouseTemplateContext);
|
|
2545
|
+
function HouseTemplateProvider({
|
|
2546
|
+
host,
|
|
2676
2547
|
documentId,
|
|
2548
|
+
editable,
|
|
2677
2549
|
notify,
|
|
2678
|
-
|
|
2550
|
+
enabled = true,
|
|
2551
|
+
children
|
|
2679
2552
|
}) {
|
|
2680
2553
|
const editor = useDocxEditor();
|
|
2681
2554
|
const track = useTrackedOrNoted();
|
|
2682
|
-
const
|
|
2683
|
-
const [
|
|
2684
|
-
|
|
2685
|
-
);
|
|
2686
|
-
const
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2555
|
+
const [template, setTemplate] = useState(builtinHouseTemplate);
|
|
2556
|
+
const [divergence, setDivergence] = useState(null);
|
|
2557
|
+
const [basedOnTemplate, setBasedOnTemplate] = useState(false);
|
|
2558
|
+
const [levels, setLevels] = useState({});
|
|
2559
|
+
const [generation, setGeneration] = useState(0);
|
|
2560
|
+
useEffect(() => {
|
|
2561
|
+
let live = true;
|
|
2562
|
+
void resolveHouseTemplate(host).then((t) => live && setTemplate(t));
|
|
2563
|
+
return () => {
|
|
2564
|
+
live = false;
|
|
2565
|
+
};
|
|
2566
|
+
}, [host]);
|
|
2567
|
+
const blocks = useMemo(() => {
|
|
2691
2568
|
try {
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
setDialog({ caret, targets: listTargets(port.paragraphs(), port.listItems(), port.levelOf) });
|
|
2696
|
-
} catch (error) {
|
|
2697
|
-
notify?.(error instanceof Error ? error.message : "Could not read the document.", "error");
|
|
2569
|
+
return listBuildingBlocks(template.bytes);
|
|
2570
|
+
} catch {
|
|
2571
|
+
return [];
|
|
2698
2572
|
}
|
|
2699
|
-
}, [
|
|
2700
|
-
|
|
2701
|
-
(
|
|
2702
|
-
|
|
2573
|
+
}, [template]);
|
|
2574
|
+
useEffect(() => {
|
|
2575
|
+
if (!editor || !enabled) return;
|
|
2576
|
+
let live = true;
|
|
2577
|
+
void editor.save().then((saved) => {
|
|
2578
|
+
if (!live || !saved) return;
|
|
2579
|
+
const bytes = new Uint8Array(saved);
|
|
2580
|
+
setBasedOnTemplate(attachedTemplateOf(bytes) === templateFileName(template));
|
|
2581
|
+
setLevels(linkedLevelStyles(bytes));
|
|
2582
|
+
setDivergence(styleDivergence(bytes, template.bytes));
|
|
2583
|
+
}).catch(() => live && setDivergence(null));
|
|
2584
|
+
return () => {
|
|
2585
|
+
live = false;
|
|
2586
|
+
};
|
|
2587
|
+
}, [editor, template, generation, enabled]);
|
|
2588
|
+
const refresh = useCallback(() => setGeneration((g) => g + 1), []);
|
|
2589
|
+
const insertBlock = useCallback(
|
|
2590
|
+
(name) => {
|
|
2591
|
+
if (!editor || !editable) return false;
|
|
2592
|
+
const fragment = buildingBlockFragment(template.bytes, name);
|
|
2593
|
+
if (!fragment) return false;
|
|
2703
2594
|
try {
|
|
2704
|
-
const
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2595
|
+
const result = track({
|
|
2596
|
+
label: `Inserted Quick Part: ${name}`,
|
|
2597
|
+
insert: (revision) => editor.exec({
|
|
2598
|
+
type: "paste",
|
|
2599
|
+
text: "",
|
|
2600
|
+
html: fragmentClipboardHtml(
|
|
2601
|
+
revision ? trackInsertion(fragment, revision, true) : fragment
|
|
2602
|
+
)
|
|
2603
|
+
}),
|
|
2604
|
+
inserted: (r) => r.ok
|
|
2605
|
+
})?.result;
|
|
2606
|
+
if (!result?.ok) throw new Error(result?.reason ?? "The block could not be inserted here.");
|
|
2607
|
+
refresh();
|
|
2608
|
+
return true;
|
|
2609
|
+
} catch (failure) {
|
|
2711
2610
|
notify?.(
|
|
2712
|
-
|
|
2611
|
+
failure instanceof Error ? failure.message : "Could not insert the block.",
|
|
2713
2612
|
"error"
|
|
2714
2613
|
);
|
|
2614
|
+
return false;
|
|
2715
2615
|
}
|
|
2716
2616
|
},
|
|
2717
|
-
[editor,
|
|
2617
|
+
[editor, editable, template, notify, refresh, track]
|
|
2718
2618
|
);
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
const refresh = () => {
|
|
2619
|
+
const setAsHouseTemplate = useMemo(
|
|
2620
|
+
() => host?.setFromDocument && documentId ? async () => {
|
|
2722
2621
|
try {
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
} catch {
|
|
2622
|
+
await host.setFromDocument(documentId);
|
|
2623
|
+
setTemplate(await resolveHouseTemplate(host));
|
|
2624
|
+
notify?.("This document is now the house template for new documents.", "success");
|
|
2625
|
+
} catch (failure) {
|
|
2626
|
+
notify?.(
|
|
2627
|
+
failure instanceof Error ? failure.message : "Could not set the house template.",
|
|
2628
|
+
"error"
|
|
2629
|
+
);
|
|
2727
2630
|
}
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
const timer = setTimeout(refresh, 1500);
|
|
2731
|
-
return () => {
|
|
2732
|
-
off();
|
|
2733
|
-
clearTimeout(timer);
|
|
2734
|
-
};
|
|
2735
|
-
}, [editor, editable]);
|
|
2736
|
-
const [report, setReport] = useState(null);
|
|
2737
|
-
const update = useCallback(() => {
|
|
2738
|
-
if (!editor || !editable) return;
|
|
2739
|
-
try {
|
|
2740
|
-
const result = preservingCaret(
|
|
2741
|
-
editor,
|
|
2742
|
-
() => withDirectEditing(editor, () => refreshCrossReferences(referencesPort(editor)))
|
|
2743
|
-
);
|
|
2744
|
-
setReport(result);
|
|
2745
|
-
} catch (error) {
|
|
2746
|
-
notify?.(
|
|
2747
|
-
error instanceof Error ? error.message : "Could not update cross-references.",
|
|
2748
|
-
"error"
|
|
2749
|
-
);
|
|
2750
|
-
}
|
|
2751
|
-
}, [editor, editable, notify]);
|
|
2752
|
-
const [passages, setPassages] = useState({});
|
|
2753
|
-
const loadPassages = useCallback(
|
|
2754
|
-
(id) => {
|
|
2755
|
-
if (!references || passages[id]) return;
|
|
2756
|
-
setPassages((p) => ({ ...p, [id]: "loading" }));
|
|
2757
|
-
references.passages(id).then((list) => setPassages((p) => ({ ...p, [id]: list }))).catch(() => setPassages((p) => ({ ...p, [id]: [] })));
|
|
2758
|
-
},
|
|
2759
|
-
[references, passages]
|
|
2631
|
+
} : void 0,
|
|
2632
|
+
[host, documentId, notify]
|
|
2760
2633
|
);
|
|
2761
|
-
useEffect(() => {
|
|
2762
|
-
if (!viewer || !references) return;
|
|
2763
|
-
const targetOf = (event) => {
|
|
2764
|
-
const link = event.target?.closest?.(
|
|
2765
|
-
'a.docx-hyperlink[data-docx-link-kind="external"]'
|
|
2766
|
-
);
|
|
2767
|
-
if (!link || !viewer.contains(link)) return null;
|
|
2768
|
-
const mouse = event;
|
|
2769
|
-
if (mouse.button !== 0 || mouse.ctrlKey || mouse.metaKey || mouse.shiftKey) return null;
|
|
2770
|
-
return references.targetOf(link.getAttribute("href") ?? "");
|
|
2771
|
-
};
|
|
2772
|
-
const take = (event) => {
|
|
2773
|
-
const target = targetOf(event);
|
|
2774
|
-
if (!target) return;
|
|
2775
|
-
event.preventDefault();
|
|
2776
|
-
event.stopPropagation();
|
|
2777
|
-
if (event.type === "click") references.open(target);
|
|
2778
|
-
};
|
|
2779
|
-
const types = ["pointerdown", "mousedown", "pointerup", "mouseup", "click"];
|
|
2780
|
-
for (const type of types) window.addEventListener(type, take, true);
|
|
2781
|
-
return () => {
|
|
2782
|
-
for (const type of types) window.removeEventListener(type, take, true);
|
|
2783
|
-
};
|
|
2784
|
-
}, [viewer, references]);
|
|
2785
2634
|
const value = useMemo(
|
|
2786
|
-
() =>
|
|
2787
|
-
|
|
2635
|
+
() => ({
|
|
2636
|
+
template,
|
|
2637
|
+
blocks,
|
|
2638
|
+
divergence,
|
|
2639
|
+
basedOnTemplate,
|
|
2640
|
+
levels,
|
|
2641
|
+
insertBlock,
|
|
2642
|
+
refresh,
|
|
2643
|
+
setAsHouseTemplate,
|
|
2644
|
+
editable
|
|
2645
|
+
}),
|
|
2646
|
+
[
|
|
2647
|
+
template,
|
|
2648
|
+
blocks,
|
|
2649
|
+
divergence,
|
|
2650
|
+
basedOnTemplate,
|
|
2651
|
+
levels,
|
|
2652
|
+
insertBlock,
|
|
2653
|
+
refresh,
|
|
2654
|
+
setAsHouseTemplate,
|
|
2655
|
+
editable
|
|
2656
|
+
]
|
|
2788
2657
|
);
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
target.id
|
|
2802
|
-
);
|
|
2803
|
-
return {
|
|
2804
|
-
title: number ? l.clause(number) : l.heading,
|
|
2805
|
-
body: passageExcerpt(target.text.replace(//g, ""))
|
|
2806
|
-
};
|
|
2807
|
-
} catch {
|
|
2808
|
-
return null;
|
|
2809
|
-
}
|
|
2658
|
+
return /* @__PURE__ */ jsx(HouseTemplateContext.Provider, { value: enabled ? value : null, children });
|
|
2659
|
+
}
|
|
2660
|
+
var FILTER_FROM = 8;
|
|
2661
|
+
function previewStyle(option) {
|
|
2662
|
+
const { fontFamily, fontSizePt, bold, italic, color } = option.preview;
|
|
2663
|
+
return {
|
|
2664
|
+
fontFamily: fontFamily ? `"${fontFamily}", serif` : void 0,
|
|
2665
|
+
// In the face of the style, but within a row: 9-18pt.
|
|
2666
|
+
fontSize: fontSizePt ? `${Math.min(18, Math.max(9, fontSizePt))}pt` : void 0,
|
|
2667
|
+
fontWeight: bold ? 700 : void 0,
|
|
2668
|
+
fontStyle: italic ? "italic" : void 0,
|
|
2669
|
+
color: color ? `#${color}` : void 0
|
|
2810
2670
|
};
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2671
|
+
}
|
|
2672
|
+
function Gallery({ close }) {
|
|
2673
|
+
const { value, setValue, options, isEnabled } = useParagraphStyle();
|
|
2674
|
+
const editor = useDocxEditor();
|
|
2675
|
+
const house = useHouseTemplate();
|
|
2676
|
+
const changed = new Set(
|
|
2677
|
+
house?.basedOnTemplate ? house.divergence?.styles.filter((s) => s.kind === "changed").map((s) => s.styleId) : []
|
|
2678
|
+
);
|
|
2679
|
+
const [query, setQuery] = useState("");
|
|
2680
|
+
const quick = useMemo(
|
|
2681
|
+
() => QUICK_STYLES.map((name) => findStyle(options, name)).filter(
|
|
2682
|
+
(o) => !!o
|
|
2822
2683
|
),
|
|
2823
|
-
|
|
2824
|
-
|
|
2684
|
+
[options]
|
|
2685
|
+
);
|
|
2686
|
+
const shown = useMemo(() => {
|
|
2687
|
+
const q = query.trim().toLowerCase();
|
|
2688
|
+
return q ? options.filter(
|
|
2689
|
+
(o) => o.name.toLowerCase().includes(q) || o.styleId.toLowerCase().includes(q)
|
|
2690
|
+
) : options;
|
|
2691
|
+
}, [options, query]);
|
|
2692
|
+
const apply = (option) => {
|
|
2693
|
+
setValue(option.styleId);
|
|
2694
|
+
close();
|
|
2695
|
+
editor?.surface?.focus();
|
|
2696
|
+
};
|
|
2697
|
+
const nav = useListKeyNav({
|
|
2698
|
+
open: true,
|
|
2699
|
+
items: shown,
|
|
2700
|
+
onPick: apply,
|
|
2701
|
+
onDismiss: close,
|
|
2702
|
+
resetKey: query
|
|
2703
|
+
});
|
|
2704
|
+
return /* @__PURE__ */ jsxs("div", { className: "native-docx-style-gallery", "data-testid": "docx-style-gallery", children: [
|
|
2705
|
+
quick.length > 0 && /* @__PURE__ */ jsx("div", { className: "native-docx-style-quick", role: "group", "aria-label": "Quick styles", children: quick.map((option) => /* @__PURE__ */ jsx(
|
|
2706
|
+
"button",
|
|
2825
2707
|
{
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
},
|
|
2839
|
-
testId: "docx-cross-reference-card"
|
|
2840
|
-
}
|
|
2841
|
-
),
|
|
2842
|
-
dialog && /* @__PURE__ */ jsx(
|
|
2843
|
-
CrossReferenceDialog,
|
|
2708
|
+
type: "button",
|
|
2709
|
+
"aria-pressed": value === option.styleId,
|
|
2710
|
+
disabled: !isEnabled,
|
|
2711
|
+
"data-style-id": option.styleId,
|
|
2712
|
+
title: styleLabel(option.name),
|
|
2713
|
+
onClick: () => apply(option),
|
|
2714
|
+
children: /* @__PURE__ */ jsx("span", { style: previewStyle(option), children: styleLabel(option.name) })
|
|
2715
|
+
},
|
|
2716
|
+
option.styleId
|
|
2717
|
+
)) }),
|
|
2718
|
+
options.length > FILTER_FROM && /* @__PURE__ */ jsx(
|
|
2719
|
+
"input",
|
|
2844
2720
|
{
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
() => markCrossReferenceTarget(referencesPort(editor), target, mode),
|
|
2856
|
-
(marked, revision) => insertCrossReferenceField(referencesPort(editor, revision), marked, dialog.caret)
|
|
2857
|
-
),
|
|
2858
|
-
onInsertPassage: (target, text) => run2(
|
|
2859
|
-
`Inserted cross-reference: ${text}`,
|
|
2860
|
-
() => void 0,
|
|
2861
|
-
(_, revision) => insertDocumentReference(
|
|
2862
|
-
referencesPort(editor, revision),
|
|
2863
|
-
{ text, url: references.linkFor(target), appName: references.appName },
|
|
2864
|
-
dialog.caret
|
|
2865
|
-
)
|
|
2866
|
-
)
|
|
2721
|
+
className: "native-docx-style-filter",
|
|
2722
|
+
placeholder: "Find a style",
|
|
2723
|
+
"aria-label": "Find a style",
|
|
2724
|
+
value: query,
|
|
2725
|
+
autoFocus: true,
|
|
2726
|
+
onChange: (event) => setQuery(event.target.value),
|
|
2727
|
+
onKeyDown: (event) => {
|
|
2728
|
+
nav.handleKeyDown(event);
|
|
2729
|
+
},
|
|
2730
|
+
...nav.comboboxProps
|
|
2867
2731
|
}
|
|
2868
2732
|
),
|
|
2869
|
-
/* @__PURE__ */
|
|
2870
|
-
|
|
2733
|
+
/* @__PURE__ */ jsxs(
|
|
2734
|
+
"ul",
|
|
2871
2735
|
{
|
|
2872
|
-
className: "
|
|
2873
|
-
|
|
2736
|
+
className: "native-docx-style-list",
|
|
2737
|
+
role: "listbox",
|
|
2738
|
+
id: nav.listboxId,
|
|
2739
|
+
"aria-label": "Document styles",
|
|
2874
2740
|
children: [
|
|
2875
|
-
/* @__PURE__ */ jsxs(
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
}
|
|
2902
|
-
)
|
|
2903
|
-
]
|
|
2904
|
-
},
|
|
2905
|
-
`${b.paragraphId}:${b.offset}`
|
|
2906
|
-
)) })
|
|
2907
|
-
] }) : /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: l.brokenNone })
|
|
2908
|
-
] }),
|
|
2909
|
-
/* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setReport(null), children: l.close }) })
|
|
2741
|
+
shown.map((option, index) => /* @__PURE__ */ jsxs(
|
|
2742
|
+
"li",
|
|
2743
|
+
{
|
|
2744
|
+
id: listOptionId(nav.listboxId, index),
|
|
2745
|
+
role: "option",
|
|
2746
|
+
"aria-selected": value === option.styleId,
|
|
2747
|
+
"aria-disabled": !isEnabled,
|
|
2748
|
+
"data-active": index === nav.selectedIndex || void 0,
|
|
2749
|
+
"data-style-id": option.styleId,
|
|
2750
|
+
onMouseEnter: () => nav.setSelectedIndex(index),
|
|
2751
|
+
onClick: () => isEnabled && apply(option),
|
|
2752
|
+
children: [
|
|
2753
|
+
/* @__PURE__ */ jsx("span", { style: previewStyle(option), children: styleLabel(option.name) }),
|
|
2754
|
+
changed.has(option.styleId) && /* @__PURE__ */ jsx(
|
|
2755
|
+
"span",
|
|
2756
|
+
{
|
|
2757
|
+
className: "native-docx-style-diverged",
|
|
2758
|
+
title: "Defined differently from the house template",
|
|
2759
|
+
"aria-label": "Defined differently from the house template"
|
|
2760
|
+
}
|
|
2761
|
+
)
|
|
2762
|
+
]
|
|
2763
|
+
},
|
|
2764
|
+
option.styleId
|
|
2765
|
+
)),
|
|
2766
|
+
shown.length === 0 && /* @__PURE__ */ jsx("li", { className: "native-docx-style-empty", children: "No matching style" })
|
|
2910
2767
|
]
|
|
2911
2768
|
}
|
|
2912
|
-
)
|
|
2769
|
+
)
|
|
2913
2770
|
] });
|
|
2914
2771
|
}
|
|
2915
|
-
function
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
const
|
|
2952
|
-
const
|
|
2772
|
+
function StyleGallery() {
|
|
2773
|
+
const { value, options } = useParagraphStyle();
|
|
2774
|
+
const current = options.find((o) => o.styleId === value);
|
|
2775
|
+
const name = current ? styleLabel(current.name) : "Styles";
|
|
2776
|
+
const house = useHouseTemplate();
|
|
2777
|
+
const diverged = house?.basedOnTemplate ? divergenceSummary(house.divergence) : null;
|
|
2778
|
+
return /* @__PURE__ */ jsx(
|
|
2779
|
+
RibbonMenu,
|
|
2780
|
+
{
|
|
2781
|
+
label: "Styles",
|
|
2782
|
+
title: diverged ? `Styles. ${diverged}` : void 0,
|
|
2783
|
+
className: "native-docx-style-trigger",
|
|
2784
|
+
testId: "docx-style-trigger",
|
|
2785
|
+
icon: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2786
|
+
/* @__PURE__ */ jsx("span", { className: "native-docx-style-current", "data-testid": "docx-current-style", children: name }),
|
|
2787
|
+
diverged && /* @__PURE__ */ jsx(
|
|
2788
|
+
"span",
|
|
2789
|
+
{
|
|
2790
|
+
className: "native-docx-style-diverged",
|
|
2791
|
+
"data-testid": "docx-style-diverged",
|
|
2792
|
+
"aria-label": diverged
|
|
2793
|
+
}
|
|
2794
|
+
),
|
|
2795
|
+
/* @__PURE__ */ jsx(ChevronDown, { size: 12, "aria-hidden": true })
|
|
2796
|
+
] }),
|
|
2797
|
+
children: (close) => /* @__PURE__ */ jsx(Gallery, { close })
|
|
2798
|
+
}
|
|
2799
|
+
);
|
|
2800
|
+
}
|
|
2801
|
+
var W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
2802
|
+
var DEFAULT_PARAGRAPH_FONT = {
|
|
2803
|
+
id: "DefaultParagraphFont",
|
|
2804
|
+
type: "character",
|
|
2805
|
+
xml: '<w:style w:type="character" w:default="1" w:styleId="DefaultParagraphFont"><w:name w:val="Default Paragraph Font"/><w:uiPriority w:val="1"/><w:semiHidden/><w:unhideWhenUsed/></w:style>'
|
|
2806
|
+
};
|
|
2807
|
+
function stylesOf(kind) {
|
|
2808
|
+
const Kind = kind === "footnote" ? "Footnote" : "Endnote";
|
|
2809
|
+
const text = `${Kind}Text`;
|
|
2810
|
+
return [
|
|
2811
|
+
{
|
|
2812
|
+
id: text,
|
|
2813
|
+
type: "paragraph",
|
|
2814
|
+
xml: `<w:style w:type="paragraph" w:styleId="${text}"><w:name w:val="${kind} text"/><w:basedOn w:val="Normal"/><w:link w:val="${text}Char"/><w:uiPriority w:val="99"/><w:semiHidden/><w:unhideWhenUsed/><w:pPr><w:spacing w:after="0" w:line="240" w:lineRule="auto"/></w:pPr><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr></w:style>`
|
|
2815
|
+
},
|
|
2816
|
+
{
|
|
2817
|
+
id: `${text}Char`,
|
|
2818
|
+
type: "character",
|
|
2819
|
+
xml: `<w:style w:type="character" w:customStyle="1" w:styleId="${text}Char"><w:name w:val="${Kind} Text Char"/><w:basedOn w:val="DefaultParagraphFont"/><w:link w:val="${text}"/><w:uiPriority w:val="99"/><w:semiHidden/><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr></w:style>`
|
|
2820
|
+
},
|
|
2821
|
+
{
|
|
2822
|
+
id: `${Kind}Reference`,
|
|
2823
|
+
type: "character",
|
|
2824
|
+
xml: `<w:style w:type="character" w:styleId="${Kind}Reference"><w:name w:val="${kind} reference"/><w:basedOn w:val="DefaultParagraphFont"/><w:uiPriority w:val="99"/><w:semiHidden/><w:unhideWhenUsed/><w:rPr><w:vertAlign w:val="superscript"/></w:rPr></w:style>`
|
|
2825
|
+
}
|
|
2826
|
+
];
|
|
2827
|
+
}
|
|
2828
|
+
function noteTextStyle(styles, kind) {
|
|
2829
|
+
const paragraph = styles.filter((style) => style.type === "paragraph");
|
|
2830
|
+
const id = kind === "footnote" ? "FootnoteText" : "EndnoteText";
|
|
2831
|
+
return paragraph.find((style) => style.name.toLowerCase() === `${kind} text`)?.styleId ?? paragraph.find((style) => style.styleId === id)?.styleId;
|
|
2832
|
+
}
|
|
2833
|
+
function missingNoteStyles(styles, kind) {
|
|
2834
|
+
const has = (type, id) => styles.some((style) => style.type === type && style.styleId === id);
|
|
2835
|
+
const text = noteTextStyle(styles, kind);
|
|
2836
|
+
const missing = stylesOf(kind).filter(
|
|
2837
|
+
(style) => style.type === "paragraph" ? !text : !has("character", style.id)
|
|
2838
|
+
);
|
|
2839
|
+
if (!missing.length) return [];
|
|
2840
|
+
const base = has("character", DEFAULT_PARAGRAPH_FONT.id) ? [] : [DEFAULT_PARAGRAPH_FONT];
|
|
2841
|
+
return [...base, ...missing].map((style) => style.xml);
|
|
2842
|
+
}
|
|
2843
|
+
var CONTENT_TYPES = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/></Types>';
|
|
2844
|
+
function stylesOnlyClipboardHtml(styles) {
|
|
2845
|
+
const fragment = zipSync({
|
|
2846
|
+
"[Content_Types].xml": strToU8(CONTENT_TYPES),
|
|
2847
|
+
"_rels/.rels": strToU8(
|
|
2848
|
+
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/></Relationships>'
|
|
2849
|
+
),
|
|
2850
|
+
"word/document.xml": strToU8(
|
|
2851
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2852
|
+
<w:document xmlns:w="${W}"><w:body><w:p/></w:body></w:document>`
|
|
2853
|
+
),
|
|
2854
|
+
"word/_rels/document.xml.rels": strToU8(
|
|
2855
|
+
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rIdStyles" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>'
|
|
2856
|
+
),
|
|
2857
|
+
"word/styles.xml": strToU8(
|
|
2858
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2859
|
+
<w:styles xmlns:w="${W}">${styles.join("")}</w:styles>`
|
|
2860
|
+
)
|
|
2861
|
+
});
|
|
2862
|
+
return fragmentClipboardHtml(fragment, false);
|
|
2863
|
+
}
|
|
2864
|
+
function ensureNoteStyles(editor, kind, direct = (fn) => fn()) {
|
|
2865
|
+
if (!editor.can({ type: "insertNote", noteKind: kind }).ok) return void 0;
|
|
2866
|
+
const missing = missingNoteStyles(editor.getDocumentStyles(), kind);
|
|
2867
|
+
if (missing.length)
|
|
2868
|
+
direct(() => editor.exec({ type: "paste", text: "", html: stylesOnlyClipboardHtml(missing) }));
|
|
2869
|
+
return noteTextStyle(editor.getDocumentStyles(), kind);
|
|
2870
|
+
}
|
|
2871
|
+
function useInsertNote(kind) {
|
|
2872
|
+
const command = useEditorCommand(kind === "footnote" ? "insert.footnote" : "insert.endnote");
|
|
2873
|
+
const editor = useDocxEditor();
|
|
2953
2874
|
const insert = () => {
|
|
2954
|
-
|
|
2955
|
-
if (
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
text
|
|
2959
|
-
);
|
|
2875
|
+
const text = editor && ensureNoteStyles(editor, kind, (fn) => withDirectEditing(editor, fn));
|
|
2876
|
+
if (!command.execute()) return false;
|
|
2877
|
+
if (editor) {
|
|
2878
|
+
if (text)
|
|
2879
|
+
withDirectEditing(editor, () => editor.exec({ type: "setParagraphStyle", styleId: text }));
|
|
2880
|
+
editor.exec({ type: "insertText", text: " " });
|
|
2881
|
+
}
|
|
2882
|
+
return true;
|
|
2960
2883
|
};
|
|
2961
|
-
return
|
|
2962
|
-
|
|
2884
|
+
return { insert, isEnabled: command.isEnabled, disabledReason: command.disabledReason };
|
|
2885
|
+
}
|
|
2886
|
+
function NoteAction({
|
|
2887
|
+
kind,
|
|
2888
|
+
label,
|
|
2889
|
+
shortcut,
|
|
2890
|
+
icon
|
|
2891
|
+
}) {
|
|
2892
|
+
const note = useInsertNote(kind);
|
|
2893
|
+
return /* @__PURE__ */ jsx(RibbonItem, { id: `references-${kind}`, label, children: /* @__PURE__ */ jsx(
|
|
2894
|
+
DocxEditor.Toolbar.Action,
|
|
2963
2895
|
{
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
},
|
|
2971
|
-
children: [
|
|
2972
|
-
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
2973
|
-
/* @__PURE__ */ jsx(DialogTitle, { children: l.title }),
|
|
2974
|
-
/* @__PURE__ */ jsx(DialogDescription, { children: l.description })
|
|
2975
|
-
] }),
|
|
2976
|
-
/* @__PURE__ */ jsxs(Tabs, { value: tab, onValueChange: (v) => setTab(v), children: [
|
|
2977
|
-
/* @__PURE__ */ jsxs(TabsList, { children: [
|
|
2978
|
-
/* @__PURE__ */ jsx(TabsTrigger, { value: "this", "data-testid": "docx-cross-reference-this", children: l.thisDocument }),
|
|
2979
|
-
references && /* @__PURE__ */ jsx(TabsTrigger, { value: "other", "data-testid": "docx-cross-reference-other", children: l.otherDocument })
|
|
2980
|
-
] }),
|
|
2981
|
-
/* @__PURE__ */ jsxs(TabsContent, { value: "this", className: "space-y-3", children: [
|
|
2982
|
-
/* @__PURE__ */ jsx(
|
|
2983
|
-
Input,
|
|
2984
|
-
{
|
|
2985
|
-
value: query,
|
|
2986
|
-
placeholder: l.filterTargets,
|
|
2987
|
-
"aria-label": l.filterTargets,
|
|
2988
|
-
onChange: (e) => setQuery(e.target.value),
|
|
2989
|
-
"data-testid": "docx-cross-reference-filter"
|
|
2990
|
-
}
|
|
2991
|
-
),
|
|
2992
|
-
/* @__PURE__ */ jsxs(
|
|
2993
|
-
"div",
|
|
2994
|
-
{
|
|
2995
|
-
role: "listbox",
|
|
2996
|
-
"aria-label": l.filterTargets,
|
|
2997
|
-
className: "max-h-64 space-y-0.5 overflow-y-auto rounded-md border border-border p-1",
|
|
2998
|
-
children: [
|
|
2999
|
-
shown.length === 0 && /* @__PURE__ */ jsx("p", { className: "p-2 text-sm text-muted-foreground", children: l.noTargets }),
|
|
3000
|
-
shown.map((t) => /* @__PURE__ */ jsxs(
|
|
3001
|
-
"button",
|
|
3002
|
-
{
|
|
3003
|
-
type: "button",
|
|
3004
|
-
role: "option",
|
|
3005
|
-
"aria-selected": chosen?.paragraphId === t.paragraphId,
|
|
3006
|
-
"data-testid": "docx-cross-reference-target",
|
|
3007
|
-
onClick: () => setChosen(t),
|
|
3008
|
-
onDoubleClick: () => onInsertTarget(t, t.number ? mode : "text"),
|
|
3009
|
-
className: cn(
|
|
3010
|
-
"flex w-full items-baseline gap-2 rounded px-2 py-1 text-left text-sm",
|
|
3011
|
-
chosen?.paragraphId === t.paragraphId ? "bg-accent" : "hover:bg-accent/50"
|
|
3012
|
-
),
|
|
3013
|
-
style: { paddingLeft: 8 + t.level * 16 },
|
|
3014
|
-
children: [
|
|
3015
|
-
/* @__PURE__ */ jsx("span", { className: "shrink-0 font-mono text-xs text-muted-foreground", children: t.marker || l.heading }),
|
|
3016
|
-
/* @__PURE__ */ jsx("span", { className: cn("truncate", t.heading && "font-medium"), children: t.text })
|
|
3017
|
-
]
|
|
3018
|
-
},
|
|
3019
|
-
t.paragraphId
|
|
3020
|
-
))
|
|
3021
|
-
]
|
|
3022
|
-
}
|
|
3023
|
-
),
|
|
3024
|
-
chosen && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 text-sm", children: [
|
|
3025
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: l.insertAs }),
|
|
3026
|
-
chosen.number && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1", children: [
|
|
3027
|
-
/* @__PURE__ */ jsx(
|
|
3028
|
-
"input",
|
|
3029
|
-
{
|
|
3030
|
-
type: "radio",
|
|
3031
|
-
name: "cross-reference-mode",
|
|
3032
|
-
checked: effectiveMode === "number",
|
|
3033
|
-
onChange: () => setMode("number")
|
|
3034
|
-
}
|
|
3035
|
-
),
|
|
3036
|
-
l.number
|
|
3037
|
-
] }),
|
|
3038
|
-
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1", children: [
|
|
3039
|
-
/* @__PURE__ */ jsx(
|
|
3040
|
-
"input",
|
|
3041
|
-
{
|
|
3042
|
-
type: "radio",
|
|
3043
|
-
name: "cross-reference-mode",
|
|
3044
|
-
checked: effectiveMode === "text",
|
|
3045
|
-
onChange: () => setMode("text")
|
|
3046
|
-
}
|
|
3047
|
-
),
|
|
3048
|
-
l.text
|
|
3049
|
-
] }),
|
|
3050
|
-
/* @__PURE__ */ jsx(
|
|
3051
|
-
"span",
|
|
3052
|
-
{
|
|
3053
|
-
className: "ml-auto rounded bg-muted px-2 py-0.5",
|
|
3054
|
-
"data-testid": "docx-cross-reference-preview",
|
|
3055
|
-
children: refResult(chosen, effectiveMode)
|
|
3056
|
-
}
|
|
3057
|
-
)
|
|
3058
|
-
] })
|
|
3059
|
-
] }),
|
|
3060
|
-
references && /* @__PURE__ */ jsxs(TabsContent, { value: "other", className: "space-y-3", children: [
|
|
3061
|
-
references.renderDocumentPicker ? references.renderDocumentPicker(chooseDocument, docId) : /* @__PURE__ */ jsxs(
|
|
3062
|
-
"select",
|
|
3063
|
-
{
|
|
3064
|
-
className: "h-9 w-full rounded-md border border-border bg-background px-2 text-sm",
|
|
3065
|
-
"aria-label": l.chooseDocument,
|
|
3066
|
-
value: docId ?? "",
|
|
3067
|
-
onChange: (e) => e.target.value && chooseDocument(e.target.value),
|
|
3068
|
-
"data-testid": "docx-cross-reference-document",
|
|
3069
|
-
children: [
|
|
3070
|
-
/* @__PURE__ */ jsx("option", { value: "", children: l.chooseDocument }),
|
|
3071
|
-
(documents ?? []).map((d) => /* @__PURE__ */ jsx("option", { value: d.id, children: d.title }, d.id))
|
|
3072
|
-
]
|
|
3073
|
-
}
|
|
3074
|
-
),
|
|
3075
|
-
docId && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3076
|
-
/* @__PURE__ */ jsx(
|
|
3077
|
-
Input,
|
|
3078
|
-
{
|
|
3079
|
-
value: passageQuery,
|
|
3080
|
-
placeholder: l.filterPassages,
|
|
3081
|
-
"aria-label": l.filterPassages,
|
|
3082
|
-
onChange: (e) => setPassageQuery(e.target.value),
|
|
3083
|
-
"data-testid": "docx-cross-reference-passage-filter"
|
|
3084
|
-
}
|
|
3085
|
-
),
|
|
3086
|
-
/* @__PURE__ */ jsx(
|
|
3087
|
-
"div",
|
|
3088
|
-
{
|
|
3089
|
-
className: "max-h-64 space-y-1 overflow-y-auto",
|
|
3090
|
-
"data-testid": "docx-cross-reference-passages",
|
|
3091
|
-
children: list === "loading" || list === void 0 ? /* @__PURE__ */ jsx("p", { className: "p-2 text-sm text-muted-foreground", children: l.loadingPassages }) : shownPassages.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-2 text-sm text-muted-foreground", children: l.noPassages }) : shownPassages.map((p, i) => /* @__PURE__ */ jsx(
|
|
3092
|
-
FactPassageItem,
|
|
3093
|
-
{
|
|
3094
|
-
index: i,
|
|
3095
|
-
page: p.page,
|
|
3096
|
-
excerpt: passageExcerpt(p.text, 200),
|
|
3097
|
-
isViewing: passage?.chunkId === p.chunkId,
|
|
3098
|
-
onSelect: () => {
|
|
3099
|
-
setPassage(p);
|
|
3100
|
-
setText(referenceText(title, p));
|
|
3101
|
-
},
|
|
3102
|
-
testId: "docx-cross-reference-passage"
|
|
3103
|
-
},
|
|
3104
|
-
p.chunkId
|
|
3105
|
-
))
|
|
3106
|
-
}
|
|
3107
|
-
),
|
|
3108
|
-
passage && /* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-sm", children: [
|
|
3109
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: l.referenceText }),
|
|
3110
|
-
/* @__PURE__ */ jsx(
|
|
3111
|
-
Input,
|
|
3112
|
-
{
|
|
3113
|
-
value: text,
|
|
3114
|
-
onChange: (e) => setText(e.target.value),
|
|
3115
|
-
"data-testid": "docx-cross-reference-text"
|
|
3116
|
-
}
|
|
3117
|
-
)
|
|
3118
|
-
] })
|
|
3119
|
-
] })
|
|
3120
|
-
] })
|
|
3121
|
-
] }),
|
|
3122
|
-
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
3123
|
-
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onCancel, children: l.cancel }),
|
|
3124
|
-
/* @__PURE__ */ jsx(Button, { disabled: !canInsert, onClick: insert, "data-testid": "docx-cross-reference-insert", children: l.insertButton })
|
|
3125
|
-
] })
|
|
3126
|
-
]
|
|
2896
|
+
label: `${label} (${shortcut})`,
|
|
2897
|
+
icon,
|
|
2898
|
+
disabled: !note.isEnabled,
|
|
2899
|
+
disabledReason: note.disabledReason ?? void 0,
|
|
2900
|
+
onSelect: () => void note.insert(),
|
|
2901
|
+
className: `native-docx-insert-${kind}`
|
|
3127
2902
|
}
|
|
3128
2903
|
) });
|
|
3129
2904
|
}
|
|
3130
|
-
function
|
|
3131
|
-
|
|
3132
|
-
icon,
|
|
3133
|
-
children,
|
|
3134
|
-
className,
|
|
3135
|
-
testId,
|
|
3136
|
-
title,
|
|
3137
|
-
keepMounted = false,
|
|
3138
|
-
popoverClassName
|
|
3139
|
-
}) {
|
|
3140
|
-
const [open, setOpen] = useState(false);
|
|
3141
|
-
const element = useRef(null);
|
|
3142
|
-
useEffect(() => {
|
|
3143
|
-
if (!open) return;
|
|
3144
|
-
const dismiss = (event) => {
|
|
3145
|
-
if (!element.current?.contains(event.target)) setOpen(false);
|
|
3146
|
-
};
|
|
3147
|
-
const escape = (event) => {
|
|
3148
|
-
if (event.key === "Escape") setOpen(false);
|
|
3149
|
-
};
|
|
3150
|
-
document.addEventListener("pointerdown", dismiss);
|
|
3151
|
-
document.addEventListener("keydown", escape);
|
|
3152
|
-
return () => {
|
|
3153
|
-
document.removeEventListener("pointerdown", dismiss);
|
|
3154
|
-
document.removeEventListener("keydown", escape);
|
|
3155
|
-
};
|
|
3156
|
-
}, [open]);
|
|
3157
|
-
return /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon-menu", ref: element, children: [
|
|
2905
|
+
function NoteButtons() {
|
|
2906
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3158
2907
|
/* @__PURE__ */ jsx(
|
|
3159
|
-
|
|
2908
|
+
NoteAction,
|
|
3160
2909
|
{
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
"
|
|
3164
|
-
|
|
3165
|
-
"aria-expanded": open,
|
|
3166
|
-
"data-testid": testId,
|
|
3167
|
-
onMouseDown: (event) => event.preventDefault(),
|
|
3168
|
-
onClick: () => setOpen((value) => !value),
|
|
3169
|
-
children: icon
|
|
2910
|
+
kind: "footnote",
|
|
2911
|
+
label: "Insert footnote",
|
|
2912
|
+
shortcut: "Ctrl+Alt+F",
|
|
2913
|
+
icon: /* @__PURE__ */ jsx(Superscript, { size: 16 })
|
|
3170
2914
|
}
|
|
3171
2915
|
),
|
|
3172
|
-
|
|
3173
|
-
|
|
2916
|
+
/* @__PURE__ */ jsx(
|
|
2917
|
+
NoteAction,
|
|
3174
2918
|
{
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
"
|
|
3178
|
-
|
|
3179
|
-
onMouseDown: (event) => {
|
|
3180
|
-
if (!(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement))
|
|
3181
|
-
event.preventDefault();
|
|
3182
|
-
},
|
|
3183
|
-
children: typeof children === "function" ? children(() => setOpen(false)) : children
|
|
2919
|
+
kind: "endnote",
|
|
2920
|
+
label: "Insert endnote",
|
|
2921
|
+
shortcut: "Ctrl+Alt+D",
|
|
2922
|
+
icon: /* @__PURE__ */ jsx(NotebookText, { size: 16 })
|
|
3184
2923
|
}
|
|
3185
2924
|
)
|
|
3186
2925
|
] });
|
|
3187
2926
|
}
|
|
3188
|
-
|
|
3189
|
-
const
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
function rememberCaret(editor) {
|
|
3193
|
-
const surface = editor.surface;
|
|
3194
|
-
const head = surface?.state().selection?.head;
|
|
3195
|
-
if (!surface || !head) return null;
|
|
3196
|
-
return { paraId: surface.session.paraIdOf(head.paragraphId), ...head };
|
|
3197
|
-
}
|
|
3198
|
-
var mounted = [];
|
|
3199
|
-
var running = 0;
|
|
3200
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
3201
|
-
var setRunning = (delta) => {
|
|
3202
|
-
running += delta;
|
|
3203
|
-
listeners.forEach((listener) => listener());
|
|
3204
|
-
};
|
|
3205
|
-
var subscribe = (listener) => {
|
|
3206
|
-
listeners.add(listener);
|
|
3207
|
-
return () => void listeners.delete(listener);
|
|
3208
|
-
};
|
|
3209
|
-
function insertionTarget(origin, caret, editors = mounted) {
|
|
3210
|
-
if (editors.includes(origin) && origin.surface) return origin;
|
|
3211
|
-
const paraId = caret?.paraId;
|
|
3212
|
-
if (!paraId) return null;
|
|
3213
|
-
for (let i = editors.length - 1; i >= 0; i--) {
|
|
3214
|
-
const session = editors[i].surface?.session;
|
|
3215
|
-
if (session?.nodeIdOf(paraId)) return editors[i];
|
|
3216
|
-
}
|
|
3217
|
-
return null;
|
|
3218
|
-
}
|
|
3219
|
-
function restoreCaret(editor, caret) {
|
|
3220
|
-
const surface = editor.surface;
|
|
3221
|
-
if (!surface || !caret) return;
|
|
3222
|
-
const paragraphId = caret.paraId && surface.session.nodeIdOf(caret.paraId) || caret.paragraphId;
|
|
3223
|
-
if (!surface.session.paragraphIds().includes(paragraphId)) return;
|
|
3224
|
-
const point = { paragraphId, offset: caret.offset };
|
|
3225
|
-
surface.setSelection({ anchor: point, head: point });
|
|
3226
|
-
}
|
|
3227
|
-
async function insertGeneratedImage(editor, bytes, prompt, caret) {
|
|
3228
|
-
const image = normalizeImageBytes(bytes);
|
|
3229
|
-
if (!image.ok) throw new Error("The generated image is not in a format Word can embed.");
|
|
3230
|
-
let result;
|
|
3231
|
-
for (let attempt = 0; attempt < 3 && !result?.ok; attempt++) {
|
|
3232
|
-
editor.focus();
|
|
3233
|
-
restoreCaret(editor, caret);
|
|
3234
|
-
result = await executeImageCommand(editor, {
|
|
3235
|
-
type: "insertImage",
|
|
3236
|
-
data: image.bytes,
|
|
3237
|
-
mime: image.mime,
|
|
3238
|
-
widthPoints: image.widthPoints,
|
|
3239
|
-
heightPoints: image.heightPoints,
|
|
3240
|
-
description: prompt.trim(),
|
|
3241
|
-
title: titleOf(prompt)
|
|
3242
|
-
});
|
|
3243
|
-
if (!result.ok && result.reason !== "stale drawing selection") break;
|
|
3244
|
-
}
|
|
3245
|
-
if (!result?.ok) throw new Error(result?.reason || "The picture could not be inserted here.");
|
|
3246
|
-
}
|
|
3247
|
-
var AUTO = "";
|
|
3248
|
-
function useGenerateImageTarget() {
|
|
2927
|
+
function WordShortcuts({ scope }) {
|
|
2928
|
+
const footnote = useInsertNote("footnote");
|
|
2929
|
+
const endnote = useInsertNote("endnote");
|
|
2930
|
+
const styles = useParagraphStyle();
|
|
3249
2931
|
const editor = useDocxEditor();
|
|
3250
|
-
const
|
|
2932
|
+
const levels = useHouseTemplate()?.levels;
|
|
2933
|
+
const latest = useRef({ footnote, endnote, styles, editor, levels });
|
|
2934
|
+
latest.current = { footnote, endnote, styles, editor, levels };
|
|
3251
2935
|
useEffect(() => {
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
if (!editor || !prompt.trim() || busy) return;
|
|
3270
|
-
const caret = rememberCaret(editor);
|
|
3271
|
-
const controller = new AbortController();
|
|
3272
|
-
abort.current = controller;
|
|
3273
|
-
setBusy(true);
|
|
3274
|
-
setRunning(1);
|
|
3275
|
-
setError("");
|
|
3276
|
-
try {
|
|
3277
|
-
const bytes = await generation.generate(prompt.trim(), {
|
|
3278
|
-
aspectRatio: aspectRatio || void 0,
|
|
3279
|
-
signal: controller.signal
|
|
3280
|
-
});
|
|
3281
|
-
if (controller.signal.aborted) return;
|
|
3282
|
-
const target = insertionTarget(editor, caret);
|
|
3283
|
-
if (!target) throw new Error("The document was closed before the generated image was ready.");
|
|
3284
|
-
await insertGeneratedImage(target, bytes, prompt, caret);
|
|
3285
|
-
setPrompt("");
|
|
3286
|
-
close.current();
|
|
3287
|
-
generation.notify?.("Generated image inserted", "success");
|
|
3288
|
-
} catch (failure) {
|
|
3289
|
-
if (controller.signal.aborted) return;
|
|
3290
|
-
const message = failure instanceof Error ? failure.message : String(failure);
|
|
3291
|
-
setError(message);
|
|
3292
|
-
generation.notify?.(message, "error");
|
|
3293
|
-
} finally {
|
|
3294
|
-
abort.current = null;
|
|
3295
|
-
setRunning(-1);
|
|
3296
|
-
setBusy(false);
|
|
3297
|
-
}
|
|
3298
|
-
}
|
|
3299
|
-
if (!editable && !generating) return null;
|
|
3300
|
-
return /* @__PURE__ */ jsx(
|
|
3301
|
-
RibbonMenu,
|
|
3302
|
-
{
|
|
3303
|
-
label: generating ? "Generating image\u2026" : "Generate image",
|
|
3304
|
-
icon: generating ? /* @__PURE__ */ jsx(Loader2, { size: 16, className: "native-docx-spin" }) : /* @__PURE__ */ jsx(Sparkles, { size: 16 }),
|
|
3305
|
-
testId: "docx-generate-image",
|
|
3306
|
-
children: (dismiss) => {
|
|
3307
|
-
close.current = dismiss;
|
|
3308
|
-
return /* @__PURE__ */ jsxs(
|
|
3309
|
-
"form",
|
|
3310
|
-
{
|
|
3311
|
-
className: "native-docx-form native-docx-generate-image",
|
|
3312
|
-
onSubmit: (event) => {
|
|
3313
|
-
event.preventDefault();
|
|
3314
|
-
void run2();
|
|
3315
|
-
},
|
|
3316
|
-
children: [
|
|
3317
|
-
/* @__PURE__ */ jsxs("label", { children: [
|
|
3318
|
-
"Describe the image",
|
|
3319
|
-
/* @__PURE__ */ jsx(
|
|
3320
|
-
"textarea",
|
|
3321
|
-
{
|
|
3322
|
-
autoFocus: true,
|
|
3323
|
-
rows: 3,
|
|
3324
|
-
value: prompt,
|
|
3325
|
-
disabled: busy,
|
|
3326
|
-
placeholder: "A watercolour of a lighthouse at dawn",
|
|
3327
|
-
"data-testid": "docx-generate-image-prompt",
|
|
3328
|
-
onChange: (event) => setPrompt(event.target.value),
|
|
3329
|
-
onKeyDown: (event) => {
|
|
3330
|
-
if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) void run2();
|
|
3331
|
-
}
|
|
3332
|
-
}
|
|
3333
|
-
)
|
|
3334
|
-
] }),
|
|
3335
|
-
generation.aspectRatios && generation.aspectRatios.length > 0 && /* @__PURE__ */ jsxs("label", { children: [
|
|
3336
|
-
"Aspect ratio",
|
|
3337
|
-
/* @__PURE__ */ jsxs(
|
|
3338
|
-
"select",
|
|
3339
|
-
{
|
|
3340
|
-
value: aspectRatio,
|
|
3341
|
-
disabled: busy,
|
|
3342
|
-
onChange: (event) => setAspectRatio(event.target.value),
|
|
3343
|
-
children: [
|
|
3344
|
-
/* @__PURE__ */ jsx("option", { value: AUTO, children: "Auto" }),
|
|
3345
|
-
generation.aspectRatios.map((ratio) => /* @__PURE__ */ jsx("option", { value: ratio.value, children: ratio.label }, ratio.value))
|
|
3346
|
-
]
|
|
3347
|
-
}
|
|
3348
|
-
)
|
|
3349
|
-
] }),
|
|
3350
|
-
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." }),
|
|
3351
|
-
error && /* @__PURE__ */ jsx("p", { role: "alert", children: error }),
|
|
3352
|
-
/* @__PURE__ */ jsx("div", { className: "native-docx-form-actions", children: busy ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => abort.current?.abort(), children: "Cancel" }) : /* @__PURE__ */ jsx(
|
|
3353
|
-
"button",
|
|
3354
|
-
{
|
|
3355
|
-
type: "submit",
|
|
3356
|
-
disabled: !prompt.trim(),
|
|
3357
|
-
"data-testid": "docx-generate-image-submit",
|
|
3358
|
-
children: "Generate"
|
|
3359
|
-
}
|
|
3360
|
-
) })
|
|
3361
|
-
]
|
|
3362
|
-
}
|
|
3363
|
-
);
|
|
2936
|
+
const element = scope.current;
|
|
2937
|
+
if (!element) return;
|
|
2938
|
+
const onKeyDown = (event) => {
|
|
2939
|
+
if (outlineTab(event)) {
|
|
2940
|
+
event.preventDefault();
|
|
2941
|
+
event.stopPropagation();
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2944
|
+
const action = wordShortcut(event);
|
|
2945
|
+
if (!action) return;
|
|
2946
|
+
const { footnote: footnote2, endnote: endnote2, styles: styles2, editor: editor2 } = latest.current;
|
|
2947
|
+
if (action === "footnote" || action === "endnote") {
|
|
2948
|
+
if (!(action === "footnote" ? footnote2 : endnote2).insert()) return;
|
|
2949
|
+
} else {
|
|
2950
|
+
const style = findStyle(styles2.options, action);
|
|
2951
|
+
if (!style) return;
|
|
2952
|
+
styles2.setValue(style.styleId);
|
|
3364
2953
|
}
|
|
2954
|
+
event.preventDefault();
|
|
2955
|
+
event.stopPropagation();
|
|
2956
|
+
editor2?.surface?.focus();
|
|
2957
|
+
};
|
|
2958
|
+
const outlineTab = (event) => {
|
|
2959
|
+
if (event.key !== "Tab" || event.ctrlKey || event.metaKey || event.altKey) return false;
|
|
2960
|
+
const { editor: editor2, levels: levels2, styles: styles2 } = latest.current;
|
|
2961
|
+
const current = editor2?.getSelectionFormatting()?.styleId;
|
|
2962
|
+
const link = current ? levels2?.[current] : void 0;
|
|
2963
|
+
const target = event.shiftKey ? link?.previous : link?.next;
|
|
2964
|
+
if (!target || !editor2 || editorPort(editor2).caret()?.offset !== 0) return false;
|
|
2965
|
+
styles2.setValue(target);
|
|
2966
|
+
return true;
|
|
2967
|
+
};
|
|
2968
|
+
element.addEventListener("keydown", onKeyDown, true);
|
|
2969
|
+
return () => element.removeEventListener("keydown", onKeyDown, true);
|
|
2970
|
+
}, [scope]);
|
|
2971
|
+
return null;
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
// src/docx/beforeSave.ts
|
|
2975
|
+
var hooks = /* @__PURE__ */ new WeakMap();
|
|
2976
|
+
function onBeforeSave(editor, fn) {
|
|
2977
|
+
let set = hooks.get(editor);
|
|
2978
|
+
if (!set) hooks.set(editor, set = /* @__PURE__ */ new Set());
|
|
2979
|
+
set.add(fn);
|
|
2980
|
+
return () => set.delete(fn);
|
|
2981
|
+
}
|
|
2982
|
+
function runBeforeSave(editor) {
|
|
2983
|
+
if (!editor) return;
|
|
2984
|
+
for (const fn of hooks.get(editor) ?? [])
|
|
2985
|
+
try {
|
|
2986
|
+
fn();
|
|
2987
|
+
} catch {
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
// src/docx/references/numbering.ts
|
|
2992
|
+
var numbers = (level) => !!level && level.numFmt !== "bullet" && level.numFmt !== "none" && (level.lvlText ?? level.markerText ?? "").length > 0;
|
|
2993
|
+
function paragraphNumber(order, items, levelOf, paragraphId) {
|
|
2994
|
+
const item = items.get(paragraphId);
|
|
2995
|
+
if (!item || !numbers(item)) return null;
|
|
2996
|
+
const at = order.indexOf(paragraphId);
|
|
2997
|
+
const markerAt = (ilvl) => {
|
|
2998
|
+
if (ilvl === item.ilvl) return item.markerText;
|
|
2999
|
+
for (let i = at - 1; i >= 0; i--) {
|
|
3000
|
+
const other = items.get(order[i]);
|
|
3001
|
+
if (other && other.numId === item.numId && other.ilvl === ilvl) return other.markerText;
|
|
3365
3002
|
}
|
|
3003
|
+
return null;
|
|
3004
|
+
};
|
|
3005
|
+
const chain = [];
|
|
3006
|
+
const texts = [];
|
|
3007
|
+
for (let u = item.ilvl; u >= 0; u--) {
|
|
3008
|
+
const level = levelOf(item.numId, u);
|
|
3009
|
+
if (!level || !numbers(level)) continue;
|
|
3010
|
+
if (u !== item.ilvl && texts.some((t) => t.includes(`%${u + 1}`))) continue;
|
|
3011
|
+
chain.push(u);
|
|
3012
|
+
texts.push(level.lvlText);
|
|
3013
|
+
}
|
|
3014
|
+
const value = chain.reverse().map((u) => markerAt(u) ?? "").join("");
|
|
3015
|
+
if (!value) return null;
|
|
3016
|
+
return value.length > 1 && value.endsWith(".") ? value.slice(0, -1) : value;
|
|
3017
|
+
}
|
|
3018
|
+
function listTargets(paragraphs, items, levelOf) {
|
|
3019
|
+
const order = paragraphs.map((p) => p.id);
|
|
3020
|
+
const out = [];
|
|
3021
|
+
for (const p of paragraphs) {
|
|
3022
|
+
const text = p.text.replace(//g, "").trim();
|
|
3023
|
+
if (!text) continue;
|
|
3024
|
+
const number = paragraphNumber(order, items, levelOf, p.id);
|
|
3025
|
+
const item = items.get(p.id);
|
|
3026
|
+
if (number !== null)
|
|
3027
|
+
out.push({
|
|
3028
|
+
paragraphId: p.id,
|
|
3029
|
+
kind: "numbered",
|
|
3030
|
+
number,
|
|
3031
|
+
marker: item?.markerText ?? "",
|
|
3032
|
+
text,
|
|
3033
|
+
heading: p.heading,
|
|
3034
|
+
level: item?.ilvl ?? 0
|
|
3035
|
+
});
|
|
3036
|
+
else if (p.heading)
|
|
3037
|
+
out.push({
|
|
3038
|
+
paragraphId: p.id,
|
|
3039
|
+
kind: "heading",
|
|
3040
|
+
number: null,
|
|
3041
|
+
marker: "",
|
|
3042
|
+
text,
|
|
3043
|
+
heading: true,
|
|
3044
|
+
level: 0
|
|
3045
|
+
});
|
|
3046
|
+
}
|
|
3047
|
+
return out;
|
|
3048
|
+
}
|
|
3049
|
+
function filterTargets(targets, query) {
|
|
3050
|
+
const q = query.trim().toLowerCase();
|
|
3051
|
+
if (!q) return [...targets];
|
|
3052
|
+
return targets.filter(
|
|
3053
|
+
(t) => (t.number ?? "").toLowerCase().startsWith(q) || t.text.toLowerCase().includes(q)
|
|
3366
3054
|
);
|
|
3367
3055
|
}
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
const
|
|
3374
|
-
if (
|
|
3375
|
-
}
|
|
3376
|
-
return template.innerHTML;
|
|
3056
|
+
|
|
3057
|
+
// src/docx/references/commands.ts
|
|
3058
|
+
var REF_BOOKMARK_PREFIX = "_Ref";
|
|
3059
|
+
function newRefBookmark(taken, random = Math.random) {
|
|
3060
|
+
for (; ; ) {
|
|
3061
|
+
const name = `${REF_BOOKMARK_PREFIX}${String(Math.floor(random() * 9e8) + 1e8)}`;
|
|
3062
|
+
if (!taken.has(name)) return name;
|
|
3063
|
+
}
|
|
3377
3064
|
}
|
|
3378
|
-
|
|
3379
|
-
|
|
3065
|
+
var styled = (style) => style ? `<w:rPr><w:rStyle w:val="${style}"/></w:rPr>` : "";
|
|
3066
|
+
var run = (text, style) => `<w:r>${styled(style)}<w:t xml:space="preserve">${esc(text)}</w:t></w:r>`;
|
|
3067
|
+
function refFieldXml(instruction, cached) {
|
|
3068
|
+
const r = (inner) => `<w:r>${styled(CROSS_REFERENCE_STYLE)}${inner}</w:r>`;
|
|
3069
|
+
return `${r('<w:fldChar w:fldCharType="begin"/>')}${r(
|
|
3070
|
+
`<w:instrText xml:space="preserve"> ${esc(instruction)} </w:instrText>`
|
|
3071
|
+
)}${r('<w:fldChar w:fldCharType="separate"/>')}${run(cached, CROSS_REFERENCE_STYLE)}${r(
|
|
3072
|
+
'<w:fldChar w:fldCharType="end"/>'
|
|
3073
|
+
)}`;
|
|
3380
3074
|
}
|
|
3381
|
-
var
|
|
3382
|
-
|
|
3383
|
-
|
|
3075
|
+
var refTooltip = (value, mode) => mode === "number" ? `Cross-reference to clause ${value}` : `Cross-reference to \u201C${value}\u201D`;
|
|
3076
|
+
function crossReferenceXml(bookmark, mode, value) {
|
|
3077
|
+
return `<w:hyperlink w:anchor="${esc(bookmark)}" w:tooltip="${esc(
|
|
3078
|
+
refTooltip(value, mode)
|
|
3079
|
+
)}" w:history="1">${refFieldXml(refInstruction(bookmark, mode), value)}</w:hyperlink>`;
|
|
3080
|
+
}
|
|
3081
|
+
var refInstruction = (bookmark, mode) => mode === "number" ? `REF ${bookmark} \\r \\h` : `REF ${bookmark} \\h`;
|
|
3082
|
+
var refResult = (target, mode) => mode === "number" && target.number ? target.number : target.text;
|
|
3083
|
+
var allBookmarks = (port) => new Set(port.paragraphs().flatMap((p) => p.bookmarks));
|
|
3084
|
+
function ensureTargetBookmark(port, target, mode) {
|
|
3085
|
+
const paragraph = port.paragraphs().find((p) => p.id === target.paragraphId);
|
|
3086
|
+
if (!paragraph) throw new Error("That clause is no longer in the document.");
|
|
3087
|
+
const existing = paragraph.bookmarks.find((b) => b.startsWith(REF_BOOKMARK_PREFIX));
|
|
3088
|
+
if (existing && mode === "number") return existing;
|
|
3089
|
+
const name = newRefBookmark(allBookmarks(port));
|
|
3090
|
+
const start = `<w:bookmarkStart w:id="0" w:name="${name}"/>`;
|
|
3091
|
+
const end = '<w:bookmarkEnd w:id="0"/>';
|
|
3092
|
+
if (mode === "number") {
|
|
3093
|
+
port.setCaret({ paragraphId: paragraph.id, offset: 0 });
|
|
3094
|
+
if (!port.paste(`<w:p>${start}${end}</w:p>`, "", false))
|
|
3095
|
+
throw new Error("The cross-reference target could not be marked.");
|
|
3096
|
+
return name;
|
|
3384
3097
|
}
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3098
|
+
const runs = port.runsXml(paragraph.id);
|
|
3099
|
+
if (!runs) throw new Error("This heading cannot be referred to by its text; use its number.");
|
|
3100
|
+
port.deleteRange(
|
|
3101
|
+
{ paragraphId: paragraph.id, offset: 0 },
|
|
3102
|
+
{ paragraphId: paragraph.id, offset: paragraph.text.length }
|
|
3103
|
+
);
|
|
3104
|
+
if (!port.paste(`<w:p>${start}${runs}${end}</w:p>`, paragraph.text, false))
|
|
3105
|
+
throw new Error("The cross-reference target could not be marked.");
|
|
3106
|
+
return name;
|
|
3107
|
+
}
|
|
3108
|
+
function pastePoint(port, caret) {
|
|
3109
|
+
const paragraph = port.paragraphs().find((p) => p.id === caret.paragraphId);
|
|
3110
|
+
if (paragraph?.text[caret.offset - 1] !== FIELD_ATOM) return caret;
|
|
3111
|
+
port.setCaret(caret);
|
|
3112
|
+
port.typeText(" ");
|
|
3113
|
+
return { paragraphId: caret.paragraphId, offset: caret.offset + 1 };
|
|
3114
|
+
}
|
|
3115
|
+
function markCrossReferenceTarget(port, target, mode) {
|
|
3116
|
+
if (mode === "number" && !target.number) mode = "text";
|
|
3117
|
+
const bookmark = ensureTargetBookmark(port, target, mode);
|
|
3118
|
+
return { bookmark, mode, value: refResult(target, mode) };
|
|
3119
|
+
}
|
|
3120
|
+
function insertCrossReferenceField(port, { bookmark, mode, value }, caret) {
|
|
3121
|
+
caret = pastePoint(port, caret);
|
|
3122
|
+
port.setCaret(caret);
|
|
3123
|
+
if (!port.paste(`<w:p>${crossReferenceXml(bookmark, mode, value)}</w:p>`, value, false))
|
|
3124
|
+
throw new Error("The cross-reference could not be inserted.");
|
|
3125
|
+
port.setCaret({ paragraphId: caret.paragraphId, offset: caret.offset + 1 });
|
|
3126
|
+
return { bookmark };
|
|
3127
|
+
}
|
|
3128
|
+
function insertCrossReference(port, target, mode, caret) {
|
|
3129
|
+
return insertCrossReferenceField(port, markCrossReferenceTarget(port, target, mode), caret);
|
|
3130
|
+
}
|
|
3131
|
+
var documentReferenceTooltip = (text, appName = "ARBI") => `Cross-reference: ${text} \u2014 opens in ${appName}`;
|
|
3132
|
+
function insertDocumentReference(port, reference, caret) {
|
|
3133
|
+
const text = reference.text.trim();
|
|
3134
|
+
if (!text) throw new Error("Enter the reference text.");
|
|
3135
|
+
const tooltip = documentReferenceTooltip(text, reference.appName).slice(0, 255);
|
|
3136
|
+
caret = pastePoint(port, caret);
|
|
3137
|
+
port.setCaret(caret);
|
|
3138
|
+
const link = `<w:p><w:hyperlink r:id="rIdRef1"${tooltip ? ` w:tooltip="${esc(tooltip)}"` : ""} w:history="1">${run(text, CROSS_REFERENCE_STYLE)}</w:hyperlink></w:p>`;
|
|
3139
|
+
if (!port.paste(link, text, false, hyperlinkRelationship("rIdRef1", reference.url)))
|
|
3140
|
+
throw new Error("The reference could not be inserted.");
|
|
3141
|
+
port.setCaret({ paragraphId: caret.paragraphId, offset: caret.offset + text.length });
|
|
3142
|
+
}
|
|
3143
|
+
var REF_FIELD = /^REF\s+(\S+)((?:\s+\\[A-Za-z*]+(?:\s+MERGEFORMAT)?)*)\s*$/i;
|
|
3144
|
+
function staleCrossReferences(port) {
|
|
3145
|
+
const paragraphs = port.paragraphs();
|
|
3146
|
+
if (!paragraphs.some((p) => p.fields?.length)) return [];
|
|
3147
|
+
const items = port.listItems();
|
|
3148
|
+
const order = paragraphs.map((p) => p.id);
|
|
3149
|
+
const owner = /* @__PURE__ */ new Map();
|
|
3150
|
+
for (const p of paragraphs) for (const b of p.bookmarks) if (!owner.has(b)) owner.set(b, p.id);
|
|
3151
|
+
const out = [];
|
|
3152
|
+
for (const p of paragraphs)
|
|
3153
|
+
for (const field of p.fields ?? []) {
|
|
3154
|
+
const match = REF_FIELD.exec(field.instruction);
|
|
3155
|
+
if (!match || !match[1].startsWith(REF_BOOKMARK_PREFIX)) continue;
|
|
3156
|
+
const switches = match[2].toLowerCase();
|
|
3157
|
+
const mode = /\\[rwn]\b/.test(switches) ? "number" : "text";
|
|
3158
|
+
const target = owner.get(match[1]);
|
|
3159
|
+
const base = {
|
|
3160
|
+
paragraphId: p.id,
|
|
3161
|
+
offset: field.offset,
|
|
3162
|
+
instruction: field.instruction,
|
|
3163
|
+
cached: field.result,
|
|
3164
|
+
bookmark: match[1],
|
|
3165
|
+
mode
|
|
3166
|
+
};
|
|
3167
|
+
if (!target) {
|
|
3168
|
+
out.push({ ...base, value: null });
|
|
3169
|
+
continue;
|
|
3446
3170
|
}
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
throw error;
|
|
3450
|
-
} finally {
|
|
3451
|
-
requireEdit(this.editor.setEditingMode("viewing"));
|
|
3171
|
+
const value = /\\[rwn]\b/.test(switches) ? switches.includes("\\r") ? paragraphNumber(order, items, port.levelOf, target) : null : (paragraphs.find((x) => x.id === target)?.text ?? "").replace(//g, "").trim();
|
|
3172
|
+
if (value && value !== field.result.trim()) out.push({ ...base, value });
|
|
3452
3173
|
}
|
|
3174
|
+
return out;
|
|
3175
|
+
}
|
|
3176
|
+
function brokenCrossReferences(port) {
|
|
3177
|
+
return port.paragraphs().flatMap(
|
|
3178
|
+
(p) => (p.marks ?? []).filter((m) => m.styleId === BROKEN_CROSS_REFERENCE_STYLE).map((m) => ({ paragraphId: p.id, offset: m.offset, text: m.text }))
|
|
3179
|
+
);
|
|
3180
|
+
}
|
|
3181
|
+
function refreshCrossReferences(port) {
|
|
3182
|
+
const stale = staleCrossReferences(port).sort(
|
|
3183
|
+
(a, b) => a.paragraphId === b.paragraphId ? b.offset - a.offset : 0
|
|
3184
|
+
);
|
|
3185
|
+
let refreshed = 0;
|
|
3186
|
+
for (const ref of stale) {
|
|
3187
|
+
const paragraph = port.paragraphs().find((p) => p.id === ref.paragraphId);
|
|
3188
|
+
if (!paragraph || paragraph.text[ref.offset - 1] === FIELD_ATOM) continue;
|
|
3189
|
+
const at = { paragraphId: ref.paragraphId, offset: ref.offset };
|
|
3190
|
+
port.deleteRange(at, { paragraphId: ref.paragraphId, offset: ref.offset + 1 });
|
|
3191
|
+
port.setCaret(at);
|
|
3192
|
+
const text = ref.value ?? (ref.cached.trim() || ref.bookmark);
|
|
3193
|
+
const body = ref.value === null ? run(text, BROKEN_CROSS_REFERENCE_STYLE) : crossReferenceXml(ref.bookmark, ref.mode, ref.value);
|
|
3194
|
+
if (port.paste(`<w:p>${body}</w:p>`, text, false) && ref.value !== null) refreshed++;
|
|
3453
3195
|
}
|
|
3454
|
-
};
|
|
3196
|
+
return { refreshed, broken: brokenCrossReferences(port) };
|
|
3197
|
+
}
|
|
3455
3198
|
|
|
3456
|
-
// src/docx/
|
|
3457
|
-
function
|
|
3458
|
-
const
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
used += moreWidth;
|
|
3462
|
-
const order = items.map((item, index) => ({ item, index })).sort((a, b) => a.item.priority - b.item.priority || b.index - a.index);
|
|
3463
|
-
for (const { item } of order) {
|
|
3464
|
-
if (used <= available) break;
|
|
3465
|
-
collapsed.add(item.id);
|
|
3466
|
-
used -= item.width;
|
|
3467
|
-
}
|
|
3468
|
-
return collapsed;
|
|
3199
|
+
// src/docx/references/passages.ts
|
|
3200
|
+
function shortTitle(title) {
|
|
3201
|
+
const base = title.replace(/\.[A-Za-z0-9]{1,5}$/, "").trim();
|
|
3202
|
+
const defined = /\(\s*(?:the\s+)?["“'‘]([^"”'’]{1,30})["”'’]\s*\)/i.exec(base);
|
|
3203
|
+
return defined ? defined[1] : base;
|
|
3469
3204
|
}
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
const row = useRef(null);
|
|
3475
|
-
const items = useRef(/* @__PURE__ */ new Map());
|
|
3476
|
-
const widths = useRef(/* @__PURE__ */ new Map());
|
|
3477
|
-
const [version, setVersion] = useState(0);
|
|
3478
|
-
const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
|
|
3479
|
-
const [slots, setSlots] = useState({});
|
|
3480
|
-
const [menu, setMenu] = useState([]);
|
|
3481
|
-
const register = useCallback((id, item) => {
|
|
3482
|
-
items.current.set(id, item);
|
|
3483
|
-
setVersion((v) => v + 1);
|
|
3484
|
-
return () => {
|
|
3485
|
-
if (items.current.get(id) === item) items.current.delete(id);
|
|
3486
|
-
setVersion((v) => v + 1);
|
|
3487
|
-
};
|
|
3488
|
-
}, []);
|
|
3489
|
-
const plan = useCallback(() => {
|
|
3490
|
-
const element = row.current;
|
|
3491
|
-
if (!element) return;
|
|
3492
|
-
const gap = parseFloat(getComputedStyle(element).columnGap) || 0;
|
|
3493
|
-
const ordered = Array.from(element.querySelectorAll("[data-ribbon-item]")).map((node) => node.dataset.ribbonItem).filter((id) => items.current.has(id));
|
|
3494
|
-
const measured = ordered.map((id) => {
|
|
3495
|
-
const { element: node, priority } = items.current.get(id);
|
|
3496
|
-
if (!node.hidden) {
|
|
3497
|
-
const width = node.getBoundingClientRect().width;
|
|
3498
|
-
widths.current.set(id, width ? width + gap : 0);
|
|
3499
|
-
}
|
|
3500
|
-
return { id, priority, width: widths.current.get(id) ?? 0 };
|
|
3501
|
-
});
|
|
3502
|
-
const next = planOverflow(measured, element.clientWidth, MORE_WIDTH);
|
|
3503
|
-
setCollapsed((current) => sameSet(current, next) ? current : next);
|
|
3504
|
-
const entries = ordered.filter((id) => next.has(id)).map((id) => ({ id, label: items.current.get(id).label }));
|
|
3505
|
-
setMenu(
|
|
3506
|
-
(current) => current.length === entries.length && current.every((entry, i) => entry.id === entries[i].id && entry.label === entries[i].label) ? current : entries
|
|
3507
|
-
);
|
|
3508
|
-
}, []);
|
|
3509
|
-
useLayoutEffect(plan, [plan, version, tab, collapsed]);
|
|
3510
|
-
useLayoutEffect(() => {
|
|
3511
|
-
const element = row.current;
|
|
3512
|
-
if (!element) return;
|
|
3513
|
-
const observer = new ResizeObserver(() => plan());
|
|
3514
|
-
observer.observe(element);
|
|
3515
|
-
for (const item of items.current.values()) observer.observe(item.element);
|
|
3516
|
-
return () => observer.disconnect();
|
|
3517
|
-
}, [plan, version]);
|
|
3518
|
-
const slotRef = useCallback(
|
|
3519
|
-
(id) => (node) => setSlots((current) => {
|
|
3520
|
-
if (node ? current[id] === node : !(id in current)) return current;
|
|
3521
|
-
const next = { ...current };
|
|
3522
|
-
if (node) next[id] = node;
|
|
3523
|
-
else delete next[id];
|
|
3524
|
-
return next;
|
|
3525
|
-
}),
|
|
3526
|
-
[]
|
|
3205
|
+
function passageClause(text) {
|
|
3206
|
+
const first = text.trim().split("\n")[0] ?? "";
|
|
3207
|
+
const match = /^(?:#+\s*)?(?:\*\*)?(?:clause|section|article)?\s*(\d+(?:\.\d+)*)[.)]?(?:\*\*)?\s+\S/i.exec(
|
|
3208
|
+
first
|
|
3527
3209
|
);
|
|
3528
|
-
|
|
3529
|
-
const shown = menu.filter((entry) => collapsed.has(entry.id));
|
|
3530
|
-
return /* @__PURE__ */ jsx(Overflow.Provider, { value: context, children: /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon-row", ref: row, "data-testid": "docx-ribbon-row", children: [
|
|
3531
|
-
children,
|
|
3532
|
-
shown.length > 0 && /* @__PURE__ */ jsx(
|
|
3533
|
-
RibbonMenu,
|
|
3534
|
-
{
|
|
3535
|
-
label: "More",
|
|
3536
|
-
title: "More commands",
|
|
3537
|
-
icon: /* @__PURE__ */ jsx(Ellipsis, { size: 16 }),
|
|
3538
|
-
testId: "docx-ribbon-more",
|
|
3539
|
-
keepMounted: true,
|
|
3540
|
-
popoverClassName: "native-docx-ribbon-more",
|
|
3541
|
-
children: (close) => /* @__PURE__ */ jsx(MoreRows, { entries: shown, slotRef, close })
|
|
3542
|
-
},
|
|
3543
|
-
tab
|
|
3544
|
-
)
|
|
3545
|
-
] }) });
|
|
3210
|
+
return match ? match[1] : null;
|
|
3546
3211
|
}
|
|
3547
|
-
function
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
const refFor = (id) => {
|
|
3554
|
-
let ref = refs.current.get(id);
|
|
3555
|
-
if (!ref) refs.current.set(id, ref = slotRef(id));
|
|
3556
|
-
return ref;
|
|
3557
|
-
};
|
|
3558
|
-
const list = useRef(null);
|
|
3559
|
-
const latest = useRef(close);
|
|
3560
|
-
latest.current = close;
|
|
3561
|
-
useEffect(() => {
|
|
3562
|
-
const element = list.current;
|
|
3563
|
-
if (!element) return;
|
|
3564
|
-
const onClick = (event) => {
|
|
3565
|
-
const target = event.target;
|
|
3566
|
-
const row = target.closest(".native-docx-ribbon-more-row");
|
|
3567
|
-
if (!row) return;
|
|
3568
|
-
const control = row.querySelector(".native-docx-ribbon-more-control");
|
|
3569
|
-
if (!control.contains(target)) {
|
|
3570
|
-
control.querySelector("button:not([disabled])")?.click();
|
|
3571
|
-
return;
|
|
3572
|
-
}
|
|
3573
|
-
const button = target.closest("button");
|
|
3574
|
-
if (button && !button.disabled && !control.querySelector(".native-docx-ribbon-popover")?.contains(button) && !button.hasAttribute("aria-expanded") && !button.hasAttribute("aria-haspopup"))
|
|
3575
|
-
latest.current();
|
|
3576
|
-
};
|
|
3577
|
-
element.addEventListener("click", onClick, true);
|
|
3578
|
-
return () => element.removeEventListener("click", onClick, true);
|
|
3579
|
-
}, []);
|
|
3580
|
-
return /* @__PURE__ */ jsx("div", { className: "native-docx-ribbon-more-list", ref: list, children: entries.map((entry) => /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon-more-row", "data-ribbon-more": entry.id, children: [
|
|
3581
|
-
/* @__PURE__ */ jsx("span", { className: "native-docx-ribbon-more-control", ref: refFor(entry.id) }),
|
|
3582
|
-
/* @__PURE__ */ jsx("span", { className: "native-docx-ribbon-more-label", children: entry.label })
|
|
3583
|
-
] }, entry.id)) });
|
|
3212
|
+
function referenceText(title, passage) {
|
|
3213
|
+
const name = shortTitle(title);
|
|
3214
|
+
const clause = passageClause(passage.text);
|
|
3215
|
+
if (clause) return `${name}, clause ${clause}`;
|
|
3216
|
+
if (passage.page) return `${name}, p. ${passage.page}`;
|
|
3217
|
+
return name;
|
|
3584
3218
|
}
|
|
3585
|
-
function
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
priority = 0,
|
|
3589
|
-
children
|
|
3590
|
-
}) {
|
|
3591
|
-
const context = useContext(Overflow);
|
|
3592
|
-
const element = useRef(null);
|
|
3593
|
-
const register = context?.register;
|
|
3594
|
-
useLayoutEffect(() => {
|
|
3595
|
-
if (!register || !element.current) return;
|
|
3596
|
-
return register(id, { label, priority, element: element.current });
|
|
3597
|
-
}, [register, id, label, priority]);
|
|
3598
|
-
if (!context) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
3599
|
-
const collapsed = context.collapsed.has(id);
|
|
3600
|
-
const slot = context.slots[id];
|
|
3601
|
-
return /* @__PURE__ */ jsx(
|
|
3602
|
-
"span",
|
|
3603
|
-
{
|
|
3604
|
-
ref: element,
|
|
3605
|
-
className: "native-docx-ribbon-item",
|
|
3606
|
-
"data-ribbon-item": id,
|
|
3607
|
-
hidden: collapsed || void 0,
|
|
3608
|
-
children: collapsed ? slot ? createPortal(children, slot) : null : children
|
|
3609
|
-
}
|
|
3610
|
-
);
|
|
3219
|
+
function passageExcerpt(text, max = 280) {
|
|
3220
|
+
const plain = text.replace(/[#*_`>|]+/g, " ").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\s+/g, " ").trim();
|
|
3221
|
+
return plain.length > max ? `${plain.slice(0, max - 1)}\u2026` : plain;
|
|
3611
3222
|
}
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
function findStyle(options, name) {
|
|
3616
|
-
const wanted = name.toLowerCase();
|
|
3617
|
-
const id = name.replace(/(^|\s)(\w)/g, (_, _s, c) => c.toUpperCase());
|
|
3618
|
-
return options.find((o) => o.name.toLowerCase() === wanted) ?? options.find((o) => o.styleId.toLowerCase() === id.toLowerCase());
|
|
3223
|
+
function filterPassages(passages, query) {
|
|
3224
|
+
const q = query.trim().toLowerCase();
|
|
3225
|
+
return q ? passages.filter((p) => p.text.toLowerCase().includes(q)) : [...passages];
|
|
3619
3226
|
}
|
|
3620
|
-
|
|
3621
|
-
|
|
3227
|
+
var RUN_PARTS = /* @__PURE__ */ new Set(["rPr", "t", "tab", "br", "lastRenderedPageBreak"]);
|
|
3228
|
+
var SKIPPED = /* @__PURE__ */ new Set(["pPr", "bookmarkStart", "bookmarkEnd", "proofErr", "permStart", "permEnd"]);
|
|
3229
|
+
function serialize(node) {
|
|
3230
|
+
if (node.kind === "textValue") return esc(node.value ?? "");
|
|
3231
|
+
const name = `${node.prefix ?? "w"}:${node.localName}`;
|
|
3232
|
+
const attrs = (node.attributes ?? []).map((a) => ` ${a.prefix ? `${a.prefix}:` : ""}${a.localName}="${esc(a.value)}"`).join("");
|
|
3233
|
+
const children = (node.children ?? []).map((c) => serialize(c)).join("");
|
|
3234
|
+
return children ? `<${name}${attrs}>${children}</${name}>` : `<${name}${attrs}/>`;
|
|
3622
3235
|
}
|
|
3623
|
-
function
|
|
3624
|
-
const
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
case "Digit3":
|
|
3639
|
-
case "Numpad3":
|
|
3640
|
-
return "heading 3";
|
|
3641
|
-
}
|
|
3236
|
+
function paragraphRunsXml(paragraph) {
|
|
3237
|
+
const runs = [];
|
|
3238
|
+
for (const child of paragraph.children ?? []) {
|
|
3239
|
+
if (child.localName && SKIPPED.has(child.localName)) continue;
|
|
3240
|
+
if (child.localName !== "r") return null;
|
|
3241
|
+
if ((child.children ?? []).some((c) => !c.localName || !RUN_PARTS.has(c.localName))) return null;
|
|
3242
|
+
runs.push(serialize(child));
|
|
3243
|
+
}
|
|
3244
|
+
return runs.length ? runs.join("") : null;
|
|
3245
|
+
}
|
|
3246
|
+
function findNode2(node, id) {
|
|
3247
|
+
if (node.id === id) return node;
|
|
3248
|
+
for (const child of node.children ?? []) {
|
|
3249
|
+
const found = findNode2(child, id);
|
|
3250
|
+
if (found) return found;
|
|
3642
3251
|
}
|
|
3643
|
-
|
|
3644
|
-
return null;
|
|
3645
|
-
}
|
|
3646
|
-
function divergenceSummary(divergence) {
|
|
3647
|
-
if (!divergence) return null;
|
|
3648
|
-
const changed = divergence.styles.filter((s) => s.kind === "changed").map((s) => styleLabel(s.name));
|
|
3649
|
-
const missing = divergence.styles.filter((s) => s.kind === "missing").map((s) => styleLabel(s.name));
|
|
3650
|
-
const parts = [
|
|
3651
|
-
divergence.docDefaults ? "default text formatting" : "",
|
|
3652
|
-
changed.length ? `${changed.length === 1 ? "style" : "styles"} ${changed.slice(0, 6).join(", ")}${changed.length > 6 ? "\u2026" : ""}` : "",
|
|
3653
|
-
missing.length ? `${missing.length} house ${missing.length === 1 ? "style" : "styles"} missing` : ""
|
|
3654
|
-
].filter(Boolean);
|
|
3655
|
-
return parts.length ? `Differs from the house template: ${parts.join("; ")}.` : null;
|
|
3252
|
+
return void 0;
|
|
3656
3253
|
}
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
const
|
|
3254
|
+
function referencesPort(editor, revision) {
|
|
3255
|
+
const base = editorPort(editor, revision);
|
|
3256
|
+
const session = () => {
|
|
3257
|
+
const s = editor.surface?.session;
|
|
3258
|
+
if (!s) throw new Error("The document is not open.");
|
|
3259
|
+
return s;
|
|
3260
|
+
};
|
|
3261
|
+
const root = () => session().part().root;
|
|
3262
|
+
let numbering;
|
|
3263
|
+
let cascade;
|
|
3264
|
+
const index = () => {
|
|
3265
|
+
if (!numbering) {
|
|
3266
|
+
const s = session();
|
|
3267
|
+
cascade = buildStyleCascadeTable(s.stylesRoot(), void 0, s.settingsRoot());
|
|
3268
|
+
numbering = withNumberingStyleLinks(buildNumberingIndex(s.numberingRoot()), cascade);
|
|
3269
|
+
}
|
|
3270
|
+
return numbering;
|
|
3271
|
+
};
|
|
3660
3272
|
return {
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3273
|
+
...base,
|
|
3274
|
+
listItems: () => {
|
|
3275
|
+
const body = root().children?.find((n) => n.kind === "body");
|
|
3276
|
+
const items = resolveStoryListItems(
|
|
3277
|
+
body?.children ?? [],
|
|
3278
|
+
index(),
|
|
3279
|
+
cascade
|
|
3280
|
+
);
|
|
3281
|
+
const out = /* @__PURE__ */ new Map();
|
|
3282
|
+
for (const [id, item] of items)
|
|
3283
|
+
out.set(id, {
|
|
3284
|
+
numId: item.numId,
|
|
3285
|
+
ilvl: item.ilvl,
|
|
3286
|
+
markerText: item.markerText,
|
|
3287
|
+
numFmt: item.numFmt
|
|
3288
|
+
});
|
|
3289
|
+
return out;
|
|
3290
|
+
},
|
|
3291
|
+
levelOf: (numId, ilvl) => {
|
|
3292
|
+
const level = resolveNumberingLevel(index(), numId, ilvl)?.level;
|
|
3293
|
+
return level ? { lvlText: level.lvlText, numFmt: level.numFmt } : null;
|
|
3294
|
+
},
|
|
3295
|
+
runsXml: (paragraphId) => {
|
|
3296
|
+
const node = findNode2(root(), paragraphId);
|
|
3297
|
+
return node ? paragraphRunsXml(node) : null;
|
|
3298
|
+
}
|
|
3667
3299
|
};
|
|
3668
3300
|
}
|
|
3669
|
-
|
|
3670
|
-
|
|
3301
|
+
var DEFAULT_CROSS_REFERENCES_LABELS = {
|
|
3302
|
+
insert: "Insert cross-reference",
|
|
3303
|
+
title: "Insert cross-reference",
|
|
3304
|
+
description: "Refer to a clause of this document, or to a passage of another document.",
|
|
3305
|
+
thisDocument: "This document",
|
|
3306
|
+
otherDocument: "Another document",
|
|
3307
|
+
filterTargets: "Find a clause or heading",
|
|
3308
|
+
noTargets: "This document has no numbered clauses or headings yet.",
|
|
3309
|
+
number: "Paragraph number",
|
|
3310
|
+
text: "Heading text",
|
|
3311
|
+
insertAs: "Insert",
|
|
3312
|
+
chooseDocument: "Choose a document",
|
|
3313
|
+
filterPassages: "Find a passage",
|
|
3314
|
+
loadingPassages: "Loading passages\u2026",
|
|
3315
|
+
noPassages: "No passage matches.",
|
|
3316
|
+
referenceText: "Reference text",
|
|
3317
|
+
insertButton: "Insert",
|
|
3318
|
+
cancel: "Cancel",
|
|
3319
|
+
clause: (number) => `Clause ${number}`,
|
|
3320
|
+
heading: "Heading",
|
|
3321
|
+
update: "Update cross-references",
|
|
3322
|
+
updated: "Cross-references updated",
|
|
3323
|
+
updateSummary: (refreshed) => refreshed ? `${refreshed} cross-reference${refreshed === 1 ? "" : "s"} brought up to date.` : "Every cross-reference is up to date.",
|
|
3324
|
+
brokenTitle: "Broken cross-references",
|
|
3325
|
+
brokenNone: "No cross-reference points at a deleted target.",
|
|
3326
|
+
brokenHelp: "Their target was deleted: the last text is kept and marked. Insert the reference again to repair it.",
|
|
3327
|
+
brokenCard: "Broken cross-reference",
|
|
3328
|
+
brokenCardBody: "Its target was deleted. Update cross-references to mark it.",
|
|
3329
|
+
show: "Show",
|
|
3330
|
+
close: "Close"
|
|
3331
|
+
};
|
|
3332
|
+
var CrossReferencesContext = createContext(null);
|
|
3333
|
+
var useCrossReferences = () => useContext(CrossReferencesContext);
|
|
3334
|
+
function CrossReferencesProvider({
|
|
3335
|
+
children,
|
|
3336
|
+
editable,
|
|
3337
|
+
references,
|
|
3338
|
+
documentId,
|
|
3339
|
+
notify,
|
|
3340
|
+
labels
|
|
3341
|
+
}) {
|
|
3671
3342
|
const editor = useDocxEditor();
|
|
3672
|
-
const
|
|
3673
|
-
const
|
|
3674
|
-
|
|
3343
|
+
const track = useTrackedOrNoted();
|
|
3344
|
+
const l = useMemo(() => ({ ...DEFAULT_CROSS_REFERENCES_LABELS, ...labels }), [labels]);
|
|
3345
|
+
const [dialog, setDialog] = useState(
|
|
3346
|
+
null
|
|
3675
3347
|
);
|
|
3676
|
-
const
|
|
3677
|
-
const
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
)
|
|
3681
|
-
|
|
3348
|
+
const marker = useRef(null);
|
|
3349
|
+
const [viewer, setViewer] = useState(null);
|
|
3350
|
+
useEffect(() => setViewer(marker.current?.closest(".native-docx-viewer") ?? null), []);
|
|
3351
|
+
const openDialog = useCallback(() => {
|
|
3352
|
+
if (!editor || !editable) return;
|
|
3353
|
+
try {
|
|
3354
|
+
const port = referencesPort(editor);
|
|
3355
|
+
const caret = port.caret();
|
|
3356
|
+
if (!caret) return notify?.("Place the cursor where the reference should go.", "error");
|
|
3357
|
+
setDialog({ caret, targets: listTargets(port.paragraphs(), port.listItems(), port.levelOf) });
|
|
3358
|
+
} catch (error) {
|
|
3359
|
+
notify?.(error instanceof Error ? error.message : "Could not read the document.", "error");
|
|
3360
|
+
}
|
|
3361
|
+
}, [editor, editable, notify]);
|
|
3362
|
+
const run2 = useCallback(
|
|
3363
|
+
(label, prepare, insert) => {
|
|
3364
|
+
if (!editor) return;
|
|
3365
|
+
try {
|
|
3366
|
+
const prepared = withDirectEditing(editor, prepare);
|
|
3367
|
+
if (dialog) referencesPort(editor).setCaret(dialog.caret);
|
|
3368
|
+
track({ label, insert: (revision) => insert(prepared, revision), inserted: () => true });
|
|
3369
|
+
setDialog(null);
|
|
3370
|
+
editor.surface?.focus();
|
|
3371
|
+
holdCaret(editor);
|
|
3372
|
+
} catch (error) {
|
|
3373
|
+
notify?.(
|
|
3374
|
+
error instanceof Error ? error.message : "Could not insert the reference.",
|
|
3375
|
+
"error"
|
|
3376
|
+
);
|
|
3377
|
+
}
|
|
3378
|
+
},
|
|
3379
|
+
[editor, dialog, track, notify]
|
|
3682
3380
|
);
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3381
|
+
useEffect(() => {
|
|
3382
|
+
if (!editor || !editable) return;
|
|
3383
|
+
const refresh = () => {
|
|
3384
|
+
try {
|
|
3385
|
+
const port = referencesPort(editor);
|
|
3386
|
+
if (!port.paragraphs().some((p) => p.fields?.length)) return;
|
|
3387
|
+
preservingCaret(editor, () => withDirectEditing(editor, () => refreshCrossReferences(port)));
|
|
3388
|
+
} catch {
|
|
3389
|
+
}
|
|
3390
|
+
};
|
|
3391
|
+
const off = onBeforeSave(editor, refresh);
|
|
3392
|
+
const timer = setTimeout(refresh, 1500);
|
|
3393
|
+
return () => {
|
|
3394
|
+
off();
|
|
3395
|
+
clearTimeout(timer);
|
|
3396
|
+
};
|
|
3397
|
+
}, [editor, editable]);
|
|
3398
|
+
const [report, setReport] = useState(null);
|
|
3399
|
+
const update = useCallback(() => {
|
|
3400
|
+
if (!editor || !editable) return;
|
|
3401
|
+
try {
|
|
3402
|
+
const result = preservingCaret(
|
|
3403
|
+
editor,
|
|
3404
|
+
() => withDirectEditing(editor, () => refreshCrossReferences(referencesPort(editor)))
|
|
3405
|
+
);
|
|
3406
|
+
setReport(result);
|
|
3407
|
+
} catch (error) {
|
|
3408
|
+
notify?.(
|
|
3409
|
+
error instanceof Error ? error.message : "Could not update cross-references.",
|
|
3410
|
+
"error"
|
|
3411
|
+
);
|
|
3412
|
+
}
|
|
3413
|
+
}, [editor, editable, notify]);
|
|
3414
|
+
const [passages, setPassages] = useState({});
|
|
3415
|
+
const loadPassages = useCallback(
|
|
3416
|
+
(id) => {
|
|
3417
|
+
if (!references || passages[id]) return;
|
|
3418
|
+
setPassages((p) => ({ ...p, [id]: "loading" }));
|
|
3419
|
+
references.passages(id).then((list) => setPassages((p) => ({ ...p, [id]: list }))).catch(() => setPassages((p) => ({ ...p, [id]: [] })));
|
|
3420
|
+
},
|
|
3421
|
+
[references, passages]
|
|
3422
|
+
);
|
|
3423
|
+
useEffect(() => {
|
|
3424
|
+
if (!viewer || !references) return;
|
|
3425
|
+
const targetOf = (event) => {
|
|
3426
|
+
const link = event.target?.closest?.(
|
|
3427
|
+
'a.docx-hyperlink[data-docx-link-kind="external"]'
|
|
3428
|
+
);
|
|
3429
|
+
if (!link || !viewer.contains(link)) return null;
|
|
3430
|
+
const mouse = event;
|
|
3431
|
+
if (mouse.button !== 0 || mouse.ctrlKey || mouse.metaKey || mouse.shiftKey) return null;
|
|
3432
|
+
return references.targetOf(link.getAttribute("href") ?? "");
|
|
3433
|
+
};
|
|
3434
|
+
const take = (event) => {
|
|
3435
|
+
const target = targetOf(event);
|
|
3436
|
+
if (!target) return;
|
|
3437
|
+
event.preventDefault();
|
|
3438
|
+
event.stopPropagation();
|
|
3439
|
+
if (event.type === "click") references.open(target);
|
|
3440
|
+
};
|
|
3441
|
+
const types = ["pointerdown", "mousedown", "pointerup", "mouseup", "click"];
|
|
3442
|
+
for (const type of types) window.addEventListener(type, take, true);
|
|
3443
|
+
return () => {
|
|
3444
|
+
for (const type of types) window.removeEventListener(type, take, true);
|
|
3445
|
+
};
|
|
3446
|
+
}, [viewer, references]);
|
|
3447
|
+
const value = useMemo(
|
|
3448
|
+
() => editable ? { openDialog, update, labels: l } : null,
|
|
3449
|
+
[editable, openDialog, update, l]
|
|
3450
|
+
);
|
|
3451
|
+
const resolveInternal = (href) => {
|
|
3452
|
+
if (!editor) return null;
|
|
3453
|
+
const name = href.replace(/^#/, "");
|
|
3454
|
+
try {
|
|
3455
|
+
const port = referencesPort(editor);
|
|
3456
|
+
const paragraphs = port.paragraphs();
|
|
3457
|
+
const target = paragraphs.find((p) => p.bookmarks.includes(name));
|
|
3458
|
+
if (!target) return { title: l.brokenCard, body: l.brokenCardBody };
|
|
3459
|
+
const number = paragraphNumber(
|
|
3460
|
+
paragraphs.map((p) => p.id),
|
|
3461
|
+
port.listItems(),
|
|
3462
|
+
port.levelOf,
|
|
3463
|
+
target.id
|
|
3464
|
+
);
|
|
3465
|
+
return {
|
|
3466
|
+
title: number ? l.clause(number) : l.heading,
|
|
3467
|
+
body: passageExcerpt(target.text.replace(//g, ""))
|
|
3468
|
+
};
|
|
3469
|
+
} catch {
|
|
3470
|
+
return null;
|
|
3471
|
+
}
|
|
3693
3472
|
};
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
resetKey: query
|
|
3700
|
-
});
|
|
3701
|
-
return /* @__PURE__ */ jsxs("div", { className: "native-docx-style-gallery", "data-testid": "docx-style-gallery", children: [
|
|
3702
|
-
quick.length > 0 && /* @__PURE__ */ jsx("div", { className: "native-docx-style-quick", role: "group", "aria-label": "Quick styles", children: quick.map((option) => /* @__PURE__ */ jsx(
|
|
3703
|
-
"button",
|
|
3704
|
-
{
|
|
3705
|
-
type: "button",
|
|
3706
|
-
"aria-pressed": value === option.styleId,
|
|
3707
|
-
disabled: !isEnabled,
|
|
3708
|
-
"data-style-id": option.styleId,
|
|
3709
|
-
title: styleLabel(option.name),
|
|
3710
|
-
onClick: () => apply(option),
|
|
3711
|
-
children: /* @__PURE__ */ jsx("span", { style: previewStyle(option), children: styleLabel(option.name) })
|
|
3712
|
-
},
|
|
3713
|
-
option.styleId
|
|
3714
|
-
)) }),
|
|
3715
|
-
options.length > FILTER_FROM && /* @__PURE__ */ jsx(
|
|
3716
|
-
"input",
|
|
3473
|
+
return /* @__PURE__ */ jsxs(CrossReferencesContext.Provider, { value, children: [
|
|
3474
|
+
/* @__PURE__ */ jsx("span", { ref: marker, hidden: true }),
|
|
3475
|
+
children,
|
|
3476
|
+
/* @__PURE__ */ jsx(
|
|
3477
|
+
LinkHoverCard,
|
|
3717
3478
|
{
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
autoFocus: true,
|
|
3723
|
-
onChange: (event) => setQuery(event.target.value),
|
|
3724
|
-
onKeyDown: (event) => {
|
|
3725
|
-
nav.handleKeyDown(event);
|
|
3726
|
-
},
|
|
3727
|
-
...nav.comboboxProps
|
|
3479
|
+
root: viewer,
|
|
3480
|
+
matches: (href) => href.startsWith(`#${REF_BOOKMARK_PREFIX}`),
|
|
3481
|
+
resolve: resolveInternal,
|
|
3482
|
+
testId: "docx-cross-reference-card"
|
|
3728
3483
|
}
|
|
3729
3484
|
),
|
|
3730
|
-
/* @__PURE__ */
|
|
3731
|
-
|
|
3485
|
+
references && /* @__PURE__ */ jsx(
|
|
3486
|
+
LinkHoverCard,
|
|
3732
3487
|
{
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
onMouseEnter: () => nav.setSelectedIndex(index),
|
|
3748
|
-
onClick: () => isEnabled && apply(option),
|
|
3749
|
-
children: [
|
|
3750
|
-
/* @__PURE__ */ jsx("span", { style: previewStyle(option), children: styleLabel(option.name) }),
|
|
3751
|
-
changed.has(option.styleId) && /* @__PURE__ */ jsx(
|
|
3752
|
-
"span",
|
|
3753
|
-
{
|
|
3754
|
-
className: "native-docx-style-diverged",
|
|
3755
|
-
title: "Defined differently from the house template",
|
|
3756
|
-
"aria-label": "Defined differently from the house template"
|
|
3757
|
-
}
|
|
3758
|
-
)
|
|
3759
|
-
]
|
|
3760
|
-
},
|
|
3761
|
-
option.styleId
|
|
3762
|
-
)),
|
|
3763
|
-
shown.length === 0 && /* @__PURE__ */ jsx("li", { className: "native-docx-style-empty", children: "No matching style" })
|
|
3764
|
-
]
|
|
3488
|
+
root: viewer,
|
|
3489
|
+
matches: (href) => !!references.targetOf(href),
|
|
3490
|
+
resolve: (href) => {
|
|
3491
|
+
const target = references.targetOf(href);
|
|
3492
|
+
if (!target) return null;
|
|
3493
|
+
const list = passages[target.documentId];
|
|
3494
|
+
if (!list) loadPassages(target.documentId);
|
|
3495
|
+
const passage = Array.isArray(list) ? list.find((p) => p.chunkId === target.chunkId) : void 0;
|
|
3496
|
+
return passage ? {
|
|
3497
|
+
body: passageExcerpt(passage.text),
|
|
3498
|
+
footer: passage.page ? `p. ${passage.page}` : void 0
|
|
3499
|
+
} : null;
|
|
3500
|
+
},
|
|
3501
|
+
testId: "docx-cross-reference-card"
|
|
3765
3502
|
}
|
|
3766
|
-
)
|
|
3767
|
-
] });
|
|
3768
|
-
}
|
|
3769
|
-
function StyleGallery() {
|
|
3770
|
-
const { value, options } = useParagraphStyle();
|
|
3771
|
-
const current = options.find((o) => o.styleId === value);
|
|
3772
|
-
const name = current ? styleLabel(current.name) : "Styles";
|
|
3773
|
-
const house = useHouseTemplate();
|
|
3774
|
-
const diverged = house?.basedOnTemplate ? divergenceSummary(house.divergence) : null;
|
|
3775
|
-
return /* @__PURE__ */ jsx(
|
|
3776
|
-
RibbonMenu,
|
|
3777
|
-
{
|
|
3778
|
-
label: "Styles",
|
|
3779
|
-
title: diverged ? `Styles. ${diverged}` : void 0,
|
|
3780
|
-
className: "native-docx-style-trigger",
|
|
3781
|
-
testId: "docx-style-trigger",
|
|
3782
|
-
icon: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3783
|
-
/* @__PURE__ */ jsx("span", { className: "native-docx-style-current", "data-testid": "docx-current-style", children: name }),
|
|
3784
|
-
diverged && /* @__PURE__ */ jsx(
|
|
3785
|
-
"span",
|
|
3786
|
-
{
|
|
3787
|
-
className: "native-docx-style-diverged",
|
|
3788
|
-
"data-testid": "docx-style-diverged",
|
|
3789
|
-
"aria-label": diverged
|
|
3790
|
-
}
|
|
3791
|
-
),
|
|
3792
|
-
/* @__PURE__ */ jsx(ChevronDown, { size: 12, "aria-hidden": true })
|
|
3793
|
-
] }),
|
|
3794
|
-
children: (close) => /* @__PURE__ */ jsx(Gallery, { close })
|
|
3795
|
-
}
|
|
3796
|
-
);
|
|
3797
|
-
}
|
|
3798
|
-
var W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
3799
|
-
var DEFAULT_PARAGRAPH_FONT = {
|
|
3800
|
-
id: "DefaultParagraphFont",
|
|
3801
|
-
type: "character",
|
|
3802
|
-
xml: '<w:style w:type="character" w:default="1" w:styleId="DefaultParagraphFont"><w:name w:val="Default Paragraph Font"/><w:uiPriority w:val="1"/><w:semiHidden/><w:unhideWhenUsed/></w:style>'
|
|
3803
|
-
};
|
|
3804
|
-
function stylesOf(kind) {
|
|
3805
|
-
const Kind = kind === "footnote" ? "Footnote" : "Endnote";
|
|
3806
|
-
const text = `${Kind}Text`;
|
|
3807
|
-
return [
|
|
3808
|
-
{
|
|
3809
|
-
id: text,
|
|
3810
|
-
type: "paragraph",
|
|
3811
|
-
xml: `<w:style w:type="paragraph" w:styleId="${text}"><w:name w:val="${kind} text"/><w:basedOn w:val="Normal"/><w:link w:val="${text}Char"/><w:uiPriority w:val="99"/><w:semiHidden/><w:unhideWhenUsed/><w:pPr><w:spacing w:after="0" w:line="240" w:lineRule="auto"/></w:pPr><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr></w:style>`
|
|
3812
|
-
},
|
|
3813
|
-
{
|
|
3814
|
-
id: `${text}Char`,
|
|
3815
|
-
type: "character",
|
|
3816
|
-
xml: `<w:style w:type="character" w:customStyle="1" w:styleId="${text}Char"><w:name w:val="${Kind} Text Char"/><w:basedOn w:val="DefaultParagraphFont"/><w:link w:val="${text}"/><w:uiPriority w:val="99"/><w:semiHidden/><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr></w:style>`
|
|
3817
|
-
},
|
|
3818
|
-
{
|
|
3819
|
-
id: `${Kind}Reference`,
|
|
3820
|
-
type: "character",
|
|
3821
|
-
xml: `<w:style w:type="character" w:styleId="${Kind}Reference"><w:name w:val="${kind} reference"/><w:basedOn w:val="DefaultParagraphFont"/><w:uiPriority w:val="99"/><w:semiHidden/><w:unhideWhenUsed/><w:rPr><w:vertAlign w:val="superscript"/></w:rPr></w:style>`
|
|
3822
|
-
}
|
|
3823
|
-
];
|
|
3824
|
-
}
|
|
3825
|
-
function noteTextStyle(styles, kind) {
|
|
3826
|
-
const paragraph = styles.filter((style) => style.type === "paragraph");
|
|
3827
|
-
const id = kind === "footnote" ? "FootnoteText" : "EndnoteText";
|
|
3828
|
-
return paragraph.find((style) => style.name.toLowerCase() === `${kind} text`)?.styleId ?? paragraph.find((style) => style.styleId === id)?.styleId;
|
|
3829
|
-
}
|
|
3830
|
-
function missingNoteStyles(styles, kind) {
|
|
3831
|
-
const has = (type, id) => styles.some((style) => style.type === type && style.styleId === id);
|
|
3832
|
-
const text = noteTextStyle(styles, kind);
|
|
3833
|
-
const missing = stylesOf(kind).filter(
|
|
3834
|
-
(style) => style.type === "paragraph" ? !text : !has("character", style.id)
|
|
3835
|
-
);
|
|
3836
|
-
if (!missing.length) return [];
|
|
3837
|
-
const base = has("character", DEFAULT_PARAGRAPH_FONT.id) ? [] : [DEFAULT_PARAGRAPH_FONT];
|
|
3838
|
-
return [...base, ...missing].map((style) => style.xml);
|
|
3839
|
-
}
|
|
3840
|
-
var CONTENT_TYPES = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/></Types>';
|
|
3841
|
-
function stylesOnlyClipboardHtml(styles) {
|
|
3842
|
-
const fragment = zipSync({
|
|
3843
|
-
"[Content_Types].xml": strToU8(CONTENT_TYPES),
|
|
3844
|
-
"_rels/.rels": strToU8(
|
|
3845
|
-
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/></Relationships>'
|
|
3846
|
-
),
|
|
3847
|
-
"word/document.xml": strToU8(
|
|
3848
|
-
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3849
|
-
<w:document xmlns:w="${W}"><w:body><w:p/></w:body></w:document>`
|
|
3850
3503
|
),
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
),
|
|
3854
|
-
"word/styles.xml": strToU8(
|
|
3855
|
-
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3856
|
-
<w:styles xmlns:w="${W}">${styles.join("")}</w:styles>`
|
|
3857
|
-
)
|
|
3858
|
-
});
|
|
3859
|
-
return fragmentClipboardHtml(fragment, false);
|
|
3860
|
-
}
|
|
3861
|
-
function ensureNoteStyles(editor, kind, direct = (fn) => fn()) {
|
|
3862
|
-
if (!editor.can({ type: "insertNote", noteKind: kind }).ok) return void 0;
|
|
3863
|
-
const missing = missingNoteStyles(editor.getDocumentStyles(), kind);
|
|
3864
|
-
if (missing.length)
|
|
3865
|
-
direct(() => editor.exec({ type: "paste", text: "", html: stylesOnlyClipboardHtml(missing) }));
|
|
3866
|
-
return noteTextStyle(editor.getDocumentStyles(), kind);
|
|
3867
|
-
}
|
|
3868
|
-
function useInsertNote(kind) {
|
|
3869
|
-
const command = useEditorCommand(kind === "footnote" ? "insert.footnote" : "insert.endnote");
|
|
3870
|
-
const editor = useDocxEditor();
|
|
3871
|
-
const insert = () => {
|
|
3872
|
-
const text = editor && ensureNoteStyles(editor, kind, (fn) => withDirectEditing(editor, fn));
|
|
3873
|
-
if (!command.execute()) return false;
|
|
3874
|
-
if (editor) {
|
|
3875
|
-
if (text)
|
|
3876
|
-
withDirectEditing(editor, () => editor.exec({ type: "setParagraphStyle", styleId: text }));
|
|
3877
|
-
editor.exec({ type: "insertText", text: " " });
|
|
3878
|
-
}
|
|
3879
|
-
return true;
|
|
3880
|
-
};
|
|
3881
|
-
return { insert, isEnabled: command.isEnabled, disabledReason: command.disabledReason };
|
|
3882
|
-
}
|
|
3883
|
-
function NoteAction({
|
|
3884
|
-
kind,
|
|
3885
|
-
label,
|
|
3886
|
-
shortcut,
|
|
3887
|
-
icon
|
|
3888
|
-
}) {
|
|
3889
|
-
const note = useInsertNote(kind);
|
|
3890
|
-
return /* @__PURE__ */ jsx(RibbonItem, { id: `references-${kind}`, label, children: /* @__PURE__ */ jsx(
|
|
3891
|
-
DocxEditor.Toolbar.Action,
|
|
3892
|
-
{
|
|
3893
|
-
label: `${label} (${shortcut})`,
|
|
3894
|
-
icon,
|
|
3895
|
-
disabled: !note.isEnabled,
|
|
3896
|
-
disabledReason: note.disabledReason ?? void 0,
|
|
3897
|
-
onSelect: () => void note.insert(),
|
|
3898
|
-
className: `native-docx-insert-${kind}`
|
|
3899
|
-
}
|
|
3900
|
-
) });
|
|
3901
|
-
}
|
|
3902
|
-
function NoteButtons() {
|
|
3903
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3904
|
-
/* @__PURE__ */ jsx(
|
|
3905
|
-
NoteAction,
|
|
3504
|
+
dialog && /* @__PURE__ */ jsx(
|
|
3505
|
+
CrossReferenceDialog,
|
|
3906
3506
|
{
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3507
|
+
targets: dialog.targets,
|
|
3508
|
+
references,
|
|
3509
|
+
documentId,
|
|
3510
|
+
passages,
|
|
3511
|
+
loadPassages,
|
|
3512
|
+
labels: l,
|
|
3513
|
+
onCancel: () => setDialog(null),
|
|
3514
|
+
onClosed: () => editor?.surface?.focus(),
|
|
3515
|
+
onInsertTarget: (target, mode) => run2(
|
|
3516
|
+
`Inserted cross-reference: ${refResult(target, mode)}`,
|
|
3517
|
+
() => markCrossReferenceTarget(referencesPort(editor), target, mode),
|
|
3518
|
+
(marked, revision) => insertCrossReferenceField(referencesPort(editor, revision), marked, dialog.caret)
|
|
3519
|
+
),
|
|
3520
|
+
onInsertPassage: (target, text) => run2(
|
|
3521
|
+
`Inserted cross-reference: ${text}`,
|
|
3522
|
+
() => void 0,
|
|
3523
|
+
(_, revision) => insertDocumentReference(
|
|
3524
|
+
referencesPort(editor, revision),
|
|
3525
|
+
{ text, url: references.linkFor(target), appName: references.appName },
|
|
3526
|
+
dialog.caret
|
|
3527
|
+
)
|
|
3528
|
+
)
|
|
3911
3529
|
}
|
|
3912
3530
|
),
|
|
3913
|
-
/* @__PURE__ */ jsx(
|
|
3914
|
-
|
|
3531
|
+
/* @__PURE__ */ jsx(Dialog, { open: !!report, onOpenChange: (open) => !open && setReport(null), children: /* @__PURE__ */ jsxs(
|
|
3532
|
+
DialogContent,
|
|
3915
3533
|
{
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3534
|
+
className: "max-h-[85dvh] overflow-y-auto",
|
|
3535
|
+
"data-testid": "docx-cross-references-report",
|
|
3536
|
+
children: [
|
|
3537
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
3538
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: l.updated }),
|
|
3539
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: report && l.updateSummary(report.refreshed) })
|
|
3540
|
+
] }),
|
|
3541
|
+
report && /* @__PURE__ */ jsxs("section", { className: "space-y-1 text-sm", children: [
|
|
3542
|
+
/* @__PURE__ */ jsx("h3", { className: "font-medium", children: l.brokenTitle }),
|
|
3543
|
+
report.broken.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3544
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: l.brokenHelp }),
|
|
3545
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-1", "data-testid": "docx-cross-references-broken", children: report.broken.map((b) => /* @__PURE__ */ jsxs(
|
|
3546
|
+
"li",
|
|
3547
|
+
{
|
|
3548
|
+
className: "flex items-center justify-between gap-2",
|
|
3549
|
+
children: [
|
|
3550
|
+
/* @__PURE__ */ jsx("span", { className: "text-red-text", children: b.text }),
|
|
3551
|
+
/* @__PURE__ */ jsx(
|
|
3552
|
+
Button,
|
|
3553
|
+
{
|
|
3554
|
+
size: "sm",
|
|
3555
|
+
variant: "ghost",
|
|
3556
|
+
onClick: () => {
|
|
3557
|
+
const point = { paragraphId: b.paragraphId, offset: b.offset };
|
|
3558
|
+
editor?.surface?.setSelection({ anchor: point, head: point });
|
|
3559
|
+
editor?.surface?.revealParagraph(b.paragraphId);
|
|
3560
|
+
setReport(null);
|
|
3561
|
+
},
|
|
3562
|
+
children: l.show
|
|
3563
|
+
}
|
|
3564
|
+
)
|
|
3565
|
+
]
|
|
3566
|
+
},
|
|
3567
|
+
`${b.paragraphId}:${b.offset}`
|
|
3568
|
+
)) })
|
|
3569
|
+
] }) : /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: l.brokenNone })
|
|
3570
|
+
] }),
|
|
3571
|
+
/* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setReport(null), children: l.close }) })
|
|
3572
|
+
]
|
|
3920
3573
|
}
|
|
3921
|
-
)
|
|
3574
|
+
) })
|
|
3922
3575
|
] });
|
|
3923
3576
|
}
|
|
3924
|
-
function
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3577
|
+
function CrossReferenceDialog({
|
|
3578
|
+
targets,
|
|
3579
|
+
references,
|
|
3580
|
+
documentId,
|
|
3581
|
+
passages,
|
|
3582
|
+
loadPassages,
|
|
3583
|
+
labels,
|
|
3584
|
+
onCancel,
|
|
3585
|
+
onInsertTarget,
|
|
3586
|
+
onInsertPassage,
|
|
3587
|
+
onClosed
|
|
3588
|
+
}) {
|
|
3589
|
+
const l = { ...DEFAULT_CROSS_REFERENCES_LABELS, ...labels };
|
|
3590
|
+
const [tab, setTab] = useState("this");
|
|
3591
|
+
const [query, setQuery] = useState("");
|
|
3592
|
+
const [chosen, setChosen] = useState(null);
|
|
3593
|
+
const [mode, setMode] = useState("number");
|
|
3594
|
+
const shown = useMemo(() => filterTargets(targets, query), [targets, query]);
|
|
3595
|
+
const effectiveMode = chosen?.number ? mode : "text";
|
|
3596
|
+
const [documents, setDocuments] = useState(null);
|
|
3597
|
+
const [docId, setDocId] = useState(null);
|
|
3598
|
+
const [passageQuery, setPassageQuery] = useState("");
|
|
3599
|
+
const [passage, setPassage] = useState(null);
|
|
3600
|
+
const [text, setText] = useState("");
|
|
3932
3601
|
useEffect(() => {
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3602
|
+
if (tab !== "other" || !references || documents) return;
|
|
3603
|
+
references.documents().then((list2) => setDocuments(list2.filter((d) => d.id !== documentId))).catch(() => setDocuments([]));
|
|
3604
|
+
}, [tab, references, documents, documentId]);
|
|
3605
|
+
const chooseDocument = (id) => {
|
|
3606
|
+
setDocId(id);
|
|
3607
|
+
setPassage(null);
|
|
3608
|
+
setText("");
|
|
3609
|
+
loadPassages(id);
|
|
3610
|
+
};
|
|
3611
|
+
const title = documents?.find((d) => d.id === docId)?.title ?? "";
|
|
3612
|
+
const list = docId ? passages[docId] : void 0;
|
|
3613
|
+
const shownPassages = Array.isArray(list) ? filterPassages(list, passageQuery) : [];
|
|
3614
|
+
const canInsert = tab === "this" ? !!chosen : !!(docId && passage && text.trim());
|
|
3615
|
+
const insert = () => {
|
|
3616
|
+
if (tab === "this" && chosen) onInsertTarget(chosen, effectiveMode);
|
|
3617
|
+
if (tab === "other" && docId && passage)
|
|
3618
|
+
onInsertPassage(
|
|
3619
|
+
{ documentId: docId, chunkId: passage.chunkId, page: passage.page ?? null },
|
|
3620
|
+
text
|
|
3621
|
+
);
|
|
3622
|
+
};
|
|
3623
|
+
return /* @__PURE__ */ jsx(Dialog, { open: true, onOpenChange: (open) => !open && onCancel(), children: /* @__PURE__ */ jsxs(
|
|
3624
|
+
DialogContent,
|
|
3625
|
+
{
|
|
3626
|
+
className: "max-h-[85dvh] overflow-y-auto",
|
|
3627
|
+
"data-testid": "docx-cross-reference-dialog",
|
|
3628
|
+
onCloseAutoFocus: (event) => {
|
|
3629
|
+
if (!onClosed) return;
|
|
3937
3630
|
event.preventDefault();
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3631
|
+
onClosed();
|
|
3632
|
+
},
|
|
3633
|
+
children: [
|
|
3634
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
3635
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: l.title }),
|
|
3636
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: l.description })
|
|
3637
|
+
] }),
|
|
3638
|
+
/* @__PURE__ */ jsxs(Tabs, { value: tab, onValueChange: (v) => setTab(v), children: [
|
|
3639
|
+
/* @__PURE__ */ jsxs(TabsList, { children: [
|
|
3640
|
+
/* @__PURE__ */ jsx(TabsTrigger, { value: "this", "data-testid": "docx-cross-reference-this", children: l.thisDocument }),
|
|
3641
|
+
references && /* @__PURE__ */ jsx(TabsTrigger, { value: "other", "data-testid": "docx-cross-reference-other", children: l.otherDocument })
|
|
3642
|
+
] }),
|
|
3643
|
+
/* @__PURE__ */ jsxs(TabsContent, { value: "this", className: "space-y-3", children: [
|
|
3644
|
+
/* @__PURE__ */ jsx(
|
|
3645
|
+
Input,
|
|
3646
|
+
{
|
|
3647
|
+
value: query,
|
|
3648
|
+
placeholder: l.filterTargets,
|
|
3649
|
+
"aria-label": l.filterTargets,
|
|
3650
|
+
onChange: (e) => setQuery(e.target.value),
|
|
3651
|
+
"data-testid": "docx-cross-reference-filter"
|
|
3652
|
+
}
|
|
3653
|
+
),
|
|
3654
|
+
/* @__PURE__ */ jsxs(
|
|
3655
|
+
"div",
|
|
3656
|
+
{
|
|
3657
|
+
role: "listbox",
|
|
3658
|
+
"aria-label": l.filterTargets,
|
|
3659
|
+
className: "max-h-64 space-y-0.5 overflow-y-auto rounded-md border border-border p-1",
|
|
3660
|
+
children: [
|
|
3661
|
+
shown.length === 0 && /* @__PURE__ */ jsx("p", { className: "p-2 text-sm text-muted-foreground", children: l.noTargets }),
|
|
3662
|
+
shown.map((t) => /* @__PURE__ */ jsxs(
|
|
3663
|
+
"button",
|
|
3664
|
+
{
|
|
3665
|
+
type: "button",
|
|
3666
|
+
role: "option",
|
|
3667
|
+
"aria-selected": chosen?.paragraphId === t.paragraphId,
|
|
3668
|
+
"data-testid": "docx-cross-reference-target",
|
|
3669
|
+
onClick: () => setChosen(t),
|
|
3670
|
+
onDoubleClick: () => onInsertTarget(t, t.number ? mode : "text"),
|
|
3671
|
+
className: cn(
|
|
3672
|
+
"flex w-full items-baseline gap-2 rounded px-2 py-1 text-left text-sm",
|
|
3673
|
+
chosen?.paragraphId === t.paragraphId ? "bg-accent" : "hover:bg-accent/50"
|
|
3674
|
+
),
|
|
3675
|
+
style: { paddingLeft: 8 + t.level * 16 },
|
|
3676
|
+
children: [
|
|
3677
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 font-mono text-xs text-muted-foreground", children: t.marker || l.heading }),
|
|
3678
|
+
/* @__PURE__ */ jsx("span", { className: cn("truncate", t.heading && "font-medium"), children: t.text })
|
|
3679
|
+
]
|
|
3680
|
+
},
|
|
3681
|
+
t.paragraphId
|
|
3682
|
+
))
|
|
3683
|
+
]
|
|
3684
|
+
}
|
|
3685
|
+
),
|
|
3686
|
+
chosen && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 text-sm", children: [
|
|
3687
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: l.insertAs }),
|
|
3688
|
+
chosen.number && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1", children: [
|
|
3689
|
+
/* @__PURE__ */ jsx(
|
|
3690
|
+
"input",
|
|
3691
|
+
{
|
|
3692
|
+
type: "radio",
|
|
3693
|
+
name: "cross-reference-mode",
|
|
3694
|
+
checked: effectiveMode === "number",
|
|
3695
|
+
onChange: () => setMode("number")
|
|
3696
|
+
}
|
|
3697
|
+
),
|
|
3698
|
+
l.number
|
|
3699
|
+
] }),
|
|
3700
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1", children: [
|
|
3701
|
+
/* @__PURE__ */ jsx(
|
|
3702
|
+
"input",
|
|
3703
|
+
{
|
|
3704
|
+
type: "radio",
|
|
3705
|
+
name: "cross-reference-mode",
|
|
3706
|
+
checked: effectiveMode === "text",
|
|
3707
|
+
onChange: () => setMode("text")
|
|
3708
|
+
}
|
|
3709
|
+
),
|
|
3710
|
+
l.text
|
|
3711
|
+
] }),
|
|
3712
|
+
/* @__PURE__ */ jsx(
|
|
3713
|
+
"span",
|
|
3714
|
+
{
|
|
3715
|
+
className: "ml-auto rounded bg-muted px-2 py-0.5",
|
|
3716
|
+
"data-testid": "docx-cross-reference-preview",
|
|
3717
|
+
children: refResult(chosen, effectiveMode)
|
|
3718
|
+
}
|
|
3719
|
+
)
|
|
3720
|
+
] })
|
|
3721
|
+
] }),
|
|
3722
|
+
references && /* @__PURE__ */ jsxs(TabsContent, { value: "other", className: "space-y-3", children: [
|
|
3723
|
+
references.renderDocumentPicker ? references.renderDocumentPicker(chooseDocument, docId) : /* @__PURE__ */ jsxs(
|
|
3724
|
+
"select",
|
|
3725
|
+
{
|
|
3726
|
+
className: "h-9 w-full rounded-md border border-border bg-background px-2 text-sm",
|
|
3727
|
+
"aria-label": l.chooseDocument,
|
|
3728
|
+
value: docId ?? "",
|
|
3729
|
+
onChange: (e) => e.target.value && chooseDocument(e.target.value),
|
|
3730
|
+
"data-testid": "docx-cross-reference-document",
|
|
3731
|
+
children: [
|
|
3732
|
+
/* @__PURE__ */ jsx("option", { value: "", children: l.chooseDocument }),
|
|
3733
|
+
(documents ?? []).map((d) => /* @__PURE__ */ jsx("option", { value: d.id, children: d.title }, d.id))
|
|
3734
|
+
]
|
|
3735
|
+
}
|
|
3736
|
+
),
|
|
3737
|
+
docId && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3738
|
+
/* @__PURE__ */ jsx(
|
|
3739
|
+
Input,
|
|
3740
|
+
{
|
|
3741
|
+
value: passageQuery,
|
|
3742
|
+
placeholder: l.filterPassages,
|
|
3743
|
+
"aria-label": l.filterPassages,
|
|
3744
|
+
onChange: (e) => setPassageQuery(e.target.value),
|
|
3745
|
+
"data-testid": "docx-cross-reference-passage-filter"
|
|
3746
|
+
}
|
|
3747
|
+
),
|
|
3748
|
+
/* @__PURE__ */ jsx(
|
|
3749
|
+
"div",
|
|
3750
|
+
{
|
|
3751
|
+
className: "max-h-64 space-y-1 overflow-y-auto",
|
|
3752
|
+
"data-testid": "docx-cross-reference-passages",
|
|
3753
|
+
children: list === "loading" || list === void 0 ? /* @__PURE__ */ jsx("p", { className: "p-2 text-sm text-muted-foreground", children: l.loadingPassages }) : shownPassages.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-2 text-sm text-muted-foreground", children: l.noPassages }) : shownPassages.map((p, i) => /* @__PURE__ */ jsx(
|
|
3754
|
+
FactPassageItem,
|
|
3755
|
+
{
|
|
3756
|
+
index: i,
|
|
3757
|
+
page: p.page,
|
|
3758
|
+
excerpt: passageExcerpt(p.text, 200),
|
|
3759
|
+
isViewing: passage?.chunkId === p.chunkId,
|
|
3760
|
+
onSelect: () => {
|
|
3761
|
+
setPassage(p);
|
|
3762
|
+
setText(referenceText(title, p));
|
|
3763
|
+
},
|
|
3764
|
+
testId: "docx-cross-reference-passage"
|
|
3765
|
+
},
|
|
3766
|
+
p.chunkId
|
|
3767
|
+
))
|
|
3768
|
+
}
|
|
3769
|
+
),
|
|
3770
|
+
passage && /* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-sm", children: [
|
|
3771
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: l.referenceText }),
|
|
3772
|
+
/* @__PURE__ */ jsx(
|
|
3773
|
+
Input,
|
|
3774
|
+
{
|
|
3775
|
+
value: text,
|
|
3776
|
+
onChange: (e) => setText(e.target.value),
|
|
3777
|
+
"data-testid": "docx-cross-reference-text"
|
|
3778
|
+
}
|
|
3779
|
+
)
|
|
3780
|
+
] })
|
|
3781
|
+
] })
|
|
3782
|
+
] })
|
|
3783
|
+
] }),
|
|
3784
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
3785
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onCancel, children: l.cancel }),
|
|
3786
|
+
/* @__PURE__ */ jsx(Button, { disabled: !canInsert, onClick: insert, "data-testid": "docx-cross-reference-insert", children: l.insertButton })
|
|
3787
|
+
] })
|
|
3788
|
+
]
|
|
3789
|
+
}
|
|
3790
|
+
) });
|
|
3969
3791
|
}
|
|
3970
3792
|
function CrossReferenceButton() {
|
|
3971
3793
|
const references = useCrossReferences();
|
|
@@ -4632,6 +4454,186 @@ function LayoutTabContent({ tab }) {
|
|
|
4632
4454
|
if (tab === PICTURE_TAB) return /* @__PURE__ */ jsx(PictureButtons, {});
|
|
4633
4455
|
return null;
|
|
4634
4456
|
}
|
|
4457
|
+
var titleOf = (prompt) => {
|
|
4458
|
+
const line = prompt.trim().split("\n")[0];
|
|
4459
|
+
return line.length > 80 ? `${line.slice(0, 79)}\u2026` : line;
|
|
4460
|
+
};
|
|
4461
|
+
function rememberCaret(editor) {
|
|
4462
|
+
const surface = editor.surface;
|
|
4463
|
+
const head = surface?.state().selection?.head;
|
|
4464
|
+
if (!surface || !head) return null;
|
|
4465
|
+
return { paraId: surface.session.paraIdOf(head.paragraphId), ...head };
|
|
4466
|
+
}
|
|
4467
|
+
var mounted = [];
|
|
4468
|
+
var running = 0;
|
|
4469
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
4470
|
+
var setRunning = (delta) => {
|
|
4471
|
+
running += delta;
|
|
4472
|
+
listeners.forEach((listener) => listener());
|
|
4473
|
+
};
|
|
4474
|
+
var subscribe = (listener) => {
|
|
4475
|
+
listeners.add(listener);
|
|
4476
|
+
return () => void listeners.delete(listener);
|
|
4477
|
+
};
|
|
4478
|
+
function insertionTarget(origin, caret, editors = mounted) {
|
|
4479
|
+
if (editors.includes(origin) && origin.surface) return origin;
|
|
4480
|
+
const paraId = caret?.paraId;
|
|
4481
|
+
if (!paraId) return null;
|
|
4482
|
+
for (let i = editors.length - 1; i >= 0; i--) {
|
|
4483
|
+
const session = editors[i].surface?.session;
|
|
4484
|
+
if (session?.nodeIdOf(paraId)) return editors[i];
|
|
4485
|
+
}
|
|
4486
|
+
return null;
|
|
4487
|
+
}
|
|
4488
|
+
function restoreCaret(editor, caret) {
|
|
4489
|
+
const surface = editor.surface;
|
|
4490
|
+
if (!surface || !caret) return;
|
|
4491
|
+
const paragraphId = caret.paraId && surface.session.nodeIdOf(caret.paraId) || caret.paragraphId;
|
|
4492
|
+
if (!surface.session.paragraphIds().includes(paragraphId)) return;
|
|
4493
|
+
const point = { paragraphId, offset: caret.offset };
|
|
4494
|
+
surface.setSelection({ anchor: point, head: point });
|
|
4495
|
+
}
|
|
4496
|
+
async function insertGeneratedImage(editor, bytes, prompt, caret) {
|
|
4497
|
+
const image = normalizeImageBytes(bytes);
|
|
4498
|
+
if (!image.ok) throw new Error("The generated image is not in a format Word can embed.");
|
|
4499
|
+
let result;
|
|
4500
|
+
for (let attempt = 0; attempt < 3 && !result?.ok; attempt++) {
|
|
4501
|
+
editor.focus();
|
|
4502
|
+
restoreCaret(editor, caret);
|
|
4503
|
+
result = await executeImageCommand(editor, {
|
|
4504
|
+
type: "insertImage",
|
|
4505
|
+
data: image.bytes,
|
|
4506
|
+
mime: image.mime,
|
|
4507
|
+
widthPoints: image.widthPoints,
|
|
4508
|
+
heightPoints: image.heightPoints,
|
|
4509
|
+
description: prompt.trim(),
|
|
4510
|
+
title: titleOf(prompt)
|
|
4511
|
+
});
|
|
4512
|
+
if (!result.ok && result.reason !== "stale drawing selection") break;
|
|
4513
|
+
}
|
|
4514
|
+
if (!result?.ok) throw new Error(result?.reason || "The picture could not be inserted here.");
|
|
4515
|
+
}
|
|
4516
|
+
var AUTO = "";
|
|
4517
|
+
function useGenerateImageTarget() {
|
|
4518
|
+
const editor = useDocxEditor();
|
|
4519
|
+
const loaded = useEditorState((snapshot) => !snapshot.isLoading);
|
|
4520
|
+
useEffect(() => {
|
|
4521
|
+
if (!editor || !loaded) return;
|
|
4522
|
+
mounted.push(editor);
|
|
4523
|
+
return () => void mounted.splice(mounted.indexOf(editor), 1);
|
|
4524
|
+
}, [editor, loaded]);
|
|
4525
|
+
}
|
|
4526
|
+
function GenerateImageButton({ generation }) {
|
|
4527
|
+
const editor = useDocxEditor();
|
|
4528
|
+
const [prompt, setPrompt] = useState("");
|
|
4529
|
+
const [aspectRatio, setAspectRatio] = useState(AUTO);
|
|
4530
|
+
const [busy, setBusy] = useState(false);
|
|
4531
|
+
const [error, setError] = useState("");
|
|
4532
|
+
const abort = useRef(null);
|
|
4533
|
+
const close = useRef(() => {
|
|
4534
|
+
});
|
|
4535
|
+
const editable = useEditorState((snapshot) => snapshot.editable);
|
|
4536
|
+
const generating = useSyncExternalStore(subscribe, () => running > 0);
|
|
4537
|
+
async function run2() {
|
|
4538
|
+
if (!editor || !prompt.trim() || busy) return;
|
|
4539
|
+
const caret = rememberCaret(editor);
|
|
4540
|
+
const controller = new AbortController();
|
|
4541
|
+
abort.current = controller;
|
|
4542
|
+
setBusy(true);
|
|
4543
|
+
setRunning(1);
|
|
4544
|
+
setError("");
|
|
4545
|
+
try {
|
|
4546
|
+
const bytes = await generation.generate(prompt.trim(), {
|
|
4547
|
+
aspectRatio: aspectRatio || void 0,
|
|
4548
|
+
signal: controller.signal
|
|
4549
|
+
});
|
|
4550
|
+
if (controller.signal.aborted) return;
|
|
4551
|
+
const target = insertionTarget(editor, caret);
|
|
4552
|
+
if (!target) throw new Error("The document was closed before the generated image was ready.");
|
|
4553
|
+
await insertGeneratedImage(target, bytes, prompt, caret);
|
|
4554
|
+
setPrompt("");
|
|
4555
|
+
close.current();
|
|
4556
|
+
generation.notify?.("Generated image inserted", "success");
|
|
4557
|
+
} catch (failure) {
|
|
4558
|
+
if (controller.signal.aborted) return;
|
|
4559
|
+
const message = failure instanceof Error ? failure.message : String(failure);
|
|
4560
|
+
setError(message);
|
|
4561
|
+
generation.notify?.(message, "error");
|
|
4562
|
+
} finally {
|
|
4563
|
+
abort.current = null;
|
|
4564
|
+
setRunning(-1);
|
|
4565
|
+
setBusy(false);
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4568
|
+
if (!editable && !generating) return null;
|
|
4569
|
+
return /* @__PURE__ */ jsx(
|
|
4570
|
+
RibbonMenu,
|
|
4571
|
+
{
|
|
4572
|
+
label: generating ? "Generating image\u2026" : "Generate image",
|
|
4573
|
+
icon: generating ? /* @__PURE__ */ jsx(Loader2, { size: 16, className: "native-docx-spin" }) : /* @__PURE__ */ jsx(Sparkles, { size: 16 }),
|
|
4574
|
+
testId: "docx-generate-image",
|
|
4575
|
+
children: (dismiss) => {
|
|
4576
|
+
close.current = dismiss;
|
|
4577
|
+
return /* @__PURE__ */ jsxs(
|
|
4578
|
+
"form",
|
|
4579
|
+
{
|
|
4580
|
+
className: "native-docx-form native-docx-generate-image",
|
|
4581
|
+
onSubmit: (event) => {
|
|
4582
|
+
event.preventDefault();
|
|
4583
|
+
void run2();
|
|
4584
|
+
},
|
|
4585
|
+
children: [
|
|
4586
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
4587
|
+
"Describe the image",
|
|
4588
|
+
/* @__PURE__ */ jsx(
|
|
4589
|
+
"textarea",
|
|
4590
|
+
{
|
|
4591
|
+
autoFocus: true,
|
|
4592
|
+
rows: 3,
|
|
4593
|
+
value: prompt,
|
|
4594
|
+
disabled: busy,
|
|
4595
|
+
placeholder: "A watercolour of a lighthouse at dawn",
|
|
4596
|
+
"data-testid": "docx-generate-image-prompt",
|
|
4597
|
+
onChange: (event) => setPrompt(event.target.value),
|
|
4598
|
+
onKeyDown: (event) => {
|
|
4599
|
+
if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) void run2();
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4602
|
+
)
|
|
4603
|
+
] }),
|
|
4604
|
+
generation.aspectRatios && generation.aspectRatios.length > 0 && /* @__PURE__ */ jsxs("label", { children: [
|
|
4605
|
+
"Aspect ratio",
|
|
4606
|
+
/* @__PURE__ */ jsxs(
|
|
4607
|
+
"select",
|
|
4608
|
+
{
|
|
4609
|
+
value: aspectRatio,
|
|
4610
|
+
disabled: busy,
|
|
4611
|
+
onChange: (event) => setAspectRatio(event.target.value),
|
|
4612
|
+
children: [
|
|
4613
|
+
/* @__PURE__ */ jsx("option", { value: AUTO, children: "Auto" }),
|
|
4614
|
+
generation.aspectRatios.map((ratio) => /* @__PURE__ */ jsx("option", { value: ratio.value, children: ratio.label }, ratio.value))
|
|
4615
|
+
]
|
|
4616
|
+
}
|
|
4617
|
+
)
|
|
4618
|
+
] }),
|
|
4619
|
+
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." }),
|
|
4620
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", children: error }),
|
|
4621
|
+
/* @__PURE__ */ jsx("div", { className: "native-docx-form-actions", children: busy ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => abort.current?.abort(), children: "Cancel" }) : /* @__PURE__ */ jsx(
|
|
4622
|
+
"button",
|
|
4623
|
+
{
|
|
4624
|
+
type: "submit",
|
|
4625
|
+
disabled: !prompt.trim(),
|
|
4626
|
+
"data-testid": "docx-generate-image-submit",
|
|
4627
|
+
children: "Generate"
|
|
4628
|
+
}
|
|
4629
|
+
) })
|
|
4630
|
+
]
|
|
4631
|
+
}
|
|
4632
|
+
);
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
);
|
|
4636
|
+
}
|
|
4635
4637
|
var contains = (node, id) => node.id === id || (node.children ?? []).some((child) => contains(child, id));
|
|
4636
4638
|
function firstParagraph(node) {
|
|
4637
4639
|
for (const child of node.children ?? []) {
|
|
@@ -4927,6 +4929,19 @@ function DocxRibbon({
|
|
|
4927
4929
|
badge && /* @__PURE__ */ jsx("span", { className: "native-docx-ribbon-badge", "data-testid": "docx-ribbon-badge", children: badge })
|
|
4928
4930
|
] });
|
|
4929
4931
|
}
|
|
4932
|
+
function CompactToolbar() {
|
|
4933
|
+
const t = useChromeTranslate();
|
|
4934
|
+
return /* @__PURE__ */ jsxs(DocxEditor.Toolbar, { t, preset: false, children: [
|
|
4935
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Undo, {}),
|
|
4936
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Redo, {}),
|
|
4937
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Bold, {}),
|
|
4938
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Italic, {}),
|
|
4939
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Underline, {}),
|
|
4940
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.BulletList, {}),
|
|
4941
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.NumberedList, {}),
|
|
4942
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Link, {})
|
|
4943
|
+
] });
|
|
4944
|
+
}
|
|
4930
4945
|
var fixedZoomMode = { type: "fixed" };
|
|
4931
4946
|
function NativeDocxEditor({
|
|
4932
4947
|
documentId,
|
|
@@ -4936,7 +4951,9 @@ function NativeDocxEditor({
|
|
|
4936
4951
|
snapshot = false,
|
|
4937
4952
|
onSaved,
|
|
4938
4953
|
stream,
|
|
4939
|
-
adapter
|
|
4954
|
+
adapter,
|
|
4955
|
+
compact = false,
|
|
4956
|
+
stateRef
|
|
4940
4957
|
}) {
|
|
4941
4958
|
const [ready, setReady] = useState(), [error, setError] = useState(""), [retry, setRetry] = useState(0);
|
|
4942
4959
|
const [status, setStatus] = useState("Connecting\u2026"), [dirty, setDirty] = useState(false), [saving, setSaving] = useState(false), [offline, setOffline] = useState(false);
|
|
@@ -5107,6 +5124,14 @@ function NativeDocxEditor({
|
|
|
5107
5124
|
room?.destroy();
|
|
5108
5125
|
};
|
|
5109
5126
|
}, [documentId, isDraft, snapshot, retry]);
|
|
5127
|
+
useEffect(() => {
|
|
5128
|
+
if (!stateRef) return;
|
|
5129
|
+
const room = ready?.room;
|
|
5130
|
+
stateRef.current = () => room ? encodeState(room) : void 0;
|
|
5131
|
+
return () => {
|
|
5132
|
+
stateRef.current = null;
|
|
5133
|
+
};
|
|
5134
|
+
}, [stateRef, ready?.room]);
|
|
5110
5135
|
useEffect(() => {
|
|
5111
5136
|
adapter.onDirtyChange?.(dirty);
|
|
5112
5137
|
const beforeUnload = (e) => {
|
|
@@ -5190,7 +5215,7 @@ function NativeDocxEditor({
|
|
|
5190
5215
|
if (!pageWidth) return;
|
|
5191
5216
|
const style = getComputedStyle(viewport);
|
|
5192
5217
|
const available = viewport.clientWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
|
|
5193
|
-
const zoom = Math.max(0.1, Math.min(2.5, available / pageWidth));
|
|
5218
|
+
const zoom = Math.max(0.1, Math.min(compact ? 1 : 2.5, available / pageWidth));
|
|
5194
5219
|
if (Math.abs(editor.getZoom() - zoom) > 1e-3) editor.setZoom(zoom);
|
|
5195
5220
|
};
|
|
5196
5221
|
const resize = new ResizeObserver(fit);
|
|
@@ -5226,7 +5251,7 @@ function NativeDocxEditor({
|
|
|
5226
5251
|
return /* @__PURE__ */ jsxs(
|
|
5227
5252
|
"section",
|
|
5228
5253
|
{
|
|
5229
|
-
className: "native-docx-viewer",
|
|
5254
|
+
className: compact ? "native-docx-viewer native-docx-compact" : "native-docx-viewer",
|
|
5230
5255
|
"data-testid": "native-docx-editor",
|
|
5231
5256
|
"aria-busy": !!stream?.busy,
|
|
5232
5257
|
"data-generation-state": stream ? stream.busy ? "generating" : stream.error ? "stopped" : "generated" : "ready",
|
|
@@ -5255,7 +5280,7 @@ function NativeDocxEditor({
|
|
|
5255
5280
|
document: ready.document,
|
|
5256
5281
|
modules: ready.modules,
|
|
5257
5282
|
fonts: ready.fonts,
|
|
5258
|
-
mode: stream || isDraft && ready.canEdit && !snapshot ? "edit" : !ready.canEdit || snapshot ? "view" : "suggesting",
|
|
5283
|
+
mode: stream || isDraft && ready.canEdit && !snapshot || compact && ready.canEdit ? "edit" : !ready.canEdit || snapshot ? "view" : "suggesting",
|
|
5259
5284
|
reviewDisplayMode: "all-markup",
|
|
5260
5285
|
zoomMode: fixedZoomMode,
|
|
5261
5286
|
author: ready.author,
|
|
@@ -5296,7 +5321,8 @@ function NativeDocxEditor({
|
|
|
5296
5321
|
trackMoves: true
|
|
5297
5322
|
}
|
|
5298
5323
|
),
|
|
5299
|
-
!stream && /* @__PURE__ */ jsx(
|
|
5324
|
+
compact && !stream && /* @__PURE__ */ jsx(CompactToolbar, {}),
|
|
5325
|
+
!compact && !stream && /* @__PURE__ */ jsx(
|
|
5300
5326
|
DocxRibbon,
|
|
5301
5327
|
{
|
|
5302
5328
|
onSave: () => void saveVersion(),
|
|
@@ -5335,13 +5361,13 @@ function NativeDocxEditor({
|
|
|
5335
5361
|
children: /* @__PURE__ */ jsx(DocxEditorReview, { children: () => /* @__PURE__ */ jsx(DocxEditorReview.Card, { children: /* @__PURE__ */ jsx(DocxCommentActivity, {}) }) })
|
|
5336
5362
|
}
|
|
5337
5363
|
),
|
|
5338
|
-
/* @__PURE__ */ jsx(DocxEditor.HeaderFooterChrome, {}),
|
|
5339
|
-
/* @__PURE__ */ jsx(DocxEditor.NotesChrome, {}),
|
|
5364
|
+
!compact && /* @__PURE__ */ jsx(DocxEditor.HeaderFooterChrome, {}),
|
|
5365
|
+
!compact && /* @__PURE__ */ jsx(DocxEditor.NotesChrome, {}),
|
|
5340
5366
|
/* @__PURE__ */ jsx(DocxEditor.Equation, {}),
|
|
5341
5367
|
/* @__PURE__ */ jsx(DocxEditor.HyperLink, {}),
|
|
5342
5368
|
/* @__PURE__ */ jsx(DocxEditor.ContextMenu, { children: /* @__PURE__ */ jsx(DocxContextMenuExtras, {}) })
|
|
5343
5369
|
] }),
|
|
5344
|
-
/* @__PURE__ */ jsx(DocxEditor.PageNumber, {})
|
|
5370
|
+
!compact && /* @__PURE__ */ jsx(DocxEditor.PageNumber, {})
|
|
5345
5371
|
]
|
|
5346
5372
|
}
|
|
5347
5373
|
)
|
|
@@ -5360,11 +5386,19 @@ function NativeDocxEditor({
|
|
|
5360
5386
|
status
|
|
5361
5387
|
] }),
|
|
5362
5388
|
/* @__PURE__ */ jsx(DocxParticipants, { session: ready?.room?.session }),
|
|
5363
|
-
/* @__PURE__ */ jsxs(
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5389
|
+
!compact && /* @__PURE__ */ jsxs(
|
|
5390
|
+
"button",
|
|
5391
|
+
{
|
|
5392
|
+
onClick: () => void exportDocument(),
|
|
5393
|
+
disabled: !ready,
|
|
5394
|
+
"aria-label": "Download DOCX",
|
|
5395
|
+
children: [
|
|
5396
|
+
/* @__PURE__ */ jsx(Download, { size: 14 }),
|
|
5397
|
+
"DOCX"
|
|
5398
|
+
]
|
|
5399
|
+
}
|
|
5400
|
+
),
|
|
5401
|
+
!compact && !snapshot && !stream && ready?.canEdit && /* @__PURE__ */ jsxs(
|
|
5368
5402
|
"button",
|
|
5369
5403
|
{
|
|
5370
5404
|
className: "native-docx-save",
|
|
@@ -5382,70 +5416,7 @@ function NativeDocxEditor({
|
|
|
5382
5416
|
}
|
|
5383
5417
|
);
|
|
5384
5418
|
}
|
|
5385
|
-
var loaded = /* @__PURE__ */ new Map();
|
|
5386
|
-
function loadDocxFonts(baseUrl = "/native-docx/fonts") {
|
|
5387
|
-
let promise = loaded.get(baseUrl);
|
|
5388
|
-
if (!promise) {
|
|
5389
|
-
promise = loadFonts(baseUrl).catch((error) => {
|
|
5390
|
-
loaded.delete(baseUrl);
|
|
5391
|
-
throw error;
|
|
5392
|
-
});
|
|
5393
|
-
loaded.set(baseUrl, promise);
|
|
5394
|
-
}
|
|
5395
|
-
return promise;
|
|
5396
|
-
}
|
|
5397
|
-
async function loadFonts(baseUrl) {
|
|
5398
|
-
const families = ["Liberation Serif", "Liberation Sans", "Liberation Mono"];
|
|
5399
|
-
const variants = [
|
|
5400
|
-
{ suffix: "Regular", weight: 400, style: "normal" },
|
|
5401
|
-
{ suffix: "Bold", weight: 700, style: "normal" },
|
|
5402
|
-
{ suffix: "Italic", weight: 400, style: "italic" },
|
|
5403
|
-
{ suffix: "BoldItalic", weight: 700, style: "italic" }
|
|
5404
|
-
];
|
|
5405
|
-
const sources = await Promise.all(
|
|
5406
|
-
families.flatMap(
|
|
5407
|
-
(family) => variants.map(async (face) => {
|
|
5408
|
-
const r = await fetch(
|
|
5409
|
-
`${baseUrl.replace(/\/$/, "")}/${family.replace(" ", "")}-${face.suffix}.ttf`
|
|
5410
|
-
);
|
|
5411
|
-
if (!r.ok) throw new Error(`Could not load the document font ${family}.`);
|
|
5412
|
-
const result = createFontSource(new Uint8Array(await r.arrayBuffer()), {
|
|
5413
|
-
family,
|
|
5414
|
-
weight: face.weight,
|
|
5415
|
-
style: face.style
|
|
5416
|
-
});
|
|
5417
|
-
if ("failure" in result)
|
|
5418
|
-
throw new Error(`Could not initialize the document font ${family}.`);
|
|
5419
|
-
return result.source;
|
|
5420
|
-
})
|
|
5421
|
-
)
|
|
5422
|
-
);
|
|
5423
|
-
const aliases = {
|
|
5424
|
-
"Times New Roman": "Liberation Serif",
|
|
5425
|
-
Cambria: "Liberation Serif",
|
|
5426
|
-
"DejaVu Serif": "Liberation Serif",
|
|
5427
|
-
Calibri: "Liberation Sans",
|
|
5428
|
-
Aptos: "Liberation Sans",
|
|
5429
|
-
Arial: "Liberation Sans",
|
|
5430
|
-
Helvetica: "Liberation Sans",
|
|
5431
|
-
"Courier New": "Liberation Mono",
|
|
5432
|
-
"DejaVu Sans Mono": "Liberation Mono"
|
|
5433
|
-
};
|
|
5434
|
-
return {
|
|
5435
|
-
epoch: 1,
|
|
5436
|
-
maxFontBytes: 12 * 1024 * 1024,
|
|
5437
|
-
sources,
|
|
5438
|
-
defaultFont: { family: "Liberation Serif", sizeHalfPoints: 22 },
|
|
5439
|
-
supportedFamilies: [...families, ...Object.keys(aliases)],
|
|
5440
|
-
substitutions: Object.entries(aliases).flatMap(
|
|
5441
|
-
([from, to]) => variants.map((face) => ({
|
|
5442
|
-
from: { family: from, weight: face.weight, style: face.style },
|
|
5443
|
-
to: { family: to, weight: face.weight, style: face.style }
|
|
5444
|
-
}))
|
|
5445
|
-
)
|
|
5446
|
-
};
|
|
5447
|
-
}
|
|
5448
5419
|
|
|
5449
|
-
export { BUILTIN_TEMPLATE_NAME, CrossReferenceDialog, CrossReferencesProvider, DEFAULT_CROSS_REFERENCES_LABELS, DEFAULT_DEFINED_TERMS_LABELS, DEFINED_TERM_TRIGGER, DefinedTermsProvider, DocxAuthorContext, DocxCommentActivity, DocxEditorReview, DocxMentions, DocxParticipants, DocxRibbon, GenerateImageButton, HouseTemplateProvider, LinkHoverCard, NativeDocxEditor, builtinHouseTemplate, contentPath, editorPort, findDefinitionsSection, insertCrossReference, insertDefinedTerm, insertDocumentReference, listTargets,
|
|
5450
|
-
//# sourceMappingURL=chunk-
|
|
5451
|
-
//# sourceMappingURL=chunk-
|
|
5420
|
+
export { BUILTIN_TEMPLATE_NAME, CrossReferenceDialog, CrossReferencesProvider, DEFAULT_CROSS_REFERENCES_LABELS, DEFAULT_DEFINED_TERMS_LABELS, DEFINED_TERM_TRIGGER, DefinedTermsProvider, DocxAuthorContext, DocxCommentActivity, DocxEditorReview, DocxMentions, DocxParticipants, DocxRibbon, GenerateImageButton, HouseTemplateProvider, LinkHoverCard, NativeDocxEditor, builtinHouseTemplate, contentPath, editorPort, findDefinitionsSection, insertCrossReference, insertDefinedTerm, insertDocumentReference, listTargets, normalizeDocxExport, paragraphNumber, referenceText, referencesPort, resolveHouseTemplate, reviewModule, shortTitle, structuralPort, templateFileName, trackedOrNoted, useCrossReferences, useDefinedTerms, useHouseTemplate, useReview, useReviewItem, useTrackedOrNoted, withDirectEditing };
|
|
5421
|
+
//# sourceMappingURL=chunk-2SJTXYRM.js.map
|
|
5422
|
+
//# sourceMappingURL=chunk-2SJTXYRM.js.map
|