@mindexec/cli 0.2.219 → 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 (22) hide show
  1. package/package.json +1 -1
  2. package/scripts/remote-fleet-render-smoke.mjs +11 -0
  3. package/wwwroot/_content/MindExecution.Shared/js/background-themes.js +28 -113
  4. package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +1910 -147
  5. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +360 -93
  6. package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +106 -43
  7. package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +77 -3
  8. package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +83 -14
  9. package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-overlay-v2.js +5 -0
  10. package/wwwroot/_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js +2 -2
  11. package/wwwroot/_framework/{MindExecution.Core.c9wgwsw9z6.dll → MindExecution.Core.xsad4wu36e.dll} +0 -0
  12. package/wwwroot/_framework/{MindExecution.Plugins.Admin.29mytzdaun.dll → MindExecution.Plugins.Admin.m5d94977um.dll} +0 -0
  13. package/wwwroot/_framework/{MindExecution.Plugins.Business.g8txk1zzic.dll → MindExecution.Plugins.Business.ntqdn1hta3.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Plugins.Concept.u9kzsgf64o.dll → MindExecution.Plugins.Concept.mqiyx4db4r.dll} +0 -0
  15. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.kibqg6rvqh.dll → MindExecution.Plugins.PlanMaster.fnortumnuu.dll} +0 -0
  16. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.089r64n2hv.dll → MindExecution.Plugins.YouTube.86osu6wpgh.dll} +0 -0
  17. package/wwwroot/_framework/{MindExecution.Shared.tbutfszqeu.dll → MindExecution.Shared.wg2otcw4bv.dll} +0 -0
  18. package/wwwroot/_framework/{MindExecution.Web.7axuoygrwi.dll → MindExecution.Web.gfio3kfxrd.dll} +0 -0
  19. package/wwwroot/_framework/blazor.boot.json +17 -17
  20. package/wwwroot/index.html +3 -3
  21. package/wwwroot/service-worker-assets.js +27 -27
  22. 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 = '20260619-ua-immediate-camera-v629';
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 = '20260619-ua-immediate-camera-v629';
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 = 10;
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',
@@ -306,8 +318,9 @@ const MINDMAP_CORE_BUILD_ID = '20260619-ua-immediate-camera-v629';
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;
@@ -376,8 +389,27 @@ const MINDMAP_CORE_BUILD_ID = '20260619-ua-immediate-camera-v629';
376
389
  tintVideoLayoutInNear: false,
377
390
  disableNearVideoGlow: false
378
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
+ }
379
401
 
380
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
+
381
413
  if (typeof document === 'undefined' || !document.body) {
382
414
  return;
383
415
  }
@@ -390,15 +422,36 @@ const MINDMAP_CORE_BUILD_ID = '20260619-ua-immediate-camera-v629';
390
422
  }
391
423
  _mindCanvasPerfClassState.target = targetElement;
392
424
 
393
- if (_mindCanvasPerfClassState.moving !== moving) {
394
- targetElement.classList.toggle('mindcanvas-is-moving', moving);
395
- _mindCanvasPerfClassState.moving = moving;
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;
396
431
  }
397
432
 
398
- if (_mindCanvasPerfClassState.dense !== dense) {
399
- targetElement.classList.toggle('mindcanvas-is-dense', dense);
400
- _mindCanvasPerfClassState.dense = dense;
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;
442
+ }
443
+
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;
401
451
  }
452
+
453
+ module._themeCameraMotionDeferred = true;
454
+ return true;
402
455
  }
403
456
 
404
457
  function syncRenderDebugBodyClasses(flags = {}) {
@@ -455,7 +508,8 @@ const MINDMAP_CORE_BUILD_ID = '20260619-ua-immediate-camera-v629';
455
508
  freezeVisibilityDuringMotion: false,
456
509
  freezeLodDuringMotion: false,
457
510
  enableFramePerfProbe: false,
458
- enableCss3dWillChange: true,
511
+ sampleWebglDuringCameraMotion: false,
512
+ enableCss3dWillChange: false,
459
513
  enableThemeAnimation: true,
460
514
  enableMenuOverlayUi: true,
461
515
  enableMultiSelectOverlayUi: true,
@@ -851,10 +905,170 @@ const MINDMAP_CORE_BUILD_ID = '20260619-ua-immediate-camera-v629';
851
905
  };
852
906
  }
853
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
+
854
1063
  function resetFramePerfPeakProfile(module) {
855
1064
  const peakProfile = createEmptyFramePerfPeakProfile();
856
1065
  if (module) {
857
1066
  module._framePerfPeakProfile = peakProfile;
1067
+ module._framePathStats = createEmptyFramePathStats(
1068
+ typeof performance !== 'undefined' && typeof performance.now === 'function'
1069
+ ? performance.now()
1070
+ : Date.now()
1071
+ );
858
1072
  }
859
1073
  return peakProfile;
860
1074
  }
@@ -865,6 +1079,7 @@ const MINDMAP_CORE_BUILD_ID = '20260619-ua-immediate-camera-v629';
865
1079
  }
866
1080
 
867
1081
  const peakProfile = module._framePerfPeakProfile || resetFramePerfPeakProfile(module);
1082
+ updateFramePathStats(module, frameProfile, updatedAt);
868
1083
  let changed = false;
869
1084
  const durationMs = Number(frameProfile.durationMs || 0);
870
1085
  if (durationMs > Number(peakProfile.durationMs || 0)) {
@@ -898,6 +1113,445 @@ const MINDMAP_CORE_BUILD_ID = '20260619-ua-immediate-camera-v629';
898
1113
  }
899
1114
  }
900
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
+
901
1555
  function getRenderDebugMetricsSnapshot() {
902
1556
  const snapshotStart = (typeof performance !== 'undefined' && typeof performance.now === 'function')
903
1557
  ? performance.now()
@@ -910,6 +1564,7 @@ const MINDMAP_CORE_BUILD_ID = '20260619-ua-immediate-camera-v629';
910
1564
  lastFrameDurationMs: 0,
911
1565
  lastFramePerfProfile: null,
912
1566
  lastFramePerfPeakProfile: null,
1567
+ framePathStats: null,
913
1568
  updatedAt: 0,
914
1569
  visibleNodes: 0,
915
1570
  isZooming: false,
@@ -985,9 +1640,24 @@ const MINDMAP_CORE_BUILD_ID = '20260619-ua-immediate-camera-v629';
985
1640
  : (flags.enablePassiveOverlayForVisibleNodes !== false
986
1641
  ? 'all-visible passive'
987
1642
  : 'selected/editing only');
988
- 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'
989
1648
  ? moduleInstance.lodRenderer.getResidentDirtyState(moduleInstance.camera, moduleInstance)
990
- : 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
+ );
991
1661
 
