@mindexec/cli 0.2.218 → 0.2.220

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 (23) hide show
  1. package/package.json +1 -1
  2. package/scripts/remote-fleet-render-smoke.mjs +11 -0
  3. package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +36 -6
  4. package/wwwroot/_content/MindExecution.Shared/js/background-themes.js +28 -113
  5. package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +2157 -104
  6. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +399 -96
  7. package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +121 -49
  8. package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +77 -3
  9. package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +83 -14
  10. package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-overlay-v2.js +5 -0
  11. package/wwwroot/_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js +2 -2
  12. package/wwwroot/_framework/{MindExecution.Core.21gc0645uz.dll → MindExecution.Core.xsad4wu36e.dll} +0 -0
  13. package/wwwroot/_framework/{MindExecution.Plugins.Admin.29mytzdaun.dll → MindExecution.Plugins.Admin.m5d94977um.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Plugins.Business.asckvekct8.dll → MindExecution.Plugins.Business.ntqdn1hta3.dll} +0 -0
  15. package/wwwroot/_framework/{MindExecution.Plugins.Concept.u9kzsgf64o.dll → MindExecution.Plugins.Concept.mqiyx4db4r.dll} +0 -0
  16. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.kibqg6rvqh.dll → MindExecution.Plugins.PlanMaster.fnortumnuu.dll} +0 -0
  17. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.089r64n2hv.dll → MindExecution.Plugins.YouTube.86osu6wpgh.dll} +0 -0
  18. package/wwwroot/_framework/{MindExecution.Shared.tlbhl3vzry.dll → MindExecution.Shared.wg2otcw4bv.dll} +0 -0
  19. package/wwwroot/_framework/{MindExecution.Web.7axuoygrwi.dll → MindExecution.Web.gfio3kfxrd.dll} +0 -0
  20. package/wwwroot/_framework/blazor.boot.json +17 -17
  21. package/wwwroot/index.html +3 -3
  22. package/wwwroot/service-worker-assets.js +28 -28
  23. package/wwwroot/service-worker.js +1 -1
@@ -5,7 +5,7 @@
5
5
  const DEBUG = false;
6
6
  const FPS_DEBUG = false;
7
7
  const FRAME_PERF_DEBUG = false;
