@extend-ai/react-docx 0.7.0-alpha.2 → 0.7.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -160,6 +160,15 @@ function __wbg_get_imports() {
160
160
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
161
161
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
162
162
  },
163
+ __wbg___wbindgen_is_null_ced4761460071341: function(arg0) {
164
+ const ret = getObject(arg0) === null;
165
+ return ret;
166
+ },
167
+ __wbg___wbindgen_is_object_3a2c414391dbf751: function(arg0) {
168
+ const val = getObject(arg0);
169
+ const ret = typeof val === "object" && val !== null;
170
+ return ret;
171
+ },
163
172
  __wbg___wbindgen_is_undefined_4410e3c20a99fa97: function(arg0) {
164
173
  const ret = getObject(arg0) === void 0;
165
174
  return ret;
@@ -175,12 +184,83 @@ function __wbg_get_imports() {
175
184
  __wbg___wbindgen_throw_bbadd78c1bac3a77: function(arg0, arg1) {
176
185
  throw new Error(getStringFromWasm0(arg0, arg1));
177
186
  },
187
+ __wbg_create_00e11a7cefe80760: function(arg0) {
188
+ const ret = Object.create(getObject(arg0));
189
+ return addHeapObject(ret);
190
+ },
191
+ __wbg_get_52a8a619f7b88df6: function() {
192
+ return handleError(function(arg0, arg1) {
193
+ const ret = Reflect.get(getObject(arg0), getObject(arg1));
194
+ return addHeapObject(ret);
195
+ }, arguments);
196
+ },
197
+ __wbg_get_unchecked_46e778e3cec74b5e: function(arg0, arg1) {
198
+ const ret = getObject(arg0)[arg1 >>> 0];
199
+ return addHeapObject(ret);
200
+ },
201
+ __wbg_instanceof_ArrayBuffer_a581da923203f29f: function(arg0) {
202
+ let result;
203
+ try {
204
+ result = getObject(arg0) instanceof ArrayBuffer;
205
+ } catch (_) {
206
+ result = false;
207
+ }
208
+ const ret = result;
209
+ return ret;
210
+ },
211
+ __wbg_instanceof_Uint8Array_b6fe1ac89eba107e: function(arg0) {
212
+ let result;
213
+ try {
214
+ result = getObject(arg0) instanceof Uint8Array;
215
+ } catch (_) {
216
+ result = false;
217
+ }
218
+ const ret = result;
219
+ return ret;
220
+ },
221
+ __wbg_isArray_139f48e3c057ede8: function(arg0) {
222
+ const ret = Array.isArray(getObject(arg0));
223
+ return ret;
224
+ },
225
+ __wbg_keys_bd51ff67a9b04698: function(arg0) {
226
+ const ret = Object.keys(getObject(arg0));
227
+ return addHeapObject(ret);
228
+ },
229
+ __wbg_length_68a9d5278d084f4f: function(arg0) {
230
+ const ret = getObject(arg0).length;
231
+ return ret;
232
+ },
233
+ __wbg_length_fb04d16d7bdf6d4c: function(arg0) {
234
+ const ret = getObject(arg0).length;
235
+ return ret;
236
+ },
237
+ __wbg_new_20b778a4c5c691c3: function() {
238
+ const ret = new Object();
239
+ return addHeapObject(ret);
240
+ },
241
+ __wbg_new_b06772b280cc6e52: function(arg0) {
242
+ const ret = new Uint8Array(getObject(arg0));
243
+ return addHeapObject(ret);
244
+ },
245
+ __wbg_new_from_slice_bb2d1778c0b87eb1: function(arg0, arg1) {
246
+ const ret = new Uint8Array(getArrayU8FromWasm0(arg0, arg1));
247
+ return addHeapObject(ret);
248
+ },
178
249
  __wbg_parse_246201845d0eb98c: function() {
179
250
  return handleError(function(arg0, arg1) {
180
251
  const ret = JSON.parse(getStringFromWasm0(arg0, arg1));
181
252
  return addHeapObject(ret);
182
253
  }, arguments);
183
254
  },
255
+ __wbg_prototypesetcall_956c7493c68e29b4: function(arg0, arg1, arg2) {
256
+ Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), getObject(arg2));
257
+ },
258
+ __wbg_set_a6ba3ac0e634b822: function() {
259
+ return handleError(function(arg0, arg1, arg2) {
260
+ const ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2));
261
+ return ret;
262
+ }, arguments);
263
+ },
184
264
  __wbg_stringify_a42c95ea9a7591c9: function() {
185
265
  return handleError(function(arg0) {
186
266
  const ret = JSON.stringify(getObject(arg0));
@@ -474,9 +554,15 @@ async function defaultWasmSource() {
474
554
  async function initWasm(source) {
475
555
  if (!initPromise) {
476
556
  const chosen = source ?? overrideSource;
477
- initPromise = (chosen !== void 0 ? Promise.resolve(chosen) : defaultWasmSource()).then(
478
- (module_or_path) => __wbg_init({ module_or_path })
479
- );
557
+ initPromise = (chosen !== void 0 ? Promise.resolve(chosen) : defaultWasmSource()).then((module_or_path) => __wbg_init({ module_or_path })).catch((error) => {
558
+ if (error instanceof WebAssembly.CompileError) {
559
+ throw new Error(
560
+ `react-docx: the bundled WebAssembly binary failed to compile. It requires WebAssembly SIMD support (Chrome 91+, Firefox 89+, Safari 16.4+, Node 16.4+). Original error: ${error.message}`,
561
+ { cause: error }
562
+ );
563
+ }
564
+ throw error;
565
+ });
480
566
  }
481
567
  return initPromise;
482
568
  }
@@ -515,7 +601,7 @@ function mapsToWasmPackage(input) {
515
601
  }
516
602
  const binaryAssets = {};
517
603
  for (const [name, asset] of input.binaryAssets.entries()) {
518
- binaryAssets[name] = Array.from(asset);
604
+ binaryAssets[name] = asset;
519
605
  }
520
606
  return { parts, binaryAssets };
521
607
  }
@@ -2906,16 +2992,17 @@ function shouldAllowStoredPageCountReduction(options) {
2906
2992
  if (targetPageCount >= estimatedPageCount) {
2907
2993
  return true;
2908
2994
  }
2995
+ const renderedBreakHintPageCount = Number.isFinite(
2996
+ options.renderedBreakHintPageCount
2997
+ ) ? Math.max(1, Math.round(options.renderedBreakHintPageCount)) : void 0;
2998
+ const renderedBreakHintsSupportTarget = options.hasLastRenderedPageBreakHints === true && renderedBreakHintPageCount !== void 0 && targetPageCount >= renderedBreakHintPageCount;
2909
2999
  if (options.hasMeasuredBodyFooterOverlap === true) {
2910
- return false;
3000
+ return renderedBreakHintsSupportTarget;
2911
3001
  }
2912
3002
  if (options.hasLastRenderedPageBreakHints !== true) {
2913
3003
  return true;
2914
3004
  }
2915
- const renderedBreakHintPageCount = Number.isFinite(
2916
- options.renderedBreakHintPageCount
2917
- ) ? Math.max(1, Math.round(options.renderedBreakHintPageCount)) : void 0;
2918
- return renderedBreakHintPageCount !== void 0 && targetPageCount >= renderedBreakHintPageCount;
3005
+ return renderedBreakHintsSupportTarget;
2919
3006
  }
2920
3007
  function shouldLatchMeasuredBodyFooterOverlap(options) {
2921
3008
  if (options.measuredBodyFooterOverlap !== true) {
@@ -4022,6 +4109,425 @@ function sliceLayoutToLineRange(layout, startLineIndex, endLineIndex) {
4022
4109
  };
4023
4110
  }
4024
4111
 
4112
+ // src/content-signature.ts
4113
+ init_cjs_shims();
4114
+ var FNV_OFFSET_BASIS = 2166136261;
4115
+ var FNV_PRIME = 16777619;
4116
+ var LONG_STRING_SAMPLE_LENGTH = 64;
4117
+ var LONG_STRING_THRESHOLD = 256;
4118
+ function fnv1aAppend(hash, text) {
4119
+ let next = hash;
4120
+ for (let index = 0; index < text.length; index += 1) {
4121
+ next ^= text.charCodeAt(index);
4122
+ next = Math.imul(next, FNV_PRIME);
4123
+ }
4124
+ return next >>> 0;
4125
+ }
4126
+ function fnv1aAppendString(hash, value) {
4127
+ if (value.length <= LONG_STRING_THRESHOLD) {
4128
+ return fnv1aAppend(fnv1aAppend(hash, `${value.length}:`), value);
4129
+ }
4130
+ const head = value.slice(0, LONG_STRING_SAMPLE_LENGTH);
4131
+ const middleIndex = Math.floor(value.length / 2);
4132
+ const middle = value.slice(
4133
+ middleIndex,
4134
+ middleIndex + LONG_STRING_SAMPLE_LENGTH
4135
+ );
4136
+ const tail = value.slice(value.length - LONG_STRING_SAMPLE_LENGTH);
4137
+ let next = fnv1aAppend(hash, `${value.length}:`);
4138
+ next = fnv1aAppend(next, head);
4139
+ next = fnv1aAppend(next, middle);
4140
+ return fnv1aAppend(next, tail);
4141
+ }
4142
+ function fnv1aAppendValue(hash, value) {
4143
+ if (value === null) {
4144
+ return fnv1aAppend(hash, "~n");
4145
+ }
4146
+ switch (typeof value) {
4147
+ case "undefined":
4148
+ return fnv1aAppend(hash, "~u");
4149
+ case "string":
4150
+ return fnv1aAppendString(fnv1aAppend(hash, "~s"), value);
4151
+ case "number":
4152
+ return fnv1aAppend(hash, `~#${value}`);
4153
+ case "boolean":
4154
+ return fnv1aAppend(hash, value ? "~t" : "~f");
4155
+ case "object":
4156
+ break;
4157
+ default:
4158
+ return fnv1aAppend(hash, "~x");
4159
+ }
4160
+ if (Array.isArray(value)) {
4161
+ let next2 = fnv1aAppend(hash, `~a${value.length}`);
4162
+ for (const entry of value) {
4163
+ next2 = fnv1aAppendValue(next2, entry);
4164
+ }
4165
+ return next2;
4166
+ }
4167
+ if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
4168
+ const byteLength = value instanceof ArrayBuffer ? value.byteLength : value.byteLength;
4169
+ return fnv1aAppend(hash, `~b${byteLength}`);
4170
+ }
4171
+ let next = fnv1aAppend(hash, "~o");
4172
+ for (const key of Object.keys(value)) {
4173
+ const entry = value[key];
4174
+ if (entry === void 0) {
4175
+ continue;
4176
+ }
4177
+ next = fnv1aAppend(next, key);
4178
+ next = fnv1aAppendValue(next, entry);
4179
+ }
4180
+ return next;
4181
+ }
4182
+ function contentHash(value) {
4183
+ return fnv1aAppendValue(FNV_OFFSET_BASIS, value).toString(36);
4184
+ }
4185
+ var nodeSignatureCache = /* @__PURE__ */ new WeakMap();
4186
+ function docNodeContentSignature(node) {
4187
+ if (typeof node !== "object" || node === null) {
4188
+ return contentHash(node);
4189
+ }
4190
+ const cached = nodeSignatureCache.get(node);
4191
+ if (cached !== void 0) {
4192
+ return cached;
4193
+ }
4194
+ const signature = contentHash(node);
4195
+ nodeSignatureCache.set(node, signature);
4196
+ return signature;
4197
+ }
4198
+ var metadataSignatureCache = /* @__PURE__ */ new WeakMap();
4199
+ function docModelThumbnailMetadataSignature(metadata) {
4200
+ const cached = metadataSignatureCache.get(metadata);
4201
+ if (cached !== void 0) {
4202
+ return cached;
4203
+ }
4204
+ const relevant = metadata;
4205
+ let hash = FNV_OFFSET_BASIS;
4206
+ hash = fnv1aAppendValue(hash, relevant.sections);
4207
+ hash = fnv1aAppendValue(hash, relevant.headerSections);
4208
+ hash = fnv1aAppendValue(hash, relevant.footerSections);
4209
+ hash = fnv1aAppendValue(hash, relevant.numberingDefinitions);
4210
+ hash = fnv1aAppendValue(hash, relevant.footnotes);
4211
+ hash = fnv1aAppendValue(hash, relevant.endnotes);
4212
+ hash = fnv1aAppendValue(hash, relevant.documentBackgroundColor);
4213
+ hash = fnv1aAppendValue(hash, relevant.compatibility);
4214
+ const signature = (hash >>> 0).toString(36);
4215
+ metadataSignatureCache.set(metadata, signature);
4216
+ return signature;
4217
+ }
4218
+
4219
+ // src/thumbnail-raster.ts
4220
+ init_cjs_shims();
4221
+ var DOCX_THUMBNAIL_EXCLUDE_ATTRIBUTE = "data-docx-thumbnail-exclude";
4222
+ var THUMBNAIL_EXCLUDED_CLONE_SELECTOR = [
4223
+ `[${DOCX_THUMBNAIL_EXCLUDE_ATTRIBUTE}="true"]`,
4224
+ "textarea",
4225
+ '[data-image-resize-handle="true"]',
4226
+ '[data-docx-table-move-handle="true"]'
4227
+ ].join(",");
4228
+ var THUMBNAIL_IMAGE_DOWNSCALE_MIN_DATA_URI_LENGTH = 32768;
4229
+ var THUMBNAIL_IMAGE_DOWNSCALE_MAX_DIMENSION_PX = 512;
4230
+ var THUMBNAIL_IMAGE_JPEG_QUALITY = 0.78;
4231
+ function thumbnailSvgDataUri(svg) {
4232
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
4233
+ }
4234
+ function thumbnailImageSourceQualifiesForDownscale(src) {
4235
+ return src.length >= THUMBNAIL_IMAGE_DOWNSCALE_MIN_DATA_URI_LENGTH && src.startsWith("data:image/") && !src.startsWith("data:image/svg");
4236
+ }
4237
+ async function loadThumbnailImage(src) {
4238
+ const image = new Image();
4239
+ image.decoding = "async";
4240
+ const loaded = new Promise((resolve, reject) => {
4241
+ image.onload = () => resolve(image);
4242
+ image.onerror = () => {
4243
+ reject(new Error("Failed to decode DOCX thumbnail image."));
4244
+ };
4245
+ });
4246
+ image.src = src;
4247
+ if (typeof image.decode === "function") {
4248
+ try {
4249
+ await image.decode();
4250
+ return image;
4251
+ } catch {
4252
+ }
4253
+ }
4254
+ return loaded;
4255
+ }
4256
+ var downscaledThumbnailImageCache = /* @__PURE__ */ new Map();
4257
+ async function downscaleThumbnailImageDataUri(src) {
4258
+ if (typeof document === "undefined") {
4259
+ return void 0;
4260
+ }
4261
+ const image = await loadThumbnailImage(src);
4262
+ const naturalWidth = image.naturalWidth || image.width;
4263
+ const naturalHeight = image.naturalHeight || image.height;
4264
+ if (!naturalWidth || !naturalHeight) {
4265
+ return void 0;
4266
+ }
4267
+ const scale = THUMBNAIL_IMAGE_DOWNSCALE_MAX_DIMENSION_PX / Math.max(naturalWidth, naturalHeight);
4268
+ if (scale >= 1) {
4269
+ return void 0;
4270
+ }
4271
+ const canvas = document.createElement("canvas");
4272
+ canvas.width = Math.max(1, Math.round(naturalWidth * scale));
4273
+ canvas.height = Math.max(1, Math.round(naturalHeight * scale));
4274
+ const context = canvas.getContext("2d");
4275
+ if (!context) {
4276
+ return void 0;
4277
+ }
4278
+ context.imageSmoothingEnabled = true;
4279
+ context.imageSmoothingQuality = "high";
4280
+ context.drawImage(image, 0, 0, canvas.width, canvas.height);
4281
+ const sourceIsJpeg = src.startsWith("data:image/jpeg") || src.startsWith("data:image/jpg");
4282
+ const downscaled = sourceIsJpeg ? canvas.toDataURL("image/jpeg", THUMBNAIL_IMAGE_JPEG_QUALITY) : canvas.toDataURL("image/png");
4283
+ return downscaled.length < src.length ? downscaled : void 0;
4284
+ }
4285
+ function getDownscaledThumbnailImageDataUri(src) {
4286
+ const cached = downscaledThumbnailImageCache.get(src);
4287
+ if (cached) {
4288
+ return cached;
4289
+ }
4290
+ const pending = downscaleThumbnailImageDataUri(src).catch(() => void 0);
4291
+ downscaledThumbnailImageCache.set(src, pending);
4292
+ return pending;
4293
+ }
4294
+ async function buildDocxThumbnailSvgMarkup(params) {
4295
+ const { pageElement, sourceWidthPx, sourceHeightPx, widthPx, heightPx } = params;
4296
+ const clone = pageElement.cloneNode(true);
4297
+ clone.querySelectorAll(THUMBNAIL_EXCLUDED_CLONE_SELECTOR).forEach((excluded) => {
4298
+ excluded.remove();
4299
+ });
4300
+ const cloneImages = Array.from(clone.querySelectorAll("img"));
4301
+ await Promise.all(
4302
+ cloneImages.map(async (cloneImage) => {
4303
+ const src = cloneImage.getAttribute("src");
4304
+ if (!src || !thumbnailImageSourceQualifiesForDownscale(src)) {
4305
+ return;
4306
+ }
4307
+ const downscaled = await getDownscaledThumbnailImageDataUri(src);
4308
+ if (downscaled) {
4309
+ cloneImage.setAttribute("src", downscaled);
4310
+ }
4311
+ })
4312
+ );
4313
+ const scaleX = widthPx / sourceWidthPx;
4314
+ const scaleY = heightPx / sourceHeightPx;
4315
+ const serializedPage = new XMLSerializer().serializeToString(clone);
4316
+ return `
4317
+ <svg xmlns="http://www.w3.org/2000/svg" width="${widthPx}" height="${heightPx}" viewBox="0 0 ${widthPx} ${heightPx}">
4318
+ <foreignObject x="0" y="0" width="100%" height="100%">
4319
+ <div xmlns="http://www.w3.org/1999/xhtml" style="width:${widthPx}px;height:${heightPx}px;overflow:hidden;">
4320
+ <div style="width:${sourceWidthPx}px;height:${sourceHeightPx}px;transform-origin:top left;transform:scale(${scaleX}, ${scaleY});">
4321
+ ${serializedPage}
4322
+ </div>
4323
+ </div>
4324
+ </foreignObject>
4325
+ </svg>
4326
+ `;
4327
+ }
4328
+ async function rasterizeDocxThumbnailSurface(params) {
4329
+ if (typeof window === "undefined" || typeof XMLSerializer === "undefined") {
4330
+ throw new Error("DOCX thumbnails require a browser environment.");
4331
+ }
4332
+ const safeSourceWidthPx = Math.max(1, Math.round(params.sourceWidthPx));
4333
+ const safeSourceHeightPx = Math.max(1, Math.round(params.sourceHeightPx));
4334
+ const svgMarkup = await buildDocxThumbnailSvgMarkup({
4335
+ pageElement: params.pageElement,
4336
+ sourceWidthPx: safeSourceWidthPx,
4337
+ sourceHeightPx: safeSourceHeightPx,
4338
+ widthPx: params.widthPx,
4339
+ heightPx: params.heightPx
4340
+ });
4341
+ const image = await loadThumbnailImage(thumbnailSvgDataUri(svgMarkup));
4342
+ const surface = document.createElement("canvas");
4343
+ surface.width = Math.max(1, Math.round(params.pixelWidthPx));
4344
+ surface.height = Math.max(1, Math.round(params.pixelHeightPx));
4345
+ const context = surface.getContext("2d");
4346
+ if (!context) {
4347
+ throw new Error("2D canvas context is unavailable for DOCX thumbnails.");
4348
+ }
4349
+ context.imageSmoothingEnabled = true;
4350
+ context.imageSmoothingQuality = "high";
4351
+ context.drawImage(image, 0, 0, surface.width, surface.height);
4352
+ return surface;
4353
+ }
4354
+ function blitDocxThumbnailSurface(surface, canvas, resolution) {
4355
+ canvas.width = Math.max(1, Math.round(resolution.pixelWidthPx));
4356
+ canvas.height = Math.max(1, Math.round(resolution.pixelHeightPx));
4357
+ canvas.style.width = `${Math.max(1, Math.round(resolution.widthPx))}px`;
4358
+ canvas.style.height = `${Math.max(1, Math.round(resolution.heightPx))}px`;
4359
+ const context = canvas.getContext("2d");
4360
+ if (!context) {
4361
+ throw new Error("2D canvas context is unavailable for DOCX thumbnails.");
4362
+ }
4363
+ context.setTransform(1, 0, 0, 1, 0, 0);
4364
+ context.clearRect(0, 0, canvas.width, canvas.height);
4365
+ context.drawImage(surface, 0, 0, canvas.width, canvas.height);
4366
+ }
4367
+ var DocxThumbnailSurfaceCache = class {
4368
+ constructor(maxEntries) {
4369
+ this.maxEntries = maxEntries;
4370
+ }
4371
+ entries = /* @__PURE__ */ new Map();
4372
+ get size() {
4373
+ return this.entries.size;
4374
+ }
4375
+ get(key) {
4376
+ const value = this.entries.get(key);
4377
+ if (value === void 0) {
4378
+ return void 0;
4379
+ }
4380
+ this.entries.delete(key);
4381
+ this.entries.set(key, value);
4382
+ return value;
4383
+ }
4384
+ set(key, value) {
4385
+ this.entries.delete(key);
4386
+ this.entries.set(key, value);
4387
+ while (this.entries.size > this.maxEntries) {
4388
+ const oldestKey = this.entries.keys().next().value;
4389
+ if (oldestKey === void 0) {
4390
+ break;
4391
+ }
4392
+ this.entries.delete(oldestKey);
4393
+ }
4394
+ }
4395
+ clear() {
4396
+ this.entries.clear();
4397
+ }
4398
+ };
4399
+ var IDLE_TASK_TIMEOUT_MS = 300;
4400
+ function defaultScheduleTask(callback) {
4401
+ const idleWindow = typeof window === "undefined" ? void 0 : window;
4402
+ if (!idleWindow || typeof idleWindow.requestIdleCallback !== "function") {
4403
+ setTimeout(callback, 16);
4404
+ return;
4405
+ }
4406
+ let invoked = false;
4407
+ const runOnce = () => {
4408
+ if (invoked) {
4409
+ return;
4410
+ }
4411
+ invoked = true;
4412
+ callback();
4413
+ };
4414
+ const idleHandle = idleWindow.requestIdleCallback(runOnce, {
4415
+ timeout: IDLE_TASK_TIMEOUT_MS
4416
+ });
4417
+ setTimeout(() => {
4418
+ if (invoked) {
4419
+ return;
4420
+ }
4421
+ if (typeof idleWindow.cancelIdleCallback === "function") {
4422
+ idleWindow.cancelIdleCallback(idleHandle);
4423
+ }
4424
+ runOnce();
4425
+ }, IDLE_TASK_TIMEOUT_MS + 50);
4426
+ }
4427
+ function defaultScheduleDelayed(callback, delayMs) {
4428
+ setTimeout(callback, delayMs);
4429
+ }
4430
+ var SerialIdleTaskQueue = class {
4431
+ pending = [];
4432
+ lastRunAtByKey = /* @__PURE__ */ new Map();
4433
+ scheduleTask;
4434
+ scheduleDelayed;
4435
+ minTaskIntervalMs;
4436
+ now;
4437
+ pumpScheduled = false;
4438
+ running = false;
4439
+ constructor(options) {
4440
+ this.scheduleTask = options?.scheduleTask ?? defaultScheduleTask;
4441
+ this.scheduleDelayed = options?.scheduleDelayed ?? defaultScheduleDelayed;
4442
+ this.minTaskIntervalMs = Math.max(0, options?.minTaskIntervalMs ?? 0);
4443
+ this.now = options?.now ?? (() => Date.now());
4444
+ }
4445
+ get pendingCount() {
4446
+ return this.pending.length;
4447
+ }
4448
+ enqueue(key, run) {
4449
+ return new Promise((resolve) => {
4450
+ const existing = this.pending.find((entry) => entry.key === key);
4451
+ if (existing) {
4452
+ existing.run = run;
4453
+ existing.resolvers.push(resolve);
4454
+ } else {
4455
+ this.pending.push({ key, run, resolvers: [resolve] });
4456
+ }
4457
+ this.schedulePump();
4458
+ });
4459
+ }
4460
+ /** Drops all queued tasks, resolving their waiters without running them. */
4461
+ clear() {
4462
+ const dropped = this.pending.splice(0, this.pending.length);
4463
+ this.lastRunAtByKey.clear();
4464
+ dropped.forEach((entry) => {
4465
+ entry.resolvers.forEach((resolveEntry) => {
4466
+ resolveEntry();
4467
+ });
4468
+ });
4469
+ }
4470
+ schedulePump() {
4471
+ if (this.pumpScheduled || this.running || this.pending.length === 0) {
4472
+ return;
4473
+ }
4474
+ this.pumpScheduled = true;
4475
+ this.scheduleTask(() => {
4476
+ this.pumpScheduled = false;
4477
+ void this.runNext();
4478
+ });
4479
+ }
4480
+ takeNextEligibleEntry() {
4481
+ if (this.pending.length === 0) {
4482
+ return void 0;
4483
+ }
4484
+ const now = this.now();
4485
+ let earliestWaitMs;
4486
+ for (let index = 0; index < this.pending.length; index += 1) {
4487
+ const candidate = this.pending[index];
4488
+ if (!candidate) {
4489
+ continue;
4490
+ }
4491
+ const lastRunAt = this.lastRunAtByKey.get(candidate.key);
4492
+ const waitMs = lastRunAt === void 0 ? 0 : lastRunAt + this.minTaskIntervalMs - now;
4493
+ if (waitMs <= 0) {
4494
+ this.pending.splice(index, 1);
4495
+ return { entry: candidate };
4496
+ }
4497
+ earliestWaitMs = earliestWaitMs === void 0 ? waitMs : Math.min(earliestWaitMs, waitMs);
4498
+ }
4499
+ return earliestWaitMs === void 0 ? void 0 : { retryDelayMs: earliestWaitMs };
4500
+ }
4501
+ async runNext() {
4502
+ if (this.running) {
4503
+ return;
4504
+ }
4505
+ const next = this.takeNextEligibleEntry();
4506
+ if (!next) {
4507
+ return;
4508
+ }
4509
+ if (!("entry" in next)) {
4510
+ this.scheduleDelayed(() => {
4511
+ this.schedulePump();
4512
+ }, next.retryDelayMs);
4513
+ return;
4514
+ }
4515
+ this.running = true;
4516
+ const { entry } = next;
4517
+ try {
4518
+ await entry.run();
4519
+ } catch {
4520
+ } finally {
4521
+ this.lastRunAtByKey.set(entry.key, this.now());
4522
+ this.running = false;
4523
+ entry.resolvers.forEach((resolveEntry) => {
4524
+ resolveEntry();
4525
+ });
4526
+ this.schedulePump();
4527
+ }
4528
+ }
4529
+ };
4530
+
4025
4531
  // src/editor.tsx
4026
4532
  var import_jsx_runtime = require("react/jsx-runtime");
4027
4533
  var HIGHLIGHT_TO_CSS = {
@@ -4118,6 +4624,9 @@ var DEFAULT_PARAGRAPH_LINE_MULTIPLE = 1;
4118
4624
  var WORD_SINGLE_LINE_AUTO_SCALE = 0.88;
4119
4625
  var WORD_SINGLE_LINE_AUTO_SCALE_SANS = 0.9;
4120
4626
  var WORD_SINGLE_LINE_AUTO_SCALE_SERIF = 1.08;
4627
+ var WORD_EMPTY_PARAGRAPH_LINE_SCALE = 1.21;
4628
+ var WORD_EMPTY_PARAGRAPH_LINE_SCALE_SERIF = 1.15;
4629
+ var WORD_EMPTY_PARAGRAPH_LINE_SCALE_SANS = 1.15;
4121
4630
  var WORD_AUTO_LINE_SCALE_BLEND_END_MULTIPLE = 1.08;
4122
4631
  var MIN_AUTO_LINE_MULTIPLE = 0.1;
4123
4632
  var MIN_PARAGRAPH_LINE_HEIGHT_PX = 14;
@@ -4162,7 +4671,6 @@ var TEXT_MEASURE_CACHE_MAX_ENTRIES = 12e3;
4162
4671
  var DEFAULT_TAB_STOP_PX = 48;
4163
4672
  var TAB_LEADER_ZONE_GAP_PX = 20;
4164
4673
  var EMPTY_PARAGRAPH_EXTRA_HEIGHT_PX = 0;
4165
- var LEADING_COVER_SPACER_EXTRA_HEIGHT_PX = 2;
4166
4674
  var PARAGRAPH_SEGMENT_TOP_BLEED_PX = 22;
4167
4675
  var PARAGRAPH_SEGMENT_DESCENDER_BLEED_PX = 6;
4168
4676
  var PARAGRAPH_SEGMENT_VISUAL_SAFETY_PX = 24;
@@ -6682,19 +7190,6 @@ function floatingTextBoxVisibleTextFromImage(image) {
6682
7190
  const normalized = normalizeFloatingTextBoxComparisonText(text);
6683
7191
  return normalized.length > 0 ? normalized : void 0;
6684
7192
  }
6685
- function absoluteFloatingTextBearingTextBoxFootprintPx(image) {
6686
- const floating = image.floating;
6687
- if (!shouldRenderAbsoluteFloatingImage(image) || !floating || image.syntheticTextBox !== true || syntheticTextBoxContainsPictureLayer(image) || !floatingTextBoxVisibleTextFromImage(image)) {
6688
- return 0;
6689
- }
6690
- const imageHeightPx = Number.isFinite(image.heightPx) && image.heightPx > 0 ? Math.round(image.heightPx) : Number.isFinite(image.widthPx) && image.widthPx > 0 ? Math.round(image.widthPx) : MIN_PARAGRAPH_LINE_HEIGHT_PX;
6691
- const distTPx = Math.max(0, Math.round(floating.distTPx ?? 0));
6692
- const distBPx = Math.max(0, Math.round(floating.distBPx ?? 0));
6693
- return Math.max(
6694
- MIN_PARAGRAPH_LINE_HEIGHT_PX,
6695
- imageHeightPx + distTPx + distBPx
6696
- );
6697
- }
6698
7193
  function paragraphVisibleTextIsOnlyAbsoluteFloatingTextBoxContent(paragraph) {
6699
7194
  if (!paragraphHasVisibleText2(paragraph) || paragraphHasFormField2(paragraph)) {
6700
7195
  return false;
@@ -6727,38 +7222,9 @@ function paragraphHasOnlyWhitespaceText(paragraph) {
6727
7222
  return child.text.replace(/[\s\u00a0]+/g, "").length === 0;
6728
7223
  });
6729
7224
  }
6730
- function paragraphHasActiveNumbering(paragraph) {
6731
- const numbering = paragraph.style?.numbering;
6732
- return Boolean(
6733
- numbering && Number.isFinite(numbering.numId) && Math.round(numbering.numId) > 0
6734
- );
6735
- }
6736
7225
  function paragraphContainsSectionBreakProperties(paragraph) {
6737
7226
  return /<w:sectPr\b/i.test(paragraph.sourceXml ?? "");
6738
7227
  }
6739
- function paragraphTextBearingAbsoluteFloatingTextBoxFootprintPx(paragraph) {
6740
- if (paragraphHasVisibleText2(paragraph) || paragraphHasFormField2(paragraph) || paragraphContainsSectionBreakProperties(paragraph)) {
6741
- return 0;
6742
- }
6743
- return paragraph.children.reduce((largest, child) => {
6744
- if (child.type !== "image") {
6745
- return largest;
6746
- }
6747
- return Math.max(
6748
- largest,
6749
- absoluteFloatingTextBearingTextBoxFootprintPx(child)
6750
- );
6751
- }, 0);
6752
- }
6753
- function paragraphAbsoluteFloatingAnchorsDependOnParagraphFlow(paragraph) {
6754
- return paragraph.children.some((child) => {
6755
- if (child.type !== "image" || !shouldRenderAbsoluteFloatingImage(child) || child.syntheticTextBox !== true || !floatingTextBoxVisibleTextFromImage(child) || child.floating?.behindDocument !== true) {
6756
- return false;
6757
- }
6758
- const verticalRelativeTo = child.floating?.verticalRelativeTo?.trim().toLowerCase();
6759
- return verticalRelativeTo === void 0 || verticalRelativeTo === "" || verticalRelativeTo === "paragraph" || verticalRelativeTo === "line";
6760
- });
6761
- }
6762
7228
  function likelyFullPageCoverImageRelativeToContentBox(image, pageContentWidthPx, pageContentHeightPx) {
6763
7229
  if (!shouldRenderAbsoluteFloatingImage(image) || !image.floating) {
6764
7230
  return false;
@@ -6841,21 +7307,6 @@ function paragraphParticipatesInLeadingCoverLayout(model, nodeIndex, pageContent
6841
7307
  }
6842
7308
  return sawLikelyCoverArtAnchor;
6843
7309
  }
6844
- function paragraphLikelyFullPageCoverFootprintPx(model, nodeIndex, paragraph, pageContentWidthPx, pageContentHeightPx) {
6845
- if (!paragraphParticipatesInLeadingCoverLayout(
6846
- model,
6847
- nodeIndex,
6848
- pageContentWidthPx,
6849
- pageContentHeightPx
6850
- ) || !paragraphIsLikelyFullPageCoverArtAnchor(
6851
- paragraph,
6852
- pageContentWidthPx,
6853
- pageContentHeightPx
6854
- )) {
6855
- return 0;
6856
- }
6857
- return Math.max(1, Math.round(pageContentHeightPx));
6858
- }
6859
7310
  function fullPageCoverImageRenderKey(nodeIndex, childIndex) {
6860
7311
  return `${nodeIndex}:${childIndex}`;
6861
7312
  }
@@ -6894,71 +7345,6 @@ function fullPageCoverAbsoluteFloatingImageStyle(image, layout, options) {
6894
7345
  zIndex: floating?.behindDocument === true ? 0 : normalizedZIndex
6895
7346
  };
6896
7347
  }
6897
- function paragraphActsAsLeadingCoverLayoutSpacer(model, nodeIndex, paragraph, pageContentWidthPx, pageContentHeightPx) {
6898
- if (nodeIndex <= 0 || !paragraphHasOnlyWhitespaceText(paragraph) || paragraphHasExplicitPageBreak2(paragraph)) {
6899
- return false;
6900
- }
6901
- if (paragraphHasActiveNumbering(paragraph)) {
6902
- return false;
6903
- }
6904
- return paragraphParticipatesInLeadingCoverLayout(
6905
- model,
6906
- nodeIndex,
6907
- pageContentWidthPx,
6908
- pageContentHeightPx
6909
- );
6910
- }
6911
- function paragraphActsAsLeadingCoverLayoutPreambleSpacer(model, nodeIndex, paragraph, pageContentWidthPx, pageContentHeightPx) {
6912
- if (!paragraphHasOnlyWhitespaceText(paragraph) || paragraphHasExplicitPageBreak2(paragraph) || paragraphHasActiveNumbering(paragraph)) {
6913
- return false;
6914
- }
6915
- const nextNode = model.nodes[nodeIndex + 1];
6916
- if (!nextNode || nextNode.type !== "paragraph") {
6917
- return false;
6918
- }
6919
- if (!paragraphParticipatesInLeadingCoverLayout(
6920
- model,
6921
- nodeIndex + 1,
6922
- pageContentWidthPx,
6923
- pageContentHeightPx
6924
- )) {
6925
- return false;
6926
- }
6927
- for (let probeIndex = nodeIndex - 1; probeIndex >= 0; probeIndex -= 1) {
6928
- const previousNode = model.nodes[probeIndex];
6929
- if (!previousNode || previousNode.type !== "paragraph") {
6930
- return false;
6931
- }
6932
- if (paragraphHasOnlyWhitespaceText(previousNode)) {
6933
- continue;
6934
- }
6935
- if (paragraphHasAbsoluteFloatingImage(previousNode)) {
6936
- return false;
6937
- }
6938
- return !paragraphIsLikelyFullPageCoverArtAnchor(
6939
- previousNode,
6940
- pageContentWidthPx,
6941
- pageContentHeightPx
6942
- );
6943
- }
6944
- return true;
6945
- }
6946
- function paragraphStartsNewPageAfterLeadingCoverLayout(model, nodeIndex, paragraph, pageContentWidthPx, pageContentHeightPx) {
6947
- if (nodeIndex <= 0 || !paragraphStartsNormalFlowContent(paragraph) || paragraphHasExplicitPageBreak2(paragraph) || paragraphHasPageBreakBefore2(paragraph)) {
6948
- return false;
6949
- }
6950
- const previousNode = model.nodes[nodeIndex - 1];
6951
- if (!previousNode || previousNode.type !== "paragraph") {
6952
- return false;
6953
- }
6954
- return paragraphActsAsLeadingCoverLayoutSpacer(
6955
- model,
6956
- nodeIndex - 1,
6957
- previousNode,
6958
- pageContentWidthPx,
6959
- pageContentHeightPx
6960
- );
6961
- }
6962
7348
  function paragraphLooksLikeCheckboxChoiceRow(paragraph) {
6963
7349
  if (paragraph.children.some((child) => child.type === "image")) {
6964
7350
  return false;
@@ -7476,7 +7862,8 @@ function buildParagraphPretextLayoutSource(paragraph, options) {
7476
7862
  let combinedText = "";
7477
7863
  const paragraphBaseFontPx = paragraphBaseFontSizePx(paragraph);
7478
7864
  const tabStopPositionsPx = options?.expandTabsForLayout ? resolveParagraphTabStopsPx(paragraph) : [];
7479
- const fallbackTabWidthPx = paragraphLooksLikeCheckboxChoiceRow(paragraph) ? checkboxChoiceRowTabWidthPx(paragraph) : DEFAULT_TAB_STOP_PX;
7865
+ const usesCheckboxRowTabFallback = paragraphLooksLikeCheckboxChoiceRow(paragraph);
7866
+ const fallbackTabWidthPx = usesCheckboxRowTabFallback ? checkboxChoiceRowTabWidthPx(paragraph) : DEFAULT_TAB_STOP_PX;
7480
7867
  let approximateLineWidthPx = 0;
7481
7868
  let lastTextStyle = firstRunStyle(paragraph);
7482
7869
  for (let childIndex = 0; childIndex < paragraph.children.length; childIndex += 1) {
@@ -7542,7 +7929,8 @@ function buildParagraphPretextLayoutSource(paragraph, options) {
7542
7929
  const tabWidthPx = resolveTabSpacerWidthPx(
7543
7930
  tabStopPositionsPx,
7544
7931
  approximateLineWidthPx,
7545
- fallbackTabWidthPx
7932
+ fallbackTabWidthPx,
7933
+ usesCheckboxRowTabFallback
7546
7934
  );
7547
7935
  const spacerText = buildParagraphPretextTabSpacerText(
7548
7936
  tabWidthPx,
@@ -8291,10 +8679,14 @@ function updateEstimatedLineWidthPxForText(currentLineWidthPx, text, style) {
8291
8679
  const trailingSegment = segments[segments.length - 1] ?? "";
8292
8680
  return estimateTextAdvanceWidthPx(trailingSegment, style);
8293
8681
  }
8294
- function resolveTabSpacerWidthPx(tabStopPositionsPx, currentLineWidthPx, fallbackWidthPx) {
8682
+ function resolveTabSpacerWidthPx(tabStopPositionsPx, currentLineWidthPx, fallbackWidthPx, fixedFallback = false) {
8295
8683
  const safeFallback = Math.max(12, Math.round(fallbackWidthPx));
8296
8684
  if (tabStopPositionsPx.length === 0) {
8297
- return safeFallback;
8685
+ if (fixedFallback) {
8686
+ return safeFallback;
8687
+ }
8688
+ const nextStop2 = (Math.floor((currentLineWidthPx + 0.5) / safeFallback) + 1) * safeFallback;
8689
+ return Math.max(2, Math.round(nextStop2 - currentLineWidthPx));
8298
8690
  }
8299
8691
  const nextStop = tabStopPositionsPx.find(
8300
8692
  (stop) => stop > currentLineWidthPx + 0.5
@@ -9047,7 +9439,26 @@ function singleLineAutoScaleForFontFamily(fontFamily) {
9047
9439
  }
9048
9440
  return WORD_SINGLE_LINE_AUTO_SCALE;
9049
9441
  }
9442
+ function emptyParagraphLineScaleForFontFamily(fontFamily) {
9443
+ const normalized = normalizeFontFamilyToken(fontFamily) ?? fontFamily?.toLowerCase();
9444
+ if (!normalized) {
9445
+ return WORD_EMPTY_PARAGRAPH_LINE_SCALE;
9446
+ }
9447
+ if (normalized === "times roman" || normalized === "times new roman" || normalized === "cambria" || normalized === "garamond" || normalized === "georgia" || normalized === "book antiqua" || normalized === "palatino linotype") {
9448
+ return WORD_EMPTY_PARAGRAPH_LINE_SCALE_SERIF;
9449
+ }
9450
+ if (normalized === "arial") {
9451
+ return WORD_EMPTY_PARAGRAPH_LINE_SCALE_SANS;
9452
+ }
9453
+ return WORD_EMPTY_PARAGRAPH_LINE_SCALE;
9454
+ }
9455
+ function paragraphRendersTextFreeLine(paragraph) {
9456
+ return paragraphHasOnlyWhitespaceText(paragraph) || paragraphIsFloatingImageAnchorOnly(paragraph);
9457
+ }
9050
9458
  function resolveParagraphSingleLineAutoScale(paragraph, fontFamily) {
9459
+ if (paragraphRendersTextFreeLine(paragraph)) {
9460
+ return emptyParagraphLineScaleForFontFamily(fontFamily);
9461
+ }
9051
9462
  const baseScale = singleLineAutoScaleForFontFamily(fontFamily);
9052
9463
  return paragraphHasCheckboxFormField(paragraph) ? Math.max(1.08, baseScale) : baseScale;
9053
9464
  }
@@ -9762,36 +10173,6 @@ function resolveMaxPretextLineRangeEndIndexThatFits(layout, startLineIndex, maxE
9762
10173
  }
9763
10174
  return bestEnd;
9764
10175
  }
9765
- function estimateAbsoluteFloatingImageFootprintPx(paragraph, image) {
9766
- if (!shouldRenderAbsoluteFloatingImage(image)) {
9767
- return 0;
9768
- }
9769
- const floating = image.floating;
9770
- if (!floating) {
9771
- return 0;
9772
- }
9773
- const wrapType = (floating.wrapType ?? "none").trim().toLowerCase();
9774
- if (wrapType !== "none") {
9775
- return 0;
9776
- }
9777
- const behavesAsTextBearingFloatingTextBox = image.syntheticTextBox && !syntheticTextBoxContainsPictureLayer(image) && Boolean(floatingTextBoxVisibleTextFromImage(image));
9778
- if (behavesAsTextBearingFloatingTextBox) {
9779
- if (paragraphHasVisibleText2(paragraph) || paragraphHasFormField2(paragraph) || paragraphContainsSectionBreakProperties(paragraph)) {
9780
- return 0;
9781
- }
9782
- const imageHeightPx = Number.isFinite(image.heightPx) && image.heightPx > 0 ? Math.round(image.heightPx) : Number.isFinite(image.widthPx) && image.widthPx > 0 ? Math.round(image.widthPx) : MIN_PARAGRAPH_LINE_HEIGHT_PX;
9783
- const distTPx = Math.max(0, Math.round(floating.distTPx ?? 0));
9784
- const distBPx = Math.max(0, Math.round(floating.distBPx ?? 0));
9785
- return Math.max(
9786
- MIN_PARAGRAPH_LINE_HEIGHT_PX,
9787
- imageHeightPx + distTPx + distBPx
9788
- );
9789
- }
9790
- if (floating.behindDocument) {
9791
- return 0;
9792
- }
9793
- return 0;
9794
- }
9795
10176
  function resolveAutoLineSpacingMultiple(lineTwips, fallbackMultiple) {
9796
10177
  if (!Number.isFinite(lineTwips)) {
9797
10178
  return Math.max(MIN_AUTO_LINE_MULTIPLE, fallbackMultiple);
@@ -9889,8 +10270,15 @@ function estimateParagraphLineHeightPx(paragraph, docGridLinePitchPx, disableDoc
9889
10270
  docGridMinimumLineHeightPx ?? 0
9890
10271
  );
9891
10272
  }
9892
- const multiple = calibrateAutoLineSpacingMultiple(
9893
- resolveAutoLineSpacingMultiple(lineTwips, defaultLineMultiple),
10273
+ const resolvedAutoMultiple = resolveAutoLineSpacingMultiple(
10274
+ lineTwips,
10275
+ defaultLineMultiple
10276
+ );
10277
+ const multiple = paragraphRendersTextFreeLine(paragraph) ? Math.max(
10278
+ MIN_AUTO_LINE_MULTIPLE,
10279
+ Number((resolvedAutoMultiple * singleLineScale).toFixed(3))
10280
+ ) : calibrateAutoLineSpacingMultiple(
10281
+ resolvedAutoMultiple,
9894
10282
  baseFontFamily,
9895
10283
  singleLineScale
9896
10284
  );
@@ -9945,10 +10333,7 @@ function estimateParagraphHeightPx(paragraph, availableWidthPx, numberingDefinit
9945
10333
  numberingLabel
9946
10334
  );
9947
10335
  const absoluteFloatingAnchorOnlyParagraph = paragraphIsAbsoluteFloatingImageAnchorOnly(paragraph);
9948
- const sectionBreakAnchorCarryoverParagraph = paragraphIsSectionBreakAnchorCarryover(paragraph);
9949
- const collapsibleAbsoluteFloatingAnchorOnlyParagraph = absoluteFloatingAnchorOnlyParagraph && (!paragraphAbsoluteFloatingAnchorsDependOnParagraphFlow(paragraph) || sectionBreakAnchorCarryoverParagraph);
9950
- const paragraphFlowAnchoredAbsoluteFloatingAnchorOnlyParagraph = absoluteFloatingAnchorOnlyParagraph && !collapsibleAbsoluteFloatingAnchorOnlyParagraph;
9951
- const decorativeBehindTextAnchorOnlyParagraph = paragraphActsAsDecorativeBehindTextBackgroundOverlay(paragraph);
10336
+ const collapsibleAbsoluteFloatingAnchorOnlyParagraph = absoluteFloatingAnchorOnlyParagraph && paragraphIsSectionBreakAnchorCarryover(paragraph);
9952
10337
  const inlineImageHeightPx = paragraph.children.reduce((largest, child) => {
9953
10338
  if (child.type !== "image") {
9954
10339
  return largest;
@@ -9967,27 +10352,14 @@ function estimateParagraphHeightPx(paragraph, availableWidthPx, numberingDefinit
9967
10352
  estimateWrappedFloatingImageFootprintPx(paragraph, child)
9968
10353
  );
9969
10354
  }, 0);
9970
- const absoluteFloatingImageHeightPx = paragraph.children.reduce(
9971
- (largest, child) => {
9972
- if (child.type !== "image") {
9973
- return largest;
9974
- }
9975
- return Math.max(
9976
- largest,
9977
- estimateAbsoluteFloatingImageFootprintPx(paragraph, child)
9978
- );
9979
- },
9980
- 0
9981
- );
9982
- const effectiveAbsoluteFloatingImageHeightPx = collapsibleAbsoluteFloatingAnchorOnlyParagraph || decorativeBehindTextAnchorOnlyParagraph ? 0 : absoluteFloatingImageHeightPx;
9983
- const emptyParagraphHeightPx = decorativeBehindTextAnchorOnlyParagraph ? 0 : paragraphIsEffectivelyEmpty(paragraph) ? lineHeightPx + EMPTY_PARAGRAPH_EXTRA_HEIGHT_PX : 0;
10355
+ const emptyParagraphHeightPx = paragraphIsEffectivelyEmpty(paragraph) ? lineHeightPx + EMPTY_PARAGRAPH_EXTRA_HEIGHT_PX : 0;
9984
10356
  const topBorderInsetPx = paragraphBorderInsetPx(
9985
10357
  paragraph.style?.borders?.top
9986
10358
  );
9987
10359
  const bottomBorderInsetPx = paragraphBorderInsetPx(
9988
10360
  paragraph.style?.borders?.bottom
9989
10361
  );
9990
- const textFlowHeightPx = collapsibleAbsoluteFloatingAnchorOnlyParagraph ? 0 : paragraphFlowAnchoredAbsoluteFloatingAnchorOnlyParagraph ? MIN_PARAGRAPH_LINE_HEIGHT_PX : (
10362
+ const textFlowHeightPx = collapsibleAbsoluteFloatingAnchorOnlyParagraph ? 0 : (
9991
10363
  // When excluding the wrapped-float footprint, the dual-wrapped block
9992
10364
  // height spans the image; but the rendered paragraph only occupies its
9993
10365
  // text lines while the float overhangs. Use the text-line height so the
@@ -9995,11 +10367,10 @@ function estimateParagraphHeightPx(paragraph, availableWidthPx, numberingDefinit
9995
10367
  dualWrappedLayout && !excludeWrappedFloatingImageFootprint ? wrappedPretextParagraphBlockHeightPx(dualWrappedLayout.layout) : lineHeightPx * lineCount
9996
10368
  );
9997
10369
  const contentHeightPx = Math.max(
9998
- collapsibleAbsoluteFloatingAnchorOnlyParagraph || decorativeBehindTextAnchorOnlyParagraph ? 0 : paragraphFlowAnchoredAbsoluteFloatingAnchorOnlyParagraph ? MIN_PARAGRAPH_LINE_HEIGHT_PX : lineHeightPx,
10370
+ collapsibleAbsoluteFloatingAnchorOnlyParagraph ? 0 : lineHeightPx,
9999
10371
  textFlowHeightPx,
10000
10372
  inlineImageHeightPx,
10001
10373
  wrappedFloatingImageHeightPx,
10002
- effectiveAbsoluteFloatingImageHeightPx,
10003
10374
  emptyParagraphHeightPx
10004
10375
  );
10005
10376
  if (excludeWrappedFloatingImageFootprint && paragraph.children.some((c) => c.type === "image")) {
@@ -11260,6 +11631,7 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
11260
11631
  let previousParagraphAfterPx = 0;
11261
11632
  let currentMetricsIndex = 0;
11262
11633
  let currentSectionPageFlowOriginPx = 0;
11634
+ let committedKeepNextChainEndNodeIndex = -1;
11263
11635
  let currentPageContentHeightPx = resolveMetricsPageContentHeightPx(
11264
11636
  0,
11265
11637
  metricsBySection[0]
@@ -11318,56 +11690,14 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
11318
11690
  previousParagraphAfterPx = 0;
11319
11691
  continue;
11320
11692
  }
11321
- if (node.type === "paragraph" && paragraphActsAsLeadingCoverLayoutPreambleSpacer(
11322
- model,
11323
- nodeIndex,
11324
- node,
11325
- nodeMetrics.pageContentWidthPx,
11326
- nodeMetrics.pageContentHeightPx
11327
- )) {
11328
- previousParagraphAfterPx = 0;
11329
- continue;
11330
- }
11331
11693
  if (node.type === "paragraph" && paragraphActsAsTrailingRenderedPageBreakSpacer(model, nodeIndex, node)) {
11332
11694
  previousParagraphAfterPx = 0;
11333
11695
  continue;
11334
11696
  }
11335
- if (node.type === "paragraph" && paragraphActsAsLeadingCoverLayoutOverlay(
11336
- model,
11337
- nodeIndex,
11338
- node,
11339
- nodeMetrics.pageContentWidthPx,
11340
- nodeMetrics.pageContentHeightPx
11341
- )) {
11342
- currentPageSegments.push({ nodeIndex });
11343
- previousParagraphAfterPx = 0;
11344
- continue;
11345
- }
11346
- if (node.type === "paragraph" && paragraphActsAsDecorativeBehindTextBackgroundOverlay(node)) {
11347
- currentPageSegments.push({ nodeIndex });
11348
- previousParagraphAfterPx = 0;
11349
- continue;
11350
- }
11351
11697
  if (node.type === "paragraph" && paragraphCollapsesIntoPreviousParagraph(node, model.nodes[nodeIndex - 1])) {
11352
11698
  continue;
11353
11699
  }
11354
11700
  if (node.type === "paragraph") {
11355
- if (paragraphStartsNewPageAfterLeadingCoverLayout(
11356
- model,
11357
- nodeIndex,
11358
- node,
11359
- nodeMetrics.pageContentWidthPx,
11360
- nodeMetrics.pageContentHeightPx
11361
- ) && currentPageSegments.length > 0) {
11362
- startNextPage();
11363
- pageConsumedHeightPx = 0;
11364
- previousParagraphAfterPx = 0;
11365
- currentSectionPageFlowOriginPx = 0;
11366
- currentPageContentHeightPx = resolveMetricsPageContentHeightPx(
11367
- currentPageIndex,
11368
- nodeMetrics
11369
- );
11370
- }
11371
11701
  if (paragraphHasPageBreakBefore2(node) && currentPageSegments.length > 0) {
11372
11702
  startNextPage();
11373
11703
  pageConsumedHeightPx = 0;
@@ -11451,18 +11781,12 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
11451
11781
  1,
11452
11782
  estimatedOrMeasuredHeightPx - directBeforeSpacingPx - directAfterSpacingPx + beforeSpacingPx + afterSpacingPx
11453
11783
  );
11454
- const coverFootprintPx = paragraphLikelyFullPageCoverFootprintPx(
11455
- model,
11456
- nodeIndex,
11457
- node,
11458
- nodeMetrics.pageContentWidthPx,
11459
- nodeMetrics.pageContentHeightPx
11460
- );
11461
- const paragraphTooTallForSinglePage = Math.max(rawNodeHeightPx, coverFootprintPx) > nodeMetrics.pageContentHeightPx + PAGE_OVERFLOW_TOLERANCE_PX;
11784
+ const paragraphTooTallForSinglePage = rawNodeHeightPx > nodeMetrics.pageContentHeightPx + PAGE_OVERFLOW_TOLERANCE_PX;
11462
11785
  const keepLinesOverflowSplit = node.style?.keepLines === true && paragraphTooTallForSinglePage;
11463
11786
  const keepNextOverflowSplit = node.style?.keepNext === true && paragraphTooTallForSinglePage;
11464
11787
  const forceOverflowSplit = keepLinesOverflowSplit || keepNextOverflowSplit;
11465
- if (forceOverflowSplit && pageConsumedHeightPx > 0 && currentPageSegments.length > 0) {
11788
+ const nodeIsWithinCommittedKeepNextChain = nodeIndex <= committedKeepNextChainEndNodeIndex;
11789
+ if (forceOverflowSplit && !nodeIsWithinCommittedKeepNextChain && pageConsumedHeightPx > 0 && currentPageSegments.length > 0) {
11466
11790
  startNextPage();
11467
11791
  pageConsumedHeightPx = 0;
11468
11792
  previousParagraphAfterPx = 0;
@@ -11475,7 +11799,7 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
11475
11799
  const collapsedMarginPx = pageConsumedHeightPx > 0 ? Math.min(previousParagraphAfterPx, beforeSpacingPx) : 0;
11476
11800
  const collapsedNodeHeightPx = Math.max(
11477
11801
  1,
11478
- Math.max(rawNodeHeightPx, coverFootprintPx) - collapsedMarginPx
11802
+ rawNodeHeightPx - collapsedMarginPx
11479
11803
  );
11480
11804
  const paragraphSupportsPretextSegmentRendering = Boolean(
11481
11805
  paragraphPretextSourceForSegmentRendering
@@ -11501,7 +11825,7 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
11501
11825
  }
11502
11826
  const collapsedNodeHeightPxAdjusted = Math.max(
11503
11827
  1,
11504
- Math.max(rawNodeHeightPx, coverFootprintPx) - collapsedMarginPx
11828
+ rawNodeHeightPx - collapsedMarginPx
11505
11829
  );
11506
11830
  const paragraphPretextLineCount = paragraphContainsExplicitLineBreakText(node) || paragraphContainsTabCharacter(node) ? resolveParagraphPretextLayoutForSegmentRendering()?.lineCount : void 0;
11507
11831
  const supportsImageParagraphLineSplit = paragraphHasImage2(node) && paragraphSupportsPretextSegmentRendering;
@@ -11725,7 +12049,8 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
11725
12049
  continue;
11726
12050
  }
11727
12051
  let requiredHeightPx = collapsedNodeHeightPxAdjusted;
11728
- if (node.style?.keepNext === true && paragraphHasVisibleText2(node)) {
12052
+ let keepNextChainEndNodeIndex = -1;
12053
+ if (node.style?.keepNext === true && !nodeIsWithinCommittedKeepNextChain && paragraphHasVisibleText2(node)) {
11729
12054
  let chainCursor = nodeIndex;
11730
12055
  let chainPreviousParagraphAfterPx = afterSpacingPx;
11731
12056
  while (chainCursor < model.nodes.length - 1) {
@@ -11785,6 +12110,7 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
11785
12110
  );
11786
12111
  chainPreviousParagraphAfterPx = nextAfterSpacingPx;
11787
12112
  }
12113
+ keepNextChainEndNodeIndex = chainCursor;
11788
12114
  }
11789
12115
  const remainingHeightPx = currentPageContentHeightPx - pageConsumedHeightPx;
11790
12116
  const canKeepTrailingSectionTailOnCurrentPage = shouldKeepTrailingSectionTailOnCurrentPage(
@@ -11826,11 +12152,11 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
11826
12152
  nodeMetrics
11827
12153
  );
11828
12154
  }
12155
+ if (pageConsumedHeightPx === 0 && keepNextChainEndNodeIndex > nodeIndex) {
12156
+ committedKeepNextChainEndNodeIndex = keepNextChainEndNodeIndex;
12157
+ }
11829
12158
  currentPageSegments.push({ nodeIndex });
11830
- const effectiveNodeHeightPx = Math.max(
11831
- pageConsumedHeightPx > 0 ? collapsedNodeHeightPx : rawNodeHeightPx,
11832
- coverFootprintPx
11833
- );
12159
+ const effectiveNodeHeightPx = pageConsumedHeightPx > 0 ? collapsedNodeHeightPx : rawNodeHeightPx;
11834
12160
  pageConsumedHeightPx += effectiveNodeHeightPx;
11835
12161
  previousParagraphAfterPx = afterSpacingPx;
11836
12162
  continue;
@@ -12637,11 +12963,15 @@ function paragraphLineHeight(paragraph, docGridLinePitchPx, disableDocGridSnap =
12637
12963
  disableDocGridSnap
12638
12964
  )}px`;
12639
12965
  }
12640
- const lineMultiple = calibrateAutoLineSpacingMultiple(
12641
- resolveAutoLineSpacingMultiple(
12642
- lineTwips,
12643
- DEFAULT_PARAGRAPH_LINE_MULTIPLE
12644
- ),
12966
+ const resolvedAutoMultiple = resolveAutoLineSpacingMultiple(
12967
+ lineTwips,
12968
+ DEFAULT_PARAGRAPH_LINE_MULTIPLE
12969
+ );
12970
+ const lineMultiple = paragraphRendersTextFreeLine(paragraph) ? Math.max(
12971
+ MIN_AUTO_LINE_MULTIPLE,
12972
+ Number((resolvedAutoMultiple * singleLineScale).toFixed(3))
12973
+ ) : calibrateAutoLineSpacingMultiple(
12974
+ resolvedAutoMultiple,
12645
12975
  baseFontFamily,
12646
12976
  singleLineScale
12647
12977
  );
@@ -12984,9 +13314,7 @@ function paragraphBlockStyle(paragraph, numberingDefinitions, headingStyles, doc
12984
13314
  const suppressTocNumberingTextIndent = isTableOfContentsParagraph(paragraph) && paragraphHasNumbering(paragraph);
12985
13315
  const suppressIndentForFloatingAnchorOnlyParagraph = paragraphIsFloatingImageAnchorOnly(paragraph);
12986
13316
  const suppressStackingContextForBehindTextAnchorOnlyParagraph = paragraphIsBehindTextAbsoluteFloatingImageAnchorOnly(paragraph);
12987
- const suppressFlowFootprintForBehindTextAnchorOnlyParagraph = paragraphActsAsDecorativeBehindTextBackgroundOverlay(paragraph);
12988
- const textBearingAbsoluteFloatingTextBoxFootprintPx = paragraphTextBearingAbsoluteFloatingTextBoxFootprintPx(paragraph);
12989
- const reservedMinHeightPx = suppressFlowFootprintForBehindTextAnchorOnlyParagraph ? void 0 : textBearingAbsoluteFloatingTextBoxFootprintPx > 0 ? textBearingAbsoluteFloatingTextBoxFootprintPx : paragraphIsEffectivelyEmpty(paragraph) ? estimateParagraphLineHeightPx(
13317
+ const reservedMinHeightPx = paragraphIsEffectivelyEmpty(paragraph) ? estimateParagraphLineHeightPx(
12990
13318
  paragraph,
12991
13319
  docGridLinePitchPx,
12992
13320
  disableDocGridSnap
@@ -13005,15 +13333,13 @@ function paragraphBlockStyle(paragraph, numberingDefinitions, headingStyles, doc
13005
13333
  // line-box strut tracks the actual content instead of the browser's
13006
13334
  // 16px default, which inflates lines for sub-12pt paragraphs.
13007
13335
  fontSize: `${paragraphBaseFontSizePx(paragraph)}px`,
13008
- lineHeight: suppressFlowFootprintForBehindTextAnchorOnlyParagraph ? 0 : paragraphLineHeight(paragraph, docGridLinePitchPx, disableDocGridSnap),
13009
- ...suppressFlowFootprintForBehindTextAnchorOnlyParagraph ? {
13010
- height: 0,
13011
- marginTop: 0,
13012
- marginBottom: 0
13013
- } : {
13014
- marginTop: beforeSpacing,
13015
- marginBottom: afterSpacing
13016
- },
13336
+ lineHeight: paragraphLineHeight(
13337
+ paragraph,
13338
+ docGridLinePitchPx,
13339
+ disableDocGridSnap
13340
+ ),
13341
+ marginTop: beforeSpacing,
13342
+ marginBottom: afterSpacing,
13017
13343
  marginLeft: suppressIndentForFloatingAnchorOnlyParagraph ? 0 : leftIndent,
13018
13344
  marginRight: suppressIndentForFloatingAnchorOnlyParagraph ? 0 : rightIndent,
13019
13345
  backgroundColor: paragraph.style?.backgroundColor,
@@ -14744,7 +15070,8 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
14744
15070
  const resolveNextTabWidthPx = () => resolveTabSpacerWidthPx(
14745
15071
  tabStopPositionsPx,
14746
15072
  approximateLineWidthPx,
14747
- fallbackTabWidthPx
15073
+ fallbackTabWidthPx,
15074
+ checkboxChoiceRow
14748
15075
  );
14749
15076
  const appendPlainTextWithSoftBreakControl = (target, keySeed, text, style, measureStyle) => {
14750
15077
  const shouldControlSoftBreakStretch = paragraph.style?.align === "justify" && text.includes("\n");
@@ -22822,12 +23149,32 @@ function ensureDocxViewerPageSurfaceRegistry(editor) {
22822
23149
  if (!registry) {
22823
23150
  registry = {
22824
23151
  pageElements: /* @__PURE__ */ new Map(),
23152
+ pageContentKeys: /* @__PURE__ */ new Map(),
22825
23153
  listeners: /* @__PURE__ */ new Set()
22826
23154
  };
22827
23155
  docxViewerPageSurfaceRegistryByEditor.set(owner, registry);
22828
23156
  }
22829
23157
  return registry;
22830
23158
  }
23159
+ function syncDocxViewerPageSurfaceContentKeys(editor, contentKeysByPage) {
23160
+ const registry = ensureDocxViewerPageSurfaceRegistry(editor);
23161
+ let changed = false;
23162
+ contentKeysByPage.forEach((contentKey, pageIndex) => {
23163
+ if (registry.pageContentKeys.get(pageIndex) !== contentKey) {
23164
+ registry.pageContentKeys.set(pageIndex, contentKey);
23165
+ changed = true;
23166
+ }
23167
+ });
23168
+ registry.pageContentKeys.forEach((_, pageIndex) => {
23169
+ if (pageIndex >= contentKeysByPage.length) {
23170
+ registry.pageContentKeys.delete(pageIndex);
23171
+ changed = true;
23172
+ }
23173
+ });
23174
+ if (changed) {
23175
+ notifyDocxViewerPageSurfaceSubscribers(registry);
23176
+ }
23177
+ }
22831
23178
  function subscribeDocxViewerPageSurfaces(editor, listener) {
22832
23179
  const registry = ensureDocxViewerPageSurfaceRegistry(editor);
22833
23180
  registry.listeners.add(listener);
@@ -22891,62 +23238,8 @@ function resolveDocxViewerPageSurfaceSize(element, fallbackWidthPx, fallbackHeig
22891
23238
  heightPx: Math.max(1, Math.round(fallbackHeightPx))
22892
23239
  };
22893
23240
  }
22894
- async function rasterizeDocxViewerPageSurfaceToCanvas(params) {
22895
- if (typeof window === "undefined" || typeof XMLSerializer === "undefined") {
22896
- throw new Error("DOCX thumbnails require a browser environment.");
22897
- }
22898
- const {
22899
- pageElement,
22900
- sourceWidthPx,
22901
- sourceHeightPx,
22902
- canvas,
22903
- widthPx,
22904
- heightPx,
22905
- pixelWidthPx,
22906
- pixelHeightPx
22907
- } = params;
22908
- const safeSourceWidthPx = Math.max(1, Math.round(sourceWidthPx));
22909
- const safeSourceHeightPx = Math.max(1, Math.round(sourceHeightPx));
22910
- const scaleX = widthPx / safeSourceWidthPx;
22911
- const scaleY = heightPx / safeSourceHeightPx;
22912
- const serializedPage = new XMLSerializer().serializeToString(
22913
- pageElement.cloneNode(true)
22914
- );
22915
- const svgMarkup = `
22916
- <svg xmlns="http://www.w3.org/2000/svg" width="${widthPx}" height="${heightPx}" viewBox="0 0 ${widthPx} ${heightPx}">
22917
- <foreignObject x="0" y="0" width="100%" height="100%">
22918
- <div xmlns="http://www.w3.org/1999/xhtml" style="width:${widthPx}px;height:${heightPx}px;overflow:hidden;">
22919
- <div style="width:${safeSourceWidthPx}px;height:${safeSourceHeightPx}px;transform-origin:top left;transform:scale(${scaleX}, ${scaleY});">
22920
- ${serializedPage}
22921
- </div>
22922
- </div>
22923
- </foreignObject>
22924
- </svg>
22925
- `;
22926
- const svgDataUrl = svgDataUri(svgMarkup);
22927
- const image = await new Promise((resolve, reject) => {
22928
- const nextImage = new Image();
22929
- nextImage.decoding = "async";
22930
- nextImage.onload = () => resolve(nextImage);
22931
- nextImage.onerror = () => {
22932
- reject(new Error("Failed to rasterize DOCX page thumbnail."));
22933
- };
22934
- nextImage.src = svgDataUrl;
22935
- });
22936
- canvas.width = Math.max(1, Math.round(pixelWidthPx));
22937
- canvas.height = Math.max(1, Math.round(pixelHeightPx));
22938
- canvas.style.width = `${Math.max(1, Math.round(widthPx))}px`;
22939
- canvas.style.height = `${Math.max(1, Math.round(heightPx))}px`;
22940
- const context = canvas.getContext("2d");
22941
- if (!context) {
22942
- throw new Error("2D canvas context is unavailable for DOCX thumbnails.");
22943
- }
22944
- context.setTransform(1, 0, 0, 1, 0, 0);
22945
- context.clearRect(0, 0, canvas.width, canvas.height);
22946
- context.imageSmoothingEnabled = true;
22947
- context.imageSmoothingQuality = "high";
22948
- context.drawImage(image, 0, 0, canvas.width, canvas.height);
22949
- }
23241
+ var DOCX_THUMBNAIL_SURFACE_CACHE_MAX_ENTRIES = 32;
23242
+ var DOCX_THUMBNAIL_MIN_RASTER_INTERVAL_MS = 200;
22950
23243
  function resolveDocxPageThumbnailResolution(options) {
22951
23244
  const safeSourceWidthPx = Math.max(1, Math.round(options.sourceWidthPx));
22952
23245
  const safeSourceHeightPx = Math.max(1, Math.round(options.sourceHeightPx));
@@ -23020,8 +23313,51 @@ function useDocxPageThumbnails(editor, options = {}) {
23020
23313
  },
23021
23314
  []
23022
23315
  );
23316
+ const thumbnailSurfaceCacheRef = React.useRef(void 0);
23317
+ const thumbnailRasterQueueRef = React.useRef(void 0);
23318
+ const lastPaintedThumbnailKeyByCanvasRef = React.useRef(
23319
+ /* @__PURE__ */ new WeakMap()
23320
+ );
23321
+ const ensureThumbnailSurfaceCache = React.useCallback(() => {
23322
+ if (!thumbnailSurfaceCacheRef.current) {
23323
+ thumbnailSurfaceCacheRef.current = new DocxThumbnailSurfaceCache(
23324
+ DOCX_THUMBNAIL_SURFACE_CACHE_MAX_ENTRIES
23325
+ );
23326
+ }
23327
+ return thumbnailSurfaceCacheRef.current;
23328
+ }, []);
23329
+ const ensureThumbnailRasterQueue = React.useCallback(() => {
23330
+ if (!thumbnailRasterQueueRef.current) {
23331
+ thumbnailRasterQueueRef.current = new SerialIdleTaskQueue({
23332
+ minTaskIntervalMs: DOCX_THUMBNAIL_MIN_RASTER_INTERVAL_MS
23333
+ });
23334
+ }
23335
+ return thumbnailRasterQueueRef.current;
23336
+ }, []);
23337
+ React.useEffect(() => {
23338
+ thumbnailSurfaceCacheRef.current?.clear();
23339
+ thumbnailRasterQueueRef.current?.clear();
23340
+ lastPaintedThumbnailKeyByCanvasRef.current = /* @__PURE__ */ new WeakMap();
23341
+ }, [editor.documentLoadNonce, pageSurfaceRegistryOwner]);
23342
+ const thumbnailResolutionOptionsKey = React.useMemo(() => {
23343
+ const bounds = options.resolution;
23344
+ const boundsKey = typeof bounds === "number" ? `n${bounds}` : bounds ? `b${bounds.maxWidth ?? ""}x${bounds.maxHeight ?? ""}` : "";
23345
+ return `${boundsKey}|${options.maxWidthPx ?? ""}|${options.maxHeightPx ?? ""}|${options.pixelRatio ?? ""}`;
23346
+ }, [
23347
+ options.maxHeightPx,
23348
+ options.maxWidthPx,
23349
+ options.pixelRatio,
23350
+ options.resolution
23351
+ ]);
23352
+ const thumbnailSkipKeyForPage = React.useCallback(
23353
+ (pageIndex) => {
23354
+ const contentKey = pageSurfaceRegistry.pageContentKeys.get(pageIndex);
23355
+ return contentKey === void 0 ? void 0 : `${contentKey}|${editor.documentTheme}|${thumbnailResolutionOptionsKey}`;
23356
+ },
23357
+ [editor.documentTheme, pageSurfaceRegistry, thumbnailResolutionOptionsKey]
23358
+ );
23023
23359
  const renderPageThumbnailToCanvas = React.useCallback(
23024
- async (pageIndex, canvas) => {
23360
+ async (pageIndex, canvas, renderOptions) => {
23025
23361
  if (options.disabled) {
23026
23362
  return;
23027
23363
  }
@@ -23029,63 +23365,101 @@ function useDocxPageThumbnails(editor, options = {}) {
23029
23365
  if (!targetCanvas) {
23030
23366
  return;
23031
23367
  }
23032
- const pageElement = mountedPageElements.get(pageIndex);
23368
+ const pageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
23033
23369
  if (!pageElement || !pageElement.isConnected) {
23034
23370
  updatePageThumbnailState(pageIndex, "unavailable");
23035
23371
  return;
23036
23372
  }
23037
- const sourceSize = resolveDocxViewerPageSurfaceSize(
23038
- pageElement,
23039
- fallbackLayout.pageWidthPx,
23040
- fallbackLayout.pageHeightPx
23041
- );
23042
- const resolution = resolveDocxPageThumbnailResolution({
23043
- sourceWidthPx: sourceSize.widthPx,
23044
- sourceHeightPx: sourceSize.heightPx,
23045
- resolution: options.resolution,
23046
- maxWidthPx: options.maxWidthPx,
23047
- maxHeightPx: options.maxHeightPx,
23048
- pixelRatio: options.pixelRatio
23049
- });
23373
+ const force = renderOptions?.force === true;
23374
+ const lastPaintedKey = lastPaintedThumbnailKeyByCanvasRef.current.get(targetCanvas);
23375
+ if (!force && lastPaintedKey !== void 0 && lastPaintedKey === thumbnailSkipKeyForPage(pageIndex)) {
23376
+ updatePageThumbnailState(pageIndex, "ready");
23377
+ return;
23378
+ }
23050
23379
  updatePageThumbnailState(pageIndex, "rendering");
23051
- try {
23052
- await rasterizeDocxViewerPageSurfaceToCanvas({
23053
- pageElement,
23380
+ await ensureThumbnailRasterQueue().enqueue(targetCanvas, async () => {
23381
+ const livePageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
23382
+ if (!livePageElement || !livePageElement.isConnected) {
23383
+ updatePageThumbnailState(pageIndex, "unavailable");
23384
+ return;
23385
+ }
23386
+ const runSkipKey = thumbnailSkipKeyForPage(pageIndex);
23387
+ const sourceSize = resolveDocxViewerPageSurfaceSize(
23388
+ livePageElement,
23389
+ fallbackLayout.pageWidthPx,
23390
+ fallbackLayout.pageHeightPx
23391
+ );
23392
+ const resolution = resolveDocxPageThumbnailResolution({
23054
23393
  sourceWidthPx: sourceSize.widthPx,
23055
23394
  sourceHeightPx: sourceSize.heightPx,
23056
- canvas: targetCanvas,
23057
- widthPx: resolution.widthPx,
23058
- heightPx: resolution.heightPx,
23059
- pixelWidthPx: resolution.pixelWidthPx,
23060
- pixelHeightPx: resolution.pixelHeightPx
23395
+ resolution: options.resolution,
23396
+ maxWidthPx: options.maxWidthPx,
23397
+ maxHeightPx: options.maxHeightPx,
23398
+ pixelRatio: options.pixelRatio
23061
23399
  });
23062
- updatePageThumbnailState(pageIndex, "ready");
23063
- } catch (error) {
23064
- updatePageThumbnailState(
23065
- pageIndex,
23066
- "error",
23067
- error instanceof Error ? error : new Error("Failed to render DOCX page thumbnail.")
23068
- );
23069
- }
23400
+ const surfaceKey = runSkipKey === void 0 ? void 0 : `${runSkipKey}|${sourceSize.widthPx}x${sourceSize.heightPx}|${resolution.pixelWidthPx}x${resolution.pixelHeightPx}`;
23401
+ const surfaceCache = ensureThumbnailSurfaceCache();
23402
+ try {
23403
+ let surface = !force && surfaceKey !== void 0 ? surfaceCache.get(surfaceKey) : void 0;
23404
+ if (!surface) {
23405
+ surface = await rasterizeDocxThumbnailSurface({
23406
+ pageElement: livePageElement,
23407
+ sourceWidthPx: sourceSize.widthPx,
23408
+ sourceHeightPx: sourceSize.heightPx,
23409
+ widthPx: resolution.widthPx,
23410
+ heightPx: resolution.heightPx,
23411
+ pixelWidthPx: resolution.pixelWidthPx,
23412
+ pixelHeightPx: resolution.pixelHeightPx
23413
+ });
23414
+ if (surfaceKey !== void 0) {
23415
+ surfaceCache.set(surfaceKey, surface);
23416
+ }
23417
+ }
23418
+ blitDocxThumbnailSurface(surface, targetCanvas, resolution);
23419
+ if (runSkipKey !== void 0) {
23420
+ lastPaintedThumbnailKeyByCanvasRef.current.set(
23421
+ targetCanvas,
23422
+ runSkipKey
23423
+ );
23424
+ }
23425
+ updatePageThumbnailState(pageIndex, "ready");
23426
+ } catch (error) {
23427
+ updatePageThumbnailState(
23428
+ pageIndex,
23429
+ "error",
23430
+ error instanceof Error ? error : new Error("Failed to render DOCX page thumbnail.")
23431
+ );
23432
+ }
23433
+ });
23070
23434
  },
23071
23435
  [
23436
+ ensureThumbnailRasterQueue,
23437
+ ensureThumbnailSurfaceCache,
23072
23438
  fallbackLayout.pageHeightPx,
23073
23439
  fallbackLayout.pageWidthPx,
23074
- mountedPageElements,
23075
23440
  options.disabled,
23076
23441
  options.resolution,
23077
23442
  options.maxHeightPx,
23078
23443
  options.maxWidthPx,
23079
23444
  options.pixelRatio,
23445
+ pageSurfaceRegistry,
23446
+ thumbnailSkipKeyForPage,
23080
23447
  updatePageThumbnailState
23081
23448
  ]
23082
23449
  );
23083
- const rerenderAttachedThumbnails = React.useCallback(async () => {
23084
- const tasks = [...attachedCanvasByPageRef.current.keys()].map(
23085
- (pageIndex) => renderPageThumbnailToCanvas(pageIndex)
23086
- );
23087
- await Promise.all(tasks);
23088
- }, [renderPageThumbnailToCanvas]);
23450
+ const requestAttachedThumbnailRenders = React.useCallback(
23451
+ async (renderOptions) => {
23452
+ const tasks = [...attachedCanvasByPageRef.current.keys()].map(
23453
+ (pageIndex) => renderPageThumbnailToCanvas(pageIndex, void 0, renderOptions)
23454
+ );
23455
+ await Promise.all(tasks);
23456
+ },
23457
+ [renderPageThumbnailToCanvas]
23458
+ );
23459
+ const rerenderAttachedThumbnails = React.useCallback(
23460
+ async () => requestAttachedThumbnailRenders({ force: true }),
23461
+ [requestAttachedThumbnailRenders]
23462
+ );
23089
23463
  const renderPageThumbnailToCanvasRef = React.useRef(
23090
23464
  renderPageThumbnailToCanvas
23091
23465
  );
@@ -23097,7 +23471,7 @@ function useDocxPageThumbnails(editor, options = {}) {
23097
23471
  renderToCanvasCallbacksRef.current.clear();
23098
23472
  }, [pageSurfaceRegistryOwner]);
23099
23473
  React.useEffect(() => {
23100
- void rerenderAttachedThumbnails();
23474
+ void requestAttachedThumbnailRenders();
23101
23475
  }, [
23102
23476
  editor.documentLoadNonce,
23103
23477
  editor.documentTheme,
@@ -23108,7 +23482,7 @@ function useDocxPageThumbnails(editor, options = {}) {
23108
23482
  options.maxHeightPx,
23109
23483
  options.maxWidthPx,
23110
23484
  options.pixelRatio,
23111
- rerenderAttachedThumbnails
23485
+ requestAttachedThumbnailRenders
23112
23486
  ]);
23113
23487
  const thumbnails = React.useMemo(() => {
23114
23488
  const totalPages = Math.max(1, editor.totalPages);
@@ -25498,6 +25872,42 @@ function DocxEditorViewer({
25498
25872
  pageNodeSegmentsByPage,
25499
25873
  primarySectionPropertiesXml
25500
25874
  ]);
25875
+ const pageThumbnailContentKeysByPage = React.useMemo(() => {
25876
+ const metadataSignature = docModelThumbnailMetadataSignature(
25877
+ editor.model.metadata
25878
+ );
25879
+ return pageNodeSegmentsByPage.map((pageSegments, pageIndex) => {
25880
+ const sectionInfo = pageSectionInfoByIndex[pageIndex];
25881
+ let nodeSignatures = "";
25882
+ for (const segment of pageSegments) {
25883
+ nodeSignatures += docNodeContentSignature(
25884
+ editor.model.nodes[segment.nodeIndex]
25885
+ );
25886
+ nodeSignatures += ",";
25887
+ }
25888
+ return [
25889
+ editor.documentLoadNonce,
25890
+ trackedChangesEnabled ? "tc1" : "tc0",
25891
+ metadataSignature,
25892
+ sectionInfo ? `${sectionInfo.sectionIndex}.${sectionInfo.pageNumber}` : "s?",
25893
+ pageNodeSegmentIdentityKeysByPage[pageIndex] ?? "",
25894
+ nodeSignatures
25895
+ ].join("|");
25896
+ });
25897
+ }, [
25898
+ editor.documentLoadNonce,
25899
+ editor.model,
25900
+ pageNodeSegmentIdentityKeysByPage,
25901
+ pageNodeSegmentsByPage,
25902
+ pageSectionInfoByIndex,
25903
+ trackedChangesEnabled
25904
+ ]);
25905
+ React.useEffect(() => {
25906
+ syncDocxViewerPageSurfaceContentKeys(
25907
+ editor,
25908
+ pageThumbnailContentKeysByPage
25909
+ );
25910
+ }, [pageSurfaceRegistryOwner, pageThumbnailContentKeysByPage]);
25501
25911
  const resolveStyleRefFieldValueForPage = React.useMemo(() => {
25502
25912
  const valueCache = /* @__PURE__ */ new Map();
25503
25913
  const nodes = editor.model.nodes;
@@ -25756,6 +26166,14 @@ function DocxEditorViewer({
25756
26166
  overscan: pageVirtualizationOverscan
25757
26167
  });
25758
26168
  const internalPageVirtualizer = internalVirtualScrollUsesWindow ? internalWindowPageVirtualizer : internalElementPageVirtualizer;
26169
+ React.useLayoutEffect(() => {
26170
+ internalElementPageVirtualizer.measure();
26171
+ internalWindowPageVirtualizer.measure();
26172
+ }, [
26173
+ estimateVirtualPageSize,
26174
+ internalElementPageVirtualizer,
26175
+ internalWindowPageVirtualizer
26176
+ ]);
25759
26177
  const internalVirtualItems = internalPageVirtualizer.getVirtualItems();
25760
26178
  const internalVisiblePageRange = React.useMemo(() => {
25761
26179
  if (!internalPageVirtualizationEnabled) {
@@ -25798,8 +26216,8 @@ function DocxEditorViewer({
25798
26216
  return internalVisiblePageRange;
25799
26217
  }
25800
26218
  const scrollDirection = internalPageVirtualizer.scrollDirection;
25801
- const renderPreviousPage = scrollDirection !== "backward";
25802
- const renderNextPage = scrollDirection === "backward";
26219
+ const renderPreviousPage = scrollDirection !== "forward";
26220
+ const renderNextPage = scrollDirection !== "backward";
25803
26221
  const startPageIndex = clampNumber(
25804
26222
  internalVisiblePageRange.startPageIndex - (renderPreviousPage ? LARGE_TABLE_PAGE_ADJACENT_RENDER_COUNT : 0),
25805
26223
  0,
@@ -33214,6 +33632,7 @@ function DocxEditorViewer({
33214
33632
  "span",
33215
33633
  {
33216
33634
  contentEditable: false,
33635
+ ...{ [DOCX_THUMBNAIL_EXCLUDE_ATTRIBUTE]: "true" },
33217
33636
  style: {
33218
33637
  position: "absolute",
33219
33638
  left: rect.left,
@@ -33236,6 +33655,7 @@ function DocxEditorViewer({
33236
33655
  "span",
33237
33656
  {
33238
33657
  contentEditable: false,
33658
+ ...{ [DOCX_THUMBNAIL_EXCLUDE_ATTRIBUTE]: "true" },
33239
33659
  style: {
33240
33660
  position: "absolute",
33241
33661
  left: caretRect.left,
@@ -33605,7 +34025,10 @@ function DocxEditorViewer({
33605
34025
  Math.round(options?.pageFlowTopPx ?? 0)
33606
34026
  );
33607
34027
  const bodyParagraphOriginLeftPx = interactiveBodyFloatingPageOriginPx ? paragraphPageLayout.marginsPx.left : void 0;
33608
- const bodyParagraphOriginTopPx = interactiveBodyFloatingPageOriginPx ? paragraphPageLayout.marginsPx.top + paragraphPageFlowTopPx : void 0;
34028
+ const bodyParagraphOriginTopPx = interactiveBodyFloatingPageOriginPx ? paragraphActsAsPageAnchoredCoverOverlayHost(
34029
+ paragraph,
34030
+ paragraphPageLayout
34031
+ ) ? paragraphPageLayout.marginsPx.top + paragraphPageFlowTopPx : 0 : void 0;
33609
34032
  const resizedWidthPxByImageIndex = /* @__PURE__ */ new Map();
33610
34033
  const resizedHeightPxByImageIndex = /* @__PURE__ */ new Map();
33611
34034
  const movePreviewByImageIndex = /* @__PURE__ */ new Map();
@@ -36439,13 +36862,6 @@ function DocxEditorViewer({
36439
36862
  options?.pageFlowForeignExclusions ?? []
36440
36863
  )
36441
36864
  );
36442
- const leadingCoverLayoutSpacer = paragraphActsAsLeadingCoverLayoutSpacer(
36443
- editor.model,
36444
- nodeIndex,
36445
- node,
36446
- paragraphContentWidthPx,
36447
- resolvedPageLayout.pageHeightPx - resolvedPageLayout.marginsPx.top - resolvedPageLayout.marginsPx.bottom
36448
- );
36449
36865
  const beforeSpacingPx = effectiveParagraphBeforeSpacingPx(
36450
36866
  editor.model,
36451
36867
  nodeIndex,
@@ -36499,14 +36915,12 @@ function DocxEditorViewer({
36499
36915
  marginTop: (typeof baseParagraphStyle.marginTop === "number" ? baseParagraphStyle.marginTop : 0) - (pageAnchoredCoverOverlayParagraph ? resolvedPageLayout.marginsPx.top : 0),
36500
36916
  marginLeft: -resolvedPageLayout.marginsPx.left,
36501
36917
  marginRight: -resolvedPageLayout.marginsPx.right,
36502
- minHeight: 0,
36503
- height: 0,
36504
- lineHeight: 0,
36918
+ // A cover-overlay host is remapped to the page surface and must
36919
+ // not displace flow; an ordinary anchor host still occupies its
36920
+ // one-line paragraph box (Word semantics).
36921
+ ...pageAnchoredCoverOverlayParagraph ? { minHeight: 0, height: 0, lineHeight: 0 } : void 0,
36505
36922
  overflow: "visible"
36506
36923
  } : requiresPageAbsoluteContext ? { position: "static" } : requiresLocalAbsoluteContext ? { position: "relative" } : hasDualWrappedFloatingImage ? { position: "relative" } : void 0,
36507
- ...leadingCoverLayoutSpacer ? {
36508
- minHeight: `${estimateParagraphLineHeightPx(node, nodeDocGridLinePitchPx) + EMPTY_PARAGRAPH_EXTRA_HEIGHT_PX + LEADING_COVER_SPACER_EXTRA_HEIGHT_PX}px`
36509
- } : void 0,
36510
36924
  // Carry the paragraph's run font on the editable host so text typed into
36511
36925
  // it (which is a bare, not-yet-committed text node, not a styled run
36512
36926
  // span) renders in the same font the committed run will use — i.e. the
@@ -37541,6 +37955,22 @@ ${currentText.slice(end)}`;
37541
37955
  const rowSpanValue = cell.style?.rowSpan && cell.style.rowSpan > 1 ? cell.style.rowSpan : 1;
37542
37956
  const colSpan = colSpanValue > 1 ? colSpanValue : void 0;
37543
37957
  const rowSpan = rowSpanValue > 1 ? rowSpanValue : void 0;
37958
+ const exactRowSpanRows = node.rows.slice(
37959
+ rowIndex,
37960
+ rowIndex + rowSpanValue
37961
+ );
37962
+ const exactRowSpanClipHeightPx = exactRowSpanRows.length > 0 && exactRowSpanRows.every((spannedRow, rowOffset) => {
37963
+ const spannedHeightPx = rowHeightsPx[rowIndex + rowOffset];
37964
+ return spannedRow.style?.heightRule === "exact" && Number.isFinite(spannedHeightPx) && spannedHeightPx > 0;
37965
+ }) ? exactRowSpanRows.reduce(
37966
+ (sum, _spannedRow, rowOffset) => sum + Math.max(
37967
+ MIN_PARAGRAPH_LINE_HEIGHT_PX,
37968
+ Math.round(
37969
+ rowHeightsPx[rowIndex + rowOffset]
37970
+ )
37971
+ ),
37972
+ 0
37973
+ ) : void 0;
37544
37974
  const startColumnIndex = columnCursor;
37545
37975
  const boundaryColumnIndex = startColumnIndex + colSpanValue - 1;
37546
37976
  columnCursor += colSpanValue;
@@ -38659,12 +39089,11 @@ ${currentText.slice(end)}`;
38659
39089
  style: {
38660
39090
  display: "grid",
38661
39091
  gap: 0,
38662
- // Word clips content of hRule="exact" rows
38663
- // at the declared height; without this the
38664
- // row balloons to fit content (height on a
38665
- // <tr>/<td> is only ever a minimum).
38666
- ...row.style?.heightRule === "exact" && !isSlicedRow && resolvedRowHeightStyle?.height ? {
38667
- maxHeight: resolvedRowHeightStyle.height,
39092
+ // Without the clip the row balloons to fit
39093
+ // content (height on a <tr>/<td> is only
39094
+ // ever a minimum).
39095
+ ...exactRowSpanClipHeightPx !== void 0 && !isSlicedRow ? {
39096
+ maxHeight: `${exactRowSpanClipHeightPx}px`,
38668
39097
  overflow: "hidden"
38669
39098
  } : void 0
38670
39099
  },
@@ -40451,11 +40880,27 @@ ${currentText.slice(end)}`;
40451
40880
  currentGroup.segments.push(segment);
40452
40881
  return;
40453
40882
  }
40883
+ if (currentGroup && parseSectionStartType(
40884
+ documentSections[currentSectionIndex]?.sectionPropertiesXml
40885
+ ) === "nextcolumn") {
40886
+ const previousColumns = sectionColumnsBySectionIndex[currentGroup.sectionIndex];
40887
+ const nextColumns = sectionColumnsBySectionIndex[currentSectionIndex];
40888
+ const sameGeometry = previousColumns && nextColumns && previousColumns.count === nextColumns.count && Math.abs(
40889
+ previousColumns.gapPx - nextColumns.gapPx
40890
+ ) <= 2 && JSON.stringify(previousColumns.widthsPx ?? null) === JSON.stringify(nextColumns.widthsPx ?? null);
40891
+ if (sameGeometry) {
40892
+ currentGroup.segments.push(segment);
40893
+ return;
40894
+ }
40895
+ }
40454
40896
  sectionGroups.push({
40455
40897
  sectionIndex: currentSectionIndex,
40456
40898
  segments: [segment]
40457
40899
  });
40458
40900
  });
40901
+ if (typeof window !== "undefined" && window.__docxDebugGroups) {
40902
+ console.log("[groups]", pageIndex, JSON.stringify(sectionGroups.map((g) => ({ s: g.sectionIndex, n: g.segments.map((x) => x.nodeIndex) }))));
40903
+ }
40459
40904
  return sectionGroups.map((group, groupIndex) => {
40460
40905
  const sectionColumns = sectionColumnsBySectionIndex[group.sectionIndex];
40461
40906
  const isLastGroupOnPage = groupIndex === sectionGroups.length - 1;
@@ -40624,7 +41069,7 @@ ${currentText.slice(end)}`;
40624
41069
  (editor.model.metadata.sections ?? []).filter(
40625
41070
  (candidate) => parseSectionStartType(
40626
41071
  candidate.sectionPropertiesXml
40627
- ) === "nextColumn"
41072
+ ) === "nextcolumn"
40628
41073
  ).map(
40629
41074
  (candidate) => Math.max(
40630
41075
  0,
@@ -42542,6 +42987,7 @@ function collectDocxEstimatedOverflowBreakStartNodeIndexes(model, hardBreakStart
42542
42987
  let pageConsumedHeightPx = 0;
42543
42988
  let previousParagraphAfterPx = 0;
42544
42989
  let currentMetricsIndex = 0;
42990
+ let committedKeepNextChainEndNodeIndex = -1;
42545
42991
  const suppressSpacingBeforeAfterPageBreak = options?.suppressSpacingBeforeAfterPageBreak ?? false;
42546
42992
  let currentPageContentHeightPx = metricsBySection[0]?.pageContentHeightPx ?? fallbackMetrics.pageContentHeightPx;
42547
42993
  for (let nodeIndex = 0; nodeIndex < model.nodes.length; nodeIndex += 1) {
@@ -42579,7 +43025,8 @@ function collectDocxEstimatedOverflowBreakStartNodeIndexes(model, hardBreakStart
42579
43025
  const collapsedMarginPx = node.type === "paragraph" && pageConsumedHeightPx > 0 ? Math.min(previousParagraphAfterPx, nodeBeforeSpacingPx) : 0;
42580
43026
  const collapsedNodeHeightPx = Math.max(1, rawNodeHeightPx - collapsedMarginPx);
42581
43027
  let requiredHeightPx = collapsedNodeHeightPx;
42582
- if (node.type === "paragraph" && node.style?.keepNext === true && callbacks.paragraphHasVisibleText(node)) {
43028
+ let keepNextChainEndNodeIndex = -1;
43029
+ if (node.type === "paragraph" && node.style?.keepNext === true && nodeIndex > committedKeepNextChainEndNodeIndex && callbacks.paragraphHasVisibleText(node)) {
42583
43030
  let chainCursor = nodeIndex;
42584
43031
  let chainPreviousParagraphAfterPx = paragraphAfterSpacingPx2(node);
42585
43032
  while (chainCursor < model.nodes.length - 1) {
@@ -42637,6 +43084,7 @@ function collectDocxEstimatedOverflowBreakStartNodeIndexes(model, hardBreakStart
42637
43084
  );
42638
43085
  chainPreviousParagraphAfterPx = paragraphAfterSpacingPx2(nextChainNode);
42639
43086
  }
43087
+ keepNextChainEndNodeIndex = chainCursor;
42640
43088
  }
42641
43089
  const remainingHeightPx = currentPageContentHeightPx - pageConsumedHeightPx;
42642
43090
  if (pageConsumedHeightPx > 0 && requiredHeightPx > remainingHeightPx + pageOverflowTolerancePx) {
@@ -42645,6 +43093,9 @@ function collectDocxEstimatedOverflowBreakStartNodeIndexes(model, hardBreakStart
42645
43093
  previousParagraphAfterPx = 0;
42646
43094
  currentPageContentHeightPx = nodeMetrics.pageContentHeightPx;
42647
43095
  }
43096
+ if (pageConsumedHeightPx === 0 && keepNextChainEndNodeIndex > nodeIndex) {
43097
+ committedKeepNextChainEndNodeIndex = keepNextChainEndNodeIndex;
43098
+ }
42648
43099
  const effectiveNodeHeightPx = pageConsumedHeightPx > 0 ? collapsedNodeHeightPx : rawNodeHeightPx;
42649
43100
  pageConsumedHeightPx += effectiveNodeHeightPx;
42650
43101
  previousParagraphAfterPx = node.type === "paragraph" ? paragraphAfterSpacingPx2(node) : 0;
@@ -42696,6 +43147,7 @@ function buildDocumentPageNodeSegments2(model, pageContentHeightPx, pageContentW
42696
43147
  let pageConsumedHeightPx = 0;
42697
43148
  let previousParagraphAfterPx = 0;
42698
43149
  let currentMetricsIndex = 0;
43150
+ let committedKeepNextChainEndNodeIndex = -1;
42699
43151
  let currentPageContentHeightPx = resolvePageContentHeightPx(
42700
43152
  0,
42701
43153
  metricsBySection[0]?.pageContentHeightPx ?? fallbackMetrics.pageContentHeightPx
@@ -42759,7 +43211,8 @@ function buildDocumentPageNodeSegments2(model, pageContentHeightPx, pageContentW
42759
43211
  const keepLinesOverflowSplit = node.style?.keepLines === true && paragraphTooTallForSinglePage;
42760
43212
  const keepNextOverflowSplit = node.style?.keepNext === true && paragraphTooTallForSinglePage;
42761
43213
  const forceOverflowSplit = keepLinesOverflowSplit || keepNextOverflowSplit;
42762
- if (forceOverflowSplit && pageConsumedHeightPx > 0 && currentPageSegments.length > 0) {
43214
+ const nodeIsWithinCommittedKeepNextChain = nodeIndex <= committedKeepNextChainEndNodeIndex;
43215
+ if (forceOverflowSplit && !nodeIsWithinCommittedKeepNextChain && pageConsumedHeightPx > 0 && currentPageSegments.length > 0) {
42763
43216
  startNextPage();
42764
43217
  pageConsumedHeightPx = 0;
42765
43218
  previousParagraphAfterPx = 0;
@@ -42909,7 +43362,8 @@ function buildDocumentPageNodeSegments2(model, pageContentHeightPx, pageContentW
42909
43362
  continue;
42910
43363
  }
42911
43364
  let requiredHeightPx = collapsedNodeHeightPx;
42912
- if (node.style?.keepNext === true && callbacks.paragraphHasVisibleText(node)) {
43365
+ let keepNextChainEndNodeIndex = -1;
43366
+ if (node.style?.keepNext === true && !nodeIsWithinCommittedKeepNextChain && callbacks.paragraphHasVisibleText(node)) {
42913
43367
  let chainCursor = nodeIndex;
42914
43368
  let chainPreviousParagraphAfterPx = afterSpacingPx;
42915
43369
  while (chainCursor < model.nodes.length - 1) {
@@ -42967,6 +43421,7 @@ function buildDocumentPageNodeSegments2(model, pageContentHeightPx, pageContentW
42967
43421
  );
42968
43422
  chainPreviousParagraphAfterPx = paragraphAfterSpacingPx2(nextChainNode);
42969
43423
  }
43424
+ keepNextChainEndNodeIndex = chainCursor;
42970
43425
  }
42971
43426
  const remainingHeightPx = currentPageContentHeightPx - pageConsumedHeightPx;
42972
43427
  if (pageConsumedHeightPx > 0 && requiredHeightPx > remainingHeightPx + pageOverflowTolerancePx) {
@@ -42978,6 +43433,9 @@ function buildDocumentPageNodeSegments2(model, pageContentHeightPx, pageContentW
42978
43433
  nodeMetrics.pageContentHeightPx
42979
43434
  );
42980
43435
  }
43436
+ if (pageConsumedHeightPx === 0 && keepNextChainEndNodeIndex > nodeIndex) {
43437
+ committedKeepNextChainEndNodeIndex = keepNextChainEndNodeIndex;
43438
+ }
42981
43439
  currentPageSegments.push({ nodeIndex });
42982
43440
  const effectiveNodeHeightPx = pageConsumedHeightPx > 0 ? collapsedNodeHeightPx : rawNodeHeightPx;
42983
43441
  pageConsumedHeightPx += effectiveNodeHeightPx;