docxodus 12.1.0 → 12.2.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 +9 -4
- package/dist/editor.bundle.js +135 -22
- package/dist/editor.d.ts +25 -3
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +132 -13
- package/dist/editor.js.map +1 -1
- package/dist/embed.bundle.js +573 -47
- package/dist/embed.d.ts +30 -1
- package/dist/embed.d.ts.map +1 -1
- package/dist/embed.iife.js +573 -47
- package/dist/embed.js +124 -13
- package/dist/embed.js.map +1 -1
- package/dist/export-assets.json +33 -33
- package/dist/history.d.ts +265 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +307 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/dist/ribbon-chrome.d.ts +2 -2
- package/dist/ribbon-chrome.d.ts.map +1 -1
- package/dist/ribbon-chrome.js +6 -4
- package/dist/ribbon-chrome.js.map +1 -1
- package/dist/ribbon.js +39 -8
- package/dist/ribbon.js.map +1 -1
- package/dist/session.js +1 -1
- package/dist/session.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm +0 -0
- package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm.br +0 -0
- package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm +0 -0
- package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm.br +0 -0
- package/dist/wasm/_framework/Docxodus.wasm +0 -0
- package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.Concurrent.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.Concurrent.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
- package/dist/wasm/_framework/System.IO.Packaging.wasm +0 -0
- package/dist/wasm/_framework/System.IO.Packaging.wasm.br +0 -0
- package/dist/wasm/_framework/System.Linq.Expressions.wasm +0 -0
- package/dist/wasm/_framework/System.Linq.Expressions.wasm.br +0 -0
- package/dist/wasm/_framework/System.Memory.wasm +0 -0
- package/dist/wasm/_framework/System.Memory.wasm.br +0 -0
- package/dist/wasm/_framework/System.Net.Http.wasm +0 -0
- package/dist/wasm/_framework/System.Net.Http.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Xml.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Xml.wasm.br +0 -0
- package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm +0 -0
- package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm.br +0 -0
- package/dist/wasm/_framework/System.Runtime.wasm +0 -0
- package/dist/wasm/_framework/System.Runtime.wasm.br +0 -0
- package/dist/wasm/_framework/System.Security.Cryptography.wasm +0 -0
- package/dist/wasm/_framework/System.Security.Cryptography.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.Json.wasm +0 -0
- package/dist/wasm/_framework/System.Text.Json.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
- package/dist/wasm/_framework/dotnet.boot.js +21 -21
- package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
- package/package.json +4 -3
package/dist/embed.iife.js
CHANGED
|
@@ -141,8 +141,14 @@ var Docxodus = (() => {
|
|
|
141
141
|
DocxDiffFormatComparison: () => DocxDiffFormatComparison,
|
|
142
142
|
DocxDiffRevisionGranularity: () => DocxDiffRevisionGranularity,
|
|
143
143
|
DocxEditor: () => DocxEditor,
|
|
144
|
+
DocxHistoryArchive: () => DocxHistoryArchive,
|
|
145
|
+
DocxHistoryClient: () => DocxHistoryClient,
|
|
146
|
+
DocxHistoryDocument: () => DocxHistoryDocument,
|
|
147
|
+
DocxHistoryError: () => DocxHistoryError,
|
|
148
|
+
DocxHistoryReader: () => DocxHistoryReader,
|
|
144
149
|
DocxSession: () => DocxSession,
|
|
145
150
|
EmptyParagraphMode: () => EmptyParagraphMode,
|
|
151
|
+
MAX_HISTORY_ARCHIVE_BYTES: () => MAX_HISTORY_ARCHIVE_BYTES,
|
|
146
152
|
PaginationEngine: () => PaginationEngine,
|
|
147
153
|
PaginationMode: () => PaginationMode,
|
|
148
154
|
PlaceholderKinds: () => PlaceholderKinds,
|
|
@@ -166,6 +172,8 @@ var Docxodus = (() => {
|
|
|
166
172
|
createBlankDocx: () => createBlankDocx,
|
|
167
173
|
createEditor: () => createEditor,
|
|
168
174
|
createExternalAnnotationSet: () => createExternalAnnotationSet,
|
|
175
|
+
createHistoryViewer: () => createHistoryViewer,
|
|
176
|
+
createMemoryHistoryStorage: () => createMemoryHistoryStorage,
|
|
169
177
|
createRibbonEditor: () => createRibbonEditor,
|
|
170
178
|
createUnavailablePageMap: () => createUnavailablePageMap,
|
|
171
179
|
createViewer: () => createViewer,
|
|
@@ -200,6 +208,7 @@ var Docxodus = (() => {
|
|
|
200
208
|
getWasmExports: () => getWasmExports,
|
|
201
209
|
hasAnnotations: () => hasAnnotations,
|
|
202
210
|
initialize: () => initialize,
|
|
211
|
+
installHistoryStorageImports: () => installHistoryStorageImports,
|
|
203
212
|
isDeletion: () => isDeletion,
|
|
204
213
|
isFormatChange: () => isFormatChange,
|
|
205
214
|
isInitialized: () => isInitialized,
|
|
@@ -207,6 +216,8 @@ var Docxodus = (() => {
|
|
|
207
216
|
isMove: () => isMove,
|
|
208
217
|
mountRibbon: () => mountRibbon,
|
|
209
218
|
navigateToPageCitation: () => navigateToPageCitation,
|
|
219
|
+
openDocxHistory: () => openDocxHistory,
|
|
220
|
+
openDocxHistoryArchive: () => openDocxHistoryArchive,
|
|
210
221
|
openDocxSession: () => openDocxSession2,
|
|
211
222
|
paginateHtml: () => paginateHtml,
|
|
212
223
|
parseSectionDimensions: () => parseSectionDimensions,
|
|
@@ -2161,6 +2172,288 @@ var Docxodus = (() => {
|
|
|
2161
2172
|
return new DocxSession(handle, bridge);
|
|
2162
2173
|
}
|
|
2163
2174
|
|
|
2175
|
+
// src/history.ts
|
|
2176
|
+
var MAX_HISTORY_ARCHIVE_BYTES = 64 * 1024 * 1024;
|
|
2177
|
+
var DocxHistoryError = class extends Error {
|
|
2178
|
+
constructor(code, message) {
|
|
2179
|
+
super(message);
|
|
2180
|
+
this.code = code;
|
|
2181
|
+
this.name = "DocxHistoryError";
|
|
2182
|
+
}
|
|
2183
|
+
};
|
|
2184
|
+
var adapters = /* @__PURE__ */ new Map();
|
|
2185
|
+
var nextAdapter = 0;
|
|
2186
|
+
function adapter(id) {
|
|
2187
|
+
const store = adapters.get(id);
|
|
2188
|
+
if (!store) throw new DocxHistoryError("Closed", "History storage adapter is closed.");
|
|
2189
|
+
return store;
|
|
2190
|
+
}
|
|
2191
|
+
function toBase64(bytes) {
|
|
2192
|
+
let binary = "";
|
|
2193
|
+
for (let offset = 0; offset < bytes.length; offset += 32768)
|
|
2194
|
+
binary += String.fromCharCode(...bytes.subarray(offset, offset + 32768));
|
|
2195
|
+
return btoa(binary);
|
|
2196
|
+
}
|
|
2197
|
+
function fromBase64(encoded) {
|
|
2198
|
+
const binary = atob(encoded);
|
|
2199
|
+
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
2200
|
+
}
|
|
2201
|
+
function installHistoryStorageImports(setModuleImports) {
|
|
2202
|
+
setModuleImports("docxodus.history", {
|
|
2203
|
+
readBlob: async (id, json) => {
|
|
2204
|
+
const reference = JSON.parse(json);
|
|
2205
|
+
const bytes = await adapter(id).readBlob(reference);
|
|
2206
|
+
if (bytes === null) return "";
|
|
2207
|
+
if (bytes.length !== reference.length) throw new DocxHistoryError("PayloadMismatch", "Stored blob length differs from its reference.");
|
|
2208
|
+
return bytes.length === 0 ? "=" : toBase64(bytes);
|
|
2209
|
+
},
|
|
2210
|
+
putBlob: async (id, json, bytes) => adapter(id).putBlob(JSON.parse(json), bytes.slice()),
|
|
2211
|
+
readHead: async (id, documentId) => JSON.stringify(await adapter(id).readHead(documentId)),
|
|
2212
|
+
advanceHead: async (id, documentId, expected, state) => JSON.stringify(await adapter(id).advanceHead(documentId, JSON.parse(expected), JSON.parse(state))),
|
|
2213
|
+
initializeHead: async (id, documentId, head) => {
|
|
2214
|
+
const storage = adapter(id);
|
|
2215
|
+
if (!storage.initializeHead) throw new DocxHistoryError("InitializationUnsupported", "Storage cannot import an exact head.");
|
|
2216
|
+
return JSON.stringify(await storage.initializeHead(documentId, JSON.parse(head)));
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
var DocxHistoryClient = class {
|
|
2221
|
+
constructor(bridge, storage) {
|
|
2222
|
+
this.bridge = bridge;
|
|
2223
|
+
this.closed = false;
|
|
2224
|
+
if (nextAdapter >= 2147483647) throw new Error("History adapter identifiers exhausted.");
|
|
2225
|
+
this.adapterId = ++nextAdapter;
|
|
2226
|
+
adapters.set(this.adapterId, storage);
|
|
2227
|
+
try {
|
|
2228
|
+
this.handle = storage.initializeHead && bridge.OpenWithInitialization ? bridge.OpenWithInitialization(this.adapterId) : bridge.Open(this.adapterId);
|
|
2229
|
+
} catch (error) {
|
|
2230
|
+
adapters.delete(this.adapterId);
|
|
2231
|
+
throw error;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
close() {
|
|
2235
|
+
if (this.closed) return;
|
|
2236
|
+
this.bridge.Close(this.handle);
|
|
2237
|
+
adapters.delete(this.adapterId);
|
|
2238
|
+
this.closed = true;
|
|
2239
|
+
}
|
|
2240
|
+
/** Bind an identity without reading, creating a version, or taking ownership of this client. */
|
|
2241
|
+
document(documentId) {
|
|
2242
|
+
return new DocxHistoryDocument(documentId, (operation, fields, bytes) => this.call(operation, documentId, fields, bytes));
|
|
2243
|
+
}
|
|
2244
|
+
async exportHistoryArchive(documentId) {
|
|
2245
|
+
return fromBase64((await this.call("exportArchive", documentId)).bytes);
|
|
2246
|
+
}
|
|
2247
|
+
async importHistoryArchive(bytes) {
|
|
2248
|
+
checkArchiveSize(bytes);
|
|
2249
|
+
return (await this.call("importArchive", "", {}, bytes)).import;
|
|
2250
|
+
}
|
|
2251
|
+
async read(documentId) {
|
|
2252
|
+
return (await this.call("read", documentId)).view;
|
|
2253
|
+
}
|
|
2254
|
+
/** Host-triggered validated metadata tail; first join starts at the latest checkpoint. */
|
|
2255
|
+
async readChangesSince(documentId, after, maxEntriesToScan = 1e4) {
|
|
2256
|
+
return (await this.call("updates", documentId, { expectedHead: after, maxEntriesToScan })).update;
|
|
2257
|
+
}
|
|
2258
|
+
async readOperationsSince(documentId, after, maxEntriesToScan = 1e4) {
|
|
2259
|
+
return (await this.call("operations", documentId, { expectedHead: after, maxEntriesToScan })).operationUpdate;
|
|
2260
|
+
}
|
|
2261
|
+
async getOperation(documentId, operationId) {
|
|
2262
|
+
return (await this.call("getOperation", documentId, { operationId })).operation;
|
|
2263
|
+
}
|
|
2264
|
+
async exportOperationProposal(documentId, operationId) {
|
|
2265
|
+
return fromBase64((await this.call("exportOperationProposal", documentId, { operationId })).bytes);
|
|
2266
|
+
}
|
|
2267
|
+
async compareVersions(documentId, beforeVersionId, afterVersionId) {
|
|
2268
|
+
return fromBase64((await this.call("compare", documentId, { beforeVersionId, afterVersionId })).bytes);
|
|
2269
|
+
}
|
|
2270
|
+
async createVersion(documentId, expectedHead, bytes, metadata, requestId) {
|
|
2271
|
+
return (await this.call("create", documentId, { expectedHead, metadata, requestId }, bytes)).view;
|
|
2272
|
+
}
|
|
2273
|
+
async listVersions(documentId, cursor = null, limit = 25) {
|
|
2274
|
+
return (await this.call("list", documentId, { versionId: cursor, limit })).page;
|
|
2275
|
+
}
|
|
2276
|
+
async getVersion(documentId, versionId) {
|
|
2277
|
+
return (await this.call("get", documentId, { versionId })).version;
|
|
2278
|
+
}
|
|
2279
|
+
async exportVersion(documentId, versionId) {
|
|
2280
|
+
return fromBase64((await this.call("export", documentId, { versionId })).bytes);
|
|
2281
|
+
}
|
|
2282
|
+
async materialize(documentId, sequence, maxEntriesToScan = 1e4) {
|
|
2283
|
+
return fromBase64((await this.call("materialize", documentId, { sequence, maxEntriesToScan })).bytes);
|
|
2284
|
+
}
|
|
2285
|
+
async replay(documentId, sequence, maxEntriesToScan = 1e4) {
|
|
2286
|
+
return fromBase64((await this.call("replay", documentId, { sequence, maxEntriesToScan })).bytes);
|
|
2287
|
+
}
|
|
2288
|
+
async resolveSequenceAtTime(documentId, cutoff, maxEntriesToScan = 1e4) {
|
|
2289
|
+
return (await this.call("resolveTime", documentId, { cutoff, maxEntriesToScan })).sequence;
|
|
2290
|
+
}
|
|
2291
|
+
async restoreVersion(documentId, expectedHead, versionId, metadata, requestId) {
|
|
2292
|
+
return (await this.call("restore", documentId, { expectedHead, versionId, metadata, requestId })).view;
|
|
2293
|
+
}
|
|
2294
|
+
async call(operation, documentId, fields = {}, bytes = new Uint8Array()) {
|
|
2295
|
+
if (this.closed) throw new DocxHistoryError("Closed", "History client is closed.");
|
|
2296
|
+
return parseResult(await this.bridge.Invoke(
|
|
2297
|
+
this.handle,
|
|
2298
|
+
JSON.stringify({ schemaVersion: 1, operation, documentId, ...fields }),
|
|
2299
|
+
bytes
|
|
2300
|
+
));
|
|
2301
|
+
}
|
|
2302
|
+
};
|
|
2303
|
+
var DocxHistoryReader = class {
|
|
2304
|
+
/** @internal Obtain through client.document() or openDocxHistoryArchive(). */
|
|
2305
|
+
constructor(documentId, call) {
|
|
2306
|
+
this.call = call;
|
|
2307
|
+
this.#documentId = documentId;
|
|
2308
|
+
}
|
|
2309
|
+
#documentId;
|
|
2310
|
+
get documentId() {
|
|
2311
|
+
return this.#documentId;
|
|
2312
|
+
}
|
|
2313
|
+
async read() {
|
|
2314
|
+
return (await this.call("read")).view;
|
|
2315
|
+
}
|
|
2316
|
+
async listVersions(cursor = null, limit = 25) {
|
|
2317
|
+
return (await this.call("list", { versionId: cursor, limit })).page;
|
|
2318
|
+
}
|
|
2319
|
+
async getVersion(versionId) {
|
|
2320
|
+
return (await this.call("get", { versionId })).version;
|
|
2321
|
+
}
|
|
2322
|
+
/** Omit versionId for latest; pass an ID to keep a UI operation pinned to a captured version. */
|
|
2323
|
+
async exportDocx(versionId = null) {
|
|
2324
|
+
return fromBase64((await this.call("exportDocx", { versionId })).bytes);
|
|
2325
|
+
}
|
|
2326
|
+
async exportHistoryArchive() {
|
|
2327
|
+
return fromBase64((await this.call("exportArchive")).bytes);
|
|
2328
|
+
}
|
|
2329
|
+
async materialize(sequence, maxEntriesToScan = 1e4) {
|
|
2330
|
+
return fromBase64((await this.call("materialize", { sequence, maxEntriesToScan })).bytes);
|
|
2331
|
+
}
|
|
2332
|
+
async replay(sequence, maxEntriesToScan = 1e4) {
|
|
2333
|
+
return fromBase64((await this.call("replay", { sequence, maxEntriesToScan })).bytes);
|
|
2334
|
+
}
|
|
2335
|
+
async resolveSequenceAtTime(cutoff, maxEntriesToScan = 1e4) {
|
|
2336
|
+
return (await this.call("resolveTime", { cutoff, maxEntriesToScan })).sequence;
|
|
2337
|
+
}
|
|
2338
|
+
async readChangesSince(after, maxEntriesToScan = 1e4) {
|
|
2339
|
+
return (await this.call("updates", { expectedHead: after, maxEntriesToScan })).update;
|
|
2340
|
+
}
|
|
2341
|
+
async readOperationsSince(after, maxEntriesToScan = 1e4) {
|
|
2342
|
+
return (await this.call("operations", { expectedHead: after, maxEntriesToScan })).operationUpdate;
|
|
2343
|
+
}
|
|
2344
|
+
async getOperation(operationId) {
|
|
2345
|
+
return (await this.call("getOperation", { operationId })).operation;
|
|
2346
|
+
}
|
|
2347
|
+
async exportOperationProposal(operationId) {
|
|
2348
|
+
return fromBase64((await this.call("exportOperationProposal", { operationId })).bytes);
|
|
2349
|
+
}
|
|
2350
|
+
/** Redlined DOCX through DocxCompare's existing accepted-input revision policy. */
|
|
2351
|
+
async compareVersions(beforeVersionId, afterVersionId) {
|
|
2352
|
+
return fromBase64((await this.call("compare", { beforeVersionId, afterVersionId })).bytes);
|
|
2353
|
+
}
|
|
2354
|
+
};
|
|
2355
|
+
var DocxHistoryDocument = class extends DocxHistoryReader {
|
|
2356
|
+
async createVersion(expectedHead, bytes, metadata, requestId) {
|
|
2357
|
+
requireRequestId(requestId);
|
|
2358
|
+
return (await this.call("create", { expectedHead, metadata, requestId }, bytes)).view;
|
|
2359
|
+
}
|
|
2360
|
+
async restoreVersion(expectedHead, versionId, metadata, requestId) {
|
|
2361
|
+
requireRequestId(requestId);
|
|
2362
|
+
return (await this.call("restore", { expectedHead, versionId, metadata, requestId })).view;
|
|
2363
|
+
}
|
|
2364
|
+
};
|
|
2365
|
+
var DocxHistoryArchive = class _DocxHistoryArchive extends DocxHistoryReader {
|
|
2366
|
+
constructor(bridge, handle, info) {
|
|
2367
|
+
super(info.documentId, async (operation, fields = {}, bytes = new Uint8Array()) => {
|
|
2368
|
+
if (this.closed) throw new DocxHistoryError("Closed", "History archive is closed.");
|
|
2369
|
+
return parseResult(await bridge.Invoke(
|
|
2370
|
+
handle,
|
|
2371
|
+
JSON.stringify({ schemaVersion: 1, operation, documentId: this.documentId, ...fields }),
|
|
2372
|
+
bytes
|
|
2373
|
+
));
|
|
2374
|
+
});
|
|
2375
|
+
this.bridge = bridge;
|
|
2376
|
+
this.handle = handle;
|
|
2377
|
+
this.info = info;
|
|
2378
|
+
this.closed = false;
|
|
2379
|
+
}
|
|
2380
|
+
/** For custom loaders; normal callers use openDocxHistoryArchive(). */
|
|
2381
|
+
static async open(bridge, bytes) {
|
|
2382
|
+
checkArchiveSize(bytes);
|
|
2383
|
+
if (!bridge.OpenArchive) throw new Error("This WASM build does not include portable history.");
|
|
2384
|
+
const result = parseResult(await bridge.OpenArchive(bytes));
|
|
2385
|
+
return new _DocxHistoryArchive(bridge, result.handle, result.archive);
|
|
2386
|
+
}
|
|
2387
|
+
close() {
|
|
2388
|
+
if (this.closed) return;
|
|
2389
|
+
this.bridge.Close(this.handle);
|
|
2390
|
+
this.closed = true;
|
|
2391
|
+
}
|
|
2392
|
+
};
|
|
2393
|
+
function parseResult(json) {
|
|
2394
|
+
const result = JSON.parse(json);
|
|
2395
|
+
if (!result.success) throw new DocxHistoryError(result.errorCode, result.message);
|
|
2396
|
+
return result;
|
|
2397
|
+
}
|
|
2398
|
+
function checkArchiveSize(bytes) {
|
|
2399
|
+
if (bytes.length > MAX_HISTORY_ARCHIVE_BYTES) throw new DocxHistoryError("ResourceLimit", "History archive exceeds 64 MiB.");
|
|
2400
|
+
}
|
|
2401
|
+
function requireRequestId(requestId) {
|
|
2402
|
+
if (typeof requestId !== "string" || !requestId.trim()) throw new DocxHistoryError("InvalidRequest", "A durable requestId is required.");
|
|
2403
|
+
}
|
|
2404
|
+
function createMemoryHistoryStorage(maxBlobBytes = 256 * 1024 * 1024) {
|
|
2405
|
+
if (!Number.isSafeInteger(maxBlobBytes) || maxBlobBytes <= 0) throw new RangeError("Invalid blob byte limit.");
|
|
2406
|
+
const blobs = /* @__PURE__ */ new Map();
|
|
2407
|
+
const heads = /* @__PURE__ */ new Map();
|
|
2408
|
+
const copy = (value) => JSON.parse(JSON.stringify(value));
|
|
2409
|
+
const key = (reference) => {
|
|
2410
|
+
if (reference.digest.algorithm !== "SHA-256" || !/^[a-f0-9]{64}$/.test(reference.digest.value) || !Number.isSafeInteger(reference.length) || reference.length < 0 || reference.length > maxBlobBytes)
|
|
2411
|
+
throw new DocxHistoryError("InvalidRequest", "Invalid blob reference or byte limit.");
|
|
2412
|
+
return `${reference.digest.value}:${reference.length}`;
|
|
2413
|
+
};
|
|
2414
|
+
const equalReference = (a, b) => a.length === b.length && a.digest.algorithm === b.digest.algorithm && a.digest.value === b.digest.value;
|
|
2415
|
+
const equalHead = (a, b) => a === null || b === null ? a === b : a.revision === b.revision && equalReference(a.state, b.state);
|
|
2416
|
+
return {
|
|
2417
|
+
async readBlob(reference) {
|
|
2418
|
+
return blobs.get(key(reference))?.slice() ?? null;
|
|
2419
|
+
},
|
|
2420
|
+
async putBlob(reference, bytes) {
|
|
2421
|
+
const blobKey = key(reference);
|
|
2422
|
+
const captured = bytes.slice();
|
|
2423
|
+
if (captured.length !== reference.length) throw new DocxHistoryError("PayloadMismatch", "Blob length mismatch.");
|
|
2424
|
+
const digest = Array.from(
|
|
2425
|
+
new Uint8Array(await crypto.subtle.digest("SHA-256", captured)),
|
|
2426
|
+
(n) => n.toString(16).padStart(2, "0")
|
|
2427
|
+
).join("");
|
|
2428
|
+
if (digest !== blobKey.split(":")[0]) throw new DocxHistoryError("PayloadMismatch", "Blob digest mismatch.");
|
|
2429
|
+
if (!blobs.has(blobKey)) blobs.set(blobKey, captured);
|
|
2430
|
+
},
|
|
2431
|
+
async readHead(documentId) {
|
|
2432
|
+
return copy(heads.get(documentId) ?? null);
|
|
2433
|
+
},
|
|
2434
|
+
async advanceHead(documentId, expected, state) {
|
|
2435
|
+
key(state);
|
|
2436
|
+
const current = heads.get(documentId) ?? null;
|
|
2437
|
+
if (!equalHead(current, expected)) return null;
|
|
2438
|
+
const revision = BigInt(current?.revision ?? "0") + 1n;
|
|
2439
|
+
if (revision > 9223372036854775807n) throw new RangeError("History revision exhausted.");
|
|
2440
|
+
const head = { revision: String(revision), state: copy(state) };
|
|
2441
|
+
heads.set(documentId, head);
|
|
2442
|
+
return copy(head);
|
|
2443
|
+
},
|
|
2444
|
+
async initializeHead(documentId, head) {
|
|
2445
|
+
key(head.state);
|
|
2446
|
+
if (typeof head.revision !== "string" || !/^[1-9][0-9]*$/.test(head.revision) || BigInt(head.revision) > 9223372036854775807n)
|
|
2447
|
+
throw new DocxHistoryError("InvalidRequest", "Imported head revision must be a positive Int64 string.");
|
|
2448
|
+
const existing = heads.get(documentId);
|
|
2449
|
+
if (existing) return { initialized: false, head: copy(existing) };
|
|
2450
|
+
const captured = copy(head);
|
|
2451
|
+
heads.set(documentId, captured);
|
|
2452
|
+
return { initialized: true, head: copy(captured) };
|
|
2453
|
+
}
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2164
2457
|
// src/page-number-format.ts
|
|
2165
2458
|
var ROMAN_ONES = ["", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"];
|
|
2166
2459
|
var ROMAN_TENS = ["", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"];
|
|
@@ -5610,11 +5903,11 @@ var Docxodus = (() => {
|
|
|
5610
5903
|
if (!this.partUriFor(which, "default")) this.seedStory(which, "default");
|
|
5611
5904
|
if (!this.partUriFor(which, kind)) this.seedStory(which, kind);
|
|
5612
5905
|
}
|
|
5613
|
-
const res =
|
|
5906
|
+
const res = parseResult2(this.bridge.EnsureHeaderFooterVisible(this.handle, this.bodyAnchorId, kind));
|
|
5614
5907
|
if (!res.success) return false;
|
|
5615
5908
|
} else {
|
|
5616
5909
|
if (!this.bridge.SetHeaderFooterKindEnabled) return false;
|
|
5617
|
-
const res =
|
|
5910
|
+
const res = parseResult2(this.bridge.SetHeaderFooterKindEnabled(this.handle, this.bodyAnchorId, kind, false));
|
|
5618
5911
|
if (!res.success) return false;
|
|
5619
5912
|
for (const which of ["header", "footer"]) {
|
|
5620
5913
|
if (this.kinds[which] === kind) this.kinds[which] = "default";
|
|
@@ -5639,7 +5932,7 @@ var Docxodus = (() => {
|
|
|
5639
5932
|
* Deliberately a PLAIN field (no `\*` switch), exactly as Word inserts one, so it follows the
|
|
5640
5933
|
* section's page-number format — see {@link setPageNumbering}. */
|
|
5641
5934
|
insertPageNumber(which, anchorId, field) {
|
|
5642
|
-
const res =
|
|
5935
|
+
const res = parseResult2(this.bridge.InsertPageNumberField(this.handle, anchorId, field, ""));
|
|
5643
5936
|
if (!res.success) return false;
|
|
5644
5937
|
this.refresh(which);
|
|
5645
5938
|
return true;
|
|
@@ -5662,7 +5955,7 @@ var Docxodus = (() => {
|
|
|
5662
5955
|
/** Set this section's page numbering (`w:pgNumType`) — Word's *Format Page Numbers…*. */
|
|
5663
5956
|
setPageNumbering(op) {
|
|
5664
5957
|
if (!this.bodyAnchorId) return false;
|
|
5665
|
-
const res =
|
|
5958
|
+
const res = parseResult2(this.bridge.SetPageNumbering(this.handle, this.bodyAnchorId, JSON.stringify(op)));
|
|
5666
5959
|
if (!res.success) return false;
|
|
5667
5960
|
this.reloadSection();
|
|
5668
5961
|
return true;
|
|
@@ -5674,7 +5967,7 @@ var Docxodus = (() => {
|
|
|
5674
5967
|
/** Remove this section's page-numbering start/format. */
|
|
5675
5968
|
clearPageNumbering() {
|
|
5676
5969
|
if (!this.bodyAnchorId) return false;
|
|
5677
|
-
const res =
|
|
5970
|
+
const res = parseResult2(this.bridge.ClearPageNumbering(this.handle, this.bodyAnchorId));
|
|
5678
5971
|
if (!res.success) return false;
|
|
5679
5972
|
this.reloadSection();
|
|
5680
5973
|
return true;
|
|
@@ -5985,7 +6278,7 @@ var Docxodus = (() => {
|
|
|
5985
6278
|
seedStory(which, kind) {
|
|
5986
6279
|
if (!this.bodyAnchorId) return;
|
|
5987
6280
|
const set = which === "header" ? this.bridge.SetHeaderText : this.bridge.SetFooterText;
|
|
5988
|
-
const res =
|
|
6281
|
+
const res = parseResult2(set(this.handle, this.bodyAnchorId, kind, ""));
|
|
5989
6282
|
if (!res.success) return;
|
|
5990
6283
|
this.callbacks.refreshAnchorMap();
|
|
5991
6284
|
this.sectionInfo = this.readSectionInfo(this.bodyAnchorId);
|
|
@@ -6141,7 +6434,7 @@ var Docxodus = (() => {
|
|
|
6141
6434
|
const range = d.caretRangeFromPoint?.(x, y);
|
|
6142
6435
|
return range ? { node: range.startContainer, offset: range.startOffset } : null;
|
|
6143
6436
|
}
|
|
6144
|
-
function
|
|
6437
|
+
function parseResult2(json) {
|
|
6145
6438
|
try {
|
|
6146
6439
|
return JSON.parse(json);
|
|
6147
6440
|
} catch {
|
|
@@ -8374,7 +8667,7 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
8374
8667
|
};
|
|
8375
8668
|
}
|
|
8376
8669
|
var honeyPotFix = makeHoneyPotFix();
|
|
8377
|
-
var
|
|
8670
|
+
var adapter2 = makeAdapter({
|
|
8378
8671
|
typeKey: "element",
|
|
8379
8672
|
defaultDropEffect: "move",
|
|
8380
8673
|
mount: function mount(api2) {
|
|
@@ -8463,8 +8756,8 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
8463
8756
|
},
|
|
8464
8757
|
onPostDispatch: honeyPotFix.getOnPostDispatch()
|
|
8465
8758
|
});
|
|
8466
|
-
var dropTargetForElements =
|
|
8467
|
-
var monitorForElements =
|
|
8759
|
+
var dropTargetForElements = adapter2.dropTarget;
|
|
8760
|
+
var monitorForElements = adapter2.monitor;
|
|
8468
8761
|
function draggable(args) {
|
|
8469
8762
|
if (true) {
|
|
8470
8763
|
if (args.dragHandle && !args.element.contains(args.dragHandle)) {
|
|
@@ -8488,7 +8781,7 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
8488
8781
|
// this is because you *must* have a draggable element to start a drag
|
|
8489
8782
|
// but you _might_ not have any drop targets immediately
|
|
8490
8783
|
// (You might create drop targets async)
|
|
8491
|
-
|
|
8784
|
+
adapter2.registerUsage(),
|
|
8492
8785
|
addToRegistry(args),
|
|
8493
8786
|
addAttribute(args.element, {
|
|
8494
8787
|
attribute: "draggable",
|
|
@@ -9935,22 +10228,55 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
9935
10228
|
const span = trimmedSpan(block, { start: Math.min(start2, end), length: Math.abs(end - start2) });
|
|
9936
10229
|
return span.length > 0 ? span : null;
|
|
9937
10230
|
}
|
|
9938
|
-
function
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
const range = document.createRange();
|
|
10231
|
+
function contentRangeIn(el, start2, length) {
|
|
10232
|
+
if (!el.isConnected) return null;
|
|
10233
|
+
const doc = el.ownerDocument;
|
|
10234
|
+
const range = doc.createRange();
|
|
9943
10235
|
const from = contentPositionIn(el, start2);
|
|
9944
10236
|
const to = contentPositionIn(el, start2 + length);
|
|
9945
10237
|
try {
|
|
9946
10238
|
range.setStart(from.node, from.offset);
|
|
9947
10239
|
range.setEnd(to.node, to.offset);
|
|
9948
10240
|
} catch {
|
|
9949
|
-
return;
|
|
10241
|
+
return null;
|
|
9950
10242
|
}
|
|
10243
|
+
return range;
|
|
10244
|
+
}
|
|
10245
|
+
function selectRange(el, start2, length) {
|
|
10246
|
+
const sel = typeof window !== "undefined" ? window.getSelection() : null;
|
|
10247
|
+
if (!sel || !el.isConnected) return;
|
|
10248
|
+
el.focus();
|
|
10249
|
+
const range = contentRangeIn(el, start2, length);
|
|
10250
|
+
if (!range) return;
|
|
9951
10251
|
sel.removeAllRanges();
|
|
9952
10252
|
sel.addRange(range);
|
|
9953
10253
|
}
|
|
10254
|
+
var FIND_HIGHLIGHT = "docxodus-find";
|
|
10255
|
+
var FIND_HIGHLIGHT_ACTIVE = "docxodus-find-active";
|
|
10256
|
+
var FIND_PAINT_LIMIT = 500;
|
|
10257
|
+
var findHighlightStyledDocuments = /* @__PURE__ */ new WeakSet();
|
|
10258
|
+
var findPaintOwner = null;
|
|
10259
|
+
function ensureFindHighlightStyles(doc) {
|
|
10260
|
+
if (findHighlightStyledDocuments.has(doc)) return;
|
|
10261
|
+
findHighlightStyledDocuments.add(doc);
|
|
10262
|
+
const style = doc.createElement("style");
|
|
10263
|
+
style.dataset.docxodusFindHighlight = "true";
|
|
10264
|
+
style.textContent = `
|
|
10265
|
+
::highlight(${FIND_HIGHLIGHT}) { background-color: rgba(15, 118, 110, .16); }
|
|
10266
|
+
::highlight(${FIND_HIGHLIGHT_ACTIVE}) { background-color: rgba(15, 118, 110, .38); }
|
|
10267
|
+
`;
|
|
10268
|
+
(doc.head ?? doc.documentElement).appendChild(style);
|
|
10269
|
+
}
|
|
10270
|
+
function highlightRegistry() {
|
|
10271
|
+
if (typeof CSS === "undefined") return null;
|
|
10272
|
+
const registry = CSS.highlights;
|
|
10273
|
+
const ctor = globalThis.Highlight;
|
|
10274
|
+
return registry && typeof ctor === "function" ? registry : null;
|
|
10275
|
+
}
|
|
10276
|
+
function makeHighlight(ranges) {
|
|
10277
|
+
const ctor = globalThis.Highlight;
|
|
10278
|
+
return new ctor(...ranges);
|
|
10279
|
+
}
|
|
9954
10280
|
function inBorderWrapper(el) {
|
|
9955
10281
|
const style = el.parentElement?.getAttribute("style") ?? "";
|
|
9956
10282
|
return /border-(top|bottom|left|right):\s*(?!none)[^;]+/i.test(style);
|
|
@@ -10321,6 +10647,7 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
10321
10647
|
document.removeEventListener("mouseup", this.onMouseUp, true);
|
|
10322
10648
|
}
|
|
10323
10649
|
this.clearDragSelection();
|
|
10650
|
+
this.clearFindMatches();
|
|
10324
10651
|
this.teardownBlockDrag();
|
|
10325
10652
|
this.gutter?.dispose();
|
|
10326
10653
|
this.gutter = null;
|
|
@@ -12719,15 +13046,65 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
12719
13046
|
}
|
|
12720
13047
|
return out;
|
|
12721
13048
|
}
|
|
12722
|
-
/**
|
|
13049
|
+
/**
|
|
13050
|
+
* Select a match, focus its block and scroll it into view — the caret lands on the hit, so
|
|
13051
|
+
* typing continues in the document. A find box that is still being typed into wants
|
|
13052
|
+
* `showFindMatches` instead; this is the commit step (closing the bar, jumping in to edit).
|
|
13053
|
+
*/
|
|
12723
13054
|
selectMatch(match) {
|
|
12724
13055
|
if (!match.block.isConnected) return;
|
|
13056
|
+
this.clearFindMatches();
|
|
12725
13057
|
this.activeBlock = match.block;
|
|
12726
13058
|
selectRange(match.block, match.start, match.length);
|
|
12727
13059
|
match.block.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
12728
13060
|
}
|
|
12729
|
-
/**
|
|
12730
|
-
|
|
13061
|
+
/**
|
|
13062
|
+
* Paint `matches` and scroll the one at `activeIndex` into view WITHOUT moving focus or the
|
|
13063
|
+
* caret. This is what a find field calls on every keystroke: the document shows where the hits
|
|
13064
|
+
* are and rides to the current one, while the keyboard stays in the search box.
|
|
13065
|
+
*
|
|
13066
|
+
* Falls back to the document selection where the CSS Custom Highlight API is missing — still
|
|
13067
|
+
* without focusing the block, so the next character typed goes to the search field either way.
|
|
13068
|
+
*/
|
|
13069
|
+
showFindMatches(matches, activeIndex) {
|
|
13070
|
+
const active = matches[activeIndex];
|
|
13071
|
+
this.clearFindMatches();
|
|
13072
|
+
if (!active || !active.block.isConnected) return;
|
|
13073
|
+
this.activeBlock = active.block;
|
|
13074
|
+
const activeRange = contentRangeIn(active.block, active.start, active.length);
|
|
13075
|
+
const registry = highlightRegistry();
|
|
13076
|
+
if (registry && activeRange) {
|
|
13077
|
+
ensureFindHighlightStyles(this.container.ownerDocument);
|
|
13078
|
+
const rest = [];
|
|
13079
|
+
for (let i = 0; i < matches.length && rest.length < FIND_PAINT_LIMIT; i++) {
|
|
13080
|
+
if (i === activeIndex) continue;
|
|
13081
|
+
const range = contentRangeIn(matches[i].block, matches[i].start, matches[i].length);
|
|
13082
|
+
if (range) rest.push(range);
|
|
13083
|
+
}
|
|
13084
|
+
if (rest.length > 0) registry.set(FIND_HIGHLIGHT, makeHighlight(rest));
|
|
13085
|
+
registry.set(FIND_HIGHLIGHT_ACTIVE, makeHighlight([activeRange]));
|
|
13086
|
+
findPaintOwner = this;
|
|
13087
|
+
} else if (activeRange) {
|
|
13088
|
+
const sel = this.container.ownerDocument.defaultView?.getSelection();
|
|
13089
|
+
sel?.removeAllRanges();
|
|
13090
|
+
sel?.addRange(activeRange);
|
|
13091
|
+
}
|
|
13092
|
+
active.block.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
13093
|
+
}
|
|
13094
|
+
/** Drop the find painting (closing the find bar, or committing a match to the caret). */
|
|
13095
|
+
clearFindMatches() {
|
|
13096
|
+
if (findPaintOwner !== this) return;
|
|
13097
|
+
const registry = highlightRegistry();
|
|
13098
|
+
registry?.delete(FIND_HIGHLIGHT);
|
|
13099
|
+
registry?.delete(FIND_HIGHLIGHT_ACTIVE);
|
|
13100
|
+
findPaintOwner = null;
|
|
13101
|
+
}
|
|
13102
|
+
/**
|
|
13103
|
+
* Replace one match's text (formatting of the surrounding run is inherited). `focus: false`
|
|
13104
|
+
* leaves the keyboard where it is — a Replace button pressed from the find bar must not drag
|
|
13105
|
+
* the caret into the document mid-search.
|
|
13106
|
+
*/
|
|
13107
|
+
replaceMatch(match, replacement, options = {}) {
|
|
12731
13108
|
const block = match.block;
|
|
12732
13109
|
if (this.closed || !block.isConnected) return false;
|
|
12733
13110
|
const unid = block.getAttribute("data-anchor");
|
|
@@ -12748,7 +13125,7 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
12748
13125
|
);
|
|
12749
13126
|
if (!res.success) return false;
|
|
12750
13127
|
const fresh = this.swapBlock(block, unid, res.modified?.[0]);
|
|
12751
|
-
if (fresh) selectRange(fresh, span.start, replacement.length);
|
|
13128
|
+
if (fresh && options.focus !== false) selectRange(fresh, span.start, replacement.length);
|
|
12752
13129
|
return true;
|
|
12753
13130
|
}
|
|
12754
13131
|
/** Replace every occurrence; returns how many were replaced. */
|
|
@@ -12767,7 +13144,11 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
12767
13144
|
for (const m of list.slice().sort((a, b) => b.start - a.start)) {
|
|
12768
13145
|
if (!current.isConnected) break;
|
|
12769
13146
|
const before = current;
|
|
12770
|
-
if (this.replaceMatch(
|
|
13147
|
+
if (this.replaceMatch(
|
|
13148
|
+
{ block: current, start: m.start, length: m.length },
|
|
13149
|
+
replacement,
|
|
13150
|
+
{ focus: options.focus }
|
|
13151
|
+
)) {
|
|
12771
13152
|
count++;
|
|
12772
13153
|
current = this.activeBlock && this.activeBlock !== before ? this.activeBlock : current;
|
|
12773
13154
|
}
|
|
@@ -13449,7 +13830,7 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
13449
13830
|
}
|
|
13450
13831
|
|
|
13451
13832
|
// src/ribbon-chrome.ts
|
|
13452
|
-
var RIBBON_STYLE_VERSION = "
|
|
13833
|
+
var RIBBON_STYLE_VERSION = "11";
|
|
13453
13834
|
var RIBBON_STYLE_ATTR = "data-docxodus-ribbon-styles";
|
|
13454
13835
|
var COMMENT_GUTTER_WIDTH = 264;
|
|
13455
13836
|
var RIBBON_CSS = `
|
|
@@ -13882,9 +14263,11 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
13882
14263
|
background: linear-gradient(to top, var(--dxr-desk), transparent);
|
|
13883
14264
|
}
|
|
13884
14265
|
.dxr[data-chrome] .dxr-surface { position: relative; margin: 26px auto; padding: 0 16px 96px; }
|
|
13885
|
-
/*
|
|
13886
|
-
|
|
13887
|
-
.dxr[data-chrome="full"] .dxr-surface[data-comments="on"]
|
|
14266
|
+
/* Reserve the markup area only while a visible comment or draft needs it. The gutter owns
|
|
14267
|
+
its empty/hidden state, so ordinary documents stay centered without disabling comments. */
|
|
14268
|
+
.dxr[data-chrome="full"] .dxr-surface[data-comments="on"]:has(> .docx-comment-gutter:not([data-empty]):not([hidden])) {
|
|
14269
|
+
padding-right: calc(var(--dxr-gutter) + 8px);
|
|
14270
|
+
}
|
|
13888
14271
|
.dxr-surface [contenteditable="true"]:focus {
|
|
13889
14272
|
outline: 2px solid var(--dxr-accent);
|
|
13890
14273
|
outline-offset: 2px;
|
|
@@ -16035,11 +16418,18 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
16035
16418
|
if (!this.live || !text.value) return;
|
|
16036
16419
|
let count = 0;
|
|
16037
16420
|
this.run("replace all", () => {
|
|
16038
|
-
count = this.live.replaceAll(text.value, replace.value, {
|
|
16421
|
+
count = this.live.replaceAll(text.value, replace.value, {
|
|
16422
|
+
matchCase: matchCase.checked,
|
|
16423
|
+
focus: false
|
|
16424
|
+
});
|
|
16039
16425
|
});
|
|
16040
16426
|
this.setStatus(`Replaced ${count} occurrence${count === 1 ? "" : "s"}`);
|
|
16041
16427
|
this.refreshFind(true);
|
|
16042
16428
|
});
|
|
16429
|
+
for (const id of ["findprev", "findnext", "replaceone", "replaceall"]) {
|
|
16430
|
+
const button = this.control(id);
|
|
16431
|
+
if (button) this.keepSelection(button);
|
|
16432
|
+
}
|
|
16043
16433
|
bar.hidden = true;
|
|
16044
16434
|
}
|
|
16045
16435
|
openFindBar(withReplace) {
|
|
@@ -16050,28 +16440,44 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
16050
16440
|
const text = this.require("findtext");
|
|
16051
16441
|
text.focus();
|
|
16052
16442
|
text.select();
|
|
16053
|
-
this.refreshFind(
|
|
16443
|
+
this.refreshFind(true);
|
|
16054
16444
|
}
|
|
16445
|
+
/**
|
|
16446
|
+
* Close the bar and hand the current hit over to the caret: the document is where the user is
|
|
16447
|
+
* going next, and Word leaves the insertion point on the match it stopped at. Until this runs
|
|
16448
|
+
* the match is only PAINTED (see `showFindMatches`), so the search field keeps the keyboard for
|
|
16449
|
+
* as long as the bar is open.
|
|
16450
|
+
*/
|
|
16055
16451
|
closeFindBar() {
|
|
16056
16452
|
const bar = this.control("findbar");
|
|
16057
16453
|
if (bar) bar.hidden = true;
|
|
16454
|
+
const match = this.findMatches[this.findIndex];
|
|
16455
|
+
if (match) this.live?.selectMatch(match);
|
|
16456
|
+
else this.live?.clearFindMatches();
|
|
16058
16457
|
this.findMatches = [];
|
|
16059
16458
|
this.findIndex = -1;
|
|
16060
16459
|
}
|
|
16061
|
-
|
|
16460
|
+
/**
|
|
16461
|
+
* Re-scan for the current query. `show` paints the hits and rides to the first one — it never
|
|
16462
|
+
* focuses the document, so typing the second character of a query that already matched goes on
|
|
16463
|
+
* refining the search instead of being typed into the document.
|
|
16464
|
+
*/
|
|
16465
|
+
refreshFind(show) {
|
|
16062
16466
|
const text = this.require("findtext");
|
|
16063
16467
|
const matchCase = this.require("findcase").checked;
|
|
16064
16468
|
this.findMatches = this.live && text.value ? this.live.find(text.value, { matchCase }) : [];
|
|
16065
16469
|
this.findIndex = this.findMatches.length > 0 ? 0 : -1;
|
|
16066
16470
|
this.updateFindCount();
|
|
16067
|
-
if (
|
|
16471
|
+
if (!show) return;
|
|
16472
|
+
if (this.findIndex >= 0) this.live?.showFindMatches(this.findMatches, this.findIndex);
|
|
16473
|
+
else this.live?.clearFindMatches();
|
|
16068
16474
|
}
|
|
16069
16475
|
stepFind(direction) {
|
|
16070
16476
|
if (!this.live) return;
|
|
16071
16477
|
if (this.findMatches.length === 0) this.refreshFind(false);
|
|
16072
16478
|
if (this.findMatches.length === 0) return;
|
|
16073
16479
|
this.findIndex = (this.findIndex + direction + this.findMatches.length) % this.findMatches.length;
|
|
16074
|
-
this.live.
|
|
16480
|
+
this.live.showFindMatches(this.findMatches, this.findIndex);
|
|
16075
16481
|
this.updateFindCount();
|
|
16076
16482
|
}
|
|
16077
16483
|
replaceCurrent() {
|
|
@@ -16080,12 +16486,12 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
16080
16486
|
const match = this.findMatches[this.findIndex];
|
|
16081
16487
|
if (!match) return;
|
|
16082
16488
|
const replacement = this.require("replacetext").value;
|
|
16083
|
-
this.run("replace", () => this.live.replaceMatch(match, replacement));
|
|
16489
|
+
this.run("replace", () => this.live.replaceMatch(match, replacement, { focus: false }));
|
|
16084
16490
|
const keep = this.findIndex;
|
|
16085
16491
|
this.refreshFind(false);
|
|
16086
16492
|
if (this.findMatches.length > 0) {
|
|
16087
16493
|
this.findIndex = Math.min(keep, this.findMatches.length - 1);
|
|
16088
|
-
this.live.
|
|
16494
|
+
this.live.showFindMatches(this.findMatches, this.findIndex);
|
|
16089
16495
|
this.updateFindCount();
|
|
16090
16496
|
}
|
|
16091
16497
|
}
|
|
@@ -16517,6 +16923,16 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
16517
16923
|
|
|
16518
16924
|
// src/index.ts
|
|
16519
16925
|
var import_meta = {};
|
|
16926
|
+
function openDocxHistory(storage) {
|
|
16927
|
+
const bridge = ensureInitialized().HistoryBridge;
|
|
16928
|
+
if (!bridge) throw new Error("This WASM build does not include history bindings.");
|
|
16929
|
+
return new DocxHistoryClient(bridge, storage);
|
|
16930
|
+
}
|
|
16931
|
+
function openDocxHistoryArchive(bytes) {
|
|
16932
|
+
const bridge = ensureInitialized().HistoryBridge;
|
|
16933
|
+
if (!bridge) throw new Error("This WASM build does not include history bindings.");
|
|
16934
|
+
return DocxHistoryArchive.open(bridge, bytes);
|
|
16935
|
+
}
|
|
16520
16936
|
function openDocxSession2(bytes, settings) {
|
|
16521
16937
|
const wasm = ensureInitialized();
|
|
16522
16938
|
return openDocxSession(bytes, wasm, settings);
|
|
@@ -16571,14 +16987,16 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
|
|
|
16571
16987
|
/* @vite-ignore */
|
|
16572
16988
|
dotnetPath
|
|
16573
16989
|
);
|
|
16574
|
-
const { getAssemblyExports, getConfig } = await dotnet.withDiagnosticTracing(false).create();
|
|
16990
|
+
const { getAssemblyExports, getConfig, setModuleImports } = await dotnet.withDiagnosticTracing(false).create();
|
|
16991
|
+
installHistoryStorageImports(setModuleImports);
|
|
16575
16992
|
const config = getConfig();
|
|
16576
16993
|
const exports = await getAssemblyExports(config.mainAssemblyName);
|
|
16577
16994
|
wasmExports = {
|
|
16578
16995
|
DocumentConverter: exports.DocxodusWasm.DocumentConverter,
|
|
16579
16996
|
DocumentComparer: exports.DocxodusWasm.DocumentComparer,
|
|
16580
16997
|
DocxDiffBridge: exports.DocxodusWasm.DocxDiffBridge,
|
|
16581
|
-
DocxSessionBridge: exports.DocxodusWasm.DocxSessionBridge
|
|
16998
|
+
DocxSessionBridge: exports.DocxodusWasm.DocxSessionBridge,
|
|
16999
|
+
HistoryBridge: exports.DocxodusWasm.HistoryBridge
|
|
16582
17000
|
};
|
|
16583
17001
|
return true;
|
|
16584
17002
|
} catch {
|
|
@@ -17953,6 +18371,20 @@ ${parsed.documentElement.outerHTML}`;
|
|
|
17953
18371
|
}
|
|
17954
18372
|
throw new Error("Docxodus embed: unsupported document source");
|
|
17955
18373
|
}
|
|
18374
|
+
function scopedDocumentFragment(mount2, html) {
|
|
18375
|
+
const parsed = new DOMParser().parseFromString(html, "text/html");
|
|
18376
|
+
const fragment = document.createDocumentFragment();
|
|
18377
|
+
parsed.querySelectorAll("style").forEach((sourceStyle) => {
|
|
18378
|
+
const style = document.createElement("style");
|
|
18379
|
+
style.textContent = sourceStyle.textContent;
|
|
18380
|
+
scopeStyleElement(style, mount2.selector);
|
|
18381
|
+
fragment.appendChild(style);
|
|
18382
|
+
});
|
|
18383
|
+
const body = document.createElement("template");
|
|
18384
|
+
body.innerHTML = parsed.body.innerHTML;
|
|
18385
|
+
fragment.appendChild(body.content);
|
|
18386
|
+
return fragment;
|
|
18387
|
+
}
|
|
17956
18388
|
async function createViewer(container, source, options = {}) {
|
|
17957
18389
|
const el = resolveContainer2(container);
|
|
17958
18390
|
const { wasmBasePath: wasmBasePath2, ...conversion } = options;
|
|
@@ -17965,18 +18397,7 @@ ${parsed.documentElement.outerHTML}`;
|
|
|
17965
18397
|
renderFootnotesAndEndnotes: true,
|
|
17966
18398
|
...opts
|
|
17967
18399
|
});
|
|
17968
|
-
|
|
17969
|
-
const fragment = document.createDocumentFragment();
|
|
17970
|
-
parsed.querySelectorAll("style").forEach((sourceStyle) => {
|
|
17971
|
-
const style = document.createElement("style");
|
|
17972
|
-
style.textContent = sourceStyle.textContent;
|
|
17973
|
-
scopeStyleElement(style, mount2.selector);
|
|
17974
|
-
fragment.appendChild(style);
|
|
17975
|
-
});
|
|
17976
|
-
const body = document.createElement("template");
|
|
17977
|
-
body.innerHTML = parsed.body.innerHTML;
|
|
17978
|
-
fragment.appendChild(body.content);
|
|
17979
|
-
mount2.root.replaceChildren(fragment);
|
|
18400
|
+
mount2.root.replaceChildren(scopedDocumentFragment(mount2, lastHtml));
|
|
17980
18401
|
};
|
|
17981
18402
|
await render(source, conversion);
|
|
17982
18403
|
return {
|
|
@@ -17990,6 +18411,111 @@ ${parsed.documentElement.outerHTML}`;
|
|
|
17990
18411
|
}
|
|
17991
18412
|
};
|
|
17992
18413
|
}
|
|
18414
|
+
async function createHistoryViewer(container, history, documentId, options = {}) {
|
|
18415
|
+
const el = resolveContainer2(container);
|
|
18416
|
+
const { wasmBasePath: wasmBasePath2, maxEntriesToScan = 1e4, ...conversion } = options;
|
|
18417
|
+
if (!Number.isSafeInteger(maxEntriesToScan) || maxEntriesToScan <= 0 || maxEntriesToScan > 2147483647)
|
|
18418
|
+
throw new RangeError("Invalid history scan budget.");
|
|
18419
|
+
await ensureWasm(wasmBasePath2);
|
|
18420
|
+
const mount2 = createScopedMount(el);
|
|
18421
|
+
let accepted = null;
|
|
18422
|
+
let acceptedBytes = null;
|
|
18423
|
+
let displayedBytes = null;
|
|
18424
|
+
let shownSequence = "0";
|
|
18425
|
+
let following = true;
|
|
18426
|
+
let closed = false;
|
|
18427
|
+
let html = "";
|
|
18428
|
+
let queue = Promise.resolve();
|
|
18429
|
+
const clone = (value) => JSON.parse(JSON.stringify(value));
|
|
18430
|
+
const assertOpen = () => {
|
|
18431
|
+
if (closed) throw new DocxHistoryError("Closed", "History viewer is destroyed.");
|
|
18432
|
+
};
|
|
18433
|
+
const enqueue = (action) => {
|
|
18434
|
+
const result = queue.then(() => {
|
|
18435
|
+
assertOpen();
|
|
18436
|
+
return action();
|
|
18437
|
+
});
|
|
18438
|
+
queue = result.then(() => void 0, () => void 0);
|
|
18439
|
+
return result;
|
|
18440
|
+
};
|
|
18441
|
+
const sameHead = (a, b) => a.revision === b.revision && a.state.length === b.state.length && a.state.digest.algorithm === b.state.digest.algorithm && a.state.digest.value === b.state.digest.value;
|
|
18442
|
+
const paint = async (bytes, sequence) => {
|
|
18443
|
+
const nextHtml = await convertDocxToHtml(bytes, { renderFootnotesAndEndnotes: true, ...conversion });
|
|
18444
|
+
assertOpen();
|
|
18445
|
+
const fragment = scopedDocumentFragment(mount2, nextHtml);
|
|
18446
|
+
mount2.root.replaceChildren(fragment);
|
|
18447
|
+
html = nextHtml;
|
|
18448
|
+
displayedBytes = bytes;
|
|
18449
|
+
shownSequence = sequence;
|
|
18450
|
+
};
|
|
18451
|
+
const refresh = async (resume = false) => {
|
|
18452
|
+
const update = await history.readChangesSince(documentId, accepted?.view.head ?? null, maxEntriesToScan);
|
|
18453
|
+
assertOpen();
|
|
18454
|
+
const duplicate = accepted !== null && sameHead(accepted.view.head, update.view.head);
|
|
18455
|
+
const bytes = duplicate ? acceptedBytes : await history.exportVersion(documentId, update.view.version.id);
|
|
18456
|
+
assertOpen();
|
|
18457
|
+
const wantLive = following || resume;
|
|
18458
|
+
const changedContent = accepted === null || accepted.view.state.snapshot.contentDigest.value !== update.view.state.snapshot.contentDigest.value || accepted.view.state.epoch !== update.view.state.epoch;
|
|
18459
|
+
if (wantLive && (changedContent || !following)) await paint(bytes, update.view.state.sequence);
|
|
18460
|
+
else if (wantLive) {
|
|
18461
|
+
displayedBytes = bytes;
|
|
18462
|
+
shownSequence = update.view.state.sequence;
|
|
18463
|
+
}
|
|
18464
|
+
assertOpen();
|
|
18465
|
+
accepted = update;
|
|
18466
|
+
acceptedBytes = bytes;
|
|
18467
|
+
if (wantLive) following = true;
|
|
18468
|
+
return clone(update);
|
|
18469
|
+
};
|
|
18470
|
+
const showSequence = async (sequence) => {
|
|
18471
|
+
const bytes = await history.materialize(documentId, sequence, maxEntriesToScan);
|
|
18472
|
+
assertOpen();
|
|
18473
|
+
await paint(bytes, sequence);
|
|
18474
|
+
following = false;
|
|
18475
|
+
};
|
|
18476
|
+
const viewer = {
|
|
18477
|
+
element: el,
|
|
18478
|
+
get html() {
|
|
18479
|
+
return html;
|
|
18480
|
+
},
|
|
18481
|
+
get head() {
|
|
18482
|
+
assertOpen();
|
|
18483
|
+
return clone(accepted.view.head);
|
|
18484
|
+
},
|
|
18485
|
+
get sequence() {
|
|
18486
|
+
return shownSequence;
|
|
18487
|
+
},
|
|
18488
|
+
get following() {
|
|
18489
|
+
return following;
|
|
18490
|
+
},
|
|
18491
|
+
refresh: () => enqueue(() => refresh()),
|
|
18492
|
+
showSequence: (sequence) => enqueue(() => showSequence(sequence)),
|
|
18493
|
+
showTime: (cutoff) => enqueue(async () => {
|
|
18494
|
+
const sequence = await history.resolveSequenceAtTime(documentId, cutoff, maxEntriesToScan);
|
|
18495
|
+
assertOpen();
|
|
18496
|
+
await showSequence(sequence);
|
|
18497
|
+
}),
|
|
18498
|
+
resume: () => enqueue(() => refresh(true)),
|
|
18499
|
+
exportDisplayed: () => {
|
|
18500
|
+
assertOpen();
|
|
18501
|
+
return displayedBytes.slice();
|
|
18502
|
+
},
|
|
18503
|
+
destroy: () => {
|
|
18504
|
+
if (closed) return;
|
|
18505
|
+
closed = true;
|
|
18506
|
+
acceptedBytes = null;
|
|
18507
|
+
displayedBytes = null;
|
|
18508
|
+
mount2.root.remove();
|
|
18509
|
+
}
|
|
18510
|
+
};
|
|
18511
|
+
try {
|
|
18512
|
+
await viewer.refresh();
|
|
18513
|
+
return viewer;
|
|
18514
|
+
} catch (error) {
|
|
18515
|
+
viewer.destroy();
|
|
18516
|
+
throw error;
|
|
18517
|
+
}
|
|
18518
|
+
}
|
|
17993
18519
|
async function createEditor(container, source, options = {}) {
|
|
17994
18520
|
const el = resolveContainer2(container);
|
|
17995
18521
|
const { wasmBasePath: wasmBasePath2, ...editorOptions } = options;
|