@mlightcad/cad-simple-viewer 1.7.1 → 1.7.3

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 (135) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +185 -185
  3. package/dist/cad-simple-viewer.js +5401 -4880
  4. package/dist/cad-simple-viewer.umd.cjs +117 -113
  5. package/dist/mtext-renderer-worker.js +7083 -6992
  6. package/icons.ts +7 -7
  7. package/lib/app/AcApDocManager.d.ts +9 -6
  8. package/lib/app/AcApDocManager.d.ts.map +1 -1
  9. package/lib/app/AcApDocManager.js +44 -18
  10. package/lib/app/AcApDocManager.js.map +1 -1
  11. package/lib/app/AcApDocument.d.ts +3 -0
  12. package/lib/app/AcApDocument.d.ts.map +1 -1
  13. package/lib/app/AcApDocument.js +31 -24
  14. package/lib/app/AcApDocument.js.map +1 -1
  15. package/lib/app/AcApOpenDatabaseOptions.d.ts +27 -17
  16. package/lib/app/AcApOpenDatabaseOptions.d.ts.map +1 -1
  17. package/lib/app/AcApOpenFileProgressController.d.ts +12 -5
  18. package/lib/app/AcApOpenFileProgressController.d.ts.map +1 -1
  19. package/lib/app/AcApOpenFileProgressController.js +24 -7
  20. package/lib/app/AcApOpenFileProgressController.js.map +1 -1
  21. package/lib/app/AcApWorkerAssets.d.ts +3 -3
  22. package/lib/app/AcApWorkerAssets.d.ts.map +1 -1
  23. package/lib/app/AcApWorkerAssets.js +3 -3
  24. package/lib/app/AcApWorkerAssets.js.map +1 -1
  25. package/lib/app/notification/AcApNotificationEventBridge.d.ts.map +1 -1
  26. package/lib/app/notification/AcApNotificationEventBridge.js.map +1 -1
  27. package/lib/command/AcApZoomCmd.d.ts +26 -0
  28. package/lib/command/AcApZoomCmd.d.ts.map +1 -1
  29. package/lib/command/AcApZoomCmd.js +69 -3
  30. package/lib/command/AcApZoomCmd.js.map +1 -1
  31. package/lib/editor/input/ui/AcEdMTextEditor.d.ts.map +1 -1
  32. package/lib/editor/input/ui/AcEdMTextEditor.js +2 -1
  33. package/lib/editor/input/ui/AcEdMTextEditor.js.map +1 -1
  34. package/lib/editor/view/AcEdBaseView.d.ts +9 -0
  35. package/lib/editor/view/AcEdBaseView.d.ts.map +1 -1
  36. package/lib/editor/view/AcEdBaseView.js.map +1 -1
  37. package/lib/i18n/ar/jig.d.ts +10 -0
  38. package/lib/i18n/ar/jig.d.ts.map +1 -1
  39. package/lib/i18n/ar/jig.js +10 -2
  40. package/lib/i18n/ar/jig.js.map +1 -1
  41. package/lib/i18n/ar/main.d.ts +2 -0
  42. package/lib/i18n/ar/main.d.ts.map +1 -1
  43. package/lib/i18n/ar/main.js +4 -2
  44. package/lib/i18n/ar/main.js.map +1 -1
  45. package/lib/i18n/cs/jig.d.ts +10 -0
  46. package/lib/i18n/cs/jig.d.ts.map +1 -1
  47. package/lib/i18n/cs/jig.js +13 -3
  48. package/lib/i18n/cs/jig.js.map +1 -1
  49. package/lib/i18n/cs/main.d.ts +2 -0
  50. package/lib/i18n/cs/main.d.ts.map +1 -1
  51. package/lib/i18n/cs/main.js +4 -2
  52. package/lib/i18n/cs/main.js.map +1 -1
  53. package/lib/i18n/en/jig.d.ts +10 -0
  54. package/lib/i18n/en/jig.d.ts.map +1 -1
  55. package/lib/i18n/en/jig.js +13 -3
  56. package/lib/i18n/en/jig.js.map +1 -1
  57. package/lib/i18n/en/main.d.ts +2 -0
  58. package/lib/i18n/en/main.d.ts.map +1 -1
  59. package/lib/i18n/en/main.js +4 -2
  60. package/lib/i18n/en/main.js.map +1 -1
  61. package/lib/i18n/tr/jig.d.ts +10 -0
  62. package/lib/i18n/tr/jig.d.ts.map +1 -1
  63. package/lib/i18n/tr/jig.js +13 -3
  64. package/lib/i18n/tr/jig.js.map +1 -1
  65. package/lib/i18n/tr/main.d.ts +2 -0
  66. package/lib/i18n/tr/main.d.ts.map +1 -1
  67. package/lib/i18n/tr/main.js +4 -2
  68. package/lib/i18n/tr/main.js.map +1 -1
  69. package/lib/i18n/zh/jig.d.ts +10 -0
  70. package/lib/i18n/zh/jig.d.ts.map +1 -1
  71. package/lib/i18n/zh/jig.js +13 -3
  72. package/lib/i18n/zh/jig.js.map +1 -1
  73. package/lib/i18n/zh/main.d.ts +2 -0
  74. package/lib/i18n/zh/main.d.ts.map +1 -1
  75. package/lib/i18n/zh/main.js +4 -2
  76. package/lib/i18n/zh/main.js.map +1 -1
  77. package/lib/index.d.ts +1 -1
  78. package/lib/index.d.ts.map +1 -1
  79. package/lib/index.js +1 -1
  80. package/lib/index.js.map +1 -1
  81. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.d.ts.map +1 -1
  82. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.js +6 -3
  83. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.js.map +1 -1
  84. package/lib/ui/AcUiDefaultNotificationUi.d.ts.map +1 -1
  85. package/lib/ui/AcUiDefaultNotificationUi.js +3 -2
  86. package/lib/ui/AcUiDefaultNotificationUi.js.map +1 -1
  87. package/lib/ui/icons.d.ts +7 -1
  88. package/lib/ui/icons.d.ts.map +1 -1
  89. package/lib/ui/icons.js +10 -2
  90. package/lib/ui/icons.js.map +1 -1
  91. package/lib/util/AcApMessageLink.d.ts +41 -0
  92. package/lib/util/AcApMessageLink.d.ts.map +1 -0
  93. package/lib/util/AcApMessageLink.js +127 -0
  94. package/lib/util/AcApMessageLink.js.map +1 -0
  95. package/lib/util/AcApOpenFileErrorMessage.d.ts +18 -1
  96. package/lib/util/AcApOpenFileErrorMessage.d.ts.map +1 -1
  97. package/lib/util/AcApOpenFileErrorMessage.js +34 -2
  98. package/lib/util/AcApOpenFileErrorMessage.js.map +1 -1
  99. package/lib/util/index.d.ts +1 -0
  100. package/lib/util/index.d.ts.map +1 -1
  101. package/lib/util/index.js +1 -0
  102. package/lib/util/index.js.map +1 -1
  103. package/lib/view/AcTrDirectBatch.d.ts.map +1 -1
  104. package/lib/view/AcTrDirectBatch.js +12 -1
  105. package/lib/view/AcTrDirectBatch.js.map +1 -1
  106. package/lib/view/AcTrExtentCluster.d.ts +30 -0
  107. package/lib/view/AcTrExtentCluster.d.ts.map +1 -0
  108. package/lib/view/AcTrExtentCluster.js +133 -0
  109. package/lib/view/AcTrExtentCluster.js.map +1 -0
  110. package/lib/view/AcTrIntelligentExtents.d.ts +22 -0
  111. package/lib/view/AcTrIntelligentExtents.d.ts.map +1 -0
  112. package/lib/view/AcTrIntelligentExtents.js +91 -0
  113. package/lib/view/AcTrIntelligentExtents.js.map +1 -0
  114. package/lib/view/AcTrLayout.d.ts +10 -1
  115. package/lib/view/AcTrLayout.d.ts.map +1 -1
  116. package/lib/view/AcTrLayout.js +11 -0
  117. package/lib/view/AcTrLayout.js.map +1 -1
  118. package/lib/view/AcTrProgressiveOpenFitController.d.ts +8 -0
  119. package/lib/view/AcTrProgressiveOpenFitController.d.ts.map +1 -1
  120. package/lib/view/AcTrProgressiveOpenFitController.js +13 -0
  121. package/lib/view/AcTrProgressiveOpenFitController.js.map +1 -1
  122. package/lib/view/AcTrView2d.d.ts +94 -9
  123. package/lib/view/AcTrView2d.d.ts.map +1 -1
  124. package/lib/view/AcTrView2d.js +430 -64
  125. package/lib/view/AcTrView2d.js.map +1 -1
  126. package/lib/view/fontLoadRegen.d.ts +25 -0
  127. package/lib/view/fontLoadRegen.d.ts.map +1 -0
  128. package/lib/view/fontLoadRegen.js +33 -0
  129. package/lib/view/fontLoadRegen.js.map +1 -0
  130. package/lib/view/index.d.ts +2 -0
  131. package/lib/view/index.d.ts.map +1 -1
  132. package/lib/view/index.js +2 -0
  133. package/lib/view/index.js.map +1 -1
  134. package/package.json +10 -8
  135. package/src/ui/icons.ts +399 -387