992
1662
  const snapshotEnd = (typeof performance !== 'undefined' && typeof performance.now === 'function')
993
1663
  ? performance.now()
@@ -1007,6 +1677,7 @@ const MINDMAP_CORE_BUILD_ID = '20260619-ua-immediate-camera-v629';
1007
1677
  lastFrameDurationMs: Number(moduleInstance._lastFrameDurationMs || 0),
1008
1678
  lastFramePerfProfile: moduleInstance._lastFramePerfProfile || null,
1009
1679
  lastFramePerfPeakProfile: moduleInstance._framePerfPeakProfile || null,
1680
+ framePathStats: moduleInstance._framePathStats || null,
1010
1681
  updatedAt: Number(moduleInstance._lastFrameMetricsAt || 0),
1011
1682
  visibleNodes: Number(moduleInstance._visibleIds?.size || moduleInstance._prevVisibleIds?.size || 0),
1012
1683
  isZooming: moduleInstance.isZooming === true,
@@ -2583,6 +3254,9 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
2583
3254
  this.lodUpdateQueue = []; // Initialize lodUpdateQueue
2584
3255
  this.isPanning = false;
2585
3256
  this._currentDpr = null;
3257
+ this._lastWebglRenderedCameraState = null;
3258
+ this._webglCanvasMotionTransformActive = false;
3259
+ this._lastWebglCanvasMotionTransformKey = '';
2586
3260
  this._lastViewportWidth = 0;
2587
3261
  this._lastViewportHeight = 0;
2588
3262
  this._lastViewportDpr = 0;
@@ -2620,6 +3294,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
2620
3294
  this._pendingViewStateSnapshot = null;
2621
3295
  this._pendingViewStateKey = null;
2622
3296
  this._lastPersistedViewStateKey = null;
3297
+ this._viewStatePersistNeedsFreshSnapshot = false;
2623
3298
  this._viewStatePersistDelayMs = 650;
2624
3299
  this._viewStatePersistScheduledAt = 0;
2625
3300
  this._postLoadVisualSyncToken = 0;
@@ -2719,6 +3394,13 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
2719
3394
  this._installForceUpdateWakeHook();
2720
3395
  this._forceUpdateFrames = 0;
2721
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 = '';
2722
3404
  this._animationIdleTimerId = null;
2723
3405
  this._animationLoopCallback = null;
2724
3406
  this._animationLoopExecuting = false;
@@ -2758,6 +3440,195 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
2758
3440
  // ▼▼▼ [New] Node snap function - use Math.round to snap to nearest grid ▼▼▼
2759
3441
  snapNodeToGrid(v) { return Math.round(v / this.GRID_SIZE) * this.GRID_SIZE; }
2760
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
+
2761
3632
  _installForceUpdateWakeHook() {
2762
3633
  if (Object.getOwnPropertyDescriptor(this, '_forceUpdateFrames')?.get) {
2763
3634
  return;
@@ -3187,9 +4058,12 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3187
4058
  console.error('[mind-map-core.js] Container not found!');
3188
4059
  return;
3189
4060
  }
3190
- this.container.style.display = 'block';
3191
- this._lastContainerWidth = this.container.clientWidth || 0;
3192
- 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));
3193
4067
  const THREE = globalThis.THREE;
3194
4068
  this.scene = new THREE.Scene();
3195
4069
  this.cssScene = new THREE.Scene();
@@ -3425,10 +4299,11 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3425
4299
  const newTheme = window.MindMapThemes.getTheme(themeName);
3426
4300
  if (!newTheme) { console.warn(`[mind-map-core.js] Theme '${themeName}' not found.`); return; }
3427
4301
  if (this.currentThemeObject) {
3428
- const oldTheme = window.MindMapThemes.getTheme(this.currentThemeName);
3429
- try { oldTheme?.dispose(this.currentThemeObject); } catch { }
3430
- if (this.scene) this.scene.remove(this.currentThemeObject);
3431
- }
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);
3432
4307
  this.currentThemeObject = newTheme.create(this.scene, this.renderer);
3433
4308
  if (this.currentThemeObject) this.scene.add(this.currentThemeObject);
3434
4309
  this.currentThemeName = themeName;
@@ -3492,6 +4367,76 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3492
4367
  this.renderer.setSize(width, height, false);
3493
4368
  }
