docxodus 12.0.1 → 12.1.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.
@@ -9448,6 +9448,12 @@ function blockPreviewText(unit) {
9448
9448
  const text = (unit.textContent ?? "").trim().replace(/\s+/g, " ");
9449
9449
  return text.length > 48 ? `${text.slice(0, 48)}\u2026` : text;
9450
9450
  }
9451
+ var BLOCK_HANDLE_WIDTH = 26;
9452
+ var BLOCK_HANDLE_HEIGHT = 28;
9453
+ var BLOCK_HANDLE_GAP = 6;
9454
+ function clampToRange(value, low, high) {
9455
+ return Math.max(low, Math.min(value, high));
9456
+ }
9451
9457
  function ensureBlockDragStyles(doc) {
9452
9458
  if (blockDragStyledDocuments.has(doc)) return;
9453
9459
  blockDragStyledDocuments.add(doc);
@@ -9910,6 +9916,10 @@ var DocxEditor = class _DocxEditor {
9910
9916
  this.blockMoveMenu = null;
9911
9917
  this.blockMoveLive = null;
9912
9918
  this.blockDragSource = null;
9919
+ /** Whether hover or focus currently wants a handle for `blockDragSource`.
9920
+ * Tracked apart from the handle's `display`, which the viewport clip also drives: a handle
9921
+ * withdrawn because its block scrolled out of view has to come back when it scrolls in. */
9922
+ this.blockHandleWanted = false;
9913
9923
  this.blockDragCleanup = [];
9914
9924
  /** Block boxes measured at drag start — see `BlockDropZone`. Empty when no drag is in flight. */
9915
9925
  this.dropZones = [];
@@ -10306,11 +10316,47 @@ var DocxEditor = class _DocxEditor {
10306
10316
  }
10307
10317
  return null;
10308
10318
  }
10319
+ /**
10320
+ * The rectangle the floating handle may occupy: the window, narrowed by every ancestor that
10321
+ * clips its overflow.
10322
+ *
10323
+ * The handle is `position: fixed`, so it is placed in viewport coordinates and no ancestor's
10324
+ * overflow clips it for us. But the editor is routinely mounted inside a bounded scroller —
10325
+ * the ribbon puts its surface in one, inside a clipped card — and a block scrolled out of that
10326
+ * scroller has to take its handle with it rather than leave it parked over the host's chrome.
10327
+ *
10328
+ * Gated on the computed `overflow` rather than on whether an element scrolls right now, so the
10329
+ * answer does not depend on how much content happens to be loaded.
10330
+ */
10331
+ blockHandleClipRect() {
10332
+ const view = this.container.ownerDocument.defaultView;
10333
+ const clip = {
10334
+ top: 0,
10335
+ left: 0,
10336
+ right: view?.innerWidth ?? Number.MAX_SAFE_INTEGER,
10337
+ bottom: view?.innerHeight ?? Number.MAX_SAFE_INTEGER
10338
+ };
10339
+ for (let el = this.editRoot; el; el = el.parentElement) {
10340
+ const style = view?.getComputedStyle(el);
10341
+ if (!style || style.overflowX === "visible" && style.overflowY === "visible") continue;
10342
+ const box = el.getBoundingClientRect();
10343
+ if (style.overflowY !== "visible") {
10344
+ clip.top = Math.max(clip.top, box.top);
10345
+ clip.bottom = Math.min(clip.bottom, box.bottom);
10346
+ }
10347
+ if (style.overflowX !== "visible") {
10348
+ clip.left = Math.max(clip.left, box.left);
10349
+ clip.right = Math.min(clip.right, box.right);
10350
+ }
10351
+ }
10352
+ return clip;
10353
+ }
10309
10354
  showBlockHandle(unit) {
10310
10355
  const handle = this.blockDragHandle;
10311
10356
  if (!handle || !this.isMovableBlockUnit(unit)) return;
10312
10357
  const changed = unit !== this.blockDragSource;
10313
10358
  this.blockDragSource = unit;
10359
+ this.blockHandleWanted = true;
10314
10360
  const known = this.blockMoveTargetsFor(unit, { cachedOnly: true });
10315
10361
  if (known?.size === 0) {
10316
10362
  handle.style.display = "none";
@@ -10318,19 +10364,31 @@ var DocxEditor = class _DocxEditor {
10318
10364
  }
10319
10365
  if (changed && known === void 0) this.prefetchBlockMoveTargets(unit);
10320
10366
  const rect = unit.getBoundingClientRect();
10367
+ const clip = this.blockHandleClipRect();
10368
+ if (rect.bottom <= clip.top || rect.top >= clip.bottom || rect.right <= clip.left || rect.left >= clip.right) {
10369
+ if (!this.blockDragging && this.blockMoveMenu?.style.display !== "block")
10370
+ handle.style.display = "none";
10371
+ return;
10372
+ }
10321
10373
  handle.style.display = "flex";
10322
- handle.style.left = `${Math.max(4, rect.left - 32)}px`;
10323
- handle.style.top = `${Math.max(4, rect.top + (unit.tagName === "TABLE" ? 6 : Math.max(0, (rect.height - 28) / 2)))}px`;
10374
+ const top2 = rect.top + (unit.tagName === "TABLE" ? 6 : Math.max(0, (rect.height - BLOCK_HANDLE_HEIGHT) / 2));
10375
+ handle.style.left = `${clampToRange(
10376
+ rect.left - (BLOCK_HANDLE_WIDTH + BLOCK_HANDLE_GAP),
10377
+ clip.left,
10378
+ clip.right - BLOCK_HANDLE_WIDTH
10379
+ )}px`;
10380
+ handle.style.top = `${clampToRange(top2, clip.top, clip.bottom - BLOCK_HANDLE_HEIGHT)}px`;
10324
10381
  const preview = blockPreviewText(unit);
10325
10382
  handle.setAttribute("aria-label", preview ? `Move block: ${preview}` : "Move block");
10326
10383
  }
10327
10384
  hideBlockHandle() {
10328
10385
  if (this.blockDragging || this.blockMoveMenu?.style.display === "block") return;
10386
+ this.blockHandleWanted = false;
10329
10387
  if (this.blockDragHandle) this.blockDragHandle.style.display = "none";
10330
10388
  }
10331
10389
  positionBlockHandle() {
10332
10390
  const source = this.currentBlockDragSource();
10333
- if (source && this.blockDragHandle?.style.display !== "none") this.showBlockHandle(source);
10391
+ if (source && this.blockHandleWanted) this.showBlockHandle(source);
10334
10392
  }
10335
10393
  /** Draw the drop line on `zone`'s requested edge, or take it away when there is no target. */
10336
10394
  paintDropIndicator(data) {
@@ -10768,6 +10826,7 @@ var DocxEditor = class _DocxEditor {
10768
10826
  this.blockMoveMenu = null;
10769
10827
  this.blockMoveLive = null;
10770
10828
  this.blockDragSource = null;
10829
+ this.blockHandleWanted = false;
10771
10830
  this.blockDragging = false;
10772
10831
  this.blockDragPointerDown = false;
10773
10832
  }
package/dist/embed.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * ```html
8
8
  * <div id="doc"></div>
9
9
  * <script type="module">
10
- * import { createViewer } from "https://cdn.jsdelivr.net/npm/docxodus@12.0.0/dist/embed.bundle.js";
10
+ * import { createViewer } from "https://cdn.jsdelivr.net/npm/docxodus@12.0.1/dist/embed.bundle.js";
11
11
  * await createViewer("#doc", "./contract.docx");
12
12
  * </script>
13
13
  * ```
@@ -9571,6 +9571,12 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
9571
9571
  const text = (unit.textContent ?? "").trim().replace(/\s+/g, " ");
9572
9572
  return text.length > 48 ? `${text.slice(0, 48)}\u2026` : text;
9573
9573
  }
9574
+ var BLOCK_HANDLE_WIDTH = 26;
9575
+ var BLOCK_HANDLE_HEIGHT = 28;
9576
+ var BLOCK_HANDLE_GAP = 6;
9577
+ function clampToRange(value, low, high) {
9578
+ return Math.max(low, Math.min(value, high));
9579
+ }
9574
9580
  function ensureBlockDragStyles(doc) {
9575
9581
  if (blockDragStyledDocuments.has(doc)) return;
9576
9582
  blockDragStyledDocuments.add(doc);
@@ -10033,6 +10039,10 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
10033
10039
  this.blockMoveMenu = null;
10034
10040
  this.blockMoveLive = null;
10035
10041
  this.blockDragSource = null;
10042
+ /** Whether hover or focus currently wants a handle for `blockDragSource`.
10043
+ * Tracked apart from the handle's `display`, which the viewport clip also drives: a handle
10044
+ * withdrawn because its block scrolled out of view has to come back when it scrolls in. */
10045
+ this.blockHandleWanted = false;
10036
10046
  this.blockDragCleanup = [];
10037
10047
  /** Block boxes measured at drag start — see `BlockDropZone`. Empty when no drag is in flight. */
10038
10048
  this.dropZones = [];
@@ -10429,11 +10439,47 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
10429
10439
  }
10430
10440
  return null;
10431
10441
  }
10442
+ /**
10443
+ * The rectangle the floating handle may occupy: the window, narrowed by every ancestor that
10444
+ * clips its overflow.
10445
+ *
10446
+ * The handle is `position: fixed`, so it is placed in viewport coordinates and no ancestor's
10447
+ * overflow clips it for us. But the editor is routinely mounted inside a bounded scroller —
10448
+ * the ribbon puts its surface in one, inside a clipped card — and a block scrolled out of that
10449
+ * scroller has to take its handle with it rather than leave it parked over the host's chrome.
10450
+ *
10451
+ * Gated on the computed `overflow` rather than on whether an element scrolls right now, so the
10452
+ * answer does not depend on how much content happens to be loaded.
10453
+ */
10454
+ blockHandleClipRect() {
10455
+ const view = this.container.ownerDocument.defaultView;
10456
+ const clip = {
10457
+ top: 0,
10458
+ left: 0,
10459
+ right: view?.innerWidth ?? Number.MAX_SAFE_INTEGER,
10460
+ bottom: view?.innerHeight ?? Number.MAX_SAFE_INTEGER
10461
+ };
10462
+ for (let el = this.editRoot; el; el = el.parentElement) {
10463
+ const style = view?.getComputedStyle(el);
10464
+ if (!style || style.overflowX === "visible" && style.overflowY === "visible") continue;
10465
+ const box = el.getBoundingClientRect();
10466
+ if (style.overflowY !== "visible") {
10467
+ clip.top = Math.max(clip.top, box.top);
10468
+ clip.bottom = Math.min(clip.bottom, box.bottom);
10469
+ }
10470
+ if (style.overflowX !== "visible") {
10471
+ clip.left = Math.max(clip.left, box.left);
10472
+ clip.right = Math.min(clip.right, box.right);
10473
+ }
10474
+ }
10475
+ return clip;
10476
+ }
10432
10477
  showBlockHandle(unit) {
10433
10478
  const handle = this.blockDragHandle;
10434
10479
  if (!handle || !this.isMovableBlockUnit(unit)) return;
10435
10480
  const changed = unit !== this.blockDragSource;
10436
10481
  this.blockDragSource = unit;
10482
+ this.blockHandleWanted = true;
10437
10483
  const known = this.blockMoveTargetsFor(unit, { cachedOnly: true });
10438
10484
  if (known?.size === 0) {
10439
10485
  handle.style.display = "none";
@@ -10441,19 +10487,31 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
10441
10487
  }
10442
10488
  if (changed && known === void 0) this.prefetchBlockMoveTargets(unit);
10443
10489
  const rect = unit.getBoundingClientRect();
10490
+ const clip = this.blockHandleClipRect();
10491
+ if (rect.bottom <= clip.top || rect.top >= clip.bottom || rect.right <= clip.left || rect.left >= clip.right) {
10492
+ if (!this.blockDragging && this.blockMoveMenu?.style.display !== "block")
10493
+ handle.style.display = "none";
10494
+ return;
10495
+ }
10444
10496
  handle.style.display = "flex";
10445
- handle.style.left = `${Math.max(4, rect.left - 32)}px`;
10446
- handle.style.top = `${Math.max(4, rect.top + (unit.tagName === "TABLE" ? 6 : Math.max(0, (rect.height - 28) / 2)))}px`;
10497
+ const top2 = rect.top + (unit.tagName === "TABLE" ? 6 : Math.max(0, (rect.height - BLOCK_HANDLE_HEIGHT) / 2));
10498
+ handle.style.left = `${clampToRange(
10499
+ rect.left - (BLOCK_HANDLE_WIDTH + BLOCK_HANDLE_GAP),
10500
+ clip.left,
10501
+ clip.right - BLOCK_HANDLE_WIDTH
10502
+ )}px`;
10503
+ handle.style.top = `${clampToRange(top2, clip.top, clip.bottom - BLOCK_HANDLE_HEIGHT)}px`;
10447
10504
  const preview = blockPreviewText(unit);
10448
10505
  handle.setAttribute("aria-label", preview ? `Move block: ${preview}` : "Move block");
10449
10506
  }
10450
10507
  hideBlockHandle() {
10451
10508
  if (this.blockDragging || this.blockMoveMenu?.style.display === "block") return;
10509
+ this.blockHandleWanted = false;
10452
10510
  if (this.blockDragHandle) this.blockDragHandle.style.display = "none";
10453
10511
  }
10454
10512
  positionBlockHandle() {
10455
10513
  const source = this.currentBlockDragSource();
10456
- if (source && this.blockDragHandle?.style.display !== "none") this.showBlockHandle(source);
10514
+ if (source && this.blockHandleWanted) this.showBlockHandle(source);
10457
10515
  }
10458
10516
  /** Draw the drop line on `zone`'s requested edge, or take it away when there is no target. */
10459
10517
  paintDropIndicator(data) {
@@ -10891,6 +10949,7 @@ span.comment-highlight.docx-comment-active { background: hsl(var(--docx-comment-
10891
10949
  this.blockMoveMenu = null;
10892
10950
  this.blockMoveLive = null;
10893
10951
  this.blockDragSource = null;
10952
+ this.blockHandleWanted = false;
10894
10953
  this.blockDragging = false;
10895
10954
  this.blockDragPointerDown = false;
10896
10955
  }
package/dist/embed.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * ```html
8
8
  * <div id="doc"></div>
9
9
  * <script type="module">
10
- * import { createViewer } from "https://cdn.jsdelivr.net/npm/docxodus@12.0.0/dist/embed.bundle.js";
10
+ * import { createViewer } from "https://cdn.jsdelivr.net/npm/docxodus@12.0.1/dist/embed.bundle.js";
11
11
  * await createViewer("#doc", "./contract.docx");
12
12
  * </script>
13
13
  * ```
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema": "https://docxodus.dev/schemas/export/export-assets/v1",
3
3
  "schemaVersion": 1,
4
- "packageVersion": "12.0.1",
4
+ "packageVersion": "12.1.0",
5
5
  "assets": [
6
6
  {
7
7
  "path": "./docxodus.worker.js",
@@ -49,13 +49,13 @@
49
49
  "path": "./wasm/_framework/Docxodus.wasm",
50
50
  "mediaType": "application/wasm",
51
51
  "byteLength": 3472665,
52
- "sha256": "f21fe5783f9333d47240e217e5d7bc77e377095895879b84870dbd247aaf025a"
52
+ "sha256": "f42c48646d24cf09d621c575e476731eaff7117a82c20a495b9378b5512957d8"
53
53
  },
54
54
  {
55
55
  "path": "./wasm/_framework/DocxodusWasm.wasm",
56
56
  "mediaType": "application/wasm",
57
57
  "byteLength": 491285,
58
- "sha256": "b3c5019fea33ee2454041f55be980f8957e7869a3c8f14aaed5a895609eef233"
58
+ "sha256": "9600c0ac776615f0fea0aff0831b6f32a040f23932c5df6dd8f26ffb69a7775e"
59
59
  },
60
60
  {
61
61
  "path": "./wasm/_framework/System.Collections.Concurrent.wasm",
@@ -247,7 +247,7 @@
247
247
  "path": "./wasm/_framework/dotnet.boot.js",
248
248
  "mediaType": "text/javascript",
249
249
  "byteLength": 9374,
250
- "sha256": "d3c8159dd193bd5baca6f2874515fe940e1299366be6e2290885a332d96712c3"
250
+ "sha256": "1d12c4a0fd969902b3072fa4763028263ec239199b69057061bd98192f1d6d12"
251
251
  },
252
252
  {
253
253
  "path": "./wasm/_framework/dotnet.js",
@@ -265,7 +265,7 @@
265
265
  "path": "./wasm/_framework/dotnet.native.wasm",
266
266
  "mediaType": "application/wasm",
267
267
  "byteLength": 8276254,
268
- "sha256": "6ce8e3d8295bfe9d3a5c0257aefe1db9db11264e605397337b6d21f43da08d85"
268
+ "sha256": "363202acedd18abc01a91f7e9e3feb94367e4938e1f07ad9ba30778d519c41df"
269
269
  },
270
270
  {
271
271
  "path": "./wasm/_framework/dotnet.runtime.js",
package/dist/index.js CHANGED
@@ -74,7 +74,7 @@ async function yieldToMain() {
74
74
  function getDefaultWasmBasePath() {
75
75
  try {
76
76
  // import.meta.url gives us the URL of this module
77
- // e.g., "https://cdn.jsdelivr.net/npm/docxodus@12.0.0/dist/index.js"
77
+ // e.g., "https://cdn.jsdelivr.net/npm/docxodus@12.0.1/dist/index.js"
78
78
  // or "file:///path/to/node_modules/docxodus/dist/index.js"
79
79
  const moduleUrl = import.meta.url;
80
80
  // Remove the filename to get the directory
Binary file
@@ -1,7 +1,7 @@
1
1
  export const config = /*json-start*/{
2
2
  "mainAssemblyName": "DocxodusWasm.dll",
3
3
  "resources": {
4
- "hash": "sha256-E6vf07wAT55HCWvVtt9pVBwCH9gGv0gVuWk3877+ZH0=",
4
+ "hash": "sha256-JK1HM76GNNB6Utp8MnYXpYDCSgUmFQEXEqqu+jo+gQY=",
5
5
  "jsModuleNative": [
6
6
  {
7
7
  "name": "dotnet.native.js"
@@ -15,7 +15,7 @@ export const config = /*json-start*/{
15
15
  "wasmNative": [
16
16
  {
17
17
  "name": "dotnet.native.wasm",
18
- "hash": "sha256-bOjj2Clb/p06XAJXrv4dudsRJk5gU5cze20h9D2gjYU="
18
+ "hash": "sha256-NjICrO3RirwBqR9+nj/rlDZ+STjh8HrZujB3jVGcQd8="
19
19
  }
20
20
  ],
21
21
  "coreAssembly": [
@@ -32,12 +32,12 @@ export const config = /*json-start*/{
32
32
  {
33
33
  "virtualPath": "Docxodus.wasm",
34
34
  "name": "Docxodus.wasm",
35
- "hash": "sha256-8h/leD+TM9RyQOIX5de8d+N3CViVh5uEhw29JHqvAlo="
35
+ "hash": "sha256-9CxIZG0kzwnWIcV15HZzHq/3EXqCwgpJW5N4tVEpV9g="
36
36
  },
37
37
  {
38
38
  "virtualPath": "DocxodusWasm.wasm",
39
39
  "name": "DocxodusWasm.wasm",
40
- "hash": "sha256-s8UBn+oz7iRUBB9VvpgPiVfnhpo8jxSq7VqJVgnu8jM="
40
+ "hash": "sha256-lgDArHdmFfD+oK/wgxtvMqBA8jkyxd9t2PJv+2mnd14="
41
41
  },
42
42
  {
43
43
  "virtualPath": "System.Collections.Concurrent.wasm",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docxodus",
3
- "version": "12.0.1",
3
+ "version": "12.1.0",
4
4
  "description": "Render, edit, diff and project Word .docx files in the browser — DOCX to HTML, a block editor, Word-grade redlines with tracked changes, and anchor-addressed markdown for LLM agents. WebAssembly, no server.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",