@pixerate/editor 0.25.0 → 0.26.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.
Files changed (112) hide show
  1. package/README.md +30 -0
  2. package/THIRD_PARTY_NOTICES.md +11 -0
  3. package/dist/agent/index.cjs +174 -11
  4. package/dist/agent/index.cjs.map +1 -1
  5. package/dist/agent/index.d.cts +18 -4
  6. package/dist/agent/index.d.ts +18 -4
  7. package/dist/agent/index.js +167 -4
  8. package/dist/agent/index.js.map +1 -1
  9. package/dist/{arrangeShortcuts-B8O3f8-P.d.cts → arrange-BICutYPk.d.cts} +243 -29
  10. package/dist/{arrangeShortcuts-sLlD2rSX.d.ts → arrange-BICutYPk.d.ts} +243 -29
  11. package/dist/builtins-CdPcLwwF.d.cts +176 -0
  12. package/dist/builtins-Chj6zv_O.d.ts +176 -0
  13. package/dist/canvas/index.cjs +3917 -19
  14. package/dist/canvas/index.cjs.map +1 -1
  15. package/dist/canvas/index.d.cts +1232 -7
  16. package/dist/canvas/index.d.ts +1232 -7
  17. package/dist/canvas/index.js +3916 -18
  18. package/dist/canvas/index.js.map +1 -1
  19. package/dist/chunk-3CC7OPUQ.cjs +831 -0
  20. package/dist/chunk-3CC7OPUQ.cjs.map +1 -0
  21. package/dist/{chunk-AL6JMGWW.cjs → chunk-CS6NZWWA.cjs} +24 -24
  22. package/dist/{chunk-AL6JMGWW.cjs.map → chunk-CS6NZWWA.cjs.map} +1 -1
  23. package/dist/{chunk-C7P22VIP.cjs → chunk-EWCOFSCQ.cjs} +17 -6
  24. package/dist/chunk-EWCOFSCQ.cjs.map +1 -0
  25. package/dist/chunk-HDYMLS3X.cjs +409 -0
  26. package/dist/chunk-HDYMLS3X.cjs.map +1 -0
  27. package/dist/{chunk-X4N4WUKB.cjs → chunk-IFHKKVXH.cjs} +150 -38
  28. package/dist/chunk-IFHKKVXH.cjs.map +1 -0
  29. package/dist/{chunk-ECCCYKGL.js → chunk-JOIREKL7.js} +63 -39
  30. package/dist/chunk-JOIREKL7.js.map +1 -0
  31. package/dist/chunk-KRGXUQ2Y.js +1696 -0
  32. package/dist/chunk-KRGXUQ2Y.js.map +1 -0
  33. package/dist/{chunk-QGYDWPRM.js → chunk-MHEPLBFB.js} +2 -2
  34. package/dist/chunk-MR2764X2.js +831 -0
  35. package/dist/chunk-MR2764X2.js.map +1 -0
  36. package/dist/{chunk-7WISYZFO.cjs → chunk-NFVBT3UZ.cjs} +67 -43
  37. package/dist/chunk-NFVBT3UZ.cjs.map +1 -0
  38. package/dist/{chunk-WSQKHXW7.cjs → chunk-NQF32I6Z.cjs} +1209 -457
  39. package/dist/chunk-NQF32I6Z.cjs.map +1 -0
  40. package/dist/{chunk-IXHSYOVY.js → chunk-Q2OHWD2A.js} +638 -866
  41. package/dist/chunk-Q2OHWD2A.js.map +1 -0
  42. package/dist/{chunk-NAXVVS5I.js → chunk-QJSOZFCZ.js} +2 -2
  43. package/dist/{chunk-V3CHHWOE.cjs → chunk-RRREAOVM.cjs} +4 -4
  44. package/dist/{chunk-V3CHHWOE.cjs.map → chunk-RRREAOVM.cjs.map} +1 -1
  45. package/dist/{chunk-UAKDG62Z.js → chunk-SWE3NPOU.js} +116 -4
  46. package/dist/chunk-SWE3NPOU.js.map +1 -0
  47. package/dist/chunk-TXXNBDQX.cjs +1696 -0
  48. package/dist/chunk-TXXNBDQX.cjs.map +1 -0
  49. package/dist/chunk-UBCFIABU.js +409 -0
  50. package/dist/chunk-UBCFIABU.js.map +1 -0
  51. package/dist/{chunk-RR7F5GUM.js → chunk-X7HXL2SB.js} +941 -189
  52. package/dist/chunk-X7HXL2SB.js.map +1 -0
  53. package/dist/{chunk-PO6A6QQH.cjs → chunk-YOMYSFJU.cjs} +742 -970
  54. package/dist/chunk-YOMYSFJU.cjs.map +1 -0
  55. package/dist/{chunk-FH6F67DN.js → chunk-ZRNHGAQT.js} +17 -6
  56. package/dist/{chunk-FH6F67DN.js.map → chunk-ZRNHGAQT.js.map} +1 -1
  57. package/dist/commands/index.cjs +352 -4
  58. package/dist/commands/index.cjs.map +1 -1
  59. package/dist/commands/index.d.cts +37 -108
  60. package/dist/commands/index.d.ts +37 -108
  61. package/dist/commands/index.js +351 -3
  62. package/dist/commands/index.js.map +1 -1
  63. package/dist/compare/index.cjs +3 -3
  64. package/dist/compare/index.js +2 -2
  65. package/dist/{controller-BIJiNoVs.d.cts → controller-CxXGOEmy.d.cts} +209 -13
  66. package/dist/{controller-BIJiNoVs.d.ts → controller-CxXGOEmy.d.ts} +209 -13
  67. package/dist/export/index.cjs +36 -0
  68. package/dist/export/index.cjs.map +1 -0
  69. package/dist/export/index.d.cts +243 -0
  70. package/dist/export/index.d.ts +243 -0
  71. package/dist/export/index.js +36 -0
  72. package/dist/export/index.js.map +1 -0
  73. package/dist/extensions/index.cjs +54 -6
  74. package/dist/extensions/index.cjs.map +1 -1
  75. package/dist/extensions/index.d.cts +7 -2
  76. package/dist/extensions/index.d.ts +7 -2
  77. package/dist/extensions/index.js +59 -11
  78. package/dist/{index-7a6bHTLy.d.ts → index-B273Zllk.d.ts} +211 -207
  79. package/dist/{index-Csd5qI8u.d.cts → index-jcJwmBYt.d.cts} +211 -207
  80. package/dist/index.cjs +144 -32
  81. package/dist/index.cjs.map +1 -1
  82. package/dist/index.d.cts +49 -13
  83. package/dist/index.d.ts +49 -13
  84. package/dist/index.js +136 -24
  85. package/dist/index.js.map +1 -1
  86. package/dist/registry-CED5C0lA.d.cts +204 -0
  87. package/dist/registry-CED5C0lA.d.ts +204 -0
  88. package/dist/{registry-D32LpIc4.d.cts → registry-D0GADKc7.d.cts} +1 -1
  89. package/dist/{registry-evKTP4ls.d.ts → registry-DhVUZioj.d.ts} +1 -1
  90. package/dist/serializers/index.cjs +4 -4
  91. package/dist/serializers/index.d.cts +2 -1
  92. package/dist/serializers/index.d.ts +2 -1
  93. package/dist/serializers/index.js +3 -3
  94. package/dist/spreadsheet/index.cjs +33 -3
  95. package/dist/spreadsheet/index.cjs.map +1 -1
  96. package/dist/spreadsheet/index.d.cts +106 -3
  97. package/dist/spreadsheet/index.d.ts +106 -3
  98. package/dist/spreadsheet/index.js +32 -2
  99. package/package.json +10 -1
  100. package/dist/chunk-7WISYZFO.cjs.map +0 -1
  101. package/dist/chunk-C7P22VIP.cjs.map +0 -1
  102. package/dist/chunk-ECCCYKGL.js.map +0 -1
  103. package/dist/chunk-IXHSYOVY.js.map +0 -1
  104. package/dist/chunk-PO6A6QQH.cjs.map +0 -1
  105. package/dist/chunk-RR7F5GUM.js.map +0 -1
  106. package/dist/chunk-UAKDG62Z.js.map +0 -1
  107. package/dist/chunk-WSQKHXW7.cjs.map +0 -1
  108. package/dist/chunk-X4N4WUKB.cjs.map +0 -1
  109. package/dist/types-DX4Mh4SL.d.cts +0 -217
  110. package/dist/types-DX4Mh4SL.d.ts +0 -217
  111. /package/dist/{chunk-QGYDWPRM.js.map → chunk-MHEPLBFB.js.map} +0 -0
  112. /package/dist/{chunk-NAXVVS5I.js.map → chunk-QJSOZFCZ.js.map} +0 -0
@@ -1,6 +1,13 @@
1
1
  import {
2
2
  compareDocuments
3
- } from "./chunk-NAXVVS5I.js";
3
+ } from "./chunk-QJSOZFCZ.js";
4
+ import {
5
+ DEFAULT_STYLES,
6
+ NORMAL_STYLE_ID,
7
+ createStyleRegistry,
8
+ isValidStyleId,
9
+ readZip
10
+ } from "./chunk-MR2764X2.js";
4
11
  import {
5
12
  INSTRUCTION_REGEX,
6
13
  TEMPLATE_REGEX,
@@ -16,20 +23,29 @@ import {
16
23
  rtfToPlainText,
17
24
  sanitizeImportedHtml,
18
25
  serializeRichClipboardPayload
19
- } from "./chunk-QGYDWPRM.js";
26
+ } from "./chunk-MHEPLBFB.js";
27
+ import {
28
+ MAX_TABLE_COLUMNS,
29
+ MAX_TABLE_ROWS,
30
+ TABLE_ACTIONS,
31
+ createTableExtensions,
32
+ getTableActionState,
33
+ getTableState,
34
+ runTableAction
35
+ } from "./chunk-UBCFIABU.js";
20
36
  import {
21
37
  escapeHtml,
22
38
  htmlToPlainText,
23
39
  markdownToTipTapHtml,
24
40
  plainTextToSlice
25
- } from "./chunk-ECCCYKGL.js";
41
+ } from "./chunk-JOIREKL7.js";
26
42
  import {
27
43
  CommentStore,
28
44
  TRACK_CHANGES_SKIP_META,
29
45
  TrackChanges,
30
46
  createReviewId,
31
47
  trackChangesPluginKey
32
- } from "./chunk-UAKDG62Z.js";
48
+ } from "./chunk-SWE3NPOU.js";
33
49
 
34
50
  // src/extensions/gradient-text.ts
35
51
  import { Mark } from "@tiptap/core";
@@ -2310,319 +2326,6 @@ var ImportAdapterRegistry = class {
2310
2326
  }
2311
2327
  };
2312
2328
 