@@ -99,7 +99,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
99
99
  import { AcCmUiYieldGate, AcDbAttribute, AcDbBlockReference, acdbHostApplicationServices, AcDbMText, AcDbRasterImage, AcDbRay, AcDbSysVarManager, AcDbViewport, AcDbXline, AcGeBox2d, AcGePoint2d, acgiForegroundColorForBackground, log } from '@mlightcad/data-model';
100
100
  import { AcDbSystemVariables } from '@mlightcad/data-model';
101
101
  import { FontManager } from '@mlightcad/mtext-renderer';
102
- import { AcTrEntity, AcTrGlyphEntity, AcTrGroup, AcTrRenderer, AcTrViewportView } from '@mlightcad/three-renderer';
102
+ import { AcTrEntity, AcTrGlyphEntity, AcTrGroup, AcTrMTextRenderer, AcTrRenderer, AcTrViewportView, hasPendingComplexLineTypeGlyphs } from '@mlightcad/three-renderer';
103
103
  import { AcTrMatrixUtil } from '@mlightcad/three-renderer';
104
104
  import * as THREE from 'three';
105
105
  import Stats from 'three/examples/jsm/libs/stats.module';
@@ -122,6 +122,7 @@ import { shouldExtendBboxForDirectEntity, tryBuildDirectEntityMeta } from './AcT
122
122
  import { AcTrEntityDisplayController } from './AcTrEntityDisplayController';
123
123
  import { assertAcTrGroupWcsBboxesConsistent, unionGroupWcsChildBoxes } from './AcTrGroupWcsBboxAssert';
124
124
  import { AcTrInheritedLayerMaterialMapper } from './AcTrInheritedLayerMaterialMapper';
125
+ import { computeIntelligentExtents } from './AcTrIntelligentExtents';
125
126
  import { AcTrLayer } from './AcTrLayer';
126
127
  import { AcTrLayerAppearanceController } from './AcTrLayerAppearanceController';
127
128
  import { AcTrLayout } from './AcTrLayout';
@@ -130,6 +131,7 @@ import { AcTrLayoutViewManager } from './AcTrLayoutViewManager';
130
131
  import { sortPickResults } from './AcTrPickResultUtil';
131
132
  import { AcTrProgressiveOpenFitController } from './AcTrProgressiveOpenFitController';
132
133
  import { AcTrScene } from './AcTrScene';
134
+ import { shouldRegenDatabaseAfterFontLoad } from './fontLoadRegen';
133
135
  /**
134
136
  * Default view option values
135
137
  */