3494
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
+
3495
4440
  refreshAgentConsoleVisibilityTracking(force = false) {
3496
4441
  if (!force && this._agentConsoleVisibilityTrackingDirty !== true) {
3497
4442
  return;
@@ -4476,10 +5421,12 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4476
5421
  ? requestIdleCallback : (cb) => setTimeout(cb, 0);
4477
5422
  scheduleWillChangeOff(() => {
4478
5423
  // 패닝/줌이 다시 시작됐으면 건너뜀
4479
- if (!moduleRef.isZooming && !moduleRef.isPanning) {
4480
- if (window.MindMapCss3DManager?.setWillChange) {
4481
- window.MindMapCss3DManager.setWillChange(false, moduleRef);
4482
- }
5424
+ if (!moduleRef.isZooming &&
5425
+ !moduleRef.isPanning &&
5426
+ moduleRef.renderDebugFlags?.enableCss3dWillChange === true) {
5427
+ if (window.MindMapCss3DManager?.setWillChange) {
5428
+ window.MindMapCss3DManager.setWillChange(false, moduleRef);
5429
+ }
4483
5430
  }
4484
5431
  });
4485
5432
  // ▲▲▲ [Fix] ▲▲▲
@@ -4488,8 +5435,12 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4488
5435
  _profileSection('state');
4489
5436
 
4490
5437
  if (isCameraMoving !== this.prevIsCameraMoving) {
4491
- // Update will-change state
4492
- 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) {
4493
5444
  window.MindMapCss3DManager.setWillChange(isCameraMoving, this);
4494
5445
  }
4495
5446
 
@@ -4540,6 +5491,19 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4540
5491
  this._overlayDirty = true;
4541
5492
  }
4542
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
+
4543
5507
  if (this.lodRenderer?.hasPendingLineDetailRestore?.(this.camera, this) === true) {
4544
5508
  this._forceUpdateFrames = Math.max(this._forceUpdateFrames || 0, 6);
4545
5509
  }
@@ -4567,8 +5531,51 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4567
5531
  // 3. Unified Visibility & LOD Pass (One Loop to Rule Them All)
4568
5532
  // ----------------------------------------------------------------
4569
5533
  this.frameCount = (this.frameCount || 0) + 1;
4570
- const hasPendingNearCssWarmup = this.useLODRendering &&
4571
- 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;
4572
5579
  const isResidentMidFarLodFrame = this.useLODRendering &&
4573
5580
  this.lodRenderer?.usesResidentMidFarInstances?.(this.camera) === true;
4574
5581
  const isFullResMode = this.lodRenderer?.isInLODMode !== true;
@@ -4622,9 +5629,15 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4622
5629
  hasForcedUpdate &&
4623
5630
  hasOnlyCameraNavigationForcedUpdate !== true;
4624
5631
  const isLodModeActive = this.lodRenderer?.isInLODMode === true;
5632
+ if (shouldDeferMotionWarmupAndResidentChecks === true && isResidentMidFarLodFrame === true) {
5633
+ this._wasLodMotionFrozenLastFrame = true;
5634
+ this._preserveVisibleSetThisFrame = true;
5635
+ }
4625
5636
  const residentDirty = isResidentMidFarLodFrame &&
5637
+ shouldDeferMotionWarmupAndResidentChecks !== true &&
4626
5638
  this.lodRenderer?.hasResidentDirtyWork?.(this.camera, this) === true;
4627
5639
  const residentBandChanging = isResidentMidFarLodFrame &&
5640
+ shouldDeferMotionWarmupAndResidentChecks !== true &&
4628
5641
  this.lodRenderer?.willLodBandChange?.(this.camera) === true;
4629
5642
  let canDeferResidentDirtyIdleFrame = false;
4630
5643
  if (isResidentMidFarLodFrame &&
@@ -4698,31 +5711,41 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4698
5711
  }
4699
5712
  const isInteractiveFrame = this.isZooming || this.isPanning || isCameraMoving || isNodeInteracting || !!this.isWindowResizing;
4700
5713
  const useTextOverlayV2 = !isBoardLoading && this.useTextOverlayV2 === true && !!window.MindMapTextOverlayV2;
5714
+ const isPassiveDomOverlayRuntimeEnabled =
5715
+ useTextOverlayV2 &&
5716
+ window.MindMapTextOverlayV2?.isPassiveDomOverlayEnabled?.() === true;
4701
5717
  const renderDebugFlags = activeRenderDebugFlags;
4702
5718
  const isCss3dEnabled = !isBoardLoading && renderDebugFlags.enableCss3d !== false;
4703
5719
 
4704
- const hasKnownTextOverlayFocus = !!(
5720
+ const hasSelectableTextOverlayFocus = !!(
4705
5721
  String(this._textOverlayV2State?.selectionNodeId || '').trim() ||
5722
+ String(this._selectableTextOverlayNodeId || '').trim()
5723
+ );
5724
+ const hasEditingTextOverlayFocus = !!(
4706
5725
  String(this._textOverlayV2State?.editing?.nodeId || '').trim() ||
4707
- String(this._selectableTextOverlayNodeId || '').trim() ||
4708
5726
  String(this._editingOverlayState?.nodeId || '').trim()
4709
5727
  );
4710
- const hasPendingCss3dTransformWork = !!(
5728
+ const hasPendingCss3dStructuralTransformWork = !!(
4711
5729
  (this._cssTransformDirtyIds?.size || 0) > 0 ||
4712
- this._cssParentRepairNeeded === true ||
5730
+ this._cssParentRepairNeeded === true
5731
+ );
5732
+ const hasPendingCss3dVisualWork = !!(
4713
5733
  this._css3dVisualChangedThisFrame === true ||
4714
5734
  this._css3dVisualChangedSinceLastRender === true
4715
5735
  );
4716
- const canUseUaImmediateCameraOnlyFrame = !!(
5736
+ const canUseUaImmediateCameraOnlyFrame = CAMERA_MOTION_VISUAL_ONLY_FAST_PATH_ENABLED === true && !!(
4717
5737
  shouldPreferCameraOnlyMotionPipeline === true &&
4718
5738
  isCss3dEnabled === true &&
4719
5739
  !!this.cssRenderer &&
4720
5740
  typeof this.cssRenderer.renderCamera === 'function' &&
5741
+ !!this.renderer &&
5742
+ !!this.scene &&
5743
+ !!this.camera &&
4721
5744
  isLodModeActive !== true &&
4722
5745
  isCameraMoving === true &&
4723
5746
  this.isSelecting !== true &&
4724
- !hasKnownTextOverlayFocus &&
4725
- !hasPendingCss3dTransformWork &&
5747
+ !hasEditingTextOverlayFocus &&
5748
+ !hasPendingCss3dStructuralTransformWork &&
4726
5749
  !isBoardLoading
4727
5750
  );
4728
5751
  if (canUseUaImmediateCameraOnlyFrame) {
@@ -4731,50 +5754,119 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4731
5754
  this._wasLodMotionFrozenLastFrame = false;
4732
5755
  this._fullResVisibilityReconcilePending = true;
4733
5756
  this._preserveVisibleSetThisFrame = true;
4734
- this._lastVisibilityCullingSkipReason = 'ua-immediate-camera-only';
4735
- if (this._businessAutomationEdgeVisualLayer?.isConnected ||
4736
- this._businessAutomationEdgeLayer?.isConnected) {
4737
- this._businessAutomationEdgesDeferredForMotion = true;
4738
- }
5757
+ this._lastVisibilityCullingSkipReason = 'ua-visual-only-motion';
4739
5758
  if (this._overlayDirty === true) {
4740
5759
  this._deferPassiveOverlayRefresh = true;
4741
5760
  }
5761
+ if (hasSelectableTextOverlayFocus) {
5762
+ this._deferPassiveOverlayRefresh = true;
5763
+ }
5764
+ if (hasPendingCss3dVisualWork) {
5765
+ this._css3dVisualDeferredForMotion = true;
5766
+ }
4742
5767
 
4743
- const approxVisibleNodeCountImmediate = Number(this._visibleIds?.size || this._prevVisibleIds?.size || 0);
4744
- const totalNodeCountImmediate = Number(this.nodeObjectsById?.size || 0);
4745
- syncMindCanvasPerfClasses({
4746
- moving: true,
4747
- dense:
4748
- approxVisibleNodeCountImmediate >= DENSE_VISIBLE_NODE_THRESHOLD ||
4749
- totalNodeCountImmediate >= DENSE_TOTAL_NODE_THRESHOLD
4750
- });
4751
-
4752
- if ((this.frameCount % CAMERA_MOTION_WEBGL_FRAME_INTERVAL) === 0) {
4753
- updateCursorDomElement(this);
4754
- if (renderDebugFlags.enableWebglRender !== false && this.renderer && this.scene && this.camera) {
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();
4755
5829
  this.renderer.render(this.scene, this.camera);
5830
+ this._recordWebglRenderedCameraState();
4756
5831
  const renderInfo = this.renderer?.info?.render || null;
4757
5832
  this._lastWebglRenderInfo = renderInfo ? {
4758
5833
  calls: Number(renderInfo.calls || 0),
4759
5834
  triangles: Number(renderInfo.triangles || 0),
4760
5835
  points: Number(renderInfo.points || 0),
4761
5836
  lines: Number(renderInfo.lines || 0),
4762
- uaImmediateCameraOnly: true
5837
+ uaVisualOnlyMotion: true,
5838
+ webglCanvasCameraBootstrap: true
4763
5839
  } : null;
4764
5840
  }
4765
- } else if (renderDebugFlags.enableWebglRender !== false) {
4766
- this._lastWebglRenderInfo = {
4767
- calls: 0,
4768
- triangles: 0,
4769
- points: 0,
4770
- lines: 0,
4771
- skipped: true,
4772
- uaImmediateCameraOnly: true
4773
- };
4774
5841
  }
5842
+ this._syncMotionGridLayerForCameraNavigation(usedWebglCanvasCameraTransformImmediate);
4775
5843
 
4776
5844
  this.cssRenderer.renderCamera(this.camera);
4777
- _profileSection('uaImmediateCameraOnly');
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');
4778
5870
 
4779
5871
  const frameEnd = performance.now();
4780
5872
  const frameDuration = frameEnd - frameStart;
@@ -4803,22 +5895,22 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4803
5895
  residentMidFarStable: false,
4804
5896
  residentDirty: false,
4805
5897
  residentBandChanging: false,
4806
- viewSyncMode: 'ua-immediate-camera-only',
5898
+ viewSyncMode: 'ua-visual-only-motion',
4807
5899
  lodUpdateCalled: false,
4808
5900
  lodUpdateSkipped: true,
4809
- lodUpdateSkipReason: 'ua-immediate-camera-only',
5901
+ lodUpdateSkipReason: 'ua-visual-only-motion',
4810
5902
  lodSkipReason: '',
4811
5903
  lodDirtyReason: '',
4812
5904
  lodDirtyState: null,
4813
5905
  viewSyncBreakdown: null,
4814
5906
  shouldRunVisibilityCulling: false,
4815
5907
  visibilityStableSkip: true,
4816
- visibilitySkipReason: 'ua-immediate-camera-only',
5908
+ visibilitySkipReason: 'ua-visual-only-motion',
4817
5909
  incrementalPanVisibility: false,
4818
5910
  shouldSyncCss3dTransforms: false,
4819
5911
  shouldRenderCss3d: false,
4820
5912
  usedCss3dCameraFastPath: true,
4821
- uaImmediateCameraOnly: true,
5913
+ uaVisualOnlyMotion: true,
4822
5914
  shouldSyncTextOverlays: false,
4823
5915
  usedPassiveOverlayCameraFastPath: false,
4824
5916
  passiveOverlaySuppressed: this._suppressPassiveDomOverlayDuringZoom === true,
@@ -4841,24 +5933,386 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4841
5933
  const isLodUpdateEnabled = renderDebugFlags.enableLodUpdate !== false;
4842
5934
  const isWebglRenderEnabled = !isBoardLoading && renderDebugFlags.enableWebglRender !== false;
4843
5935
  const isThemeAnimationEnabled = !isBoardLoading && renderDebugFlags.enableThemeAnimation !== false;
4844
- const themeAnimationMode =
4845
- window.MindMapThemes?.getAnimationMode?.(this.currentThemeName, this.currentThemeObject) ||
4846
- 'none';
4847
- const isContinuousTheme = themeAnimationMode === 'continuous';
4848
5936
  const isCameraNavigationOnlyInteractiveFrame =
4849
5937
  (this.isPanning === true || this.isZooming === true || isCameraMoving === true) &&
4850
5938
  !isNodeInteracting &&
4851
5939
  !this.isWindowResizing &&
4852
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;
4853
6302
  const allowContinuousThemeAnimationThisFrame =
4854
- isContinuousTheme !== true ||
4855
- (isInteractiveFrame === true && !isCameraNavigationOnlyInteractiveFrame) ||
4856
- (shouldUpdate === true && !isCameraNavigationOnlyInteractiveFrame) ||
4857
- hasNonCameraNavigationForcedUpdate === true ||
4858
- isBoardLoading === true ||
4859
- shouldUseAnimation60Fps === true ||
4860
- shouldUseAnimationLowFpsFrame === true ||
4861
- 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
+ }
4862
6316
  const isMenuOverlayUiEnabled = !isBoardLoading && renderDebugFlags.enableMenuOverlayUi !== false;
4863
6317
  const isMultiSelectOverlayUiEnabled = !isBoardLoading && renderDebugFlags.enableMultiSelectOverlayUi !== false;
4864
6318
  _profileSection('pipelineScheduling');
@@ -4886,17 +6340,15 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4886
6340
  shouldPreferCameraOnlyMotionPipeline === true &&
4887
6341
  isCss3dEnabled === true &&
4888
6342
  !!this.cssRenderer &&
4889
- !hasFocusedTextOverlay &&
4890
6343
  !hasEditingOverlay;
4891
- const canUseUaEarlyCameraOnlyFrame = !!(
6344
+ const canUseUaEarlyCameraOnlyFrame = CAMERA_MOTION_PARTIAL_FRAME_SKIP_ENABLED === true && !!(
4892
6345
  isUaStyleCameraTransformFrame === true &&
4893
6346
  isFullResMode === true &&
4894
6347
  isResidentMidFarLodFrame !== true &&
4895
6348
  isCameraMoving === true &&
4896
6349
  this.isSelecting !== true &&
4897
6350
  typeof this.cssRenderer.renderCamera === 'function' &&
4898
- this._css3dVisualChangedThisFrame !== true &&
4899
- this._css3dVisualChangedSinceLastRender !== true
6351
+ !hasPendingCss3dStructuralTransformWork
4900
6352
  );
4901
6353
  if (canUseUaEarlyCameraOnlyFrame) {
4902
6354
  this._fullResMotionFrozen = true;
@@ -4909,20 +6361,35 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4909
6361
  this._businessAutomationEdgeLayer?.isConnected) {
4910
6362
  this._businessAutomationEdgesDeferredForMotion = true;
4911
6363
  }
6364
+ if (hasPendingCss3dVisualWork) {
6365
+ this._css3dVisualDeferredForMotion = true;
6366
+ }
4912
6367
 
4913
- const approxVisibleNodeCountEarly = Number(this._visibleIds?.size || this._prevVisibleIds?.size || 0);
4914
- const totalNodeCountEarly = Number(this.nodeObjectsById?.size || 0);
4915
- syncMindCanvasPerfClasses({
4916
- moving: true,
4917
- dense:
4918
- approxVisibleNodeCountEarly >= DENSE_VISIBLE_NODE_THRESHOLD ||
4919
- totalNodeCountEarly >= DENSE_TOTAL_NODE_THRESHOLD
4920
- });
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
+ }
4921
6382
 
4922
- if ((this.frameCount % CAMERA_MOTION_WEBGL_FRAME_INTERVAL) === 0) {
6383
+ const shouldSampleWebglDuringCameraMotion =
6384
+ renderDebugFlags.sampleWebglDuringCameraMotion === true &&
6385
+ (this.frameCount % CAMERA_MOTION_WEBGL_FRAME_INTERVAL) === 0;
6386
+ let usedWebglCanvasCameraTransformEarly = false;
6387
+ if (shouldSampleWebglDuringCameraMotion) {
4923
6388
  updateCursorDomElement(this);
4924
6389
  if (isWebglRenderEnabled && this.renderer && this.scene && this.camera) {
6390
+ this._clearWebglCanvasCameraTransform();
4925
6391
  this.renderer.render(this.scene, this.camera);
6392
+ this._recordWebglRenderedCameraState();
4926
6393
  const renderInfo = this.renderer?.info?.render || null;
4927
6394
  this._lastWebglRenderInfo = renderInfo ? {
4928
6395
  calls: Number(renderInfo.calls || 0),
@@ -4933,16 +6400,60 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4933
6400
  } : null;
4934
6401
  }
4935
6402
  } else if (isWebglRenderEnabled) {
4936
- this._lastWebglRenderInfo = {
4937
- calls: 0,
4938
- triangles: 0,
4939
- points: 0,
4940
- lines: 0,
4941
- skipped: true,
4942
- uaEarlyCameraOnly: true
4943
- };
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
+ }
4944
6454
  }
4945
6455
 
6456
+ this._syncMotionGridLayerForCameraNavigation(usedWebglCanvasCameraTransformEarly);
4946
6457
  this.cssRenderer.renderCamera(this.camera);
4947
6458
  _profileSection('uaEarlyCameraOnly');
4948
6459
 
@@ -5006,21 +6517,126 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5006
6517
  this._scheduleAnimationLoop(loop, 0);
5007
6518
  return;
5008
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
+ }
5009
6622
  const isMotionFreezeEligible =
5010
6623
  (this.isZooming === true || this.isPanning === true || isCameraMoving === true) &&
5011
6624
  !isNodeInteracting &&
5012
6625
  !hasNonCameraNavigationForcedUpdate &&
5013
6626
  !hasPendingNearCssWarmup &&
5014
6627
  !this.isWindowResizing;
6628
+ const needsPerfClassSync = shouldSyncMindCanvasPerfClasses();
5015
6629
  const approxVisibleNodeCount = Number(this._visibleIds?.size || this._prevVisibleIds?.size || 0);
5016
6630
  const totalNodeCount = Number(this.nodeObjectsById?.size || 0);
5017
6631
  const shouldUseDensePerfClass =
5018
6632
  approxVisibleNodeCount >= DENSE_VISIBLE_NODE_THRESHOLD ||
5019
6633
  totalNodeCount >= DENSE_TOTAL_NODE_THRESHOLD;
5020
- syncMindCanvasPerfClasses({
5021
- moving: isInteractiveFrame === true,
5022
- dense: shouldUseDensePerfClass
5023
- });
6634
+ if (needsPerfClassSync) {
6635
+ syncMindCanvasPerfClasses({
6636
+ moving: isInteractiveFrame === true,
6637
+ dense: shouldUseDensePerfClass
6638
+ });
6639
+ }
5024
6640
  const isMotionHeavyScene =
5025
6641
  approxVisibleNodeCount >= AUTO_FREEZE_VISIBLE_NODE_THRESHOLD ||
5026
6642
  Number(this._lastFrameDurationMs || 0) >= AUTO_FREEZE_FRAME_MS ||
@@ -5049,7 +6665,6 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5049
6665
  const shouldFreezeFullResDuringMotion =
5050
6666
  isFullResMode &&
5051
6667
  isMotionFreezeEligible &&
5052
- !hasFocusedTextOverlay &&
5053
6668
  !hasEditingOverlay &&
5054
6669
  (
5055
6670
  isUaStyleCameraTransformFrame === true ||
@@ -5342,10 +6957,10 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5342
6957
  const isInLodMode = this.lodRenderer?.isInLODMode === true;
5343
6958
  const shouldRenderCss3dInLod = isInLodMode && this.lodRenderer?.shouldRenderCss3dInLod?.() === true;
5344
6959
  const shouldRenderPassiveDomTextOverlay =
5345
- useTextOverlayV2 &&
6960
+ isPassiveDomOverlayRuntimeEnabled &&
5346
6961
  window.MindMapTextOverlayV2?.shouldRenderPassiveDomOverlays?.(this) === true;
5347
6962
  const hasLiveInteractiveDomOverlay =
5348
- useTextOverlayV2 &&
6963
+ isPassiveDomOverlayRuntimeEnabled &&
5349
6964
  window.MindMapTextOverlayV2?.hasLiveInteractiveOverlay?.(this) === true;
5350
6965
  const hasPassiveDomOverlaySurface =
5351
6966
  shouldRenderPassiveDomTextOverlay ||
@@ -5360,6 +6975,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5360
6975
  )
5361
6976
  );
5362
6977
  const hasOverlayFocus = hasFocusedTextOverlay || hasEditingOverlay;
6978
+ const hasMotionBlockingOverlayFocus = hasEditingOverlay;
5363
6979
  const visibleOverlayCount = Math.max(
5364
6980
  Number(this._visibleIds?.size || 0),
5365
6981
  Number(this._prevVisibleIds?.size || 0)
@@ -5370,7 +6986,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5370
6986
  !isInLodMode &&
5371
6987
  !isCtrlWheelVerticalScrolling &&
5372
6988
  !isCameraNavigationOverlayPriority &&
5373
- !hasOverlayFocus &&
6989
+ !hasMotionBlockingOverlayFocus &&
5374
6990
  !isNodeInteracting &&
5375
6991
  !hasNonCameraNavigationForcedUpdate &&
5376
6992
  !hasPendingNearCssWarmup &&
@@ -5427,7 +7043,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5427
7043
  this._deferPassiveOverlayRefresh === true &&
5428
7044
  shouldDeferInteractivePassiveOverlayRefresh &&
5429
7045
  !isInLodMode &&
5430
- !hasOverlayFocus &&
7046
+ !hasMotionBlockingOverlayFocus &&
5431
7047
  !isNodeInteracting &&
5432
7048
  !hasNonCameraNavigationForcedUpdate &&
5433
7049
  !this.isWindowResizing;
@@ -5450,7 +7066,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5450
7066
  !isInLodMode &&
5451
7067
  (this.isZooming === true || this.isPanning === true || isCameraMoving) &&
5452
7068
  !isCtrlWheelVerticalScrolling &&
5453
- !hasOverlayFocus &&
7069
+ !hasMotionBlockingOverlayFocus &&
5454
7070
  !hasOverlayV2DirtyState &&
5455
7071
  !effectiveOverlayDirty &&
5456
7072
  isCameraOnlyOverlayFrame &&
@@ -5469,11 +7085,14 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5469
7085
  useTextOverlayV2 &&
5470
7086
  hasPassiveDomOverlaySurface &&
5471
7087
  !isPassiveOverlaySuppressed &&
5472
- !hasOverlayFocus &&
7088
+ !hasMotionBlockingOverlayFocus &&
5473
7089
  !hasOverlayV2DirtyState &&
5474
7090
  !this.isWindowResizing &&
5475
7091
  (this.isZooming === true || this.isPanning === true || isCameraMoving);
5476
- window.MindMapTextOverlayV2?.setMotionHint?.(this, shouldUsePassiveOverlayMotionHint);
7092
+ if (isPassiveDomOverlayRuntimeEnabled ||
7093
+ this._textOverlayV2State?.motionHintActive === true) {
7094
+ window.MindMapTextOverlayV2?.setMotionHint?.(this, shouldUsePassiveOverlayMotionHint);
7095
+ }
5477
7096
  const isPureCss3dCameraMotionFrame = !!(
5478
7097
  this.cssRenderer &&
5479
7098
  isCss3dEnabled &&
@@ -5484,12 +7103,14 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5484
7103
  !isNodeInteracting &&
5485
7104
  !hasNonCameraNavigationForcedUpdate &&
5486
7105
  !hasPendingNearCssWarmup &&
5487
- !hasOverlayFocus &&
5488
- this._css3dVisualChangedThisFrame !== true &&
5489
- this._css3dVisualChangedSinceLastRender !== true &&
7106
+ !hasMotionBlockingOverlayFocus &&
7107
+ !hasPendingCss3dStructuralTransformWork &&
5490
7108
  !lodVisualEpochChangedThisFrame &&
5491
7109
  this.isWindowResizing !== true
5492
7110
  );
7111
+ if (isPureCss3dCameraMotionFrame === true && hasPendingCss3dVisualWork) {
7112
+ this._css3dVisualDeferredForMotion = true;
7113
+ }
5493
7114
 
5494
7115
  // 4. CSS3D Position Sync
5495
7116
  // ----------------------------------------------------------------
@@ -5511,19 +7132,29 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5511
7132
  !isCameraMoving &&
5512
7133
  !isNodeInteracting &&
5513
7134
  !this.isWindowResizing;
5514
- window.MindMapNodes?.updateAnimatedImageStatusTextures?.(this, frameStart, {
5515
- allowAnimation: shouldAnimateImageStatusTextures
5516
- });
7135
+ if (this._activeImageStatusTextures?.size > 0) {
7136
+ window.MindMapNodes?.updateAnimatedImageStatusTextures?.(this, frameStart, {
7137
+ allowAnimation: shouldAnimateImageStatusTextures
7138
+ });
7139
+ }
5517
7140
 
5518
7141
  // 5. Cursor DOM position
5519
7142
  // ----------------------------------------------------------------
5520
- 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) {
5521
7147
  updateCursorDomElement(this);
5522
7148
  }
5523
7149
 
5524
7150
  // 6. Theme Animation
5525
7151
  // ----------------------------------------------------------------
5526
7152
  const isGpuParticleTheme = this.currentThemeName === 'Space3D' || this.currentThemeName === 'SpiritFlow';
7153
+ const shouldUseMotionGridThemeSubstitute =
7154
+ false &&
7155
+ isCameraNavigationOnlyInteractiveFrame === true &&
7156
+ this._isMotionGridThemeEnabled() === true &&
7157
+ !isBoardLoading;
5527
7158
  const shouldForceGpuParticleAnimation =
5528
7159
  isGpuParticleTheme &&
5529
7160
  !isBoardLoading &&
@@ -5542,8 +7173,8 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5542
7173
  this._forceThemeAnimationFrame === true;
5543
7174
  const themeAnimationInterval = this._getThemeAnimationInterval(isInteractiveFrame, themeAnimationMode);
5544
7175
  const effectiveThemeAnimationInterval =
5545
- isPureCss3dCameraMotionFrame === true && forceThemeCameraUpdate !== true
5546
- ? Math.max(themeAnimationInterval, CAMERA_MOTION_WEBGL_FRAME_INTERVAL)
7176
+ isCameraNavigationOnlyInteractiveFrame === true
7177
+ ? 1
5547
7178
  : themeAnimationInterval;
5548
7179
  const shouldCheckThemeAnimation =
5549
7180
  forceThemeCameraUpdate ||
@@ -5568,40 +7199,120 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5568
7199
  this._forceThemeAnimationFrame = false;
5569
7200
  }
5570
7201
  }
5571
-
7202
+
5572
7203
  // 7. Render
5573
7204
  // ----------------------------------------------------------------
5574
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);
5575
7214
  const shouldRenderWebglFrame =