2313
- // src/import/zip.ts
2314
- var ZipError = class extends Error {
2315
- code;
2316
- entryName;
2317
- constructor(code, message, details = {}) {
2318
- super(message, details.cause !== void 0 ? { cause: details.cause } : void 0);
2319
- this.name = "ZipError";
2320
- this.code = code;
2321
- this.entryName = details.entryName;
2322
- }
2323
- };
2324
- var DEFAULT_ZIP_MAX_ENTRIES = 1e4;
2325
- var DEFAULT_ZIP_MAX_TOTAL_SIZE = 256 * 1024 * 1024;
2326
- var SIG_EOCD = 101010256;
2327
- var SIG_ZIP64_EOCD = 101075792;
2328
- var SIG_ZIP64_LOCATOR = 117853008;
2329
- var SIG_CENTRAL = 33639248;
2330
- var SIG_LOCAL = 67324752;
2331
- var EOCD_SIZE = 22;
2332
- var MAX_COMMENT = 65535;
2333
- var crcTable = null;
2334
- function zipCrc32(data) {
2335
- if (!crcTable) {
2336
- crcTable = new Uint32Array(256);
2337
- for (let n = 0; n < 256; n++) {
2338
- let c = n;
2339
- for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
2340
- crcTable[n] = c >>> 0;
2341
- }
2342
- }
2343
- let crc = 4294967295;
2344
- for (let i = 0; i < data.length; i++) crc = crcTable[(crc ^ data[i]) & 255] ^ crc >>> 8;
2345
- return (crc ^ 4294967295) >>> 0;
2346
- }
2347
- function toBytes(data) {
2348
- if (ArrayBuffer.isView(data)) return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
2349
- return new Uint8Array(data);
2350
- }
2351
- function isUnsafeZipPath(name) {
2352
- if (!name || name.includes("\0")) return true;
2353
- const normalized = name.replace(/\\/g, "/");
2354
- if (normalized.startsWith("/") || /^[a-zA-Z]:/.test(normalized)) return true;
2355
- return normalized.split("/").some((segment) => segment === "..");
2356
- }
2357
- var utf8 = new TextDecoder("utf-8");
2358
- var CP437_HIGH = "\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xEF\xEE\xEC\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xF2\xFB\xF9\xFF\xD6\xDC\xA2\xA3\xA5\u20A7\u0192\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\u2310\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0";
2359
- function decodeName(bytes, isUtf8) {
2360
- if (isUtf8 || bytes.every((b) => b < 128)) return utf8.decode(bytes);
2361
- let out = "";
2362
- for (const b of bytes) out += b < 128 ? String.fromCharCode(b) : CP437_HIGH[b - 128];
2363
- return out;
2364
- }
2365
- var ZipArchive = class {
2366
- constructor(bytes, entries, maxTotalSize) {
2367
- this.bytes = bytes;
2368
- this.maxTotalSize = maxTotalSize;
2369
- this.entries = entries;
2370
- entries.forEach((entry) => {
2371
- if (!this.byName.has(entry.name)) this.byName.set(entry.name, entry);
2372
- });
2373
- }
2374
- bytes;
2375
- maxTotalSize;
2376
- /** Entries in central-directory order. */
2377
- entries;
2378
- byName = /* @__PURE__ */ new Map();
2379
- extracted = 0;
2380
- /** Looks up an entry by exact name (a leading `/` is ignored). */
2381
- get(name) {
2382
- return this.byName.get(name.replace(/^\/+/, ""));
2383
- }
2384
- has(name) {
2385
- return !!this.get(name);
2386
- }
2387
- /** Uncompressed bytes extracted so far (counted against `maxTotalSize`). */
2388
- get extractedSize() {
2389
- return this.extracted;
2390
- }
2391
- /** Extracts an entry's data, verifying its size and CRC-32. */
2392
- async read(entryOrName) {
2393
- const entry = typeof entryOrName === "string" ? this.get(entryOrName) : entryOrName;
2394
- if (!entry) {
2395
- throw new ZipError("entry_not_found", `ZIP entry "${String(entryOrName)}" not found.`, {
2396
- entryName: String(entryOrName)
2397
- });
2398
- }
2399
- const name = entry.name;
2400
- if (entry.encrypted) {
2401
- throw new ZipError("encrypted", `ZIP entry "${name}" is encrypted; encrypted archives are not supported.`, {
2402
- entryName: name
2403
- });
2404
- }
2405
- if (entry.isDirectory) return new Uint8Array(0);
2406
- const remaining = this.maxTotalSize - this.extracted;
2407
- if (entry.size > remaining) {
2408
- throw new ZipError(
2409
- "too_large",
2410
- `Extracting "${name}" (${entry.size} bytes) would exceed the ${this.maxTotalSize}-byte limit.`,
2411
- { entryName: name }
2412
- );
2413
- }
2414
- const { bytes } = this;
2415
- const offset = entry.localHeaderOffset;
2416
- if (offset + 30 > bytes.length || readU32(bytes, offset) !== SIG_LOCAL) {
2417
- throw new ZipError("corrupt_entry", `ZIP entry "${name}" has no valid local header.`, { entryName: name });
2418
- }
2419
- const dataStart = offset + 30 + readU16(bytes, offset + 26) + readU16(bytes, offset + 28);
2420
- const dataEnd = dataStart + entry.compressedSize;
2421
- if (dataEnd > bytes.length) {
2422
- throw new ZipError("corrupt_entry", `ZIP entry "${name}" is truncated.`, { entryName: name });
2423
- }
2424
- const raw = bytes.subarray(dataStart, dataEnd);
2425
- let data;
2426
- if (entry.method === 0) {
2427
- if (entry.compressedSize !== entry.size) {
2428
- throw new ZipError("corrupt_entry", `Stored ZIP entry "${name}" has inconsistent sizes.`, { entryName: name });
2429
- }
2430
- data = raw.slice();
2431
- } else if (entry.method === 8) {
2432
- data = await inflateRaw(raw, entry.size, name);
2433
- } else {
2434
- throw new ZipError(
2435
- "unsupported_feature",
2436
- `ZIP entry "${name}" uses unsupported compression method ${entry.method}; only stored and deflate are supported.`,
2437
- { entryName: name }
2438
- );
2439
- }
2440
- if (data.length !== entry.size) {
2441
- throw new ZipError("corrupt_entry", `ZIP entry "${name}" size does not match the directory.`, { entryName: name });
2442
- }
2443
- this.extracted += data.length;
2444
- if (zipCrc32(data) !== entry.crc32) {
2445
- throw new ZipError("corrupt_entry", `ZIP entry "${name}" failed its CRC-32 check.`, { entryName: name });
2446
- }
2447
- return data;
2448
- }
2449
- /** Extracts an entry and decodes it as UTF-8 (a leading BOM is removed). */
2450
- async readText(entryOrName) {
2451
- const text = utf8.decode(await this.read(entryOrName));
2452
- return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
2453
- }
2454
- };
2455
- function readU16(bytes, offset) {
2456
- return bytes[offset] | bytes[offset + 1] << 8;
2457
- }
2458
- function readU32(bytes, offset) {
2459
- return (bytes[offset] | bytes[offset + 1] << 8 | bytes[offset + 2] << 16 | bytes[offset + 3] << 24) >>> 0;
2460
- }
2461
- function readU64(bytes, offset) {
2462
- const value = readU32(bytes, offset) + readU32(bytes, offset + 4) * 4294967296;
2463
- if (!Number.isSafeInteger(value)) {
2464
- throw new ZipError("unsupported_feature", "ZIP64 value exceeds the supported range.");
2465
- }
2466
- return value;
2467
- }
2468
- async function inflateRaw(raw, limit, entryName) {
2469
- const Stream = globalThis.DecompressionStream;
2470
- if (typeof Stream !== "function") {
2471
- throw new ZipError(
2472
- "unsupported_environment",
2473
- "Inflating ZIP entries needs DecompressionStream (modern browsers, Node 18+).",
2474
- { entryName }
2475
- );
2476
- }
2477
- const stream = new Stream("deflate-raw");
2478
- const writer = stream.writable.getWriter();
2479
- const reader = stream.readable.getReader();
2480
- const CHUNK = 64 * 1024;
2481
- const feeding = (async () => {
2482
- for (let i = 0; i < raw.length; i += CHUNK) await writer.write(raw.subarray(i, i + CHUNK));
2483
- await writer.close();
2484
- })();
2485
- feeding.catch(() => {
2486
- });
2487
- const chunks = [];
2488
- let total = 0;
2489
- try {
2490
- for (; ; ) {
2491
- const { done, value } = await reader.read();
2492
- if (done) break;
2493
- total += value.length;
2494
- if (total > limit) {
2495
- throw new ZipError(
2496
- "corrupt_entry",
2497
- `ZIP entry "${entryName}" inflates beyond its declared size of ${limit} bytes.`,
2498
- { entryName }
2499
- );
2500
- }
2501
- chunks.push(value);
2502
- }
2503
- } catch (error) {
2504
- reader.cancel().catch(() => {
2505
- });
2506
- writer.abort().catch(() => {
2507
- });
2508
- if (error instanceof ZipError) throw error;
2509
- throw new ZipError("corrupt_entry", `ZIP entry "${entryName}" has an invalid deflate stream.`, {
2510
- entryName,
2511
- cause: error
2512
- });
2513
- }
2514
- await feeding.catch(() => {
2515
- });
2516
- const out = new Uint8Array(total);
2517
- let pos = 0;
2518
- for (const chunk of chunks) {
2519
- out.set(chunk, pos);
2520
- pos += chunk.length;
2521
- }
2522
- return out;
2523
- }
2524
- function findEndOfCentralDirectory(bytes) {
2525
- const min = Math.max(0, bytes.length - EOCD_SIZE - MAX_COMMENT);
2526
- for (let i = bytes.length - EOCD_SIZE; i >= min; i--) {
2527
- if (readU32(bytes, i) === SIG_EOCD && i + EOCD_SIZE + readU16(bytes, i + 20) <= bytes.length) return i;
2528
- }
2529
- return -1;
2530
- }
2531
- async function readZip(data, options = {}) {
2532
- const bytes = toBytes(data);
2533
- const maxEntries = options.maxEntries ?? DEFAULT_ZIP_MAX_ENTRIES;
2534
- const maxTotalSize = options.maxTotalSize ?? DEFAULT_ZIP_MAX_TOTAL_SIZE;
2535
- const eocd = findEndOfCentralDirectory(bytes);
2536
- if (eocd < 0) throw new ZipError("invalid_archive", "Not a ZIP archive (no end of central directory record).");
2537
- let diskNumber = readU16(bytes, eocd + 4);
2538
- let cdDisk = readU16(bytes, eocd + 6);
2539
- let count = readU16(bytes, eocd + 10);
2540
- let cdSize = readU32(bytes, eocd + 12);
2541
- let cdOffset = readU32(bytes, eocd + 16);
2542
- if (count === 65535 || cdSize === 4294967295 || cdOffset === 4294967295) {
2543
- const locator = eocd - 20;
2544
- if (locator < 0 || readU32(bytes, locator) !== SIG_ZIP64_LOCATOR) {
2545
- throw new ZipError("invalid_archive", "ZIP64 archive is missing its end of central directory locator.");
2546
- }
2547
- const record = readU64(bytes, locator + 8);
2548
- if (record + 56 > bytes.length || readU32(bytes, record) !== SIG_ZIP64_EOCD) {
2549
- throw new ZipError("invalid_archive", "ZIP64 end of central directory record not found.");
2550
- }
2551
- diskNumber = readU32(bytes, record + 16);
2552
- cdDisk = readU32(bytes, record + 20);
2553
- count = readU64(bytes, record + 32);
2554
- cdSize = readU64(bytes, record + 40);
2555
- cdOffset = readU64(bytes, record + 48);
2556
- }
2557
- if (diskNumber !== 0 || cdDisk !== 0) {
2558
- throw new ZipError("unsupported_feature", "Multi-disk (spanned) ZIP archives are not supported.");
2559
- }
2560
- if (count > maxEntries) {
2561
- throw new ZipError("too_many_entries", `ZIP archive has ${count} entries; the limit is ${maxEntries}.`);
2562
- }
2563
- if (cdOffset + cdSize > bytes.length) {
2564
- throw new ZipError("invalid_archive", "ZIP central directory lies outside the file.");
2565
- }
2566
- const entries = [];
2567
- let p = cdOffset;
2568
- for (let i = 0; i < count; i++) {
2569
- if (p + 46 > bytes.length || readU32(bytes, p) !== SIG_CENTRAL) {
2570
- throw new ZipError("invalid_archive", "ZIP central directory is corrupt.");
2571
- }
2572
- const flags = readU16(bytes, p + 8);
2573
- const method = readU16(bytes, p + 10);
2574
- const crc32 = readU32(bytes, p + 16);
2575
- let compressedSize = readU32(bytes, p + 20);
2576
- let size = readU32(bytes, p + 24);
2577
- const nameLength = readU16(bytes, p + 28);
2578
- const extraLength = readU16(bytes, p + 30);
2579
- const commentLength = readU16(bytes, p + 32);
2580
- let localHeaderOffset = readU32(bytes, p + 42);
2581
- const nameStart = p + 46;
2582
- const extraStart = nameStart + nameLength;
2583
- const next = extraStart + extraLength + commentLength;
2584
- if (next > bytes.length) throw new ZipError("invalid_archive", "ZIP central directory is truncated.");
2585
- for (let e = extraStart; e + 4 <= extraStart + extraLength; ) {
2586
- const id = readU16(bytes, e);
2587
- const len = readU16(bytes, e + 2);
2588
- if (id === 1) {
2589
- let f = e + 4;
2590
- const end = f + len;
2591
- if (size === 4294967295 && f + 8 <= end) {
2592
- size = readU64(bytes, f);
2593
- f += 8;
2594
- }
2595
- if (compressedSize === 4294967295 && f + 8 <= end) {
2596
- compressedSize = readU64(bytes, f);
2597
- f += 8;
2598
- }
2599
- if (localHeaderOffset === 4294967295 && f + 8 <= end) localHeaderOffset = readU64(bytes, f);
2600
- }
2601
- e += 4 + len;
2602
- }
2603
- const rawName = decodeName(bytes.subarray(nameStart, extraStart), (flags & 2048) !== 0);
2604
- const name = rawName.replace(/\\/g, "/");
2605
- p = next;
2606
- if (isUnsafeZipPath(rawName)) {
2607
- if (options.unsafePaths === "skip") continue;
2608
- throw new ZipError("unsafe_path", `ZIP entry name "${rawName}" is absolute or escapes the archive root.`, {
2609
- entryName: rawName
2610
- });
2611
- }
2612
- entries.push({
2613
- name,
2614
- isDirectory: name.endsWith("/"),
2615
- encrypted: (flags & 1) !== 0,
2616
- method,
2617
- compressedSize,
2618
- size,
2619
- crc32,
2620
- localHeaderOffset
2621
- });
2622
- }
2623
- return new ZipArchive(bytes, entries, maxTotalSize);
2624
- }
2625
-
2626
2329
  // src/import/docx.ts
