@mlightcad/cad-simple-viewer 1.7.5 → 1.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/cad-simple-viewer.js +9270 -8077
  2. package/dist/cad-simple-viewer.umd.cjs +83 -83
  3. package/dist/mtext-renderer-worker.js +2987 -2815
  4. package/find.ts +7 -0
  5. package/lib/app/AcApDocManager.d.ts +5 -0
  6. package/lib/app/AcApDocManager.d.ts.map +1 -1
  7. package/lib/app/AcApDocManager.js +39 -21
  8. package/lib/app/AcApDocManager.js.map +1 -1
  9. package/lib/app/AcApOpenDatabaseOptions.d.ts +8 -6
  10. package/lib/app/AcApOpenDatabaseOptions.d.ts.map +1 -1
  11. package/lib/app/AcApOpenFileProgressController.d.ts +17 -9
  12. package/lib/app/AcApOpenFileProgressController.d.ts.map +1 -1
  13. package/lib/app/AcApOpenFileProgressController.js +41 -20
  14. package/lib/app/AcApOpenFileProgressController.js.map +1 -1
  15. package/lib/app/AcApProgress.d.ts +5 -0
  16. package/lib/app/AcApProgress.d.ts.map +1 -1
  17. package/lib/app/AcApProgress.js +10 -0
  18. package/lib/app/AcApProgress.js.map +1 -1
  19. package/lib/app/openFileProgress.d.ts +8 -1
  20. package/lib/app/openFileProgress.d.ts.map +1 -1
  21. package/lib/app/openFileProgress.js +13 -2
  22. package/lib/app/openFileProgress.js.map +1 -1
  23. package/lib/app/syncCurrentSpaceFromTileMode.d.ts +25 -0
  24. package/lib/app/syncCurrentSpaceFromTileMode.d.ts.map +1 -0
  25. package/lib/app/syncCurrentSpaceFromTileMode.js +85 -0
  26. package/lib/app/syncCurrentSpaceFromTileMode.js.map +1 -0
  27. package/lib/editor/input/AcEdOsnapCenterMarks.d.ts +15 -2
  28. package/lib/editor/input/AcEdOsnapCenterMarks.d.ts.map +1 -1
  29. package/lib/editor/input/AcEdOsnapCenterMarks.js +63 -13
  30. package/lib/editor/input/AcEdOsnapCenterMarks.js.map +1 -1
  31. package/lib/editor/input/AcEdOsnapResolver.d.ts +14 -0
  32. package/lib/editor/input/AcEdOsnapResolver.d.ts.map +1 -1
  33. package/lib/editor/input/AcEdOsnapResolver.js +246 -32
  34. package/lib/editor/input/AcEdOsnapResolver.js.map +1 -1
  35. package/lib/editor/input/AcEdSelectionFilter.d.ts +6 -0
  36. package/lib/editor/input/AcEdSelectionFilter.d.ts.map +1 -1
  37. package/lib/editor/input/AcEdSelectionFilter.js +19 -2
  38. package/lib/editor/input/AcEdSelectionFilter.js.map +1 -1
  39. package/lib/editor/input/AcEditor.d.ts +6 -0
  40. package/lib/editor/input/AcEditor.d.ts.map +1 -1
  41. package/lib/editor/input/AcEditor.js +9 -0
  42. package/lib/editor/input/AcEditor.js.map +1 -1
  43. package/lib/editor/input/ui/AcEdCommandLine.d.ts +12 -0
  44. package/lib/editor/input/ui/AcEdCommandLine.d.ts.map +1 -1
  45. package/lib/editor/input/ui/AcEdCommandLine.js +54 -16
  46. package/lib/editor/input/ui/AcEdCommandLine.js.map +1 -1
  47. package/lib/editor/input/ui/AcEdInputManager.d.ts +15 -0
  48. package/lib/editor/input/ui/AcEdInputManager.d.ts.map +1 -1
  49. package/lib/editor/input/ui/AcEdInputManager.js +41 -10
  50. package/lib/editor/input/ui/AcEdInputManager.js.map +1 -1
  51. package/lib/editor/view/AcEdBaseView.d.ts +34 -0
  52. package/lib/editor/view/AcEdBaseView.d.ts.map +1 -1
  53. package/lib/editor/view/AcEdBaseView.js +35 -44
  54. package/lib/editor/view/AcEdBaseView.js.map +1 -1
  55. package/lib/editor/view/AcEdSpatialQueryResult.d.ts +7 -0
  56. package/lib/editor/view/AcEdSpatialQueryResult.d.ts.map +1 -1
  57. package/lib/editor/view/AcEdSpatialQueryResult.js.map +1 -1
  58. package/lib/find/AcApFindNavigator.d.ts +59 -0
  59. package/lib/find/AcApFindNavigator.d.ts.map +1 -0
  60. package/lib/find/AcApFindNavigator.js +145 -0
  61. package/lib/find/AcApFindNavigator.js.map +1 -0
  62. package/lib/find/findDrawing.d.ts +100 -0
  63. package/lib/find/findDrawing.d.ts.map +1 -0
  64. package/lib/find/findDrawing.js +183 -0
  65. package/lib/find/findDrawing.js.map +1 -0
  66. package/lib/find/findText.d.ts +147 -0
  67. package/lib/find/findText.d.ts.map +1 -0
  68. package/lib/find/findText.js +561 -0
  69. package/lib/find/findText.js.map +1 -0
  70. package/lib/find/index.d.ts +3 -0
  71. package/lib/find/index.d.ts.map +1 -0
  72. package/lib/find/index.js +3 -0
  73. package/lib/find/index.js.map +1 -0
  74. package/lib/i18n/ar/jig.d.ts +2 -0
  75. package/lib/i18n/ar/jig.d.ts.map +1 -1
  76. package/lib/i18n/ar/jig.js +1 -1
  77. package/lib/i18n/ar/jig.js.map +1 -1
  78. package/lib/i18n/cs/jig.d.ts +2 -0
  79. package/lib/i18n/cs/jig.d.ts.map +1 -1
  80. package/lib/i18n/cs/jig.js +2 -0
  81. package/lib/i18n/cs/jig.js.map +1 -1
  82. package/lib/i18n/en/jig.d.ts +2 -0
  83. package/lib/i18n/en/jig.d.ts.map +1 -1
  84. package/lib/i18n/en/jig.js +2 -0
  85. package/lib/i18n/en/jig.js.map +1 -1
  86. package/lib/i18n/tr/jig.d.ts +2 -0
  87. package/lib/i18n/tr/jig.d.ts.map +1 -1
  88. package/lib/i18n/tr/jig.js +2 -0
  89. package/lib/i18n/tr/jig.js.map +1 -1
  90. package/lib/i18n/zh/jig.d.ts +2 -0
  91. package/lib/i18n/zh/jig.d.ts.map +1 -1
  92. package/lib/i18n/zh/jig.js +2 -0
  93. package/lib/i18n/zh/jig.js.map +1 -1
  94. package/lib/index.d.ts +2 -0
  95. package/lib/index.d.ts.map +1 -1
  96. package/lib/index.js +2 -0
  97. package/lib/index.js.map +1 -1
  98. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.d.ts +29 -7
  99. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.d.ts.map +1 -1
  100. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.js +77 -38
  101. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.js.map +1 -1
  102. package/lib/view/AcTrConvertQueue.d.ts +78 -0
  103. package/lib/view/AcTrConvertQueue.d.ts.map +1 -0
  104. package/lib/view/AcTrConvertQueue.js +143 -0
  105. package/lib/view/AcTrConvertQueue.js.map +1 -0
  106. package/lib/view/AcTrGroupWcsBboxAssert.d.ts +9 -0
  107. package/lib/view/AcTrGroupWcsBboxAssert.d.ts.map +1 -1
  108. package/lib/view/AcTrGroupWcsBboxAssert.js +12 -0
  109. package/lib/view/AcTrGroupWcsBboxAssert.js.map +1 -1
  110. package/lib/view/AcTrLayer.d.ts.map +1 -1
  111. package/lib/view/AcTrLayer.js +5 -1
  112. package/lib/view/AcTrLayer.js.map +1 -1
  113. package/lib/view/AcTrLayout.d.ts.map +1 -1
  114. package/lib/view/AcTrLayout.js +15 -0
  115. package/lib/view/AcTrLayout.js.map +1 -1
  116. package/lib/view/AcTrLayoutView.d.ts.map +1 -1
  117. package/lib/view/AcTrLayoutView.js +3 -1
  118. package/lib/view/AcTrLayoutView.js.map +1 -1
  119. package/lib/view/AcTrProgressiveOpenFitController.d.ts +1 -0
  120. package/lib/view/AcTrProgressiveOpenFitController.d.ts.map +1 -1
  121. package/lib/view/AcTrProgressiveOpenFitController.js +2 -1
  122. package/lib/view/AcTrProgressiveOpenFitController.js.map +1 -1
  123. package/lib/view/AcTrView2d.d.ts +210 -20
  124. package/lib/view/AcTrView2d.d.ts.map +1 -1
  125. package/lib/view/AcTrView2d.js +962 -273
  126. package/lib/view/AcTrView2d.js.map +1 -1
  127. package/package.json +17 -10
  128. package/src/find/AcApFindNavigator.ts +190 -0
  129. package/src/find/findDrawing.ts +211 -0
  130. package/src/find/findText.ts +624 -0
  131. package/src/find/index.ts +2 -0
@@ -119,6 +119,7 @@ import { collectTextStyleFontNames, planTextStyleFontPreload } from '../util/AcA
119
119
  import { ACAP_READING_MODE_BACKGROUND, AcApReadingModeState } from './AcApReadingMode';
120
120
  import { trySelectReviewOverlay, trySelectReviewOverlaysByBox } from './AcEdReviewOverlayPick';
121
121
  import { AcEdViewKeyHandler } from './AcEdViewKeyHandler';
122
+ import { absorbConvertQueue, adaptConvertSliceSize, createConvertPending, hasPendingActiveOwner } from './AcTrConvertQueue';
122
123
  import { shouldExtendBboxForDirectEntity, tryBuildDirectEntityMeta } from './AcTrDirectBatch';
123
124
  import { AcTrEntityDisplayController } from './AcTrEntityDisplayController';
124
125
  import { assertAcTrGroupWcsBboxesConsistent, unionGroupWcsChildBoxes } from './AcTrGroupWcsBboxAssert';
