@docx-editor.dev/core 2.31.0 → 2.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -1
- package/THIRD_PARTY_NOTICES.md +1 -0
- package/dist/automation.cjs +1 -1
- package/dist/automation.d.ts +16 -54
- package/dist/automation.js +1 -1
- package/dist/binding.cjs +1 -1
- package/dist/binding.d.ts +17 -15
- package/dist/binding.js +1 -1
- package/dist/{chrome-print-BWvaVEhD.d.ts → chrome-print-CrPI9ZQc.d.ts} +128 -38
- package/dist/chunk-23MKR33J.js +4 -0
- package/dist/chunk-2CWQY27B.cjs +1 -0
- package/dist/chunk-2DEY5NUC.cjs +32 -0
- package/dist/chunk-2DR4TH6B.js +1 -0
- package/dist/chunk-2F2UEAE2.js +2 -0
- package/dist/chunk-2GPGAFOJ.cjs +5 -0
- package/dist/chunk-ALD626VU.cjs +2 -0
- package/dist/chunk-AU33YWEJ.js +236 -0
- package/dist/chunk-AXU63LYW.cjs +1 -0
- package/dist/chunk-CM5VZRBV.js +32 -0
- package/dist/chunk-D7FAFFTN.js +1 -0
- package/dist/chunk-DYUVJNDD.cjs +1 -0
- package/dist/chunk-ESYUKD5D.js +2 -0
- package/dist/chunk-F7TGQMVR.js +5 -0
- package/dist/chunk-FLW7KIQX.js +30 -0
- package/dist/chunk-JG4A6W6I.js +7 -0
- package/dist/chunk-JNGLSBPH.js +2 -0
- package/dist/chunk-KE6ZWRIY.cjs +236 -0
- package/dist/chunk-L3YXJDPX.js +1 -0
- package/dist/chunk-LI42N7OW.cjs +4 -0
- package/dist/chunk-PLKDS4JT.js +1 -0
- package/dist/chunk-PXMTQ73F.cjs +2 -0
- package/dist/chunk-R4R3AAE4.js +1 -0
- package/dist/chunk-RFUNFTGV.cjs +1 -0
- package/dist/chunk-SVM6PGMS.js +3 -0
- package/dist/chunk-SZURLHR3.cjs +3 -0
- package/dist/chunk-UYCGCVZ2.cjs +1 -0
- package/dist/chunk-WN7ICTA5.js +1 -0
- package/dist/chunk-X34QRAUU.cjs +1 -0
- package/dist/chunk-XNBYG4XJ.cjs +30 -0
- package/dist/chunk-ZDFJW5XS.cjs +2 -0
- package/dist/chunk-ZWO4JHNN.cjs +7 -0
- package/dist/collaboration/replication.cjs +1 -1
- package/dist/collaboration/replication.d.ts +29 -4
- package/dist/collaboration/replication.js +1 -1
- package/dist/collaboration.cjs +1 -1
- package/dist/collaboration.d.ts +5 -5
- package/dist/collaboration.js +1 -1
- package/dist/contracts/document.d.ts +3 -3
- package/dist/contracts/editor.d.ts +13 -9
- package/dist/contracts/interaction.d.ts +2 -2
- package/dist/contracts/modules.d.ts +15 -12
- package/dist/contracts/types.d.ts +135 -2
- package/dist/{custom-node-writes-TeUvMP13.d.ts → custom-node-writes-AqY5KpVx.d.ts} +7 -2
- package/dist/diagnostic-context-CuzhOX0g.d.ts +98 -0
- package/dist/{diagnostic-hints-C9JlLz1L.d.ts → diagnostic-hints-BtCaCqQC.d.ts} +4 -4
- package/dist/{document-protection-C8x3P2J6.d.ts → document-protection-BvbpSXbQ.d.ts} +1 -1
- package/dist/drawing-graphic-kind-CXYGYbB3.d.ts +324 -0
- package/dist/drawing-preview-package-B9lSLRg4.d.ts +115 -0
- package/dist/{editor-B2ShlO23.d.ts → editor-B7OOAAie.d.ts} +181 -244
- package/dist/editor-fonts-D_-vjuJi.d.ts +53 -0
- package/dist/{editor-scope-C7ke1KJG.d.ts → editor-scope-CA6egMaK.d.ts} +6 -2
- package/dist/editor.cjs +1 -1
- package/dist/editor.css +4 -4
- package/dist/editor.d.ts +259 -31
- package/dist/editor.js +1 -1
- package/dist/export.cjs +2 -2
- package/dist/export.d.ts +48 -19
- package/dist/export.js +2 -2
- package/dist/{failure-NS2lsya4.d.ts → failure-6KGFrDO9.d.ts} +1 -1
- package/dist/{font-resolver-CWh40DYp.d.ts → font-resolver-Bm9XqTNO.d.ts} +15 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +43 -26
- package/dist/index.js +1 -1
- package/dist/{layout-shaping-CWS3j0Xr.d.ts → layout-shaping-Ci_BTnSU.d.ts} +1 -1
- package/dist/layout.cjs +1 -1
- package/dist/layout.d.ts +181 -99
- package/dist/layout.js +1 -1
- package/dist/metafile-drawing-DtMFyMKq.d.ts +107 -0
- package/dist/{note-properties-QsKh24sM.d.ts → note-properties-DCIJUKIy.d.ts} +1 -1
- package/dist/output.cjs +1 -1
- package/dist/output.d.ts +11 -7
- package/dist/output.js +1 -1
- package/dist/{protocol-COZJUKUY.d.ts → protocol-Cp_QzVxS.d.ts} +75 -5
- package/dist/{review-paragraph-order-C_QbCKcH.d.ts → review-paragraph-order-BJQ43_r0.d.ts} +1 -1
- package/dist/revision-projection-OVusTS77.d.ts +258 -0
- package/dist/{semantic-cell-selection-Cub9FDa8.d.ts → semantic-cell-selection-CoI5hHBm.d.ts} +2 -2
- package/dist/{semantic-interaction-dApRIYpt.d.ts → semantic-interaction-BbA4dgEo.d.ts} +1 -1
- package/dist/{semantic-paint-DLzA8sjP.d.ts → semantic-paint-B5JcIv9c.d.ts} +19 -9
- package/dist/{semantic-record-queries-3yp9o11g.d.ts → semantic-record-queries-DnO8J5_j.d.ts} +1 -1
- package/dist/{semantic-records-B-akgRch.d.ts → semantic-records-DoK0lNjS.d.ts} +439 -418
- package/dist/store.cjs +1 -1
- package/dist/store.d.ts +511 -20
- package/dist/store.js +1 -1
- package/dist/{types-barrel-CkF2JbU4.d.ts → tree-op-types-Df4oY2u4.d.ts} +1081 -524
- package/dist/{tree-package-store-B8l8DX3Z.d.ts → tree-package-store-DXhX1PE-.d.ts} +8 -3
- package/dist/{tree-session-contract-DeLRqohz.d.ts → tree-session-contract-CF2625mv.d.ts} +5 -5
- package/dist/unsupported-elements-CUojsaCr.d.ts +23 -0
- package/docs/accept-reject-without-scrolling.md +95 -0
- package/docs/comment-on-found-text.md +72 -0
- package/docs/custom-review-cards.md +178 -0
- package/docs/highlight-ranges.md +48 -0
- package/docs/index.md +36 -0
- package/docs/propose-rewrite.md +247 -0
- package/docs/tracked-replacement.md +68 -0
- package/licenses/ECMA-376.md +29 -0
- package/package.json +3 -2
- package/dist/bookmark-names-DDuAra_V.d.ts +0 -37
- package/dist/chunk-243W4WW2.cjs +0 -2
- package/dist/chunk-2E2RPEKX.js +0 -1
- package/dist/chunk-4S24UOPG.js +0 -30
- package/dist/chunk-5JPIEN54.cjs +0 -3
- package/dist/chunk-5SO5QDMV.cjs +0 -2
- package/dist/chunk-6IDOVU6L.cjs +0 -1
- package/dist/chunk-6JIJOZDW.js +0 -1
- package/dist/chunk-BTQKU64Q.cjs +0 -1
- package/dist/chunk-DBOOTED4.cjs +0 -30
- package/dist/chunk-EOTAXOJY.cjs +0 -5
- package/dist/chunk-FDAKJ2SJ.cjs +0 -30
- package/dist/chunk-FNNOJNGY.js +0 -3
- package/dist/chunk-FT33HRZP.js +0 -5
- package/dist/chunk-HC6YNKUI.cjs +0 -1
- package/dist/chunk-HZ66UCIS.js +0 -2
- package/dist/chunk-IBRBNT37.cjs +0 -1
- package/dist/chunk-IVIN7X43.js +0 -30
- package/dist/chunk-LZY6B6IV.js +0 -4
- package/dist/chunk-MOYYRVFV.cjs +0 -2
- package/dist/chunk-N6B3BXCL.js +0 -1
- package/dist/chunk-Q5K2GGNG.js +0 -2
- package/dist/chunk-R2XINWVE.js +0 -7
- package/dist/chunk-SUEXVBPR.js +0 -1
- package/dist/chunk-T2QQHG5C.js +0 -18
- package/dist/chunk-VWIJKH4A.js +0 -1
- package/dist/chunk-WO4V6PNS.cjs +0 -18
- package/dist/chunk-XMJKFURD.cjs +0 -4
- package/dist/chunk-XSTGE3NI.cjs +0 -1
- package/dist/chunk-YQWMI6J5.cjs +0 -7
- package/dist/chunk-ZCJS7D7S.js +0 -2
- package/dist/diagnostic-context-DJ0P1uZE.d.ts +0 -31
- package/dist/headless-document-view-qOpaCF82.d.ts +0 -143
- package/dist/omml-equation-BsLAa2lX.d.ts +0 -115
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a5 as OoxmlPackage, an as SupportedImageMime, O as OoxmlPart, a4 as OoxmlNode, av as OoxmlElement, aH as PackageWatermarkWrite, aI as OoxmlInvariantIssue, ad as ImageDecodePort, aE as RevisionAttributionInput, aJ as DrawingTreeDocOp, aK as ImpactClass, au as TreeDocOp, aF as TreeOpRejection, aL as OoxmlParagraphNode, aM as OoxmlNoteNode, aN as OoxmlContinuationSeparatorNode, aO as OoxmlEndnotesNode, aP as OoxmlFootnotesNode, aQ as OoxmlNoteRefNode, aR as OoxmlNoteReferenceNode, aS as OoxmlSeparatorNode } from './tree-op-types-Df4oY2u4.js';
|
|
2
2
|
|
|
3
3
|
/** A freshly allocated drawing property id, or the reason one could not be minted. */
|
|
4
4
|
type DrawingPropertyIdResult = {
|
|
@@ -1098,6 +1098,11 @@ interface FragmentPasteInput {
|
|
|
1098
1098
|
* merged document shows it. Omitted (solo) keeps Word's dense sequences.
|
|
1099
1099
|
*/
|
|
1100
1100
|
readonly actorId?: string;
|
|
1101
|
+
/**
|
|
1102
|
+
* Suggesting mode: the author and date the whole paste is proposed under, as one tracked
|
|
1103
|
+
* insertion (`trackedFragment`). The caller tracks `priorOps` itself.
|
|
1104
|
+
*/
|
|
1105
|
+
readonly revision?: RevisionAttributionInput;
|
|
1101
1106
|
}
|
|
1102
1107
|
type FragmentPasteResult = (Extract<PackageTransactResult, {
|
|
1103
1108
|
ok: true;
|
|
@@ -1381,5 +1386,5 @@ declare class TreePackageStore {
|
|
|
1381
1386
|
private publishSynthetic;
|
|
1382
1387
|
}
|
|
1383
1388
|
|
|
1384
|
-
export { NOTE_CONTINUATION_SEPARATOR_ID as B, DEFAULT_MAX_EDITABLE_STORY_PARTS as D, NOTE_ID_MAX as G, NOTE_ID_MIN as J, NOTE_SEPARATOR_ID as K, MAX_FRAGMENT_DECODED_BYTES as M, TreePackageStore as T, allocateDrawingPropertyId as a8, allocateNoteId as a9, isNoteNode as aA, isNoteRefNode as aB, isNoteReferenceNode as aC, isSeparatorNode as aD, normalNoteIds as aE, noteAtomText as aF, noteIdOf as aG, noteKindOf as aH, noteRefKindOf as aI, noteReferenceKindOf as aJ, noteTypeOf as aK, notesOf as aL, notesPartHasId as aM, observeHistoryGroup as aN, parentNodeOf as aO, parseNoteId as aP, parseNoteScopeId as aQ, removeNode as aR, replaceChildren as aS, replaceNode as aT, resolveHeaderFooterParts as aU, resolveHeaderFooterPartsBySection as aV, resolveHeaderFooterResolutionBySection as aW, resolveNotesPart as aX, withBinaryPart as aY, withEmbeddedImage as aZ, withoutUnreferencedImagePart as a_, applyEdits as aa, applyFragmentPaste as ab, applyHeaderFooterLifecycleOp as ac, applyNoteLifecycleOp as ad, atomicNoteSpansOf as ae, cascadeDeletedNoteReferences as af, collectNodeIds as ag, collectNoteReferences as ah, collectPackageNoteReferences as ai, collectSectionPropertyNodes as aj, createNodeIdAllocator as ak, createNoteReferenceScanBudget as al, customMarkFollows as am, diagnoseNoteReferences as an, findNode as ao, findNoteById as ap, formatNoteScopeId as aq, hasNode as ar, insertChildren as as, isContinuationSeparatorNode as at, isEndnotesNode as au, isFootnotesNode as av, isHeaderFooterLifecycleOp as aw, isNormalNote as ax, isNoteAtomNode as ay, isNoteLifecycleOp as az, TreeDocumentStore as
|
|
1385
|
-
export type { PackageTransactResult as $, ApplyImagePropertiesInput as A, ContentLockPolicy as C, EditOptions as E, FieldResultsMode as F,
|
|
1389
|
+
export { NOTE_CONTINUATION_SEPARATOR_ID as B, DEFAULT_MAX_EDITABLE_STORY_PARTS as D, NOTE_ID_MAX as G, NOTE_ID_MIN as J, NOTE_SEPARATOR_ID as K, MAX_FRAGMENT_DECODED_BYTES as M, TreePackageStore as T, allocateDrawingPropertyId as a8, allocateNoteId as a9, isNoteNode as aA, isNoteRefNode as aB, isNoteReferenceNode as aC, isSeparatorNode as aD, normalNoteIds as aE, noteAtomText as aF, noteIdOf as aG, noteKindOf as aH, noteRefKindOf as aI, noteReferenceKindOf as aJ, noteTypeOf as aK, notesOf as aL, notesPartHasId as aM, observeHistoryGroup as aN, parentNodeOf as aO, parseNoteId as aP, parseNoteScopeId as aQ, removeNode as aR, replaceChildren as aS, replaceNode as aT, resolveHeaderFooterParts as aU, resolveHeaderFooterPartsBySection as aV, resolveHeaderFooterResolutionBySection as aW, resolveNotesPart as aX, withBinaryPart as aY, withEmbeddedImage as aZ, withoutUnreferencedImagePart as a_, applyEdits as aa, applyFragmentPaste as ab, applyHeaderFooterLifecycleOp as ac, applyNoteLifecycleOp as ad, atomicNoteSpansOf as ae, cascadeDeletedNoteReferences as af, collectNodeIds as ag, collectNoteReferences as ah, collectPackageNoteReferences as ai, collectSectionPropertyNodes as aj, createNodeIdAllocator as ak, createNoteReferenceScanBudget as al, customMarkFollows as am, diagnoseNoteReferences as an, findNode as ao, findNoteById as ap, formatNoteScopeId as aq, hasNode as ar, insertChildren as as, isContinuationSeparatorNode as at, isEndnotesNode as au, isFootnotesNode as av, isHeaderFooterLifecycleOp as aw, isNormalNote as ax, isNoteAtomNode as ay, isNoteLifecycleOp as az, TreeDocumentStore as d, reportHistoryGroup as r, MAX_NOTES_PER_PART as w, MAX_NOTE_REFERENCE_PARTS as x, MAX_NOTE_REFERENCE_SCAN as y, NOTE_ATOM_CHAR as z };
|
|
1390
|
+
export type { PackageTransactResult as $, ApplyImagePropertiesInput as A, ContentLockPolicy as C, EditOptions as E, FieldResultsMode as F, HeaderFooterVariant as H, InsertImageInput as I, NoteDiagnostic as L, NoteKind as N, NoteDiagnosticCode as O, NoteLifecycleImpact as P, NoteLifecycleOp as Q, ReplaceImageOptions as R, StoryScope as S, NoteLifecycleOptions as U, NoteLifecycleRejection as V, NoteLifecycleResult as W, NoteReferenceHit as X, NoteReferenceScanBudget as Y, NoteType as Z, OoxmlEditResult as _, StoryTargetRejection as a, RemotePackageAttribution as a0, StoryResolveResult as a1, TransactionRevisionIds as a2, TreeDocumentStoreOptions as a3, TreePackageStoreOptions as a4, TreeStoryRef as a5, TransactResult as a6, TransactionContext as a7, HistoryGroup as b, TreeModelChange as c, HeaderFooterParts as e, HeaderFooterSectionResolution as f, SelectionMark as g, TransactOptions as h, ImageIntentResult as i, FragmentPasteInput as j, FragmentPasteResult as k, AtomicNoteSpan as l, CascadeDeletedNoteReferencesOptions as m, DrawingPropertyIdResult as n, HeaderFooterKind as o, HeaderFooterLifecycleImpact as p, HeaderFooterLifecycleOp as q, HeaderFooterLifecycleRejection as s, HeaderFooterLifecycleResult as t, HeaderFooterSlotMeta as u, HistoryCaptureKind as v };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { S as StoryScope, g as SelectionMark, h as TransactOptions,
|
|
2
|
-
import {
|
|
3
|
-
import { D as DocumentTrackingSettings, I as InsertCustomNodeWrite, C as CustomNodeWriteResult, a as CustomNodeSweepOutcome, E as EmbeddedFont, B as BookmarkIndex, L as ListKind } from './custom-node-writes-
|
|
4
|
-
import { H as HeadlessDocumentView,
|
|
1
|
+
import { S as StoryScope, g as SelectionMark, h as TransactOptions, b as HistoryGroup, a as StoryTargetRejection, c as TreeModelChange, e as HeaderFooterParts, f as HeaderFooterSectionResolution, I as InsertImageInput, i as ImageIntentResult, R as ReplaceImageOptions, A as ApplyImagePropertiesInput, j as FragmentPasteInput, k as FragmentPasteResult } from './tree-package-store-DXhX1PE-.js';
|
|
2
|
+
import { au as TreeDocOp, O as OoxmlPart, a4 as OoxmlNode, a5 as OoxmlPackage, av as OoxmlElement, a as ReviewItem, an as SupportedImageMime, ad as ImageDecodePort, aE as RevisionAttributionInput } from './tree-op-types-Df4oY2u4.js';
|
|
3
|
+
import { D as DocumentTrackingSettings, I as InsertCustomNodeWrite, C as CustomNodeWriteResult, a as CustomNodeSweepOutcome, E as EmbeddedFont, B as BookmarkIndex, L as ListKind } from './custom-node-writes-AqY5KpVx.js';
|
|
4
|
+
import { H as HeadlessDocumentView, c as DocumentProperties } from './drawing-graphic-kind-CXYGYbB3.js';
|
|
5
5
|
import { Node } from 'prosemirror-model';
|
|
6
6
|
import { CollaborationDocumentPort } from './collaboration/replication.js';
|
|
7
|
-
import { V as ViewScope } from './editor-scope-
|
|
7
|
+
import { V as ViewScope } from './editor-scope-CA6egMaK.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Why an edited projection could not be mapped back into tree ops.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { U as UnsupportedElement } from './semantic-records-DoK0lNjS.js';
|
|
2
|
+
|
|
3
|
+
/** A component mount point and its built-in fallback. @public */
|
|
4
|
+
interface UnsupportedElementHost {
|
|
5
|
+
/** Stable identity for one painted occurrence. */
|
|
6
|
+
readonly key: string;
|
|
7
|
+
readonly element: HTMLElement;
|
|
8
|
+
readonly fallback: HTMLElement;
|
|
9
|
+
readonly slot: HTMLElement;
|
|
10
|
+
readonly unsupported: UnsupportedElement;
|
|
11
|
+
/** Hide the default and expose custom controls to assistive technology. Restore with false. */
|
|
12
|
+
setCustomized(custom: boolean): void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Observe placeholder mounts across repaint, pagination, and document replacement.
|
|
16
|
+
* The returned function disconnects the observer. Component mutations do not publish
|
|
17
|
+
* another list unless the engine's mount points change.
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
declare function observeUnsupportedElements(container: HTMLElement, listener: (hosts: readonly UnsupportedElementHost[]) => void): () => void;
|
|
21
|
+
|
|
22
|
+
export { observeUnsupportedElements as o };
|
|
23
|
+
export type { UnsupportedElementHost as U };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<!-- Generated from docs/site/content/recipes by scripts/sync-agent-docs.mjs. Edit the source and run `bun run docs:agent`. -->
|
|
2
|
+
|
|
3
|
+
# Accept or reject without scrolling
|
|
4
|
+
|
|
5
|
+
When your own list drives the scroll, open an item with `useReview().setActive(key, { reveal: false })`. The caret moves to the item, and the page stays where it is. Then resolve the item with `accept(item)` or `reject(item)`. Review writes such as accept, reject, and comment never scroll the page.
|
|
6
|
+
|
|
7
|
+
**React**
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { useReview, type ReviewItemView } from '@docx-editor.dev/pro/react';
|
|
11
|
+
|
|
12
|
+
export function ChangeRow({ item }: { item: ReviewItemView }) {
|
|
13
|
+
const { setActive, accept, reject } = useReview();
|
|
14
|
+
return (
|
|
15
|
+
<li onClick={() => setActive(item.key, { reveal: false })}>
|
|
16
|
+
{item.text}
|
|
17
|
+
<button
|
|
18
|
+
disabled={item.readOnly}
|
|
19
|
+
onClick={(event) => {
|
|
20
|
+
event.stopPropagation();
|
|
21
|
+
accept(item);
|
|
22
|
+
}}
|
|
23
|
+
>
|
|
24
|
+
Accept
|
|
25
|
+
</button>
|
|
26
|
+
<button
|
|
27
|
+
disabled={item.readOnly}
|
|
28
|
+
onClick={(event) => {
|
|
29
|
+
event.stopPropagation();
|
|
30
|
+
reject(item);
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
33
|
+
Reject
|
|
34
|
+
</button>
|
|
35
|
+
</li>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`accept` and `reject` return `false` when the editor refuses the decision, for example in viewing mode. Read `useReview().lastRefusal` for the diagnostic code.
|
|
41
|
+
|
|
42
|
+
**Vue**
|
|
43
|
+
|
|
44
|
+
```vue
|
|
45
|
+
<script setup lang="ts">
|
|
46
|
+
import { useReview, type ReviewItemView } from '@docx-editor.dev/pro/vue';
|
|
47
|
+
|
|
48
|
+
defineProps<{ item: ReviewItemView }>();
|
|
49
|
+
const { setActive, accept, reject } = useReview();
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<template>
|
|
53
|
+
<li @click="setActive(item.key, { reveal: false })">
|
|
54
|
+
{{ item.text }}
|
|
55
|
+
<button :disabled="item.readOnly" @click.stop="accept(item)">Accept</button>
|
|
56
|
+
<button :disabled="item.readOnly" @click.stop="reject(item)">Reject</button>
|
|
57
|
+
</li>
|
|
58
|
+
</template>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`accept` and `reject` return `false` when the editor refuses the decision, for example in viewing mode. `useReview()` returns `lastRefusal` as a ref, so read `lastRefusal.value?.code` in script, or `lastRefusal?.code` in the template.
|
|
62
|
+
|
|
63
|
+
The buttons stop the click from reaching the row, so a decision does not also activate the item.
|
|
64
|
+
|
|
65
|
+
`reveal` takes `false` or one of four placements:
|
|
66
|
+
|
|
67
|
+
| Value | Where the item lands |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `'centerIfNeeded'` (default) | Stays still when the item is on screen; centered when it has to travel. |
|
|
70
|
+
| `'center'` | Always centered. |
|
|
71
|
+
| `'nearest'` | The smallest scroll that brings it into view. |
|
|
72
|
+
| `'start'` | Near the top of the viewport. |
|
|
73
|
+
|
|
74
|
+
Add `announce: true` to open the packaged balloon or card for the item.
|
|
75
|
+
|
|
76
|
+
## Decide in a headless pipeline
|
|
77
|
+
|
|
78
|
+
The editing API has no viewport. Load the revisions and call `Revision.accept()` or `Revision.reject()`.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { DocxEditor } from '@docx-editor.dev/editor-api';
|
|
82
|
+
|
|
83
|
+
const runtime = await DocxEditor.createServer(bytes, { author: 'Review bot' });
|
|
84
|
+
await runtime.run(async (context) => {
|
|
85
|
+
const revisions = context.document.body.revisions;
|
|
86
|
+
revisions.load('items/author');
|
|
87
|
+
await context.sync();
|
|
88
|
+
for (const revision of revisions.items) {
|
|
89
|
+
if (revision.author === 'Review bot') revision.accept();
|
|
90
|
+
}
|
|
91
|
+
await context.sync();
|
|
92
|
+
});
|
|
93
|
+
const saved = await runtime.save();
|
|
94
|
+
runtime.dispose();
|
|
95
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<!-- Generated from docs/site/content/recipes by scripts/sync-agent-docs.mjs. Edit the source and run `bun run docs:agent`. -->
|
|
2
|
+
|
|
3
|
+
# Comment on found text
|
|
4
|
+
|
|
5
|
+
In a browser host, find the text with `editor.findMatches`, select the first match with `editor.selectMatch`, and then call `useReview().comment`. The comment is written as the editor's `author`, so set `author` on the editor and add `reviewModule()` to its modules. `selectMatch` opens the header, footer, or note that holds the match, so a match in any story works.
|
|
6
|
+
|
|
7
|
+
**React**
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { useDocxEditor } from '@docx-editor.dev/react';
|
|
11
|
+
import { useReview } from '@docx-editor.dev/pro/react';
|
|
12
|
+
|
|
13
|
+
export function CommentOnTerm({ term }: { term: string }) {
|
|
14
|
+
const editor = useDocxEditor();
|
|
15
|
+
const { comment } = useReview();
|
|
16
|
+
const run = () => {
|
|
17
|
+
const [match] = editor?.findMatches(term, { wholeWord: true }) ?? [];
|
|
18
|
+
if (match && editor?.selectMatch(match).ok) comment(`Define "${term}".`);
|
|
19
|
+
};
|
|
20
|
+
return <button onClick={run}>Comment on {term}</button>;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`comment` returns `false` when the editor refuses the write. Read `useReview().lastRefusal` for the diagnostic code.
|
|
25
|
+
|
|
26
|
+
**Vue**
|
|
27
|
+
|
|
28
|
+
```vue
|
|
29
|
+
<script setup lang="ts">
|
|
30
|
+
import { useDocxEditor } from '@docx-editor.dev/vue';
|
|
31
|
+
import { useReview } from '@docx-editor.dev/pro/vue';
|
|
32
|
+
|
|
33
|
+
const props = defineProps<{ term: string }>();
|
|
34
|
+
const editor = useDocxEditor();
|
|
35
|
+
const { comment } = useReview();
|
|
36
|
+
function run() {
|
|
37
|
+
const [match] = editor.value?.findMatches(props.term, { wholeWord: true }) ?? [];
|
|
38
|
+
if (match && editor.value?.selectMatch(match).ok) comment(`Define "${props.term}".`);
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template>
|
|
43
|
+
<button @click="run">Comment on {{ term }}</button>
|
|
44
|
+
</template>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`comment` returns `false` when the editor refuses the write. `useReview()` returns `lastRefusal` as a ref, so read `lastRefusal.value?.code` in script, or `lastRefusal?.code` in the template.
|
|
48
|
+
|
|
49
|
+
## Comment in a headless pipeline
|
|
50
|
+
|
|
51
|
+
On a server or in an agent pipeline, search the body and call `Range.insertComment`. The runtime writes the comment as its `author` option.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { DocxEditor } from '@docx-editor.dev/editor-api';
|
|
55
|
+
|
|
56
|
+
const runtime = await DocxEditor.createServer(bytes, { author: 'Review bot' });
|
|
57
|
+
await runtime.run(async (context) => {
|
|
58
|
+
const found = context.document.body.search(term, { matchWholeWord: true });
|
|
59
|
+
found.load('items');
|
|
60
|
+
await context.sync();
|
|
61
|
+
found.items[0]?.insertComment(`Define "${term}".`);
|
|
62
|
+
await context.sync();
|
|
63
|
+
});
|
|
64
|
+
const saved = await runtime.save();
|
|
65
|
+
runtime.dispose();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Both paths save the same comment markup: the same `w:comment` attributes, the same date format, and the same range markers.
|
|
69
|
+
|
|
70
|
+
## Reply to a thread
|
|
71
|
+
|
|
72
|
+
To answer an existing comment, call `useReview().reply(item, text)` in a browser host, or `Comment.reply(text)` in the editing API.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<!-- Generated from docs/site/content/recipes by scripts/sync-agent-docs.mjs. Edit the source and run `bun run docs:agent`. -->
|
|
2
|
+
|
|
3
|
+
# Custom cards in the review rail
|
|
4
|
+
|
|
5
|
+
To draw your own cards, render `DocxEditorReview` with `preset` set to `false` and put your card component inside it. Your component reads the items from `useReview()`, measures each card, stacks the cards with `useStackedReviewPositions`, and converts positions to pixels with `useReviewRailMetrics()`.
|
|
6
|
+
|
|
7
|
+
Measure every card with a `ResizeObserver` and pass the heights in CSS pixels. Without measured heights, each card reserves only `defaultHeight`, and a taller card overlaps the next one.
|
|
8
|
+
|
|
9
|
+
**React**
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react';
|
|
13
|
+
import {
|
|
14
|
+
DocxEditorReview,
|
|
15
|
+
useReview,
|
|
16
|
+
useReviewRailMetrics,
|
|
17
|
+
useStackedReviewPositions,
|
|
18
|
+
} from '@docx-editor.dev/pro/react';
|
|
19
|
+
|
|
20
|
+
type OnHeight = (key: string, height: number) => void;
|
|
21
|
+
|
|
22
|
+
function MeasuredCard({
|
|
23
|
+
id,
|
|
24
|
+
top,
|
|
25
|
+
onHeight,
|
|
26
|
+
children,
|
|
27
|
+
}: {
|
|
28
|
+
id: string;
|
|
29
|
+
top: number;
|
|
30
|
+
onHeight: OnHeight;
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
}) {
|
|
33
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const node = ref.current!;
|
|
36
|
+
const observer = new ResizeObserver(() => onHeight(id, node.offsetHeight));
|
|
37
|
+
observer.observe(node);
|
|
38
|
+
return () => observer.disconnect();
|
|
39
|
+
}, [id, onHeight]);
|
|
40
|
+
return (
|
|
41
|
+
<div ref={ref} style={{ position: 'absolute', top }}>
|
|
42
|
+
{children}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function MyCards() {
|
|
48
|
+
const { items, selectionAnchorY, accept } = useReview();
|
|
49
|
+
const metrics = useReviewRailMetrics();
|
|
50
|
+
const [heights, setHeights] = useState<ReadonlyMap<string, number>>(() => new Map());
|
|
51
|
+
const onHeight = useCallback<OnHeight>(
|
|
52
|
+
(key, height) =>
|
|
53
|
+
setHeights((prev) => (prev.get(key) === height ? prev : new Map(prev).set(key, height))),
|
|
54
|
+
[]
|
|
55
|
+
);
|
|
56
|
+
const positions = useStackedReviewPositions(items, heights, {
|
|
57
|
+
scale: metrics.scale,
|
|
58
|
+
defaultHeight: 96,
|
|
59
|
+
});
|
|
60
|
+
const top = (y: number) => metrics.top + y * metrics.scale;
|
|
61
|
+
return (
|
|
62
|
+
<>
|
|
63
|
+
{items.map((item) => (
|
|
64
|
+
<MeasuredCard
|
|
65
|
+
key={item.key}
|
|
66
|
+
id={item.key}
|
|
67
|
+
top={top(positions.get(item.key) ?? 0)}
|
|
68
|
+
onHeight={onHeight}
|
|
69
|
+
>
|
|
70
|
+
{item.text}
|
|
71
|
+
{item.kind === 'revision' && <button onClick={() => accept(item)}>Accept</button>}
|
|
72
|
+
</MeasuredCard>
|
|
73
|
+
))}
|
|
74
|
+
<DocxEditorReview.Draft top={top(selectionAnchorY ?? 0)} />
|
|
75
|
+
</>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function MyRail() {
|
|
80
|
+
return (
|
|
81
|
+
<DocxEditorReview preset={false}>
|
|
82
|
+
<MyCards />
|
|
83
|
+
<DocxEditorReview.AddComment />
|
|
84
|
+
</DocxEditorReview>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The heights map gets a new reference only when a height changes, so the stack recomputes only then. Keep that rule if you store heights another way.
|
|
90
|
+
|
|
91
|
+
**Vue**
|
|
92
|
+
|
|
93
|
+
```vue
|
|
94
|
+
<!-- MyCards.vue -->
|
|
95
|
+
<script setup lang="ts">
|
|
96
|
+
import { onBeforeUnmount, shallowReactive } from 'vue';
|
|
97
|
+
import {
|
|
98
|
+
DocxEditorReview,
|
|
99
|
+
useReview,
|
|
100
|
+
useReviewRailMetrics,
|
|
101
|
+
useStackedReviewPositions,
|
|
102
|
+
} from '@docx-editor.dev/pro/vue';
|
|
103
|
+
|
|
104
|
+
const { items, selectionAnchorY, accept } = useReview();
|
|
105
|
+
const { metrics } = useReviewRailMetrics();
|
|
106
|
+
// Reactive, so a new measurement restacks the cards.
|
|
107
|
+
const heights = shallowReactive(new Map<string, number>());
|
|
108
|
+
const positions = useStackedReviewPositions(items, heights, () => ({
|
|
109
|
+
scale: metrics.value.scale,
|
|
110
|
+
defaultHeight: 96,
|
|
111
|
+
}));
|
|
112
|
+
const top = (y: number) => `${metrics.value.top + y * metrics.value.scale}px`;
|
|
113
|
+
|
|
114
|
+
const observed = new Map<string, { el: Element; observer: ResizeObserver }>();
|
|
115
|
+
function measure(key: string, el: unknown) {
|
|
116
|
+
const current = observed.get(key);
|
|
117
|
+
if (current?.el === el) return;
|
|
118
|
+
current?.observer.disconnect();
|
|
119
|
+
observed.delete(key);
|
|
120
|
+
if (!(el instanceof HTMLElement)) {
|
|
121
|
+
heights.delete(key);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const observer = new ResizeObserver(() => heights.set(key, el.offsetHeight));
|
|
125
|
+
observer.observe(el);
|
|
126
|
+
observed.set(key, { el, observer });
|
|
127
|
+
}
|
|
128
|
+
onBeforeUnmount(() => observed.forEach(({ observer }) => observer.disconnect()));
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<template>
|
|
132
|
+
<div
|
|
133
|
+
v-for="item in items"
|
|
134
|
+
:key="item.key"
|
|
135
|
+
:ref="(el) => measure(item.key, el)"
|
|
136
|
+
:style="{ position: 'absolute', top: top(positions.get(item.key) ?? 0) }"
|
|
137
|
+
>
|
|
138
|
+
{{ item.text }}
|
|
139
|
+
<button v-if="item.kind === 'revision'" @click="accept(item)">Accept</button>
|
|
140
|
+
</div>
|
|
141
|
+
<DocxEditorReview.Draft :top="metrics.top + (selectionAnchorY ?? 0) * metrics.scale" />
|
|
142
|
+
</template>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```vue
|
|
146
|
+
<!-- MyRail.vue -->
|
|
147
|
+
<script setup lang="ts">
|
|
148
|
+
import { DocxEditorReview } from '@docx-editor.dev/pro/vue';
|
|
149
|
+
import MyCards from './MyCards.vue';
|
|
150
|
+
</script>
|
|
151
|
+
|
|
152
|
+
<template>
|
|
153
|
+
<DocxEditorReview :preset="false">
|
|
154
|
+
<MyCards />
|
|
155
|
+
<DocxEditorReview.AddComment />
|
|
156
|
+
</DocxEditorReview>
|
|
157
|
+
</template>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
A plain `Map` is not reactive, so writes to it would not restack the cards. Use `shallowReactive(new Map())`, or a ref that you replace on each change.
|
|
161
|
+
|
|
162
|
+
## Root parts and card parts
|
|
163
|
+
|
|
164
|
+
`DocxEditorReview` finds its parts among its own children by element type.
|
|
165
|
+
|
|
166
|
+
- **Root parts** are `List`, `Markers`, `AddComment`, `Draft`, and `Balloon`. Put them as direct children of `DocxEditorReview`. Fragments are transparent in React and in Vue. The rail gives these parts geometry that only it can compute, such as the comment button's `top`. A root part inside your own wrapper component is not a part. It renders as ordinary content, without that wiring.
|
|
167
|
+
- **Card parts** are `Card`, `Empty`, and the parts inside a card, such as `Author`, `Summary`, `Accept`, and `Reply`. Put them inside `DocxEditorReview.List`.
|
|
168
|
+
|
|
169
|
+
With `preset` set to `false`, the rail adds no defaults. Root parts that you pass keep their wiring. Every other child renders as is inside the rail element, where `useReviewRailMetrics()` reads the rail's own measurement.
|
|
170
|
+
|
|
171
|
+
## Position the compose box
|
|
172
|
+
|
|
173
|
+
`DocxEditorReview.Draft` renders only while a comment draft is open. A draft opens from `DocxEditorReview.AddComment` or from the `review.addComment` toolbar control. You can place `Draft` in either of these places:
|
|
174
|
+
|
|
175
|
+
- As a direct child of `DocxEditorReview`.
|
|
176
|
+
- Inside your own card component, as in the example. It still renders only while the draft is open.
|
|
177
|
+
|
|
178
|
+
Without `top`, the box sits where the packaged card stack puts it. That position comes from the packaged cards, not yours, so when you render your own cards, pass `top` from your own stack. In the narrow compact rail, the box takes the compact card's `left` in both cases, so it does not overflow the marker strip. The example places the box at the selection's anchor. To keep it clear of your cards, add an entry with the key of your choice and the anchor `selectionAnchorY` to the list that you pass to `useStackedReviewPositions`, in document order.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!-- Generated from docs/site/content/recipes by scripts/sync-agent-docs.mjs. Edit the source and run `bun run docs:agent`. -->
|
|
2
|
+
|
|
3
|
+
# Highlight ranges
|
|
4
|
+
|
|
5
|
+
Use `useHighlights(name, source, options)` to mark text as a named set while a component is mounted. A function source runs again after document changes, so the marks follow the text. Highlights are view state. They do not change the document and are not saved.
|
|
6
|
+
|
|
7
|
+
**React**
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { useCallback } from 'react';
|
|
11
|
+
import { useHighlights, type Editor } from '@docx-editor.dev/react';
|
|
12
|
+
|
|
13
|
+
export function GlossaryMarks({ terms }: { terms: readonly string[] }) {
|
|
14
|
+
const findTerms = useCallback(
|
|
15
|
+
(editor: Editor) => editor.findMatches(terms, { wholeWord: true }).flat(),
|
|
16
|
+
[terms]
|
|
17
|
+
);
|
|
18
|
+
const { applied } = useHighlights('glossary', findTerms, { className: 'glossary-term' });
|
|
19
|
+
return <span>{applied} marked</span>;
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Keep a function source stable with `useCallback`.
|
|
24
|
+
|
|
25
|
+
**Vue**
|
|
26
|
+
|
|
27
|
+
```vue
|
|
28
|
+
<script setup lang="ts">
|
|
29
|
+
import { useHighlights, type Editor } from '@docx-editor.dev/vue';
|
|
30
|
+
|
|
31
|
+
const props = defineProps<{ terms: readonly string[] }>();
|
|
32
|
+
const result = useHighlights(
|
|
33
|
+
'glossary',
|
|
34
|
+
(editor: Editor) => editor.findMatches(props.terms, { wholeWord: true }).flat(),
|
|
35
|
+
{ className: 'glossary-term' }
|
|
36
|
+
);
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<span>{{ result.applied }} marked</span>
|
|
41
|
+
</template>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
A function source that reads props or refs runs again when they change.
|
|
45
|
+
|
|
46
|
+
Use a unique `name` for each set. Two calls with the same name replace each other's ranges. Set `activeIndex` in the options to show one range in the active color, for example the current result of your own search list.
|
|
47
|
+
|
|
48
|
+
Outside a component, call `watchHighlights(editor, name, source, options)` from `@docx-editor.dev/core/editor`, or `editor.setHighlights` for a fixed set.
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!-- Generated from docs/site/content/recipes by scripts/sync-agent-docs.mjs. Edit the source and run `bun run docs:agent`. -->
|
|
2
|
+
|
|
3
|
+
# Which API for which job
|
|
4
|
+
|
|
5
|
+
The library has two ways to change a document. Each one fits a different host. Choose the path first, then use the symbol for your task.
|
|
6
|
+
|
|
7
|
+
| Job | Use |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| Build a review UI in the browser | The React or Vue adapter hooks, plus the review hooks and `DocxEditorReview` from `@docx-editor.dev/pro/react` or `@docx-editor.dev/pro/vue`. |
|
|
10
|
+
| Edit in a headless, server, or agent pipeline | The editing API in `@docx-editor.dev/editor-api`, which follows the Office.js object model. |
|
|
11
|
+
|
|
12
|
+
| Task | Browser host (React or Vue) | Headless, server, or agent |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| [Comment on found text](./comment-on-found-text.md) | `editor.findMatches`, `editor.selectMatch`, then `useReview().comment` | `body.search`, then `Range.insertComment` |
|
|
15
|
+
| Reply to a thread | `useReview().reply(item, text)` | `Comment.reply(text)` |
|
|
16
|
+
| [Record a tracked replacement](./tracked-replacement.md) | Suggesting mode, then `editor.exec({ type: 'replaceMatch', match, text })` | `document.changeTrackingMode = 'TrackMineOnly'`, then `Range.insertText(text, 'Replace')` |
|
|
17
|
+
| [Propose an AI rewrite as tracked changes](./propose-rewrite.md) | The same code through `DocxEditor.createBrowser` from `@docx-editor.dev/editor-api/browser` | `prepareRewrite` from `@docx-editor.dev/editor-api/agent` |
|
|
18
|
+
| [Accept or reject a change](./accept-reject-without-scrolling.md) | `useReview().accept(item)` or `useReview().reject(item)` | `Revision.accept()` or `Revision.reject()` |
|
|
19
|
+
| [Open or reveal a change without scrolling](./accept-reject-without-scrolling.md) | `useReview().setActive(key, { reveal: false })` | Not applicable: no viewport |
|
|
20
|
+
| [Highlight ranges](./highlight-ranges.md) | `useHighlights(name, source, options)` | Not applicable |
|
|
21
|
+
| [Custom review rail cards](./custom-review-cards.md) | `DocxEditorReview` with `preset` set to `false`, `useReview().items`, `useStackedReviewPositions`, and `useReviewRailMetrics` | Not applicable |
|
|
22
|
+
| Comment-on-selection button | `DocxEditorReview.AddComment` as a direct child of `DocxEditorReview`, or `useEditorCommand('review.addComment')` | Not applicable |
|
|
23
|
+
| Page setup and paragraph dialogs from a custom toolbar | `DocxEditor.PageSetupDialog` or `DocxEditor.ParagraphDialog` with `open` and a close handler (`onClose` in React, `@close` in Vue); enable the button from `usePageSetup().isEnabled` or `useParagraphFormat().isEnabled` | `Section.pageSetup`, and `Paragraph` properties such as `alignment` |
|
|
24
|
+
|
|
25
|
+
The review hooks need `reviewModule()` from `@docx-editor.dev/pro` in the editor's modules, and an editor `author` for comments and tracked changes. The editing API takes its author from the `author` option of `DocxEditor.createServer`, imported from `@docx-editor.dev/editor-api`, or of `DocxEditor.createBrowser`, imported from `@docx-editor.dev/editor-api/browser`.
|
|
26
|
+
|
|
27
|
+
## Recipes
|
|
28
|
+
|
|
29
|
+
- [Comment on found text](./comment-on-found-text.md)
|
|
30
|
+
- [Replace as a tracked change](./tracked-replacement.md)
|
|
31
|
+
- [Propose an AI rewrite as tracked changes](./propose-rewrite.md)
|
|
32
|
+
- [Custom cards in the review rail](./custom-review-cards.md)
|
|
33
|
+
- [Highlight ranges](./highlight-ranges.md)
|
|
34
|
+
- [Accept or reject without scrolling](./accept-reject-without-scrolling.md)
|
|
35
|
+
|
|
36
|
+
For the full hook reference, see [React hooks](https://www.docx-editor.dev/docs/2.x/react/hooks) and [Vue composables](https://www.docx-editor.dev/docs/2.x/vue/composables). For the editing API, see [Editing API](https://www.docx-editor.dev/docs/2.x/editor-api).
|