8
- const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
8
+ const MINDMAP_CORE_BUILD_ID = '20260619-ultra-resident-camera-v666';
9
9
  const CanvasPhase = Object.freeze({
10
10
  Booting: 'booting',
11
11
  BoardFileLoading: 'board-file-loading',
@@ -94,7 +94,19 @@ const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
94
94
  const VISIBILITY_CULLING_ZOOM_HEAVY_FRAME_INTERVAL = 10;
95
95
  const FULL_RES_VISIBILITY_CULLING_PAN_FRAME_INTERVAL = 10;
96
96
  const FULL_RES_VISIBILITY_EXIT_MARGIN_SCALE = 1.8;
97
- const CAMERA_MOTION_WEBGL_FRAME_INTERVAL = 4;
97
+ const CAMERA_MOTION_WEBGL_FRAME_INTERVAL = 1;
98
+ const CAMERA_MOTION_GRID_HIDE_DELAY_MS = 90;
99
+ const CAMERA_MOTION_GRID_MIN_STEP_PX = 3;
100
+ const CAMERA_MOTION_GRID_MAX_STEP_PX = 48;
101
+ const CAMERA_MOTION_GRID_OVERSCAN_PX = 512;
102
+ const CAMERA_MOTION_GRID_Z_BUCKET_WORLD = 80;
103
+ const CAMERA_MOTION_GRID_FALLBACK_ENABLED = false;
104
+ const CAMERA_MOTION_PARTIAL_FRAME_SKIP_ENABLED = false;
105
+ const CAMERA_MOTION_VISUAL_ONLY_FAST_PATH_ENABLED = true;
106
+ const CAMERA_MOTION_WEBGL_CANVAS_TRANSFORM_ENABLED = true;
107
+ const CAMERA_STATIONARY_INTERACTION_FRAME_DELAY_MS = 48;
108
+ const CAMERA_MOTION_GLOBAL_STYLE_CLASS_ENABLED = false;
109
+ const CAMERA_DENSE_GLOBAL_STYLE_CLASS_ENABLED = false;
98
110
  const PIPELINE_PROFILE_SECTION_KEYS = Object.freeze([
99
111
  'pipelineViewport',
100
112
  'pipelineScheduling',
@@ -107,8 +119,8 @@ const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
107
119
  const FULL_RES_LOD_PAN_REFRESH_INTERVAL_MS = 96;
108
120
  const FULL_RES_LOD_ZOOM_REFRESH_INTERVAL_MS = 48;
109
121
  const CAMERA_LERP_BASE_FRAME_MS = 1000 / 60;
110
- const CAMERA_KEYBOARD_PAN_LERP_60HZ = 0.25;
111
- const CAMERA_MOUSE_PAN_LERP_60HZ = 0.25;
122
+ const CAMERA_KEYBOARD_PAN_LERP_60HZ = 1.0;
123
+ const CAMERA_MOUSE_PAN_LERP_60HZ = 1.0;
112
124
  const NODE_ADD_FRAME_BUDGET_MS = 6;
113
125
  const NODE_ADD_LOADING_FRAME_BUDGET_MS = 8;
114
126
  const NODE_ADD_MIN_SLICE = 8;
@@ -306,8 +318,9 @@ const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
306
318
  return false;
307
319
  }
308
320
 
309
- const width = Math.max(1, Number(module.container.clientWidth || 0));
310
- const height = Math.max(1, Number(module.container.clientHeight || 0));
321
+ const viewportMetrics = getCachedViewportMetrics(module);
322
+ const width = Math.max(1, Number(viewportMetrics.width || 0));
323
+ const height = Math.max(1, Number(viewportMetrics.height || 0));
311
324
  const camera = module.camera;
312
325
  const cursor = module.cursorPosition;
313
326
  const vfov = (camera.fov * Math.PI) / 180;
@@ -368,28 +381,77 @@ const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
368
381
 
369
382
  const _mindCanvasPerfClassState = {
370
383
  moving: false,
371
- dense: false
384
+ dense: false,
385
+ target: null
372
386
  };
373
387
  const _renderDebugBodyClassState = {
374
388
  hideVideoSurfaceInNear: false,
375
389
  tintVideoLayoutInNear: false,
376
390
  disableNearVideoGlow: false
377
391
  };
392
+ const MINDCANVAS_PERF_CLASSES_ENABLED =
393
+ CAMERA_MOTION_GLOBAL_STYLE_CLASS_ENABLED === true ||
394
+ CAMERA_DENSE_GLOBAL_STYLE_CLASS_ENABLED === true;
395
+
396
+ function shouldSyncMindCanvasPerfClasses() {
397
+ return MINDCANVAS_PERF_CLASSES_ENABLED === true ||
398
+ !!(_mindCanvasPerfClassState.target &&
399
+ (_mindCanvasPerfClassState.moving || _mindCanvasPerfClassState.dense));
400
+ }
378
401
 
379
402
  function syncMindCanvasPerfClasses({ moving = false, dense = false } = {}) {
403
+ if (MINDCANVAS_PERF_CLASSES_ENABLED !== true) {
404
+ if (_mindCanvasPerfClassState.target &&
405
+ (_mindCanvasPerfClassState.moving || _mindCanvasPerfClassState.dense)) {
406
+ _mindCanvasPerfClassState.target.classList?.remove?.('mindcanvas-is-moving', 'mindcanvas-is-dense');
407
+ }
408
+ _mindCanvasPerfClassState.moving = false;
409
+ _mindCanvasPerfClassState.dense = false;
410
+ return;
411
+ }
412
+
380
413
  if (typeof document === 'undefined' || !document.body) {
381
414
  return;
382
415
  }
383
416
 
384
- if (_mindCanvasPerfClassState.moving !== moving) {
385
- document.body.classList.toggle('mindcanvas-is-moving', moving);
386
- _mindCanvasPerfClassState.moving = moving;
417
+ const targetElement = moduleInstance?.cssRenderer?.domElement || document.body;
418
+ if (_mindCanvasPerfClassState.target && _mindCanvasPerfClassState.target !== targetElement) {
419
+ _mindCanvasPerfClassState.target.classList?.remove?.('mindcanvas-is-moving', 'mindcanvas-is-dense');
420
+ _mindCanvasPerfClassState.moving = false;
421
+ _mindCanvasPerfClassState.dense = false;
422
+ }
423
+ _mindCanvasPerfClassState.target = targetElement;
424
+
425
+ const normalizedMoving = CAMERA_MOTION_GLOBAL_STYLE_CLASS_ENABLED === true && moving === true;
426
+ const normalizedDense = CAMERA_DENSE_GLOBAL_STYLE_CLASS_ENABLED === true && dense === true;
427
+
428
+ if (_mindCanvasPerfClassState.moving !== normalizedMoving) {
429
+ targetElement.classList.toggle('mindcanvas-is-moving', normalizedMoving);
430
+ _mindCanvasPerfClassState.moving = normalizedMoving;
431
+ }
432
+
433
+ if (_mindCanvasPerfClassState.dense !== normalizedDense) {
434
+ targetElement.classList.toggle('mindcanvas-is-dense', normalizedDense);
435
+ _mindCanvasPerfClassState.dense = normalizedDense;
436
+ }
437
+ }
438
+
439
+ function isContinuousBackgroundTheme(module) {
440
+ if (!module?.currentThemeObject || typeof window === 'undefined') {
441
+ return false;
387
442
  }
388
443
 
389
- if (_mindCanvasPerfClassState.dense !== dense) {
390
- document.body.classList.toggle('mindcanvas-is-dense', dense);
391
- _mindCanvasPerfClassState.dense = dense;
444
+ const mode = window.MindMapThemes?.getAnimationMode?.(module.currentThemeName, module.currentThemeObject);
445
+ return mode === 'continuous';
446
+ }
447
+
448
+ function deferContinuousThemeForCameraMotion(module) {
449
+ if (!isContinuousBackgroundTheme(module)) {
450
+ return false;
392
451
  }
452
+
453
+ module._themeCameraMotionDeferred = true;
454
+ return true;
393
455
  }
394
456
 
395
457
  function syncRenderDebugBodyClasses(flags = {}) {
@@ -446,7 +508,8 @@ const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
446
508
  freezeVisibilityDuringMotion: false,
447
509
  freezeLodDuringMotion: false,
448
510
  enableFramePerfProbe: false,
449
- enableCss3dWillChange: true,
511
+ sampleWebglDuringCameraMotion: false,
512
+ enableCss3dWillChange: false,
450
513
  enableThemeAnimation: true,
451
514
  enableMenuOverlayUi: true,
452
515
  enableMultiSelectOverlayUi: true,
@@ -842,10 +905,170 @@ const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
842
905
  };
843
906
  }
844
907
 
908
+ function createEmptyFramePathStats(startedAt = 0) {
909
+ return {
910
+ total: 0,
911
+ visualOnly: 0,
912
+ stationaryVisual: 0,
913
+ residentVisual: 0,
914
+ earlyCameraOnly: 0,
915
+ css3dCameraFast: 0,
916
+ normal: 0,
917
+ visibilityCulling: 0,
918
+ lodUpdateCalled: 0,
919
+ css3dTransformSync: 0,
920
+ css3dRender: 0,
921
+ textOverlaySync: 0,
922
+ hiddenWork: 0,
923
+ visualOnlyPercent: 0,
924
+ cameraFastPercent: 0,
925
+ normalPercent: 0,
926
+ hiddenWorkPercent: 0,
927
+ lastMode: '',
928
+ lastReason: '',
929
+ lastViewSyncMode: '',
930
+ lastLodSkipReason: '',
931
+ lastVisibilitySkipReason: '',
932
+ startedAt: Number(startedAt || 0),
933
+ updatedAt: 0
934
+ };
935
+ }
936
+
937
+ function getFrameProfilePathMode(frameProfile) {
938
+ if (!frameProfile) {
939
+ return 'none';
940
+ }
941
+
942
+ if (frameProfile.uaUltraResidentCameraOnly === true) {
943
+ return 'ultra-resident-camera-only';
944
+ }
945
+
946
+ if (frameProfile.uaUltraEarlyCameraOnly === true) {
947
+ return 'ultra-camera-only';
948
+ }
949
+
950
+ if (frameProfile.uaVisualOnlyMotion === true) {
951
+ return 'visual-only';
952
+ }
953
+
954
+ if (frameProfile.uaStationaryVisualMotion === true) {
955
+ return 'stationary-visual';
956
+ }
957
+
958
+ if (frameProfile.uaResidentVisualOnlyMotion === true) {
959
+ return 'resident-visual';
960
+ }
961
+
962
+ const viewSyncMode = String(frameProfile.viewSyncMode || '');
963
+ if (viewSyncMode === 'ua-early-camera-only') {
964
+ return 'early-camera-only';
965
+ }
966
+
967
+ if (viewSyncMode === 'ua-stationary-camera-navigation') {
968
+ return 'stationary-camera-navigation';
969
+ }
970
+
971
+ if (frameProfile.usedCss3dCameraFastPath === true) {
972
+ return 'css3d-camera-fast';
973
+ }
974
+
975
+ return 'normal';
976
+ }
977
+
978
+ function updateFramePathStats(module, frameProfile, updatedAt = 0) {
979
+ if (!module || !frameProfile) {
980
+ return;
981
+ }
982
+
983
+ const now = Number(
984
+ updatedAt ||
985
+ (typeof performance !== 'undefined' && typeof performance.now === 'function'
986
+ ? performance.now()
987
+ : Date.now())
988
+ );
989
+ const previousStats = module._framePathStats || null;
990
+ const shouldStartWindow = !previousStats || Number(previousStats.total || 0) >= 600;
991
+ const stats = shouldStartWindow ? createEmptyFramePathStats(now) : previousStats;
992
+ const mode = getFrameProfilePathMode(frameProfile);
993
+ const visibilitySkipReason = String(frameProfile.visibilitySkipReason || '');
994
+ const lodSkipReason = String(frameProfile.lodUpdateSkipReason || frameProfile.lodSkipReason || '');
995
+ const viewSyncMode = String(frameProfile.viewSyncMode || '');
996
+ const hiddenWork =
997
+ frameProfile.shouldRunVisibilityCulling === true ||
998
+ frameProfile.lodUpdateCalled === true ||
999
+ frameProfile.shouldSyncCss3dTransforms === true ||
1000
+ frameProfile.shouldRenderCss3d === true ||
1001
+ frameProfile.shouldSyncTextOverlays === true;
1002
+
1003
+ stats.total += 1;
1004
+ if (frameProfile.uaVisualOnlyMotion === true) {
1005
+ stats.visualOnly += 1;
1006
+ }
1007
+ if (frameProfile.uaStationaryVisualMotion === true) {
1008
+ stats.stationaryVisual += 1;
1009
+ }
1010
+ if (frameProfile.uaResidentVisualOnlyMotion === true ||
1011
+ frameProfile.uaUltraResidentCameraOnly === true) {
1012
+ stats.residentVisual += 1;
1013
+ }
1014
+ if (mode === 'ultra-camera-only' ||
1015
+ mode === 'ultra-resident-camera-only' ||
1016
+ mode === 'early-camera-only' ||
1017
+ mode === 'stationary-camera-navigation') {
1018
+ stats.earlyCameraOnly += 1;
1019
+ }
1020
+ if (frameProfile.usedCss3dCameraFastPath === true) {
1021
+ stats.css3dCameraFast += 1;
1022
+ }
1023
+ if (mode === 'normal') {
1024
+ stats.normal += 1;
1025
+ }
1026
+ if (frameProfile.shouldRunVisibilityCulling === true) {
1027
+ stats.visibilityCulling += 1;
1028
+ }
1029
+ if (frameProfile.lodUpdateCalled === true) {
1030
+ stats.lodUpdateCalled += 1;
1031
+ }
1032
+ if (frameProfile.shouldSyncCss3dTransforms === true) {
1033
+ stats.css3dTransformSync += 1;
1034
+ }
1035
+ if (frameProfile.shouldRenderCss3d === true) {
1036
+ stats.css3dRender += 1;
1037
+ }
1038
+ if (frameProfile.shouldSyncTextOverlays === true) {
1039
+ stats.textOverlaySync += 1;
1040
+ }
1041
+ if (hiddenWork) {
1042
+ stats.hiddenWork += 1;
1043
+ }
1044
+
1045
+ const total = Math.max(1, Number(stats.total || 0));
1046
+ const visualOnlyTotal = Number(stats.visualOnly || 0) +
1047
+ Number(stats.stationaryVisual || 0) +
1048
+ Number(stats.residentVisual || 0) +
1049
+ Number(stats.earlyCameraOnly || 0);
1050
+ stats.visualOnlyPercent = Number((visualOnlyTotal / total * 100).toFixed(1));
1051
+ stats.cameraFastPercent = Number((Number(stats.css3dCameraFast || 0) / total * 100).toFixed(1));
1052
+ stats.normalPercent = Number((Number(stats.normal || 0) / total * 100).toFixed(1));
1053
+ stats.hiddenWorkPercent = Number((Number(stats.hiddenWork || 0) / total * 100).toFixed(1));
1054
+ stats.lastMode = mode;
1055
+ stats.lastReason = visibilitySkipReason || lodSkipReason || viewSyncMode || mode;
1056
+ stats.lastViewSyncMode = viewSyncMode;
1057
+ stats.lastLodSkipReason = lodSkipReason;
1058
+ stats.lastVisibilitySkipReason = visibilitySkipReason;
1059
+ stats.updatedAt = now;
1060
+ module._framePathStats = stats;
1061
+ }
1062
+
845
1063
  function resetFramePerfPeakProfile(module) {
846
1064
  const peakProfile = createEmptyFramePerfPeakProfile();
847
1065
  if (module) {
848
1066
  module._framePerfPeakProfile = peakProfile;
1067
+ module._framePathStats = createEmptyFramePathStats(
1068
+ typeof performance !== 'undefined' && typeof performance.now === 'function'
1069
+ ? performance.now()
1070
+ : Date.now()
1071
+ );
849
1072
  }
850
1073
  return peakProfile;
851
1074
  }
@@ -856,6 +1079,7 @@ const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
856
1079
  }
857
1080
 
858
1081
  const peakProfile = module._framePerfPeakProfile || resetFramePerfPeakProfile(module);
1082
+ updateFramePathStats(module, frameProfile, updatedAt);
859
1083
  let changed = false;
860
1084
  const durationMs = Number(frameProfile.durationMs || 0);
861
1085
  if (durationMs > Number(peakProfile.durationMs || 0)) {
@@ -889,6 +1113,445 @@ const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
889
1113
  }
890
1114
  }
891
1115
 
1116
+ function tryRunUltraEarlyCameraOnlyFrame(module, loop, frameStart, isCameraMoving, shouldCaptureFramePerf, frameProfTimes, profileSection) {
1117
+ if (!module ||
1118
+ CAMERA_MOTION_VISUAL_ONLY_FAST_PATH_ENABLED !== true ||
1119
+ isCameraMoving !== true ||
1120
+ module.isLoading === true ||
1121
+ module.isSelecting === true ||
1122
+ module.isDraggingNode === true ||
1123
+ module.isDraggingMultipleNodes === true ||
1124
+ module.isResizing === true ||
1125
+ module.isWindowResizing === true ||
1126
+ !module.cssRenderer ||
1127
+ typeof module.cssRenderer.renderCamera !== 'function' ||
1128
+ !module.camera) {
1129
+ return false;
1130
+ }
1131
+
1132
+ const renderDebugFlags = module.renderDebugFlags || getRenderDebugFlagsSnapshot();
1133
+ if (renderDebugFlags.enableCss3d === false) {
1134
+ return false;
1135
+ }
1136
+
1137
+ if (module.lodRenderer?.isInLODMode === true) {
1138
+ return false;
1139
+ }
1140
+
1141
+ const forceUpdateFrames = Math.max(0, Number(module._forceUpdateFrames || 0));
1142
+ const cameraNavigationForceFrames = Math.max(0, Number(module._cameraNavigationForceFrames || 0));
1143
+ const panLiveRefreshForceFrames = Math.max(0, Number(module._panLiveRefreshForceFrames || 0));
1144
+ const hasForcedUpdate = forceUpdateFrames > 0;
1145
+ const hasOnlyCameraNavigationForcedUpdate =
1146
+ hasForcedUpdate &&
1147
+ (module.isPanning === true || module.isZooming === true || isCameraMoving === true) &&
1148
+ cameraNavigationForceFrames >= forceUpdateFrames;
1149
+ const hasOnlyPanLiveRefreshForcedUpdate =
1150
+ hasForcedUpdate &&
1151
+ module.isPanning === true &&
1152
+ module.isZooming !== true &&
1153
+ panLiveRefreshForceFrames >= forceUpdateFrames;
1154
+ const hasNonCameraNavigationForcedUpdate =
1155
+ hasForcedUpdate &&
1156
+ hasOnlyCameraNavigationForcedUpdate !== true &&
1157
+ hasOnlyPanLiveRefreshForcedUpdate !== true;
1158
+ if (hasNonCameraNavigationForcedUpdate) {
1159
+ return false;
1160
+ }
1161
+
1162
+ const hasEditingTextOverlayFocus = !!(
1163
+ String(module._textOverlayV2State?.editing?.nodeId || '').trim() ||
1164
+ String(module._editingOverlayState?.nodeId || '').trim()
1165
+ );
1166
+ if (hasEditingTextOverlayFocus) {
1167
+ return false;
1168
+ }
1169
+
1170
+ const hasPendingCss3dStructuralTransformWork = !!(
1171
+ (module._cssTransformDirtyIds?.size || 0) > 0 ||
1172
+ module._cssParentRepairNeeded === true
1173
+ );
1174
+ if (hasPendingCss3dStructuralTransformWork) {
1175
+ return false;
1176
+ }
1177
+
1178
+ const hasPendingCss3dVisualWork = !!(
1179
+ module._css3dVisualChangedThisFrame === true ||
1180
+ module._css3dVisualChangedSinceLastRender === true
1181
+ );
1182
+ if (hasPendingCss3dVisualWork) {
1183
+ module._css3dVisualDeferredForMotion = true;
1184
+ }
1185
+
1186
+ if (hasForcedUpdate) {
1187
+ module._forceUpdateFrames = Math.max(0, forceUpdateFrames - 1);
1188
+ if (cameraNavigationForceFrames > 0) {
1189
+ module._cameraNavigationForceFrames = Math.max(0, cameraNavigationForceFrames - 1);
1190
+ }
1191
+ if (panLiveRefreshForceFrames > 0) {
1192
+ module._panLiveRefreshForceFrames = Math.max(0, panLiveRefreshForceFrames - 1);
1193
+ }
1194
+ }
1195
+
1196
+ module._fullResMotionFrozen = true;
1197
+ module._wasViewStateMotionFrozenLastFrame = true;
1198
+ module._wasLodMotionFrozenLastFrame = false;
1199
+ module._fullResVisibilityReconcilePending = true;
1200
+ module._preserveVisibleSetThisFrame = true;
1201
+ module._lastVisibilityCullingSkipReason = 'ua-ultra-camera-only';
1202
+ if (module._overlayDirty === true ||
1203
+ String(module._textOverlayV2State?.selectionNodeId || '').trim() ||
1204
+ String(module._selectableTextOverlayNodeId || '').trim()) {
1205
+ module._deferPassiveOverlayRefresh = true;
1206
+ }
1207
+
1208
+ if (shouldSyncMindCanvasPerfClasses()) {
1209
+ const visibleNodeCount = Number(module._visibleIds?.size || module._prevVisibleIds?.size || 0);
1210
+ const totalNodeCount = Number(module.nodeObjectsById?.size || 0);
1211
+ syncMindCanvasPerfClasses({
1212
+ moving: true,
1213
+ dense:
1214
+ visibleNodeCount >= DENSE_VISIBLE_NODE_THRESHOLD ||
1215
+ totalNodeCount >= DENSE_TOTAL_NODE_THRESHOLD
1216
+ });
1217
+ }
1218
+
1219
+ updateCursorDomElement(module);
1220
+
1221
+ const isWebglRenderEnabled = renderDebugFlags.enableWebglRender !== false;
1222
+ let usedWebglCanvasCameraTransform = false;
1223
+ if (isWebglRenderEnabled && module.renderer && module.scene && module.camera) {
1224
+ const canReuseWebglCanvas = !!(
1225
+ CAMERA_MOTION_WEBGL_CANVAS_TRANSFORM_ENABLED === true &&
1226
+ module._lastWebglRenderedCameraState &&
1227
+ module.renderer?.domElement
1228
+ );
1229
+ if (canReuseWebglCanvas === true) {
1230
+ deferContinuousThemeForCameraMotion(module);
1231
+ usedWebglCanvasCameraTransform = module._applyWebglCanvasCameraTransform?.() === true;
1232
+ module._webglCameraMotionDeferred = true;
1233
+ module._lastWebglRenderInfo = {
1234
+ calls: 0,
1235
+ triangles: 0,
1236
+ points: 0,
1237
+ lines: 0,
1238
+ skipped: true,
1239
+ webglCanvasCameraTransform: usedWebglCanvasCameraTransform === true,
1240
+ uaUltraEarlyCameraOnly: true
1241
+ };
1242
+ } else {
1243
+ module._clearWebglCanvasCameraTransform?.();
1244
+ module.renderer.render(module.scene, module.camera);
1245
+ module._recordWebglRenderedCameraState?.();
1246
+ const renderInfo = module.renderer?.info?.render || null;
1247
+ module._lastWebglRenderInfo = renderInfo ? {
1248
+ calls: Number(renderInfo.calls || 0),
1249
+ triangles: Number(renderInfo.triangles || 0),
1250
+ points: Number(renderInfo.points || 0),
1251
+ lines: Number(renderInfo.lines || 0),
1252
+ uaUltraEarlyCameraOnly: true,
1253
+ webglCanvasCameraBootstrap: true
1254
+ } : null;
1255
+ }
1256
+ }
1257
+
1258
+ module._syncMotionGridLayerForCameraNavigation?.(usedWebglCanvasCameraTransform);
1259
+ module.cssRenderer.renderCamera(module.camera);
1260
+
1261
+ if ((module._businessAutomationEdgeVisualLayer?.isConnected ||
1262
+ module._businessAutomationEdgeLayer?.isConnected) &&
1263
+ window.MindMapCss3DManager?.syncBusinessAutomationEdgesForCameraMotion) {
1264
+ const transformedEdges =
1265
+ window.MindMapCss3DManager.syncBusinessAutomationEdgesForCameraMotion(module) === true;
1266
+ module._businessAutomationEdgesDeferredForMotion = transformedEdges !== false;
1267
+ }
1268
+
1269
+ profileSection?.('uaUltraEarlyCameraOnly');
1270
+
1271
+ const frameEnd = performance.now();
1272
+ const frameDuration = frameEnd - frameStart;
1273
+ module._lastFrameDurationMs = frameDuration;
1274
+ module._lastFrameMetricsAt = frameEnd;
1275
+ if (shouldCaptureFramePerf) {
1276
+ const sectionTimes = {};
1277
+ Object.entries(frameProfTimes || {})
1278
+ .filter(([key, value]) => key !== 'visibleNodes' && typeof value === 'number')
1279
+ .forEach(([key, value]) => {
1280
+ sectionTimes[key] = Number(value.toFixed(2));
1281
+ });
1282
+ module._lastFramePerfProfile = {
1283
+ durationMs: Number(frameDuration.toFixed(2)),
1284
+ update: false,
1285
+ cameraZ: Number((module.camera?.position?.z || 0).toFixed(2)),
1286
+ isZooming: module.isZooming === true,
1287
+ isPanning: module.isPanning === true,
1288
+ isCameraMoving: true,
1289
+ isInLodMode: false,
1290
+ lodBand: module.lodRenderer?._currentLodBand || null,
1291
+ visibleNodes: Number(module._visibleIds?.size || module._prevVisibleIds?.size || 0),
1292
+ freezeVisibility: true,
1293
+ freezeLod: false,
1294
+ freezeFullRes: true,
1295
+ residentMidFarStable: false,
1296
+ residentDirty: false,
1297
+ residentBandChanging: false,
1298
+ viewSyncMode: 'ua-ultra-camera-only',
1299
+ lodUpdateCalled: false,
1300
+ lodUpdateSkipped: true,
1301
+ lodUpdateSkipReason: 'ua-ultra-camera-only',
1302
+ lodSkipReason: '',
1303
+ lodDirtyReason: '',
1304
+ lodDirtyState: null,
1305
+ viewSyncBreakdown: null,
1306
+ shouldRunVisibilityCulling: false,
1307
+ visibilityStableSkip: true,
1308
+ visibilitySkipReason: 'ua-ultra-camera-only',
1309
+ incrementalPanVisibility: false,
1310
+ shouldSyncCss3dTransforms: false,
1311
+ shouldRenderCss3d: false,
1312
+ usedCss3dCameraFastPath: true,
1313
+ uaUltraEarlyCameraOnly: true,
1314
+ shouldSyncTextOverlays: false,
1315
+ usedPassiveOverlayCameraFastPath: false,
1316
+ passiveOverlaySuppressed: module._suppressPassiveDomOverlayDuringZoom === true,
1317
+ webgl: module._lastWebglRenderInfo || null,
1318
+ sections: sectionTimes
1319
+ };
1320
+ updateFramePerfPeakProfile(module, module._lastFramePerfProfile, frameEnd);
1321
+ } else if (module._lastFramePerfProfile) {
1322
+ module._lastFramePerfProfile = null;
1323
+ }
1324
+
1325
+ module._lastAnimationLoopDelayMs = 0;
1326
+ module._animationLoopExecuting = false;
1327
+ module._scheduleAnimationLoop(loop, 0);
1328
+ return true;
1329
+ }
1330
+
1331
+ function tryRunUltraEarlyResidentCameraOnlyFrame(module, loop, frameStart, isCameraMoving, shouldCaptureFramePerf, frameProfTimes, profileSection) {
1332
+ if (!module ||
1333
+ CAMERA_MOTION_VISUAL_ONLY_FAST_PATH_ENABLED !== true ||
1334
+ isCameraMoving !== true ||
1335
+ module.isLoading === true ||
1336
+ module.isSelecting === true ||
1337
+ module.isDraggingNode === true ||
1338
+ module.isDraggingMultipleNodes === true ||
1339
+ module.isResizing === true ||
1340
+ module.isWindowResizing === true ||
1341
+ !module.camera ||
1342
+ !module.lodRenderer ||
1343
+ module.lodRenderer.isInLODMode !== true ||
1344
+ typeof module.lodRenderer.canUseUltraResidentCameraOnlyFrame !== 'function' ||
1345
+ module.lodRenderer.canUseUltraResidentCameraOnlyFrame(module.camera, module) !== true) {
1346
+ return false;
1347
+ }
1348
+
1349
+ const renderDebugFlags = module.renderDebugFlags || getRenderDebugFlagsSnapshot();
1350
+ const isWebglRenderEnabled = renderDebugFlags.enableWebglRender !== false;
1351
+ const isCss3dEnabled = renderDebugFlags.enableCss3d !== false;
1352
+ const canRenderCss3dCamera = !!(
1353
+ isCss3dEnabled === true &&
1354
+ module.cssRenderer &&
1355
+ typeof module.cssRenderer.renderCamera === 'function' &&
1356
+ (
1357
+ (module.lodRenderer._lodImageCssFallbackNodeIds?.size || 0) > 0 ||
1358
+ (module.lodRenderer._lodTemplateCssNodeIds?.size || 0) > 0
1359
+ )
1360
+ );
1361
+ if (isWebglRenderEnabled !== true && canRenderCss3dCamera !== true) {
1362
+ return false;
1363
+ }
1364
+
1365
+ const forceUpdateFrames = Math.max(0, Number(module._forceUpdateFrames || 0));
1366
+ const cameraNavigationForceFrames = Math.max(0, Number(module._cameraNavigationForceFrames || 0));
1367
+ const panLiveRefreshForceFrames = Math.max(0, Number(module._panLiveRefreshForceFrames || 0));
1368
+ const hasForcedUpdate = forceUpdateFrames > 0;
1369
+ const hasOnlyCameraNavigationForcedUpdate =
1370
+ hasForcedUpdate &&
1371
+ (module.isPanning === true || module.isZooming === true || isCameraMoving === true) &&
1372
+ cameraNavigationForceFrames >= forceUpdateFrames;
1373
+ const hasOnlyPanLiveRefreshForcedUpdate =
1374
+ hasForcedUpdate &&
1375
+ module.isPanning === true &&
1376
+ module.isZooming !== true &&
1377
+ panLiveRefreshForceFrames >= forceUpdateFrames;
1378
+ const hasNonCameraNavigationForcedUpdate =
1379
+ hasForcedUpdate &&
1380
+ hasOnlyCameraNavigationForcedUpdate !== true &&
1381
+ hasOnlyPanLiveRefreshForcedUpdate !== true;
1382
+ if (hasNonCameraNavigationForcedUpdate) {
1383
+ return false;
1384
+ }
1385
+
1386
+ const hasEditingTextOverlayFocus = !!(
1387
+ String(module._textOverlayV2State?.editing?.nodeId || '').trim() ||
1388
+ String(module._editingOverlayState?.nodeId || '').trim()
1389
+ );
1390
+ if (hasEditingTextOverlayFocus) {
1391
+ return false;
1392
+ }
1393
+
1394
+ const hasPendingCss3dStructuralTransformWork = !!(
1395
+ (module._cssTransformDirtyIds?.size || 0) > 0 ||
1396
+ module._cssParentRepairNeeded === true
1397
+ );
1398
+ if (hasPendingCss3dStructuralTransformWork) {
1399
+ return false;
1400
+ }
1401
+
1402
+ const hasPendingCss3dVisualWork = !!(
1403
+ module._css3dVisualChangedThisFrame === true ||
1404
+ module._css3dVisualChangedSinceLastRender === true
1405
+ );
1406
+ if (hasPendingCss3dVisualWork) {
1407
+ module._css3dVisualDeferredForMotion = true;
1408
+ }
1409
+
1410
+ if (hasForcedUpdate) {
1411
+ module._forceUpdateFrames = Math.max(0, forceUpdateFrames - 1);
1412
+ if (cameraNavigationForceFrames > 0) {
1413
+ module._cameraNavigationForceFrames = Math.max(0, cameraNavigationForceFrames - 1);
1414
+ }
1415
+ if (panLiveRefreshForceFrames > 0) {
1416
+ module._panLiveRefreshForceFrames = Math.max(0, panLiveRefreshForceFrames - 1);
1417
+ }
1418
+ }
1419
+
1420
+ module._fullResMotionFrozen = false;
1421
+ module._wasViewStateMotionFrozenLastFrame = false;
1422
+ module._wasLodMotionFrozenLastFrame = true;
1423
+ module._preserveVisibleSetThisFrame = true;
1424
+ module._lastVisibilityCullingSkipReason = 'ua-ultra-resident-camera-only';
1425
+ if (module._overlayDirty === true ||
1426
+ String(module._textOverlayV2State?.selectionNodeId || '').trim() ||
1427
+ String(module._selectableTextOverlayNodeId || '').trim()) {
1428
+ module._deferPassiveOverlayRefresh = true;
1429
+ }
1430
+
1431
+ if (shouldSyncMindCanvasPerfClasses()) {
1432
+ const visibleNodeCount = Number(module._visibleIds?.size || module._prevVisibleIds?.size || 0);
1433
+ const totalNodeCount = Number(module.nodeObjectsById?.size || 0);
1434
+ syncMindCanvasPerfClasses({
1435
+ moving: true,
1436
+ dense:
1437
+ visibleNodeCount >= DENSE_VISIBLE_NODE_THRESHOLD ||
1438
+ totalNodeCount >= DENSE_TOTAL_NODE_THRESHOLD
1439
+ });
1440
+ }
1441
+
1442
+ updateCursorDomElement(module);
1443
+
1444
+ let usedWebglCanvasCameraTransform = false;
1445
+ if (isWebglRenderEnabled === true && module.renderer && module.scene && module.camera) {
1446
+ const canReuseWebglCanvas = !!(
1447
+ CAMERA_MOTION_WEBGL_CANVAS_TRANSFORM_ENABLED === true &&
1448
+ module._lastWebglRenderedCameraState &&
1449
+ module.renderer?.domElement
1450
+ );
1451
+ if (canReuseWebglCanvas === true) {
1452
+ deferContinuousThemeForCameraMotion(module);
1453
+ usedWebglCanvasCameraTransform = module._applyWebglCanvasCameraTransform?.() === true;
1454
+ module._webglCameraMotionDeferred = true;
1455
+ module._lastWebglRenderInfo = {
1456
+ calls: 0,
1457
+ triangles: 0,
1458
+ points: 0,
1459
+ lines: 0,
1460
+ skipped: true,
1461
+ webglCanvasCameraTransform: usedWebglCanvasCameraTransform === true,
1462
+ uaUltraResidentCameraOnly: true
1463
+ };
1464
+ } else {
1465
+ module._clearWebglCanvasCameraTransform?.();
1466
+ module.renderer.render(module.scene, module.camera);
1467
+ module._recordWebglRenderedCameraState?.();
1468
+ const renderInfo = module.renderer?.info?.render || null;
1469
+ module._lastWebglRenderInfo = renderInfo ? {
1470
+ calls: Number(renderInfo.calls || 0),
1471
+ triangles: Number(renderInfo.triangles || 0),
1472
+ points: Number(renderInfo.points || 0),
1473
+ lines: Number(renderInfo.lines || 0),
1474
+ uaUltraResidentCameraOnly: true,
1475
+ webglCanvasCameraBootstrap: true
1476
+ } : null;
1477
+ }
1478
+ }
1479
+
1480
+ module._syncMotionGridLayerForCameraNavigation?.(usedWebglCanvasCameraTransform);
1481
+ if (canRenderCss3dCamera === true) {
1482
+ module.cssRenderer.renderCamera(module.camera);
1483
+ }
1484
+
1485
+ if ((module._businessAutomationEdgeVisualLayer?.isConnected ||
1486
+ module._businessAutomationEdgeLayer?.isConnected) &&
1487
+ window.MindMapCss3DManager?.syncBusinessAutomationEdgesForCameraMotion) {
1488
+ const transformedEdges =
1489
+ window.MindMapCss3DManager.syncBusinessAutomationEdgesForCameraMotion(module) === true;
1490
+ module._businessAutomationEdgesDeferredForMotion = transformedEdges !== false;
1491
+ }
1492
+
1493
+ profileSection?.('uaUltraResidentCameraOnly');
1494
+
1495
+ const frameEnd = performance.now();
1496
+ const frameDuration = frameEnd - frameStart;
1497
+ module._lastFrameDurationMs = frameDuration;
1498
+ module._lastFrameMetricsAt = frameEnd;
1499
+ if (shouldCaptureFramePerf) {
1500
+ const sectionTimes = {};
1501
+ Object.entries(frameProfTimes || {})
1502
+ .filter(([key, value]) => key !== 'visibleNodes' && typeof value === 'number')
1503
+ .forEach(([key, value]) => {
1504
+ sectionTimes[key] = Number(value.toFixed(2));
1505
+ });
1506
+ module._lastFramePerfProfile = {
1507
+ durationMs: Number(frameDuration.toFixed(2)),
1508
+ update: false,
1509
+ cameraZ: Number((module.camera?.position?.z || 0).toFixed(2)),
1510
+ isZooming: module.isZooming === true,
1511
+ isPanning: module.isPanning === true,
1512
+ isCameraMoving: true,
1513
+ isInLodMode: true,
1514
+ lodBand: module.lodRenderer?._currentLodBand || null,
1515
+ visibleNodes: Number(module._visibleIds?.size || module._prevVisibleIds?.size || 0),
1516
+ freezeVisibility: true,
1517
+ freezeLod: true,
1518
+ freezeFullRes: false,
1519
+ residentMidFarStable: true,
1520
+ residentDirty: false,
1521
+ residentBandChanging: false,
1522
+ viewSyncMode: 'ua-ultra-resident-camera-only',
1523
+ lodUpdateCalled: false,
1524
+ lodUpdateSkipped: true,
1525
+ lodUpdateSkipReason: 'ua-ultra-resident-camera-only',
1526
+ lodSkipReason: '',
1527
+ lodDirtyReason: '',
1528
+ lodDirtyState: null,
1529
+ viewSyncBreakdown: null,
1530
+ shouldRunVisibilityCulling: false,
1531
+ visibilityStableSkip: true,
1532
+ visibilitySkipReason: 'ua-ultra-resident-camera-only',
1533
+ incrementalPanVisibility: false,
1534
+ shouldSyncCss3dTransforms: false,
1535
+ shouldRenderCss3d: false,
1536
+ usedCss3dCameraFastPath: canRenderCss3dCamera === true,
1537
+ uaUltraResidentCameraOnly: true,
1538
+ shouldSyncTextOverlays: false,
1539
+ usedPassiveOverlayCameraFastPath: false,
1540
+ passiveOverlaySuppressed: module._suppressPassiveDomOverlayDuringZoom === true,
1541
+ webgl: module._lastWebglRenderInfo || null,
1542
+ sections: sectionTimes
1543
+ };
1544
+ updateFramePerfPeakProfile(module, module._lastFramePerfProfile, frameEnd);
1545
+ } else if (module._lastFramePerfProfile) {
1546
+ module._lastFramePerfProfile = null;
1547
+ }
1548
+
1549
+ module._lastAnimationLoopDelayMs = 0;
1550
+ module._animationLoopExecuting = false;
1551
+ module._scheduleAnimationLoop(loop, 0);
1552
+ return true;
1553
+ }
1554
+
892
1555
  function getRenderDebugMetricsSnapshot() {
893
1556
  const snapshotStart = (typeof performance !== 'undefined' && typeof performance.now === 'function')
894
1557
  ? performance.now()
@@ -901,6 +1564,7 @@ const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
901
1564
  lastFrameDurationMs: 0,
902
1565
  lastFramePerfProfile: null,
903
1566
  lastFramePerfPeakProfile: null,
1567
+ framePathStats: null,
904
1568
  updatedAt: 0,
905
1569
  visibleNodes: 0,
906
1570
  isZooming: false,
@@ -976,9 +1640,24 @@ const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
976
1640
  : (flags.enablePassiveOverlayForVisibleNodes !== false
977
1641
  ? 'all-visible passive'
978
1642
  : 'selected/editing only');
979
- const residentDirtyState = typeof moduleInstance.lodRenderer?.getResidentDirtyState === 'function'
1643
+ const shouldBuildResidentDirtySnapshot =
1644
+ flags.enableFramePerfProbe === true ||
1645
+ flags.enableNodeFrameDebug === true;
1646
+ const residentDirtyState = shouldBuildResidentDirtySnapshot === true &&
1647
+ typeof moduleInstance.lodRenderer?.getResidentDirtyState === 'function'
980
1648
  ? moduleInstance.lodRenderer.getResidentDirtyState(moduleInstance.camera, moduleInstance)
981
- : null;
1649
+ : (
1650
+ moduleInstance.lodRenderer
1651
+ ? {
1652
+ dirty: moduleInstance.lodRenderer.canUseUltraResidentCameraOnlyFrame?.(moduleInstance.camera, moduleInstance) !== true,
1653
+ approximate: true,
1654
+ lodBand: currentLodBand || '',
1655
+ currentLodBand,
1656
+ hasResidentInstances: moduleInstance.lodRenderer._hasResidentLodInstances?.() === true,
1657
+ detailedSnapshotSkipped: true
1658
+ }
1659
+ : null
1660
+ );
982
1661
 
983
1662
  const snapshotEnd = (typeof performance !== 'undefined' && typeof performance.now === 'function')
984
1663
  ? performance.now()
@@ -998,6 +1677,7 @@ const MINDMAP_CORE_BUILD_ID = '20260617-flow-run-hitfix-v586';
998
1677
  lastFrameDurationMs: Number(moduleInstance._lastFrameDurationMs || 0),
999
1678
  lastFramePerfProfile: moduleInstance._lastFramePerfProfile || null,
1000
1679
  lastFramePerfPeakProfile: moduleInstance._framePerfPeakProfile || null,
1680
+ framePathStats: moduleInstance._framePathStats || null,
1001
1681
  updatedAt: Number(moduleInstance._lastFrameMetricsAt || 0),
1002
1682
  visibleNodes: Number(moduleInstance._visibleIds?.size || moduleInstance._prevVisibleIds?.size || 0),
1003
1683
  isZooming: moduleInstance.isZooming === true,
@@ -2574,6 +3254,9 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
2574
3254
  this.lodUpdateQueue = []; // Initialize lodUpdateQueue
2575
3255
  this.isPanning = false;
2576
3256
  this._currentDpr = null;
3257
+ this._lastWebglRenderedCameraState = null;
3258
+ this._webglCanvasMotionTransformActive = false;
3259
+ this._lastWebglCanvasMotionTransformKey = '';
2577
3260
  this._lastViewportWidth = 0;
2578
3261
  this._lastViewportHeight = 0;
2579
3262
  this._lastViewportDpr = 0;
@@ -2611,6 +3294,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
2611
3294
  this._pendingViewStateSnapshot = null;
2612
3295
  this._pendingViewStateKey = null;
2613
3296
  this._lastPersistedViewStateKey = null;
3297
+ this._viewStatePersistNeedsFreshSnapshot = false;
2614
3298
  this._viewStatePersistDelayMs = 650;
2615
3299
  this._viewStatePersistScheduledAt = 0;
2616
3300
  this._postLoadVisualSyncToken = 0;
@@ -2710,6 +3394,13 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
2710
3394
  this._installForceUpdateWakeHook();
2711
3395
  this._forceUpdateFrames = 0;
2712
3396
  this._forceThemeAnimationFrame = false;
3397
+ this._webglCameraMotionDeferred = false;
3398
+ this._themeCameraMotionDeferred = false;
3399
+ this._css3dVisualDeferredForMotion = false;
3400
+ this._motionGridLayer = null;
3401
+ this._motionGridVisible = false;
3402
+ this._motionGridHideTimer = null;
3403
+ this._lastMotionGridKey = '';
2713
3404
  this._animationIdleTimerId = null;
2714
3405
  this._animationLoopCallback = null;
2715
3406
  this._animationLoopExecuting = false;
@@ -2749,6 +3440,195 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
2749
3440
  // ▼▼▼ [New] Node snap function - use Math.round to snap to nearest grid ▼▼▼
2750
3441
  snapNodeToGrid(v) { return Math.round(v / this.GRID_SIZE) * this.GRID_SIZE; }
2751
3442
 
3443
+ _isMotionGridThemeEnabled() {
3444
+ const themeName = String(this.currentThemeName || '').trim();
3445
+ return themeName === 'SpatialGrid2D' ||
3446
+ themeName === 'XYGrid' ||
3447
+ themeName === 'GridPlane' ||
3448
+ themeName === 'Space3D' ||
3449
+ themeName === 'SpiritFlow' ||
3450
+ themeName === 'SpinningBoxes';
3451
+ }
3452
+
3453
+ _ensureMotionGridLayer() {
3454
+ if (!this.container) {
3455
+ return null;
3456
+ }
3457
+ if (this._motionGridLayer?.isConnected) {
3458
+ return this._motionGridLayer;
3459
+ }
3460
+
3461
+ const layer = document.createElement('div');
3462
+ layer.className = 'mindcanvas-motion-grid-layer';
3463
+ layer.setAttribute('aria-hidden', 'true');
3464
+ layer.style.position = 'absolute';
3465
+ layer.style.inset = `-${CAMERA_MOTION_GRID_OVERSCAN_PX}px`;
3466
+ layer.style.zIndex = '0';
3467
+ layer.style.pointerEvents = 'none';
3468
+ layer.style.display = 'none';
3469
+ layer.style.opacity = '0';
3470
+ layer.style.backgroundRepeat = 'repeat';
3471
+ layer.style.contain = 'strict';
3472
+ layer.style.willChange = 'transform, opacity';
3473
+ layer.style.transform = 'translate3d(0, 0, 0)';
3474
+ layer.style.transition = 'none';
3475
+
3476
+ const cssElement = this.cssRenderer?.domElement;
3477
+ if (cssElement?.parentNode === this.container) {
3478
+ this.container.insertBefore(layer, cssElement);
3479
+ } else {
3480
+ this.container.appendChild(layer);
3481
+ }
3482
+
3483
+ this._motionGridLayer = layer;
3484
+ this._lastMotionGridKey = '';
3485
+ this._lastMotionGridTransformKey = '';
3486
+ return layer;
3487
+ }
3488
+
3489
+ _setMotionGridVisible(visible) {
3490
+ const layer = this._motionGridLayer;
3491
+ if (!layer) {
3492
+ return;
3493
+ }
3494
+
3495
+ if (visible === true) {
3496
+ if (this._motionGridHideTimer) {
3497
+ clearTimeout(this._motionGridHideTimer);
3498
+ this._motionGridHideTimer = null;
3499
+ }
3500
+ if (this._motionGridVisible === true) {
3501
+ if (layer.style.display !== 'block') {
3502
+ layer.style.display = 'block';
3503
+ }
3504
+ return;
3505
+ }
3506
+ this._motionGridVisible = true;
3507
+ if (layer.style.display !== 'block') {
3508
+ layer.style.display = 'block';
3509
+ }
3510
+ return;
3511
+ }
3512
+
3513
+ if (this._motionGridVisible !== true) {
3514
+ return;
3515
+ }
3516
+
3517
+ this._motionGridVisible = false;
3518
+ if (layer.style.opacity !== '0') {
3519
+ layer.style.opacity = '0';
3520
+ }
3521
+ if (this._motionGridHideTimer) {
3522
+ return;
3523
+ }
3524
+ this._motionGridHideTimer = setTimeout(() => {
3525
+ this._motionGridHideTimer = null;
3526
+ if (this._motionGridVisible !== true && layer.isConnected) {
3527
+ layer.style.display = 'none';
3528
+ }
3529
+ }, CAMERA_MOTION_GRID_HIDE_DELAY_MS);
3530
+ }
3531
+
3532
+ _syncMotionGridLayer(visible = true) {
3533
+ if (visible !== true) {
3534
+ this._setMotionGridVisible(false);
3535
+ return false;
3536
+ }
3537
+
3538
+ if (!this._isMotionGridThemeEnabled() || !this.container || !this.camera?.isPerspectiveCamera) {
3539
+ this._setMotionGridVisible(false);
3540
+ return false;
3541
+ }
3542
+
3543
+ const layer = this._ensureMotionGridLayer();
3544
+ if (!layer) {
3545
+ return false;
3546
+ }
3547
+
3548
+ const viewportMetrics = getCachedViewportMetrics(this);
3549
+ const width = Math.max(1, Number(viewportMetrics.width || 0));
3550
+ const height = Math.max(1, Number(viewportMetrics.height || 0));
3551
+ const camera = this.camera;
3552
+ const rawCameraZ = Math.max(1, Number(camera.position?.z || 1));
3553
+ const isCameraNavigationFrame =
3554
+ this.isPanning === true ||
3555
+ this.isZooming === true ||
3556
+ this._webglCameraMotionDeferred === true;
3557
+ const cameraZ = isCameraNavigationFrame
3558
+ ? Math.max(1, Math.round(rawCameraZ / CAMERA_MOTION_GRID_Z_BUCKET_WORLD) * CAMERA_MOTION_GRID_Z_BUCKET_WORLD)
3559
+ : rawCameraZ;
3560
+ const vfov = (Number(camera.fov || 45) * Math.PI) / 180;
3561
+ const viewHeight = 2 * Math.tan(vfov / 2) * cameraZ;
3562
+ const pxPerWorld = height / Math.max(1, viewHeight);
3563
+ const worldGrid = Math.max(1, Number(this.GRID_SIZE || 10));
3564
+ const smallStepPx = Math.max(
3565
+ CAMERA_MOTION_GRID_MIN_STEP_PX,
3566
+ Math.min(CAMERA_MOTION_GRID_MAX_STEP_PX, worldGrid * pxPerWorld)
3567
+ );
3568
+ const majorStepPx = smallStepPx * 10;
3569
+ const cameraX = Number(camera.position?.x || 0);
3570
+ const cameraY = Number(camera.position?.y || 0);
3571
+ const themeName = String(this.currentThemeName || '').trim();
3572
+ const isParticleMotionTheme =
3573
+ themeName === 'Space3D' ||
3574
+ themeName === 'SpiritFlow' ||
3575
+ themeName === 'SpinningBoxes';
3576
+ const originX = width / 2 - cameraX * pxPerWorld;
3577
+ const originY = height / 2 + cameraY * pxPerWorld;
3578
+ const positiveModulo = (value, size) => ((value % size) + size) % size;
3579
+ const translateX = positiveModulo(originX + CAMERA_MOTION_GRID_OVERSCAN_PX, majorStepPx);
3580
+ const translateY = positiveModulo(originY + CAMERA_MOTION_GRID_OVERSCAN_PX, majorStepPx);
3581
+ const opacity = isParticleMotionTheme
3582
+ ? 0.96
3583
+ : (cameraZ <= 1600
3584
+ ? 1
3585
+ : Math.max(0.38, 1 - ((cameraZ - 1600) / 12000)));
3586
+ const backgroundColor = themeName === 'GridPlane' || themeName === 'XYGrid'
3587
+ ? '#e8edf3'
3588
+ : '#e3e7ee';
3589
+ const motionGridKey = [
3590
+ width,
3591
+ height,
3592
+ themeName,
3593
+ Math.round(cameraZ),
3594
+ Math.round(smallStepPx * 100),
3595
+ Math.round(majorStepPx * 100),
3596
+ Math.round(opacity * 100)
3597
+ ].join('|');
3598
+
3599
+ if (motionGridKey !== this._lastMotionGridKey) {
3600
+ this._lastMotionGridKey = motionGridKey;
3601
+ layer.style.backgroundColor = backgroundColor;
3602
+ layer.style.backgroundImage = [
3603
+ 'radial-gradient(circle at 0 0, rgba(94, 103, 115, 0.34) 1px, transparent 1.45px)',
3604
+ 'radial-gradient(circle at 0 0, rgba(77, 86, 99, 0.52) 1.8px, transparent 2.2px)'
3605
+ ].join(', ');
3606
+ layer.style.backgroundSize = `${smallStepPx.toFixed(3)}px ${smallStepPx.toFixed(3)}px, ${majorStepPx.toFixed(3)}px ${majorStepPx.toFixed(3)}px`;
3607
+ layer.style.backgroundPosition = '0 0, 0 0';
3608
+ layer.style.opacity = opacity.toFixed(3);
3609
+ }
3610
+ const transformKey = `${Math.round(translateX * 100)}|${Math.round(translateY * 100)}`;
3611
+ if (transformKey !== this._lastMotionGridTransformKey) {
3612
+ this._lastMotionGridTransformKey = transformKey;
3613
+ layer.style.transform = `translate3d(${translateX.toFixed(3)}px, ${translateY.toFixed(3)}px, 0)`;
3614
+ }
3615
+
3616
+ this._setMotionGridVisible(opacity > 0.01);
3617
+ return opacity > 0.01;
3618
+ }
3619
+
3620
+ _syncMotionGridLayerForCameraNavigation(usedWebglCanvasCameraTransform = false) {
3621
+ if (usedWebglCanvasCameraTransform === true ||
3622
+ CAMERA_MOTION_GRID_FALLBACK_ENABLED !== true) {
3623
+ if (this._motionGridVisible === true || this._motionGridLayer) {
3624
+ this._syncMotionGridLayer(false);
3625
+ }
3626
+ return false;
3627
+ }
3628
+
3629
+ return this._syncMotionGridLayer(true);
3630
+ }
3631
+
2752
3632
  _installForceUpdateWakeHook() {
2753
3633
  if (Object.getOwnPropertyDescriptor(this, '_forceUpdateFrames')?.get) {
2754
3634
  return;
@@ -3178,9 +4058,12 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3178
4058
  console.error('[mind-map-core.js] Container not found!');
3179
4059
  return;
3180
4060
  }
3181
- this.container.style.display = 'block';
3182
- this._lastContainerWidth = this.container.clientWidth || 0;
3183
- this._lastContainerHeight = this.container.clientHeight || 0;
4061
+ this.container.style.display = 'block';
4062
+ this._lastContainerWidth = this.container.clientWidth || 0;
4063
+ this._lastContainerHeight = this.container.clientHeight || 0;
4064
+ this._lastViewportWidth = this._lastContainerWidth;
4065
+ this._lastViewportHeight = this._lastContainerHeight;
4066
+ this._lastViewportDpr = Math.max(1, Number(window.devicePixelRatio || 1));
3184
4067
  const THREE = globalThis.THREE;
3185
4068
  this.scene = new THREE.Scene();
3186
4069
  this.cssScene = new THREE.Scene();
@@ -3416,10 +4299,11 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3416
4299
  const newTheme = window.MindMapThemes.getTheme(themeName);
3417
4300
  if (!newTheme) { console.warn(`[mind-map-core.js] Theme '${themeName}' not found.`); return; }
3418
4301
  if (this.currentThemeObject) {
3419
- const oldTheme = window.MindMapThemes.getTheme(this.currentThemeName);
3420
- try { oldTheme?.dispose(this.currentThemeObject); } catch { }
3421
- if (this.scene) this.scene.remove(this.currentThemeObject);
3422
- }
4302
+ const oldTheme = window.MindMapThemes.getTheme(this.currentThemeName);
4303
+ try { oldTheme?.dispose(this.currentThemeObject); } catch { }
4304
+ if (this.scene) this.scene.remove(this.currentThemeObject);
4305
+ }
4306
+ this._syncMotionGridLayer(false);
3423
4307
  this.currentThemeObject = newTheme.create(this.scene, this.renderer);
3424
4308
  if (this.currentThemeObject) this.scene.add(this.currentThemeObject);
3425
4309
  this.currentThemeName = themeName;
@@ -3483,6 +4367,76 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3483
4367
  this.renderer.setSize(width, height, false);
3484
4368
  }
3485
4369
 
4370
+ _recordWebglRenderedCameraState() {
4371
+ if (!this.camera) {
4372
+ return;
4373
+ }
4374
+
4375
+ const viewportMetrics = getCachedViewportMetrics(this);
4376
+ this._lastWebglRenderedCameraState = {
4377
+ x: Number(this.camera.position?.x || 0),
4378
+ y: Number(this.camera.position?.y || 0),
4379
+ z: Math.max(1, Number(this.camera.position?.z || 1)),
4380
+ width: Math.max(1, Number(viewportMetrics.width || 1)),
4381
+ height: Math.max(1, Number(viewportMetrics.height || 1)),
4382
+ fov: Number(this.camera.fov || 45)
4383
+ };
4384
+ }
4385
+
4386
+ _clearWebglCanvasCameraTransform() {
4387
+ const element = this.renderer?.domElement;
4388
+ if (!element || this._webglCanvasMotionTransformActive !== true) {
4389
+ return false;
4390
+ }
4391
+
4392
+ element.style.transform = '';
4393
+ element.style.transformOrigin = '';
4394
+ element.style.willChange = '';
4395
+ this._webglCanvasMotionTransformActive = false;
4396
+ this._lastWebglCanvasMotionTransformKey = '';
4397
+ return true;
4398
+ }
4399
+
4400
+ _applyWebglCanvasCameraTransform() {
4401
+ const element = this.renderer?.domElement;
4402
+ const camera = this.camera;
4403
+ const last = this._lastWebglRenderedCameraState;
4404
+ if (!element || !camera || !last) {
4405
+ return false;
4406
+ }
4407
+
4408
+ const viewportMetrics = getCachedViewportMetrics(this);
4409
+ const width = Math.max(1, Number(viewportMetrics.width || last.width || 1));
4410
+ const height = Math.max(1, Number(viewportMetrics.height || last.height || 1));
4411
+ const currentZ = Math.max(1, Number(camera.position?.z || 1));
4412
+ const lastZ = Math.max(1, Number(last.z || currentZ));
4413
+ const vfov = (Number(camera.fov || last.fov || 45) * Math.PI) / 180;
4414
+ const viewHeight = 2 * Math.tan(vfov / 2) * currentZ;
4415
+ const pxPerWorld = height / Math.max(1, viewHeight);
4416
+ const scale = Math.max(0.05, Math.min(20, lastZ / currentZ));
4417
+ const currentX = Number(camera.position?.x || 0);
4418
+ const currentY = Number(camera.position?.y || 0);
4419
+ const translateX = (Number(last.x || 0) - currentX) * pxPerWorld;
4420
+ const translateY = (currentY - Number(last.y || 0)) * pxPerWorld;
4421
+ const transformKey = [
4422
+ Math.round(width),
4423
+ Math.round(height),
4424
+ Math.round(translateX * 100),
4425
+ Math.round(translateY * 100),
4426
+ Math.round(scale * 10000)
4427
+ ].join('|');
4428
+
4429
+ if (transformKey !== this._lastWebglCanvasMotionTransformKey) {
4430
+ this._lastWebglCanvasMotionTransformKey = transformKey;
4431
+ element.style.transformOrigin = '50% 50%';
4432
+ element.style.willChange = 'transform';
4433
+ element.style.transform = `translate3d(${translateX.toFixed(3)}px, ${translateY.toFixed(3)}px, 0) scale(${scale.toFixed(5)})`;
4434
+ }
4435
+
4436
+ this._webglCanvasMotionTransformActive = true;
4437
+ return true;
4438
+ }
4439
+
3486
4440
  refreshAgentConsoleVisibilityTracking(force = false) {
3487
4441
  if (!force && this._agentConsoleVisibilityTrackingDirty !== true) {
3488
4442
  return;
@@ -4457,6 +5411,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4457
5411
  if (this.isZooming && !isCameraMoving && timeSinceLastInput > ZOOM_END_DELAY_MS) {
4458
5412
  this.isZooming = false;
4459
5413
  document.body.classList.remove('is-zooming');
5414
+ this.cssRenderer?.domElement?.classList?.remove?.('is-zooming');
4460
5415
  if (FRAME_PERF_DEBUG) {
4461
5416
  console.log(`[Perf] Zoom ended (${timeSinceLastInput.toFixed(0)}ms since last input)`);
4462
5417
  }
@@ -4466,10 +5421,12 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4466
5421
  ? requestIdleCallback : (cb) => setTimeout(cb, 0);
4467
5422
  scheduleWillChangeOff(() => {
4468
5423
  // 패닝/줌이 다시 시작됐으면 건너뜀
4469
- if (!moduleRef.isZooming && !moduleRef.isPanning) {
4470
- if (window.MindMapCss3DManager?.setWillChange) {
4471
- window.MindMapCss3DManager.setWillChange(false, moduleRef);
4472
- }
5424
+ if (!moduleRef.isZooming &&
5425
+ !moduleRef.isPanning &&
5426
+ moduleRef.renderDebugFlags?.enableCss3dWillChange === true) {
5427
+ if (window.MindMapCss3DManager?.setWillChange) {
5428
+ window.MindMapCss3DManager.setWillChange(false, moduleRef);
5429
+ }
4473
5430
  }
4474
5431
  });
4475
5432
  // ▲▲▲ [Fix] ▲▲▲
@@ -4478,8 +5435,12 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4478
5435
  _profileSection('state');
4479
5436
 
4480
5437
  if (isCameraMoving !== this.prevIsCameraMoving) {
4481
- // Update will-change state
4482
- if (!this.isZooming && !this.isPanning && window.MindMapCss3DManager?.setWillChange) {
5438
+ const shouldSyncCss3dWillChangeForCameraMotion =
5439
+ this.renderDebugFlags?.enableCss3dWillChange === true;
5440
+ if (!this.isZooming &&
5441
+ !this.isPanning &&
5442
+ shouldSyncCss3dWillChangeForCameraMotion &&
5443
+ window.MindMapCss3DManager?.setWillChange) {
4483
5444
  window.MindMapCss3DManager.setWillChange(isCameraMoving, this);
4484
5445
  }
4485
5446
 
@@ -4530,6 +5491,19 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4530
5491
  this._overlayDirty = true;
4531
5492
  }
4532
5493
 
5494
+ if (this._webglCameraMotionDeferred === true ||
5495
+ this._themeCameraMotionDeferred === true ||
5496
+ this._css3dVisualDeferredForMotion === true) {
5497
+ this._forceUpdateFrames = Math.max(this._forceUpdateFrames || 0, 2);
5498
+ this._forceThemeAnimationFrame = true;
5499
+ if (this._css3dVisualDeferredForMotion === true) {
5500
+ this._css3dVisualChangedSinceLastRender = true;
5501
+ }
5502
+ this._webglCameraMotionDeferred = false;
5503
+ this._themeCameraMotionDeferred = false;
5504
+ this._css3dVisualDeferredForMotion = false;
5505
+ }
5506
+
4533
5507
  if (this.lodRenderer?.hasPendingLineDetailRestore?.(this.camera, this) === true) {
4534
5508
  this._forceUpdateFrames = Math.max(this._forceUpdateFrames || 0, 6);
4535
5509
  }
@@ -4557,8 +5531,51 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4557
5531
  // 3. Unified Visibility & LOD Pass (One Loop to Rule Them All)
4558
5532
  // ----------------------------------------------------------------
4559
5533
  this.frameCount = (this.frameCount || 0) + 1;
4560
- const hasPendingNearCssWarmup = this.useLODRendering &&
4561
- this.lodRenderer?.hasPendingNearCssWarmup?.(this) === true;
5534
+ if (tryRunUltraEarlyCameraOnlyFrame(
5535
+ this,
5536
+ loop,
5537
+ frameStart,
5538
+ isCameraMoving,
5539
+ shouldCaptureFramePerf,
5540
+ _frameProfTimes,
5541
+ _profileSection
5542
+ ) === true) {
5543
+ return;
5544
+ }
5545
+ if (tryRunUltraEarlyResidentCameraOnlyFrame(
5546
+ this,
5547
+ loop,
5548
+ frameStart,
5549
+ isCameraMoving,
5550
+ shouldCaptureFramePerf,
5551
+ _frameProfTimes,
5552
+ _profileSection
5553
+ ) === true) {
5554
+ return;
5555
+ }
5556
+
5557
+ const warmupProbeForceUpdateFrames = Math.max(0, Number(this._forceUpdateFrames || 0));
5558
+ const warmupProbeCameraNavigationForceFrames = Math.max(0, Number(this._cameraNavigationForceFrames || 0));
5559
+ const warmupProbePanLiveRefreshForceFrames = Math.max(0, Number(this._panLiveRefreshForceFrames || 0));
5560
+ const warmupProbeCameraWakeFrames = Math.max(
5561
+ warmupProbeCameraNavigationForceFrames,
5562
+ warmupProbePanLiveRefreshForceFrames
5563
+ );
5564
+ const shouldDeferMotionWarmupAndResidentChecks =
5565
+ (this.isPanning === true || this.isZooming === true || isCameraMoving === true) &&
5566
+ this.isDraggingNode !== true &&
5567
+ this.isDraggingMultipleNodes !== true &&
5568
+ this.isResizing !== true &&
5569
+ this.isWindowResizing !== true &&
5570
+ this.isLoading !== true &&
5571
+ (
5572
+ warmupProbeForceUpdateFrames <= 0 ||
5573
+ warmupProbeCameraWakeFrames >= warmupProbeForceUpdateFrames
5574
+ );
5575
+ const hasPendingNearCssWarmup = shouldDeferMotionWarmupAndResidentChecks === true
5576
+ ? false
5577
+ : this.useLODRendering &&
5578
+ this.lodRenderer?.hasPendingNearCssWarmup?.(this) === true;
4562
5579
  const isResidentMidFarLodFrame = this.useLODRendering &&
4563
5580
  this.lodRenderer?.usesResidentMidFarInstances?.(this.camera) === true;
4564
5581
  const isFullResMode = this.lodRenderer?.isInLODMode !== true;
@@ -4612,9 +5629,15 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4612
5629
  hasForcedUpdate &&
4613
5630
  hasOnlyCameraNavigationForcedUpdate !== true;
4614
5631
  const isLodModeActive = this.lodRenderer?.isInLODMode === true;
5632
+ if (shouldDeferMotionWarmupAndResidentChecks === true && isResidentMidFarLodFrame === true) {
5633
+ this._wasLodMotionFrozenLastFrame = true;
5634
+ this._preserveVisibleSetThisFrame = true;
5635
+ }
4615
5636
  const residentDirty = isResidentMidFarLodFrame &&
5637
+ shouldDeferMotionWarmupAndResidentChecks !== true &&
4616
5638
  this.lodRenderer?.hasResidentDirtyWork?.(this.camera, this) === true;
4617
5639
  const residentBandChanging = isResidentMidFarLodFrame &&
5640
+ shouldDeferMotionWarmupAndResidentChecks !== true &&
4618
5641
  this.lodRenderer?.willLodBandChange?.(this.camera) === true;
4619
5642
  let canDeferResidentDirtyIdleFrame = false;
4620
5643
  if (isResidentMidFarLodFrame &&
@@ -4639,13 +5662,21 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4639
5662
  !isNodeInteracting &&
4640
5663
  !hasNonPanForcedUpdate &&
4641
5664
  !hasPendingNearCssWarmup;
4642
- const movingUpdateDue = isCameraMoving && (
4643
- !isLodModeActive ||
4644
- (
4645
- !residentMidFarStable &&
4646
- (this.frameCount % (this.isZooming ? 12 : 8) === 0)
4647
- )
4648
- );
5665
+ const shouldPreferCameraOnlyMotionPipeline =
5666
+ isCameraMoving === true &&
5667
+ !isNodeInteracting &&
5668
+ !hasNonCameraNavigationForcedUpdate &&
5669
+ !hasPendingNearCssWarmup &&
5670
+ !this.isWindowResizing;
5671
+ const movingUpdateDue = shouldPreferCameraOnlyMotionPipeline === true
5672
+ ? false
5673
+ : isCameraMoving && (
5674
+ !isLodModeActive ||
5675
+ (
5676
+ !residentMidFarStable &&
5677
+ (this.frameCount % (this.isZooming ? 12 : 8) === 0)
5678
+ )
5679
+ );
4649
5680
  const idleUpdateDue = false;
4650
5681
  const isLodPanVisibilityRefreshDue =
4651
5682
  !isResidentMidFarLodFrame &&
@@ -4680,31 +5711,608 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4680
5711
  }
4681
5712
  const isInteractiveFrame = this.isZooming || this.isPanning || isCameraMoving || isNodeInteracting || !!this.isWindowResizing;
4682
5713
  const useTextOverlayV2 = !isBoardLoading && this.useTextOverlayV2 === true && !!window.MindMapTextOverlayV2;
5714
+ const isPassiveDomOverlayRuntimeEnabled =
5715
+ useTextOverlayV2 &&
5716
+ window.MindMapTextOverlayV2?.isPassiveDomOverlayEnabled?.() === true;
4683
5717
  const renderDebugFlags = activeRenderDebugFlags;
4684
5718
  const isCss3dEnabled = !isBoardLoading && renderDebugFlags.enableCss3d !== false;
4685
- const isCss3dTransformSyncEnabled = isCss3dEnabled && renderDebugFlags.enableCss3dTransformSync !== false;
4686
- const isVisibilityCullingEnabled = renderDebugFlags.enableVisibilityCulling !== false;
4687
- const isLodUpdateEnabled = renderDebugFlags.enableLodUpdate !== false;
4688
- const isWebglRenderEnabled = !isBoardLoading && renderDebugFlags.enableWebglRender !== false;
5719
+
5720
+ const hasSelectableTextOverlayFocus = !!(
5721
+ String(this._textOverlayV2State?.selectionNodeId || '').trim() ||
5722
+ String(this._selectableTextOverlayNodeId || '').trim()
5723
+ );
5724
+ const hasEditingTextOverlayFocus = !!(
5725
+ String(this._textOverlayV2State?.editing?.nodeId || '').trim() ||
5726
+ String(this._editingOverlayState?.nodeId || '').trim()
5727
+ );
5728
+ const hasPendingCss3dStructuralTransformWork = !!(
5729
+ (this._cssTransformDirtyIds?.size || 0) > 0 ||
5730
+ this._cssParentRepairNeeded === true
5731
+ );
5732
+ const hasPendingCss3dVisualWork = !!(
5733
+ this._css3dVisualChangedThisFrame === true ||
5734
+ this._css3dVisualChangedSinceLastRender === true
5735
+ );
5736
+ const canUseUaImmediateCameraOnlyFrame = CAMERA_MOTION_VISUAL_ONLY_FAST_PATH_ENABLED === true && !!(
5737
+ shouldPreferCameraOnlyMotionPipeline === true &&
5738
+ isCss3dEnabled === true &&
5739
+ !!this.cssRenderer &&
5740
+ typeof this.cssRenderer.renderCamera === 'function' &&
5741
+ !!this.renderer &&
5742
+ !!this.scene &&
5743
+ !!this.camera &&
5744
+ isLodModeActive !== true &&
5745
+ isCameraMoving === true &&
5746
+ this.isSelecting !== true &&
5747
+ !hasEditingTextOverlayFocus &&
5748
+ !hasPendingCss3dStructuralTransformWork &&
5749
+ !isBoardLoading
5750
+ );
5751
+ if (canUseUaImmediateCameraOnlyFrame) {
5752
+ this._fullResMotionFrozen = true;
5753
+ this._wasViewStateMotionFrozenLastFrame = true;
5754
+ this._wasLodMotionFrozenLastFrame = false;
5755
+ this._fullResVisibilityReconcilePending = true;
5756
+ this._preserveVisibleSetThisFrame = true;
5757
+ this._lastVisibilityCullingSkipReason = 'ua-visual-only-motion';
5758
+ if (this._overlayDirty === true) {
5759
+ this._deferPassiveOverlayRefresh = true;
5760
+ }
5761
+ if (hasSelectableTextOverlayFocus) {
5762
+ this._deferPassiveOverlayRefresh = true;
5763
+ }
5764
+ if (hasPendingCss3dVisualWork) {
5765
+ this._css3dVisualDeferredForMotion = true;
5766
+ }
5767
+
5768
+ const needsImmediatePerfClassSync = shouldSyncMindCanvasPerfClasses();
5769
+ const approxVisibleNodeCountImmediate =
5770
+ needsImmediatePerfClassSync || shouldCaptureFramePerf
5771
+ ? Number(this._visibleIds?.size || this._prevVisibleIds?.size || 0)
5772
+ : 0;
5773
+ if (needsImmediatePerfClassSync) {
5774
+ const totalNodeCountImmediate = Number(this.nodeObjectsById?.size || 0);
5775
+ syncMindCanvasPerfClasses({
5776
+ moving: true,
5777
+ dense:
5778
+ approxVisibleNodeCountImmediate >= DENSE_VISIBLE_NODE_THRESHOLD ||
5779
+ totalNodeCountImmediate >= DENSE_TOTAL_NODE_THRESHOLD
5780
+ });
5781
+ }
5782
+
5783
+ updateCursorDomElement(this);
5784
+ const canReuseWebglCanvasForImmediateMotion = !!(
5785
+ CAMERA_MOTION_WEBGL_CANVAS_TRANSFORM_ENABLED === true &&
5786
+ renderDebugFlags.enableWebglRender !== false &&
5787
+ this._lastWebglRenderedCameraState &&
5788
+ this.renderer?.domElement
5789
+ );
5790
+ if (canReuseWebglCanvasForImmediateMotion === true) {
5791
+ deferContinuousThemeForCameraMotion(this);
5792
+ }
5793
+ if (!canReuseWebglCanvasForImmediateMotion &&
5794
+ renderDebugFlags.enableThemeAnimation !== false &&
5795
+ this.currentThemeObject &&
5796
+ typeof this.currentThemeObject.animate === 'function') {
5797
+ const themeRunner = window.MindMapThemes?.runAnimationIfNeeded;
5798
+ if (typeof themeRunner === 'function') {
5799
+ themeRunner(this.currentThemeName, this.currentThemeObject, this.camera, {
5800
+ force: this._forceThemeAnimationFrame === true,
5801
+ isCameraMoving: true,
5802
+ isPanning: this.isPanning === true,
5803
+ isZooming: this.isZooming === true,
5804
+ isResizing: this.isWindowResizing === true,
5805
+ isInteractiveFrame: true
5806
+ });
5807
+ } else {
5808
+ this.currentThemeObject.animate(this.camera);
5809
+ }
5810
+ this._forceThemeAnimationFrame = false;
5811
+ }
5812
+ let usedWebglCanvasCameraTransformImmediate = false;
5813
+ if (renderDebugFlags.enableWebglRender !== false) {
5814
+ if (canReuseWebglCanvasForImmediateMotion === true &&
5815
+ this._applyWebglCanvasCameraTransform() === true) {
5816
+ usedWebglCanvasCameraTransformImmediate = true;
5817
+ this._webglCameraMotionDeferred = true;
5818
+ this._lastWebglRenderInfo = {
5819
+ calls: 0,
5820
+ triangles: 0,
5821
+ points: 0,
5822
+ lines: 0,
5823
+ skipped: true,
5824
+ webglCanvasCameraTransform: true,
5825
+ uaVisualOnlyMotion: true
5826
+ };
5827
+ } else {
5828
+ this._clearWebglCanvasCameraTransform();
5829
+ this.renderer.render(this.scene, this.camera);
5830
+ this._recordWebglRenderedCameraState();
5831
+ const renderInfo = this.renderer?.info?.render || null;
5832
+ this._lastWebglRenderInfo = renderInfo ? {
5833
+ calls: Number(renderInfo.calls || 0),
5834
+ triangles: Number(renderInfo.triangles || 0),
5835
+ points: Number(renderInfo.points || 0),
5836
+ lines: Number(renderInfo.lines || 0),
5837
+ uaVisualOnlyMotion: true,
5838
+ webglCanvasCameraBootstrap: true
5839
+ } : null;
5840
+ }
5841
+ }
5842
+ this._syncMotionGridLayerForCameraNavigation(usedWebglCanvasCameraTransformImmediate);
5843
+
5844
+ this.cssRenderer.renderCamera(this.camera);
5845
+ const shouldSyncPassiveOverlayTransformDuringMotion =
5846
+ window.MindMapTextOverlayV2?.isPassiveDomOverlayEnabled?.() === true &&
5847
+ this.useTextOverlayV2 === true &&
5848
+ this.renderDebugFlags?.enableDomOverlay !== false;
5849
+ if (shouldSyncPassiveOverlayTransformDuringMotion &&
5850
+ window.MindMapTextOverlayV2?.syncPassiveViewTransform) {
5851
+ window.MindMapTextOverlayV2.syncPassiveViewTransform(this);
5852
+ }
5853
+ if ((this._businessAutomationEdgeVisualLayer?.isConnected ||
5854
+ this._businessAutomationEdgeLayer?.isConnected) &&
5855
+ window.MindMapCss3DManager?.renderBusinessAutomationEdges) {
5856
+ const hasBusinessAutomationConnectionDraft =
5857
+ window.MindMapCss3DManager?.hasBusinessAutomationConnectionDraft?.() === true;
5858
+ const transformedBusinessAutomationEdges =
5859
+ window.MindMapCss3DManager.syncBusinessAutomationEdgesForCameraMotion?.(this) === true;
5860
+ if (transformedBusinessAutomationEdges) {
5861
+ this._businessAutomationEdgesDeferredForMotion = true;
5862
+ } else if (hasBusinessAutomationConnectionDraft) {
5863
+ window.MindMapCss3DManager.renderBusinessAutomationEdges(this);
5864
+ this._businessAutomationEdgesDeferredForMotion = false;
5865
+ } else {
5866
+ this._businessAutomationEdgesDeferredForMotion = true;
5867
+ }
5868
+ }
5869
+ _profileSection('uaVisualOnlyMotion');
5870
+
5871
+ const frameEnd = performance.now();
5872
+ const frameDuration = frameEnd - frameStart;
5873
+ this._lastFrameDurationMs = frameDuration;
5874
+ this._lastFrameMetricsAt = frameEnd;
5875
+ if (shouldCaptureFramePerf) {
5876
+ const sectionTimes = {};
5877
+ Object.entries(_frameProfTimes)
5878
+ .filter(([key, value]) => key !== 'visibleNodes' && typeof value === 'number')
5879
+ .forEach(([key, value]) => {
5880
+ sectionTimes[key] = Number(value.toFixed(2));
5881
+ });
5882
+ this._lastFramePerfProfile = {
5883
+ durationMs: Number(frameDuration.toFixed(2)),
5884
+ update: false,
5885
+ cameraZ: Number((this.camera?.position?.z || 0).toFixed(2)),
5886
+ isZooming: this.isZooming === true,
5887
+ isPanning: this.isPanning === true,
5888
+ isCameraMoving: true,
5889
+ isInLodMode: false,
5890
+ lodBand: this.lodRenderer?._currentLodBand || null,
5891
+ visibleNodes: approxVisibleNodeCountImmediate,
5892
+ freezeVisibility: true,
5893
+ freezeLod: false,
5894
+ freezeFullRes: true,
5895
+ residentMidFarStable: false,
5896
+ residentDirty: false,
5897
+ residentBandChanging: false,
5898
+ viewSyncMode: 'ua-visual-only-motion',
5899
+ lodUpdateCalled: false,
5900
+ lodUpdateSkipped: true,
5901
+ lodUpdateSkipReason: 'ua-visual-only-motion',
5902
+ lodSkipReason: '',
5903
+ lodDirtyReason: '',
5904
+ lodDirtyState: null,
5905
+ viewSyncBreakdown: null,
5906
+ shouldRunVisibilityCulling: false,
5907
+ visibilityStableSkip: true,
5908
+ visibilitySkipReason: 'ua-visual-only-motion',
5909
+ incrementalPanVisibility: false,
5910
+ shouldSyncCss3dTransforms: false,
5911
+ shouldRenderCss3d: false,
5912
+ usedCss3dCameraFastPath: true,
5913
+ uaVisualOnlyMotion: true,
5914
+ shouldSyncTextOverlays: false,
5915
+ usedPassiveOverlayCameraFastPath: false,
5916
+ passiveOverlaySuppressed: this._suppressPassiveDomOverlayDuringZoom === true,
5917
+ webgl: this._lastWebglRenderInfo || null,
5918
+ sections: sectionTimes
5919
+ };
5920
+ updateFramePerfPeakProfile(this, this._lastFramePerfProfile, frameEnd);
5921
+ } else if (this._lastFramePerfProfile) {
5922
+ this._lastFramePerfProfile = null;
5923
+ }
5924
+
5925
+ this._lastAnimationLoopDelayMs = 0;
5926
+ this._animationLoopExecuting = false;
5927
+ this._scheduleAnimationLoop(loop, 0);
5928
+ return;
5929
+ }
5930
+
5931
+ const isCss3dTransformSyncEnabled = isCss3dEnabled && renderDebugFlags.enableCss3dTransformSync !== false;
5932
+ const isVisibilityCullingEnabled = renderDebugFlags.enableVisibilityCulling !== false;
5933
+ const isLodUpdateEnabled = renderDebugFlags.enableLodUpdate !== false;
5934
+ const isWebglRenderEnabled = !isBoardLoading && renderDebugFlags.enableWebglRender !== false;
4689
5935
  const isThemeAnimationEnabled = !isBoardLoading && renderDebugFlags.enableThemeAnimation !== false;
4690
- const themeAnimationMode =
4691
- window.MindMapThemes?.getAnimationMode?.(this.currentThemeName, this.currentThemeObject) ||
4692
- 'none';
4693
- const isContinuousTheme = themeAnimationMode === 'continuous';
4694
5936
  const isCameraNavigationOnlyInteractiveFrame =
4695
5937
  (this.isPanning === true || this.isZooming === true || isCameraMoving === true) &&
4696
5938
  !isNodeInteracting &&
4697
5939
  !this.isWindowResizing &&
4698
5940
  !hasNonCameraNavigationForcedUpdate;
5941
+ const canUseUaStationaryCameraNavigationAnyModeFrame = CAMERA_MOTION_VISUAL_ONLY_FAST_PATH_ENABLED === true && !!(
5942
+ isCameraNavigationOnlyInteractiveFrame === true &&
5943
+ isCameraMoving !== true &&
5944
+ (this.isPanning === true || this.isZooming === true) &&
5945
+ shouldUpdate !== true &&
5946
+ this.isSelecting !== true &&
5947
+ !hasEditingTextOverlayFocus &&
5948
+ !hasPendingCss3dStructuralTransformWork &&
5949
+ !hasPendingCss3dVisualWork &&
5950
+ !hasPendingNearCssWarmup &&
5951
+ !isBoardLoading &&
5952
+ this.isWindowResizing !== true &&
5953
+ (isWebglRenderEnabled !== true || !!this._lastWebglRenderedCameraState)
5954
+ );
5955
+ if (canUseUaStationaryCameraNavigationAnyModeFrame) {
5956
+ this._preserveVisibleSetThisFrame = true;
5957
+ this._lastVisibilityCullingSkipReason = 'ua-stationary-visual-motion';
5958
+ if (this._overlayDirty === true || hasSelectableTextOverlayFocus) {
5959
+ this._deferPassiveOverlayRefresh = true;
5960
+ }
5961
+
5962
+ updateCursorDomElement(this);
5963
+ const canReuseWebglCanvasForStationaryMotion = !!(
5964
+ CAMERA_MOTION_WEBGL_CANVAS_TRANSFORM_ENABLED === true &&
5965
+ isWebglRenderEnabled &&
5966
+ this._lastWebglRenderedCameraState &&
5967
+ this.renderer?.domElement
5968
+ );
5969
+ if (canReuseWebglCanvasForStationaryMotion === true) {
5970
+ deferContinuousThemeForCameraMotion(this);
5971
+ }
5972
+ if (!canReuseWebglCanvasForStationaryMotion &&
5973
+ renderDebugFlags.enableThemeAnimation !== false &&
5974
+ this.currentThemeObject &&
5975
+ typeof this.currentThemeObject.animate === 'function') {
5976
+ const themeRunner = window.MindMapThemes?.runAnimationIfNeeded;
5977
+ if (typeof themeRunner === 'function') {
5978
+ themeRunner(this.currentThemeName, this.currentThemeObject, this.camera, {
5979
+ force: this._forceThemeAnimationFrame === true,
5980
+ isCameraMoving: false,
5981
+ isPanning: this.isPanning === true,
5982
+ isZooming: this.isZooming === true,
5983
+ isResizing: this.isWindowResizing === true,
5984
+ isInteractiveFrame: true
5985
+ });
5986
+ } else {
5987
+ this.currentThemeObject.animate(this.camera);
5988
+ }
5989
+ this._forceThemeAnimationFrame = false;
5990
+ }
5991
+ let usedWebglCanvasCameraTransformStationary = false;
5992
+ if (isWebglRenderEnabled && this.renderer && this.scene && this.camera) {
5993
+ if (canReuseWebglCanvasForStationaryMotion === true &&
5994
+ this._applyWebglCanvasCameraTransform() === true) {
5995
+ usedWebglCanvasCameraTransformStationary = true;
5996
+ this._webglCameraMotionDeferred = true;
5997
+ this._lastWebglRenderInfo = {
5998
+ calls: 0,
5999
+ triangles: 0,
6000
+ points: 0,
6001
+ lines: 0,
6002
+ skipped: true,
6003
+ webglCanvasCameraTransform: true,
6004
+ uaStationaryVisualMotion: true
6005
+ };
6006
+ } else {
6007
+ this._clearWebglCanvasCameraTransform();
6008
+ this.renderer.render(this.scene, this.camera);
6009
+ this._recordWebglRenderedCameraState();
6010
+ const renderInfo = this.renderer?.info?.render || null;
6011
+ this._lastWebglRenderInfo = renderInfo ? {
6012
+ calls: Number(renderInfo.calls || 0),
6013
+ triangles: Number(renderInfo.triangles || 0),
6014
+ points: Number(renderInfo.points || 0),
6015
+ lines: Number(renderInfo.lines || 0),
6016
+ uaStationaryVisualMotion: true,
6017
+ webglCanvasCameraBootstrap: true
6018
+ } : null;
6019
+ }
6020
+ }
6021
+ this._syncMotionGridLayerForCameraNavigation(usedWebglCanvasCameraTransformStationary);
6022
+ if (this.cssRenderer && typeof this.cssRenderer.renderCamera === 'function') {
6023
+ this.cssRenderer.renderCamera(this.camera);
6024
+ }
6025
+ if ((this._businessAutomationEdgeVisualLayer?.isConnected ||
6026
+ this._businessAutomationEdgeLayer?.isConnected) &&
6027
+ window.MindMapCss3DManager?.renderBusinessAutomationEdges) {
6028
+ const hasBusinessAutomationConnectionDraft =
6029
+ window.MindMapCss3DManager?.hasBusinessAutomationConnectionDraft?.() === true;
6030
+ const transformedBusinessAutomationEdges =
6031
+ window.MindMapCss3DManager.syncBusinessAutomationEdgesForCameraMotion?.(this) === true;
6032
+ if (transformedBusinessAutomationEdges) {
6033
+ this._businessAutomationEdgesDeferredForMotion = true;
6034
+ } else if (hasBusinessAutomationConnectionDraft) {
6035
+ window.MindMapCss3DManager.renderBusinessAutomationEdges(this);
6036
+ this._businessAutomationEdgesDeferredForMotion = false;
6037
+ } else {
6038
+ this._businessAutomationEdgesDeferredForMotion = true;
6039
+ }
6040
+ }
6041
+
6042
+ _profileSection('uaStationaryVisualMotion');
6043
+
6044
+ const frameEnd = performance.now();
6045
+ const frameDuration = frameEnd - frameStart;
6046
+ this._lastFrameDurationMs = frameDuration;
6047
+ this._lastFrameMetricsAt = frameEnd;
6048
+ if (shouldCaptureFramePerf) {
6049
+ const sectionTimes = {};
6050
+ Object.entries(_frameProfTimes)
6051
+ .filter(([key, value]) => key !== 'visibleNodes' && typeof value === 'number')
6052
+ .forEach(([key, value]) => {
6053
+ sectionTimes[key] = Number(value.toFixed(2));
6054
+ });
6055
+ this._lastFramePerfProfile = {
6056
+ durationMs: Number(frameDuration.toFixed(2)),
6057
+ update: false,
6058
+ cameraZ: Number((this.camera?.position?.z || 0).toFixed(2)),
6059
+ isZooming: this.isZooming === true,
6060
+ isPanning: this.isPanning === true,
6061
+ isCameraMoving: false,
6062
+ isInLodMode: this.lodRenderer?.isInLODMode === true,
6063
+ lodBand: this.lodRenderer?._currentLodBand || null,
6064
+ visibleNodes: Number(this._visibleIds?.size || this._prevVisibleIds?.size || 0),
6065
+ freezeVisibility: true,
6066
+ freezeLod: true,
6067
+ freezeFullRes: true,
6068
+ residentMidFarStable: residentMidFarStable === true,
6069
+ residentDirty: residentDirty === true,
6070
+ residentBandChanging: residentBandChanging === true,
6071
+ viewSyncMode: 'ua-stationary-visual-motion',
6072
+ lodUpdateCalled: false,
6073
+ lodUpdateSkipped: true,
6074
+ lodUpdateSkipReason: 'ua-stationary-visual-motion',
6075
+ lodSkipReason: '',
6076
+ lodDirtyReason: '',
6077
+ lodDirtyState: null,
6078
+ viewSyncBreakdown: null,
6079
+ shouldRunVisibilityCulling: false,
6080
+ visibilityStableSkip: true,
6081
+ visibilitySkipReason: 'ua-stationary-visual-motion',
6082
+ incrementalPanVisibility: false,
6083
+ shouldSyncCss3dTransforms: false,
6084
+ shouldRenderCss3d: false,
6085
+ usedCss3dCameraFastPath: false,
6086
+ uaStationaryVisualMotion: true,
6087
+ shouldSyncTextOverlays: false,
6088
+ usedPassiveOverlayCameraFastPath: false,
6089
+ passiveOverlaySuppressed: this._suppressPassiveDomOverlayDuringZoom === true,
6090
+ webgl: this._lastWebglRenderInfo || null,
6091
+ sections: sectionTimes
6092
+ };
6093
+ updateFramePerfPeakProfile(this, this._lastFramePerfProfile, frameEnd);
6094
+ } else if (this._lastFramePerfProfile) {
6095
+ this._lastFramePerfProfile = null;
6096
+ }
6097
+
6098
+ this._lastAnimationLoopDelayMs = 0;
6099
+ this._animationLoopExecuting = false;
6100
+ this._scheduleAnimationLoop(loop, 0);
6101
+ return;
6102
+ }
6103
+ const shouldRenderCss3dInLodForUaResidentCameraOnly = !!(
6104
+ isCss3dEnabled === true &&
6105
+ !!this.cssRenderer &&
6106
+ typeof this.cssRenderer.renderCamera === 'function' &&
6107
+ this.lodRenderer?.shouldRenderCss3dInLod?.() === true
6108
+ );
6109
+ const canUseUaResidentCameraOnlyFrame = CAMERA_MOTION_VISUAL_ONLY_FAST_PATH_ENABLED === true && !!(
6110
+ shouldPreferCameraOnlyMotionPipeline === true &&
6111
+ isCameraNavigationOnlyInteractiveFrame === true &&
6112
+ isResidentMidFarLodFrame === true &&
6113
+ residentMidFarStable === true &&
6114
+ shouldUpdate !== true &&
6115
+ isCameraMoving === true &&
6116
+ this.isSelecting !== true &&
6117
+ !hasEditingTextOverlayFocus &&
6118
+ !hasPendingCss3dStructuralTransformWork &&
6119
+ !isBoardLoading &&
6120
+ this.isWindowResizing !== true &&
6121
+ (
6122
+ isWebglRenderEnabled === true ||
6123
+ shouldRenderCss3dInLodForUaResidentCameraOnly === true
6124
+ )
6125
+ );
6126
+ if (canUseUaResidentCameraOnlyFrame) {
6127
+ this._fullResMotionFrozen = false;
6128
+ this._wasViewStateMotionFrozenLastFrame = false;
6129
+ this._wasLodMotionFrozenLastFrame = true;
6130
+ this._preserveVisibleSetThisFrame = true;
6131
+ this._lastVisibilityCullingSkipReason = 'ua-resident-visual-only-motion';
6132
+ if (this._overlayDirty === true || hasSelectableTextOverlayFocus) {
6133
+ this._deferPassiveOverlayRefresh = true;
6134
+ }
6135
+ if (hasPendingCss3dVisualWork) {
6136
+ this._css3dVisualDeferredForMotion = true;
6137
+ }
6138
+
6139
+ const needsResidentPerfClassSync = shouldSyncMindCanvasPerfClasses();
6140
+ const approxVisibleNodeCountResident =
6141
+ needsResidentPerfClassSync || shouldCaptureFramePerf
6142
+ ? Number(this._visibleIds?.size || this._prevVisibleIds?.size || 0)
6143
+ : 0;
6144
+ if (needsResidentPerfClassSync) {
6145
+ const totalNodeCountResident = Number(this.nodeObjectsById?.size || 0);
6146
+ syncMindCanvasPerfClasses({
6147
+ moving: true,
6148
+ dense:
6149
+ approxVisibleNodeCountResident >= DENSE_VISIBLE_NODE_THRESHOLD ||
6150
+ totalNodeCountResident >= DENSE_TOTAL_NODE_THRESHOLD
6151
+ });
6152
+ }
6153
+
6154
+ updateCursorDomElement(this);
6155
+ const canReuseWebglCanvasForResidentMotion = !!(
6156
+ CAMERA_MOTION_WEBGL_CANVAS_TRANSFORM_ENABLED === true &&
6157
+ isWebglRenderEnabled === true &&
6158
+ this._lastWebglRenderedCameraState &&
6159
+ this.renderer?.domElement
6160
+ );
6161
+ if (canReuseWebglCanvasForResidentMotion === true) {
6162
+ deferContinuousThemeForCameraMotion(this);
6163
+ }
6164
+ if (!canReuseWebglCanvasForResidentMotion &&
6165
+ renderDebugFlags.enableThemeAnimation !== false &&
6166
+ this.currentThemeObject &&
6167
+ typeof this.currentThemeObject.animate === 'function') {
6168
+ const themeRunner = window.MindMapThemes?.runAnimationIfNeeded;
6169
+ if (typeof themeRunner === 'function') {
6170
+ themeRunner(this.currentThemeName, this.currentThemeObject, this.camera, {
6171
+ force: this._forceThemeAnimationFrame === true,
6172
+ isCameraMoving: true,
6173
+ isPanning: this.isPanning === true,
6174
+ isZooming: this.isZooming === true,
6175
+ isResizing: this.isWindowResizing === true,
6176
+ isInteractiveFrame: true
6177
+ });
6178
+ } else {
6179
+ this.currentThemeObject.animate(this.camera);
6180
+ }
6181
+ this._forceThemeAnimationFrame = false;
6182
+ }
6183
+ if (isWebglRenderEnabled === true) {
6184
+ if (this.renderer && this.scene && this.camera) {
6185
+ if (canReuseWebglCanvasForResidentMotion === true &&
6186
+ this._applyWebglCanvasCameraTransform() === true) {
6187
+ this._webglCameraMotionDeferred = true;
6188
+ this._lastWebglRenderInfo = {
6189
+ calls: 0,
6190
+ triangles: 0,
6191
+ points: 0,
6192
+ lines: 0,
6193
+ skipped: true,
6194
+ webglCanvasCameraTransform: true,
6195
+ uaResidentVisualOnlyMotion: true
6196
+ };
6197
+ } else {
6198
+ this._clearWebglCanvasCameraTransform();
6199
+ this.renderer.render(this.scene, this.camera);
6200
+ this._recordWebglRenderedCameraState();
6201
+ const renderInfo = this.renderer?.info?.render || null;
6202
+ this._lastWebglRenderInfo = renderInfo ? {
6203
+ calls: Number(renderInfo.calls || 0),
6204
+ triangles: Number(renderInfo.triangles || 0),
6205
+ points: Number(renderInfo.points || 0),
6206
+ lines: Number(renderInfo.lines || 0),
6207
+ uaResidentVisualOnlyMotion: true,
6208
+ webglCanvasCameraBootstrap: true
6209
+ } : null;
6210
+ }
6211
+ }
6212
+ }
6213
+
6214
+ if (shouldRenderCss3dInLodForUaResidentCameraOnly === true) {
6215
+ this.cssRenderer.renderCamera(this.camera);
6216
+ }
6217
+ if ((this._businessAutomationEdgeVisualLayer?.isConnected ||
6218
+ this._businessAutomationEdgeLayer?.isConnected) &&
6219
+ window.MindMapCss3DManager?.renderBusinessAutomationEdges) {
6220
+ const hasBusinessAutomationConnectionDraft =
6221
+ window.MindMapCss3DManager?.hasBusinessAutomationConnectionDraft?.() === true;
6222
+ const transformedBusinessAutomationEdges =
6223
+ window.MindMapCss3DManager.syncBusinessAutomationEdgesForCameraMotion?.(this) === true;
6224
+ if (transformedBusinessAutomationEdges) {
6225
+ this._businessAutomationEdgesDeferredForMotion = true;
6226
+ } else if (hasBusinessAutomationConnectionDraft) {
6227
+ window.MindMapCss3DManager.renderBusinessAutomationEdges(this);
6228
+ this._businessAutomationEdgesDeferredForMotion = false;
6229
+ } else {
6230
+ this._businessAutomationEdgesDeferredForMotion = true;
6231
+ }
6232
+ }
6233
+ _profileSection('uaResidentVisualOnlyMotion');
6234
+
6235
+ const frameEnd = performance.now();
6236
+ const frameDuration = frameEnd - frameStart;
6237
+ this._lastFrameDurationMs = frameDuration;
6238
+ this._lastFrameMetricsAt = frameEnd;
6239
+ if (shouldCaptureFramePerf) {
6240
+ const sectionTimes = {};
6241
+ Object.entries(_frameProfTimes)
6242
+ .filter(([key, value]) => key !== 'visibleNodes' && typeof value === 'number')
6243
+ .forEach(([key, value]) => {
6244
+ sectionTimes[key] = Number(value.toFixed(2));
6245
+ });
6246
+ this._lastFramePerfProfile = {
6247
+ durationMs: Number(frameDuration.toFixed(2)),
6248
+ update: false,
6249
+ cameraZ: Number((this.camera?.position?.z || 0).toFixed(2)),
6250
+ isZooming: this.isZooming === true,
6251
+ isPanning: this.isPanning === true,
6252
+ isCameraMoving: true,
6253
+ isInLodMode: true,
6254
+ lodBand: this.lodRenderer?._currentLodBand || null,
6255
+ visibleNodes: approxVisibleNodeCountResident,
6256
+ freezeVisibility: true,
6257
+ freezeLod: true,
6258
+ freezeFullRes: false,
6259
+ residentMidFarStable: true,
6260
+ residentDirty: residentDirty === true,
6261
+ residentBandChanging: false,
6262
+ viewSyncMode: 'ua-resident-visual-only-motion',
6263
+ lodUpdateCalled: false,
6264
+ lodUpdateSkipped: true,
6265
+ lodUpdateSkipReason: 'ua-resident-visual-only-motion',
6266
+ lodSkipReason: '',
6267
+ lodDirtyReason: '',
6268
+ lodDirtyState: null,
6269
+ viewSyncBreakdown: null,
6270
+ shouldRunVisibilityCulling: false,
6271
+ visibilityStableSkip: true,
6272
+ visibilitySkipReason: 'ua-resident-visual-only-motion',
6273
+ incrementalPanVisibility: false,
6274
+ shouldSyncCss3dTransforms: false,
6275
+ shouldRenderCss3d: false,
6276
+ usedCss3dCameraFastPath: shouldRenderCss3dInLodForUaResidentCameraOnly === true,
6277
+ uaResidentVisualOnlyMotion: true,
6278
+ shouldSyncTextOverlays: false,
6279
+ usedPassiveOverlayCameraFastPath: false,
6280
+ passiveOverlaySuppressed: this._suppressPassiveDomOverlayDuringZoom === true,
6281
+ webgl: this._lastWebglRenderInfo || null,
6282
+ sections: sectionTimes
6283
+ };
6284
+ updateFramePerfPeakProfile(this, this._lastFramePerfProfile, frameEnd);
6285
+ } else if (this._lastFramePerfProfile) {
6286
+ this._lastFramePerfProfile = null;
6287
+ }
6288
+
6289
+ this._lastAnimationLoopDelayMs = 0;
6290
+ this._animationLoopExecuting = false;
6291
+ this._scheduleAnimationLoop(loop, 0);
6292
+ return;
6293
+ }
6294
+ const themeAnimationMode =
6295
+ window.MindMapThemes?.getAnimationMode?.(this.currentThemeName, this.currentThemeObject) ||
6296
+ 'none';
6297
+ const isContinuousTheme = themeAnimationMode === 'continuous';
6298
+ const shouldPauseThemeForCameraNavigation =
6299
+ isContinuousTheme === true &&
6300
+ isCameraNavigationOnlyInteractiveFrame === true &&
6301
+ this._forceThemeAnimationFrame !== true;
4699
6302
  const allowContinuousThemeAnimationThisFrame =
4700
- isContinuousTheme !== true ||
4701
- (isInteractiveFrame === true && !isCameraNavigationOnlyInteractiveFrame) ||
4702
- (shouldUpdate === true && !isCameraNavigationOnlyInteractiveFrame) ||
4703
- hasNonCameraNavigationForcedUpdate === true ||
4704
- isBoardLoading === true ||
4705
- shouldUseAnimation60Fps === true ||
4706
- shouldUseAnimationLowFpsFrame === true ||
4707
- this._forceThemeAnimationFrame === true;
6303
+ shouldPauseThemeForCameraNavigation === true
6304
+ ? false
6305
+ : (isContinuousTheme !== true ||
6306
+ (isInteractiveFrame === true && !isCameraNavigationOnlyInteractiveFrame) ||
6307
+ (shouldUpdate === true && !isCameraNavigationOnlyInteractiveFrame) ||
6308
+ hasNonCameraNavigationForcedUpdate === true ||
6309
+ isBoardLoading === true ||
6310
+ shouldUseAnimation60Fps === true ||
6311
+ shouldUseAnimationLowFpsFrame === true ||
6312
+ this._forceThemeAnimationFrame === true);
6313
+ if (shouldPauseThemeForCameraNavigation === true) {
6314
+ deferContinuousThemeForCameraMotion(this);
6315
+ }
4708
6316
  const isMenuOverlayUiEnabled = !isBoardLoading && renderDebugFlags.enableMenuOverlayUi !== false;
4709
6317
  const isMultiSelectOverlayUiEnabled = !isBoardLoading && renderDebugFlags.enableMultiSelectOverlayUi !== false;
4710
6318
  _profileSection('pipelineScheduling');
@@ -4728,21 +6336,307 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4728
6336
  _profileSection('pipelineOverlayPlan');
4729
6337
  const hasFocusedTextOverlay = !!selectableOverlayNodeId;
4730
6338
  const hasEditingOverlay = !!editingOverlayNodeId;
6339
+ const isUaStyleCameraTransformFrame =
6340
+ shouldPreferCameraOnlyMotionPipeline === true &&
6341
+ isCss3dEnabled === true &&
6342
+ !!this.cssRenderer &&
6343
+ !hasEditingOverlay;
6344
+ const canUseUaEarlyCameraOnlyFrame = CAMERA_MOTION_PARTIAL_FRAME_SKIP_ENABLED === true && !!(
6345
+ isUaStyleCameraTransformFrame === true &&
6346
+ isFullResMode === true &&
6347
+ isResidentMidFarLodFrame !== true &&
6348
+ isCameraMoving === true &&
6349
+ this.isSelecting !== true &&
6350
+ typeof this.cssRenderer.renderCamera === 'function' &&
6351
+ !hasPendingCss3dStructuralTransformWork
6352
+ );
6353
+ if (canUseUaEarlyCameraOnlyFrame) {
6354
+ this._fullResMotionFrozen = true;
6355
+ this._wasViewStateMotionFrozenLastFrame = true;
6356
+ this._wasLodMotionFrozenLastFrame = false;
6357
+ this._fullResVisibilityReconcilePending = true;
6358
+ this._preserveVisibleSetThisFrame = true;
6359
+ this._lastVisibilityCullingSkipReason = 'ua-early-camera-only';
6360
+ if (this._businessAutomationEdgeVisualLayer?.isConnected ||
6361
+ this._businessAutomationEdgeLayer?.isConnected) {
6362
+ this._businessAutomationEdgesDeferredForMotion = true;
6363
+ }
6364
+ if (hasPendingCss3dVisualWork) {
6365
+ this._css3dVisualDeferredForMotion = true;
6366
+ }
6367
+
6368
+ const needsEarlyPerfClassSync = shouldSyncMindCanvasPerfClasses();
6369
+ const approxVisibleNodeCountEarly =
6370
+ needsEarlyPerfClassSync || shouldCaptureFramePerf
6371
+ ? Number(this._visibleIds?.size || this._prevVisibleIds?.size || 0)
6372
+ : 0;
6373
+ if (needsEarlyPerfClassSync) {
6374
+ const totalNodeCountEarly = Number(this.nodeObjectsById?.size || 0);
6375
+ syncMindCanvasPerfClasses({
6376
+ moving: true,
6377
+ dense:
6378
+ approxVisibleNodeCountEarly >= DENSE_VISIBLE_NODE_THRESHOLD ||
6379
+ totalNodeCountEarly >= DENSE_TOTAL_NODE_THRESHOLD
6380
+ });
6381
+ }
6382
+
6383
+ const shouldSampleWebglDuringCameraMotion =
6384
+ renderDebugFlags.sampleWebglDuringCameraMotion === true &&
6385
+ (this.frameCount % CAMERA_MOTION_WEBGL_FRAME_INTERVAL) === 0;
6386
+ let usedWebglCanvasCameraTransformEarly = false;
6387
+ if (shouldSampleWebglDuringCameraMotion) {
6388
+ updateCursorDomElement(this);
6389
+ if (isWebglRenderEnabled && this.renderer && this.scene && this.camera) {
6390
+ this._clearWebglCanvasCameraTransform();
6391
+ this.renderer.render(this.scene, this.camera);
6392
+ this._recordWebglRenderedCameraState();
6393
+ const renderInfo = this.renderer?.info?.render || null;
6394
+ this._lastWebglRenderInfo = renderInfo ? {
6395
+ calls: Number(renderInfo.calls || 0),
6396
+ triangles: Number(renderInfo.triangles || 0),
6397
+ points: Number(renderInfo.points || 0),
6398
+ lines: Number(renderInfo.lines || 0),
6399
+ uaEarlyCameraOnly: true
6400
+ } : null;
6401
+ }
6402
+ } else if (isWebglRenderEnabled) {
6403
+ usedWebglCanvasCameraTransformEarly =
6404
+ this._applyWebglCanvasCameraTransform() === true;
6405
+ this._webglCameraMotionDeferred = true;
6406
+ if (usedWebglCanvasCameraTransformEarly === true) {
6407
+ deferContinuousThemeForCameraMotion(this);
6408
+ this._lastWebglRenderInfo = {
6409
+ calls: 0,
6410
+ triangles: 0,
6411
+ points: 0,
6412
+ lines: 0,
6413
+ skipped: true,
6414
+ webglCanvasCameraTransform: true,
6415
+ uaEarlyCameraOnly: true
6416
+ };
6417
+ } else if (this.renderer && this.scene && this.camera) {
6418
+ let hiddenEarlyCameraMotionThemeObject = null;
6419
+ this._clearWebglCanvasCameraTransform();
6420
+ if (this._isMotionGridThemeEnabled() === true &&
6421
+ this.currentThemeObject &&
6422
+ this.currentThemeObject.visible !== false) {
6423
+ hiddenEarlyCameraMotionThemeObject = this.currentThemeObject;
6424
+ hiddenEarlyCameraMotionThemeObject.visible = false;
6425
+ }
6426
+ try {
6427
+ this.renderer.render(this.scene, this.camera);
6428
+ } finally {
6429
+ if (hiddenEarlyCameraMotionThemeObject) {
6430
+ hiddenEarlyCameraMotionThemeObject.visible = true;
6431
+ }
6432
+ }
6433
+ this._recordWebglRenderedCameraState();
6434
+ const renderInfo = this.renderer?.info?.render || null;
6435
+ this._lastWebglRenderInfo = renderInfo ? {
6436
+ calls: Number(renderInfo.calls || 0),
6437
+ triangles: Number(renderInfo.triangles || 0),
6438
+ points: Number(renderInfo.points || 0),
6439
+ lines: Number(renderInfo.lines || 0),
6440
+ backgroundThemeHidden: hiddenEarlyCameraMotionThemeObject ? true : undefined,
6441
+ uaEarlyCameraOnly: true,
6442
+ webglCanvasCameraBootstrap: true
6443
+ } : null;
6444
+ } else {
6445
+ this._lastWebglRenderInfo = {
6446
+ calls: 0,
6447
+ triangles: 0,
6448
+ points: 0,
6449
+ lines: 0,
6450
+ skipped: true,
6451
+ uaEarlyCameraOnly: true
6452
+ };
6453
+ }
6454
+ }
6455
+
6456
+ this._syncMotionGridLayerForCameraNavigation(usedWebglCanvasCameraTransformEarly);
6457
+ this.cssRenderer.renderCamera(this.camera);
6458
+ _profileSection('uaEarlyCameraOnly');
6459
+
6460
+ const frameEnd = performance.now();
6461
+ const frameDuration = frameEnd - frameStart;
6462
+ this._lastFrameDurationMs = frameDuration;
6463
+ this._lastFrameMetricsAt = frameEnd;
6464
+
6465
+ if (shouldCaptureFramePerf) {
6466
+ const sectionTimes = {};
6467
+ Object.entries(_frameProfTimes)
6468
+ .filter(([key, value]) => key !== 'visibleNodes' && typeof value === 'number')
6469
+ .forEach(([key, value]) => {
6470
+ sectionTimes[key] = Number(value.toFixed(2));
6471
+ });
6472
+ this._lastFramePerfProfile = {
6473
+ durationMs: Number(frameDuration.toFixed(2)),
6474
+ update: false,
6475
+ cameraZ: Number((this.camera?.position?.z || 0).toFixed(2)),
6476
+ isZooming: this.isZooming === true,
6477
+ isPanning: this.isPanning === true,
6478
+ isCameraMoving: true,
6479
+ isInLodMode: false,
6480
+ lodBand: this.lodRenderer?._currentLodBand || null,
6481
+ visibleNodes: approxVisibleNodeCountEarly,
6482
+ freezeVisibility: true,
6483
+ freezeLod: false,
6484
+ freezeFullRes: true,
6485
+ residentMidFarStable: false,
6486
+ residentDirty: false,
6487
+ residentBandChanging: false,
6488
+ viewSyncMode: 'ua-early-camera-only',
6489
+ lodUpdateCalled: false,
6490
+ lodUpdateSkipped: true,
6491
+ lodUpdateSkipReason: 'ua-early-camera-only',
6492
+ lodSkipReason: '',
6493
+ lodDirtyReason: '',
6494
+ lodDirtyState: null,
6495
+ viewSyncBreakdown: null,
6496
+ shouldRunVisibilityCulling: false,
6497
+ visibilityStableSkip: true,
6498
+ visibilitySkipReason: 'ua-early-camera-only',
6499
+ incrementalPanVisibility: false,
6500
+ shouldSyncCss3dTransforms: false,
6501
+ shouldRenderCss3d: false,
6502
+ usedCss3dCameraFastPath: true,
6503
+ uaEarlyCameraOnly: true,
6504
+ shouldSyncTextOverlays: false,
6505
+ usedPassiveOverlayCameraFastPath: false,
6506
+ passiveOverlaySuppressed: this._suppressPassiveDomOverlayDuringZoom === true,
6507
+ webgl: this._lastWebglRenderInfo || null,
6508
+ sections: sectionTimes
6509
+ };
6510
+ updateFramePerfPeakProfile(this, this._lastFramePerfProfile, frameEnd);
6511
+ } else if (this._lastFramePerfProfile) {
6512
+ this._lastFramePerfProfile = null;
6513
+ }
6514
+
6515
+ this._lastAnimationLoopDelayMs = 0;
6516
+ this._animationLoopExecuting = false;
6517
+ this._scheduleAnimationLoop(loop, 0);
6518
+ return;
6519
+ }
6520
+ const canUseUaStationaryCameraNavigationFrame = CAMERA_MOTION_PARTIAL_FRAME_SKIP_ENABLED === true && !!(
6521
+ isUaStyleCameraTransformFrame === true &&
6522
+ isFullResMode === true &&
6523
+ isResidentMidFarLodFrame !== true &&
6524
+ isCameraMoving !== true &&
6525
+ (this.isPanning === true || this.isZooming === true) &&
6526
+ this.isSelecting !== true &&
6527
+ !isNodeInteracting &&
6528
+ !hasNonCameraNavigationForcedUpdate &&
6529
+ !hasPendingNearCssWarmup &&
6530
+ !hasEditingOverlay &&
6531
+ !hasPendingCss3dStructuralTransformWork &&
6532
+ !hasPendingCss3dVisualWork &&
6533
+ shouldUpdate !== true &&
6534
+ !isBoardLoading &&
6535
+ this.isWindowResizing !== true
6536
+ );
6537
+ if (canUseUaStationaryCameraNavigationFrame) {
6538
+ this._fullResMotionFrozen = true;
6539
+ this._wasViewStateMotionFrozenLastFrame = true;
6540
+ this._wasLodMotionFrozenLastFrame = false;
6541
+ this._preserveVisibleSetThisFrame = true;
6542
+ this._lastVisibilityCullingSkipReason = 'ua-stationary-camera-navigation';
6543
+ if (this._businessAutomationEdgeVisualLayer?.isConnected ||
6544
+ this._businessAutomationEdgeLayer?.isConnected) {
6545
+ this._businessAutomationEdgesDeferredForMotion = true;
6546
+ }
6547
+ if (this._overlayDirty === true || hasFocusedTextOverlay) {
6548
+ this._deferPassiveOverlayRefresh = true;
6549
+ }
6550
+ if (isWebglRenderEnabled) {
6551
+ this._lastWebglRenderInfo = {
6552
+ calls: 0,
6553
+ triangles: 0,
6554
+ points: 0,
6555
+ lines: 0,
6556
+ skipped: true,
6557
+ uaStationaryCameraNavigation: true
6558
+ };
6559
+ }
6560
+
6561
+ _profileSection('uaStationaryCameraNavigation');
6562
+
6563
+ const frameEnd = performance.now();
6564
+ const frameDuration = frameEnd - frameStart;
6565
+ this._lastFrameDurationMs = frameDuration;
6566
+ this._lastFrameMetricsAt = frameEnd;
6567
+ if (shouldCaptureFramePerf) {
6568
+ const sectionTimes = {};
6569
+ Object.entries(_frameProfTimes)
6570
+ .filter(([key, value]) => key !== 'visibleNodes' && typeof value === 'number')
6571
+ .forEach(([key, value]) => {
6572
+ sectionTimes[key] = Number(value.toFixed(2));
6573
+ });
6574
+ this._lastFramePerfProfile = {
6575
+ durationMs: Number(frameDuration.toFixed(2)),
6576
+ update: false,
6577
+ cameraZ: Number((this.camera?.position?.z || 0).toFixed(2)),
6578
+ isZooming: this.isZooming === true,
6579
+ isPanning: this.isPanning === true,
6580
+ isCameraMoving: false,
6581
+ isInLodMode: false,
6582
+ lodBand: this.lodRenderer?._currentLodBand || null,
6583
+ visibleNodes: Number(this._visibleIds?.size || this._prevVisibleIds?.size || 0),
6584
+ freezeVisibility: true,
6585
+ freezeLod: false,
6586
+ freezeFullRes: true,
6587
+ residentMidFarStable: false,
6588
+ residentDirty: false,
6589
+ residentBandChanging: false,
6590
+ viewSyncMode: 'ua-stationary-camera-navigation',
6591
+ lodUpdateCalled: false,
6592
+ lodUpdateSkipped: true,
6593
+ lodUpdateSkipReason: 'ua-stationary-camera-navigation',
6594
+ lodSkipReason: '',
6595
+ lodDirtyReason: '',
6596
+ lodDirtyState: null,
6597
+ viewSyncBreakdown: null,
6598
+ shouldRunVisibilityCulling: false,
6599
+ visibilityStableSkip: true,
6600
+ visibilitySkipReason: 'ua-stationary-camera-navigation',
6601
+ incrementalPanVisibility: false,
6602
+ shouldSyncCss3dTransforms: false,
6603
+ shouldRenderCss3d: false,
6604
+ usedCss3dCameraFastPath: false,
6605
+ uaStationaryCameraNavigation: true,
6606
+ shouldSyncTextOverlays: false,
6607
+ usedPassiveOverlayCameraFastPath: false,
6608
+ passiveOverlaySuppressed: this._suppressPassiveDomOverlayDuringZoom === true,
6609
+ webgl: this._lastWebglRenderInfo || null,
6610
+ sections: sectionTimes
6611
+ };
6612
+ updateFramePerfPeakProfile(this, this._lastFramePerfProfile, frameEnd);
6613
+ } else if (this._lastFramePerfProfile) {
6614
+ this._lastFramePerfProfile = null;
6615
+ }
6616
+
6617
+ this._lastAnimationLoopDelayMs = CAMERA_STATIONARY_INTERACTION_FRAME_DELAY_MS;
6618
+ this._animationLoopExecuting = false;
6619
+ this._scheduleAnimationLoop(loop, CAMERA_STATIONARY_INTERACTION_FRAME_DELAY_MS);
6620
+ return;
6621
+ }
4731
6622
  const isMotionFreezeEligible =
4732
6623
  (this.isZooming === true || this.isPanning === true || isCameraMoving === true) &&
4733
6624
  !isNodeInteracting &&
4734
6625
  !hasNonCameraNavigationForcedUpdate &&
4735
6626
  !hasPendingNearCssWarmup &&
4736
6627
  !this.isWindowResizing;
6628
+ const needsPerfClassSync = shouldSyncMindCanvasPerfClasses();
4737
6629
  const approxVisibleNodeCount = Number(this._visibleIds?.size || this._prevVisibleIds?.size || 0);
4738
6630
  const totalNodeCount = Number(this.nodeObjectsById?.size || 0);
4739
6631
  const shouldUseDensePerfClass =
4740
6632
  approxVisibleNodeCount >= DENSE_VISIBLE_NODE_THRESHOLD ||
4741
6633
  totalNodeCount >= DENSE_TOTAL_NODE_THRESHOLD;
4742
- syncMindCanvasPerfClasses({
4743
- moving: isInteractiveFrame === true,
4744
- dense: shouldUseDensePerfClass
4745
- });
6634
+ if (needsPerfClassSync) {
6635
+ syncMindCanvasPerfClasses({
6636
+ moving: isInteractiveFrame === true,
6637
+ dense: shouldUseDensePerfClass
6638
+ });
6639
+ }
4746
6640
  const isMotionHeavyScene =
4747
6641
  approxVisibleNodeCount >= AUTO_FREEZE_VISIBLE_NODE_THRESHOLD ||
4748
6642
  Number(this._lastFrameDurationMs || 0) >= AUTO_FREEZE_FRAME_MS ||
@@ -4771,9 +6665,9 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4771
6665
  const shouldFreezeFullResDuringMotion =
4772
6666
  isFullResMode &&
4773
6667
  isMotionFreezeEligible &&
4774
- !hasFocusedTextOverlay &&
4775
6668
  !hasEditingOverlay &&
4776
6669
  (
6670
+ isUaStyleCameraTransformFrame === true ||
4777
6671
  renderDebugFlags.freezeVisibilityDuringMotion === true ||
4778
6672
  isHeavyFullResScene
4779
6673
  );
@@ -4892,7 +6786,14 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4892
6786
  hasForcedUpdate === true ||
4893
6787
  hasPendingNearCssWarmup === true
4894
6788
  );
4895
- const shouldUpdateCss3d = shouldUpdate || shouldUpdateResidentCssMediaFallback;
6789
+ const shouldUseCss3dCameraTransformFrame =
6790
+ shouldPreferCameraOnlyMotionPipeline === true &&
6791
+ isCss3dEnabled === true &&
6792
+ !!this.cssRenderer;
6793
+ const shouldUpdateCss3d =
6794
+ shouldUpdate ||
6795
+ shouldUpdateResidentCssMediaFallback ||
6796
+ shouldUseCss3dCameraTransformFrame;
4896
6797
  const prevVisibilityVersion = Number(this._visibilityVersion || 0);
4897
6798
  const prevLodVisualEpoch = Number(this.lodRenderer?._lodVisualEpoch || 0);
4898
6799
  this._css3dVisualChangedThisFrame = false;
@@ -5056,10 +6957,10 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5056
6957
  const isInLodMode = this.lodRenderer?.isInLODMode === true;
5057
6958
  const shouldRenderCss3dInLod = isInLodMode && this.lodRenderer?.shouldRenderCss3dInLod?.() === true;
5058
6959
  const shouldRenderPassiveDomTextOverlay =
5059
- useTextOverlayV2 &&
6960
+ isPassiveDomOverlayRuntimeEnabled &&
5060
6961
  window.MindMapTextOverlayV2?.shouldRenderPassiveDomOverlays?.(this) === true;
5061
6962
  const hasLiveInteractiveDomOverlay =
5062
- useTextOverlayV2 &&
6963
+ isPassiveDomOverlayRuntimeEnabled &&
5063
6964
  window.MindMapTextOverlayV2?.hasLiveInteractiveOverlay?.(this) === true;
5064
6965
  const hasPassiveDomOverlaySurface =
5065
6966
  shouldRenderPassiveDomTextOverlay ||
@@ -5074,6 +6975,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5074
6975
  )
5075
6976
  );
5076
6977
  const hasOverlayFocus = hasFocusedTextOverlay || hasEditingOverlay;
6978
+ const hasMotionBlockingOverlayFocus = hasEditingOverlay;
5077
6979
  const visibleOverlayCount = Math.max(
5078
6980
  Number(this._visibleIds?.size || 0),
5079
6981
  Number(this._prevVisibleIds?.size || 0)
@@ -5084,7 +6986,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5084
6986
  !isInLodMode &&
5085
6987
  !isCtrlWheelVerticalScrolling &&
5086
6988
  !isCameraNavigationOverlayPriority &&
5087
- !hasOverlayFocus &&
6989
+ !hasMotionBlockingOverlayFocus &&
5088
6990
  !isNodeInteracting &&
5089
6991
  !hasNonCameraNavigationForcedUpdate &&
5090
6992
  !hasPendingNearCssWarmup &&
@@ -5141,7 +7043,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5141
7043
  this._deferPassiveOverlayRefresh === true &&
5142
7044
  shouldDeferInteractivePassiveOverlayRefresh &&
5143
7045
  !isInLodMode &&
5144
- !hasOverlayFocus &&
7046
+ !hasMotionBlockingOverlayFocus &&
5145
7047
  !isNodeInteracting &&
5146
7048
  !hasNonCameraNavigationForcedUpdate &&
5147
7049
  !this.isWindowResizing;
@@ -5164,7 +7066,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5164
7066
  !isInLodMode &&
5165
7067
  (this.isZooming === true || this.isPanning === true || isCameraMoving) &&
5166
7068
  !isCtrlWheelVerticalScrolling &&
5167
- !hasOverlayFocus &&
7069
+ !hasMotionBlockingOverlayFocus &&
5168
7070
  !hasOverlayV2DirtyState &&
5169
7071
  !effectiveOverlayDirty &&
5170
7072
  isCameraOnlyOverlayFrame &&
@@ -5183,11 +7085,14 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5183
7085
  useTextOverlayV2 &&
5184
7086
  hasPassiveDomOverlaySurface &&
5185
7087
  !isPassiveOverlaySuppressed &&
5186
- !hasOverlayFocus &&
7088
+ !hasMotionBlockingOverlayFocus &&
5187
7089
  !hasOverlayV2DirtyState &&
5188
7090
  !this.isWindowResizing &&
5189
7091
  (this.isZooming === true || this.isPanning === true || isCameraMoving);
5190
- window.MindMapTextOverlayV2?.setMotionHint?.(this, shouldUsePassiveOverlayMotionHint);
7092
+ if (isPassiveDomOverlayRuntimeEnabled ||
7093
+ this._textOverlayV2State?.motionHintActive === true) {
7094
+ window.MindMapTextOverlayV2?.setMotionHint?.(this, shouldUsePassiveOverlayMotionHint);
7095
+ }
5191
7096
  const isPureCss3dCameraMotionFrame = !!(
5192
7097
  this.cssRenderer &&
5193
7098
  isCss3dEnabled &&
@@ -5198,12 +7103,14 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5198
7103
  !isNodeInteracting &&
5199
7104
  !hasNonCameraNavigationForcedUpdate &&
5200
7105
  !hasPendingNearCssWarmup &&
5201
- !hasOverlayFocus &&
5202
- this._css3dVisualChangedThisFrame !== true &&
5203
- this._css3dVisualChangedSinceLastRender !== true &&
7106
+ !hasMotionBlockingOverlayFocus &&
7107
+ !hasPendingCss3dStructuralTransformWork &&
5204
7108
  !lodVisualEpochChangedThisFrame &&
5205
7109
  this.isWindowResizing !== true
5206
7110
  );
7111
+ if (isPureCss3dCameraMotionFrame === true && hasPendingCss3dVisualWork) {
7112
+ this._css3dVisualDeferredForMotion = true;
7113
+ }
5207
7114
 
5208
7115
  // 4. CSS3D Position Sync
5209
7116
  // ----------------------------------------------------------------
@@ -5225,19 +7132,29 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5225
7132
  !isCameraMoving &&
5226
7133
  !isNodeInteracting &&
5227
7134
  !this.isWindowResizing;
5228
- window.MindMapNodes?.updateAnimatedImageStatusTextures?.(this, frameStart, {
5229
- allowAnimation: shouldAnimateImageStatusTextures
5230
- });
7135
+ if (this._activeImageStatusTextures?.size > 0) {
7136
+ window.MindMapNodes?.updateAnimatedImageStatusTextures?.(this, frameStart, {
7137
+ allowAnimation: shouldAnimateImageStatusTextures
7138
+ });
7139
+ }
5231
7140
 
5232
7141
  // 5. Cursor DOM position
5233
7142
  // ----------------------------------------------------------------
5234
- if (!isPureCss3dCameraMotionFrame || (this.frameCount % CAMERA_MOTION_WEBGL_FRAME_INTERVAL) === 0) {
7143
+ const shouldSampleWebglDuringCameraMotion =
7144
+ renderDebugFlags.sampleWebglDuringCameraMotion === true &&
7145
+ (this.frameCount % CAMERA_MOTION_WEBGL_FRAME_INTERVAL) === 0;
7146
+ if (!isPureCss3dCameraMotionFrame || shouldSampleWebglDuringCameraMotion) {
5235
7147
  updateCursorDomElement(this);
5236
7148
  }
5237
7149
 
5238
7150
  // 6. Theme Animation
5239
7151
  // ----------------------------------------------------------------
5240
7152
  const isGpuParticleTheme = this.currentThemeName === 'Space3D' || this.currentThemeName === 'SpiritFlow';
7153
+ const shouldUseMotionGridThemeSubstitute =
7154
+ false &&
7155
+ isCameraNavigationOnlyInteractiveFrame === true &&
7156
+ this._isMotionGridThemeEnabled() === true &&
7157
+ !isBoardLoading;
5241
7158
  const shouldForceGpuParticleAnimation =
5242
7159
  isGpuParticleTheme &&
5243
7160
  !isBoardLoading &&
@@ -5247,7 +7164,6 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5247
7164
  (isThemeAnimationEnabled || shouldForceGpuParticleAnimation) &&
5248
7165
  this.currentThemeObject &&
5249
7166
  typeof this.currentThemeObject.animate === 'function') {
5250
- const themeAnimationInterval = this._getThemeAnimationInterval(isInteractiveFrame, themeAnimationMode);
5251
7167
  const forceThemeCameraUpdate =
5252
7168
  hasNonCameraNavigationForcedUpdate === true ||
5253
7169
  zoomEndedThisFrame === true ||
@@ -5255,10 +7171,15 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5255
7171
  cameraEndedThisFrame === true ||
5256
7172
  resizeEndedThisFrame === true ||
5257
7173
  this._forceThemeAnimationFrame === true;
7174
+ const themeAnimationInterval = this._getThemeAnimationInterval(isInteractiveFrame, themeAnimationMode);
7175
+ const effectiveThemeAnimationInterval =
7176
+ isCameraNavigationOnlyInteractiveFrame === true
7177
+ ? 1
7178
+ : themeAnimationInterval;
5258
7179
  const shouldCheckThemeAnimation =
5259
7180
  forceThemeCameraUpdate ||
5260
- themeAnimationInterval <= 1 ||
5261
- (this.frameCount % themeAnimationInterval === 0);
7181
+ effectiveThemeAnimationInterval <= 1 ||
7182
+ (this.frameCount % effectiveThemeAnimationInterval === 0);
5262
7183
 
5263
7184
  if (shouldCheckThemeAnimation) {
5264
7185
  const themeRunner = window.MindMapThemes?.runAnimationIfNeeded;
@@ -5278,40 +7199,120 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5278
7199
  this._forceThemeAnimationFrame = false;
5279
7200
  }
5280
7201
  }
5281
-
7202
+
5282
7203
  // 7. Render
5283
7204
  // ----------------------------------------------------------------
5284
7205
  _profileSection('css3dSync');
7206
+ const shouldFinalizeWebglCanvasCameraTransform = !!(
7207
+ this._webglCanvasMotionTransformActive === true &&
7208
+ isCameraNavigationOnlyInteractiveFrame !== true
7209
+ );
7210
+ const shouldUseAnimationCadenceForWebglFrame =
7211
+ isCameraNavigationOnlyInteractiveFrame === true
7212
+ ? false
7213
+ : (shouldUseAnimation60Fps === true || shouldUseAnimationLowFpsFrame === true);
5285
7214
  const shouldRenderWebglFrame =
5286
7215
  isWebglRenderEnabled &&
5287
7216
  (
5288
7217
  isBoardLoading === true ||
5289
7218
  shouldUpdate === true ||
7219
+ shouldFinalizeWebglCanvasCameraTransform === true ||
5290
7220
  isInteractiveFrame === true ||
5291
- shouldUseAnimation60Fps === true ||
5292
- shouldUseAnimationLowFpsFrame === true ||
7221
+ shouldUseAnimationCadenceForWebglFrame === true ||
5293
7222
  themeAnimatedThisFrame === true
5294
7223
  ) &&
5295
7224
  (
5296
7225
  isPureCss3dCameraMotionFrame !== true ||
5297
7226
  themeAnimatedThisFrame === true ||
5298
- (this.frameCount % CAMERA_MOTION_WEBGL_FRAME_INTERVAL) === 0
7227
+ shouldSampleWebglDuringCameraMotion === true
5299
7228
  );
7229
+ const shouldHideBackgroundThemeDuringCameraNavigation = !!(
7230
+ shouldUseMotionGridThemeSubstitute === true &&
7231
+ this.currentThemeObject &&
7232
+ this.currentThemeObject.visible !== false
7233
+ );
7234
+ let hiddenCameraMotionThemeObject = null;
7235
+ let usedWebglCanvasCameraTransform = false;
7236
+ const shouldUseWebglCanvasCameraTransformFrame = !!(
7237
+ isWebglRenderEnabled &&
7238
+ isCameraNavigationOnlyInteractiveFrame === true &&
7239
+ shouldUpdate !== true &&
7240
+ themeAnimatedThisFrame !== true &&
7241
+ shouldUseAnimationCadenceForWebglFrame !== true &&
7242
+ !isBoardLoading &&
7243
+ this._lastWebglRenderedCameraState &&
7244
+ this.renderer?.domElement
7245
+ );
5300
7246
  if (shouldRenderWebglFrame) {
5301
- this.renderer.render(this.scene, this.camera);
5302
- const renderInfo = this.renderer?.info?.render || null;
5303
- this._lastWebglRenderInfo = renderInfo ? {
5304
- calls: Number(renderInfo.calls || 0),
5305
- triangles: Number(renderInfo.triangles || 0),
5306
- points: Number(renderInfo.points || 0),
5307
- lines: Number(renderInfo.lines || 0)
5308
- } : null;
7247
+ if (shouldUseWebglCanvasCameraTransformFrame === true &&
7248
+ this._applyWebglCanvasCameraTransform() === true) {
7249
+ usedWebglCanvasCameraTransform = true;
7250
+ this._webglCameraMotionDeferred = true;
7251
+ this._lastWebglRenderInfo = {
7252
+ calls: 0,
7253
+ triangles: 0,
7254
+ points: 0,
7255
+ lines: 0,
7256
+ skipped: true,
7257
+ webglCanvasCameraTransform: true
7258
+ };
7259
+ } else {
7260
+ this._clearWebglCanvasCameraTransform();
7261
+ if (shouldHideBackgroundThemeDuringCameraNavigation) {
7262
+ hiddenCameraMotionThemeObject = this.currentThemeObject;
7263
+ hiddenCameraMotionThemeObject.visible = false;
7264
+ }
7265
+ try {
7266
+ this.renderer.render(this.scene, this.camera);
7267
+ } finally {
7268
+ if (hiddenCameraMotionThemeObject) {
7269
+ hiddenCameraMotionThemeObject.visible = true;
7270
+ }
7271
+ }
7272
+ this._recordWebglRenderedCameraState();
7273
+ const renderInfo = this.renderer?.info?.render || null;
7274
+ this._lastWebglRenderInfo = renderInfo ? {
7275
+ calls: Number(renderInfo.calls || 0),
7276
+ triangles: Number(renderInfo.triangles || 0),
7277
+ points: Number(renderInfo.points || 0),
7278
+ lines: Number(renderInfo.lines || 0),
7279
+ backgroundThemeHidden: hiddenCameraMotionThemeObject ? true : undefined
7280
+ } : null;
7281
+ }
5309
7282
  } else if (isWebglRenderEnabled) {
5310
- this._lastWebglRenderInfo = { calls: 0, triangles: 0, points: 0, lines: 0, skipped: true };
7283
+ if (isPureCss3dCameraMotionFrame === true) {
7284
+ this._webglCameraMotionDeferred = true;
7285
+ }
7286
+ if (isCameraNavigationOnlyInteractiveFrame === true) {
7287
+ usedWebglCanvasCameraTransform = this._applyWebglCanvasCameraTransform() === true;
7288
+ } else {
7289
+ this._clearWebglCanvasCameraTransform();
7290
+ }
7291
+ this._lastWebglRenderInfo = {
7292
+ calls: 0,
7293
+ triangles: 0,
7294
+ points: 0,
7295
+ lines: 0,
7296
+ skipped: true,
7297
+ webglCanvasCameraTransform: usedWebglCanvasCameraTransform || undefined
7298
+ };
5311
7299
  } else if (isBoardLoading) {
7300
+ this._clearWebglCanvasCameraTransform();
5312
7301
  this._lastWebglRenderInfo = { calls: 0, triangles: 0, points: 0, lines: 0 };
5313
7302
  }
5314
-
7303
+ if (shouldUseMotionGridThemeSubstitute === true &&
7304
+ isCameraNavigationOnlyInteractiveFrame === true) {
7305
+ this._syncMotionGridLayerForCameraNavigation(usedWebglCanvasCameraTransform);
7306
+ }
7307
+ if (shouldRenderWebglFrame === true &&
7308
+ this._motionGridVisible === true &&
7309
+ isPureCss3dCameraMotionFrame !== true &&
7310
+ this.isPanning !== true &&
7311
+ this.isZooming !== true &&
7312
+ isCameraMoving !== true) {
7313
+ this._syncMotionGridLayer(false);
7314
+ }
7315
+
5315
7316
  _profileSection('webglRender');
5316
7317
  // ▼▼▼ [FIX] LOD 모드(MID/FAR)에서는 CSS3D 렌더러 호출 건너뛰기 ▼▼▼
5317
7318
  // CSS3D가 모두 display:none이어도 렌더러가 813개 객체를 순회하면서 시간 소비
@@ -5342,6 +7343,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5342
7343
  }
5343
7344
  _profileSection('css3dRender');
5344
7345
  if (canUseCss3dCameraFastPath) {
7346
+ this._syncMotionGridLayerForCameraNavigation(usedWebglCanvasCameraTransform);
5345
7347
  this.cssRenderer.renderCamera(this.camera);
5346
7348
  }
5347
7349
  _profileSection('css3dCamera');
@@ -5354,18 +7356,32 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5354
7356
  );
5355
7357
  const hasBusinessAutomationConnectionDraft =
5356
7358
  window.MindMapCss3DManager?.hasBusinessAutomationConnectionDraft?.() === true;
5357
- const shouldThrottleBusinessAutomationEdgesForCameraMotion =
5358
- isPureCss3dCameraMotionFrame === true &&
7359
+ const shouldTransformBusinessAutomationEdgesForCameraMotion =
7360
+ hasBusinessAutomationEdgeLayer &&
7361
+ hasBusinessAutomationConnectionDraft !== true &&
7362
+ window.MindMapCss3DManager?.syncBusinessAutomationEdgesForCameraMotion &&
7363
+ (this.isPanning === true || this.isZooming === true || isCameraMoving === true) &&
7364
+ !isNodeInteracting &&
7365
+ !hasNonCameraNavigationForcedUpdate &&
7366
+ !visibilityChangedThisFrame &&
7367
+ !lodVisualEpochChangedThisFrame &&
7368
+ window.MindMapCss3DManager.syncBusinessAutomationEdgesForCameraMotion(this) === true;
7369
+ const shouldThrottleBusinessAutomationEdgesForCameraMotion = shouldTransformBusinessAutomationEdgesForCameraMotion === true;
7370
+ const shouldDeferBusinessAutomationEdgesForCameraMotion = !!(
7371
+ hasBusinessAutomationEdgeLayer &&
5359
7372
  hasBusinessAutomationConnectionDraft !== true &&
7373
+ shouldThrottleBusinessAutomationEdgesForCameraMotion !== true &&
7374
+ (this.isPanning === true || this.isZooming === true || isCameraMoving === true) &&
5360
7375
  !isNodeInteracting &&
5361
- hasNonCameraNavigationForcedUpdate !== true &&
5362
- visibilityChangedThisFrame !== true &&
5363
- lodVisualEpochChangedThisFrame !== true &&
5364
- (this.frameCount % CAMERA_MOTION_WEBGL_FRAME_INTERVAL) !== 0;
7376
+ !hasNonCameraNavigationForcedUpdate &&
7377
+ !visibilityChangedThisFrame &&
7378
+ !lodVisualEpochChangedThisFrame
7379
+ );
5365
7380
  const shouldSyncBusinessAutomationEdges =
5366
7381
  hasBusinessAutomationEdgeLayer &&
5367
7382
  window.MindMapCss3DManager?.renderBusinessAutomationEdges &&
5368
7383
  shouldThrottleBusinessAutomationEdgesForCameraMotion !== true &&
7384
+ shouldDeferBusinessAutomationEdgesForCameraMotion !== true &&
5369
7385
  (
5370
7386
  this._businessAutomationEdgeRenderActive === true ||
5371
7387
  this.isPanning === true ||
@@ -5376,7 +7392,8 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5376
7392
  visibilityChangedThisFrame === true ||
5377
7393
  lodVisualEpochChangedThisFrame === true
5378
7394
  );
5379
- if (shouldThrottleBusinessAutomationEdgesForCameraMotion === true) {
7395
+ if (shouldThrottleBusinessAutomationEdgesForCameraMotion === true ||
7396
+ shouldDeferBusinessAutomationEdgesForCameraMotion === true) {
5380
7397
  this._businessAutomationEdgesDeferredForMotion = true;
5381
7398
  }
5382
7399
  if (shouldSyncBusinessAutomationEdges) {
@@ -5394,8 +7411,8 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5394
7411
  const shouldSyncTextOverlays =
5395
7412
  effectiveOverlayDirty === true ||
5396
7413
  (useTextOverlayV2
5397
- ? (hasOverlayV2DirtyState || shouldRefreshPassiveDomTextOverlay || shouldFinalizePassiveOverlayLayout || (hasOverlayFocus && shouldUpdateCss3d))
5398
- : (hasOverlayFocus && shouldUpdateCss3d));
7414
+ ? (hasOverlayV2DirtyState || shouldRefreshPassiveDomTextOverlay || shouldFinalizePassiveOverlayLayout || (hasMotionBlockingOverlayFocus && shouldUpdateCss3d))
7415
+ : (hasMotionBlockingOverlayFocus && shouldUpdateCss3d));
5399
7416
  const shouldAttemptPassiveOverlayCameraFastPath =
5400
7417
  useTextOverlayV2 &&
5401
7418
  shouldUsePassiveOverlayCameraFastPath;
@@ -5408,7 +7425,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5408
7425
  const shouldDeferFullPassiveOverlayFallback =
5409
7426
  shouldFallbackToFullPassiveOverlayRefresh &&
5410
7427
  shouldRenderPassiveDomTextOverlay &&
5411
- !hasOverlayFocus &&
7428
+ !hasMotionBlockingOverlayFocus &&
5412
7429
  (!isInLodMode || hasLiveInteractiveDomOverlay) &&
5413
7430
  (isInteractiveFrame || this.isPanning === true || this.isZooming === true || visibleOverlayCount > 80);
5414
7431
  if (useTextOverlayV2 && isPassiveOverlaySuppressed) {
@@ -5427,7 +7444,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5427
7444
  // full overlay layout update instead of waiting for the
5428
7445
  // next deferred refresh window.
5429
7446
  window.MindMapTextOverlayV2.updateFrame(this);
5430
- if (shouldRenderPassiveDomTextOverlay && !hasOverlayFocus && (!isInLodMode || hasLiveInteractiveDomOverlay)) {
7447
+ if (shouldRenderPassiveDomTextOverlay && !hasMotionBlockingOverlayFocus && (!isInLodMode || hasLiveInteractiveDomOverlay)) {
5431
7448
  this._lastPassiveOverlayFullRefreshAt = frameStart;
5432
7449
  this._overlayDebugRefreshAgeMs = 0;
5433
7450
  this._overlayDebugRefreshDeferred = false;
@@ -5458,8 +7475,8 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5458
7475
  const shouldDeferFloatingUiDuringMotion =
5459
7476
  (this.isPanning === true || this.isZooming === true || isCameraMoving === true) &&
5460
7477
  !isNodeInteracting &&
5461
- !hasForcedUpdate &&
5462
- !hasOverlayFocus &&
7478
+ !hasNonCameraNavigationForcedUpdate &&
7479
+ !hasMotionBlockingOverlayFocus &&
5463
7480
  !this.isWindowResizing;
5464
7481
  if (isMenuOverlayUiEnabled && window.MindMapMenuManager && shouldDeferFloatingUiDuringMotion !== true) {
5465
7482
  window.MindMapMenuManager.update();
@@ -5708,6 +7725,11 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5708
7725
  return;
5709
7726
  }
5710
7727
 
7728
+ if (!immediate && this._viewStatePersistTimer) {
7729
+ this._viewStatePersistNeedsFreshSnapshot = true;
7730
+ return;
7731
+ }
7732
+
5711
7733
  const snapshot = this.buildViewStateSnapshot();
5712
7734
  if (!snapshot) {
5713
7735
  return;
@@ -5720,6 +7742,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5720
7742
 
5721
7743
  this._pendingViewStateSnapshot = snapshot;
5722
7744
  this._pendingViewStateKey = snapshotKey;
7745
+ this._viewStatePersistNeedsFreshSnapshot = false;
5723
7746
 
5724
7747
  const isCameraStillSettling = () => {
5725
7748
  if (!this.camera) {
@@ -5757,10 +7780,24 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5757
7780
  const persist = async () => {
5758
7781
  this._viewStatePersistTimer = null;
5759
7782
  if (!immediate && isNavigationActive()) {
7783
+ this._viewStatePersistNeedsFreshSnapshot = true;
5760
7784
  armPersistTimer();
5761
7785
  return;
5762
7786
  }
5763
7787
 
7788
+ if (this._viewStatePersistNeedsFreshSnapshot === true) {
7789
+ const freshSnapshot = this.buildViewStateSnapshot();
7790
+ if (!freshSnapshot) {
7791
+ this._viewStatePersistNeedsFreshSnapshot = false;
7792
+ return;
7793
+ }
7794
+
7795
+ const freshSnapshotKey = this.buildViewStateKey(freshSnapshot);
7796
+ this._pendingViewStateSnapshot = freshSnapshot;
7797
+ this._pendingViewStateKey = freshSnapshotKey;
7798
+ this._viewStatePersistNeedsFreshSnapshot = false;
7799
+ }
7800
+
5764
7801
  const pendingSnapshot = this._pendingViewStateSnapshot;
5765
7802
  const pendingKey = this._pendingViewStateKey;
5766
7803
  if (!pendingSnapshot || !pendingKey || pendingKey === this._lastPersistedViewStateKey) {
@@ -5787,6 +7824,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5787
7824
  clearTimeout(this._viewStatePersistTimer);
5788
7825
  this._viewStatePersistTimer = null;
5789
7826
  }
7827
+ this._viewStatePersistNeedsFreshSnapshot = false;
5790
7828
  void persist();
5791
7829
  return;
5792
7830
  }
@@ -5838,6 +7876,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5838
7876
 
5839
7877
  hide() {
5840
7878
  this.scheduleViewStatePersist(true);
7879
+ this._syncMotionGridLayer(false);
5841
7880
  if (this.container) {
5842
7881
  if (window.MindMapMenuManager?.hideMenu) {
5843
7882
  window.MindMapMenuManager.hideMenu();
@@ -5935,11 +7974,23 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5935
7974
  cleanupAndDispose() {
5936
7975
  if (!this.container) return;
5937
7976
  this.pauseAnimationLoop();
7977
+ if (this._motionGridHideTimer) {
7978
+ clearTimeout(this._motionGridHideTimer);
7979
+ this._motionGridHideTimer = null;
7980
+ }
7981
+ if (this._motionGridLayer) {
7982
+ this._motionGridLayer.remove();
7983
+ this._motionGridLayer = null;
7984
+ this._motionGridVisible = false;
7985
+ this._lastMotionGridKey = '';
7986
+ this._lastMotionGridTransformKey = '';
7987
+ }
5938
7988
  removeCursorDomElement(this);
5939
7989
  if (this._viewStatePersistTimer) {
5940
7990
  clearTimeout(this._viewStatePersistTimer);
5941
7991
  this._viewStatePersistTimer = null;
5942
7992
  }
7993
+ this._viewStatePersistNeedsFreshSnapshot = false;
5943
7994
 
5944
7995
  MindMapInteractions.removeEventListeners(this);
5945
7996
  MindMapDnD.removeEventListeners(this);
@@ -6139,7 +8190,9 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
6139
8190
  if (this.lodRenderer?.requestVisualRefresh) {
6140
8191
  this.lodRenderer.requestVisualRefresh('project-thumbnail-capture');
6141
8192
  }
8193
+ this._clearWebglCanvasCameraTransform();
6142
8194
  this.renderer.render(this.scene, this.camera);
8195
+ this._recordWebglRenderedCameraState();
6143
8196
  if (this.cssRenderer && typeof this.cssRenderer.render === 'function') {
6144
8197
  this.cssRenderer.render(this.cssScene || this.scene, this.camera);
6145
8198
  }