@@ -179,6 +180,11 @@ var AcTrView2d = /** @class */ (function (_super) {
179
180
  var container = (_a = mergedOptions.container) !== null && _a !== void 0 ? _a : document.createElement('div');
180
181
  mergedOptions.container = container;
181
182
  container.style.overflow = 'hidden';
183
+ // Allow focusing the container after canvas clicks so document-level
184
+ // shortcuts stay scoped to this viewer without stealing host-page focus.
185
+ if (!container.hasAttribute('tabindex')) {
186
+ container.tabIndex = -1;
187
+ }
182
188
  var renderer = new THREE.WebGLRenderer({
183
189
  antialias: true,
184
190
  alpha: true
@@ -288,6 +294,13 @@ var AcTrView2d = /** @class */ (function (_super) {
288
294
  * `setTimeout` storm after the loading overlay hides.
289
295
  */
290
296
  _this._convertQueue = [];
297
+ /**
298
+ * Paper-space block table record ids whose viewport entities were converted
299
+ * before an {@link AcTrLayoutView} existed, so {@link batchConvert} could
300
+ * not create the {@link AcTrViewportView}. Repaired when that layout view
301
+ * is created.
302
+ */
303
+ _this._viewportRepairOwnerIds = new Set();
291
304
  /** In-flight progressive convert drain; shared so awaiters wait for the queue. */
292
305
  _this._convertDrainPromise = null;
293
306
  /**
@@ -296,8 +309,15 @@ var AcTrView2d = /** @class */ (function (_super) {
296
309
  * document open.
297
310
  */
298
311
  _this._convertEpoch = 0;
299
- /** Last time progressive open marked the canvas dirty for paint. */
312
+ /** Last time progressive open actually painted mid-convert. */
300
313
  _this._lastProgressivePaintAt = 0;
314
+ /**
315
+ * When true, the next progressive mid-open paint must not be throttled
316
+ * (camera moved — user pan/zoom or incremental auto-fit).
317
+ */
318
+ _this._progressivePaintUrgent = false;
319
+ /** Pause convert until this `performance.now()` after user input. */
320
+ _this._progressiveInputPauseUntil = 0;
301
321
  /** Mid-open WebGL paints while progressive convert was still running. */
302
322
  _this._progressivePaintCount = 0;
303
323
  /** Cooperative yields taken inside progressive {@link batchConvert}. */
@@ -344,6 +364,12 @@ var AcTrView2d = /** @class */ (function (_super) {
344
364
  * ENTITY parse and linework convert; glyph jobs await this before draw.
345
365
  */
346
366
  _this._textStyleFontPreloadPromise = null;
367
+ /**
368
+ * Aborts canvas / document DOM listeners registered on this view.
369
+ * Custom singleton listeners are removed explicitly in {@link dispose}.
370
+ */
371
+ _this._viewDomAbort = new AbortController();
372
+ _this._viewDisposed = false;
347
373
  /** Transient reading mode forces black linework on a white canvas. */
348
374
  _this._readingMode = new AcApReadingModeState({
349
375
  getCurrentBackgroundColor: function () { return _this._renderer.currentBackgroundColor; },
@@ -376,17 +402,39 @@ var AcTrView2d = /** @class */ (function (_super) {
376
402
  camera: _this.internalCamera
377
403
  });
378
404
  var stillLoading = _this._numOfEntitiesToProcess > 0;
379
- var deferRenderWhileLoading = stillLoading && !_this._progressiveRendering;
380
- if (!_this._isDirty && !_this._htmlDirty && !stillLoading)
405
+ var sceneBusy = stillLoading || _this._pendingGeometryJobs > 0;
406
+ // Headless non-progressive (`cooperativeYield` false): skip WebGL until
407
+ // idle. Export reads scene batches, not the framebuffer, and software
408
+ // WebGL + uncapped rAF spent most of open time rasterizing. Interactive
409
+ // non-progressive still paints under "Rendering drawing ..." — throttled
410
+ // below so each glyph completion does not redraw the full scene.
411
+ var deferRenderWhileLoading = !_this._progressiveRendering && !_this._cooperativeYield && sceneBusy;
412
+ if (!_this._isDirty && !_this._htmlDirty && !sceneBusy) {
381
413
  return;
414
+ }
382
415
  if (deferRenderWhileLoading)
383
416
  return;
384
417
  if (!_this._isDirty && !_this._htmlDirty)
385
418
  return;
419
+ // Mid-open throttle: progressive convert paints, and interactive
420
+ // non-progressive paints while the scene is still busy. Camera changes
421
+ // set `_progressivePaintUrgent` so pan/zoom still redraw now.
422
+ var countMidOpenPaint = _this.shouldCountMidOpenPaint(stillLoading, sceneBusy);
423
+ var throttleOpenPaint = _this._isDirty && !_this._progressivePaintUrgent && countMidOpenPaint;
424
+ if (throttleOpenPaint) {
425
+ var intervalMs = _this._progressiveRendering
426
+ ? AcTrView2d.PROGRESSIVE_OPEN_PAINT_INTERVAL_MS
427
+ : AcTrView2d.NON_PROGRESSIVE_OPEN_PAINT_INTERVAL_MS;
428
+ if (performance.now() - _this._lastProgressivePaintAt < intervalMs) {
429
+ return;
430
+ }
431
+ }
386
432
  var needsRedraw = false;
387
433
  if (_this._isDirty) {
388
- if (_this._progressiveRendering && stillLoading) {
434
+ if (countMidOpenPaint) {
389
435
  _this._progressivePaintCount++;
436
+ _this._lastProgressivePaintAt = performance.now();
437
+ _this._progressivePaintUrgent = false;
390
438
  }
391
439
  needsRedraw = _this._layoutViewManager.render(_this._scene);
392
440
  }
@@ -404,6 +452,31 @@ var AcTrView2d = /** @class */ (function (_super) {
404
452
  _this._htmlDirty = false;
405
453
  };
406
454
  _this._disposeCanvasTouchCallout = disposeCanvasTouchCallout;
455
+ var onProgressiveInput = function () { return _this.noteProgressiveUserInput(); };
456
+ // Pointer and wheel both claim shortcut focus so undo/erase still work
457
+ // after zooming without a prior click, without stealing focus from host
458
+ // inputs/textareas/contenteditable fields.
459
+ var onCanvasPointerDown = function () {
460
+ onProgressiveInput();
461
+ _this.focusViewerForShortcuts();
462
+ };
463
+ var onCanvasWheel = function () {
464
+ onProgressiveInput();
465
+ _this.focusViewerForShortcuts();
466
+ };
467
+ renderer.domElement.addEventListener('pointerdown', onCanvasPointerDown, {
468
+ capture: true
469
+ });
470
+ renderer.domElement.addEventListener('wheel', onCanvasWheel, {
471
+ capture: true,
472
+ passive: true
473
+ });
474
+ _this._disposeProgressiveInputWatch = function () {
475
+ renderer.domElement.removeEventListener('pointerdown', onCanvasPointerDown, { capture: true });
476
+ renderer.domElement.removeEventListener('wheel', onCanvasWheel, {
477
+ capture: true
478
+ });
479
+ };
407
480
  _this._gripManager = new AcEdGripManager(_this);
408
481
  _this._keyHandler = new AcEdViewKeyHandler(_this);
409
482
  if (options.calculateSizeCallback) {
@@ -417,27 +490,32 @@ var AcTrView2d = /** @class */ (function (_super) {
417
490
  setAcTrDrawOrderZAllocator(function () { return _this._renderer.allocateDrawOrderZ(); });
418
491
  var fontMapping = AcApSettingManager.instance.fontMapping;
419
492
  _this._renderer.setFontMapping(fontMapping);
420
- _this._renderer.events.fontNotFound.addEventListener(function (args) {
493
+ _this._onFontNotFound = function (args) {
421
494
  var _a;
422
495
  eventBus.emit('font-not-found', {
423
496
  fontName: args.fontName,
424
497
  count: (_a = args.count) !== null && _a !== void 0 ? _a : 0
425
498
  });
426
- });
427
- _this._renderer.events.fontLoaded.addEventListener(function (args) {
499
+ };
500
+ _this._onFontLoaded = function (args) {
428
501
  // Lazy load success clears FontManager.missedFonts; refresh status-bar state.
429
502
  eventBus.emit('missed-data-changed', {});
430
503
  // On-demand faces (e.g. style `malgun`) may finish after the first glyph
431
504
  // bake. Rebuild text so Hangul is not left as permanent '?'.
432
505
  _this.scheduleGlyphRedrawAfterFontLoad(args === null || args === void 0 ? void 0 : args.fontName);
433
- });
506
+ };
507
+ _this._renderer.events.fontNotFound.addEventListener(_this._onFontNotFound);
508
+ _this._renderer.events.fontLoaded.addEventListener(_this._onFontLoaded);
434
509
  _this._scene = _this.createScene();
435
510
  _this._layerAppearance = new AcTrLayerAppearanceController(_this._scene, _this._renderer);
436
511
  _this._inheritedLayerMaterialMapper = new AcTrInheritedLayerMaterialMapper(function (layerName) { return _this._layerAppearance.getEffectiveLayerTraits(layerName); }, _this._renderer);
437
512
  // Initialize background color through setter to keep renderer/cursor in sync.
438
513
  _this.backgroundColor =
439
514
  (_b = mergedOptions.background) !== null && _b !== void 0 ? _b : ACGI_MODEL_SPACE_BACKGROUND;
440
- _this._stats = _this.createStats(AcApSettingManager.instance.isShowStats && _this.isActiveManagedView());
515
+ // During main-view construction the manager singleton is not published yet,
516
+ // so treat a missing manager as "this is the primary canvas".
517
+ _this._stats = _this.createStats(AcApSettingManager.instance.isShowStats &&
518
+ (AcApDocManager.tryGetInstance() == null || _this.isActiveManagedView()));
441
519
  // Layout background sysvars drive the canvas clear colour and ACI-7
442
520
  // inversion (`MODELBKCOLOR` for model space, `PAPERBKCOLOR` for the
443
521
  // active layout). `COLORTHEME` only affects UI chrome — see
@@ -445,23 +523,28 @@ var AcTrView2d = /** @class */ (function (_super) {
445
523
  var sysVarManager = AcDbSysVarManager.instance();
446
524
  var modelBkVar = AcDbSystemVariables.MODELBKCOLOR.toLowerCase();
447
525
  var paperBkVar = AcDbSystemVariables.PAPERBKCOLOR.toLowerCase();
448
- sysVarManager.events.sysVarChanged.addEventListener(function (args) {
526
+ _this._onSysVarChanged = function (args) {
449
527
  var nameLower = args.name.toLowerCase();
450
528
  if (nameLower === modelBkVar || nameLower === paperBkVar) {
451
- var isModelSpace = _this.isModelSpaceLayout(args.database);
529
+ var database = args.database;
530
+ if (!database)
531
+ return;
532
+ var isModelSpace = _this.isModelSpaceLayout(database);
452
533
  var applies = (nameLower === modelBkVar && isModelSpace) ||
453
534
  (nameLower === paperBkVar && !isModelSpace);
454
535
  if (!applies) {
455
536
  return;
456
537
  }
457
- _this.applyCanvasBackground(readLayoutBackgroundColor(args.database, isModelSpace));
538
+ _this.applyCanvasBackground(readLayoutBackgroundColor(database, isModelSpace));
458
539
  }
459
- });
460
- AcApSettingManager.instance.events.modified.addEventListener(function (args) {
540
+ };
541
+ sysVarManager.events.sysVarChanged.addEventListener(_this._onSysVarChanged);
542
+ _this._onSettingsModified = function (args) {
461
543
  if (args.key == 'isShowStats') {
462
544
  _this.toggleStatsVisibility(_this._stats, args.value);
463
545
  }
464
- });
546
+ };
547
+ AcApSettingManager.instance.events.modified.addEventListener(_this._onSettingsModified);
465
548
  var selectionStartWcs = null;
466
549
  var selectionStartCanvas = null;
467
550
  var selectionPreviewEl = null;
@@ -567,6 +650,7 @@ var AcTrView2d = /** @class */ (function (_super) {
567
650
  selectionStartWcs = null;
568
651
  selectionStartCanvas = null;
569
652
  };
653
+ var viewDomSignal = _this._viewDomAbort.signal;
570
654
  _this.canvas.addEventListener('mousedown', function (e) {
571
655
  if (e.button !== 0)
572
656
  return;
@@ -576,7 +660,7 @@ var AcTrView2d = /** @class */ (function (_super) {
576
660
  return;
577
661
  }
578
662
  beginSelectionPreview(e.clientX, e.clientY);
579
- });
663
+ }, { signal: viewDomSignal });
580
664
  _this.canvas.addEventListener('mousemove', function (e) {
581
665
  if (!selectionStartWcs || !selectionPreviewEl || !selectionStartCanvas) {
582
666
  return;
@@ -585,7 +669,7 @@ var AcTrView2d = /** @class */ (function (_super) {
585
669
  return;
586
670
  }
587
671
  updateSelectionPreview(e.clientX, e.clientY, _this.getPointerSelectionAction(e));
588
- });
672
+ }, { signal: viewDomSignal });
589
673
  _this.canvas.addEventListener('mouseup', function (e) {
590
674
  if (_this._gripManager.isDragging) {
591
675
  resetSelectionDrag();
@@ -602,8 +686,8 @@ var AcTrView2d = /** @class */ (function (_super) {
602
686
  });
603
687
  var action = _this.getPointerSelectionAction(e);
604
688
  finishSelection(e.clientX, e.clientY, action, _this.isSelectionClick(selectionStartCanvas, endCanvas));
605
- });
606
- acedAttachMobileBoxGesture({
689
+ }, { signal: viewDomSignal });
690
+ _this._disposeMobileBoxGesture = acedAttachMobileBoxGesture({
607
691
  element: _this.canvas,
608
692
  shouldStart: function () { return canHandleMobileBoxGesture(); },
609
693
  setNavigationEnabled: function (enabled) {
@@ -642,16 +726,19 @@ var AcTrView2d = /** @class */ (function (_super) {
642
726
  return;
643
727
  }
644
728
  void _this.openPickedEntityEditor(e);
645
- });
729
+ }, { signal: viewDomSignal });
646
730
  // When using OrbitControls in THREE.js, it attaches its own event listeners to the DOM elements,
647
731
  // such as the canvas or the entire document. This can interfere with other event listeners you
648
- // add, including the keydown event.
732
+ // add, including the keydown event. Scope to this view's container so host-page shortcuts
733
+ // (and post-destroy key presses) are not captured.
649
734
  document.addEventListener('keydown', function (e) {
650
735
  if (!_this.isActiveManagedView())
651
736
  return;
737
+ if (!_this.isShortcutEventInsideViewer(e))
738
+ return;
652
739
  _this._keyHandler.handleKeyDown(e);
653
- });
654
- acdbHostApplicationServices().layoutManager.events.layoutSwitched.addEventListener(function (args) {
740
+ }, { signal: viewDomSignal });
741
+ _this._onLayoutSwitched = function (args) {
655
742
  var _a, _b;
656
743
  if (!_this.isActiveManagedView())
657
744
  return;
@@ -691,7 +778,8 @@ var AcTrView2d = /** @class */ (function (_super) {
691
778
  if (isFirstVisit) {
692
779
  _this.applyInitialZoom(btrId, args.layout);
693
780
  }
694
- });
781
+ };
782
+ acdbHostApplicationServices().layoutManager.events.layoutSwitched.addEventListener(_this._onLayoutSwitched);
695
783
  _this._css2dRenderer = new CSS2DRenderer();
696
784
  _this._css2dRenderer.setSize(_this.width, _this.height);
697
785
  _this._css2dRenderer.domElement.style.position = 'absolute';
@@ -790,12 +878,14 @@ var AcTrView2d = /** @class */ (function (_super) {
790
878
  // This method is called after camera and render are created.
791
879
  // Children class can override this method to add its own logic
792
880
  this.setCursor(AcEdCorsorType.Crosshair);
793
- this.editor.events.commandWillStart.addEventListener(function () {
881
+ this._onCommandWillStartHitTest = function () {
794
882
  _this.htmlTransientManager.setHitTestEnabled(false);
795
- });
796
- this.editor.events.commandEnded.addEventListener(function () {
883
+ };
884
+ this._onCommandEndedHitTest = function () {
797
885
  _this.htmlTransientManager.setHitTestEnabled(true);
798
- });
886
+ };
887
+ this.editor.events.commandWillStart.addEventListener(this._onCommandWillStartHitTest);
888
+ this.editor.events.commandEnded.addEventListener(this._onCommandEndedHitTest);
799
889
  };
800
890
  Object.defineProperty(AcTrView2d.prototype, "mode", {
801
891
  /**
@@ -910,10 +1000,9 @@ var AcTrView2d = /** @class */ (function (_super) {
910
1000
  * The open-file progress overlay uses this when
911
1001
  * {@link AcApOpenDatabaseOptions.progressiveRendering} is off, so
912
1002
  * "Rendering drawing ..." stays up until deferred glyph jobs finish.
913
- * When progressive rendering is on, the overlay uses
914
- * {@link isConvertingEntities} and may hide while text geometry continues
915
- * in the deferred pool (pan/zoom enabled). Deprecated `waitForTextGeometry`
916
- * no longer selects this gate.
1003
+ * When progressive rendering is on, the overlay hides at CONVERSION `END`
1004
+ * and does not wait on this flag (pan/zoom enabled while convert continues).
1005
+ * Deprecated `waitForTextGeometry` no longer selects this gate.
917
1006
  */
918
1007
  get: function () {
919
1008
  return this._numOfEntitiesToProcess > 0 || this._pendingGeometryJobs > 0;
@@ -1004,7 +1093,9 @@ var AcTrView2d = /** @class */ (function (_super) {
1004
1093
  });
1005
1094
  Object.defineProperty(AcTrView2d.prototype, "cooperativeYield", {
1006
1095
  /**
1007
- * When false, entity convert skips cooperative yields (headless export).
1096
+ * When false, entity convert skips cooperative yields and non-progressive
1097
+ * mid-open WebGL paints (headless export). Interactive viewers keep this
1098
+ * `true` so "Rendering drawing ..." can still refresh the canvas.
1008
1099
  */
1009
1100
  get: function () {
1010
1101
  return this._cooperativeYield;
@@ -1018,8 +1109,8 @@ var AcTrView2d = /** @class */ (function (_super) {
1018
1109
  Object.defineProperty(AcTrView2d.prototype, "progressiveOpenStats", {
1019
1110
  /**
1020
1111
  * Open-convert counters for OPENPROF / palette (cooperative yields during
1021
- * convert, and mid-open paints when progressive rendering is enabled).
1022
- * Reset when progressive mode is enabled for an open.
1112
+ * convert, and throttled mid-open paints). Reset when progressive mode is
1113
+ * enabled for an open.
1023
1114
  */
1024
1115
  get: function () {
1025
1116
  return {
@@ -1034,6 +1125,85 @@ var AcTrView2d = /** @class */ (function (_super) {
1034
1125
  this._progressivePaintCount = 0;
1035
1126
  this._progressiveYieldCount = 0;
1036
1127
  this._lastProgressivePaintAt = 0;
1128
+ this._progressivePaintUrgent = false;
1129
+ this._progressiveInputPauseUntil = 0;
1130
+ };
1131
+ /**
1132
+ * True while a recent canvas pointerdown/wheel asked convert to pause.
1133
+ * Does not treat hover mousemove as a pause (that starved convert).
1134
+ */
1135
+ AcTrView2d.prototype.isProgressiveGesturePauseActive = function () {
1136
+ return performance.now() < this._progressiveInputPauseUntil;
1137
+ };
1138
+ /**
1139
+ * True when the browser reports pending discrete input (clicks, keys).
1140
+ * Continuous events (mousemove) are excluded so hover does not throttle
1141
+ * progressive convert.
1142
+ */
1143
+ AcTrView2d.prototype.isProgressiveDiscreteInputPending = function () {
1144
+ var _a;
1145
+ var scheduling = navigator.scheduling;
1146
+ return ((_a = scheduling === null || scheduling === void 0 ? void 0 : scheduling.isInputPending) === null || _a === void 0 ? void 0 : _a.call(scheduling)) === true;
1147
+ };
1148
+ /**
1149
+ * Notes user interaction during progressive convert so paints stay urgent
1150
+ * and the drain loop pauses briefly for OrbitControls.
1151
+ */
1152
+ AcTrView2d.prototype.noteProgressiveUserInput = function () {
1153
+ if (!this._progressiveRendering || this._numOfEntitiesToProcess <= 0) {
1154
+ return;
1155
+ }
1156
+ this._progressivePaintUrgent = true;
1157
+ this._isDirty = true;
1158
+ this._progressiveInputPauseUntil =
1159
+ performance.now() + AcTrView2d.PROGRESSIVE_INPUT_PAUSE_MS;
1160
+ };
1161
+ /**
1162
+ * Yields during progressive convert: honor the time budget; after
1163
+ * pointerdown/wheel pause briefly for OrbitControls; on discrete pending
1164
+ * input yield once so handlers run without extending the pause window.
1165
+ */
1166
+ AcTrView2d.prototype.progressiveYieldForInput = function (yieldGate, yieldToEventLoop) {
1167
+ return __awaiter(this, void 0, void 0, function () {
1168
+ var didYield;
1169
+ return __generator(this, function (_a) {
1170
+ switch (_a.label) {
1171
+ case 0:
1172
+ if (!this.isProgressiveGesturePauseActive()) return [3 /*break*/, 5];
1173
+ return [4 /*yield*/, yieldToEventLoop()];
1174
+ case 1:
1175
+ _a.sent();
1176
+ _a.label = 2;
1177
+ case 2:
1178
+ if (!(performance.now() < this._progressiveInputPauseUntil)) return [3 /*break*/, 4];
1179
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 16); })];
1180
+ case 3:
1181
+ _a.sent();
1182
+ return [3 /*break*/, 2];
1183
+ case 4:
1184
+ this._progressiveYieldCount++;
1185
+ return [2 /*return*/];
1186
+ case 5:
1187
+ if (!this.isProgressiveDiscreteInputPending()) return [3 /*break*/, 7];
1188
+ return [4 /*yield*/, yieldToEventLoop()];
1189
+ case 6:
1190
+ _a.sent();
1191
+ this._progressiveYieldCount++;
1192
+ return [2 /*return*/];
1193
+ case 7:
1194
+ if (!yieldGate) {
1195
+ return [2 /*return*/];
1196
+ }
1197
+ return [4 /*yield*/, yieldGate.maybeYield(yieldToEventLoop)];
1198
+ case 8:
1199
+ didYield = _a.sent();
1200
+ if (didYield) {
1201
+ this._progressiveYieldCount++;
1202
+ }
1203
+ return [2 /*return*/];
1204
+ }
1205
+ });
1206
+ });
1037
1207
  };
1038
1208
  /**
1039
1209
  * Enables progressive camera framing while entities are batch-converted at
@@ -1480,6 +1650,31 @@ var AcTrView2d = /** @class */ (function (_super) {
1480
1650
  includeLayouts = (options === null || options === void 0 ? void 0 : options.includeLayouts) !== false;
1481
1651
  db = AcApDocManager.instance.curDocument.database;
1482
1652
  pending = [];
1653
+ // Open streams entities through `drainConvertQueue` and claims each id
1654
+ // before the scene commit (deferred glyphs / INSERT text). Headless
1655
+ // export used to scan here as soon as parse returned, while that drain
1656
+ // was still running. Claimed ids are not in the scene yet, so they
1657
+ // looked missing and `batchConvert({ forExport: true })` expanded them
1658
+ // again — serially, because export convert does not defer glyph work.
1659
+ // On a text-heavy drawing that second pass dominates wall time and
1660
+ // leaves duplicate geometry in the snapshot. Wait until the open drain
1661
+ // has claimed or committed every streamed entity, then skip those ids.
1662
+ return [4 /*yield*/, this.drainConvertQueue()
1663
+ // Paper-space tabs the user never visited exist in the layout table but
1664
+ // may be missing from the scene until first switch. HTML export needs
1665
+ // every layout's geometry, so register those BTRs before collecting.
1666
+ ];
1667
+ case 1:
1668
+ // Open streams entities through `drainConvertQueue` and claims each id
1669
+ // before the scene commit (deferred glyphs / INSERT text). Headless
1670
+ // export used to scan here as soon as parse returned, while that drain
1671
+ // was still running. Claimed ids are not in the scene yet, so they
1672
+ // looked missing and `batchConvert({ forExport: true })` expanded them
1673
+ // again — serially, because export convert does not defer glyph work.
1674
+ // On a text-heavy drawing that second pass dominates wall time and
1675
+ // leaves duplicate geometry in the snapshot. Wait until the open drain
1676
+ // has claimed or committed every streamed entity, then skip those ids.
1677
+ _j.sent();
1483
1678
  layoutTable = (_h = db.objects) === null || _h === void 0 ? void 0 : _h.layout;
1484
1679
  if (includeLayouts && (layoutTable === null || layoutTable === void 0 ? void 0 : layoutTable.newIterator)) {
1485
1680
  try {
@@ -1509,7 +1704,7 @@ var AcTrView2d = /** @class */ (function (_super) {
1509
1704
  if (!blockTableRecord) {
1510
1705
  continue;
1511
1706
  }
1512
- pending.push.apply(pending, __spreadArray([], __read(this._entityDisplay.collectMissingEntitiesForExport(blockTableRecord, function (objectId) { return _this.hasEntity(objectId); }, includeInvisibleLayers)), false));
1707
+ pending.push.apply(pending, __spreadArray([], __read(this._entityDisplay.collectMissingEntitiesForExport(blockTableRecord, function (objectId) { return _this.isEntityConvertScheduled(objectId); }, includeInvisibleLayers)), false));
1513
1708
  }
1514
1709
  }
1515
1710
  catch (e_5_1) { e_5 = { error: e_5_1 }; }
@@ -1519,14 +1714,14 @@ var AcTrView2d = /** @class */ (function (_super) {
1519
1714
  }
1520
1715
  finally { if (e_5) throw e_5.error; }
1521
1716
  }
1522
- if (!(pending.length > 0)) return [3 /*break*/, 2];
1717
+ if (!(pending.length > 0)) return [3 /*break*/, 3];
1523
1718
  this._numOfEntitiesToProcess += pending.length;
1524
1719
  return [4 /*yield*/, this.batchConvert(pending, { forExport: true })];
1525
- case 1:
1720
+ case 2:
1526
1721
  _j.sent();
1527
- _j.label = 2;
1528
- case 2: return [4 /*yield*/, this.waitUntilIdle()];
1529
- case 3:
1722
+ _j.label = 3;
1723
+ case 3: return [4 /*yield*/, this.waitUntilIdle(120000)];
1724
+ case 4:
1530
1725
  idle = _j.sent();
1531
1726
  if (!idle) {
1532
1727
  log.warn('[AcTrView2d] Timed out waiting for deferred geometry before export');
@@ -2035,6 +2230,37 @@ var AcTrView2d = /** @class */ (function (_super) {
2035
2230
  var sortedResults = sortPickResults(results, point);
2036
2231
  return pickOneOnly ? sortedResults.slice(0, 1) : sortedResults;
2037
2232
  };
2233
+ /**
2234
+ * @inheritdoc
2235
+ */
2236
+ AcTrView2d.prototype.getViewportCoordinateMapper = function (viewportId) {
2237
+ var e_7, _a;
2238
+ var _b, _c;
2239
+ var vpView;
2240
+ try {
2241
+ for (var _d = __values((_c = (_b = this.activeLayoutView) === null || _b === void 0 ? void 0 : _b.viewportViews) !== null && _c !== void 0 ? _c : []), _e = _d.next(); !_e.done; _e = _d.next()) {
2242
+ var candidate = _e.value;
2243
+ if (candidate.viewport.id === viewportId) {
2244
+ vpView = candidate;
2245
+ break;
2246
+ }
2247
+ }
2248
+ }
2249
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
2250
+ finally {
2251
+ try {
2252
+ if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
2253
+ }
2254
+ finally { if (e_7) throw e_7.error; }
2255
+ }
2256
+ if (!vpView)
2257
+ return undefined;
2258
+ return {
2259
+ paperToModel: function (point) { return vpView.paperPointToModel(point); },
2260
+ modelToPaper: function (point) { return vpView.modelPointToPaper(point); },
2261
+ containsPaperPoint: function (point) { return vpView.containsPaperPoint(point); }
2262
+ };
2263
+ };
2038
2264
  /**
2039
2265
  * Resolves hits against the model-space layout for each viewport the
2040
2266
  * click drills through. Appends the matches into `results` (caller
@@ -2052,7 +2278,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2052
2278
  * paper-space pick.
2053
2279
  */
2054
2280
  AcTrView2d.prototype.pickThroughViewports = function (paperPoint, paperBox, viewports, results) {
2055
- var e_7, _a;
2281
+ var e_8, _a;
2056
2282
  var modelLayout = this._scene.modelSpaceLayout;
2057
2283
  if (!modelLayout)
2058
2284
  return;
@@ -2077,7 +2303,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2077
2303
  if (!isEffectiveSpatialQueryHit(item))
2078
2304
  return;
2079
2305
  if (modelLayout.isIntersectWith(item.id, vpRaycaster)) {
2080
- results.push(item);
2306
+ results.push(__assign(__assign({}, item), { viewportId: vpView.viewport.id }));
2081
2307
  }
2082
2308
  });
2083
2309
  };
@@ -2087,12 +2313,12 @@ var AcTrView2d = /** @class */ (function (_super) {
2087
2313
  _loop_1(vpView);
2088
2314
  }
2089
2315
  }
2090
- catch (e_7_1) { e_7 = { error: e_7_1 }; }
2316
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
2091
2317
  finally {
2092
2318
  try {
2093
2319
  if (viewports_1_1 && !viewports_1_1.done && (_a = viewports_1.return)) _a.call(viewports_1);
2094
2320
  }
2095
- finally { if (e_7) throw e_7.error; }
2321
+ finally { if (e_8) throw e_8.error; }
2096
2322
  }
2097
2323
  };
2098
2324
  /**
@@ -2125,7 +2351,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2125
2351
  * that interior also selects those polylines and can stall highlight work.
2126
2352
  */
2127
2353
  AcTrView2d.prototype.refineCrossingSelectionHits = function (box, results) {
2128
- var e_8, _a;
2354
+ var e_9, _a;
2129
2355
  var _b;
2130
2356
  var database = (_b = AcApDocManager.instance.curDocument) === null || _b === void 0 ? void 0 : _b.database;
2131
2357
  if (!database) {
@@ -2150,12 +2376,12 @@ var AcTrView2d = /** @class */ (function (_super) {
2150
2376
  }
2151
2377
  }
2152
2378
  }
2153
- catch (e_8_1) { e_8 = { error: e_8_1 }; }
2379
+ catch (e_9_1) { e_9 = { error: e_9_1 }; }
2154
2380
  finally {
2155
2381
  try {
2156
2382
  if (results_1_1 && !results_1_1.done && (_a = results_1.return)) _a.call(results_1);
2157
2383
  }
2158
- finally { if (e_8) throw e_8.error; }
2384
+ finally { if (e_9) throw e_9.error; }
2159
2385
  }
2160
2386
  return ids;
2161
2387
  };
@@ -2171,7 +2397,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2171
2397
  * @inheritdoc
2172
2398
  */
2173
2399
  AcTrView2d.prototype.updateLayer = function (layer, changes) {
2174
- var e_9, _a;
2400
+ var e_10, _a;
2175
2401
  var touchedObjectIds = this._scene.updateLayer(this.toLayerInfo(layer)).touchedObjectIds;
2176
2402
  if (this._layerAppearance.layerStyleMayHaveChanged(changes)) {
2177
2403
  this._layerAppearance.syncFromLiveRecord(layer);
@@ -2193,12 +2419,12 @@ var AcTrView2d = /** @class */ (function (_super) {
2193
2419
  this.applySessionHiddenObjectState(objectId);
2194
2420
  }
2195
2421
  }
2196
- catch (e_9_1) { e_9 = { error: e_9_1 }; }
2422
+ catch (e_10_1) { e_10 = { error: e_10_1 }; }
2197
2423
  finally {
2198
2424
  try {
2199
2425
  if (touchedObjectIds_1_1 && !touchedObjectIds_1_1.done && (_a = touchedObjectIds_1.return)) _a.call(touchedObjectIds_1);
2200
2426
  }
2201
- finally { if (e_9) throw e_9.error; }
2427
+ finally { if (e_10) throw e_10.error; }
2202
2428
  }
2203
2429
  this._isDirty = true;
2204
2430
  };
@@ -2359,6 +2585,17 @@ var AcTrView2d = /** @class */ (function (_super) {
2359
2585
  AcTrView2d.prototype.hasEntity = function (objectId) {
2360
2586
  return this._scene.hasEntity(objectId);
2361
2587
  };
2588
+ /**
2589
+ * True when an entity is already in the scene or claimed by an in-flight
2590
+ * convert (including deferred glyph commits that have not called
2591
+ * `addEntity` yet).
2592
+ */
2593
+ AcTrView2d.prototype.isEntityConvertScheduled = function (objectId) {
2594
+ var id = String(objectId !== null && objectId !== void 0 ? objectId : '');
2595
+ if (!id)
2596
+ return false;
2597
+ return this.hasEntity(objectId) || this._claimedConvertObjectIds.has(id);
2598
+ };
2362
2599
  /**
2363
2600
  * @inheritdoc
2364
2601
  */
@@ -2461,7 +2698,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2461
2698
  * drawable entity that must be rebuilt (for example ATTRIB → INSERT).
2462
2699
  */
2463
2700
  AcTrView2d.prototype.resolveSceneUpdateEntities = function (entities) {
2464
- var e_10, _a;
2701
+ var e_11, _a;
2465
2702
  var _b;
2466
2703
  var db = (_b = AcApDocManager.instance.curDocument) === null || _b === void 0 ? void 0 : _b.database;
2467
2704
  var resolved = [];
@@ -2483,12 +2720,12 @@ var AcTrView2d = /** @class */ (function (_super) {
2483
2720
  resolved.push(target);
2484
2721
  }
2485
2722
  }
2486
- catch (e_10_1) { e_10 = { error: e_10_1 }; }
2723
+ catch (e_11_1) { e_11 = { error: e_11_1 }; }
2487
2724
  finally {
2488
2725
  try {
2489
2726
  if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1);
2490
2727
  }
2491
- finally { if (e_10) throw e_10.error; }
2728
+ finally { if (e_11) throw e_11.error; }
2492
2729
  }
2493
2730
  return resolved;
2494
2731
  };
@@ -2517,6 +2754,17 @@ var AcTrView2d = /** @class */ (function (_super) {
2517
2754
  this._initializedLayouts.add(layoutBtrId);
2518
2755
  this._externallyFramedLayouts.add(layoutBtrId);
2519
2756
  };
2757
+ /**
2758
+ * Whether the layout has already been visited (or framed by the
2759
+ * application layer), i.e. a switch to it will not run the first-visit
2760
+ * auto zoom. Lets callers that frame a layout themselves (FIND) call
2761
+ * {@link markLayoutAsInitialized} only when it is still needed.
2762
+ *
2763
+ * @param layoutBtrId Input the block table record id of the layout
2764
+ */
2765
+ AcTrView2d.prototype.isLayoutInitialized = function (layoutBtrId) {
2766
+ return this._initializedLayouts.has(layoutBtrId);
2767
+ };
2520
2768
  /**
2521
2769
  * Returns the 2D box of drawable geometry in the active layout.
2522
2770
  *
@@ -2610,20 +2858,31 @@ var AcTrView2d = /** @class */ (function (_super) {
2610
2858
  waiter.start();
2611
2859
  };
2612
2860
  /**
2613
- * @inheritdoc
2861
+ * Invalidates in-flight progressive convert and empties convert / deferred
2862
+ * queues without tearing down the renderer or DOM.
2863
+ *
2864
+ * Call before {@link AcApDocManager.destroy} tears down documents so
2865
+ * in-flight commits do not keep reading the manager singleton.
2614
2866
  */
2615
- AcTrView2d.prototype.clear = function () {
2867
+ AcTrView2d.prototype.abortConversion = function () {
2616
2868
  // Invalidate any in-flight progressive convert so it neither paints into
2617
2869
  // the cleared scene nor double-decrements the processing counter.
2618
2870
  this._convertEpoch++;
2619
2871
  this.clearFontLoadedRedrawTimer();
2620
2872
  this._convertQueue.length = 0;
2873
+ this._viewportRepairOwnerIds.clear();
2621
2874
  this._numOfEntitiesToProcess = 0;
2622
2875
  this._claimedConvertObjectIds.clear();
2623
2876
  this._entityConvertGeneration.clear();
2624
2877
  this.cancelOpenLineworkFrame();
2625
2878
  this.resetDeferredGeometryQueue();
2626
2879
  this._entityProcessingIdleAt = 0;
2880
+ };
2881
+ /**
2882
+ * @inheritdoc
2883
+ */
2884
+ AcTrView2d.prototype.clear = function () {
2885
+ this.abortConversion();
2627
2886
  this._scene.clear();
2628
2887
  this._renderer.resetDrawOrderZ();
2629
2888
  this._isDirty = true;
@@ -2664,6 +2923,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2664
2923
  this.clearFontLoadedRedrawTimer();
2665
2924
  this.cancelOpenLineworkFrame();
2666
2925
  this._convertQueue.length = 0;
2926
+ this._viewportRepairOwnerIds.clear();
2667
2927
  this._numOfEntitiesToProcess = 0;
2668
2928
  this._claimedConvertObjectIds.clear();
2669
2929
  this._entityConvertGeneration.clear();
@@ -2696,6 +2956,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2696
2956
  this.clearFontLoadedRedrawTimer();
2697
2957
  this.cancelOpenLineworkFrame();
2698
2958
  this._convertQueue.length = 0;
2959
+ this._viewportRepairOwnerIds.clear();
2699
2960
  this._numOfEntitiesToProcess = 0;
2700
2961
  this._claimedConvertObjectIds.clear();
2701
2962
  this._entityConvertGeneration.clear();
@@ -2738,12 +2999,44 @@ var AcTrView2d = /** @class */ (function (_super) {
2738
2999
  /**
2739
3000
  * True when this view is the document manager's live canvas.
2740
3001
  * Satellite/preview views must ignore global shortcuts and layout switches.
3002
+ * Returns false when no manager exists so post-destroy key handlers do not
3003
+ * reach {@link AcApDocManager.instance}.
2741
3004
  */
2742
3005
  AcTrView2d.prototype.isActiveManagedView = function () {
2743
3006
  var _a;
2744
3007
  var singleton = AcApDocManager;
2745
3008
  var current = (_a = singleton._instance) === null || _a === void 0 ? void 0 : _a.curView;
2746
- return current == null || current === this;
3009
+ return current != null && current === this;
3010
+ };
3011
+ /**
3012
+ * Moves keyboard focus into the viewer container after canvas interaction so
3013
+ * document-level shortcuts stay scoped to this view.
3014
+ *
3015
+ * Skips host editable fields so wheel-over-canvas does not interrupt typing.
3016
+ */
3017
+ AcTrView2d.prototype.focusViewerForShortcuts = function () {
3018
+ var active = document.activeElement;
3019
+ if (active instanceof Node && this.container.contains(active)) {
3020
+ return;
3021
+ }
3022
+ if (active instanceof HTMLElement &&
3023
+ (active.tagName === 'INPUT' ||
3024
+ active.tagName === 'TEXTAREA' ||
3025
+ active.isContentEditable)) {
3026
+ return;
3027
+ }
3028
+ this.container.focus({ preventScroll: true });
3029
+ };
3030
+ /**
3031
+ * True when the key event originated from (or is focused within) this view.
3032
+ */
3033
+ AcTrView2d.prototype.isShortcutEventInsideViewer = function (e) {
3034
+ var target = e.target;
3035
+ if (target instanceof Node && this.container.contains(target)) {
3036
+ return true;
3037
+ }
3038
+ var active = document.activeElement;
3039
+ return active instanceof Node && this.container.contains(active);
2747
3040
  };
2748
3041
  /**
2749
3042
  * Drains the convert queue on a single serial worker so ENTITY flush chunks
@@ -2770,21 +3063,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2770
3063
  _a.label = 3;
2771
3064
  case 3: return [2 /*return*/];
2772
3065
  case 4:
2773
- this._convertDrainPromise = (function () { return __awaiter(_this, void 0, void 0, function () {
2774
- var batch;
2775
- return __generator(this, function (_a) {
2776
- switch (_a.label) {
2777
- case 0:
2778
- if (!(this._convertQueue.length > 0)) return [3 /*break*/, 2];
2779
- batch = this._convertQueue.splice(0, this._convertQueue.length);
2780
- return [4 /*yield*/, this.batchConvert(batch)];
2781
- case 1:
2782
- _a.sent();
2783
- return [3 /*break*/, 0];
2784
- case 2: return [2 /*return*/];
2785
- }
2786
- });
2787
- }); })().finally(function () {
3066
+ this._convertDrainPromise = this.runConvertDrain().finally(function () {
2788
3067
  _this._convertDrainPromise = null;
2789
3068
  });
2790
3069
  return [4 /*yield*/, this._convertDrainPromise];
@@ -2795,9 +3074,188 @@ var AcTrView2d = /** @class */ (function (_super) {
2795
3074
  });
2796
3075
  });
2797
3076
  };
3077
+ /**
3078
+ * Converts the queued entities in slices, active layout first.
3079
+ *
3080
+ * `drainConvertQueue` used to hand the whole queue to one
3081
+ * {@link batchConvert}. Model-space block references queued ahead of the
3082
+ * sheet then blocked the sheet's frame and viewports for the entire batch,
3083
+ * and a layout activated during that batch stayed starved until it
3084
+ * finished. Slices return here so a newly active layout is ordered ahead
3085
+ * of the remainder. Paper-active opens use a modest slice budget so the
3086
+ * sheet can paint soon; model-active opens match the inner
3087
+ * {@link batchConvert} yield cadence and grow slices for throughput so
3088
+ * Model-first drawings are not slowed by fine outer slicing. Headless
3089
+ * opens (`cooperativeYield === false`) still convert in one batch after
3090
+ * that reorder.
3091
+ */
3092
+ AcTrView2d.prototype.runConvertDrain = function () {
3093
+ return __awaiter(this, void 0, void 0, function () {
3094
+ var epoch, interactive, slice, lastYieldAt, state, queued, activeNow, activeChanged, end, startedAt, took, modelActive, modelYieldBudget, budget;
3095
+ return __generator(this, function (_a) {
3096
+ switch (_a.label) {
3097
+ case 0:
3098
+ epoch = this._convertEpoch;
3099
+ interactive = this._cooperativeYield;
3100
+ slice = !interactive
3101
+ ? Number.POSITIVE_INFINITY
3102
+ : this.activeLayoutBtrId === this.modelSpaceBtrId
3103
+ ? AcTrView2d.CONVERT_SLICE_MAX
3104
+ : 1;
3105
+ lastYieldAt = performance.now();
3106
+ state = createConvertPending(this._convertQueue.splice(0, this._convertQueue.length), this.activeLayoutBtrId);
3107
+ this.repairViewportViewsFor(state.activeOwnerId);
3108
+ _a.label = 1;
3109
+ case 1:
3110
+ if (!(state.cursor < state.pending.length ||
3111
+ this._convertQueue.length > 0)) return [3 /*break*/, 12];
3112
+ if (this._convertEpoch !== epoch)
3113
+ return [2 /*return*/];
3114
+ queued = this._convertQueue.splice(0, this._convertQueue.length);
3115
+ activeNow = this.activeLayoutBtrId;
3116
+ activeChanged = activeNow !== state.activeOwnerId;
3117
+ state = absorbConvertQueue(state, queued, activeNow);
3118
+ if (activeChanged) {
3119
+ this.repairViewportViewsFor(activeNow);
3120
+ // Drop back to a small slice so the newly active layout's own
3121
+ // entities are not buried inside a leftover large model-space bite.
3122
+ if (interactive)
3123
+ slice = 1;
3124
+ }
3125
+ this.flushViewportViewRepairs();
3126
+ if (state.cursor >= state.pending.length)
3127
+ return [3 /*break*/, 12];
3128
+ end = Math.min(state.pending.length, state.cursor + slice);
3129
+ startedAt = performance.now();
3130
+ return [4 /*yield*/, this.batchConvert(state.pending.slice(state.cursor, end))];
3131
+ case 2:
3132
+ _a.sent();
3133
+ state.cursor = end;
3134
+ if (this._convertEpoch !== epoch)
3135
+ return [2 /*return*/];
3136
+ if (!interactive)
3137
+ return [3 /*break*/, 1];
3138
+ took = performance.now() - startedAt;
3139
+ modelActive = state.activeOwnerId === this.modelSpaceBtrId;
3140
+ modelYieldBudget = this._progressiveRendering
3141
+ ? AcTrView2d.PROGRESSIVE_OPEN_YIELD_BUDGET_MS
3142
+ : AcTrView2d.OPEN_CONVERT_YIELD_BUDGET_MS;
3143
+ budget = modelActive
3144
+ ? modelYieldBudget
3145
+ : AcTrView2d.CONVERT_SLICE_BUDGET_MS;
3146
+ slice = adaptConvertSliceSize({
3147
+ slice: slice,
3148
+ tookMs: took,
3149
+ budgetMs: budget,
3150
+ maxSlice: AcTrView2d.CONVERT_SLICE_MAX,
3151
+ // Fine 2× growth only while a paper sheet still needs converting.
3152
+ // Model-active (or paper already drained) prefers 4× throughput.
3153
+ activeLayoutPending: !modelActive && hasPendingActiveOwner(state)
3154
+ });
3155
+ if (!(this._progressiveRendering &&
3156
+ this.isProgressiveGesturePauseActive())) return [3 /*break*/, 7];
3157
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
3158
+ case 3:
3159
+ _a.sent();
3160
+ _a.label = 4;
3161
+ case 4:
3162
+ if (!(performance.now() < this._progressiveInputPauseUntil)) return [3 /*break*/, 6];
3163
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 16); })];
3164
+ case 5:
3165
+ _a.sent();
3166
+ return [3 /*break*/, 4];
3167
+ case 6:
3168
+ lastYieldAt = performance.now();
3169
+ if (this._convertEpoch !== epoch)
3170
+ return [2 /*return*/];
3171
+ return [3 /*break*/, 11];
3172
+ case 7:
3173
+ if (!(this._progressiveRendering &&
3174
+ this.isProgressiveDiscreteInputPending())) return [3 /*break*/, 9];
3175
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
3176
+ case 8:
3177
+ _a.sent();
3178
+ lastYieldAt = performance.now();
3179
+ if (this._convertEpoch !== epoch)
3180
+ return [2 /*return*/];
3181
+ return [3 /*break*/, 11];
3182
+ case 9:
3183
+ if (!(performance.now() - lastYieldAt >= budget)) return [3 /*break*/, 11];
3184
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
3185
+ case 10:
3186
+ _a.sent();
3187
+ lastYieldAt = performance.now();
3188
+ if (this._convertEpoch !== epoch)
3189
+ return [2 /*return*/];
3190
+ _a.label = 11;
3191
+ case 11: return [3 /*break*/, 1];
3192
+ case 12:
3193
+ this.repairViewportViewsFor(this.activeLayoutBtrId);
3194
+ this.flushViewportViewRepairs();
3195
+ return [2 /*return*/];
3196
+ }
3197
+ });
3198
+ });
3199
+ };
3200
+ /**
3201
+ * Creates missing {@link AcTrViewportView}s for one paper layout.
3202
+ *
3203
+ * Safe to call before the layout's entities have been committed to the
3204
+ * scene: the database block table record already holds the viewports.
3205
+ * Model space is skipped so a large model is not walked on every slice.
3206
+ *
3207
+ * @param layoutBtrId - Block table record id of the layout to repair.
3208
+ */
3209
+ AcTrView2d.prototype.repairViewportViewsFor = function (layoutBtrId) {
3210
+ var _a;
3211
+ if (!layoutBtrId || layoutBtrId === this.modelSpaceBtrId)
3212
+ return;
3213
+ var layoutView = this._layoutViewManager.getAt(layoutBtrId);
3214
+ if (!layoutView || layoutView.viewportCount > 0) {
3215
+ if (layoutView && layoutView.viewportCount > 0) {
3216
+ this._viewportRepairOwnerIds.delete(layoutBtrId);
3217
+ }
3218
+ return;
3219
+ }
3220
+ try {
3221
+ var db = (_a = AcApDocManager.instance.curDocument) === null || _a === void 0 ? void 0 : _a.database;
3222
+ var blockTableRecord = db === null || db === void 0 ? void 0 : db.tables.blockTable.getIdAt(layoutBtrId);
3223
+ if (!blockTableRecord)
3224
+ return;
3225
+ var before = layoutView.viewportCount;
3226
+ this.ensureViewportViews(blockTableRecord, layoutView);
3227
+ if (layoutView.viewportCount > before) {
3228
+ this._isDirty = true;
3229
+ }
3230
+ // The block table record was readable. Drop the id even when every
3231
+ // viewport was the default paper-space one, so a model-sized record
3232
+ // is not walked again on the next slice.
3233
+ this._viewportRepairOwnerIds.delete(layoutBtrId);
3234
+ }
3235
+ catch (error) {
3236
+ log.error('[AcTrView2d] Error repairing viewport views:', error);
3237
+ }
3238
+ };
3239
+ /**
3240
+ * Retries viewport-view creation for owners recorded while their layout
3241
+ * view did not exist yet.
3242
+ */
3243
+ AcTrView2d.prototype.flushViewportViewRepairs = function () {
3244
+ if (this._viewportRepairOwnerIds.size === 0)
3245
+ return;
3246
+ var ownerIds = Array.from(this._viewportRepairOwnerIds);
3247
+ for (var i = 0; i < ownerIds.length; i++) {
3248
+ this.repairViewportViewsFor(ownerIds[i]);
3249
+ }
3250
+ };
2798
3251
  /**
2799
3252
  * Marks the canvas dirty for progressive open, throttled to avoid painting
2800
3253
  * on every entity (which dominated total open time).
3254
+ *
3255
+ * Does not stamp {@link _lastProgressivePaintAt} — that updates only when
3256
+ * {@link animate} actually paints. Does not set
3257
+ * {@link _progressivePaintUrgent}; camera moves do that so pan/zoom stay
3258
+ * immediate while convert-driven paints remain rare.
2801
3259
  */
2802
3260
  AcTrView2d.prototype.markProgressiveDirty = function (force) {
2803
3261
  if (force === void 0) { force = false; }
@@ -2806,9 +3264,28 @@ var AcTrView2d = /** @class */ (function (_super) {
2806
3264
  now - this._lastProgressivePaintAt >=
2807
3265
  AcTrView2d.PROGRESSIVE_OPEN_PAINT_INTERVAL_MS) {
2808
3266
  this._isDirty = true;
2809
- this._lastProgressivePaintAt = now;
2810
3267
  }
2811
3268
  };
3269
+ /**
3270
+ * Interactive non-progressive open: dirty the canvas so {@link animate} can
3271
+ * refresh under "Rendering drawing ...". Headless (`cooperativeYield`
3272
+ * false) leaves this unset; the pool-drain / convert-end dirty is enough.
3273
+ * {@link animate} applies {@link NON_PROGRESSIVE_OPEN_PAINT_INTERVAL_MS}.
3274
+ */
3275
+ AcTrView2d.prototype.markInteractiveOpenDirty = function () {
3276
+ if (this._progressiveRendering || !this._cooperativeYield) {
3277
+ return;
3278
+ }
3279
+ this._isDirty = true;
3280
+ };
3281
+ /**
3282
+ * True when this frame's WebGL paint should count toward OPENPROF mid-open
3283
+ * paints and use the open-time throttle (unless the camera marked urgent).
3284
+ */
3285
+ AcTrView2d.prototype.shouldCountMidOpenPaint = function (stillLoading, sceneBusy) {
3286
+ return ((this._progressiveRendering && stillLoading) ||
3287
+ (!this._progressiveRendering && this._cooperativeYield && sceneBusy));
3288
+ };
2812
3289
  /**
2813
3290
  * @inheritdoc
2814
3291
  */
@@ -2858,18 +3335,63 @@ var AcTrView2d = /** @class */ (function (_super) {
2858
3335
  }
2859
3336
  };
2860
3337
  /**
2861
- * Releases canvas DOM listeners and stops the animation loop.
3338
+ * Releases canvas DOM, WebGL context, convert work, and listeners.
2862
3339
  *
2863
3340
  * Call when this view will no longer be used (manager destroy / split
2864
3341
  * view teardown). Safe to call more than once.
2865
3342
  */
2866
3343
  AcTrView2d.prototype.dispose = function () {
2867
- var _a;
3344
+ var _a, _b, _c;
3345
+ if (this._viewDisposed)
3346
+ return;
3347
+ this._viewDisposed = true;
3348
+ // Stop convert before tearing down documents/singletons that commit paths read.
3349
+ this.abortConversion();
2868
3350
  (_a = this._disposeCanvasTouchCallout) === null || _a === void 0 ? void 0 : _a.call(this);
2869
3351
  this._disposeCanvasTouchCallout = undefined;
3352
+ (_b = this._disposeProgressiveInputWatch) === null || _b === void 0 ? void 0 : _b.call(this);
3353
+ this._disposeProgressiveInputWatch = undefined;
3354
+ (_c = this._disposeMobileBoxGesture) === null || _c === void 0 ? void 0 : _c.call(this);
3355
+ this._disposeMobileBoxGesture = undefined;
3356
+ this._viewDomAbort.abort();
3357
+ if (this._onFontNotFound) {
3358
+ this._renderer.events.fontNotFound.removeEventListener(this._onFontNotFound);
3359
+ }
3360
+ if (this._onFontLoaded) {
3361
+ this._renderer.events.fontLoaded.removeEventListener(this._onFontLoaded);
3362
+ }
3363
+ if (this._onSysVarChanged) {
3364
+ AcDbSysVarManager.instance().events.sysVarChanged.removeEventListener(this._onSysVarChanged);
3365
+ }
3366
+ if (this._onSettingsModified) {
3367
+ AcApSettingManager.instance.events.modified.removeEventListener(this._onSettingsModified);
3368
+ }
3369
+ if (this._onLayoutSwitched) {
3370
+ acdbHostApplicationServices().layoutManager.events.layoutSwitched.removeEventListener(this._onLayoutSwitched);
3371
+ }
3372
+ if (this._onCommandWillStartHitTest) {
3373
+ this.editor.events.commandWillStart.removeEventListener(this._onCommandWillStartHitTest);
3374
+ }
3375
+ if (this._onCommandEndedHitTest) {
3376
+ this.editor.events.commandEnded.removeEventListener(this._onCommandEndedHitTest);
3377
+ }
3378
+ this._gripManager.dispose();
2870
3379
  this.clearFontLoadedRedrawTimer();
2871
3380
  this.cancelOpenLineworkFrame();
2872
3381
  this.stopAnimationLoop();
3382
+ this.selectionSet.clear();
3383
+ this.htmlTransientManager.dispose();
3384
+ this._scene.clear();
3385
+ this._renderer.dispose();
3386
+ AcDbRenderingCache.instance.clear();
3387
+ var webgl = this._renderer.internalRenderer;
3388
+ webgl.dispose();
3389
+ webgl.forceContextLoss();
3390
+ this.canvas.remove();
3391
+ this._css2dRenderer.domElement.remove();
3392
+ this._stats.dom.remove();
3393
+ // Editor / command line / ResizeObserver (after canvas/CSS2D removed).
3394
+ _super.prototype.dispose.call(this);
2873
3395
  };
2874
3396
  /**
2875
3397
  * @inheritdoc
@@ -2934,11 +3456,19 @@ var AcTrView2d = /** @class */ (function (_super) {
2934
3456
  layoutView.events.viewChanged.addEventListener(function () {
2935
3457
  _this._progressiveOpenFit.onLayoutViewChanged();
2936
3458
  _this._isDirty = true;
3459
+ // Bypass mid-open paint throttle so pan/zoom (and incremental fit)
3460
+ // remain visible while convert continues.
3461
+ if (_this._progressiveRendering && _this._numOfEntitiesToProcess > 0) {
3462
+ _this._progressivePaintUrgent = true;
3463
+ }
2937
3464
  _this.events.viewChanged.dispatch();
2938
3465
  _this.clearHover();
2939
3466
  });
2940
3467
  this._layoutViewManager.add(layoutView);
2941
3468
  }
3469
+ // Viewport entities may already have been converted while this view was
3470
+ // missing. Creating it is the moment those AcTrViewportViews can exist.
3471
+ this.flushViewportViewRepairs();
2942
3472
  return layoutView;
2943
3473
  };
2944
3474
  /**
@@ -2963,7 +3493,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2963
3493
  * @param layoutBtrId Input the block table record id of the layout
2964
3494
  */
2965
3495
  AcTrView2d.prototype.loadLayoutEntitiesIfNeeded = function (layoutBtrId) {
2966
- var e_11, _a;
3496
+ var e_12, _a;
2967
3497
  var _this = this;
2968
3498
  try {
2969
3499
  var db = AcApDocManager.instance.curDocument.database;
@@ -2974,41 +3504,20 @@ var AcTrView2d = /** @class */ (function (_super) {
2974
3504
  var existingLayout = this._scene.layouts.get(layoutBtrId);
2975
3505
  if (existingLayout && existingLayout.isLoaded) {
2976
3506
  // Streamed layouts flip `isLoaded` from `addEntity` before scene
2977
- // commits finish. Still repair the viewport-view race when entities
2978
- // are already in the scene but AcTrViewportView creation was skipped.
2979
- var layoutView_1 = this._layoutViewManager.getAt(layoutBtrId);
2980
- if (existingLayout.entityCount > 0 &&
2981
- layoutView_1 &&
2982
- layoutView_1.viewportCount === 0) {
2983
- this.ensureViewportViews(blockTableRecord, layoutView_1);
2984
- }
3507
+ // commits finish (`entityCount` still 0). Viewport views are read
3508
+ // from the block table record, so repair them even while the sheet's
3509
+ // own entities are still queued behind model space.
3510
+ this.repairViewportViewsFor(layoutBtrId);
2985
3511
  return;
2986
3512
  }
2987
3513
  if (this._loadingLayouts.has(layoutBtrId)) {
2988
3514
  return;
2989
3515
  }
2990
- // Ensure `AcTrViewportView`s exist for every real `AcDbViewport`
2991
- // in this layout when the layout's entities were already streamed
2992
- // in by the document parser. There is a race in the parser-driven
2993
- // load path: `addLayout(layout)` creates the `AcTrLayoutView`,
2994
- // but the parser may dispatch the AcDbViewport entities before
2995
- // that happens. When that races, `batchConvert`'s viewport
2996
- // handler does `_layoutViewManager.getAt(entity.ownerId)`, gets
2997
- // `undefined`, and **silently skips creating the
2998
- // AcTrViewportView**. The reload path below used to mask this by
2999
- // re-running batchConvert after the layoutView existed, but the
3000
- // entityCount-skip optimization that follows removes that
3001
- // side-effect, so we do the viewport-view-only pass explicitly
3002
- // here. Skipped when the layout is empty — in that case the
3003
- // batchConvert path below will create the viewport views directly
3004
- // as it processes each entity.
3005
- var layoutView = this._layoutViewManager.getAt(layoutBtrId);
3006
- if (existingLayout &&
3007
- existingLayout.entityCount > 0 &&
3008
- layoutView &&
3009
- layoutView.viewportCount === 0) {
3010
- this.ensureViewportViews(blockTableRecord, layoutView);
3011
- }
3516
+ // Same repair as the isLoaded path: the layout view can exist before
3517
+ // any of its entities have been committed to the scene. Viewport
3518
+ // entities converted before `addLayout` created that view are recorded
3519
+ // in `_viewportRepairOwnerIds` and attached here.
3520
+ this.repairViewportViewsFor(layoutBtrId);
3012
3521
  // Model space (and any other layout pre-populated by the document
3013
3522
  // parser at open time) lands here without `isLoaded` ever having
3014
3523
  // been flipped — the initial entity stream goes through
@@ -3048,12 +3557,12 @@ var AcTrView2d = /** @class */ (function (_super) {
3048
3557
  entities_2.push(entity);
3049
3558
  }
3050
3559
  }
3051
- catch (e_11_1) { e_11 = { error: e_11_1 }; }
3560
+ catch (e_12_1) { e_12 = { error: e_12_1 }; }
3052
3561
  finally {
3053
3562
  try {
3054
3563
  if (iterator_1_1 && !iterator_1_1.done && (_a = iterator_1.return)) _a.call(iterator_1);
3055
3564
  }
3056
- finally { if (e_11) throw e_11.error; }
3565
+ finally { if (e_12) throw e_12.error; }
3057
3566
  }
3058
3567
  if (entities_2.length === 0) {
3059
3568
  // Empty layout (e.g. a freshly-created paper space tab). Mark as
@@ -3377,8 +3886,8 @@ var AcTrView2d = /** @class */ (function (_super) {
3377
3886
  */
3378
3887
  AcTrView2d.prototype.redrawGlyphEntitiesAfterFontLoad = function (epoch, fontName) {
3379
3888
  return __awaiter(this, void 0, void 0, function () {
3380
- var redrawEpoch, glyphs, _a, _b, layout, glyphs_1, glyphs_1_1, glyph, e_12_1, defaults, normalized_1, msSinceIdle, db;
3381
- var e_13, _c, e_12, _d;
3889
+ var redrawEpoch, glyphs, _a, _b, layout, glyphs_1, glyphs_1_1, glyph, e_13_1, defaults, normalized_1, msSinceIdle, db;
3890
+ var e_14, _c, e_13, _d;
3382
3891
  var _e, _f;
3383
3892
  return __generator(this, function (_g) {
3384
3893
  switch (_g.label) {
@@ -3400,12 +3909,12 @@ var AcTrView2d = /** @class */ (function (_super) {
3400
3909
  });
3401
3910
  }
3402
3911
  }
3403
- catch (e_13_1) { e_13 = { error: e_13_1 }; }
3912
+ catch (e_14_1) { e_14 = { error: e_14_1 }; }
3404
3913
  finally {
3405
3914
  try {
3406
3915
  if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
3407
3916
  }
3408
- finally { if (e_13) throw e_13.error; }
3917
+ finally { if (e_14) throw e_14.error; }
3409
3918
  }
3410
3919
  _g.label = 1;
3411
3920
  case 1:
@@ -3428,14 +3937,14 @@ var AcTrView2d = /** @class */ (function (_super) {
3428
3937
  return [3 /*break*/, 2];
3429
3938
  case 5: return [3 /*break*/, 8];
3430
3939
  case 6:
3431
- e_12_1 = _g.sent();
3432
- e_12 = { error: e_12_1 };
3940
+ e_13_1 = _g.sent();
3941
+ e_13 = { error: e_13_1 };
3433
3942
  return [3 /*break*/, 8];
3434
3943
  case 7:
3435
3944
  try {
3436
3945
  if (glyphs_1_1 && !glyphs_1_1.done && (_d = glyphs_1.return)) _d.call(glyphs_1);
3437
3946
  }
3438
- finally { if (e_12) throw e_12.error; }
3947
+ finally { if (e_13) throw e_13.error; }
3439
3948
  return [7 /*endfinally*/];
3440
3949
  case 8:
3441
3950
  if (glyphs.length > 0) {
@@ -3486,7 +3995,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3486
3995
  });
3487
3996
  };
3488
3997
  AcTrView2d.prototype.groupHasPendingGlyphGeometry = function (group) {
3489
- var e_14, _a;
3998
+ var e_15, _a;
3490
3999
  try {
3491
4000
  // Prefer O(block-def shells). A full THREE.traverse over a mega-block
3492
4001
  // (100k+ flattened mesh leaves) was dominating scene-convert wall time.
@@ -3497,12 +4006,12 @@ var AcTrView2d = /** @class */ (function (_super) {
3497
4006
  }
3498
4007
  }
3499
4008
  }
3500
- catch (e_14_1) { e_14 = { error: e_14_1 }; }
4009
+ catch (e_15_1) { e_15 = { error: e_15_1 }; }
3501
4010
  finally {
3502
4011
  try {
3503
4012
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
3504
4013
  }
3505
- finally { if (e_14) throw e_14.error; }
4014
+ finally { if (e_15) throw e_15.error; }
3506
4015
  }
3507
4016
  return AcTrView2d.directChildrenHavePendingGlyphs(group);
3508
4017
  };
@@ -3513,7 +4022,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3513
4022
  * a whole-floor INSERT is O(glyph shells), not O(all drawable leaves).
3514
4023
  */
3515
4024
  AcTrView2d.directChildrenHavePendingGlyphs = function (root) {
3516
- var e_15, _a;
4025
+ var e_16, _a;
3517
4026
  var children = root.children;
3518
4027
  for (var i = 0; i < children.length; i++) {
3519
4028
  var child = children[i];
@@ -3525,7 +4034,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3525
4034
  }
3526
4035
  if (child instanceof AcTrGroup) {
3527
4036
  try {
3528
- for (var _b = (e_15 = void 0, __values(child.getSourceEntities())), _c = _b.next(); !_c.done; _c = _b.next()) {
4037
+ for (var _b = (e_16 = void 0, __values(child.getSourceEntities())), _c = _b.next(); !_c.done; _c = _b.next()) {
3529
4038
  var entity = _c.value;
3530
4039
  if (entity instanceof AcTrGlyphEntity &&
3531
4040
  !entity.hasDrawableGeometry()) {
@@ -3533,12 +4042,12 @@ var AcTrView2d = /** @class */ (function (_super) {
3533
4042
  }
3534
4043
  }
3535
4044
  }
3536
- catch (e_15_1) { e_15 = { error: e_15_1 }; }
4045
+ catch (e_16_1) { e_16 = { error: e_16_1 }; }
3537
4046
  finally {
3538
4047
  try {
3539
4048
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
3540
4049
  }
3541
- finally { if (e_15) throw e_15.error; }
4050
+ finally { if (e_16) throw e_16.error; }
3542
4051
  }
3543
4052
  if (AcTrView2d.directChildrenHavePendingGlyphs(child)) {
3544
4053
  return true;
@@ -3586,9 +4095,13 @@ var AcTrView2d = /** @class */ (function (_super) {
3586
4095
  void job
3587
4096
  .run()
3588
4097
  .then(function () {
3589
- // Convert counter often hits 0 before fonts finish; without this,
3590
- // text added later never paints until the user pans/zooms.
3591
- if (job.epoch === _this._convertEpoch) {
4098
+ // Progressive: dirty every glyph (animate throttles paints).
4099
+ // Interactive non-progressive: same, under a longer open-paint
4100
+ // interval so "Rendering drawing ..." still shows progress.
4101
+ // Headless non-progressive: skip — the pool-drain dirty below
4102
+ // queues the single frame export/scripts need.
4103
+ if (job.epoch === _this._convertEpoch &&
4104
+ (_this._progressiveRendering || _this._cooperativeYield)) {
3592
4105
  _this._isDirty = true;
3593
4106
  }
3594
4107
  })
@@ -3648,6 +4161,31 @@ var AcTrView2d = /** @class */ (function (_super) {
3648
4161
  });
3649
4162
  });
3650
4163
  };
4164
+ /**
4165
+ * Adds an {@link AcTrViewportView} for a real paper-space viewport.
4166
+ *
4167
+ * When the layout view does not exist yet, the owner id is remembered and
4168
+ * {@link flushViewportViewRepairs} creates the view once the layout view
4169
+ * does. Adding twice for the same viewport id replaces the previous view.
4170
+ *
4171
+ * @param entity - Viewport entity, already past the default-viewport filter
4172
+ * at the caller's discretion. Checked again here.
4173
+ */
4174
+ AcTrView2d.prototype.attachViewportView = function (entity) {
4175
+ if (AcTrViewportView.isDefaultPaperSpaceViewport(entity))
4176
+ return;
4177
+ var ownerId = entity.ownerId;
4178
+ var layoutView = ownerId
4179
+ ? this._layoutViewManager.getAt(ownerId)
4180
+ : undefined;
4181
+ if (!layoutView) {
4182
+ if (ownerId)
4183
+ this._viewportRepairOwnerIds.add(ownerId);
4184
+ return;
4185
+ }
4186
+ layoutView.addViewport(new AcTrViewportView(layoutView, this.toGiViewportWithFrozenLayers(entity), this._renderer));
4187
+ this._isDirty = true;
4188
+ };
3651
4189
  /**
3652
4190
  * Walks the given block table record once and creates one
3653
4191
  * `AcTrViewportView` for every real `AcDbViewport` entity it finds
@@ -3667,7 +4205,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3667
4205
  * paper layouts.
3668
4206
  */
3669
4207
  AcTrView2d.prototype.ensureViewportViews = function (blockTableRecord, layoutView) {
3670
- var e_16, _a;
4208
+ var e_17, _a;
3671
4209
  var iterator = blockTableRecord.newIterator();
3672
4210
  try {
3673
4211
  for (var iterator_2 = __values(iterator), iterator_2_1 = iterator_2.next(); !iterator_2_1.done; iterator_2_1 = iterator_2.next()) {
@@ -3676,17 +4214,55 @@ var AcTrView2d = /** @class */ (function (_super) {
3676
4214
  continue;
3677
4215
  if (AcTrViewportView.isDefaultPaperSpaceViewport(entity))
3678
4216
  continue;
3679
- var viewportView = new AcTrViewportView(layoutView, entity.toGiViewport(), this._renderer);
4217
+ var viewportView = new AcTrViewportView(layoutView, this.toGiViewportWithFrozenLayers(entity), this._renderer);
3680
4218
  layoutView.addViewport(viewportView);
3681
4219
  }
3682
4220
  }
3683
- catch (e_16_1) { e_16 = { error: e_16_1 }; }
4221
+ catch (e_17_1) { e_17 = { error: e_17_1 }; }
3684
4222
  finally {
3685
4223
  try {
3686
4224
  if (iterator_2_1 && !iterator_2_1.done && (_a = iterator_2.return)) _a.call(iterator_2);
3687
4225
  }
3688
- finally { if (e_16) throw e_16.error; }
4226
+ finally { if (e_17) throw e_17.error; }
4227
+ }
4228
+ };
4229
+ /**
4230
+ * Builds an {@link AcGiViewport} and ensures VPLAYER frozen layers are
4231
+ * present as resolved names. Older data-model builds read group 331 into
4232
+ * {@link AcDbViewport.frozenLayerIds} but omit them from `toGiViewport()`.
4233
+ */
4234
+ AcTrView2d.prototype.toGiViewportWithFrozenLayers = function (entity) {
4235
+ var e_18, _a;
4236
+ var _b;
4237
+ var gi = entity.toGiViewport();
4238
+ if (gi.frozenLayers && gi.frozenLayers.length > 0) {
4239
+ return gi;
4240
+ }
4241
+ var ids = entity.frozenLayerIds;
4242
+ if (!(ids === null || ids === void 0 ? void 0 : ids.length))
4243
+ return gi;
4244
+ var names = [];
4245
+ var layerTable = (_b = entity.database) === null || _b === void 0 ? void 0 : _b.tables.layerTable;
4246
+ if (layerTable) {
4247
+ try {
4248
+ for (var ids_1 = __values(ids), ids_1_1 = ids_1.next(); !ids_1_1.done; ids_1_1 = ids_1.next()) {
4249
+ var id = ids_1_1.value;
4250
+ var layer = layerTable.getIdAt(id);
4251
+ if (layer)
4252
+ names.push(layer.name);
4253
+ }
4254
+ }
4255
+ catch (e_18_1) { e_18 = { error: e_18_1 }; }
4256
+ finally {
4257
+ try {
4258
+ if (ids_1_1 && !ids_1_1.done && (_a = ids_1.return)) _a.call(ids_1);
4259
+ }
4260
+ finally { if (e_18) throw e_18.error; }
4261
+ }
3689
4262
  }
4263
+ gi.frozenLayerIds = __spreadArray([], __read(ids), false);
4264
+ gi.frozenLayers = names;
4265
+ return gi;
3690
4266
  };
3691
4267
  /**
3692
4268
  * Converts the specified database entities to three entities
@@ -3695,12 +4271,12 @@ var AcTrView2d = /** @class */ (function (_super) {
3695
4271
  */
3696
4272
  AcTrView2d.prototype.batchConvert = function (entities_3) {
3697
4273
  return __awaiter(this, arguments, void 0, function (entities, options) {
3698
- var epoch, db, progressive, yieldGate, yieldToEventLoop, _loop_4, this_3, i;
4274
+ var epoch, db, progressive, yieldBudgetMs, yieldGate, yieldToEventLoop, _loop_4, this_3, i;
3699
4275
  var _this = this;
3700
- var _a, _b, _c;
4276
+ var _a, _b, _c, _d;
3701
4277
  if (options === void 0) { options = {}; }
3702
- return __generator(this, function (_d) {
3703
- switch (_d.label) {
4278
+ return __generator(this, function (_e) {
4279
+ switch (_e.label) {
3704
4280
  case 0:
3705
4281
  epoch = this._convertEpoch;
3706
4282
  // Fallback: if STYLE-stage preload never started (e.g. non-conversion open
@@ -3713,21 +4289,24 @@ var AcTrView2d = /** @class */ (function (_super) {
3713
4289
  }
3714
4290
  }
3715
4291
  progressive = this._progressiveRendering && !options.forExport;
4292
+ yieldBudgetMs = progressive
4293
+ ? AcTrView2d.PROGRESSIVE_OPEN_YIELD_BUDGET_MS
4294
+ : AcTrView2d.OPEN_CONVERT_YIELD_BUDGET_MS;
3716
4295
  yieldGate = options.forExport || !this._cooperativeYield
3717
4296
  ? undefined
3718
- : new AcCmUiYieldGate(AcTrView2d.OPEN_CONVERT_YIELD_BUDGET_MS);
4297
+ : new AcCmUiYieldGate(yieldBudgetMs);
3719
4298
  yieldToEventLoop = function () {
3720
4299
  return new Promise(function (resolve) { return setTimeout(resolve, 0); });
3721
4300
  };
3722
4301
  _loop_4 = function (i) {
3723
- var entity, shouldConvert, objectId_1, convertGen_1, directMeta, added, superseded, threeEntity_1, isMultiLayerGroup, deferGeometry, isExtendBbox_1, commitEntity, layoutView, viewportView, fileName, error_3, didYield;
3724
- return __generator(this, function (_e) {
3725
- switch (_e.label) {
4302
+ var entity, shouldConvert, objectId_1, convertGen_1, directMeta, added, superseded, threeEntity_1, isMultiLayerGroup, deferGeometry, isExtendBbox_1, commitEntity, fileName, error_3, failedId, didYield;
4303
+ return __generator(this, function (_f) {
4304
+ switch (_f.label) {
3726
4305
  case 0:
3727
4306
  entity = entities[i];
3728
- _e.label = 1;
4307
+ _f.label = 1;
3729
4308
  case 1:
3730
- _e.trys.push([1, 9, 10, 11]);
4309
+ _f.trys.push([1, 9, 10, 11]);
3731
4310
  // Document was cleared / replaced while this batch was draining.
3732
4311
  if (epoch !== this_3._convertEpoch) {
3733
4312
  return [2 /*return*/, "continue"];
@@ -3755,16 +4334,20 @@ var AcTrView2d = /** @class */ (function (_super) {
3755
4334
  }
3756
4335
  objectId_1 = String((_a = entity.objectId) !== null && _a !== void 0 ? _a : '');
3757
4336
  convertGen_1 = 0;
3758
- if (!options.forExport) {
3759
- if (objectId_1 &&
3760
- (this_3.hasEntity(objectId_1) ||
3761
- this_3._claimedConvertObjectIds.has(objectId_1))) {
3762
- return [2 /*return*/, "continue"];
3763
- }
3764
- if (objectId_1) {
3765
- this_3._claimedConvertObjectIds.add(objectId_1);
3766
- convertGen_1 = (_b = this_3._entityConvertGeneration.get(objectId_1)) !== null && _b !== void 0 ? _b : 0;
4337
+ if (objectId_1 &&
4338
+ (this_3.hasEntity(objectId_1) ||
4339
+ this_3._claimedConvertObjectIds.has(objectId_1))) {
4340
+ // The border may already be in the scene from a pass that ran
4341
+ // before the layout view existed. Still attach the viewport
4342
+ // view; a second border is not drawn because we continue below.
4343
+ if (entity instanceof AcDbViewport) {
4344
+ this_3.attachViewportView(entity);
3767
4345
  }
4346
+ return [2 /*return*/, "continue"];
4347
+ }
4348
+ if (objectId_1) {
4349
+ this_3._claimedConvertObjectIds.add(objectId_1);
4350
+ convertGen_1 = (_b = this_3._entityConvertGeneration.get(objectId_1)) !== null && _b !== void 0 ? _b : 0;
3768
4351
  }
3769
4352
  directMeta = tryBuildDirectEntityMeta(entity, this_3._renderer);
3770
4353
  if (directMeta) {
@@ -3783,6 +4366,9 @@ var AcTrView2d = /** @class */ (function (_super) {
3783
4366
  this_3.markProgressiveDirty();
3784
4367
  this_3._progressiveOpenFit.afterGeometryBatch(function () { return _this.getDrawingExtents(); }, i);
3785
4368
  }
4369
+ else {
4370
+ this_3.markInteractiveOpenDirty();
4371
+ }
3786
4372
  }
3787
4373
  }
3788
4374
  }
@@ -3797,8 +4383,14 @@ var AcTrView2d = /** @class */ (function (_super) {
3797
4383
  threeEntity_1 = this_3.drawEntity(entity, false);
3798
4384
  // Viewports may produce no border geometry (e.g. on a no-plot layer) while
3799
4385
  // still needing an AcTrViewportView for model content below.
3800
- if (!threeEntity_1 && !(entity instanceof AcDbViewport))
4386
+ if (!threeEntity_1 && !(entity instanceof AcDbViewport)) {
4387
+ // Release the claim so a later export pass can retry; otherwise
4388
+ // `forExport` claim checks would skip this id forever.
4389
+ if (objectId_1) {
4390
+ this_3._claimedConvertObjectIds.delete(objectId_1);
4391
+ }
3801
4392
  return [2 /*return*/, "continue"];
4393
+ }
3802
4394
  if (!threeEntity_1) return [3 /*break*/, 8];
3803
4395
  threeEntity_1.objectId = entity.objectId;
3804
4396
  threeEntity_1.ownerId = entity.ownerId;
@@ -3823,8 +4415,8 @@ var AcTrView2d = /** @class */ (function (_super) {
3823
4415
  return [3 /*break*/, 4];
3824
4416
  case 2: return [4 /*yield*/, this_3.handleGroup(threeEntity_1, progressive, epoch, convertGen_1)];
3825
4417
  case 3:
3826
- _e.sent();
3827
- _e.label = 4;
4418
+ _f.sent();
4419
+ _f.label = 4;
3828
4420
  case 4: return [3 /*break*/, 8];
3829
4421
  case 5:
3830
4422
  isExtendBbox_1 = !(entity instanceof AcDbRay || entity instanceof AcDbXline);
@@ -3863,6 +4455,9 @@ var AcTrView2d = /** @class */ (function (_super) {
3863
4455
  this.markProgressiveDirty();
3864
4456
  this._progressiveOpenFit.afterGeometryBatch(function () { return _this.getDrawingExtents(); }, i);
3865
4457
  }
4458
+ else {
4459
+ this.markInteractiveOpenDirty();
4460
+ }
3866
4461
  return [2 /*return*/];
3867
4462
  }
3868
4463
  });
@@ -3872,8 +4467,8 @@ var AcTrView2d = /** @class */ (function (_super) {
3872
4467
  return [3 /*break*/, 8];
3873
4468
  case 6: return [4 /*yield*/, commitEntity()];
3874
4469
  case 7:
3875
- _e.sent();
3876
- _e.label = 8;
4470
+ _f.sent();
4471
+ _f.label = 8;
3877
4472
  case 8:
3878
4473
  if (epoch !== this_3._convertEpoch) {
3879
4474
  return [2 /*return*/, "continue"];
@@ -3884,13 +4479,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3884
4479
  // user-created viewport. The redundant check below is kept as
3885
4480
  // a defensive guard in case a future refactor reorders the
3886
4481
  // early-skip — it costs ~nothing and prevents a regression.
3887
- if (!AcTrViewportView.isDefaultPaperSpaceViewport(entity)) {
3888
- layoutView = this_3._layoutViewManager.getAt(entity.ownerId);
3889
- if (layoutView) {
3890
- viewportView = new AcTrViewportView(layoutView, entity.toGiViewport(), this_3._renderer);
3891
- layoutView.addViewport(viewportView);
3892
- }
3893
- }
4482
+ this_3.attachViewportView(entity);
3894
4483
  }
3895
4484
  else if (entity instanceof AcDbRasterImage) {
3896
4485
  fileName = entity.imageFileName;
@@ -3903,7 +4492,11 @@ var AcTrView2d = /** @class */ (function (_super) {
3903
4492
  }
3904
4493
  return [3 /*break*/, 11];
3905
4494
  case 9:
3906
- error_3 = _e.sent();
4495
+ error_3 = _f.sent();
4496
+ failedId = String((_d = entity.objectId) !== null && _d !== void 0 ? _d : '');
4497
+ if (failedId) {
4498
+ this_3._claimedConvertObjectIds.delete(failedId);
4499
+ }
3907
4500
  log.error("[AcTrView2d] Failed to convert entity ".concat(entity.objectId, " (").concat(entity.type, "):"), error_3);
3908
4501
  return [3 /*break*/, 11];
3909
4502
  case 10:
@@ -3917,27 +4510,37 @@ var AcTrView2d = /** @class */ (function (_super) {
3917
4510
  if (epoch !== this_3._convertEpoch) {
3918
4511
  return [2 /*return*/, "continue"];
3919
4512
  }
3920
- if (!yieldGate) return [3 /*break*/, 13];
3921
- return [4 /*yield*/, yieldGate.maybeYield(yieldToEventLoop)];
4513
+ if (!progressive) return [3 /*break*/, 13];
4514
+ // Prefer input-aware yields so pan/zoom are not starved after the
4515
+ // overlay is gone; paint throttling stays in markProgressiveDirty.
4516
+ return [4 /*yield*/, this_3.progressiveYieldForInput(yieldGate, yieldToEventLoop)];
3922
4517
  case 12:
3923
- didYield = _e.sent();
4518
+ // Prefer input-aware yields so pan/zoom are not starved after the
4519
+ // overlay is gone; paint throttling stays in markProgressiveDirty.
4520
+ _f.sent();
4521
+ return [3 /*break*/, 15];
4522
+ case 13:
4523
+ if (!yieldGate) return [3 /*break*/, 15];
4524
+ return [4 /*yield*/, yieldGate.maybeYield(yieldToEventLoop)];
4525
+ case 14:
4526
+ didYield = _f.sent();
3924
4527
  if (didYield) {
3925
4528
  this_3._progressiveYieldCount++;
3926
4529
  }
3927
- _e.label = 13;
3928
- case 13: return [2 /*return*/];
4530
+ _f.label = 15;
4531
+ case 15: return [2 /*return*/];
3929
4532
  }
3930
4533
  });
3931
4534
  };
3932
4535
  this_3 = this;
3933
4536
  i = 0;
3934
- _d.label = 1;
4537
+ _e.label = 1;
3935
4538
  case 1:
3936
4539
  if (!(i < entities.length)) return [3 /*break*/, 4];
3937
4540
  return [5 /*yield**/, _loop_4(i)];
3938
4541
  case 2:
3939
- _d.sent();
3940
- _d.label = 3;
4542
+ _e.sent();
4543
+ _e.label = 3;
3941
4544
  case 3:
3942
4545
  ++i;
3943
4546
  return [3 /*break*/, 1];
@@ -3951,6 +4554,12 @@ var AcTrView2d = /** @class */ (function (_super) {
3951
4554
  * with their union before spatial-index registration.
3952
4555
  */
3953
4556
  AcTrView2d.prototype.syncGroupSpatialBoundsForIndexing = function (group) {
4557
+ var lazy = group.peekLazyChildBoxes();
4558
+ if (lazy && lazy.boxes.length > 0) {
4559
+ var userData_1 = group.userData;
4560
+ userData_1.lazySpatialChildren = lazy;
4561
+ return;
4562
+ }
3954
4563
  group.refreshWcsChildBoxesFromChildren();
3955
4564
  if (group.wcsChildBoxes.length === 0) {
3956
4565
  return;
@@ -3968,45 +4577,45 @@ var AcTrView2d = /** @class */ (function (_super) {
3968
4577
  };
3969
4578
  AcTrView2d.prototype.handleGroup = function (group_1, progressive_1) {
3970
4579
  return __awaiter(this, arguments, void 0, function (group, progressive, epoch, convertGen) {
3971
- var t0, objectId, children, objectsGroupByLayer_1, children_1, children_1_1, child, renderContext_1, groupObjectId_1, groupLayerName_1, groupChildBoxes_1, aggregateSpatialBbox_1, registeredChildIndex_1;
3972
- var e_17, _a;
4580
+ var t0, objectId, children, objectsGroupByLayer_2, children_1, children_1_1, child, renderContext, groupObjectId, groupLayerName, lazyChildren, groupChildBoxes, aggregateSpatialBbox, registeredChildIndex, yieldToEventLoop, lastGroupYieldAt, _loop_5, this_4, objectsGroupByLayer_1, objectsGroupByLayer_1_1, _a, layerName, objects, e_19_1;
4581
+ var e_20, _b, e_19, _c;
3973
4582
  var _this = this;
3974
- var _b, _c;
4583
+ var _d, _e;
3975
4584
  if (epoch === void 0) { epoch = this._convertEpoch; }
3976
4585
  if (convertGen === void 0) { convertGen = 0; }
3977
- return __generator(this, function (_d) {
3978
- switch (_d.label) {
4586
+ return __generator(this, function (_f) {
4587
+ switch (_f.label) {
3979
4588
  case 0:
3980
4589
  t0 = performance.now();
3981
- _d.label = 1;
4590
+ _f.label = 1;
3982
4591
  case 1:
3983
- _d.trys.push([1, , 3, 4]);
4592
+ _f.trys.push([1, , 11, 12]);
3984
4593
  return [4 /*yield*/, this.finishEntityGeometry(group, progressive)];
3985
4594
  case 2:
3986
- _d.sent();
4595
+ _f.sent();
3987
4596
  if (epoch !== this._convertEpoch) {
3988
4597
  group.dispose();
3989
4598
  return [2 /*return*/];
3990
4599
  }
3991
- objectId = String((_b = group.objectId) !== null && _b !== void 0 ? _b : '');
4600
+ objectId = String((_d = group.objectId) !== null && _d !== void 0 ? _d : '');
3992
4601
  if (objectId &&
3993
- ((_c = this._entityConvertGeneration.get(objectId)) !== null && _c !== void 0 ? _c : 0) !== convertGen) {
4602
+ ((_e = this._entityConvertGeneration.get(objectId)) !== null && _e !== void 0 ? _e : 0) !== convertGen) {
3994
4603
  group.dispose();
3995
4604
  return [2 /*return*/];
3996
4605
  }
3997
4606
  this.syncGroupSpatialBoundsForIndexing(group);
3998
4607
  children = group.children;
3999
- objectsGroupByLayer_1 = new Map();
4608
+ objectsGroupByLayer_2 = new Map();
4000
4609
  children.forEach(function (child) {
4001
4610
  var _a;
4002
4611
  if (child.visible === false) {
4003
4612
  return;
4004
4613
  }
4005
4614
  var layerName = child.userData.layerName;
4006
- if (!objectsGroupByLayer_1.has(layerName)) {
4007
- objectsGroupByLayer_1.set(layerName, []);
4615
+ if (!objectsGroupByLayer_2.has(layerName)) {
4616
+ objectsGroupByLayer_2.set(layerName, []);
4008
4617
  }
4009
- (_a = objectsGroupByLayer_1.get(layerName)) === null || _a === void 0 ? void 0 : _a.push(child);
4618
+ (_a = objectsGroupByLayer_2.get(layerName)) === null || _a === void 0 ? void 0 : _a.push(child);
4010
4619
  });
4011
4620
  // Important:
4012
4621
  // Sometimes one group may contain huge amount of objects (> 100,000). So it is important
@@ -4024,89 +4633,136 @@ var AcTrView2d = /** @class */ (function (_super) {
4024
4633
  child.parent = null;
4025
4634
  }
4026
4635
  }
4027
- catch (e_17_1) { e_17 = { error: e_17_1 }; }
4636
+ catch (e_20_1) { e_20 = { error: e_20_1 }; }
4028
4637
  finally {
4029
4638
  try {
4030
- if (children_1_1 && !children_1_1.done && (_a = children_1.return)) _a.call(children_1);
4639
+ if (children_1_1 && !children_1_1.done && (_b = children_1.return)) _b.call(children_1);
4031
4640
  }
4032
- finally { if (e_17) throw e_17.error; }
4641
+ finally { if (e_20) throw e_20.error; }
4033
4642
  }
4034
- renderContext_1 = group.renderContext;
4035
- groupObjectId_1 = group.objectId;
4036
- groupLayerName_1 = group.layerName;
4037
- // AcDbRenderingCache.draw (and similar paths such as AcDbTable) already call
4038
- // applyMatrix on the group, which updates wcsBbbox and wcsChildBoxes to WCS.
4039
- // Do not multiply group.matrix here — that would double-transform spatial bounds.
4040
- if (process.env.NODE_ENV !== 'production') {
4643
+ renderContext = group.renderContext;
4644
+ groupObjectId = group.objectId;
4645
+ groupLayerName = group.layerName;
4646
+ lazyChildren = group.userData.lazySpatialChildren;
4647
+ if (process.env.NODE_ENV !== 'production' && !lazyChildren) {
4041
4648
  assertAcTrGroupWcsBboxesConsistent(group);
4042
4649
  }
4043
- groupChildBoxes_1 = group.wcsChildBoxes.map(function (box) { return ({
4044
- minX: box.minX,
4045
- minY: box.minY,
4046
- maxX: box.maxX,
4047
- maxY: box.maxY,
4048
- id: box.id
4049
- }); });
4050
- aggregateSpatialBbox_1 = groupChildBoxes_1.length > 0
4051
- ? unionGroupWcsChildBoxes(group)
4052
- : group.wcsBbox.clone();
4053
- if (groupChildBoxes_1.length > 0) {
4054
- group.wcsBbox = aggregateSpatialBbox_1.clone();
4650
+ groupChildBoxes = lazyChildren
4651
+ ? []
4652
+ : group.wcsChildBoxes.map(function (box) { return ({
4653
+ minX: box.minX,
4654
+ minY: box.minY,
4655
+ maxX: box.maxX,
4656
+ maxY: box.maxY,
4657
+ id: box.id
4658
+ }); });
4659
+ aggregateSpatialBbox = lazyChildren
4660
+ ? group.wcsBbox.clone()
4661
+ : groupChildBoxes.length > 0
4662
+ ? unionGroupWcsChildBoxes(group)
4663
+ : group.wcsBbox.clone();
4664
+ if (groupChildBoxes.length > 0) {
4665
+ group.wcsBbox = aggregateSpatialBbox.clone();
4055
4666
  }
4056
- registeredChildIndex_1 = false;
4057
- objectsGroupByLayer_1.forEach(function (objects, layerName) {
4058
- // Nested layer-0 may already be resolved to an inner INSERT layer during
4059
- // flatten. Remaining "0" buckets inherit this (outermost) INSERT layer.
4060
- var effectiveLayerName = layerName === '0' ? groupLayerName_1 : layerName;
4061
- // Material remap must still treat authored layer-0 drawables as layer-0
4062
- // ByLayer even when nest resolution already rewrote layerName.
4063
- var sourceLayerForMaterials = objects.some(function (object) {
4064
- var _a;
4065
- var data = object.userData;
4066
- return ((_a = data.authoredLayerName) !== null && _a !== void 0 ? _a : layerName) === '0';
4067
- })
4068
- ? '0'
4069
- : layerName;
4070
- // Keep runtime layer metadata/material cache aligned with the inherited layer so
4071
- // later layer style edits (color, linetype, lineweight, transparency) target this
4072
- // object set correctly.
4073
- _this._inheritedLayerMaterialMapper.remap(objects, sourceLayerForMaterials, effectiveLayerName);
4074
- // One INSERT can expand to children from multiple layers. Here we create one
4075
- // render entity per layer bucket but preserve the INSERT object id for all
4076
- // buckets, so selection/highlight still maps back to the same database object.
4077
- // Within each layer bucket, the object id remains unique in scene indexing.
4078
- var entity = new AcTrEntity(renderContext_1);
4079
- // Copy the INSERT matrix exactly — applyMatrix4 decomposes and drops
4080
- // reflections from mirrored block scales.
4081
- entity.matrix.copy(group.matrix);
4082
- entity.matrixAutoUpdate = false;
4083
- entity.matrixWorldNeedsUpdate = true;
4084
- entity.objectId = groupObjectId_1;
4085
- entity.ownerId = group.ownerId;
4086
- // If block-definition entities are on layer "0", this bucket now uses the layer
4087
- // of the block reference itself (effectiveLayerName).
4088
- entity.layerName = effectiveLayerName;
4089
- entity.userData.insertLayerName = groupLayerName_1;
4090
- entity.wcsBbox = aggregateSpatialBbox_1.clone();
4091
- var entityUserData = entity.userData;
4092
- if (!registeredChildIndex_1 && groupChildBoxes_1.length > 0) {
4093
- entityUserData.spatialIndexChildBoxes = groupChildBoxes_1;
4094
- registeredChildIndex_1 = true;
4095
- }
4096
- // Important:
4097
- // DO NOT USE spread operator when adding objects because it may be one very large array
4098
- // and can result in maximum call stack size exceeded
4099
- for (var i = 0; i < objects.length; i++) {
4100
- entity.add(objects[i]);
4101
- }
4102
- entity.updateMatrixWorld(true);
4103
- _this._layerAppearance.refreshTextMaterialsInObjectTree(entity);
4104
- var tAdd = performance.now();
4105
- _this._scene.addEntity(entity, true);
4106
- _this._convertPhaseMs.addEntity += performance.now() - tAdd;
4107
- _this.applySessionHiddenObjectState(groupObjectId_1);
4108
- entity.dispose();
4109
- });
4667
+ registeredChildIndex = false;
4668
+ yieldToEventLoop = function () {
4669
+ return new Promise(function (resolve) { return setTimeout(resolve, 0); });
4670
+ };
4671
+ lastGroupYieldAt = progressive ? performance.now() : 0;
4672
+ _loop_5 = function (layerName, objects) {
4673
+ var effectiveLayerName, sourceLayerForMaterials, entity, entityUserData, i, tAdd;
4674
+ return __generator(this, function (_g) {
4675
+ switch (_g.label) {
4676
+ case 0:
4677
+ effectiveLayerName = layerName === '0' ? groupLayerName : layerName;
4678
+ sourceLayerForMaterials = objects.some(function (object) {
4679
+ var _a;
4680
+ var data = object.userData;
4681
+ return ((_a = data.authoredLayerName) !== null && _a !== void 0 ? _a : layerName) === '0';
4682
+ })
4683
+ ? '0'
4684
+ : layerName;
4685
+ // Keep runtime layer metadata/material cache aligned with the inherited layer so
4686
+ // later layer style edits (color, linetype, lineweight, transparency) target this
4687
+ // object set correctly.
4688
+ this_4._inheritedLayerMaterialMapper.remap(objects, sourceLayerForMaterials, effectiveLayerName);
4689
+ entity = new AcTrEntity(renderContext);
4690
+ // Copy the INSERT matrix exactly — applyMatrix4 decomposes and drops
4691
+ // reflections from mirrored block scales.
4692
+ entity.matrix.copy(group.matrix);
4693
+ entity.matrixAutoUpdate = false;
4694
+ entity.matrixWorldNeedsUpdate = true;
4695
+ entity.objectId = groupObjectId;
4696
+ entity.ownerId = group.ownerId;
4697
+ // If block-definition entities are on layer "0", this bucket now uses the layer
4698
+ // of the block reference itself (effectiveLayerName).
4699
+ entity.layerName = effectiveLayerName;
4700
+ entity.userData.insertLayerName = groupLayerName;
4701
+ entity.wcsBbox = aggregateSpatialBbox.clone();
4702
+ entityUserData = entity.userData;
4703
+ if (!registeredChildIndex && lazyChildren) {
4704
+ ;
4705
+ entity.userData.lazySpatialChildren = lazyChildren;
4706
+ registeredChildIndex = true;
4707
+ }
4708
+ else if (!registeredChildIndex && groupChildBoxes.length > 0) {
4709
+ entityUserData.spatialIndexChildBoxes = groupChildBoxes;
4710
+ registeredChildIndex = true;
4711
+ }
4712
+ // Important:
4713
+ // DO NOT USE spread operator when adding objects because it may be one very large array
4714
+ // and can result in maximum call stack size exceeded
4715
+ for (i = 0; i < objects.length; i++) {
4716
+ entity.add(objects[i]);
4717
+ }
4718
+ entity.updateMatrixWorld(true);
4719
+ this_4._layerAppearance.refreshTextMaterialsInObjectTree(entity);
4720
+ tAdd = performance.now();
4721
+ this_4._scene.addEntity(entity, true);
4722
+ this_4._convertPhaseMs.addEntity += performance.now() - tAdd;
4723
+ this_4.applySessionHiddenObjectState(groupObjectId);
4724
+ entity.dispose();
4725
+ if (!(progressive &&
4726
+ epoch === this_4._convertEpoch &&
4727
+ performance.now() - lastGroupYieldAt >=
4728
+ AcTrView2d.PROGRESSIVE_OPEN_YIELD_BUDGET_MS)) return [3 /*break*/, 2];
4729
+ return [4 /*yield*/, yieldToEventLoop()];
4730
+ case 1:
4731
+ _g.sent();
4732
+ lastGroupYieldAt = performance.now();
4733
+ _g.label = 2;
4734
+ case 2: return [2 /*return*/];
4735
+ }
4736
+ });
4737
+ };
4738
+ this_4 = this;
4739
+ _f.label = 3;
4740
+ case 3:
4741
+ _f.trys.push([3, 8, 9, 10]);
4742
+ objectsGroupByLayer_1 = __values(objectsGroupByLayer_2), objectsGroupByLayer_1_1 = objectsGroupByLayer_1.next();
4743
+ _f.label = 4;
4744
+ case 4:
4745
+ if (!!objectsGroupByLayer_1_1.done) return [3 /*break*/, 7];
4746
+ _a = __read(objectsGroupByLayer_1_1.value, 2), layerName = _a[0], objects = _a[1];
4747
+ return [5 /*yield**/, _loop_5(layerName, objects)];
4748
+ case 5:
4749
+ _f.sent();
4750
+ _f.label = 6;
4751
+ case 6:
4752
+ objectsGroupByLayer_1_1 = objectsGroupByLayer_1.next();
4753
+ return [3 /*break*/, 4];
4754
+ case 7: return [3 /*break*/, 10];
4755
+ case 8:
4756
+ e_19_1 = _f.sent();
4757
+ e_19 = { error: e_19_1 };
4758
+ return [3 /*break*/, 10];
4759
+ case 9:
4760
+ try {
4761
+ if (objectsGroupByLayer_1_1 && !objectsGroupByLayer_1_1.done && (_c = objectsGroupByLayer_1.return)) _c.call(objectsGroupByLayer_1);
4762
+ }
4763
+ finally { if (e_19) throw e_19.error; }
4764
+ return [7 /*endfinally*/];
4765
+ case 10:
4110
4766
  group.dispose();
4111
4767
  if (progressive) {
4112
4768
  this.markProgressiveDirty();
@@ -4114,11 +4770,14 @@ var AcTrView2d = /** @class */ (function (_super) {
4114
4770
  return _this.getDrawingExtents();
4115
4771
  });
4116
4772
  }
4117
- return [3 /*break*/, 4];
4118
- case 3:
4773
+ else {
4774
+ this.markInteractiveOpenDirty();
4775
+ }
4776
+ return [3 /*break*/, 12];
4777
+ case 11:
4119
4778
  this._convertPhaseMs.handleGroup += performance.now() - t0;
4120
4779
  return [7 /*endfinally*/];
4121
- case 4: return [2 /*return*/];
4780
+ case 12: return [2 /*return*/];
4122
4781
  }
4123
4782
  });
4124
4783
  });
@@ -4173,27 +4832,57 @@ var AcTrView2d = /** @class */ (function (_super) {
4173
4832
  }
4174
4833
  };
4175
4834
  /**
4176
- * Wall-time between cooperative yields during scene convert (ms).
4177
- * Used for both progressive and non-progressive opens so large drawings
4178
- * (e.g. multi-sheet architectural DWGs) cannot freeze the main thread long
4179
- * enough for Chromium to show "Page Unresponsive" while the overlay still
4180
- * reads "Rendering drawing ...". Kept relatively large so convert
4181
- * throughput stays high; smaller budgets made open 2–3× slower.
4835
+ * Wall-time between cooperative yields during scene convert (ms) when
4836
+ * progressive rendering is off. Overlay still blocks input, so a larger
4837
+ * budget keeps throughput high while avoiding Chromium "Page Unresponsive".
4182
4838
  */
4183
4839
  AcTrView2d.OPEN_CONVERT_YIELD_BUDGET_MS = 300;
4184
4840
  /**
4185
- * Minimum interval between progressive mid-open paints (ms).
4186
- * Full-scene WebGL paints dominate open wall time on large drawings;
4187
- * paint much less often than we yield. Independent of convert yields —
4188
- * non-progressive opens still yield without mid-open WebGL paints.
4841
+ * Yield cadence during progressive open (ms). Overlay stays hidden; keep
4842
+ * slices near one frame so pan/zoom handlers can run. Discrete
4843
+ * `navigator.scheduling.isInputPending` yields once so handlers run;
4844
+ * pointerdown/wheel set a short pause so OrbitControls can catch up.
4845
+ */
4846
+ AcTrView2d.PROGRESSIVE_OPEN_YIELD_BUDGET_MS = 32;
4847
+ /** Extra idle window after canvas pointerdown/wheel so gestures stay fluid. */
4848
+ AcTrView2d.PROGRESSIVE_INPUT_PAUSE_MS = 120;
4849
+ /**
4850
+ * Minimum interval between progressive mid-open paints driven by convert
4851
+ * (ms). Full-scene WebGL paints dominate open wall time; user camera
4852
+ * changes bypass this throttle via {@link _progressivePaintUrgent}.
4853
+ */
4854
+ AcTrView2d.PROGRESSIVE_OPEN_PAINT_INTERVAL_MS = 2500;
4855
+ /**
4856
+ * Minimum interval between interactive non-progressive mid-open paints (ms)
4857
+ * while convert / deferred glyphs run under "Rendering drawing ...".
4858
+ * Longer than {@link PROGRESSIVE_OPEN_PAINT_INTERVAL_MS}: the overlay is
4859
+ * semi-transparent (`rgba(0,0,0,0.45)`), but each paint still redraws the
4860
+ * full scene and used to fire on every glyph completion. Headless
4861
+ * (`cooperativeYield` false) skips these paints entirely.
4189
4862
  */
4190
- AcTrView2d.PROGRESSIVE_OPEN_PAINT_INTERVAL_MS = 1000;
4863
+ AcTrView2d.NON_PROGRESSIVE_OPEN_PAINT_INTERVAL_MS = 4000;
4191
4864
  /**
4192
4865
  * Max concurrent deferred glyph/INSERT geometry finalizers. Large drawings
4193
4866
  * enqueue thousands of jobs; keep this modest to limit peak JS heap during
4194
4867
  * open while retaining reasonable text throughput after convert finishes.
4195
4868
  */
4196
4869
  AcTrView2d.DEFERRED_GEOMETRY_CONCURRENCY = 8;
4870
+ /**
4871
+ * Target wall time of one interactive convert slice (ms) while a paper
4872
+ * layout is active.
4873
+ *
4874
+ * Slices return to the drain so a layout activated mid-open can jump ahead
4875
+ * of a long model-space batch. Kept well above one frame (≈50–100 ms) so
4876
+ * the fixed cost of each slice — yield, paint, queue absorb — does not
4877
+ * dominate total load time. The 300ms yield inside {@link batchConvert}
4878
+ * still applies within a slice.
4879
+ */
4880
+ AcTrView2d.CONVERT_SLICE_BUDGET_MS = 64;
4881
+ /**
4882
+ * Upper bound on entities per slice. Heavy block references stay at 1
4883
+ * because a slice that overruns the time budget is halved.
4884
+ */
4885
+ AcTrView2d.CONVERT_SLICE_MAX = 512;
4197
4886
  return AcTrView2d;
4198
4887
  }(AcEdBaseView));
4199
4888
  export { AcTrView2d };