5576
7215
  isWebglRenderEnabled &&
5577
7216
  (
5578
7217
  isBoardLoading === true ||
5579
7218
  shouldUpdate === true ||
7219
+ shouldFinalizeWebglCanvasCameraTransform === true ||
5580
7220
  isInteractiveFrame === true ||
5581
- shouldUseAnimation60Fps === true ||
5582
- shouldUseAnimationLowFpsFrame === true ||
7221
+ shouldUseAnimationCadenceForWebglFrame === true ||
5583
7222
  themeAnimatedThisFrame === true
5584
7223
  ) &&
5585
7224
  (
5586
7225
  isPureCss3dCameraMotionFrame !== true ||
5587
7226
  themeAnimatedThisFrame === true ||
5588
- (this.frameCount % CAMERA_MOTION_WEBGL_FRAME_INTERVAL) === 0
7227
+ shouldSampleWebglDuringCameraMotion === true
5589
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
+ );
5590
7246
  if (shouldRenderWebglFrame) {
5591
- this.renderer.render(this.scene, this.camera);
5592
- const renderInfo = this.renderer?.info?.render || null;
5593
- this._lastWebglRenderInfo = renderInfo ? {
5594
- calls: Number(renderInfo.calls || 0),
5595
- triangles: Number(renderInfo.triangles || 0),
5596
- points: Number(renderInfo.points || 0),
5597
- lines: Number(renderInfo.lines || 0)
5598
- } : 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
+ }
5599
7282
  } else if (isWebglRenderEnabled) {
5600
- 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
+ };
5601
7299
  } else if (isBoardLoading) {
7300
+ this._clearWebglCanvasCameraTransform();
5602
7301
  this._lastWebglRenderInfo = { calls: 0, triangles: 0, points: 0, lines: 0 };
5603
7302
  }
