web-doc 0.7.0 → 0.8.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/THIRD_PARTY_NOTICES.md +10 -3
- package/dist/contracts.d.ts +6 -0
- package/dist/edit/ai/outline.d.ts +47 -0
- package/dist/edit/ai/outline.js +338 -0
- package/dist/edit/ai/targets.d.ts +16 -0
- package/dist/edit/ai/targets.js +309 -0
- package/dist/edit/ai/tools.d.ts +28 -0
- package/dist/edit/ai/tools.js +605 -0
- package/dist/edit/ai/types.d.ts +175 -0
- package/dist/edit/ai/types.js +1 -0
- package/dist/edit/docx/engine.d.ts +10 -4
- package/dist/edit/docx/engine.js +80 -6
- package/dist/edit/docx/model.d.ts +2 -0
- package/dist/edit/docx/model.js +11 -0
- package/dist/edit/docx/operations.d.ts +10 -0
- package/dist/edit/docx/provider.d.ts +4 -1
- package/dist/edit/docx/provider.js +3 -0
- package/dist/edit/docx/session.d.ts +12 -1
- package/dist/edit/docx/session.js +41 -0
- package/dist/edit/docx/structure-ops.js +39 -4
- package/dist/edit/docx/table-ops.js +26 -4
- package/dist/edit/docx/text-ops.d.ts +22 -0
- package/dist/edit/docx/text-ops.js +49 -16
- package/dist/edit/docx/text.js +21 -0
- package/dist/edit/docx/tracked.d.ts +52 -0
- package/dist/edit/docx/tracked.js +347 -0
- package/dist/edit/docx/types.d.ts +16 -1
- package/dist/edit/docx/write.d.ts +19 -5
- package/dist/edit/docx/write.js +31 -8
- package/dist/edit/engine.d.ts +14 -3
- package/dist/edit/history.d.ts +27 -3
- package/dist/edit/history.js +29 -7
- package/dist/edit/pdf/range-map.d.ts +4 -2
- package/dist/edit/pdf/session.d.ts +10 -0
- package/dist/edit/pdf/session.js +39 -0
- package/dist/edit/pptx/handler.js +10 -2
- package/dist/edit/pptx/session.d.ts +10 -0
- package/dist/edit/pptx/session.js +31 -0
- package/dist/edit/session.d.ts +11 -0
- package/dist/edit/session.js +269 -27
- package/dist/edit/types.d.ts +17 -3
- package/dist/edit/worker-engine.d.ts +2 -2
- package/dist/edit/worker-engine.js +2 -2
- package/dist/fuzzy-alignment.d.ts +11 -4
- package/dist/fuzzy-alignment.js +3 -9
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +4 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +11 -2
- package/dist/limits.js +3 -0
- package/dist/worker-protocol.d.ts +1 -1
- package/dist/workers/fuzzy-search-worker.js +1 -1
- package/dist/workers/ooxml-edit-worker.js +1400 -859
- package/dist/workers/pdf-edit-worker.js +4 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApplyOptions, BinaryData, EditColor, EditElement, EditReceipt, EditSessionBase, SavedDocument, SaveOptions, TextRange } from "../types.js";
|
|
1
|
+
import type { ApplyOptions, BinaryData, EditColor, EditElement, EditReceipt, EditSessionBase, ReadOptions, ReadResult, SavedDocument, SaveOptions, TextRange } from "../types.js";
|
|
2
2
|
export type DocxElementKind = "paragraph" | "table" | "image" | "other";
|
|
3
3
|
export type DocxTextAlign = "left" | "center" | "right" | "justify";
|
|
4
4
|
/** Word's sixteen highlight colours (`w:highlight/@w:val`). */
|
|
@@ -165,11 +165,26 @@ export interface DocxInsertImageOperation {
|
|
|
165
165
|
export type DocxOperation = DocxReplaceTextOperation | DocxSetTextStyleOperation | DocxSetParagraphStyleOperation | DocxInsertParagraphOperation | DocxDeleteElementOperation | DocxMoveElementOperation | DocxInsertTableOperation | DocxSetTableCellOperation | DocxInsertImageOperation;
|
|
166
166
|
/** DOCX saves have no fields of their own. */
|
|
167
167
|
export type DocxSaveOptions = SaveOptions;
|
|
168
|
+
/** One revision of a paragraph, as `getRevisions()` lists them in document order. */
|
|
169
|
+
export interface DocxRevision {
|
|
170
|
+
readonly kind: "ins" | "del" | "moveFrom" | "moveTo" | "rPrChange" | "pPrChange";
|
|
171
|
+
/** The `w:id`; -1 when the file leaves it out. */
|
|
172
|
+
readonly id: number;
|
|
173
|
+
readonly author?: string;
|
|
174
|
+
/** ISO 8601, as the file writes it. */
|
|
175
|
+
readonly date?: string;
|
|
176
|
+
/** Runs of the paragraph, its paragraph mark, or the paragraph's properties. */
|
|
177
|
+
readonly scope: "runs" | "mark" | "paragraph";
|
|
178
|
+
/** The inserted, deleted or restyled text; "\n" for a paragraph mark; absent for a property change. */
|
|
179
|
+
readonly text?: string;
|
|
180
|
+
}
|
|
168
181
|
/** An operation's fields without its `op`, as the typed methods take them. */
|
|
169
182
|
export type DocxFields<T extends DocxOperation> = T extends unknown ? Omit<T, "op"> : never;
|
|
170
183
|
export interface DocxEditSession extends EditSessionBase<DocxOperation, DocxElement> {
|
|
171
184
|
readonly format: "docx";
|
|
172
185
|
save(options?: DocxSaveOptions): Promise<SavedDocument>;
|
|
186
|
+
/** The tracked changes a paragraph holds, in document order; empty for other elements. */
|
|
187
|
+
getRevisions(elementId: string, options?: ReadOptions): Promise<ReadResult<DocxRevision>>;
|
|
173
188
|
/** Replaces the whole text of a paragraph, or the part a range covers. */
|
|
174
189
|
replaceText(fields: DocxFields<DocxReplaceTextOperation>, options?: ApplyOptions): Promise<EditReceipt>;
|
|
175
190
|
/** Changes run properties; unspecified ones keep their bytes. */
|
|
@@ -3,6 +3,10 @@ import { type XmlElement, type XmlPart } from "../ooxml/xml.js";
|
|
|
3
3
|
import type { EditColor } from "../types.js";
|
|
4
4
|
import { type DocxStyles } from "./style.js";
|
|
5
5
|
import type { DocxParagraphStyleChange, DocxTextStyleChange } from "./types.js";
|
|
6
|
+
/** Children of `w:rPr` in schema order (CT_RPr). */
|
|
7
|
+
export declare const RPR_ORDER: string[];
|
|
8
|
+
/** Children of `w:pPr` in schema order (CT_PPr). */
|
|
9
|
+
export declare const PPR_ORDER: string[];
|
|
6
10
|
/** Why a text cannot be written, or undefined when it can. */
|
|
7
11
|
export declare function textProblem(text: string): string | undefined;
|
|
8
12
|
/** Why a value cannot be written as an attribute: text problems plus the breaks text may hold. */
|
|
@@ -23,15 +27,25 @@ export declare function runXml(rPr: string, content: string): string;
|
|
|
23
27
|
* where the schema puts them. Returns "" for no properties at all.
|
|
24
28
|
*/
|
|
25
29
|
export declare function mergedProperties(part: XmlPart, source: XmlElement | undefined, tag: "w:rPr" | "w:pPr", order: readonly string[], set: ReadonlyMap<string, string | null>): string;
|
|
26
|
-
/**
|
|
27
|
-
|
|
30
|
+
/**
|
|
31
|
+
* `w:rPr` bytes with a text style change applied. With `revision` (the
|
|
32
|
+
* attributes of a tracked change) the previous properties are kept in a
|
|
33
|
+
* `w:rPrChange`, so Word shows the change as a suggestion.
|
|
34
|
+
*/
|
|
35
|
+
export declare function changedRunProperties(part: XmlPart, rPr: XmlElement | undefined, change: DocxTextStyleChange, styles: DocxStyles, revision?: string): string;
|
|
28
36
|
/** `w:color` for a colour: RGB, automatic, or a theme name with the theme's value. */
|
|
29
37
|
export declare function colorXml(color: EditColor, styles: DocxStyles): string;
|
|
30
|
-
/**
|
|
31
|
-
export declare function
|
|
38
|
+
/** The children of a properties element as bytes, the named ones left out. */
|
|
39
|
+
export declare function innerPropertiesXml(part: XmlPart, properties: XmlElement | undefined, without: readonly string[]): string;
|
|
40
|
+
/**
|
|
41
|
+
* `w:pPr` bytes with alignment and spacing changed, other children kept.
|
|
42
|
+
* With `revision` the previous paragraph properties are kept in a
|
|
43
|
+
* `w:pPrChange`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function changedParagraphProperties(part: XmlPart, pPr: XmlElement | undefined, change: DocxParagraphStyleChange, revision?: string): string;
|
|
32
46
|
export declare function alignValue(align: NonNullable<DocxParagraphStyleChange["align"]>): string;
|
|
33
47
|
/** `w:pPr` bytes with the paragraph mark's `w:rPr` changed (created when absent). */
|
|
34
|
-
export declare function paragraphMarkProperties(part: XmlPart, pPr: XmlElement | undefined, change: DocxTextStyleChange, styles: DocxStyles): string;
|
|
48
|
+
export declare function paragraphMarkProperties(part: XmlPart, pPr: XmlElement | undefined, change: DocxTextStyleChange, styles: DocxStyles, revision?: string): string;
|
|
35
49
|
/** `w:pPr` bytes without the section properties, for a paragraph copied next to one that ends a section. */
|
|
36
50
|
export declare function paragraphPropertiesWithoutSection(part: XmlPart, pPr: XmlElement | undefined): string;
|
|
37
51
|
/**
|
package/dist/edit/docx/write.js
CHANGED
|
@@ -11,7 +11,7 @@ import { LINE_BREAK, PAGE_BREAK, TAB } from "./text.js";
|
|
|
11
11
|
*/
|
|
12
12
|
const MC_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006";
|
|
13
13
|
/** Children of `w:rPr` in schema order (CT_RPr). */
|
|
14
|
-
const RPR_ORDER = [
|
|
14
|
+
export const RPR_ORDER = [
|
|
15
15
|
"rStyle",
|
|
16
16
|
"rFonts",
|
|
17
17
|
"b",
|
|
@@ -54,7 +54,7 @@ const RPR_ORDER = [
|
|
|
54
54
|
"rPrChange",
|
|
55
55
|
];
|
|
56
56
|
/** Children of `w:pPr` in schema order (CT_PPr). */
|
|
57
|
-
const PPR_ORDER = [
|
|
57
|
+
export const PPR_ORDER = [
|
|
58
58
|
"pStyle",
|
|
59
59
|
"keepNext",
|
|
60
60
|
"keepLines",
|
|
@@ -215,9 +215,15 @@ export function mergedProperties(part, source, tag, order, set) {
|
|
|
215
215
|
function toggleXml(local, on) {
|
|
216
216
|
return on ? `<w:${local}/>` : `<w:${local} w:val="0"/>`;
|
|
217
217
|
}
|
|
218
|
-
/**
|
|
219
|
-
|
|
218
|
+
/**
|
|
219
|
+
* `w:rPr` bytes with a text style change applied. With `revision` (the
|
|
220
|
+
* attributes of a tracked change) the previous properties are kept in a
|
|
221
|
+
* `w:rPrChange`, so Word shows the change as a suggestion.
|
|
222
|
+
*/
|
|
223
|
+
export function changedRunProperties(part, rPr, change, styles, revision) {
|
|
220
224
|
const set = new Map();
|
|
225
|
+
if (revision !== undefined)
|
|
226
|
+
set.set("rPrChange", `<w:rPrChange${revision}>${wrapped("w:rPr", innerPropertiesXml(part, rPr, ["rPrChange"]))}</w:rPrChange>`);
|
|
221
227
|
if (change.bold !== undefined) {
|
|
222
228
|
set.set("b", toggleXml("b", change.bold));
|
|
223
229
|
set.set("bCs", toggleXml("bCs", change.bold));
|
|
@@ -261,9 +267,26 @@ function fontsXml(part, rPr, family) {
|
|
|
261
267
|
const rest = kept.map((a) => ` ${a.name}="${a.rawValue}"`).join("");
|
|
262
268
|
return `<w:rFonts w:ascii="${face}" w:hAnsi="${face}"${rest}/>`;
|
|
263
269
|
}
|
|
264
|
-
/** `
|
|
265
|
-
|
|
270
|
+
/** `inner` inside `tag`, self-closing when empty. */
|
|
271
|
+
function wrapped(tag, inner) {
|
|
272
|
+
return inner.length === 0 ? `<${tag}/>` : `<${tag}>${inner}</${tag}>`;
|
|
273
|
+
}
|
|
274
|
+
/** The children of a properties element as bytes, the named ones left out. */
|
|
275
|
+
export function innerPropertiesXml(part, properties, without) {
|
|
276
|
+
return (properties?.children ?? [])
|
|
277
|
+
.filter((child) => !without.includes(child.local))
|
|
278
|
+
.map((child) => sliceOf(part, child))
|
|
279
|
+
.join("");
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* `w:pPr` bytes with alignment and spacing changed, other children kept.
|
|
283
|
+
* With `revision` the previous paragraph properties are kept in a
|
|
284
|
+
* `w:pPrChange`.
|
|
285
|
+
*/
|
|
286
|
+
export function changedParagraphProperties(part, pPr, change, revision) {
|
|
266
287
|
const set = new Map();
|
|
288
|
+
if (revision !== undefined)
|
|
289
|
+
set.set("pPrChange", `<w:pPrChange${revision}>${wrapped("w:pPr", innerPropertiesXml(part, pPr, ["rPr", "sectPr", "pPrChange"]))}</w:pPrChange>`);
|
|
267
290
|
if (change.align !== undefined)
|
|
268
291
|
set.set("jc", `<w:jc w:val="${alignValue(change.align)}"/>`);
|
|
269
292
|
if (change.spacing !== undefined) {
|
|
@@ -304,9 +327,9 @@ export function alignValue(align) {
|
|
|
304
327
|
}
|
|
305
328
|
}
|
|
306
329
|
/** `w:pPr` bytes with the paragraph mark's `w:rPr` changed (created when absent). */
|
|
307
|
-
export function paragraphMarkProperties(part, pPr, change, styles) {
|
|
330
|
+
export function paragraphMarkProperties(part, pPr, change, styles, revision) {
|
|
308
331
|
const rPr = pPr?.children.find((child) => child.local === "rPr" && child.namespace === W_NS);
|
|
309
|
-
const changed = changedRunProperties(part, rPr, change, styles);
|
|
332
|
+
const changed = changedRunProperties(part, rPr, change, styles, revision);
|
|
310
333
|
return mergedProperties(part, pPr, "w:pPr", PPR_ORDER, new Map([["rPr", changed || null]]));
|
|
311
334
|
}
|
|
312
335
|
/** `w:pPr` bytes without the section properties, for a paragraph copied next to one that ends a section. */
|
package/dist/edit/engine.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DocumentFormat, RegisteredFont, ResourceLimits, TextRun, ViewerWarning } from "../contracts.js";
|
|
2
2
|
import type { EditSession } from "./sessions.js";
|
|
3
|
-
import type { EditableFormat, EditElement, EditFindOptions, EditOperation, EditSessionBase, ElementQuery, OperationIssue, OperationSchemaSet, PagePoint, ReadItem, ReadOptions, ReadResult, TextTarget } from "./types.js";
|
|
3
|
+
import type { ChangeMode, EditableFormat, EditElement, EditFindOptions, EditOperation, EditSessionBase, ElementQuery, OperationIssue, OperationSchemaSet, PagePoint, ReadItem, ReadOptions, ReadResult, TextTarget } from "./types.js";
|
|
4
4
|
export interface EditEngineContext {
|
|
5
5
|
readonly format: EditableFormat;
|
|
6
6
|
readonly fileName?: string;
|
|
@@ -16,6 +16,8 @@ export interface EditEngineContext {
|
|
|
16
16
|
* earlier calls and carry the same envelope as the core's own reads.
|
|
17
17
|
*/
|
|
18
18
|
export interface EditSessionCore extends EditSessionBase<EditOperation, EditElement> {
|
|
19
|
+
/** The host's limits, for reads a typed session builds over the core. */
|
|
20
|
+
readonly limits: ResourceLimits;
|
|
19
21
|
readItem<T>(options: ReadOptions | undefined, task: (engine: EditEngine, signal: AbortSignal) => Promise<T | undefined>): Promise<ReadItem<T>>;
|
|
20
22
|
readItems<T>(options: ReadOptions | undefined, task: (engine: EditEngine, signal: AbortSignal) => Promise<readonly T[]>): Promise<ReadResult<T>>;
|
|
21
23
|
}
|
|
@@ -44,8 +46,17 @@ export interface MaterializedDocument {
|
|
|
44
46
|
/** For example `privacy-not-guaranteed` when a PDF full save could not be compacted. */
|
|
45
47
|
readonly warnings: readonly ViewerWarning[];
|
|
46
48
|
}
|
|
49
|
+
/** How a batch is written; travels with the batch to the engine and into the history, so a replay writes it the same way. */
|
|
50
|
+
export interface BatchMode {
|
|
51
|
+
/** Default `direct`; `tracked` writes revisions where the format has them. */
|
|
52
|
+
readonly changeMode?: ChangeMode;
|
|
53
|
+
/** The author of tracked changes. */
|
|
54
|
+
readonly author?: string;
|
|
55
|
+
/** ISO 8601, the date of tracked changes. */
|
|
56
|
+
readonly timestamp?: string;
|
|
57
|
+
}
|
|
47
58
|
/** A batch with the identity the core assigned to the state after it. */
|
|
48
|
-
export interface EngineBatch {
|
|
59
|
+
export interface EngineBatch extends BatchMode {
|
|
49
60
|
/** Unique within the session and never reused; engines derive created ids from it. */
|
|
50
61
|
readonly stateId: number;
|
|
51
62
|
readonly operations: readonly EditOperation[];
|
|
@@ -82,7 +93,7 @@ export interface EngineChange {
|
|
|
82
93
|
export interface EditEngine {
|
|
83
94
|
readonly schemas: OperationSchemaSet;
|
|
84
95
|
/** Validates against the current state; an empty result means the batch can be applied. */
|
|
85
|
-
validate(operations: readonly EditOperation[], signal: AbortSignal): Promise<readonly OperationIssue[]>;
|
|
96
|
+
validate(operations: readonly EditOperation[], signal: AbortSignal, mode?: BatchMode): Promise<readonly OperationIssue[]>;
|
|
86
97
|
/**
|
|
87
98
|
* Applies an already validated batch to the working copy. Same-batch
|
|
88
99
|
* references (`"$<n>"` targets) are resolved here; one that resolves to an
|
package/dist/edit/history.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { EngineBatch } from "./engine.js";
|
|
1
|
+
import type { BatchMode, EngineBatch } from "./engine.js";
|
|
2
2
|
import type { EditOperation } from "./types.js";
|
|
3
|
-
export interface HistoryEntry {
|
|
3
|
+
export interface HistoryEntry extends BatchMode {
|
|
4
4
|
readonly operations: readonly EditOperation[];
|
|
5
5
|
readonly label?: string;
|
|
6
6
|
/** Ids the batch created; an undo removes them again. */
|
|
@@ -15,6 +15,19 @@ export interface HistoryEntry {
|
|
|
15
15
|
readonly pageCountAfter: number;
|
|
16
16
|
/** Identifies the content after this batch; equal ids mean equal content. */
|
|
17
17
|
readonly stateId: number;
|
|
18
|
+
/**
|
|
19
|
+
* A restore to a named checkpoint: the content after this entry is the
|
|
20
|
+
* checkpoint's, so a replay starts from its retained bytes or from the
|
|
21
|
+
* original plus `batches`, never from the entries before.
|
|
22
|
+
*/
|
|
23
|
+
readonly base?: HistoryBase;
|
|
24
|
+
}
|
|
25
|
+
/** Where a restore entry's content comes from. */
|
|
26
|
+
export interface HistoryBase {
|
|
27
|
+
/** The checkpoint's state id; its bytes may be retained under it. */
|
|
28
|
+
readonly stateId: number;
|
|
29
|
+
/** The batches that build the checkpoint's state from the original. */
|
|
30
|
+
readonly batches: readonly EngineBatch[];
|
|
18
31
|
}
|
|
19
32
|
/**
|
|
20
33
|
* Linear undo history over an immutable original. Entries beyond the limit are
|
|
@@ -35,6 +48,8 @@ export declare class EditHistory {
|
|
|
35
48
|
get pageCount(): number;
|
|
36
49
|
/** State ids of every entry still in the history, folded and redo tail included. */
|
|
37
50
|
get stateIds(): readonly number[];
|
|
51
|
+
/** Every entry still in the history: folded ones, then the undoable ones and the redo tail. */
|
|
52
|
+
get allEntries(): readonly HistoryEntry[];
|
|
38
53
|
/** The entry `undo()` would revert, if any. */
|
|
39
54
|
get undoEntry(): HistoryEntry | undefined;
|
|
40
55
|
/** The entry `redo()` would re-apply, if any. */
|
|
@@ -46,8 +61,17 @@ export declare class EditHistory {
|
|
|
46
61
|
/** Entries applied when `position` undoable entries are applied, folded ones first. */
|
|
47
62
|
entriesAt(position: number): readonly HistoryEntry[];
|
|
48
63
|
get position(): number;
|
|
49
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Appends an entry with a fresh state id, or with `stateId` when the entry
|
|
66
|
+
* reproduces a known state (a restore to a checkpoint): the same id means
|
|
67
|
+
* the same content, so `dirty` stays exact.
|
|
68
|
+
*/
|
|
69
|
+
push(entry: Omit<HistoryEntry, "stateId">, stateId?: number): HistoryEntry;
|
|
50
70
|
undo(): void;
|
|
51
71
|
redo(): void;
|
|
52
72
|
clear(): void;
|
|
53
73
|
}
|
|
74
|
+
/** The engine batch an entry replays as: its operations and how they were written. */
|
|
75
|
+
export declare function batchOf(entry: HistoryEntry): EngineBatch;
|
|
76
|
+
/** The write mode fields of a batch or an entry, only those set. */
|
|
77
|
+
export declare function modeOf(mode: BatchMode): BatchMode;
|
package/dist/edit/history.js
CHANGED
|
@@ -42,7 +42,11 @@ export class EditHistory {
|
|
|
42
42
|
}
|
|
43
43
|
/** State ids of every entry still in the history, folded and redo tail included. */
|
|
44
44
|
get stateIds() {
|
|
45
|
-
return
|
|
45
|
+
return this.allEntries.map((entry) => entry.stateId);
|
|
46
|
+
}
|
|
47
|
+
/** Every entry still in the history: folded ones, then the undoable ones and the redo tail. */
|
|
48
|
+
get allEntries() {
|
|
49
|
+
return [...this.#folded, ...this.#entries];
|
|
46
50
|
}
|
|
47
51
|
/** The entry `undo()` would revert, if any. */
|
|
48
52
|
get undoEntry() {
|
|
@@ -58,10 +62,7 @@ export class EditHistory {
|
|
|
58
62
|
}
|
|
59
63
|
/** Batches applied to the original when `position` undoable entries are applied. */
|
|
60
64
|
batchesAt(position) {
|
|
61
|
-
return this.entriesAt(position).map(
|
|
62
|
-
stateId: entry.stateId,
|
|
63
|
-
operations: entry.operations,
|
|
64
|
-
}));
|
|
65
|
+
return this.entriesAt(position).map(batchOf);
|
|
65
66
|
}
|
|
66
67
|
/** Entries applied when `position` undoable entries are applied, folded ones first. */
|
|
67
68
|
entriesAt(position) {
|
|
@@ -70,10 +71,15 @@ export class EditHistory {
|
|
|
70
71
|
get position() {
|
|
71
72
|
return this.#position;
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Appends an entry with a fresh state id, or with `stateId` when the entry
|
|
76
|
+
* reproduces a known state (a restore to a checkpoint): the same id means
|
|
77
|
+
* the same content, so `dirty` stays exact.
|
|
78
|
+
*/
|
|
79
|
+
push(entry, stateId) {
|
|
74
80
|
const stored = Object.freeze({
|
|
75
81
|
...entry,
|
|
76
|
-
stateId: this.#nextStateId++,
|
|
82
|
+
stateId: stateId ?? this.#nextStateId++,
|
|
77
83
|
});
|
|
78
84
|
// A new change after an undo drops the redo tail.
|
|
79
85
|
this.#entries.length = this.#position;
|
|
@@ -99,3 +105,19 @@ export class EditHistory {
|
|
|
99
105
|
this.#position = 0;
|
|
100
106
|
}
|
|
101
107
|
}
|
|
108
|
+
/** The engine batch an entry replays as: its operations and how they were written. */
|
|
109
|
+
export function batchOf(entry) {
|
|
110
|
+
return {
|
|
111
|
+
stateId: entry.stateId,
|
|
112
|
+
operations: entry.operations,
|
|
113
|
+
...modeOf(entry),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** The write mode fields of a batch or an entry, only those set. */
|
|
117
|
+
export function modeOf(mode) {
|
|
118
|
+
return {
|
|
119
|
+
...(mode.changeMode === undefined ? {} : { changeMode: mode.changeMode }),
|
|
120
|
+
...(mode.author === undefined ? {} : { author: mode.author }),
|
|
121
|
+
...(mode.timestamp === undefined ? {} : { timestamp: mode.timestamp }),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -2,10 +2,12 @@ import type { EditOperation, EditReceipt, TextRange } from "../types.js";
|
|
|
2
2
|
export interface MutationRecord {
|
|
3
3
|
/** The revision the call produced. */
|
|
4
4
|
readonly revision: number;
|
|
5
|
-
readonly kind: "apply" | "undo" | "redo" | "reset";
|
|
5
|
+
readonly kind: "apply" | "undo" | "redo" | "reset" | "restore";
|
|
6
6
|
/**
|
|
7
7
|
* The operations the call applied (apply, redo) or took back (undo, reset);
|
|
8
|
-
* for a reset every batch of the session, in order.
|
|
8
|
+
* for a reset every batch of the session, in order. A checkpoint restore
|
|
9
|
+
* carries none: its receipt's ids say what went and what came back, and
|
|
10
|
+
* offsets inside surviving elements are left as they were.
|
|
9
11
|
*/
|
|
10
12
|
readonly operations: readonly EditOperation[];
|
|
11
13
|
readonly receipt: EditReceipt;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TextSelection } from "../../contracts.js";
|
|
2
|
+
import type { DescribeOptions, DocumentDescription, EditCheckpoint, OutlineOptions, OutlineResult, TargetCandidate, TargetQuery, ToolCall, ToolCallOptions, ToolResult, ToolSet } from "../ai/types.js";
|
|
2
3
|
import type { EditSessionCore } from "../engine.js";
|
|
3
4
|
import type { ApplyOptions, AssetOptions, EditFindOptions, EditOperation, EditReceipt, EditState, ElementQuery, HistoryOptions, OperationSchemaSet, PagePoint, PageRect, ReadItem, ReadOptions, ReadResult, SavedDocument, TextPosition, TextRange, TextTarget } from "../types.js";
|
|
4
5
|
import type { DeleteElementOperation, DeletePageOperation, Fields, InsertImageOperation, InsertPageOperation, InsertShapeOperation, InsertTableOperation, InsertTextBoxOperation, MoveElementOperation, MovePageOperation, PdfEditSession, PdfElement, PdfOperation, PdfSaveOptions, ReplaceTextOperation, ResizeElementOperation, RotatePageOperation, SetShapeStyleOperation, SetTableCellOperation, PageBitmap, PageLayout, RenderOptions, SetTextStyleOperation, TextLayout } from "./types.js";
|
|
@@ -28,6 +29,15 @@ export declare class PdfSession implements PdfEditSession {
|
|
|
28
29
|
getElement(id: string, options?: ReadOptions): Promise<ReadItem<PdfElement>>;
|
|
29
30
|
elementsAt(pageIndex: number, point: PagePoint, options?: ReadOptions): Promise<ReadResult<PdfElement>>;
|
|
30
31
|
findText(query: string, options?: EditFindOptions): Promise<ReadResult<TextTarget>>;
|
|
32
|
+
getOutline(options?: OutlineOptions): Promise<OutlineResult>;
|
|
33
|
+
describe(options?: DescribeOptions): Promise<ReadItem<DocumentDescription>>;
|
|
34
|
+
resolveTargets(query: TargetQuery, options?: ReadOptions): Promise<ReadResult<TargetCandidate>>;
|
|
35
|
+
createCheckpoint(label?: string): Promise<EditCheckpoint>;
|
|
36
|
+
listCheckpoints(): readonly EditCheckpoint[];
|
|
37
|
+
restoreCheckpoint(id: string, options?: HistoryOptions): Promise<EditReceipt>;
|
|
38
|
+
dropCheckpoint(id: string): void;
|
|
39
|
+
get tools(): ToolSet;
|
|
40
|
+
callTool(call: ToolCall, options?: ToolCallOptions): Promise<ToolResult>;
|
|
31
41
|
getTextLayout(elementId: string, options?: ReadOptions): Promise<ReadItem<TextLayout>>;
|
|
32
42
|
positionAt(pageIndex: number, point: PagePoint, options?: ReadOptions): Promise<ReadItem<TextPosition>>;
|
|
33
43
|
rangeRects(range: TextRange, options?: ReadOptions): Promise<ReadResult<PageRect>>;
|
package/dist/edit/pdf/session.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { ViewerError } from "../../errors.js";
|
|
2
|
+
import { readDescription, readOutline } from "../ai/outline.js";
|
|
3
|
+
import { resolveTargets } from "../ai/targets.js";
|
|
4
|
+
import { buildToolSet, callTool as runTool } from "../ai/tools.js";
|
|
2
5
|
import { reportError } from "../session.js";
|
|
3
6
|
import { rectContains } from "./engine/geometry.js";
|
|
4
7
|
import { mapRangeThrough } from "./range-map.js";
|
|
@@ -11,6 +14,7 @@ import { resolveSelection } from "./selection.js";
|
|
|
11
14
|
export class PdfSession {
|
|
12
15
|
format = "pdf";
|
|
13
16
|
#core;
|
|
17
|
+
#tools;
|
|
14
18
|
/** Committed calls, oldest first, for `mapRange`; bounded by `LOG_LIMIT`. */
|
|
15
19
|
#log = [];
|
|
16
20
|
/** Batches applied and not undone, and those undone and not redone. */
|
|
@@ -81,6 +85,12 @@ export class PdfSession {
|
|
|
81
85
|
this.#applied = [];
|
|
82
86
|
this.#undone = [];
|
|
83
87
|
break;
|
|
88
|
+
case "restore":
|
|
89
|
+
// The checkpoint's content replaces whatever the stacks describe;
|
|
90
|
+
// the undo of a restore is the core's to replay, not the log's.
|
|
91
|
+
this.#applied = [];
|
|
92
|
+
this.#undone = [];
|
|
93
|
+
break;
|
|
84
94
|
}
|
|
85
95
|
this.#log.push({
|
|
86
96
|
revision: receipt.revision,
|
|
@@ -156,6 +166,35 @@ export class PdfSession {
|
|
|
156
166
|
findText(query, options) {
|
|
157
167
|
return this.#core.findText(query, options);
|
|
158
168
|
}
|
|
169
|
+
getOutline(options) {
|
|
170
|
+
return readOutline(this, this.#core.limits, options);
|
|
171
|
+
}
|
|
172
|
+
describe(options) {
|
|
173
|
+
return readDescription(this, this.#core.limits, options);
|
|
174
|
+
}
|
|
175
|
+
resolveTargets(query, options) {
|
|
176
|
+
return resolveTargets(this, query, options);
|
|
177
|
+
}
|
|
178
|
+
createCheckpoint(label) {
|
|
179
|
+
return this.#core.createCheckpoint(label);
|
|
180
|
+
}
|
|
181
|
+
listCheckpoints() {
|
|
182
|
+
return this.#core.listCheckpoints();
|
|
183
|
+
}
|
|
184
|
+
async restoreCheckpoint(id, options) {
|
|
185
|
+
const receipt = await this.#core.restoreCheckpoint(id, options);
|
|
186
|
+
this.#record("restore", [], receipt);
|
|
187
|
+
return receipt;
|
|
188
|
+
}
|
|
189
|
+
dropCheckpoint(id) {
|
|
190
|
+
this.#core.dropCheckpoint(id);
|
|
191
|
+
}
|
|
192
|
+
get tools() {
|
|
193
|
+
return (this.#tools ??= buildToolSet(this.format, this.schemas));
|
|
194
|
+
}
|
|
195
|
+
callTool(call, options) {
|
|
196
|
+
return runTool(this, this.tools, call, options);
|
|
197
|
+
}
|
|
159
198
|
getTextLayout(elementId, options) {
|
|
160
199
|
return this.#core.readItem(options, (engine, signal) => pdfReads(engine).textLayout(elementId, signal));
|
|
161
200
|
}
|
|
@@ -13,6 +13,12 @@ export function createOoxmlEditHandler() {
|
|
|
13
13
|
throw new ViewerError("lifecycle-error", "No package is open for editing");
|
|
14
14
|
return state;
|
|
15
15
|
};
|
|
16
|
+
const docx = () => {
|
|
17
|
+
const current = engine();
|
|
18
|
+
if (!(current instanceof DocxEditEngine))
|
|
19
|
+
throw new ViewerError("edit-unsupported", "The open document is not a Word document", { details: { format: "pptx", reason: "no-reads" } });
|
|
20
|
+
return current;
|
|
21
|
+
};
|
|
16
22
|
const pptx = () => {
|
|
17
23
|
const current = engine();
|
|
18
24
|
if (!(current instanceof PptxEditEngine))
|
|
@@ -41,8 +47,8 @@ export function createOoxmlEditHandler() {
|
|
|
41
47
|
return result;
|
|
42
48
|
}
|
|
43
49
|
case "edit-validate": {
|
|
44
|
-
const { operations } = payload;
|
|
45
|
-
return engine().validate(operations, signal);
|
|
50
|
+
const { operations, mode } = payload;
|
|
51
|
+
return engine().validate(operations, signal, mode);
|
|
46
52
|
}
|
|
47
53
|
case "edit-apply": {
|
|
48
54
|
const { batch } = payload;
|
|
@@ -80,6 +86,8 @@ export function createOoxmlEditHandler() {
|
|
|
80
86
|
return pptx().slides(signal);
|
|
81
87
|
case "edit-pptx-layouts":
|
|
82
88
|
return pptx().layouts(signal);
|
|
89
|
+
case "edit-docx-revisions":
|
|
90
|
+
return docx().revisions(payload.id, signal);
|
|
83
91
|
case "edit-dispose":
|
|
84
92
|
await state?.dispose();
|
|
85
93
|
state = undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DescribeOptions, DocumentDescription, EditCheckpoint, OutlineOptions, OutlineResult, TargetCandidate, TargetQuery, ToolCall, ToolCallOptions, ToolResult, ToolSet } from "../ai/types.js";
|
|
1
2
|
import type { EditSessionCore } from "../engine.js";
|
|
2
3
|
import type { ApplyOptions, AssetOptions, EditFindOptions, EditOperation, EditReceipt, EditState, ElementQuery, HistoryOptions, OperationSchemaSet, PagePoint, ReadItem, ReadOptions, ReadResult, SavedDocument, TextTarget } from "../types.js";
|
|
3
4
|
import type { PptxDeleteElementOperation, PptxDeleteSlideOperation, PptxDuplicateSlideOperation, PptxEditSession, PptxElement, PptxFields, PptxInsertImageOperation, PptxInsertSlideOperation, PptxInsertTableOperation, PptxInsertTextBoxOperation, PptxLayoutInfo, PptxMoveElementOperation, PptxMoveSlideOperation, PptxOperation, PptxReplaceTextOperation, PptxResizeElementOperation, PptxSaveOptions, PptxSetShapeStyleOperation, PptxSetTableCellOperation, PptxSetTextStyleOperation, PptxSlideInfo } from "./types.js";
|
|
@@ -25,6 +26,15 @@ export declare class PptxSession implements PptxEditSession {
|
|
|
25
26
|
getElement(id: string, options?: ReadOptions): Promise<ReadItem<PptxElement>>;
|
|
26
27
|
elementsAt(pageIndex: number, point: PagePoint, options?: ReadOptions): Promise<ReadResult<PptxElement>>;
|
|
27
28
|
findText(query: string, options?: EditFindOptions): Promise<ReadResult<TextTarget>>;
|
|
29
|
+
getOutline(options?: OutlineOptions): Promise<OutlineResult>;
|
|
30
|
+
describe(options?: DescribeOptions): Promise<ReadItem<DocumentDescription>>;
|
|
31
|
+
resolveTargets(query: TargetQuery, options?: ReadOptions): Promise<ReadResult<TargetCandidate>>;
|
|
32
|
+
createCheckpoint(label?: string): Promise<EditCheckpoint>;
|
|
33
|
+
listCheckpoints(): readonly EditCheckpoint[];
|
|
34
|
+
restoreCheckpoint(id: string, options?: HistoryOptions): Promise<EditReceipt>;
|
|
35
|
+
dropCheckpoint(id: string): void;
|
|
36
|
+
get tools(): ToolSet;
|
|
37
|
+
callTool(call: ToolCall, options?: ToolCallOptions): Promise<ToolResult>;
|
|
28
38
|
getSlides(options?: ReadOptions): Promise<ReadResult<PptxSlideInfo>>;
|
|
29
39
|
getLayouts(options?: ReadOptions): Promise<ReadResult<PptxLayoutInfo>>;
|
|
30
40
|
replaceText(fields: PptxFields<PptxReplaceTextOperation>, options?: ApplyOptions): Promise<EditReceipt>;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { ViewerError } from "../../errors.js";
|
|
2
|
+
import { readDescription, readOutline } from "../ai/outline.js";
|
|
3
|
+
import { resolveTargets } from "../ai/targets.js";
|
|
4
|
+
import { buildToolSet, callTool as runTool } from "../ai/tools.js";
|
|
2
5
|
/**
|
|
3
6
|
* The PPTX session: the core session narrowed to PPTX operations and
|
|
4
7
|
* elements, plus the slide and layout reads. Each typed method is `apply()`
|
|
@@ -7,6 +10,7 @@ import { ViewerError } from "../../errors.js";
|
|
|
7
10
|
export class PptxSession {
|
|
8
11
|
format = "pptx";
|
|
9
12
|
#core;
|
|
13
|
+
#tools;
|
|
10
14
|
constructor(core) {
|
|
11
15
|
this.#core = core;
|
|
12
16
|
}
|
|
@@ -55,6 +59,33 @@ export class PptxSession {
|
|
|
55
59
|
findText(query, options) {
|
|
56
60
|
return this.#core.findText(query, options);
|
|
57
61
|
}
|
|
62
|
+
getOutline(options) {
|
|
63
|
+
return readOutline(this, this.#core.limits, options);
|
|
64
|
+
}
|
|
65
|
+
describe(options) {
|
|
66
|
+
return readDescription(this, this.#core.limits, options);
|
|
67
|
+
}
|
|
68
|
+
resolveTargets(query, options) {
|
|
69
|
+
return resolveTargets(this, query, options);
|
|
70
|
+
}
|
|
71
|
+
createCheckpoint(label) {
|
|
72
|
+
return this.#core.createCheckpoint(label);
|
|
73
|
+
}
|
|
74
|
+
listCheckpoints() {
|
|
75
|
+
return this.#core.listCheckpoints();
|
|
76
|
+
}
|
|
77
|
+
restoreCheckpoint(id, options) {
|
|
78
|
+
return this.#core.restoreCheckpoint(id, options);
|
|
79
|
+
}
|
|
80
|
+
dropCheckpoint(id) {
|
|
81
|
+
this.#core.dropCheckpoint(id);
|
|
82
|
+
}
|
|
83
|
+
get tools() {
|
|
84
|
+
return (this.#tools ??= buildToolSet(this.format, this.schemas));
|
|
85
|
+
}
|
|
86
|
+
callTool(call, options) {
|
|
87
|
+
return runTool(this, this.tools, call, options);
|
|
88
|
+
}
|
|
58
89
|
getSlides(options) {
|
|
59
90
|
return this.#core.readItems(options, (engine, signal) => pptxReads(engine).slides(signal));
|
|
60
91
|
}
|
package/dist/edit/session.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ResourceLimits, ViewerEventMap } from "../contracts.js";
|
|
2
|
+
import type { DescribeOptions, DocumentDescription, EditCheckpoint, OutlineOptions, OutlineResult, TargetCandidate, TargetQuery, ToolCall, ToolCallOptions, ToolResult, ToolSet } from "./ai/types.js";
|
|
2
3
|
import type { EditEngine, EditSessionCore } from "./engine.js";
|
|
3
4
|
import type { AssetOptions, ApplyOptions, EditableFormat, EditElement, EditFindOptions, EditOperation, EditReceipt, EditState, ElementQuery, HistoryOptions, ReadItem, ReadOptions, ReadResult, OperationSchemaSet, PagePoint, SavedDocument, SaveOptions, TextTarget } from "./types.js";
|
|
4
5
|
/** What the viewer provides to a session: a place to show bytes, and events. */
|
|
@@ -38,6 +39,7 @@ export declare class EditSessionController implements EditSessionCore {
|
|
|
38
39
|
readonly sessionId: string;
|
|
39
40
|
constructor(engine: EditEngine, host: EditSessionHost, original: Uint8Array, originalPageCount: number);
|
|
40
41
|
get state(): EditState;
|
|
42
|
+
get limits(): ResourceLimits;
|
|
41
43
|
applyJson(operations: readonly EditOperation[], options?: ApplyOptions): Promise<EditReceipt>;
|
|
42
44
|
apply(operations: readonly EditOperation[], options?: ApplyOptions): Promise<EditReceipt>;
|
|
43
45
|
undo(options?: HistoryOptions): Promise<EditReceipt>;
|
|
@@ -50,6 +52,15 @@ export declare class EditSessionController implements EditSessionCore {
|
|
|
50
52
|
getElement(id: string, options?: ReadOptions): Promise<ReadItem<EditElement>>;
|
|
51
53
|
elementsAt(pageIndex: number, point: PagePoint, options?: ReadOptions): Promise<ReadResult<EditElement>>;
|
|
52
54
|
findText(query: string, options?: EditFindOptions): Promise<ReadResult<TextTarget>>;
|
|
55
|
+
getOutline(options?: OutlineOptions): Promise<OutlineResult>;
|
|
56
|
+
describe(options?: DescribeOptions): Promise<ReadItem<DocumentDescription>>;
|
|
57
|
+
resolveTargets(query: TargetQuery, options?: ReadOptions): Promise<ReadResult<TargetCandidate>>;
|
|
58
|
+
createCheckpoint(label?: string): Promise<EditCheckpoint>;
|
|
59
|
+
listCheckpoints(): readonly EditCheckpoint[];
|
|
60
|
+
restoreCheckpoint(id: string, options?: HistoryOptions): Promise<EditReceipt>;
|
|
61
|
+
dropCheckpoint(id: string): void;
|
|
62
|
+
get tools(): ToolSet;
|
|
63
|
+
callTool(call: ToolCall, options?: ToolCallOptions): Promise<ToolResult>;
|
|
53
64
|
readItem<T>(options: ReadOptions | undefined, task: (engine: EditEngine, signal: AbortSignal) => Promise<T | undefined>): Promise<ReadItem<T>>;
|
|
54
65
|
readItems<T>(options: ReadOptions | undefined, task: (engine: EditEngine, signal: AbortSignal) => Promise<readonly T[]>): Promise<ReadResult<T>>;
|
|
55
66
|
/**
|