@@ -247,6 +249,21 @@ var AcTrView2d = /** @class */ (function (_super) {
247
249
  * wasted conversion work — this set skips re-entry until the current pass ends.
248
250
  */
249
251
  _this._convertingLayers = new Set();
252
+ /**
253
+ * Object ids claimed by an in-flight interactive {@link batchConvert},
254
+ * including deferred glyph commits that have not yet called
255
+ * {@link AcTrScene.addEntity}. Without this, a second convert pass can
256
+ * append another batched slot for the same id (visible as doubled TEXT
257
+ * linetype labels) because {@link hasEntity} is still false.
258
+ */
259
+ _this._claimedConvertObjectIds = new Set();
260
+ /**
261
+ * Per-objectId convert generation. Bumped by {@link updateEntity} so an
262
+ * in-flight progressive {@link batchConvert} / deferred commit for the same
263
+ * id cannot `addEntity` after the scene was cleared for reconversion
264
+ * (ghost RasterImage / pre-replacement pixels).
265
+ */
266
+ _this._entityConvertGeneration = new Map();
250
267
  /**
251
268
  * When true, entity conversion during document open yields cooperatively so
252
269
  * geometry paints incrementally while the open overlay is still visible.
@@ -273,6 +290,13 @@ var AcTrView2d = /** @class */ (function (_super) {
273
290
  _this._progressivePaintCount = 0;
274
291
  /** Cooperative yields taken inside progressive {@link batchConvert}. */
275
292
  _this._progressiveYieldCount = 0;
293
+ /**
294
+ * Saved view was not applied at open. Frame batch bounds once linework
295
+ * convert drains, so the canvas is not blank until deferred glyphs finish.
296
+ * Applied on a timer so it does not run on the entity-parse stack.
297
+ */
298
+ _this._openLineworkFramePending = false;
299
+ _this._openLineworkFrameTimer = null;
276
300
  /**
277
301
  * In-flight + queued glyph/group geometry jobs that await fonts via asyncDraw.
278
302
  * Counted separately so linework convert can continue while text waits.
@@ -292,6 +316,22 @@ var AcTrView2d = /** @class */ (function (_super) {
292
316
  */
293
317
  _this._fontLoadedRedrawTimer = null;
294
318
  _this._fontLoadedRedrawEpoch = 0;
319
+ /**
320
+ * `performance.now()` when convert and deferred glyph jobs last reached
321
+ * idle together. Zero until that first transition. Used to ignore the
322
+ * open-time `fontLoaded` regen that replays the loading spinner.
323
+ */
324
+ _this._entityProcessingIdleAt = 0;
325
+ /**
326
+ * Convert epoch for which text-style font preload was started.
327
+ */
328
+ _this._textStyleFontPreloadEpoch = -1;
329
+ /**
330
+ * In-flight (or resolved) preload of {@link AcDbTextStyleTable.fonts}.
331
+ * Started at conversion stage `STYLE` END so download overlaps LAYER/BLOCK/
332
+ * ENTITY parse and linework convert; glyph jobs await this before draw.
333
+ */
334
+ _this._textStyleFontPreloadPromise = null;
295
335
  /** Transient reading mode forces black linework on a white canvas. */
296
336
  _this._readingMode = new AcApReadingModeState({
297
337
  getCurrentBackgroundColor: function () { return _this._renderer.currentBackgroundColor; },
@@ -784,10 +824,13 @@ var AcTrView2d = /** @class */ (function (_super) {
784
824
  * fully drawable scene (export, scripted zoom) should wait on this (as
785
825
  * {@link waitUntilIdle} / {@link zoomToFitDrawing} do).
786
826
  *
787
- * The open-file progress overlay intentionally uses
788
- * {@link isConvertingEntities} instead so "Rendering drawing ..." can hide
789
- * once linework convert finishes while text geometry continues in the
790
- * deferred pool.
827
+ * The open-file progress overlay uses this when
828
+ * {@link AcApOpenDatabaseOptions.progressiveRendering} is off, so
829
+ * "Rendering drawing ..." stays up until deferred glyph jobs finish.
830
+ * When progressive rendering is on, the overlay uses
831
+ * {@link isConvertingEntities} and may hide while text geometry continues
832
+ * in the deferred pool (pan/zoom enabled). Deprecated `waitForTextGeometry`
833
+ * no longer selects this gate.
791
834
  */
792
835
  get: function () {
793
836
  return this._numOfEntitiesToProcess > 0 || this._pendingGeometryJobs > 0;
@@ -1182,13 +1225,16 @@ var AcTrView2d = /** @class */ (function (_super) {
1182
1225
  threeEntity.visible = entity.visibility !== false;
1183
1226
  if (threeEntity instanceof AcTrGroup &&
1184
1227
  threeEntity.isOnTheSameLayer) {
1185
- // Children authored on layer "0" inherit the INSERT layer for
1186
- // ByLayer traits (color, etc.), same as the primary-document path.
1187
- this._inheritedLayerMaterialMapper.remap(threeEntity.children, '0', threeEntity.layerName);
1228
+ threeEntity.userData.insertLayerName = threeEntity.layerName;
1188
1229
  }
1189
1230
  return [4 /*yield*/, this.finishEntityGeometry(threeEntity, false)];
1190
1231
  case 5:
1191
1232
  _g.sent();
1233
+ if (threeEntity instanceof AcTrGroup &&
1234
+ threeEntity.isOnTheSameLayer) {
1235
+ // Remap after glyph geometry exists — see same-layer commit path.
1236
+ this._inheritedLayerMaterialMapper.remap(threeEntity.children, '0', threeEntity.layerName);
1237
+ }
1192
1238
  layout.addEntity(threeEntity);
1193
1239
  threeEntity.dispose();
1194
1240
  return [3 /*break*/, 7];
@@ -1468,6 +1514,40 @@ var AcTrView2d = /** @class */ (function (_super) {
1468
1514
  this._isDirty = true;
1469
1515
  }
1470
1516
  };
1517
+ /**
1518
+ * When open framing waits on {@link zoomToFitDrawing}, show the linework
1519
+ * bounds as soon as entity convert drains. The final fit still runs after
1520
+ * deferred glyphs so text extents can refine the camera.
1521
+ */
1522
+ AcTrView2d.prototype.requestOpenLineworkFrame = function () {
1523
+ this._openLineworkFramePending = true;
1524
+ this.scheduleOpenLineworkFrame();
1525
+ };
1526
+ AcTrView2d.prototype.cancelOpenLineworkFrame = function () {
1527
+ this._openLineworkFramePending = false;
1528
+ if (this._openLineworkFrameTimer != null) {
1529
+ clearTimeout(this._openLineworkFrameTimer);
1530
+ this._openLineworkFrameTimer = null;
1531
+ }
1532
+ };
1533
+ AcTrView2d.prototype.scheduleOpenLineworkFrame = function () {
1534
+ var _this = this;
1535
+ if (this._openLineworkFrameTimer != null ||
1536
+ !this._openLineworkFramePending) {
1537
+ return;
1538
+ }
1539
+ var delay = this.isConvertingEntities ? 50 : 0;
1540
+ this._openLineworkFrameTimer = setTimeout(function () {
1541
+ _this._openLineworkFrameTimer = null;
1542
+ if (!_this._openLineworkFramePending)
1543
+ return;
1544
+ if (_this.isConvertingEntities) {
1545
+ _this.scheduleOpenLineworkFrame();
1546
+ return;
1547
+ }
1548
+ _this.frameOpenLineworkIfReady();
1549
+ }, delay);
1550
+ };
1471
1551
  /**
1472
1552
  * @inheritdoc
1473
1553
  */
@@ -1507,6 +1587,34 @@ var AcTrView2d = /** @class */ (function (_super) {
1507
1587
  }
1508
1588
  return false;
1509
1589
  };
1590
+ /**
1591
+ * @inheritdoc
1592
+ */
1593
+ AcTrView2d.prototype.zoomToSmartExtents = function (timeout) {
1594
+ var _this = this;
1595
+ if (timeout === void 0) { timeout = 0; }
1596
+ var waiter = new AcEdConditionWaiter(function () { return !_this.isProcessingEntities; }, function () {
1597
+ var smart = _this.resolveSmartFitBox();
1598
+ if (smart) {
1599
+ _this.zoomTo(smart);
1600
+ _this._isDirty = true;
1601
+ _this.endProgressiveOpenFit();
1602
+ return;
1603
+ }
1604
+ _this._progressiveOpenFit.applyFinalFit(function () { return _this.resolveLayoutFitBox(); });
1605
+ _this.endProgressiveOpenFit();
1606
+ }, 300, timeout);
1607
+ waiter.start();
1608
+ };
1609
+ /**
1610
+ * Resolves intelligent zoom extents from spatial-index entity boxes.
1611
+ */
1612
+ AcTrView2d.prototype.resolveSmartFitBox = function () {
1613
+ var activeLayout = this._scene.activeLayout;
1614
+ if (!activeLayout)
1615
+ return undefined;
1616
+ return computeIntelligentExtents(activeLayout.collectSpatialExtentBoxes());
1617
+ };
1510
1618
  /**
1511
1619
  * @inheritdoc
1512
1620
  */
@@ -2031,7 +2139,28 @@ var AcTrView2d = /** @class */ (function (_super) {
2031
2139
  */
2032
2140
  AcTrView2d.prototype.addEntity = function (entity) {
2033
2141
  var _a;
2142
+ var _b;
2034
2143
  var entities = Array.isArray(entity) ? entity : [entity];
2144
+ // Mark each owner layout as loaded as soon as the open-time ENTITY stream
2145
+ // enqueues work. Progressive convert may not have committed scene entities
2146
+ // yet (`entityCount` still 0); without this, `loadLayoutEntitiesIfNeeded`
2147
+ // (from `onAfterOpenDocument` → `setActiveLayout`) re-iterates the BTR,
2148
+ // double-increments `_numOfEntitiesToProcess`, and keeps
2149
+ // "Rendering drawing ..." up long after linework should have finished —
2150
+ // so progressive rendering cannot release pan/zoom on time.
2151
+ for (var i = 0; i < entities.length; i++) {
2152
+ var ownerId = (_b = entities[i]) === null || _b === void 0 ? void 0 : _b.ownerId;
2153
+ if (!ownerId)
2154
+ continue;
2155
+ var layout = this._scene.layouts.get(ownerId);
2156
+ if (!layout) {
2157
+ this._scene.addEmptyLayout(ownerId);
2158
+ layout = this._scene.layouts.get(ownerId);
2159
+ }
2160
+ if (layout && !layout.isLoaded) {
2161
+ layout.isLoaded = true;
2162
+ }
2163
+ }
2035
2164
  this._numOfEntitiesToProcess += entities.length;
2036
2165
  // Always serialize convert through one drain loop. Non-progressive opens
2037
2166
  // used to `void batchConvert(chunk)` per ENTITY flush chunk, which ran
@@ -2102,6 +2231,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2102
2231
  */
2103
2232
  AcTrView2d.prototype.updateEntity = function (entity) {
2104
2233
  var _this = this;
2234
+ var _a, _b;
2105
2235
  var entities = this.resolveSceneUpdateEntities(Array.isArray(entity) ? entity : [entity]);
2106
2236
  if (entities.length === 0)
2107
2237
  return;
@@ -2110,6 +2240,17 @@ var AcTrView2d = /** @class */ (function (_super) {
2110
2240
  .filter(function (objectId) { return _this.selectionSet.has(objectId); });
2111
2241
  for (var i = 0; i < entities.length; ++i) {
2112
2242
  var item = entities[i];
2243
+ var objectId = String((_a = item.objectId) !== null && _a !== void 0 ? _a : '');
2244
+ if (objectId) {
2245
+ // Invalidate in-flight commits for this id before releasing the claim /
2246
+ // clearing the scene, so a stale progressive convert cannot re-add the
2247
+ // pre-update drawable after (or instead of) the reconversion pass.
2248
+ this._entityConvertGeneration.set(objectId, ((_b = this._entityConvertGeneration.get(objectId)) !== null && _b !== void 0 ? _b : 0) + 1);
2249
+ // Allow reconversion: open-time batchConvert claims objectIds to prevent
2250
+ // duplicate progressive slots. updateEntity must release that claim or
2251
+ // batchConvert skips the entity (scene already cleared → blank RasterImage).
2252
+ this._claimedConvertObjectIds.delete(objectId);
2253
+ }
2113
2254
  if (this._scene.hasEntity(item.objectId)) {
2114
2255
  this._scene.removeEntity(item.objectId);
2115
2256
  }
@@ -2129,6 +2270,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2129
2270
  this.highlight(selectedIds);
2130
2271
  }
2131
2272
  this._gripManager.refresh();
2273
+ this._isDirty = true;
2132
2274
  return [2 /*return*/];
2133
2275
  }
2134
2276
  });
@@ -2299,7 +2441,11 @@ var AcTrView2d = /** @class */ (function (_super) {
2299
2441
  this.clearFontLoadedRedrawTimer();
2300
2442
  this._convertQueue.length = 0;
2301
2443
  this._numOfEntitiesToProcess = 0;
2444
+ this._claimedConvertObjectIds.clear();
2445
+ this._entityConvertGeneration.clear();
2446
+ this.cancelOpenLineworkFrame();
2302
2447
  this.resetDeferredGeometryQueue();
2448
+ this._entityProcessingIdleAt = 0;
2303
2449
  this._scene.clear();
2304
2450
  this._isDirty = true;
2305
2451
  this._missedImages.clear();
@@ -2335,9 +2481,13 @@ var AcTrView2d = /** @class */ (function (_super) {
2335
2481
  var _a;
2336
2482
  this._convertEpoch++;
2337
2483
  this.clearFontLoadedRedrawTimer();
2484
+ this.cancelOpenLineworkFrame();
2338
2485
  this._convertQueue.length = 0;
2339
2486
  this._numOfEntitiesToProcess = 0;
2487
+ this._claimedConvertObjectIds.clear();
2488
+ this._entityConvertGeneration.clear();
2340
2489
  this.resetDeferredGeometryQueue();
2490
+ this._entityProcessingIdleAt = 0;
2341
2491
  this._scene = state.scene;
2342
2492
  this._layoutViewManager = state.layoutViewManager;
2343
2493
  this._initializedLayouts = state.initializedLayouts;
@@ -2363,9 +2513,13 @@ var AcTrView2d = /** @class */ (function (_super) {
2363
2513
  var parked = this.captureSessionState();
2364
2514
  this._convertEpoch++;
2365
2515
  this.clearFontLoadedRedrawTimer();
2516
+ this.cancelOpenLineworkFrame();
2366
2517
  this._convertQueue.length = 0;
2367
2518
  this._numOfEntitiesToProcess = 0;
2519
+ this._claimedConvertObjectIds.clear();
2520
+ this._entityConvertGeneration.clear();
2368
2521
  this.resetDeferredGeometryQueue();
2522
+ this._entityProcessingIdleAt = 0;
2369
2523
  this._scene = this.createScene();
2370
2524
  this._layoutViewManager = new AcTrLayoutViewManager();
2371
2525
  this._initializedLayouts = new Set();
@@ -2533,6 +2687,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2533
2687
  (_a = this._disposeCanvasTouchCallout) === null || _a === void 0 ? void 0 : _a.call(this);
2534
2688
  this._disposeCanvasTouchCallout = undefined;
2535
2689
  this.clearFontLoadedRedrawTimer();
2690
+ this.cancelOpenLineworkFrame();
2536
2691
  this.stopAnimationLoop();
2537
2692
  };
2538
2693
  /**
@@ -2637,6 +2792,15 @@ var AcTrView2d = /** @class */ (function (_super) {
2637
2792
  }
2638
2793
  var existingLayout = this._scene.layouts.get(layoutBtrId);
2639
2794
  if (existingLayout && existingLayout.isLoaded) {
2795
+ // Streamed layouts flip `isLoaded` from `addEntity` before scene
2796
+ // commits finish. Still repair the viewport-view race when entities
2797
+ // are already in the scene but AcTrViewportView creation was skipped.
2798
+ var layoutView_1 = this._layoutViewManager.getAt(layoutBtrId);
2799
+ if (existingLayout.entityCount > 0 &&
2800
+ layoutView_1 &&
2801
+ layoutView_1.viewportCount === 0) {
2802
+ this.ensureViewportViews(blockTableRecord, layoutView_1);
2803
+ }
2640
2804
  return;
2641
2805
  }
2642
2806
  if (this._loadingLayouts.has(layoutBtrId)) {
@@ -2680,6 +2844,11 @@ var AcTrView2d = /** @class */ (function (_super) {
2680
2844
  // is only for layouts whose entities were never streamed in
2681
2845
  // (typically non-active paper-space layouts loaded on first user
2682
2846
  // visit).
2847
+ //
2848
+ // Note: `addEntity` now sets `isLoaded` when the open stream
2849
+ // enqueues work, so the common progressive-open race (entityCount
2850
+ // still 0 at `onAfterOpenDocument` while the convert queue is full)
2851
+ // is handled by the `isLoaded` early return above.
2683
2852
  if (existingLayout && existingLayout.entityCount > 0) {
2684
2853
  existingLayout.isLoaded = true;
2685
2854
  return;
@@ -2821,34 +2990,125 @@ var AcTrView2d = /** @class */ (function (_super) {
2821
2990
  AcTrView2d.prototype.drawEntity = function (entity, delay) {
2822
2991
  return entity.worldDraw(this._renderer, delay);
2823
2992
  };
2993
+ /**
2994
+ * Starts loading fonts referenced by the drawing text style table.
2995
+ *
2996
+ * Call when conversion stage `STYLE` ends so the download overlaps later
2997
+ * parse stages and linework convert. Does not block the caller — glyph
2998
+ * finalize awaits {@link awaitTextStyleFontsReady} instead.
2999
+ *
3000
+ * @param database - Database whose text style table has just been filled.
3001
+ */
3002
+ AcTrView2d.prototype.startTextStyleFontPreload = function (database) {
3003
+ var _a;
3004
+ var epoch = this._convertEpoch;
3005
+ if (this._textStyleFontPreloadEpoch === epoch &&
3006
+ this._textStyleFontPreloadPromise) {
3007
+ return;
3008
+ }
3009
+ this._textStyleFontPreloadEpoch = epoch;
3010
+ var names = [];
3011
+ try {
3012
+ names = (_a = database.tables.textStyleTable.fonts) !== null && _a !== void 0 ? _a : [];
3013
+ }
3014
+ catch (_b) {
3015
+ names = [];
3016
+ }
3017
+ // Style fonts alone are not enough: awaitFontsBeforeDraw only *awaits*
3018
+ // content/style faces and kicks default/symbol fallbacks in the background.
3019
+ // Drawings with empty primary font files (font falls back to the STYLE name)
3020
+ // need those fallbacks loaded before the first glyph bake, otherwise Latin
3021
+ // (and often all) text is permanently baked as '?'.
3022
+ var fallbackFonts = FontManager.instance.getFontsToLoad();
3023
+ var preloadNames = __spreadArray([], __read(new Set(__spreadArray(__spreadArray([], __read(names), false), __read(fallbackFonts), false))), false);
3024
+ if (preloadNames.length === 0) {
3025
+ this._textStyleFontPreloadPromise = Promise.resolve();
3026
+ return;
3027
+ }
3028
+ var mtextRenderer = AcTrMTextRenderer.getInstance();
3029
+ this._textStyleFontPreloadPromise = Promise.all([
3030
+ FontManager.instance.requestFonts(preloadNames),
3031
+ // Worker isolates have their own FontManager; main-thread requestFonts
3032
+ // alone does not populate them before asyncRenderMText.
3033
+ mtextRenderer.loadFonts(fallbackFonts)
3034
+ ]).then(function () { }, function () {
3035
+ // Glyph draw still falls back via FontManager defaults / '?'.
3036
+ });
3037
+ };
3038
+ /**
3039
+ * Waits until {@link startTextStyleFontPreload} has finished (or starts a
3040
+ * fallback preload from this view's bound draw database if STYLE was missed).
3041
+ */
3042
+ AcTrView2d.prototype.awaitTextStyleFontsReady = function () {
3043
+ return __awaiter(this, void 0, void 0, function () {
3044
+ var db;
3045
+ return __generator(this, function (_a) {
3046
+ switch (_a.label) {
3047
+ case 0:
3048
+ if (!this._textStyleFontPreloadPromise) {
3049
+ db = this._renderer.context.database;
3050
+ if (db) {
3051
+ this.startTextStyleFontPreload(db);
3052
+ }
3053
+ else {
3054
+ return [2 /*return*/];
3055
+ }
3056
+ }
3057
+ return [4 /*yield*/, this._textStyleFontPreloadPromise];
3058
+ case 1:
3059
+ _a.sent();
3060
+ return [2 /*return*/];
3061
+ }
3062
+ });
3063
+ });
3064
+ };
2824
3065
  /**
2825
3066
  * Finishes geometry for a converted entity.
2826
3067
  *
2827
- * Glyph entities and block groups use {@link AcTrEntity.asyncDraw} so
2828
- * {@link FontManager.awaitFontsBeforeDraw} can wait for fonts without
2829
- * relying on a full-scene regen. Other entities keep the sync finalize path.
3068
+ * Glyph entities, complex-linetype lines, and block groups use
3069
+ * {@link AcTrEntity.asyncDraw} so {@link FontManager.awaitFontsBeforeDraw}
3070
+ * can wait for fonts without relying on a full-scene regen. Other entities
3071
+ * keep the sync finalize path.
2830
3072
  */
2831
3073
  AcTrView2d.prototype.finishEntityGeometry = function (threeEntity, _progressive) {
2832
3074
  return __awaiter(this, void 0, void 0, function () {
3075
+ var needsGlyphDraw;
2833
3076
  return __generator(this, function (_a) {
2834
3077
  switch (_a.label) {
2835
3078
  case 0:
2836
- if (!(threeEntity instanceof AcTrGroup)) return [3 /*break*/, 2];
3079
+ needsGlyphDraw = (threeEntity instanceof AcTrGroup &&
3080
+ this.groupHasPendingGlyphGeometry(threeEntity)) ||
3081
+ hasPendingComplexLineTypeGlyphs(threeEntity) ||
3082
+ (threeEntity instanceof AcTrGlyphEntity &&
3083
+ !threeEntity.hasDrawableGeometry());
3084
+ if (!needsGlyphDraw) return [3 /*break*/, 2];
3085
+ return [4 /*yield*/, this.awaitTextStyleFontsReady()];
3086
+ case 1:
3087
+ _a.sent();
3088
+ _a.label = 2;
3089
+ case 2:
3090
+ if (!(threeEntity instanceof AcTrGroup)) return [3 /*break*/, 4];
2837
3091
  // Linework-only INSERTs (no empty glyph shells) skip asyncDraw; spatial
2838
3092
  // boxes are refreshed by syncGroupSpatialBoundsForIndexing after commit.
2839
3093
  if (!this.groupHasPendingGlyphGeometry(threeEntity)) {
2840
3094
  return [2 /*return*/];
2841
3095
  }
2842
3096
  return [4 /*yield*/, threeEntity.asyncDraw()];
2843
- case 1:
3097
+ case 3:
2844
3098
  _a.sent();
2845
3099
  return [2 /*return*/];
2846
- case 2:
3100
+ case 4:
3101
+ if (!hasPendingComplexLineTypeGlyphs(threeEntity)) return [3 /*break*/, 6];
3102
+ return [4 /*yield*/, threeEntity.asyncDraw()];
3103
+ case 5:
3104
+ _a.sent();
3105
+ return [2 /*return*/];
3106
+ case 6:
2847
3107
  if (threeEntity.hasDrawableGeometry()) {
2848
3108
  return [2 /*return*/];
2849
3109
  }
2850
3110
  return [4 /*yield*/, threeEntity.asyncDraw()];
2851
- case 3:
3111
+ case 7:
2852
3112
  _a.sent();
2853
3113
  return [2 /*return*/];
2854
3114
  }
@@ -2864,7 +3124,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2864
3124
  if (threeEntity instanceof AcTrGroup) {
2865
3125
  return this.groupHasPendingGlyphGeometry(threeEntity);
2866
3126
  }
2867
- return false;
3127
+ return hasPendingComplexLineTypeGlyphs(threeEntity);
2868
3128
  };
2869
3129
  AcTrView2d.prototype.clearFontLoadedRedrawTimer = function () {
2870
3130
  if (this._fontLoadedRedrawTimer != null) {
@@ -2892,12 +3152,15 @@ var AcTrView2d = /** @class */ (function (_super) {
2892
3152
  *
2893
3153
  * Live {@link AcTrGlyphEntity} shells (rare: mid-convert / not yet batched)
2894
3154
  * are re-drawn in place. Committed text is already flattened into batches and
2895
- * disposed, so on-demand faces (style `malgun` → Noto Sans KR) need a database
2896
- * regen once conversion is idle — otherwise Hangul stays baked as '?'.
3155
+ * disposed. A database regen does not rebuild those batches — `entityAppended`
3156
+ * skips ids the view already has — but it does replay open-file CONVERSION
3157
+ * progress. That replay is skipped while the open has just gone idle
3158
+ * ({@link shouldRegenDatabaseAfterFontLoad}); a face that arrives later can
3159
+ * still regen.
2897
3160
  */
2898
3161
  AcTrView2d.prototype.redrawGlyphEntitiesAfterFontLoad = function (epoch, fontName) {
2899
3162
  return __awaiter(this, void 0, void 0, function () {
2900
- var redrawEpoch, glyphs, _a, _b, layout, glyphs_1, glyphs_1_1, glyph, e_12_1, defaults, normalized_1, db;
3163
+ var redrawEpoch, glyphs, _a, _b, layout, glyphs_1, glyphs_1_1, glyph, e_12_1, defaults, normalized_1, msSinceIdle, db;
2901
3164
  var e_13, _c, e_12, _d;
2902
3165
  var _e, _f;
2903
3166
  return __generator(this, function (_g) {
@@ -2964,8 +3227,10 @@ var AcTrView2d = /** @class */ (function (_super) {
2964
3227
  }
2965
3228
  return [2 /*return*/];
2966
3229
  }
2967
- // Default preset loads during open are covered by awaitFontsBeforeDraw;
2968
- // regenerating for those would thrash after every open.
3230
+ // Default/symbol preset faces are requested in the background under lazy
3231
+ // loading (mtext-renderer awaitFontsBeforeDraw only waits on content/style
3232
+ // fonts). Regenerating for every default face load would thrash after open;
3233
+ // style fonts that finish late still take the regen path below.
2969
3234
  if (fontName) {
2970
3235
  defaults = FontManager.instance.defaultFonts;
2971
3236
  normalized_1 = fontName.toLowerCase();
@@ -2976,8 +3241,21 @@ var AcTrView2d = /** @class */ (function (_super) {
2976
3241
  }
2977
3242
  }
2978
3243
  if (epoch !== this._convertEpoch ||
2979
- redrawEpoch !== this._fontLoadedRedrawEpoch ||
2980
- this.isProcessingEntities) {
3244
+ redrawEpoch !== this._fontLoadedRedrawEpoch) {
3245
+ return [2 /*return*/];
3246
+ }
3247
+ // Font load before the first convert idle (empty scene, ENTITY stream not
3248
+ // started yet). Glyph jobs await that font. regen() here replays the
3249
+ // in-flight database: measured as thousands of duplicate convert claims
3250
+ // and a ~1GB heap spike at the start of open.
3251
+ if (this._entityProcessingIdleAt <= 0) {
3252
+ return [2 /*return*/];
3253
+ }
3254
+ msSinceIdle = performance.now() - this._entityProcessingIdleAt;
3255
+ // Open-time text awaits its fonts, then this debounced callback runs.
3256
+ // Regen here only flashes the loading spinner again after
3257
+ // "Rendering drawing ..." has already hidden (progressive rendering on).
3258
+ if (!shouldRegenDatabaseAfterFontLoad(this.isProcessingEntities, 0, msSinceIdle)) {
2981
3259
  return [2 /*return*/];
2982
3260
  }
2983
3261
  db = (_f = (_e = AcApDocManager.instance) === null || _e === void 0 ? void 0 : _e.curDocument) === null || _f === void 0 ? void 0 : _f.database;
@@ -3047,13 +3325,13 @@ var AcTrView2d = /** @class */ (function (_super) {
3047
3325
  if (_this._pendingGeometryJobs === 0) {
3048
3326
  _this._isDirty = true;
3049
3327
  }
3328
+ _this.stampEntityProcessingIdle();
3050
3329
  }
3051
3330
  _this.pumpDeferredGeometryQueue();
3052
3331
  });
3053
3332
  };
3054
3333
  var this_2 = this;
3055
- while (this._deferredGeometryActive <
3056
- AcTrView2d.DEFERRED_GEOMETRY_CONCURRENCY &&
3334
+ while (this._deferredGeometryActive < AcTrView2d.DEFERRED_GEOMETRY_CONCURRENCY &&
3057
3335
  this._deferredGeometryQueue.length > 0) {
3058
3336
  _loop_3();
3059
3337
  }
@@ -3065,6 +3343,8 @@ var AcTrView2d = /** @class */ (function (_super) {
3065
3343
  // slots until their `finally` runs. Zeroing here lets pump over-schedule
3066
3344
  // when those completions decrement the counter afterward.
3067
3345
  this._pendingGeometryJobs = 0;
3346
+ this._textStyleFontPreloadPromise = null;
3347
+ this._textStyleFontPreloadEpoch = -1;
3068
3348
  };
3069
3349
  /**
3070
3350
  * Waits until side-pool glyph/group jobs for the current convert epoch finish.
@@ -3136,13 +3416,23 @@ var AcTrView2d = /** @class */ (function (_super) {
3136
3416
  */
3137
3417
  AcTrView2d.prototype.batchConvert = function (entities_3) {
3138
3418
  return __awaiter(this, arguments, void 0, function (entities, options) {
3139
- var epoch, progressive, yieldGate, yieldToEventLoop, _loop_4, this_3, i;
3419
+ var epoch, db, progressive, yieldGate, yieldToEventLoop, _loop_4, this_3, i;
3140
3420
  var _this = this;
3421
+ var _a, _b, _c;
3141
3422
  if (options === void 0) { options = {}; }
3142
- return __generator(this, function (_a) {
3143
- switch (_a.label) {
3423
+ return __generator(this, function (_d) {
3424
+ switch (_d.label) {
3144
3425
  case 0:
3145
3426
  epoch = this._convertEpoch;
3427
+ // Fallback: if STYLE-stage preload never started (e.g. non-conversion open
3428
+ // paths), kick it off without blocking linework convert. Glyph finalize
3429
+ // awaits the promise via {@link awaitTextStyleFontsReady}.
3430
+ if (!options.forExport && !this._textStyleFontPreloadPromise) {
3431
+ db = this._renderer.context.database;
3432
+ if (db) {
3433
+ this.startTextStyleFontPreload(db);
3434
+ }
3435
+ }
3146
3436
  progressive = this._progressiveRendering && !options.forExport;
3147
3437
  yieldGate = options.forExport
3148
3438
  ? undefined
@@ -3151,14 +3441,14 @@ var AcTrView2d = /** @class */ (function (_super) {
3151
3441
  return new Promise(function (resolve) { return setTimeout(resolve, 0); });
3152
3442
  };
3153
3443
  _loop_4 = function (i) {
3154
- var entity, shouldConvert, directMeta, added, threeEntity_1, isMultiLayerGroup, deferGeometry, isExtendBbox_1, commitEntity, layoutView, viewportView, fileName, error_2, didYield;
3155
- return __generator(this, function (_b) {
3156
- switch (_b.label) {
3444
+ var entity, shouldConvert, objectId_1, convertGen_1, directMeta, added, superseded, threeEntity_1, isMultiLayerGroup, deferGeometry, isExtendBbox_1, commitEntity, layoutView, viewportView, fileName, error_2, didYield;
3445
+ return __generator(this, function (_e) {
3446
+ switch (_e.label) {
3157
3447
  case 0:
3158
3448
  entity = entities[i];
3159
- _b.label = 1;
3449
+ _e.label = 1;
3160
3450
  case 1:
3161
- _b.trys.push([1, 9, 10, 11]);
3451
+ _e.trys.push([1, 9, 10, 11]);
3162
3452
  // Document was cleared / replaced while this batch was draining.
3163
3453
  if (epoch !== this_3._convertEpoch) {
3164
3454
  return [2 /*return*/, "continue"];
@@ -3184,23 +3474,43 @@ var AcTrView2d = /** @class */ (function (_super) {
3184
3474
  if (!shouldConvert) {
3185
3475
  return [2 /*return*/, "continue"];
3186
3476
  }
3477
+ objectId_1 = String((_a = entity.objectId) !== null && _a !== void 0 ? _a : '');
3478
+ convertGen_1 = 0;
3479
+ if (!options.forExport) {
3480
+ if (objectId_1 &&
3481
+ (this_3.hasEntity(objectId_1) ||
3482
+ this_3._claimedConvertObjectIds.has(objectId_1))) {
3483
+ return [2 /*return*/, "continue"];
3484
+ }
3485
+ if (objectId_1) {
3486
+ this_3._claimedConvertObjectIds.add(objectId_1);
3487
+ convertGen_1 = (_b = this_3._entityConvertGeneration.get(objectId_1)) !== null && _b !== void 0 ? _b : 0;
3488
+ }
3489
+ }
3187
3490
  directMeta = tryBuildDirectEntityMeta(entity, this_3._renderer);
3188
3491
  if (directMeta) {
3189
3492
  added = false;
3493
+ superseded = false;
3190
3494
  try {
3191
- added = this_3._scene.addDirectEntity(directMeta, shouldExtendBboxForDirectEntity(entity));
3192
- if (added) {
3193
- this_3.applySessionHiddenObjectState(entity.objectId);
3194
- if (progressive) {
3195
- this_3.markProgressiveDirty();
3196
- this_3._progressiveOpenFit.afterGeometryBatch(function () { return _this.resolveLayoutFitBox(); }, i);
3495
+ if (objectId_1 &&
3496
+ ((_c = this_3._entityConvertGeneration.get(objectId_1)) !== null && _c !== void 0 ? _c : 0) !== convertGen_1) {
3497
+ superseded = true;
3498
+ }
3499
+ else {
3500
+ added = this_3._scene.addDirectEntity(directMeta, shouldExtendBboxForDirectEntity(entity));
3501
+ if (added) {
3502
+ this_3.applySessionHiddenObjectState(entity.objectId);
3503
+ if (progressive) {
3504
+ this_3.markProgressiveDirty();
3505
+ this_3._progressiveOpenFit.afterGeometryBatch(function () { return _this.resolveLayoutFitBox(); }, i);
3506
+ }
3197
3507
  }
3198
3508
  }
3199
3509
  }
3200
3510
  finally {
3201
3511
  directMeta.geometry.dispose();
3202
3512
  }
3203
- if (added) {
3513
+ if (superseded || added) {
3204
3514
  return [2 /*return*/, "continue"];
3205
3515
  }
3206
3516
  // Append refused (e.g. invisible) — fall through to the legacy path.
@@ -3217,9 +3527,10 @@ var AcTrView2d = /** @class */ (function (_super) {
3217
3527
  threeEntity_1.visible = entity.visibility !== false;
3218
3528
  if (threeEntity_1 instanceof AcTrGroup &&
3219
3529
  threeEntity_1.isOnTheSameLayer) {
3220
- // Even when a block expands to a single layer bucket, children authored on
3221
- // layer "0" still inherit the INSERT layer for ByLayer traits (color, etc.).
3222
- this_3._inheritedLayerMaterialMapper.remap(threeEntity_1.children, '0', threeEntity_1.layerName);
3530
+ // Layer-0 inheritance must run AFTER finishEntityGeometry so TEXT/
3531
+ // MTEXT glyph materials exist. Remapping earlier (before asyncDraw)
3532
+ // leaves GM/GB-style labels as ACI-7 white while the block frame
3533
+ // remaps correctly (GAS-Meter / GAS-Box, A517B / A517E).
3223
3534
  threeEntity_1.userData.insertLayerName = threeEntity_1.layerName;
3224
3535
  }
3225
3536
  isMultiLayerGroup = threeEntity_1 instanceof AcTrGroup &&
@@ -3228,29 +3539,39 @@ var AcTrView2d = /** @class */ (function (_super) {
3228
3539
  if (!isMultiLayerGroup) return [3 /*break*/, 5];
3229
3540
  if (!deferGeometry) return [3 /*break*/, 2];
3230
3541
  this_3.enqueueDeferredGeometry(function () {
3231
- return _this.handleGroup(threeEntity_1, progressive, epoch);
3542
+ return _this.handleGroup(threeEntity_1, progressive, epoch, convertGen_1);
3232
3543
  }, epoch);
3233
3544
  return [3 /*break*/, 4];
3234
- case 2: return [4 /*yield*/, this_3.handleGroup(threeEntity_1, progressive, epoch)];
3545
+ case 2: return [4 /*yield*/, this_3.handleGroup(threeEntity_1, progressive, epoch, convertGen_1)];
3235
3546
  case 3:
3236
- _b.sent();
3237
- _b.label = 4;
3547
+ _e.sent();
3548
+ _e.label = 4;
3238
3549
  case 4: return [3 /*break*/, 8];
3239
3550
  case 5:
3240
3551
  isExtendBbox_1 = !(entity instanceof AcDbRay || entity instanceof AcDbXline);
3241
3552
  commitEntity = function () { return __awaiter(_this, void 0, void 0, function () {
3242
3553
  var _this = this;
3243
- return __generator(this, function (_a) {
3244
- switch (_a.label) {
3554
+ var _a;
3555
+ return __generator(this, function (_b) {
3556
+ switch (_b.label) {
3245
3557
  case 0: return [4 /*yield*/, this.finishEntityGeometry(threeEntity_1, progressive)];
3246
3558
  case 1:
3247
- _a.sent();
3559
+ _b.sent();
3248
3560
  if (epoch !== this._convertEpoch) {
3249
3561
  threeEntity_1.dispose();
3250
3562
  return [2 /*return*/];
3251
3563
  }
3564
+ if (objectId_1 &&
3565
+ ((_a = this._entityConvertGeneration.get(objectId_1)) !== null && _a !== void 0 ? _a : 0) !==
3566
+ convertGen_1) {
3567
+ threeEntity_1.dispose();
3568
+ return [2 /*return*/];
3569
+ }
3252
3570
  if (threeEntity_1 instanceof AcTrGroup) {
3253
3571
  this.syncGroupSpatialBoundsForIndexing(threeEntity_1);
3572
+ if (threeEntity_1.isOnTheSameLayer) {
3573
+ this._inheritedLayerMaterialMapper.remap(threeEntity_1.children, '0', threeEntity_1.layerName);
3574
+ }
3254
3575
  }
3255
3576
  this._scene.addEntity(threeEntity_1, isExtendBbox_1);
3256
3577
  this.applySessionHiddenObjectState(entity.objectId);
@@ -3269,8 +3590,8 @@ var AcTrView2d = /** @class */ (function (_super) {
3269
3590
  return [3 /*break*/, 8];
3270
3591
  case 6: return [4 /*yield*/, commitEntity()];
3271
3592
  case 7:
3272
- _b.sent();
3273
- _b.label = 8;
3593
+ _e.sent();
3594
+ _e.label = 8;
3274
3595
  case 8:
3275
3596
  if (epoch !== this_3._convertEpoch) {
3276
3597
  return [2 /*return*/, "continue"];
@@ -3294,10 +3615,13 @@ var AcTrView2d = /** @class */ (function (_super) {
3294
3615
  if (fileName && !entity.image) {
3295
3616
  this_3._missedImages.set(entity.objectId, fileName);
3296
3617
  }
3618
+ else if (entity.image) {
3619
+ this_3._missedImages.delete(entity.objectId);
3620
+ }
3297
3621
  }
3298
3622
  return [3 /*break*/, 11];
3299
3623
  case 9:
3300
- error_2 = _b.sent();
3624
+ error_2 = _e.sent();
3301
3625
  log.error("[AcTrView2d] Failed to convert entity ".concat(entity.objectId, " (").concat(entity.type, "):"), error_2);
3302
3626
  return [3 /*break*/, 11];
3303
3627
  case 10:
@@ -3314,24 +3638,24 @@ var AcTrView2d = /** @class */ (function (_super) {
3314
3638
  if (!yieldGate) return [3 /*break*/, 13];
3315
3639
  return [4 /*yield*/, yieldGate.maybeYield(yieldToEventLoop)];
3316
3640
  case 12:
3317
- didYield = _b.sent();
3641
+ didYield = _e.sent();
3318
3642
  if (didYield) {
3319
3643
  this_3._progressiveYieldCount++;
3320
3644
  }
3321
- _b.label = 13;
3645
+ _e.label = 13;
3322
3646
  case 13: return [2 /*return*/];
3323
3647
  }
3324
3648
  });
3325
3649
  };
3326
3650
  this_3 = this;
3327
3651
  i = 0;
3328
- _a.label = 1;
3652
+ _d.label = 1;
3329
3653
  case 1:
3330
3654
  if (!(i < entities.length)) return [3 /*break*/, 4];
3331
3655
  return [5 /*yield**/, _loop_4(i)];
3332
3656
  case 2:
3333
- _a.sent();
3334
- _a.label = 3;
3657
+ _d.sent();
3658
+ _d.label = 3;
3335
3659
  case 3:
3336
3660
  ++i;
3337
3661
  return [3 /*break*/, 1];
@@ -3361,20 +3685,28 @@ var AcTrView2d = /** @class */ (function (_super) {
3361
3685
  userData.spatialIndexChildBoxes = childBoxes;
3362
3686
  };
3363
3687
  AcTrView2d.prototype.handleGroup = function (group_1, progressive_1) {
3364
- return __awaiter(this, arguments, void 0, function (group, progressive, epoch) {
3365
- var children, objectsGroupByLayer, children_1, children_1_1, child, renderContext, groupObjectId, groupLayerName, groupChildBoxes, aggregateSpatialBbox;
3688
+ return __awaiter(this, arguments, void 0, function (group, progressive, epoch, convertGen) {
3689
+ var objectId, children, objectsGroupByLayer, children_1, children_1_1, child, renderContext, groupObjectId, groupLayerName, groupChildBoxes, aggregateSpatialBbox, registeredChildIndex;
3366
3690
  var e_15, _a;
3367
3691
  var _this = this;
3692
+ var _b, _c;
3368
3693
  if (epoch === void 0) { epoch = this._convertEpoch; }
3369
- return __generator(this, function (_b) {
3370
- switch (_b.label) {
3694
+ if (convertGen === void 0) { convertGen = 0; }
3695
+ return __generator(this, function (_d) {
3696
+ switch (_d.label) {
3371
3697
  case 0: return [4 /*yield*/, this.finishEntityGeometry(group, progressive)];
3372
3698
  case 1:
3373
- _b.sent();
3699
+ _d.sent();
3374
3700
  if (epoch !== this._convertEpoch) {
3375
3701
  group.dispose();
3376
3702
  return [2 /*return*/];
3377
3703
  }
3704
+ objectId = String((_b = group.objectId) !== null && _b !== void 0 ? _b : '');
3705
+ if (objectId &&
3706
+ ((_c = this._entityConvertGeneration.get(objectId)) !== null && _c !== void 0 ? _c : 0) !== convertGen) {
3707
+ group.dispose();
3708
+ return [2 /*return*/];
3709
+ }
3378
3710
  this.syncGroupSpatialBoundsForIndexing(group);
3379
3711
  children = group.children;
3380
3712
  objectsGroupByLayer = new Map();
@@ -3434,6 +3766,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3434
3766
  if (groupChildBoxes.length > 0) {
3435
3767
  group.wcsBbox = aggregateSpatialBbox.clone();
3436
3768
  }
3769
+ registeredChildIndex = false;
3437
3770
  objectsGroupByLayer.forEach(function (objects, layerName) {
3438
3771
  // Nested layer-0 may already be resolved to an inner INSERT layer during
3439
3772
  // flatten. Remaining "0" buckets inherit this (outermost) INSERT layer.
@@ -3469,7 +3802,10 @@ var AcTrView2d = /** @class */ (function (_super) {
3469
3802
  entity.userData.insertLayerName = groupLayerName;
3470
3803
  entity.wcsBbox = aggregateSpatialBbox.clone();
3471
3804
  var entityUserData = entity.userData;
3472
- entityUserData.spatialIndexChildBoxes = groupChildBoxes;
3805
+ if (!registeredChildIndex && groupChildBoxes.length > 0) {
3806
+ entityUserData.spatialIndexChildBoxes = groupChildBoxes;
3807
+ registeredChildIndex = true;
3808
+ }
3473
3809
  // Important:
3474
3810
  // DO NOT USE spread operator when adding objects because it may be one very large array
3475
3811
  // and can result in maximum call stack size exceeded
@@ -3506,6 +3842,36 @@ var AcTrView2d = /** @class */ (function (_super) {
3506
3842
  // the user pans/zooms (animate bails when !_isDirty && !_htmlDirty &&
3507
3843
  // !stillLoading).
3508
3844
  this._isDirty = true;
3845
+ // Missed images (and fonts already tracked) are collected during convert;
3846
+ // notify the Resources / External References palette once the queue is idle.
3847
+ eventBus.emit('missed-data-changed', {});
3848
+ }
3849
+ this.stampEntityProcessingIdle();
3850
+ };
3851
+ /**
3852
+ * Zooms to batch geometry once, after entity convert has drained.
3853
+ */
3854
+ AcTrView2d.prototype.frameOpenLineworkIfReady = function () {
3855
+ if (!this._openLineworkFramePending || this.isConvertingEntities) {
3856
+ return;
3857
+ }
3858
+ var box = this.resolveLayoutFitBox();
3859
+ if (!box || box.isEmpty()) {
3860
+ this._openLineworkFramePending = false;
3861
+ return;
3862
+ }
3863
+ this._openLineworkFramePending = false;
3864
+ // Programmatic so a progressive open does not treat this as a user zoom
3865
+ // and skip the glyph-aware final fit.
3866
+ this._progressiveOpenFit.frameProgrammatically(box);
3867
+ };
3868
+ /**
3869
+ * Records the moment convert and deferred glyph jobs are both idle.
3870
+ * Intermediate completions (linework done, text still queued) do not stamp.
3871
+ */
3872
+ AcTrView2d.prototype.stampEntityProcessingIdle = function () {
3873
+ if (this._numOfEntitiesToProcess === 0 && this._pendingGeometryJobs === 0) {
3874
+ this._entityProcessingIdleAt = performance.now();
3509
3875
  }
3510
3876
  };
3511
3877
  /**