5604
-
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
+
5605
7316
  _profileSection('webglRender');
5606
7317
  // ▼▼▼ [FIX] LOD 모드(MID/FAR)에서는 CSS3D 렌더러 호출 건너뛰기 ▼▼▼
5607
7318
  // CSS3D가 모두 display:none이어도 렌더러가 813개 객체를 순회하면서 시간 소비
@@ -5632,6 +7343,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5632
7343
  }
5633
7344
  _profileSection('css3dRender');
5634
7345
  if (canUseCss3dCameraFastPath) {
7346
+ this._syncMotionGridLayerForCameraNavigation(usedWebglCanvasCameraTransform);
5635
7347
  this.cssRenderer.renderCamera(this.camera);
5636
7348
  }
5637
7349
  _profileSection('css3dCamera');
@@ -5644,18 +7356,32 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5644
7356
  );
5645
7357
  const hasBusinessAutomationConnectionDraft =
5646
7358
  window.MindMapCss3DManager?.hasBusinessAutomationConnectionDraft?.() === true;
5647
- const shouldThrottleBusinessAutomationEdgesForCameraMotion =
5648
- isPureCss3dCameraMotionFrame === true &&
7359
+ const shouldTransformBusinessAutomationEdgesForCameraMotion =
7360
+ hasBusinessAutomationEdgeLayer &&
5649
7361
  hasBusinessAutomationConnectionDraft !== true &&
