@docx-editor.dev/react 2.35.0-canary.20261104032723.sha-43c2012 → 2.35.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 +7 -12
- package/dist/index.d.mts +96 -5
- package/dist/index.d.ts +96 -5
- package/dist/index.js +3 -3
- package/dist/index.mjs +3 -3
- package/docs/accept-reject-without-scrolling.md +22 -38
- package/docs/comment-on-found-text.md +38 -19
- package/docs/custom-review-cards.md +39 -25
- package/docs/highlight-ranges.md +25 -4
- package/docs/index.md +21 -21
- package/docs/propose-rewrite.md +87 -138
- package/docs/tracked-replacement.md +36 -18
- package/package.json +3 -3
|
@@ -2,44 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
# Accept or reject without scrolling
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Resolve a tracked change from your own list with `useReview().accept(item)` or `reject(item)`. Review writes such as accept, reject, and comment never scroll the page, and they need no prior activation.
|
|
6
|
+
|
|
7
|
+
## Resolve a change without scrolling
|
|
8
|
+
|
|
9
|
+
The following row opens its item without scrolling and resolves it from two buttons:
|
|
6
10
|
|
|
7
11
|
```tsx
|
|
8
12
|
import { useReview, type ReviewItemView } from '@docx-editor.dev/pro/react';
|
|
9
13
|
|
|
10
14
|
export function ChangeRow({ item }: { item: ReviewItemView }) {
|
|
11
|
-
const { setActive, accept, reject } = useReview();
|
|
15
|
+
const { setActive, accept, reject, lastRefusal } = useReview();
|
|
12
16
|
return (
|
|
13
|
-
<li
|
|
14
|
-
{item.text}
|
|
15
|
-
<button
|
|
16
|
-
disabled={item.readOnly}
|
|
17
|
-
onClick={(event) => {
|
|
18
|
-
event.stopPropagation();
|
|
19
|
-
accept(item);
|
|
20
|
-
}}
|
|
21
|
-
>
|
|
17
|
+
<li>
|
|
18
|
+
<button onClick={() => setActive(item.key, { reveal: false })}>{item.text}</button>
|
|
19
|
+
<button disabled={item.readOnly} onClick={() => accept(item)}>
|
|
22
20
|
Accept
|
|
23
21
|
</button>
|
|
24
|
-
<button
|
|
25
|
-
disabled={item.readOnly}
|
|
26
|
-
onClick={(event) => {
|
|
27
|
-
event.stopPropagation();
|
|
28
|
-
reject(item);
|
|
29
|
-
}}
|
|
30
|
-
>
|
|
22
|
+
<button disabled={item.readOnly} onClick={() => reject(item)}>
|
|
31
23
|
Reject
|
|
32
24
|
</button>
|
|
25
|
+
{lastRefusal?.key === item.key && <p role="alert">{lastRefusal.code}</p>}
|
|
33
26
|
</li>
|
|
34
27
|
);
|
|
35
28
|
}
|
|
36
29
|
```
|
|
37
30
|
|
|
38
|
-
`accept` and `reject` return `false` when the editor refuses the decision, for example in viewing mode.
|
|
31
|
+
`accept` and `reject` return `false` when the editor refuses the decision, for example in viewing mode. `lastRefusal` holds the item key and the diagnostic code. In Vue it is a ref, so read `lastRefusal.value?.code` in script.
|
|
39
32
|
|
|
40
|
-
|
|
33
|
+
## Activate without scrolling
|
|
41
34
|
|
|
42
|
-
`reveal` takes `false` or one of four placements:
|
|
35
|
+
`setActive(key, { reveal: false })` moves the caret to the item and keeps the scroll position. `reveal` takes `false` or one of four placements:
|
|
43
36
|
|
|
44
37
|
| Value | Where the item lands |
|
|
45
38
|
| --- | --- |
|
|
@@ -50,23 +43,14 @@ The buttons stop the click from reaching the row, so a decision does not also ac
|
|
|
50
43
|
|
|
51
44
|
Add `announce: true` to open the packaged balloon or card for the item.
|
|
52
45
|
|
|
53
|
-
##
|
|
46
|
+
## Limits
|
|
54
47
|
|
|
55
|
-
|
|
48
|
+
- Disable the decision buttons for items where `item.readOnly` is `true`.
|
|
49
|
+
- If the whole row is clickable, stop button clicks from reaching it, so a decision does not also activate the item.
|
|
50
|
+
- Headless pipelines have no viewport. To decide changes on a server, call `Revision.accept()` or `Revision.reject()`. For more information, see [Tracked changes](https://www.docx-editor.dev/docs/2.x/editor-api/revisions).
|
|
56
51
|
|
|
57
|
-
|
|
58
|
-
import { DocxEditor } from '@docx-editor.dev/editor-api';
|
|
52
|
+
## See also
|
|
59
53
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
revisions.load('items/author');
|
|
64
|
-
await context.sync();
|
|
65
|
-
for (const revision of revisions.items) {
|
|
66
|
-
if (revision.author === 'Review bot') revision.accept();
|
|
67
|
-
}
|
|
68
|
-
await context.sync();
|
|
69
|
-
});
|
|
70
|
-
const saved = await runtime.save();
|
|
71
|
-
runtime.dispose();
|
|
72
|
-
```
|
|
54
|
+
- [Tracked changes](https://www.docx-editor.dev/docs/2.x/pro/tracked-changes)
|
|
55
|
+
- [Diagnostics](https://www.docx-editor.dev/docs/2.x/guides/diagnostics)
|
|
56
|
+
- [Custom cards in the review rail](./custom-review-cards.md)
|
|
@@ -2,46 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
# Comment on found text
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Find text with `editor.findMatches`, select a match with `editor.selectMatch`, and comment with `useReview().comment`. On a server, search with `body.search` and call `Range.insertComment`.
|
|
6
|
+
|
|
7
|
+
## Comment on a search result
|
|
8
|
+
|
|
9
|
+
The following button comments on the first whole-word match of a term. The editor needs an `author` and `reviewModule()` in its modules.
|
|
6
10
|
|
|
7
11
|
```tsx
|
|
12
|
+
import { useState } from 'react';
|
|
8
13
|
import { useDocxEditor } from '@docx-editor.dev/react';
|
|
9
14
|
import { useReview } from '@docx-editor.dev/pro/react';
|
|
10
15
|
|
|
11
16
|
export function CommentOnTerm({ term }: { term: string }) {
|
|
12
17
|
const editor = useDocxEditor();
|
|
13
|
-
const { comment } = useReview();
|
|
18
|
+
const { comment, lastRefusal } = useReview();
|
|
19
|
+
const [status, setStatus] = useState('');
|
|
14
20
|
const run = () => {
|
|
15
21
|
const [match] = editor?.findMatches(term, { wholeWord: true }) ?? [];
|
|
16
|
-
if (match
|
|
22
|
+
if (!match || !editor?.selectMatch(match).ok) return setStatus('No match');
|
|
23
|
+
setStatus(comment(`Define "${term}".`) ? '' : 'Refused');
|
|
17
24
|
};
|
|
18
|
-
return
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
<button onClick={run}>Comment on {term}</button>
|
|
28
|
+
{/* lastRefusal updates on the next render, so read it here. */}
|
|
29
|
+
<span role="status">{status === 'Refused' ? lastRefusal?.code : status}</span>
|
|
30
|
+
</>
|
|
31
|
+
);
|
|
19
32
|
}
|
|
20
33
|
```
|
|
21
34
|
|
|
22
|
-
`comment` returns `false` when the editor refuses the write
|
|
35
|
+
Comment only after `selectMatch(match).ok` is `true`. `selectMatch` opens the header, footer, or note that holds the match. `comment` returns `false` when the editor refuses the write, and `lastRefusal` holds the diagnostic code.
|
|
23
36
|
|
|
24
|
-
## Comment
|
|
37
|
+
## Comment on a server
|
|
25
38
|
|
|
26
|
-
|
|
39
|
+
The following example searches the body, not every story, and saves the result:
|
|
27
40
|
|
|
28
41
|
```ts
|
|
29
42
|
import { DocxEditor } from '@docx-editor.dev/editor-api';
|
|
30
43
|
|
|
31
44
|
const runtime = await DocxEditor.createServer(bytes, { author: 'Review bot' });
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
try {
|
|
46
|
+
await runtime.run(async (context) => {
|
|
47
|
+
const found = context.document.body.search(term, { matchWholeWord: true });
|
|
48
|
+
found.load('items');
|
|
49
|
+
await context.sync();
|
|
50
|
+
if (found.items.length === 0) return;
|
|
51
|
+
found.items[0]!.insertComment(`Define "${term}".`);
|
|
52
|
+
await context.sync();
|
|
53
|
+
});
|
|
54
|
+
const saved = await runtime.save();
|
|
55
|
+
} finally {
|
|
56
|
+
runtime.dispose();
|
|
57
|
+
}
|
|
41
58
|
```
|
|
42
59
|
|
|
43
|
-
|
|
60
|
+
Load the search results before you read them, and end with `await context.sync()` to send the queued comment. The runtime writes the comment as its `author` option.
|
|
44
61
|
|
|
45
|
-
##
|
|
62
|
+
## See also
|
|
46
63
|
|
|
47
|
-
|
|
64
|
+
- [Comments](https://www.docx-editor.dev/docs/2.x/pro/comments)
|
|
65
|
+
- [Diagnostics](https://www.docx-editor.dev/docs/2.x/guides/diagnostics)
|
|
66
|
+
- [Replace as a tracked change](./tracked-replacement.md)
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
# Custom cards in the review rail
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Render `DocxEditorReview` with `preset` set to `false` and put your card component inside it. The component reads items from `useReview()`, stacks measured cards with `useStackedReviewPositions`, and converts positions to pixels with `useReviewRailMetrics()`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Render custom cards
|
|
8
|
+
|
|
9
|
+
The following example measures each card with a `ResizeObserver`, stacks the cards, and shows a refused decision:
|
|
8
10
|
|
|
9
11
|
```tsx
|
|
10
12
|
import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react';
|
|
@@ -17,17 +19,8 @@ import {
|
|
|
17
19
|
|
|
18
20
|
type OnHeight = (key: string, height: number) => void;
|
|
19
21
|
|
|
20
|
-
function MeasuredCard({
|
|
21
|
-
id,
|
|
22
|
-
top,
|
|
23
|
-
onHeight,
|
|
24
|
-
children,
|
|
25
|
-
}: {
|
|
26
|
-
id: string;
|
|
27
|
-
top: number;
|
|
28
|
-
onHeight: OnHeight;
|
|
29
|
-
children: ReactNode;
|
|
30
|
-
}) {
|
|
22
|
+
function MeasuredCard(props: { id: string; top: number; onHeight: OnHeight; children: ReactNode }) {
|
|
23
|
+
const { id, top, onHeight, children } = props;
|
|
31
24
|
const ref = useRef<HTMLDivElement>(null);
|
|
32
25
|
useEffect(() => {
|
|
33
26
|
const node = ref.current!;
|
|
@@ -43,8 +36,9 @@ function MeasuredCard({
|
|
|
43
36
|
}
|
|
44
37
|
|
|
45
38
|
function MyCards() {
|
|
46
|
-
const { items, selectionAnchorY, accept } = useReview();
|
|
39
|
+
const { items, selectionAnchorY, accept, lastRefusal } = useReview();
|
|
47
40
|
const metrics = useReviewRailMetrics();
|
|
41
|
+
// A new map only when a height changes, so the stack recomputes only then.
|
|
48
42
|
const [heights, setHeights] = useState<ReadonlyMap<string, number>>(() => new Map());
|
|
49
43
|
const onHeight = useCallback<OnHeight>(
|
|
50
44
|
(key, height) =>
|
|
@@ -67,6 +61,7 @@ function MyCards() {
|
|
|
67
61
|
>
|
|
68
62
|
{item.text}
|
|
69
63
|
{item.kind === 'revision' && <button onClick={() => accept(item)}>Accept</button>}
|
|
64
|
+
{lastRefusal?.key === item.key && <p>{lastRefusal.code}</p>}
|
|
70
65
|
</MeasuredCard>
|
|
71
66
|
))}
|
|
72
67
|
<DocxEditorReview.Draft top={top(selectionAnchorY ?? 0)} />
|
|
@@ -84,22 +79,41 @@ export function MyRail() {
|
|
|
84
79
|
}
|
|
85
80
|
```
|
|
86
81
|
|
|
87
|
-
|
|
82
|
+
`accept` returns `false` when the editor refuses, and `lastRefusal` names the item and the diagnostic code. `DocxEditorReview` already announces each refusal in a live region, so show it in the card as plain text. For a translated sentence, call `useReviewRefusalMessage()`.
|
|
83
|
+
|
|
84
|
+
## Position cards and drafts
|
|
85
|
+
|
|
86
|
+
Convert a layout position to CSS pixels with `metrics.top + position * metrics.scale`.
|
|
87
|
+
|
|
88
|
+
| Input to `useStackedReviewPositions` | Description |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| `heights` | Measured card heights in CSS pixels, by item key. A card without a height reserves `defaultHeight`, and a taller card overlaps the next one. |
|
|
91
|
+
| `scale` | `metrics.scale`, CSS pixels per layout point. |
|
|
92
|
+
| `defaultHeight` | The height to reserve before a card is measured. |
|
|
93
|
+
|
|
94
|
+
`DocxEditorReview.Draft` renders only while a comment draft is open. Without `top`, it takes the packaged stack's position, so pass `top` from your own stack. To keep the box clear of your cards, add an entry at `selectionAnchorY` to the list that you stack, in document order.
|
|
88
95
|
|
|
89
|
-
##
|
|
96
|
+
## Place review parts
|
|
90
97
|
|
|
91
|
-
`DocxEditorReview` finds its parts among its own children by element type.
|
|
98
|
+
`DocxEditorReview` finds its parts among its own children by element type. Fragments are transparent in React and Vue.
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
| Part | Placement |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| `List`, `Markers`, `AddComment`, `Balloon` | Direct children of `DocxEditorReview`. Inside your own wrapper, a part renders as plain content without its geometry. |
|
|
103
|
+
| `Draft` | A direct child, or inside your own card component. Inside a custom stack, pass `top` explicitly. |
|
|
104
|
+
| `Card`, `Empty`, and card parts such as `Author`, `Summary`, `Accept`, and `Reply` | Inside `DocxEditorReview.List`. |
|
|
95
105
|
|
|
96
|
-
With `preset` set to `false`, the rail adds no defaults
|
|
106
|
+
With `preset` set to `false`, the rail adds no defaults, but recognized parts keep their wiring. Other children render inside the rail element.
|
|
97
107
|
|
|
98
|
-
##
|
|
108
|
+
## Limits
|
|
99
109
|
|
|
100
|
-
|
|
110
|
+
- Disconnect each observer when its card unmounts or its item changes.
|
|
111
|
+
- In React, keep the heights map reference-stable. In Vue, keep it reactive.
|
|
112
|
+
- In the compact rail, `metrics.compact` is `true`. Place cards at `metrics.compactCardLeft`; `Draft` does this itself.
|
|
113
|
+
- Keep the column hidden while `metrics.left` is `null`.
|
|
101
114
|
|
|
102
|
-
|
|
103
|
-
- Inside your own card component, as in the example. It still renders only while the draft is open.
|
|
115
|
+
## See also
|
|
104
116
|
|
|
105
|
-
|
|
117
|
+
- [Review cards](https://www.docx-editor.dev/docs/2.x/customization/review-cards)
|
|
118
|
+
- [Tracked changes](https://www.docx-editor.dev/docs/2.x/pro/tracked-changes)
|
|
119
|
+
- [Accept or reject without scrolling](./accept-reject-without-scrolling.md)
|
package/docs/highlight-ranges.md
CHANGED
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
# Highlight ranges
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Call `useHighlights(name, source, options)` to mark text as a named set while a component is mounted. Highlights are local view state. They do not change the document and are not saved.
|
|
6
|
+
|
|
7
|
+
## Highlight matching text
|
|
8
|
+
|
|
9
|
+
The following component marks each whole-word match of a list of terms. Style the `glossary-term` class with your own CSS, or omit `className` for the default fill.
|
|
6
10
|
|
|
7
11
|
```tsx
|
|
8
12
|
import { useCallback } from 'react';
|
|
9
13
|
import { useHighlights, type Editor } from '@docx-editor.dev/react';
|
|
10
14
|
|
|
11
15
|
export function GlossaryMarks({ terms }: { terms: readonly string[] }) {
|
|
16
|
+
// A stable function source, so the set refreshes only when `terms` changes.
|
|
12
17
|
const findTerms = useCallback(
|
|
13
18
|
(editor: Editor) => editor.findMatches(terms, { wholeWord: true }).flat(),
|
|
14
19
|
[terms]
|
|
@@ -18,8 +23,24 @@ export function GlossaryMarks({ terms }: { terms: readonly string[] }) {
|
|
|
18
23
|
}
|
|
19
24
|
```
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
A function source runs again after each document change, so the marks follow the text. The set is removed when the component unmounts.
|
|
27
|
+
|
|
28
|
+
## Configure a highlight set
|
|
29
|
+
|
|
30
|
+
| Argument or field | Description |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `name` | The set name. Two sets with the same name replace each other's ranges. |
|
|
33
|
+
| `source` | The ranges, or a function of the editor that returns them. |
|
|
34
|
+
| `options.className` | CSS classes added to every mark of the set. |
|
|
35
|
+
| `options.activeIndex` | The range to show in the active color, such as the current result of your own search list. |
|
|
36
|
+
| `applied` | The number of ranges that resolve and paint. |
|
|
37
|
+
|
|
38
|
+
## Use highlights outside components
|
|
39
|
+
|
|
40
|
+
Call `watchHighlights(editor, name, source, options)` from `@docx-editor.dev/core/editor` to follow document changes. Call `stop()` on the returned watch to remove the set and stop watching. For a fixed set that does not refresh, call `editor.setHighlights`.
|
|
22
41
|
|
|
23
|
-
|
|
42
|
+
## See also
|
|
24
43
|
|
|
25
|
-
|
|
44
|
+
- [Highlight text ranges](https://www.docx-editor.dev/docs/2.x/guides/navigation#highlight-text-ranges)
|
|
45
|
+
- [React hooks](https://www.docx-editor.dev/docs/2.x/react/hooks#usehighlights)
|
|
46
|
+
- [Vue composables](https://www.docx-editor.dev/docs/2.x/vue/composables#usehighlights)
|
package/docs/index.md
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
<!-- Generated from docs/site/content/recipes by scripts/sync-agent-docs.mjs. Edit the source and run `bun run docs:agent`. -->
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# Task recipes
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Each recipe solves one task with a complete code sample. Choose the API first, then find the symbol for your task.
|
|
6
6
|
|
|
7
|
-
|
|
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. |
|
|
7
|
+
## Which API for which job
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
Use the React or Vue adapter hooks, the review hooks, and `DocxEditorReview` from `@docx-editor.dev/pro/react` or `@docx-editor.dev/pro/vue` to build browser review controls. Use the editing API in `@docx-editor.dev/editor-api` for document automation. It follows the Office.js object model and runs in browser and server hosts.
|
|
10
|
+
|
|
11
|
+
| Task | Browser controls (React or Vue) | Document automation |
|
|
13
12
|
| --- | --- | --- |
|
|
14
13
|
| [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)` |
|
|
14
|
+
| [Reply to a thread](https://www.docx-editor.dev/docs/2.x/pro/comments) | `useReview().reply(item, text)` | `Comment.reply(text)` |
|
|
16
15
|
| [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) |
|
|
16
|
+
| [Propose an AI rewrite as tracked changes](./propose-rewrite.md) | `prepareRewrite` through `DocxEditor.createBrowser` | `prepareRewrite` from `@docx-editor.dev/editor-api/agent` |
|
|
18
17
|
| [Accept or reject a change](./accept-reject-without-scrolling.md) | `useReview().accept(item)` or `useReview().reject(item)` | `Revision.accept()` or `Revision.reject()` |
|
|
19
|
-
| [Open
|
|
18
|
+
| [Open a change without scrolling](./accept-reject-without-scrolling.md) | `useReview().setActive(key, { reveal: false })` | Not applicable: no viewport |
|
|
20
19
|
| [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`, `
|
|
20
|
+
| [Custom review rail cards](./custom-review-cards.md) | `DocxEditorReview` with `preset` set to `false`, `useStackedReviewPositions`, and `useReviewRailMetrics` | Not applicable |
|
|
22
21
|
| Comment-on-selection button | `DocxEditorReview.AddComment` as a direct child of `DocxEditorReview`, or `useEditorCommand('review.addComment')` | Not applicable |
|
|
23
|
-
| Page setup and paragraph dialogs
|
|
22
|
+
| [Page setup and paragraph dialogs](https://www.docx-editor.dev/docs/2.x/customization/dialogs) | `DocxEditor.PageSetupDialog` or `DocxEditor.ParagraphDialog` | `Section.pageSetup`, and `Paragraph` properties such as `alignment` |
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
## Check prerequisites
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
- The review hooks need `reviewModule()` from `@docx-editor.dev/pro` in the editor's modules.
|
|
27
|
+
- Comments and tracked changes need an author: the editor's `author` prop, or the runtime's `author` option.
|
|
28
|
+
- Create a server runtime with `DocxEditor.createServer` from `@docx-editor.dev/editor-api`. Create a browser runtime with `DocxEditor.createBrowser` from `@docx-editor.dev/editor-api/browser`.
|
|
29
|
+
- Review controls stay separate from the Office.js-shaped document model.
|
|
30
|
+
- `@docx-editor.dev/pro` and `@docx-editor.dev/editor-api` require the EigenPal Pro License. For more information, see [Pro features](https://www.docx-editor.dev/docs/2.x/pro).
|
|
28
31
|
|
|
29
|
-
|
|
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)
|
|
32
|
+
## See also
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
- [React hooks](https://www.docx-editor.dev/docs/2.x/react/hooks)
|
|
35
|
+
- [Vue composables](https://www.docx-editor.dev/docs/2.x/vue/composables)
|
|
36
|
+
- [Editing API](https://www.docx-editor.dev/docs/2.x/editor-api)
|