2627
2330
  var DOCX_MIME_TYPES = [
2628
2331
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
@@ -2630,6 +2333,8 @@ var DOCX_MIME_TYPES = [
2630
2333
  "application/vnd.ms-word.document.macroenabled.12"
2631
2334
  ];
2632
2335
  var DEFAULT_DOCX_MAX_IMAGE_SIZE = 10 * 1024 * 1024;
2336
+ var MAX_COMMENTS = 5e3;
2337
+ var MAX_PARENT_CHAIN = 64;
2633
2338
  var W_NAMESPACES = /* @__PURE__ */ new Set([
2634
2339
  "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
2635
2340
  "http://purl.oclc.org/ooxml/wordprocessingml/main"
@@ -2642,6 +2347,7 @@ var IMAGE_MIME_TYPES = {
2642
2347
  gif: "image/gif",
2643
2348
  webp: "image/webp"
2644
2349
  };
2350
+ var TASK_GLYPHS = { "\u2610": false, "\u2612": true, "\u2611": true };
2645
2351
  function isW(node, localName) {
2646
2352
  if (!node || node.nodeType !== 1) return false;
2647
2353
  const el = node;
@@ -2778,6 +2484,65 @@ function readNumPr(pPr) {
2778
2484
  if (numId === void 0) return void 0;
2779
2485
  return { numId, ilvl: Number(attr(wChild(numPr, "ilvl"), "val") ?? 0) || 0 };
2780
2486
  }
2487
+ function twipsToCss(value) {
2488
+ const n = Number(value);
2489
+ return value !== void 0 && value !== "" && Number.isFinite(n) ? `${Math.round(n / 20 * 100) / 100}pt` : void 0;
2490
+ }
2491
+ var ALIGNMENTS = {
2492
+ left: "left",
2493
+ start: "left",
2494
+ center: "center",
2495
+ right: "right",
2496
+ end: "right",
2497
+ both: "justify",
2498
+ distribute: "justify"
2499
+ };
2500
+ function readParagraphFormat(pPr) {
2501
+ const format = {};
2502
+ if (!pPr) return format;
2503
+ const jc = attr(wChild(pPr, "jc"), "val");
2504
+ if (jc && ALIGNMENTS[jc]) format.textAlign = ALIGNMENTS[jc];
2505
+ const spacing = wChild(pPr, "spacing");
2506
+ const before = twipsToCss(attr(spacing, "before"));
2507
+ const after = twipsToCss(attr(spacing, "after"));
2508
+ if (before) format.spaceBefore = before;
2509
+ if (after) format.spaceAfter = after;
2510
+ const line = Number(attr(spacing, "line"));
2511
+ if (Number.isFinite(line) && line > 0) {
2512
+ const rule = attr(spacing, "lineRule") ?? "auto";
2513
+ format.lineHeight = rule === "auto" ? String(Math.round(line / 240 * 100) / 100) : twipsToCss(String(line));
2514
+ }
2515
+ const ind = wChild(pPr, "ind");
2516
+ const left = twipsToCss(attr(ind, "left") ?? attr(ind, "start"));
2517
+ const right = twipsToCss(attr(ind, "right") ?? attr(ind, "end"));
2518
+ const first = attr(ind, "firstLine");
2519
+ const hanging = attr(ind, "hanging");
2520
+ if (left) format.indentLeft = left;
2521
+ if (right) format.indentRight = right;
2522
+ if (first !== void 0) format.indentFirstLine = twipsToCss(first);
2523
+ else if (hanging !== void 0 && Number.isFinite(Number(hanging))) format.indentFirstLine = twipsToCss(String(-Number(hanging)));
2524
+ return format;
2525
+ }
2526
+ function readCharacterFormat(rPr) {
2527
+ const format = {};
2528
+ if (!rPr) return format;
2529
+ const f = readFormat(rPr);
2530
+ if (f.b !== void 0) format.bold = f.b;
2531
+ if (f.i !== void 0) format.italic = f.i;
2532
+ if (f.u !== void 0) format.underline = f.u;
2533
+ if (f.s !== void 0) format.strike = f.s;
2534
+ const fonts = wChild(rPr, "rFonts");
2535
+ const font = attr(fonts, "ascii") ?? attr(fonts, "hAnsi");
2536
+ if (font && /^[\w .-]{1,64}$/.test(font)) format.fontFamily = font;
2537
+ const size = Number(attr(wChild(rPr, "sz"), "val"));
2538
+ if (Number.isFinite(size) && size > 0 && size < 3300) format.fontSize = `${size / 2}pt`;
2539
+ const color = attr(wChild(rPr, "color"), "val");
2540
+ if (color && /^[0-9a-f]{6}$/i.test(color)) format.color = `#${color.toLowerCase()}`;
2541
+ if (toggle(wChild(rPr, "caps"))) format.textTransform = "uppercase";
2542
+ const smallCaps = toggle(wChild(rPr, "smallCaps"));
2543
+ if (smallCaps !== void 0) format.smallCaps = smallCaps;
2544
+ return format;
2545
+ }
2781
2546
  function readStyles(doc) {
2782
2547
  const styles = /* @__PURE__ */ new Map();
2783
2548
  if (!doc) return styles;
@@ -2785,17 +2550,85 @@ function readStyles(doc) {
2785
2550
  const id = attr(style, "styleId");
2786
2551
  if (!id) return;
2787
2552
  const pPr = wChild(style, "pPr");
2553
+ const rPr = wChild(style, "rPr");
2788
2554
  const outline = attr(wChild(pPr, "outlineLvl"), "val");
2555
+ const priority = Number(attr(wChild(style, "uiPriority"), "val"));
2789
2556
  styles.set(id, {
2790
2557
  name: attr(wChild(style, "name"), "val") ?? id,
2558
+ type: attr(style, "type") ?? "paragraph",
2791
2559
  basedOn: attr(wChild(style, "basedOn"), "val"),
2560
+ next: attr(wChild(style, "next"), "val"),
2561
+ link: attr(wChild(style, "link"), "val"),
2562
+ priority: Number.isFinite(priority) && attr(wChild(style, "uiPriority"), "val") !== void 0 ? priority : void 0,
2563
+ quick: toggle(wChild(style, "qFormat")),
2564
+ hidden: toggle(wChild(style, "semiHidden")) || toggle(wChild(style, "hidden")),
2792
2565
  outlineLevel: outline !== void 0 ? Number(outline) : void 0,
2793
2566
  numPr: readNumPr(pPr),
2794
- format: readFormat(wChild(style, "rPr"))
2567
+ format: readFormat(rPr),
2568
+ paragraph: readParagraphFormat(pPr),
2569
+ character: readCharacterFormat(rPr)
2795
2570
  });
2796
2571
  });
2797
2572
  return styles;
2798
2573
  }
2574
+ function displayStyleName(name) {
2575
+ const heading = /^heading ([1-9])$/.exec(name);
2576
+ return heading ? `Heading ${heading[1]}` : name;
2577
+ }
2578
+ function mapStyleIds(styles) {
2579
+ const ids = /* @__PURE__ */ new Map();
2580
+ const used = /* @__PURE__ */ new Set();
2581
+ styles.forEach((style, fileId) => {
2582
+ if (style.type !== "paragraph" && style.type !== "character") return;
2583
+ let id = fileId.replace(/[^A-Za-z0-9_-]/g, "").slice(0, 60);
2584
+ if (!/^[A-Za-z]/.test(id)) id = `S${id}`;
2585
+ let unique = id;
2586
+ for (let n = 2; used.has(unique); n++) unique = `${id}${n}`;
2587
+ if (!isValidStyleId(unique)) return;
2588
+ used.add(unique);
2589
+ ids.set(fileId, unique);
2590
+ });
2591
+ return ids;
2592
+ }
2593
+ var styleKind = (style) => style?.type === "character" ? "character" : "paragraph";
2594
+ function styleDefinitions(styles, ids) {
2595
+ const out = [];
2596
+ styles.forEach((style, fileId) => {
2597
+ const id = ids.get(fileId);
2598
+ if (!id) return;
2599
+ const kind = styleKind(style);
2600
+ const sameKind = (other) => other && ids.has(other) && styleKind(styles.get(other)) === kind ? ids.get(other) : void 0;
2601
+ const definition = { id, name: displayStyleName(style.name || fileId), kind };
2602
+ const basedOn = sameKind(style.basedOn);
2603
+ if (basedOn) definition.basedOn = basedOn;
2604
+ if (kind === "paragraph") {
2605
+ const next = sameKind(style.next);
2606
+ if (next) definition.next = next;
2607
+ }
2608
+ const linked = style.link ? ids.get(style.link) : void 0;
2609
+ if (linked) definition.linked = linked;
2610
+ if (kind === "paragraph" && Object.keys(style.paragraph).length) definition.paragraph = style.paragraph;
2611
+ if (Object.keys(style.character).length) definition.character = style.character;
2612
+ if (kind === "paragraph") {
2613
+ const heading = /^heading([1-6])$/i.exec(fileId) ?? /^heading ([1-6])$/i.exec(style.name);
2614
+ if (heading) definition.block = { type: "heading", attrs: { level: Number(heading[1]) } };
2615
+ else if (style.outlineLevel !== void 0 && style.outlineLevel >= 0 && style.outlineLevel <= 5) {
2616
+ definition.block = { type: "heading", attrs: { level: style.outlineLevel + 1 } };
2617
+ } else if (id === "Code") definition.block = { type: "codeBlock" };
2618
+ else if (id === NORMAL_STYLE_ID) definition.block = { type: "paragraph" };
2619
+ }
2620
+ if (style.priority !== void 0) definition.priority = style.priority;
2621
+ if (style.quick) definition.quick = true;
2622
+ if (style.hidden) definition.hidden = true;
2623
+ const builtin = DEFAULT_STYLES.find((s) => s.id === id && s.kind === kind);
2624
+ if (builtin) {
2625
+ definition.builtin = true;
2626
+ definition.block ??= builtin.block;
2627
+ }
2628
+ out.push(definition);
2629
+ });
2630
+ return out;
2631
+ }
2799
2632
  function styleChain(styles, id) {
2800
2633
  const chain = [];
2801
2634
  const seen = /* @__PURE__ */ new Set();
@@ -2821,6 +2654,16 @@ function styleHeadingLevel(styles, id) {
2821
2654
  if (id && /^title$/i.test(id)) return 1;
2822
2655
  return void 0;
2823
2656
  }
2657
+ function specialStyle(styles, id) {
2658
+ if (!id) return void 0;
2659
+ for (const style of styleChain(styles, id)) {
2660
+ if (/^block ?text$/i.test(style.name)) return "quote";
2661
+ if (/^code$/i.test(style.name)) return "code";
2662
+ }
2663
+ if (/^blocktext$/i.test(id)) return "quote";
2664
+ if (id === "Code") return "code";
2665
+ return void 0;
2666
+ }
2824
2667
  function readNumbering(doc) {
2825
2668
  const numbering = { abstracts: /* @__PURE__ */ new Map(), nums: /* @__PURE__ */ new Map() };
2826
2669
  if (!doc) return numbering;
@@ -2850,7 +2693,184 @@ function readNumbering(doc) {
2850
2693
  });
2851
2694
  return numbering;
2852
2695
  }
2853
- var formatKey = (f) => `${f.b ? 1 : 0}${f.i ? 1 : 0}${f.u ? 1 : 0}${f.s ? 1 : 0}${f.sup ? 1 : 0}${f.sub ? 1 : 0}|${f.href ?? ""}`;
2696
+ function readPeople(doc) {
2697
+ const people = /* @__PURE__ */ new Map();
2698
+ if (!doc) return people;
2699
+ descendants(doc, "person").forEach((person) => {
2700
+ const author = attr(person, "author");
2701
+ const userId = attr(descendants(person, "presenceInfo")[0], "userId");
2702
+ if (author && userId && !people.has(author)) people.set(author, userId);
2703
+ });
2704
+ return people;
2705
+ }
2706
+ function dateOf(value) {
2707
+ const ms = value ? Date.parse(value) : NaN;
2708
+ return Number.isFinite(ms) ? ms : 0;
2709
+ }
2710
+ function authorOf(ctx, name) {
2711
+ const authorName = (name ?? "").trim() || "Unknown";
2712
+ return { id: ctx.people.get(authorName) ?? authorName, name: authorName };
2713
+ }
2714
+ function commentBody(comment) {
2715
+ const lines = [];
2716
+ const walk = (el, line) => {
2717
+ for (const child of childElements(el)) {
2718
+ if (!isW(child)) continue;
2719
+ switch (child.localName) {
2720
+ case "t":
2721
+ line.push(child.textContent ?? "");
2722
+ break;
2723
+ case "tab":
2724
+ line.push(" ");
2725
+ break;
2726
+ case "br":
2727
+ case "cr":
2728
+ line.push("\n");
2729
+ break;
2730
+ case "delText":
2731
+ case "del":
2732
+ case "moveFrom":
2733
+ break;
2734
+ default:
2735
+ walk(child, line);
2736
+ }
2737
+ }
2738
+ };
2739
+ for (const block of childElements(comment)) {
2740
+ if (!isW(block)) continue;
2741
+ const paragraphs = block.localName === "p" ? [block] : descendants(block, "p").filter((p) => isW(p));
2742
+ for (const p of paragraphs) {
2743
+ const line = [];
2744
+ walk(p, line);
2745
+ lines.push(line.join(""));
2746
+ }
2747
+ }
2748
+ return lines.join("\n").trim();
2749
+ }
2750
+ function readComments(commentsXml, extendedXml, people) {
2751
+ const byFileId = /* @__PURE__ */ new Map();
2752
+ if (!commentsXml) return { threads: [], byFileId };
2753
+ const entries = /* @__PURE__ */ new Map();
2754
+ const byParaId = /* @__PURE__ */ new Map();
2755
+ for (const comment of descendants(commentsXml, "comment").filter((el) => isW(el))) {
2756
+ if (entries.size >= MAX_COMMENTS) break;
2757
+ const fileId = attr(comment, "id");
2758
+ if (fileId === void 0 || entries.has(fileId)) continue;
2759
+ entries.set(fileId, {
2760
+ fileId,
2761
+ author: authorOf({ people }, attr(comment, "author")),
2762
+ createdAt: dateOf(attr(comment, "date")),
2763
+ body: commentBody(comment),
2764
+ done: false
2765
+ });
2766
+ const paragraphs = descendants(comment, "p");
2767
+ const paraId = attr(paragraphs[paragraphs.length - 1], "paraId");
2768
+ if (paraId) byParaId.set(paraId, fileId);
2769
+ }
2770
+ if (extendedXml) {
2771
+ for (const ex of descendants(extendedXml, "commentEx")) {
2772
+ const fileId = byParaId.get(attr(ex, "paraId") ?? "");
2773
+ const entry = fileId !== void 0 ? entries.get(fileId) : void 0;
2774
+ if (!entry) continue;
2775
+ const parent = byParaId.get(attr(ex, "paraIdParent") ?? "");
2776
+ if (parent !== void 0 && parent !== fileId) entry.parent = parent;
2777
+ const done = attr(ex, "done");
2778
+ entry.done = done === "1" || done === "true";
2779
+ }
2780
+ }
2781
+ const rootOf = (entry) => {
2782
+ let current = entry;
2783
+ for (let i = 0; i < MAX_PARENT_CHAIN && current.parent !== void 0; i++) {
2784
+ const parent = entries.get(current.parent);
2785
+ if (!parent || parent === entry) break;
2786
+ current = parent;
2787
+ }
2788
+ return current;
2789
+ };
2790
+ const threads = /* @__PURE__ */ new Map();
2791
+ for (const entry of entries.values()) {
2792
+ if (rootOf(entry) !== entry) continue;
2793
+ const id = createReviewId("comment");
2794
+ byFileId.set(entry.fileId, id);
2795
+ threads.set(entry.fileId, {
2796
+ id,
2797
+ anchor: null,
2798
+ author: entry.author,
2799
+ createdAt: entry.createdAt,
2800
+ body: entry.body,
2801
+ replies: [],
2802
+ resolved: entry.done,
2803
+ orphaned: false
2804
+ });
2805
+ }
2806
+ for (const entry of entries.values()) {
2807
+ const root = rootOf(entry);
2808
+ if (root === entry) continue;
2809
+ const thread = threads.get(root.fileId);
2810
+ if (!thread) continue;
2811
+ byFileId.set(entry.fileId, thread.id);
2812
+ const reply = {
2813
+ id: createReviewId("reply"),
2814
+ author: entry.author,
2815
+ body: entry.body,
2816
+ createdAt: entry.createdAt
2817
+ };
2818
+ thread.replies.push(reply);
2819
+ }
2820
+ return { threads: Array.from(threads.values()), byFileId };
2821
+ }
2822
+ function applyMarker(el, ctx) {
2823
+ switch (el.localName) {
2824
+ case "commentRangeStart": {
2825
+ const thread = ctx.commentThreads.get(attr(el, "id") ?? "");
2826
+ if (thread) ctx.openComments.set(thread, (ctx.openComments.get(thread) ?? 0) + 1);
2827
+ return true;
2828
+ }
2829
+ case "commentRangeEnd": {
2830
+ const thread = ctx.commentThreads.get(attr(el, "id") ?? "");
2831
+ const open = thread ? ctx.openComments.get(thread) : void 0;
2832
+ if (thread && open !== void 0) {
2833
+ if (open <= 1) ctx.openComments.delete(thread);
2834
+ else ctx.openComments.set(thread, open - 1);
2835
+ }
2836
+ return true;
2837
+ }
2838
+ case "moveFromRangeStart":
2839
+ case "moveToRangeStart": {
2840
+ const id = attr(el, "id");
2841
+ const name = attr(el, "name") ?? `range-${id}`;
2842
+ let moveId = ctx.moveIds.get(name);
2843
+ if (!moveId) {
2844
+ moveId = createReviewId("move");
2845
+ ctx.moveIds.set(name, moveId);
2846
+ }
2847
+ if (id !== void 0) {
2848
+ ctx.moveRanges.delete(id);
2849
+ ctx.moveRanges.set(id, { moveId, to: el.localName === "moveToRangeStart" });
2850
+ }
2851
+ return true;
2852
+ }
2853
+ case "moveFromRangeEnd":
2854
+ case "moveToRangeEnd":
2855
+ ctx.moveRanges.delete(attr(el, "id") ?? "");
2856
+ return true;
2857
+ default:
2858
+ return false;
2859
+ }
2860
+ }
2861
+ var FORMAT_MARK_TYPES = [
2862
+ ["b", "bold"],
2863
+ ["i", "italic"],
2864
+ ["u", "underline"],
2865
+ ["s", "strike"],
2866
+ ["sub", "subscript"],
2867
+ ["sup", "superscript"]
2868
+ ];
2869
+ var formatMarks = (f) => FORMAT_MARK_TYPES.filter(([key]) => f[key]).map(([, type]) => ({ type }));
2870
+ var formatKey = (f) => `${f.b ? 1 : 0}${f.i ? 1 : 0}${f.u ? 1 : 0}${f.s ? 1 : 0}${f.sup ? 1 : 0}${f.sub ? 1 : 0}|${f.href ?? ""}` + (f.rev ? `|${f.rev.kind}:${f.rev.changeId}:${f.rev.moveId ?? ""}` : "") + (f.fmt ? `|f:${f.fmt.changeId}` : "") + (f.comments ? `|c:${f.comments.join(",")}` : "");
2871
+ function changeAttrs(change) {
2872
+ return ` data-change-id="${escapeHtml(change.changeId)}" data-author-id="${escapeHtml(change.authorId)}" data-author-name="${escapeHtml(change.authorName)}" data-created-at="${change.createdAt}"`;
2873
+ }
2854
2874
  function wrapFormat(inner, f) {
2855
2875
  let html = inner;
2856
2876
  if (f.sup) html = `<sup>${html}</sup>`;
@@ -2859,10 +2879,19 @@ function wrapFormat(inner, f) {
2859
2879
  if (f.u) html = `<u>${html}</u>`;
2860
2880
  if (f.i) html = `<em>${html}</em>`;
2861
2881
  if (f.b) html = `<strong>${html}</strong>`;
2882
+ if (f.fmt) {
2883
+ html = `<span data-change-type="format"${changeAttrs(f.fmt)} data-format-before="${escapeHtml(JSON.stringify(f.fmt.before))}" data-format-after="${escapeHtml(JSON.stringify(f.fmt.after))}">${html}</span>`;
2884
+ }
2885
+ if (f.rev) {
2886
+ const tag = f.rev.kind === "insertion" ? "ins" : "del";
2887
+ const move = f.rev.moveId ? ` data-move-id="${escapeHtml(f.rev.moveId)}"` : "";
2888
+ html = `<${tag} data-change-type="${f.rev.kind}"${changeAttrs(f.rev)}${move}>${html}</${tag}>`;
2889
+ }
2890
+ for (const id of [...f.comments ?? []].reverse()) html = `<span data-comment-id="${escapeHtml(id)}">${html}</span>`;
2862
2891
  if (f.href) html = `<a href="${escapeHtml(f.href)}">${html}</a>`;
2863
2892
  return html;
2864
2893
  }
2865
- function renderSegments(segments) {
2894
+ function renderSegments(segments, code = false) {
2866
2895
  let html = "";
2867
2896
  let i = 0;
2868
2897
  while (i < segments.length) {
@@ -2872,11 +2901,12 @@ function renderSegments(segments) {
2872
2901
  for (; j < segments.length && formatKey(segments[j].format) === key; j++) {
2873
2902
  const seg = segments[j];
2874
2903
  if (seg.kind === "text") inner += escapeHtml(seg.text);
2875
- else if (seg.kind === "br") inner += "<br>";
2876
- else if (seg.kind === "img") inner += `<img src="${escapeHtml(seg.src)}" alt="${escapeHtml(seg.alt)}">`;
2904
+ else if (seg.kind === "br") inner += code ? "\n" : "<br>";
2905
+ else if (seg.kind === "img") inner += code ? "" : `<img src="${escapeHtml(seg.src)}" alt="${escapeHtml(seg.alt)}">`;
2877
2906
  else inner += `<sup>${seg.n}</sup>`;
2878
2907
  }
2879
- html += wrapFormat(inner, segments[i].format);
2908
+ const format = segments[i].format;
2909
+ html += wrapFormat(inner, code ? { rev: format.rev, comments: format.comments } : format);
2880
2910
  i = j;
2881
2911
  }
2882
2912
  return html;
@@ -2901,9 +2931,33 @@ var ParagraphRenderer = class {
2901
2931
  segments = [];
2902
2932
  /** Open complex fields (`w:fldChar`), innermost last. */
2903
2933
  fields = [];
2904
- render(p) {
2934
+ collect(p) {
2905
2935
  this.walk(p, {});
2906
- return renderSegments(this.segments);
2936
+ return this;
2937
+ }
2938
+ render(p) {
2939
+ return this.collect(p).html();
2940
+ }
2941
+ html(code = false) {
2942
+ return renderSegments(this.segments, code);
2943
+ }
2944
+ /** Removes a leading task-list glyph (`☐ ` / `☒ `) and returns its checked state. */
2945
+ takeTaskGlyph() {
2946
+ const first = this.segments[0];
2947
+ if (!first || first.kind !== "text") return void 0;
2948
+ const glyph = first.text.charAt(0);
2949
+ if (!(glyph in TASK_GLYPHS)) return void 0;
2950
+ const rest = first.text.slice(1).replace(/^ /, "");
2951
+ if (rest) first.text = rest;
2952
+ else {
2953
+ this.segments.shift();
2954
+ const next = this.segments[0];
2955
+ if (next?.kind === "text") {
2956
+ next.text = next.text.replace(/^ /, "");
2957
+ if (!next.text) this.segments.shift();
2958
+ }
2959
+ }
2960
+ return TASK_GLYPHS[glyph];
2907
2961
  }
2908
2962
  fieldHref() {
2909
2963
  for (let i = this.fields.length - 1; i >= 0; i--) {
@@ -2914,8 +2968,38 @@ var ParagraphRenderer = class {
2914
2968
  inFieldInstruction() {
2915
2969
  return this.fields.some((field) => !field.separated);
2916
2970
  }
2971
+ /** A tracked change id shared with the previous change when adjacent and by the same author at the same time. */
2972
+ changeId(key) {
2973
+ const last = this.ctx.lastChange;
2974
+ if (last && last.key === key && last.end === this.ctx.segmentCount) return last.changeId;
2975
+ return createReviewId("change");
2976
+ }
2977
+ change(el, kind, extra = "") {
2978
+ const author = authorOf(this.ctx, attr(el, "author"));
2979
+ const createdAt = dateOf(attr(el, "date"));
2980
+ const key = `${kind}|${author.name}|${createdAt}|${extra}`;
2981
+ return {
2982
+ key,
2983
+ info: { changeId: this.changeId(key), authorId: author.id, authorName: author.name, createdAt }
2984
+ };
2985
+ }
2986
+ revision(el, kind) {
2987
+ let moveId;
2988
+ if (el.localName === "moveFrom" || el.localName === "moveTo") {
2989
+ const to = el.localName === "moveTo";
2990
+ for (const range of this.ctx.moveRanges.values()) if (range.to === to) moveId = range.moveId;
2991
+ moveId ??= createReviewId("move");
2992
+ }
2993
+ const { info, key } = this.change(el, kind, moveId ?? "");
2994
+ return { rev: { ...info, kind, ...moveId ? { moveId } : {} }, key };
2995
+ }
2996
+ walkChange(el, base, kind) {
2997
+ const { rev, key } = this.revision(el, kind);
2998
+ this.walk(el, { ...base, rev });
2999
+ this.ctx.lastChange = { key, changeId: rev.changeId, end: this.ctx.segmentCount };
3000
+ }
2917
3001
  walk(el, base) {
2918
- const accepted = this.ctx.tracked === "accepted";
3002
+ const tracked = this.ctx.tracked;
2919
3003
  for (const child of childElements(el)) {
2920
3004
  if (!isW(child)) {
2921
3005
  continue;
@@ -2939,11 +3023,13 @@ var ParagraphRenderer = class {
2939
3023
  }
2940
3024
  case "ins":
2941
3025
  case "moveTo":
2942
- if (accepted) this.walk(child, base);
3026
+ if (tracked === "accepted") this.walk(child, base);
3027
+ else if (tracked === "marks") this.walkChange(child, base, "insertion");
2943
3028
  break;
2944
3029
  case "del":
2945
3030
  case "moveFrom":
2946
- if (!accepted) this.walk(child, base);
3031
+ if (tracked === "original") this.walk(child, base);
3032
+ else if (tracked === "marks") this.walkChange(child, base, "deletion");
2947
3033
  break;
2948
3034
  case "sdt":
2949
3035
  this.walk(wChild(child, "sdtContent") ?? child, base);
@@ -2956,24 +3042,42 @@ var ParagraphRenderer = class {
2956
3042
  this.walk(child, base);
2957
3043
  break;
2958
3044
  default:
3045
+ applyMarker(child, this.ctx);
2959
3046
  break;
2960
3047
  }
2961
3048
  }
2962
3049
  }
2963
3050
  run(r, base) {
2964
3051
  const rPr = wChild(r, "rPr");
2965
- const styleFormat = this.ctx.styles.get(attr(wChild(rPr, "rStyle"), "val") ?? "")?.format ?? {};
3052
+ const runStyle = this.ctx.styles.get(attr(wChild(rPr, "rStyle"), "val") ?? "");
3053
+ const styleFormat = base.href && runStyle && /^hyperlink$/i.test(runStyle.name) ? {} : runStyle?.format ?? {};
2966
3054
  const own = readFormat(rPr);
2967
3055
  const format = { ...base, ...styleFormat, ...own };
3056
+ const rPrChange = wChild(rPr, "rPrChange");
3057
+ let changeKey;
3058
+ if (rPrChange && this.ctx.tracked === "marks") {
3059
+ const { info, key } = this.change(rPrChange, "format");
3060
+ const before = { ...styleFormat, ...readFormat(wChild(rPrChange, "rPr")) };
3061
+ format.fmt = { ...info, before: formatMarks(before), after: formatMarks(format) };
3062
+ changeKey = key;
3063
+ }
2968
3064
  for (const child of childElements(r)) {
2969
3065
  this.runChild(child, format);
2970
3066
  }
3067
+ if (format.fmt && changeKey) {
3068
+ this.ctx.lastChange = { key: changeKey, changeId: format.fmt.changeId, end: this.ctx.segmentCount };
3069
+ }
2971
3070
  }
2972
3071
  runChild(child, format) {
2973
3072
  const push = (segment) => {
2974
3073
  if (this.inFieldInstruction()) return;
2975
3074
  const href = format.href ?? this.fieldHref();
2976
- this.segments.push(href && !format.href ? { ...segment, format: { ...format, href } } : segment);
3075
+ let out = href && !format.href ? { ...segment, format: { ...format, href } } : segment;
3076
+ if (this.ctx.openComments.size) {
3077
+ out = { ...out, format: { ...out.format, comments: Array.from(this.ctx.openComments.keys()) } };
3078
+ }
3079
+ this.segments.push(out);
3080
+ this.ctx.segmentCount++;
2977
3081
  };
2978
3082
  if (child.localName === "AlternateContent") {
2979
3083
  const branch = childElements(child).find(
@@ -2988,7 +3092,7 @@ var ParagraphRenderer = class {
2988
3092
  push({ kind: "text", text: child.textContent ?? "", format });
2989
3093
  break;
2990
3094
  case "delText":
2991
- if (this.ctx.tracked === "original") push({ kind: "text", text: child.textContent ?? "", format });
3095
+ if (this.ctx.tracked !== "accepted") push({ kind: "text", text: child.textContent ?? "", format });
2992
3096
  break;
2993
3097
  case "tab":
2994
3098
  case "ptab":
@@ -3052,8 +3156,16 @@ var ParagraphRenderer = class {
3052
3156
  push({ kind: "img", src, alt, format });
3053
3157
  }
3054
3158
  };
3159
+ var MARKERS = /* @__PURE__ */ new Set([
3160
+ "commentRangeStart",
3161
+ "commentRangeEnd",
3162
+ "moveFromRangeStart",
3163
+ "moveFromRangeEnd",
3164
+ "moveToRangeStart",
3165
+ "moveToRangeEnd"
3166
+ ]);
3055
3167
  function flattenBlocks(container, ctx, out = []) {
3056
- const accepted = ctx.tracked === "accepted";
3168
+ const tracked = ctx.tracked;
3057
3169
  for (const child of childElements(container)) {
3058
3170
  if (!isW(child)) continue;
3059
3171
  switch (child.localName) {
@@ -3070,13 +3182,14 @@ function flattenBlocks(container, ctx, out = []) {
3070
3182
  break;
3071
3183
  case "ins":
3072
3184
  case "moveTo":
3073
- if (accepted) flattenBlocks(child, ctx, out);
3185
+ if (tracked !== "original") flattenBlocks(child, ctx, out);
3074
3186
  break;
3075
3187
  case "del":
3076
3188
  case "moveFrom":
3077
- if (!accepted) flattenBlocks(child, ctx, out);
3189
+ if (tracked !== "accepted") flattenBlocks(child, ctx, out);
3078
3190
  break;
3079
3191
  default:
3192
+ if (MARKERS.has(child.localName)) out.push(child);
3080
3193
  break;
3081
3194
  }
3082
3195
  }
@@ -3105,9 +3218,30 @@ function paragraphInfo(p, ctx) {
3105
3218
  start: num?.starts.get(numPr.ilvl) ?? def?.start ?? 1
3106
3219
  };
3107
3220
  }
3108
- return { heading, list };
3221
+ const special = heading === void 0 && !list ? specialStyle(ctx.styles, styleId) : void 0;
3222
+ return { heading, list, styleId, special, pPr };
3223
+ }
3224
+ function blockChangeAttr(pPr, ctx) {
3225
+ const change = wChild(pPr, "pPrChange");
3226
+ if (!change) return "";
3227
+ const oldPPr = wChild(change, "pPr");
3228
+ const oldStyle = attr(wChild(oldPPr, "pStyle"), "val");
3229
+ const oldOutline = attr(wChild(oldPPr, "outlineLvl"), "val");
3230
+ const level = oldOutline !== void 0 ? Number(oldOutline) >= 0 && Number(oldOutline) <= 5 ? Number(oldOutline) + 1 : void 0 : styleHeadingLevel(ctx.styles, oldStyle);
3231
+ const mappedStyle = oldStyle ? ctx.styleIds.get(oldStyle) : void 0;
3232
+ const previous = level !== void 0 ? { type: "heading", attrs: { level }, wrappers: [] } : specialStyle(ctx.styles, oldStyle) === "code" ? { type: "codeBlock", attrs: {}, wrappers: [] } : { type: "paragraph", attrs: mappedStyle && mappedStyle !== NORMAL_STYLE_ID ? { styleId: mappedStyle } : {}, wrappers: [] };
3233
+ const author = authorOf(ctx, attr(change, "author"));
3234
+ const record = {
3235
+ changeId: createReviewId("change"),
3236
+ authorId: author.id,
3237
+ authorName: author.name,
3238
+ authorColor: null,
3239
+ createdAt: dateOf(attr(change, "date")),
3240
+ previous
3241
+ };
3242
+ return ` data-block-change="${escapeHtml(JSON.stringify(record))}"`;
3109
3243
  }
3110
- function renderBlocks(container, ctx, part) {
3244
+ function renderBlocks(container, ctx, part, topLevel = false) {
3111
3245
  let html = "";
3112
3246
  const stack = [];
3113
3247
  const closeList = () => {
@@ -3117,34 +3251,62 @@ function renderBlocks(container, ctx, part) {
3117
3251
  const closeAll = () => {
3118
3252
  while (stack.length) closeList();
3119
3253
  };
3254
+ let group = null;
3255
+ const flush = () => {
3256
+ if (!group) return;
3257
+ html += group.kind === "quote" ? `<blockquote>${group.parts.join("")}</blockquote>` : `<pre><code>${group.parts.join("\n")}</code></pre>`;
3258
+ group = null;
3259
+ };
3120
3260
  for (const block of flattenBlocks(container, ctx)) {
3261
+ if (MARKERS.has(block.localName)) {
3262
+ applyMarker(block, ctx);
3263
+ continue;
3264
+ }
3121
3265
  if (block.localName === "tbl") {
3122
3266
  closeAll();
3267
+ flush();
3123
3268
  html += renderTable(block, ctx, part);
3124
3269
  continue;
3125
3270
  }
3126
- const { heading, list } = paragraphInfo(block, ctx);
3127
- const inner = new ParagraphRenderer(ctx, part).render(block);
3128
- const blockHtml = heading ? `<h${heading}>${inner}</h${heading}>` : `<p>${inner}</p>`;
3271
+ const { heading, list, styleId, special, pPr } = paragraphInfo(block, ctx);
3272
+ const renderer = new ParagraphRenderer(ctx, part).collect(block);
3273
+ if (special) {
3274
+ closeAll();
3275
+ if (group && group.kind !== special) flush();
3276
+ group ??= { kind: special, parts: [] };
3277
+ group.parts.push(special === "code" ? renderer.html(true) : `<p>${renderer.html()}</p>`);
3278
+ continue;
3279
+ }
3280
+ flush();
3281
+ const task = list && !list.ordered ? renderer.takeTaskGlyph() : void 0;
3282
+ const inner = renderer.html();
3283
+ const mappedStyle = styleId ? ctx.styleIds.get(styleId) : void 0;
3284
+ const styleAttr = !heading && !list && mappedStyle && mappedStyle !== NORMAL_STYLE_ID ? ` data-style="${escapeHtml(mappedStyle)}"` : "";
3285
+ const changeAttr = topLevel && !list && ctx.tracked === "marks" ? blockChangeAttr(pPr, ctx) : "";
3286
+ const blockHtml = heading ? `<h${heading}${changeAttr}>${inner}</h${heading}>` : `<p${styleAttr}${changeAttr}>${inner}</p>`;
3129
3287
  if (!list) {
3130
3288
  closeAll();
3131
3289
  html += blockHtml;
3132
3290
  continue;
3133
3291
  }
3134
3292
  const tag = list.ordered ? "ol" : "ul";
3293
+ const isTask = task !== void 0;
3294
+ const item = isTask ? `<li data-type="taskItem" data-checked="${task ? "true" : "false"}">` : "<li>";
3135
3295
  while (stack.length && stack[stack.length - 1].level > list.level) closeList();
3136
3296
  const top = stack[stack.length - 1];
3137
- if (top && top.level === list.level && top.tag === tag) {
3138
- html += "</li><li>";
3297
+ if (top && top.level === list.level && top.tag === tag && top.task === isTask) {
3298
+ html += `</li>${item}`;
3139
3299
  } else {
3140
3300
  if (top && top.level === list.level) closeList();
3141
3301
  const start2 = tag === "ol" && list.start !== 1 ? ` start="${list.start}"` : "";
3142
- html += `<${tag}${start2}><li>`;
3143
- stack.push({ level: list.level, tag });
3302
+ const type = isTask ? ' data-type="taskList"' : "";
3303
+ html += `<${tag}${type}${start2}>${item}`;
3304
+ stack.push({ level: list.level, tag, task: isTask });
3144
3305
  }
3145
3306
  html += blockHtml;
3146
3307
  }
3147
3308
  closeAll();
3309
+ flush();
3148
3310
  return html;
3149
3311
  }
3150
3312
  function renderTable(tbl, ctx, part) {
@@ -3238,12 +3400,38 @@ async function docxToDocumentImport(data, options = {}) {
3238
3400
  const baseDir = dirname(mainPath);
3239
3401
  const stylesPath = partByType("styles") ?? `${baseDir}styles.xml`;
3240
3402
  const numberingPath = partByType("numbering") ?? `${baseDir}numbering.xml`;
3403
+ const styles = readStyles(await readXmlPart(zip, Parser, stylesPath));
3404
+ const useStyles = options.paragraphStyles !== false;
3405
+ const styleIds = useStyles ? mapStyleIds(styles) : /* @__PURE__ */ new Map();
3406
+ const peoplePath = partByType("people");
3407
+ const people = readPeople(peoplePath ? await readXmlPart(zip, Parser, peoplePath) : null);
3408
+ let threads = [];
3409
+ let commentThreads = /* @__PURE__ */ new Map();
3410
+ if (options.comments !== false) {
3411
+ const commentsPath = partByType("comments");
3412
+ const extendedPath = partByType("commentsExtended");
3413
+ const read = readComments(
3414
+ commentsPath ? await readXmlPart(zip, Parser, commentsPath) : null,
3415
+ extendedPath ? await readXmlPart(zip, Parser, extendedPath) : null,
3416
+ people
3417
+ );
3418
+ threads = read.threads;
3419
+ commentThreads = read.byFileId;
3420
+ }
3241
3421
  const ctx = {
3242
- styles: readStyles(await readXmlPart(zip, Parser, stylesPath)),
3422
+ styles,
3243
3423
  numbering: readNumbering(await readXmlPart(zip, Parser, numberingPath)),
3244
3424
  notes: /* @__PURE__ */ new Map(),
3245
3425
  noteOrder: [],
3246
- tracked: options.trackedChanges ?? "accepted"
3426
+ tracked: options.trackedChanges ?? "accepted",
3427
+ styleIds,
3428
+ people,
3429
+ commentThreads,
3430
+ openComments: /* @__PURE__ */ new Map(),
3431
+ moveRanges: /* @__PURE__ */ new Map(),
3432
+ moveIds: /* @__PURE__ */ new Map(),
3433
+ segmentCount: 0,
3434
+ lastChange: null
3247
3435
  };
3248
3436
  for (const kind of ["footnote", "endnote"]) {
3249
3437
  const path = partByType(`${kind}s`) ?? `${baseDir}${kind}s.xml`;
@@ -3258,7 +3446,7 @@ async function docxToDocumentImport(data, options = {}) {
3258
3446
  });
3259
3447
  }
3260
3448
  const part = { rels, images: await loadImages(zip, documentXml, rels, options) };
3261
- let html = renderBlocks(body, ctx, part);
3449
+ let html = renderBlocks(body, ctx, part, true);
3262
3450
  if (ctx.noteOrder.length) {
3263
3451
  let notesHtml = "";
3264
3452
  for (let i = 0; i < ctx.noteOrder.length; i++) {
@@ -3268,7 +3456,13 @@ async function docxToDocumentImport(data, options = {}) {
3268
3456
  html += `<hr><ol>${notesHtml}</ol>`;
3269
3457
  }
3270
3458
  const sanitized = sanitizeImportedHtml(html);
3271
- return { html: sanitized, text: htmlToPlainText(sanitized) };
3459
+ const result = { html: sanitized, text: htmlToPlainText(sanitized) };
3460
+ if (threads.length) result.comments = { version: 1, threads };
3461
+ if (useStyles) {
3462
+ const definitions = styleDefinitions(styles, styleIds);
3463
+ if (definitions.length) result.styles = createStyleRegistry({ styles: definitions });
3464
+ }
3465
+ return result;
3272
3466
  }
3273
3467
  function createDocxImportAdapter(options = {}) {
3274
3468
  return {
@@ -3385,6 +3579,30 @@ function insertImportedDocument(target, content, options = {}) {
3385
3579
  view.dispatch(tr.scrollIntoView().setMeta("uiEvent", "drop"));
3386
3580
  return end;
3387
3581
  }
3582
+ function applyImportedReviewData(editor, content) {
3583
+ const storage = editor?.storage ?? {};
3584
+ let comments = 0;
3585
+ let styles = 0;
3586
+ const store = storage.comment?.store;
3587
+ const threads = content.comments?.threads ?? [];
3588
+ if (store && threads.length) {
3589
+ const existing = store.toJSON().threads;
3590
+ const fresh = threads.filter((thread) => !existing.some((t) => t.id === thread.id));
3591
+ if (fresh.length) {
3592
+ store.load({ version: 1, threads: [...existing, ...fresh] });
3593
+ comments = fresh.length;
3594
+ }
3595
+ }
3596
+ const registry = storage.paragraphStyles?.registry;
3597
+ if (registry && content.styles) {
3598
+ for (const style of content.styles.list()) {
3599
+ if (registry.get(style.id)) continue;
3600
+ registry.upsert(style);
3601
+ styles++;
3602
+ }
3603
+ }
3604
+ return { comments, styles };
3605
+ }
3388
3606
  async function importFilesIntoEditor(target, files, options = {}) {
3389
3607
  const registry = options.registry ?? defaultImportRegistry;
3390
3608
  const outcomes = [];
@@ -3398,6 +3616,9 @@ async function importFilesIntoEditor(target, files, options = {}) {
3398
3616
  const view = toView(target);
3399
3617
  if (view.isDestroyed) break;
3400
3618
  at = insertImportedDocument(view, outcome.result, { at, asPlainText: options.asPlainText });
3619
+ if (!options.asPlainText && "storage" in target) {
3620
+ applyImportedReviewData(target, outcome.result);
3621
+ }
3401
3622
  outcomes.push(outcome);
3402
3623
  options.onImport?.(outcome, file);
3403
3624
  } catch (error) {
@@ -6520,510 +6741,6 @@ function safeClipboard(get) {
6520
6741
  // src/extensions/paragraph-styles/extension.ts
6521
6742
  import { Extension as Extension13, Mark as Mark5, mergeAttributes as mergeAttributes7 } from "@tiptap/core";
6522
6743
  import { Plugin as Plugin15, PluginKey as PluginKey12 } from "@tiptap/pm/state";
6523
-
6524
- // src/extensions/paragraph-styles/registry.ts
6525
- var MAX_CHAIN = 32;
6526
- var STYLE_ID = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
6527
- var UNSAFE_CSS = /[;{}<>\\]|\/\*|url\s*\(|expression\s*\(|@import|javascript:/i;
6528
- var NORMAL_STYLE_ID = "Normal";
6529
- var PREVIEW_PROPERTIES = /* @__PURE__ */ new Set([
6530
- "font-family",
6531
- "font-weight",
6532
- "font-style",
6533
- "color",
6534
- "text-decoration-line",
6535
- "text-transform",
6536
- "letter-spacing",
6537
- "font-variant-caps"
6538
- ]);
6539
- function isValidStyleId(id) {
6540
- return typeof id === "string" && STYLE_ID.test(id);
6541
- }
6542
- var MUTED = "var(--pixerate-style-muted, #6b7280)";
6543
- var HEADING_SIZES = ["2em", "1.5em", "1.25em", "1.1em", "1em", "0.9em"];
6544
- function builtinStyles() {
6545
- const styles = [
6546
- {
6547
- id: NORMAL_STYLE_ID,
6548
- name: "Normal",
6549
- kind: "paragraph",
6550
- block: { type: "paragraph" },
6551
- priority: 0,
6552
- quick: true,
6553
- builtin: true
6554
- },
6555
- {
6556
- id: "Title",
6557
- name: "Title",
6558
- kind: "paragraph",
6559
- basedOn: NORMAL_STYLE_ID,
6560
- next: NORMAL_STYLE_ID,
6561
- linked: "TitleChar",
6562
- paragraph: { spaceAfter: "0.25em", lineHeight: "1.15" },
6563
- character: { fontSize: "2.5em", bold: true, letterSpacing: "-0.01em" },
6564
- priority: 10,
6565
- quick: true,
6566
- builtin: true
6567
- },
6568
- {
6569
- id: "Subtitle",
6570
- name: "Subtitle",
6571
- kind: "paragraph",
6572
- basedOn: NORMAL_STYLE_ID,
6573
- next: NORMAL_STYLE_ID,
6574
- linked: "SubtitleChar",
6575
- paragraph: { spaceAfter: "0.75em" },
6576
- character: { fontSize: "1.25em", color: MUTED },
6577
- priority: 11,
6578
- quick: true,
6579
- builtin: true
6580
- }
6581
- ];
6582
- HEADING_SIZES.forEach((fontSize, i) => {
6583
- const level = i + 1;
6584
- styles.push({
6585
- id: `Heading${level}`,
6586
- name: `Heading ${level}`,
6587
- kind: "paragraph",
6588
- basedOn: NORMAL_STYLE_ID,
6589
- next: NORMAL_STYLE_ID,
6590
- linked: `Heading${level}Char`,
6591
- block: { type: "heading", attrs: { level } },
6592
- paragraph: { spaceBefore: level <= 2 ? "1em" : "0.75em", spaceAfter: "0.25em" },
6593
- character: { fontSize, bold: true },
6594
- priority: 11 + level,
6595
- quick: level <= 3,
6596
- builtin: true
6597
- });
6598
- });
6599
- styles.push(
6600
- {
6601
- id: "Quote",
6602
- name: "Quote",
6603
- kind: "paragraph",
6604
- basedOn: NORMAL_STYLE_ID,
6605
- next: NORMAL_STYLE_ID,
6606
- linked: "QuoteChar",
6607
- paragraph: { indentLeft: "1em", paddingLeft: "0.75em", borderLeft: "3px solid currentColor" },
6608
- character: { italic: true, color: MUTED },
6609
- priority: 29,
6610
- quick: true,
6611
- builtin: true
6612
- },
6613
- {
6614
- id: "Code",
6615
- name: "Code",
6616
- kind: "paragraph",
6617
- basedOn: NORMAL_STYLE_ID,
6618
- next: NORMAL_STYLE_ID,
6619
- block: { type: "codeBlock" },
6620
- character: {
6621
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
6622
- fontSize: "0.9em"
6623
- },
6624
- priority: 30,
6625
- quick: true,
6626
- builtin: true
6627
- },
6628
- {
6629
- id: "ListParagraph",
6630
- name: "List Paragraph",
6631
- kind: "paragraph",
6632
- basedOn: NORMAL_STYLE_ID,
6633
- paragraph: { indentLeft: "2em" },
6634
- priority: 34,
6635
- quick: true,
6636
- builtin: true
6637
- }
6638
- );
6639
- const linked = ["Title", "Subtitle", "Quote", ...HEADING_SIZES.map((_, i) => `Heading${i + 1}`)];
6640
- linked.forEach((paragraphId) => {
6641
- const name = styles.find((s) => s.id === paragraphId).name;
6642
- styles.push({
6643
- id: `${paragraphId}Char`,
6644
- name: `${name} Char`,
6645
- kind: "character",
6646
- linked: paragraphId,
6647
- priority: 99,
6648
- hidden: true,
6649
- builtin: true
6650
- });
6651
- });
6652
- styles.push(
6653
- {
6654
- id: "Emphasis",
6655
- name: "Emphasis",
6656
- kind: "character",
6657
- character: { italic: true },
6658
- priority: 20,
6659
- quick: true,
6660
- builtin: true
6661
- },
6662
- {
6663
- id: "Strong",
6664
- name: "Strong",
6665
- kind: "character",
6666
- character: { bold: true },
6667
- priority: 22,
6668
- quick: true,
6669
- builtin: true
6670
- }
6671
- );
6672
- return styles;
6673
- }
6674
- var DEFAULT_STYLES = Object.freeze(builtinStyles());
6675
- var clone = (value) => value === void 0 ? value : JSON.parse(JSON.stringify(value));
6676
- function overlay(base, patch) {
6677
- if (!patch) return base;
6678
- const out = { ...base };
6679
- Object.entries(patch).forEach(([key, value]) => {
6680
- if (value !== void 0 && value !== null) out[key] = value;
6681
- });
6682
- return out;
6683
- }
6684
- function sameBlock(a, type, attrs) {
6685
- if (a.type !== type) return false;
6686
- return Object.entries(a.attrs ?? {}).every(([key, value]) => attrs[key] === value);
6687
- }
6688
- var StyleRegistry = class {
6689
- styles = /* @__PURE__ */ new Map();
6690
- defaults;
6691
- version = 0;
6692
- snapshot = null;
6693
- listeners = /* @__PURE__ */ new Set();
6694
- constructor(options = {}) {
6695
- this.defaults = clone(options.defaults ?? {});
6696
- if (options.builtins !== false) DEFAULT_STYLES.forEach((style) => this.styles.set(style.id, clone(style)));
6697
- (options.styles ?? []).forEach((style) => this.put(style));
6698
- }
6699
- // -- Reading --------------------------------------------------------------
6700
- get(id) {
6701
- return id ? this.styles.get(id) : void 0;
6702
- }
6703
- /** Finds a style by id or display name (case-insensitive). */
6704
- find(nameOrId) {
6705
- const direct = this.styles.get(nameOrId);
6706
- if (direct) return direct;
6707
- const lower = nameOrId.toLowerCase();
6708
- return this.list().find((style) => style.name.toLowerCase() === lower);
6709
- }
6710
- /** All styles (optionally of one kind) in gallery order. */
6711
- list(kind) {
6712
- return Array.from(this.styles.values()).filter((style) => !kind || style.kind === kind).sort((a, b) => (a.priority ?? 99) - (b.priority ?? 99) || a.name.localeCompare(b.name));
6713
- }
6714
- /** Styles shown in the gallery (`quick`, not hidden), paragraph styles by default. */
6715
- gallery(kind = "paragraph") {
6716
- return this.list(kind).filter((style) => style.quick && !style.hidden);
6717
- }
6718
- /** The `basedOn` chain from the root style down to `id` (cycle-safe, bounded). */
6719
- chain(id) {
6720
- const out = [];
6721
- let current = this.styles.get(id);
6722
- while (current && out.length < MAX_CHAIN && !out.includes(current)) {
6723
- out.push(current);
6724
- current = current.basedOn ? this.styles.get(current.basedOn) : void 0;
6725
- }
6726
- return out.reverse();
6727
- }
6728
- /**
6729
- * Resolves a paragraph style: defaults, then each style of the chain (base
6730
- * first). Unknown ids resolve to the defaults in a `paragraph` block.
6731
- */
6732
- resolve(id) {
6733
- let paragraph = overlay({}, this.defaults.paragraph);
6734
- let character = overlay({}, this.defaults.character);
6735
- let block = { type: "paragraph" };
6736
- this.chain(id).forEach((style) => {
6737
- if (style.kind !== "paragraph") return;
6738
- paragraph = overlay(paragraph, style.paragraph);
6739
- character = overlay(character, style.character);
6740
- if (style.block) block = clone(style.block);
6741
- });
6742
- return { id, paragraph, character, block, next: this.nextStyleId(id) };
6743
- }
6744
- /** Character formatting a paragraph style's chain sets (without defaults). */
6745
- paragraphCharacter(id) {
6746
- let character = {};
6747
- this.chain(id).forEach((style) => {
6748
- if (style.kind === "paragraph") character = overlay(character, style.character);
6749
- });
6750
- return character;
6751
- }
6752
- /**
6753
- * Character formatting of a character style's chain. A style linked to a
6754
- * paragraph style takes that style's character formatting first.
6755
- */
6756
- resolveCharacterStyle(id) {
6757
- let character = {};
6758
- this.chain(id).forEach((style) => {
6759
- if (style.kind === "character" && style.linked && this.get(style.linked)?.kind === "paragraph") {
6760
- character = overlay(character, this.paragraphCharacter(style.linked));
6761
- }
6762
- character = overlay(character, style.character);
6763
- });
6764
- return character;
6765
- }
6766
- /**
6767
- * Character formatting of text: defaults → paragraph style chain →
6768
- * character style chain → direct formatting.
6769
- */
6770
- resolveText(input) {
6771
- let character = this.resolve(input.paragraphStyle || NORMAL_STYLE_ID).character;
6772
- if (input.characterStyle) character = overlay(character, this.resolveCharacterStyle(input.characterStyle));
6773
- return overlay(character, input.direct);
6774
- }
6775
- /** The style after Enter at the end of a paragraph of `id` (itself when unset or unknown). */
6776
- nextStyleId(id) {
6777
- const next = this.styles.get(id)?.next;
6778
- return next && this.styles.get(next)?.kind === "paragraph" ? next : id;
6779
- }
6780
- /** The linked character style of a paragraph style, if any. */
6781
- linkedCharacterStyle(id) {
6782
- const style = this.styles.get(id);
6783
- const linked = style?.linked ? this.styles.get(style.linked) : void 0;
6784
- return style?.kind === "paragraph" && linked?.kind === "character" ? linked : void 0;
6785
- }
6786
- /**
6787
- * The style a block uses when it names none: Normal for paragraphs,
6788
- * Heading N for headings of level N, Code for code blocks (the built-in
6789
- * style whose own `block` matches).
6790
- */
6791
- defaultStyleFor(type, attrs = {}) {
6792
- if (type === "paragraph" && this.styles.get(NORMAL_STYLE_ID)) return NORMAL_STYLE_ID;
6793
- const candidates = this.list("paragraph").filter(
6794
- (style) => style.block && sameBlock(style.block, type, attrs)
6795
- );
6796
- return (candidates.find((s) => s.builtin) ?? candidates[0])?.id ?? null;
6797
- }
6798
- /** The style of a block: its `styleId` when known, otherwise the block's default style. */
6799
- styleOfBlock(type, attrs = {}) {
6800
- const id = attrs.styleId;
6801
- if (id && this.styles.get(id)?.kind === "paragraph") return id;
6802
- return this.defaultStyleFor(type, attrs);
6803
- }
6804
- getSnapshot = () => {
6805
- if (!this.snapshot) {
6806
- this.snapshot = Object.freeze({
6807
- styles: Object.freeze(this.list()),
6808
- defaults: this.defaults,
6809
- version: this.version
6810
- });
6811
- }
6812
- return this.snapshot;
6813
- };
6814
- subscribe = (listener) => {
6815
- this.listeners.add(listener);
6816
- listener(this.getSnapshot());
6817
- return () => {
6818
- this.listeners.delete(listener);
6819
- };
6820
- };
6821
- toJSON() {
6822
- return { styles: clone(Array.from(this.styles.values())), defaults: clone(this.defaults) };
6823
- }
6824
- // -- Writing --------------------------------------------------------------
6825
- /** Adds or replaces a style. Throws on an invalid id. */
6826
- upsert(style) {
6827
- const stored = this.put(style);
6828
- this.changed();
6829
- return stored;
6830
- }
6831
- /** Merges `patch` into a style (formats are merged key by key). Returns `undefined` for unknown ids. */
6832
- update(id, patch) {
6833
- const current = this.styles.get(id);
6834
- if (!current) return void 0;
6835
- const next = {
6836
- ...current,
6837
- ...patch,
6838
- id,
6839
- paragraph: patch.paragraph ? { ...current.paragraph, ...patch.paragraph } : current.paragraph,
6840
- character: patch.character ? { ...current.character, ...patch.character } : current.character
6841
- };
6842
- return this.upsert(next);
6843
- }
6844
- /**
6845
- * Removes a style and returns it. Built-in styles cannot be removed. Styles
6846
- * based on it are rebased onto its own base, and `next`/`linked`
6847
- * references to it are dropped.
6848
- */
6849
- remove(id) {
6850
- const gone = this.styles.get(id);
6851
- if (!gone || gone.builtin) return void 0;
6852
- this.styles.delete(id);
6853
- this.styles.forEach((style, key) => {
6854
- let changed = style;
6855
- if (style.basedOn === id) {
6856
- changed = { ...changed, basedOn: gone.basedOn && gone.basedOn !== key ? gone.basedOn : null };
6857
- }
6858
- if (style.next === id) changed = { ...changed, next: null };
6859
- if (style.linked === id) changed = { ...changed, linked: null };
6860
- if (changed !== style) this.styles.set(key, changed);
6861
- });
6862
- this.changed();
6863
- return gone;
6864
- }
6865
- /** Replaces the document defaults. */
6866
- setDefaults(defaults) {
6867
- this.defaults = clone(defaults);
6868
- this.changed();
6869
- }
6870
- /** An unused id derived from a display name (`My Style` → `MyStyle`, then `MyStyle1`, …). */
6871
- newId(name) {
6872
- let base = name.replace(/[^A-Za-z0-9_-]+/g, "").replace(/^[^A-Za-z]+/, "").slice(0, 56);
6873
- if (!base) base = "Style";
6874
- if (!this.styles.has(base)) return base;
6875
- for (let i = 1; i < 1e5; i++) {
6876
- const id = `${base}${i}`;
6877
- if (!this.styles.has(id)) return id;
6878
- }
6879
- return `${base}${Date.now()}`;
6880
- }
6881
- // -- CSS --------------------------------------------------------------------
6882
- /** CSS declarations (`property: value`) of a resolved paragraph or character style. */
6883
- cssDeclarations(id) {
6884
- const style = this.styles.get(id);
6885
- if (style?.kind === "character") return characterCSS(this.resolveCharacterStyle(id));
6886
- const resolved = this.resolve(id);
6887
- return { ...paragraphCSS(resolved.paragraph), ...characterCSS(resolved.character) };
6888
- }
6889
- /**
6890
- * Declarations for previewing a style in a menu or gallery: font family,
6891
- * weight, style, color, decoration, case and letter spacing (no sizes,
6892
- * margins or borders).
6893
- */
6894
- previewDeclarations(id) {
6895
- return Object.fromEntries(
6896
- Object.entries(this.cssDeclarations(id)).filter(([property]) => PREVIEW_PROPERTIES.has(property))
6897
- );
6898
- }
6899
- /**
6900
- * A stylesheet for the styles: `[data-style="Id"]` rules for paragraph
6901
- * styles (plus the plain element of blocks that use their default style,
6902
- * e.g. `h2:not([data-style])`), `[data-char-style="Id"]` for character
6903
- * styles and the defaults on the scope element.
6904
- */
6905
- toCSS(options = {}) {
6906
- const scope = options.scope ?? ".ProseMirror";
6907
- const tagFor = options.tagFor ?? defaultTagFor;
6908
- const rules = [];
6909
- const rule = (selectors, declarations) => {
6910
- const body = Object.entries(declarations).map(([property, value]) => `${property}: ${value};`).join(" ");
6911
- if (body) rules.push(`${selectors.join(", ")} { ${body} }`);
6912
- };
6913
- rule([scope], {
6914
- ...paragraphCSS(this.defaults.paragraph ?? {}),
6915
- ...characterCSS(this.defaults.character ?? {})
6916
- });
6917
- this.list().forEach((style) => {
6918
- if (style.kind === "character") {
6919
- rule([`${scope} [data-char-style="${style.id}"]`], this.cssDeclarations(style.id));
6920
- return;
6921
- }
6922
- const selectors = [`${scope} [data-style="${style.id}"]`];
6923
- const block = this.resolve(style.id).block;
6924
- if (this.defaultStyleFor(block.type, block.attrs ?? {}) === style.id) {
6925
- const tag = tagFor(block);
6926
- if (tag) selectors.push(`${scope} ${tag}:not([data-style])`);
6927
- }
6928
- const resolved = this.resolve(style.id);
6929
- const own = {
6930
- ...paragraphCSS(withoutDefaults(resolved.paragraph, this.defaults.paragraph)),
6931
- ...characterCSS(withoutDefaults(resolved.character, this.defaults.character))
6932
- };
6933
- rule(selectors, own);
6934
- });
6935
- return rules.join("\n");
6936
- }
6937
- // -- Internals --------------------------------------------------------------
6938
- put(style) {
6939
- if (!isValidStyleId(style.id)) throw new Error(`Invalid style id: ${String(style.id)}`);
6940
- if (style.kind !== "paragraph" && style.kind !== "character") {
6941
- throw new Error(`Invalid style kind: ${String(style.kind)}`);
6942
- }
6943
- const stored = clone({ ...style, name: style.name || style.id });
6944
- this.styles.set(style.id, stored);
6945
- return stored;
6946
- }
6947
- changed() {
6948
- this.version++;
6949
- this.snapshot = null;
6950
- const snapshot = this.getSnapshot();
6951
- this.listeners.forEach((listener) => {
6952
- try {
6953
- listener(snapshot);
6954
- } catch (err) {
6955
- console.error("[StyleRegistry] Error in listener:", err);
6956
- }
6957
- });
6958
- }
6959
- };
6960
- function withoutDefaults(format, defaults) {
6961
- if (!defaults) return format;
6962
- const out = {};
6963
- Object.entries(format).forEach(([key, value]) => {
6964
- if (defaults[key] !== value) out[key] = value;
6965
- });
6966
- return out;
6967
- }
6968
- function defaultTagFor(block) {
6969
- if (block.type === "paragraph") return "p";
6970
- if (block.type === "heading") {
6971
- const level = Number(block.attrs?.level);
6972
- return level >= 1 && level <= 6 ? `h${level}` : null;
6973
- }
6974
- if (block.type === "codeBlock") return "pre";
6975
- return null;
6976
- }
6977
- function safeCSSValue(value) {
6978
- if (typeof value !== "string") return void 0;
6979
- const trimmed = value.trim();
6980
- if (!trimmed || trimmed.length > 200 || UNSAFE_CSS.test(trimmed)) return void 0;
6981
- return trimmed;
6982
- }
6983
- function paragraphCSS(format) {
6984
- const out = {};
6985
- const set = (property, value) => {
6986
- const safe = safeCSSValue(value);
6987
- if (safe) out[property] = safe;
6988
- };
6989
- set("text-align", format.textAlign);
6990
- set("line-height", format.lineHeight);
6991
- set("margin-top", format.spaceBefore);
6992
- set("margin-bottom", format.spaceAfter);
6993
- set("margin-left", format.indentLeft);
6994
- set("margin-right", format.indentRight);
6995
- set("text-indent", format.indentFirstLine);
6996
- set("border-left", format.borderLeft);
6997
- set("padding-left", format.paddingLeft);
6998
- set("background-color", format.backgroundColor);
6999
- return out;
7000
- }
7001
- function characterCSS(format) {
7002
- const out = {};
7003
- const set = (property, value) => {
7004
- const safe = safeCSSValue(value);
7005
- if (safe) out[property] = safe;
7006
- };
7007
- set("font-family", format.fontFamily);
7008
- set("font-size", format.fontSize);
7009
- if (format.bold !== void 0) out["font-weight"] = format.bold ? "700" : "400";
7010
- if (format.italic !== void 0) out["font-style"] = format.italic ? "italic" : "normal";
7011
- const lines = [format.underline && "underline", format.strike && "line-through"].filter(Boolean);
7012
- if (format.underline !== void 0 || format.strike !== void 0) {
7013
- out["text-decoration-line"] = lines.length ? lines.join(" ") : "none";
7014
- }
7015
- set("color", format.color);
7016
- set("background-color", format.backgroundColor);
7017
- set("letter-spacing", format.letterSpacing);
7018
- set("text-transform", format.textTransform);
7019
- if (format.smallCaps !== void 0) out["font-variant-caps"] = format.smallCaps ? "small-caps" : "normal";
7020
- return out;
7021
- }
7022
- function createStyleRegistry(options = {}) {
7023
- return new StyleRegistry(options);
7024
- }
7025
-
7026
- // src/extensions/paragraph-styles/extension.ts
7027
6744
  var STYLE_ID_ATTRIBUTE = "styleId";
7028
6745
  var CHARACTER_STYLE_MARK_NAME = "characterStyle";
7029
6746
  var paragraphStylesPluginKey = new PluginKey12("paragraphStyles");
@@ -7476,6 +7193,9 @@ function createRichTextPreset(options = {}) {
7476
7193
  placeholder: typeof placeholder === "function" ? placeholder : () => placeholder
7477
7194
  })
7478
7195
  ];
7196
+ if (options.table !== false) {
7197
+ extensions2.push(...createTableExtensions(optionObject(options.table)));
7198
+ }
7479
7199
  if (characterLimit) {
7480
7200
  extensions2.push(
7481
7201
  CharacterCount.configure({
@@ -7722,6 +7442,69 @@ function validateFindPattern(query, options = {}) {
7722
7442
  return result.ok ? null : result.error;
7723
7443
  }
7724
7444
 
7445
+ // src/extensions/table/commands.ts
7446
+ var PARAMS = {
7447
+ insertTable: {
7448
+ type: "object",
7449
+ properties: {
7450
+ rows: { type: "integer", minimum: 1, maximum: MAX_TABLE_ROWS, description: "Rows (default 3)." },
7451
+ cols: { type: "integer", minimum: 1, maximum: MAX_TABLE_COLUMNS, description: "Columns (default 3)." },
7452
+ withHeaderRow: { type: "boolean", description: "Make the first row a header row (default true)." }
7453
+ },
7454
+ additionalProperties: false
7455
+ },
7456
+ setCellAlignment: {
7457
+ type: "object",
7458
+ properties: {
7459
+ align: {
7460
+ type: ["string", "null"],
7461
+ enum: ["left", "center", "right", "justify", null],
7462
+ description: "Horizontal alignment; null clears it."
7463
+ }
7464
+ },
7465
+ required: ["align"],
7466
+ additionalProperties: false
7467
+ },
7468
+ setCellBackground: {
7469
+ type: "object",
7470
+ properties: {
7471
+ color: { type: ["string", "null"], description: "CSS color (e.g. #fde68a); null clears it." }
7472
+ },
7473
+ required: ["color"],
7474
+ additionalProperties: false
7475
+ }
7476
+ };
7477
+ function createTableCommands(editor, options = {}) {
7478
+ const prefix = options.prefix ?? "table.";
7479
+ const group = options.group ?? "Table";
7480
+ const scope = options.scope ? { scope: options.scope } : {};
7481
+ const focus = options.focus ?? true;
7482
+ return TABLE_ACTIONS.map(({ action, label, description, keywords }) => {
7483
+ const params = PARAMS[action];
7484
+ return {
7485
+ id: `${prefix}${action}`,
7486
+ label,
7487
+ group,
7488
+ keywords: [...keywords],
7489
+ description,
7490
+ ...params ? { params } : {},
7491
+ ...scope,
7492
+ enabled: (_ctx, p) => getTableActionState(editor, action, p ?? {}),
7493
+ run: (_ctx, p) => {
7494
+ if (!runTableAction(editor, action, p ?? {}, { focus })) {
7495
+ const reason = getTableActionState(editor, action, p ?? {});
7496
+ throw new Error(reason === true ? `${label} did not change the document.` : reason);
7497
+ }
7498
+ const state = getTableState(editor);
7499
+ return {
7500
+ done: true,
7501
+ table: state.inTable ? { rows: state.rows, columns: state.columns, row: state.row, column: state.column } : null
7502
+ };
7503
+ }
7504
+ };
7505
+ });
7506
+ }
7507
+
7725
7508
  export {
7726
7509
  GradientText,
7727
7510
  SlashCommands,
@@ -7758,13 +7541,6 @@ export {
7758
7541
  ImportError,
7759
7542
  createImportContext,
7760
7543
  ImportAdapterRegistry,
7761
- ZipError,
7762
- DEFAULT_ZIP_MAX_ENTRIES,
7763
- DEFAULT_ZIP_MAX_TOTAL_SIZE,
7764
- zipCrc32,
7765
- isUnsafeZipPath,
7766
- ZipArchive,
7767
- readZip,
7768
7544
  DOCX_MIME_TYPES,
7769
7545
  DEFAULT_DOCX_MAX_IMAGE_SIZE,
7770
7546
  docxToDocumentImport,
@@ -7784,6 +7560,7 @@ export {
7784
7560
  getImportAccept,
7785
7561
  htmlToSlice,
7786
7562
  insertImportedDocument,
7563
+ applyImportedReviewData,
7787
7564
  importFilesIntoEditor,
7788
7565
  fileImportPluginKey,
7789
7566
  FileImport,
@@ -7845,12 +7622,6 @@ export {
7845
7622
  FIND_OPTION_TOGGLES,
7846
7623
  formatFindStatus,
7847
7624
  FindReplace,
7848
- NORMAL_STYLE_ID,
7849
- isValidStyleId,
7850
- DEFAULT_STYLES,
7851
- StyleRegistry,
7852
- safeCSSValue,
7853
- createStyleRegistry,
7854
7625
  STYLE_ID_ATTRIBUTE,
7855
7626
  CHARACTER_STYLE_MARK_NAME,
7856
7627
  paragraphStylesPluginKey,
@@ -7863,6 +7634,7 @@ export {
7863
7634
  createParagraphStylesStore,
7864
7635
  createRichTextPreset,
7865
7636
  createFindReplaceCommands,
7866
- validateFindPattern
7637
+ validateFindPattern,
7638
+ createTableCommands
7867
7639
  };
7868
- //# sourceMappingURL=chunk-IXHSYOVY.js.map
7640
+ //# sourceMappingURL=chunk-Q2OHWD2A.js.map