7362
+ window.MindMapCss3DManager?.syncBusinessAutomationEdgesForCameraMotion &&
7363
+ (this.isPanning === true || this.isZooming === true || isCameraMoving === true) &&
5650
7364
  !isNodeInteracting &&
5651
- hasNonCameraNavigationForcedUpdate !== true &&
5652
- visibilityChangedThisFrame !== true &&
5653
- lodVisualEpochChangedThisFrame !== true &&
5654
- (this.frameCount % CAMERA_MOTION_WEBGL_FRAME_INTERVAL) !== 0;
7365
+ !hasNonCameraNavigationForcedUpdate &&
7366
+ !visibilityChangedThisFrame &&
7367
+ !lodVisualEpochChangedThisFrame &&
7368
+ window.MindMapCss3DManager.syncBusinessAutomationEdgesForCameraMotion(this) === true;
7369
+ const shouldThrottleBusinessAutomationEdgesForCameraMotion = shouldTransformBusinessAutomationEdgesForCameraMotion === true;
7370
+ const shouldDeferBusinessAutomationEdgesForCameraMotion = !!(
7371
+ hasBusinessAutomationEdgeLayer &&
7372
+ hasBusinessAutomationConnectionDraft !== true &&
7373
+ shouldThrottleBusinessAutomationEdgesForCameraMotion !== true &&
7374
+ (this.isPanning === true || this.isZooming === true || isCameraMoving === true) &&
7375
+ !isNodeInteracting &&
7376
+ !hasNonCameraNavigationForcedUpdate &&
7377
+ !visibilityChangedThisFrame &&
7378
+ !lodVisualEpochChangedThisFrame
7379
+ );
5655
7380
  const shouldSyncBusinessAutomationEdges =
5656
7381
  hasBusinessAutomationEdgeLayer &&
5657
7382
  window.MindMapCss3DManager?.renderBusinessAutomationEdges &&
5658
7383
  shouldThrottleBusinessAutomationEdgesForCameraMotion !== true &&
7384
+ shouldDeferBusinessAutomationEdgesForCameraMotion !== true &&
5659
7385
  (
5660
7386
  this._businessAutomationEdgeRenderActive === true ||
5661
7387
  this.isPanning === true ||
@@ -5666,7 +7392,8 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5666
7392
  visibilityChangedThisFrame === true ||
5667
7393
  lodVisualEpochChangedThisFrame === true
5668
7394
  );
5669
- if (shouldThrottleBusinessAutomationEdgesForCameraMotion === true) {
7395
+ if (shouldThrottleBusinessAutomationEdgesForCameraMotion === true ||
7396
+ shouldDeferBusinessAutomationEdgesForCameraMotion === true) {
5670
7397
  this._businessAutomationEdgesDeferredForMotion = true;
5671
7398
  }
5672
7399
  if (shouldSyncBusinessAutomationEdges) {
@@ -5684,8 +7411,8 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5684
7411
  const shouldSyncTextOverlays =
5685
7412
  effectiveOverlayDirty === true ||
5686
7413
  (useTextOverlayV2
5687
- ? (hasOverlayV2DirtyState || shouldRefreshPassiveDomTextOverlay || shouldFinalizePassiveOverlayLayout || (hasOverlayFocus && shouldUpdateCss3d))
5688
- : (hasOverlayFocus && shouldUpdateCss3d));
7414
+ ? (hasOverlayV2DirtyState || shouldRefreshPassiveDomTextOverlay || shouldFinalizePassiveOverlayLayout || (hasMotionBlockingOverlayFocus && shouldUpdateCss3d))
7415
+ : (hasMotionBlockingOverlayFocus && shouldUpdateCss3d));
5689
7416
  const shouldAttemptPassiveOverlayCameraFastPath =
5690
7417
  useTextOverlayV2 &&
5691
7418
  shouldUsePassiveOverlayCameraFastPath;
@@ -5698,7 +7425,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5698
7425
  const shouldDeferFullPassiveOverlayFallback =
5699
7426
  shouldFallbackToFullPassiveOverlayRefresh &&
5700
7427
  shouldRenderPassiveDomTextOverlay &&
5701
- !hasOverlayFocus &&
7428
+ !hasMotionBlockingOverlayFocus &&
5702
7429
  (!isInLodMode || hasLiveInteractiveDomOverlay) &&
5703
7430
  (isInteractiveFrame || this.isPanning === true || this.isZooming === true || visibleOverlayCount > 80);
5704
7431
  if (useTextOverlayV2 && isPassiveOverlaySuppressed) {
@@ -5717,7 +7444,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5717
7444
  // full overlay layout update instead of waiting for the
5718
7445
  // next deferred refresh window.
5719
7446
  window.MindMapTextOverlayV2.updateFrame(this);
5720
- if (shouldRenderPassiveDomTextOverlay && !hasOverlayFocus && (!isInLodMode || hasLiveInteractiveDomOverlay)) {
7447
+ if (shouldRenderPassiveDomTextOverlay && !hasMotionBlockingOverlayFocus && (!isInLodMode || hasLiveInteractiveDomOverlay)) {
5721
7448
  this._lastPassiveOverlayFullRefreshAt = frameStart;
5722
7449
  this._overlayDebugRefreshAgeMs = 0;
5723
7450
  this._overlayDebugRefreshDeferred = false;
@@ -5749,7 +7476,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5749
7476
  (this.isPanning === true || this.isZooming === true || isCameraMoving === true) &&
5750
7477
  !isNodeInteracting &&
5751
7478
  !hasNonCameraNavigationForcedUpdate &&
5752
- !hasOverlayFocus &&
7479
+ !hasMotionBlockingOverlayFocus &&
5753
7480
  !this.isWindowResizing;
5754
7481
  if (isMenuOverlayUiEnabled && window.MindMapMenuManager && shouldDeferFloatingUiDuringMotion !== true) {
5755
7482
  window.MindMapMenuManager.update();
@@ -5998,6 +7725,11 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5998
7725
  return;
5999
7726
  }
6000
7727
 
7728
+ if (!immediate && this._viewStatePersistTimer) {
7729
+ this._viewStatePersistNeedsFreshSnapshot = true;
7730
+ return;
7731
+ }
7732
+
6001
7733
  const snapshot = this.buildViewStateSnapshot();
6002
7734
  if (!snapshot) {
6003
7735
  return;
@@ -6010,6 +7742,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
6010
7742
 
6011
7743
  this._pendingViewStateSnapshot = snapshot;
6012
7744
  this._pendingViewStateKey = snapshotKey;
7745
+ this._viewStatePersistNeedsFreshSnapshot = false;
6013
7746
 
6014
7747
  const isCameraStillSettling = () => {
6015
7748
  if (!this.camera) {
@@ -6047,10 +7780,24 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
6047
7780
  const persist = async () => {
6048
7781
  this._viewStatePersistTimer = null;
6049
7782
  if (!immediate && isNavigationActive()) {
7783
+ this._viewStatePersistNeedsFreshSnapshot = true;
6050
7784
  armPersistTimer();
6051
7785
  return;
6052
7786
  }
6053
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
+
6054
7801
  const pendingSnapshot = this._pendingViewStateSnapshot;
6055
7802
  const pendingKey = this._pendingViewStateKey;
6056
7803
  if (!pendingSnapshot || !pendingKey || pendingKey === this._lastPersistedViewStateKey) {
@@ -6077,6 +7824,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
6077
7824
  clearTimeout(this._viewStatePersistTimer);
6078
7825
  this._viewStatePersistTimer = null;
6079
7826
  }
7827
+ this._viewStatePersistNeedsFreshSnapshot = false;
6080
7828
  void persist();
6081
7829
  return;
6082
7830
  }
@@ -6128,6 +7876,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
6128
7876
 
6129
7877
  hide() {
6130
7878
  this.scheduleViewStatePersist(true);
7879
+ this._syncMotionGridLayer(false);
6131
7880
  if (this.container) {
6132
7881
  if (window.MindMapMenuManager?.hideMenu) {
6133
7882
  window.MindMapMenuManager.hideMenu();
@@ -6225,11 +7974,23 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
6225
7974
  cleanupAndDispose() {
6226
7975
  if (!this.container) return;
6227
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
+ }
6228
7988
  removeCursorDomElement(this);
6229
7989
  if (this._viewStatePersistTimer) {
6230
7990
  clearTimeout(this._viewStatePersistTimer);
6231
7991
  this._viewStatePersistTimer = null;
6232
7992
  }
7993
+ this._viewStatePersistNeedsFreshSnapshot = false;
6233
7994
 
6234
7995
  MindMapInteractions.removeEventListeners(this);
6235
7996
  MindMapDnD.removeEventListeners(this);
@@ -6429,7 +8190,9 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
6429
8190
  if (this.lodRenderer?.requestVisualRefresh) {
6430
8191
  this.lodRenderer.requestVisualRefresh('project-thumbnail-capture');
6431
8192
  }
8193
+ this._clearWebglCanvasCameraTransform();
6432
8194
  this.renderer.render(this.scene, this.camera);
8195
+ this._recordWebglRenderedCameraState();
6433
8196
  if (this.cssRenderer && typeof this.cssRenderer.render === 'function') {
6434
8197
  this.cssRenderer.render(this.cssScene || this.scene, this.camera);
6435
8198
  }