@mindexec/cli 0.2.291 → 0.2.292

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.291",
3
+ "version": "0.2.292",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -208,55 +208,11 @@ body.mind-debug-video-tint-layout .css3d-resolution-wrapper.node-type-video .vid
208
208
  }
209
209
  /* ▲▲▲ [Fix] ▲▲▲ */
210
210
 
211
- body.is-zooming .css3d-resolution-wrapper,
212
- body.is-panning .css3d-resolution-wrapper,
213
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper,
214
- #css3d-dom-wrapper.is-panning .css3d-resolution-wrapper {
215
- -webkit-transition: none !important;
216
- transition: none !important;
217
- }
218
-
219
- body.is-zooming .css3d-resolution-wrapper.selected,
220
- body.is-panning .css3d-resolution-wrapper.selected,
221
- body.mindcanvas-is-moving .css3d-resolution-wrapper.selected,
222
- body.mindcanvas-is-dense .css3d-resolution-wrapper.selected,
223
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper.selected,
224
- #css3d-dom-wrapper.is-panning .css3d-resolution-wrapper.selected,
225
- #css3d-dom-wrapper.mindcanvas-is-moving .css3d-resolution-wrapper.selected,
226
- #css3d-dom-wrapper.mindcanvas-is-dense .css3d-resolution-wrapper.selected {
227
- -webkit-filter: none !important;
228
- filter: none !important;
229
- }
230
-
231
211
  body.is-resizing .css3d-resolution-wrapper {
232
212
  -webkit-transition: none !important;
233
213
  transition: none !important;
234
214
  }
235
215
 
236
- html.mindcanvas-platform-apple body.is-zooming .css3d-resolution-wrapper.selected,
237
- html.mindcanvas-platform-apple body.is-panning .css3d-resolution-wrapper.selected,
238
- html.mindcanvas-platform-apple body.mindcanvas-is-moving .css3d-resolution-wrapper.selected,
239
- html.mindcanvas-platform-apple body.mindcanvas-is-dense .css3d-resolution-wrapper.selected {
240
- -webkit-filter: none !important;
241
- filter: none !important;
242
- }
243
-
244
- body.is-zooming .node-menu-wrapper,
245
- body.is-panning .node-menu-wrapper,
246
- body.is-zooming .map-node-memo__icon-popover,
247
- body.is-panning .map-node-memo__icon-popover,
248
- #css3d-dom-wrapper.is-zooming .node-menu-wrapper,
249
- #css3d-dom-wrapper.is-panning .node-menu-wrapper,
250
- #css3d-dom-wrapper.is-zooming .map-node-memo__icon-popover,
251
- #css3d-dom-wrapper.is-panning .map-node-memo__icon-popover {
252
- -webkit-backdrop-filter: none !important;
253
- backdrop-filter: none !important;
254
- -webkit-box-shadow: none !important;
255
- box-shadow: none !important;
256
- -webkit-transition: none !important;
257
- transition: none !important;
258
- }
259
-
260
216
  .mindcanvas-node-toolbar,
261
217
  .global-ai-input-shell {
262
218
  position: relative;
@@ -1131,17 +1087,14 @@ body.is-panning .map-node-memo__icon-popover,
1131
1087
  /* ▼▼▼ [CRITICAL] Zoom/Drag 중 포인터 이벤트 비활성화 ▼▼▼ */
1132
1088
  /* Hover raycast is already short-circuited in JS during pan, so keep CSS hover/focus
1133
1089
  intact while middle-button panning. We still suppress hit-testing for zoom/drag. */
1134
- #css3d-dom-wrapper.is-dragging,
1135
- #css3d-dom-wrapper.is-zooming,
1136
- body.is-zooming #css3d-dom-wrapper {
1090
+ #css3d-dom-wrapper.is-dragging {
1137
1091
  pointer-events: none !important;
1138
1092
  }
1139
1093
 
1140
1094
  html.mindcanvas-browser-selection-locked,
1141
1095
  body.mindcanvas-browser-selection-locked,
1142
1096
  body.is-node-dragging,
1143
- body.is-resizing,
1144
- body.is-zooming {
1097
+ body.is-resizing {
1145
1098
  user-select: none !important;
1146
1099
  -webkit-user-select: none !important;
1147
1100
  }
@@ -1149,10 +1102,6 @@ body.is-zooming {
1149
1102
  body.mindcanvas-browser-selection-locked *,
1150
1103
  body.is-node-dragging *,
1151
1104
  body.is-resizing *,
1152
- #css3d-dom-wrapper.is-zooming *,
1153
- body.is-zooming #css3d-dom-wrapper *,
1154
- #css3d-dom-wrapper.is-panning,
1155
- #css3d-dom-wrapper.is-panning *,
1156
1105
  #css3d-dom-wrapper.is-dragging,
1157
1106
  #css3d-dom-wrapper.is-dragging *,
1158
1107
  .multi-select-overlay,
@@ -1172,13 +1121,7 @@ body.is-node-dragging .mind-map-text-overlay-v2-card *,
1172
1121
  body.is-resizing .mind-map-text-overlay.mind-map-text-overlay-selectable .mind-map-text-overlay-fragment,
1173
1122
  body.is-resizing .mind-map-text-overlay.mind-map-text-overlay-selectable .mind-map-text-overlay-fragment *,
1174
1123
  body.is-resizing .mind-map-text-overlay-v2-card,
1175
- body.is-resizing .mind-map-text-overlay-v2-card *,
1176
- body.is-zooming .mind-map-text-overlay.mind-map-text-overlay-selectable .mind-map-text-overlay-fragment,
1177
- body.is-zooming .mind-map-text-overlay.mind-map-text-overlay-selectable .mind-map-text-overlay-fragment *,
1178
- #css3d-dom-wrapper.is-zooming .mind-map-text-overlay.mind-map-text-overlay-selectable .mind-map-text-overlay-fragment,
1179
- #css3d-dom-wrapper.is-zooming .mind-map-text-overlay.mind-map-text-overlay-selectable .mind-map-text-overlay-fragment *,
1180
- #css3d-dom-wrapper.is-panning .mind-map-text-overlay.mind-map-text-overlay-selectable .mind-map-text-overlay-fragment,
1181
- #css3d-dom-wrapper.is-panning .mind-map-text-overlay.mind-map-text-overlay-selectable .mind-map-text-overlay-fragment * {
1124
+ body.is-resizing .mind-map-text-overlay-v2-card * {
1182
1125
  user-select: none !important;
1183
1126
  -webkit-user-select: none !important;
1184
1127
  cursor: inherit !important;
@@ -1518,11 +1461,6 @@ html.mindcanvas-platform-apple .css3d-resolution-wrapper.is-automation-relation-
1518
1461
  pointer-events: auto;
1519
1462
  }
1520
1463
 
1521
- body.is-panning .mind-map-text-overlay-v2-card.is-passive .mind-map-text-overlay-v2-body,
1522
- #css3d-dom-wrapper.is-panning .mind-map-text-overlay-v2-card.is-passive .mind-map-text-overlay-v2-body {
1523
- pointer-events: none !important;
1524
- }
1525
-
1526
1464
  .mind-map-text-overlay-v2-shell {
1527
1465
  width: 100%;
1528
1466
  height: 100%;
@@ -2456,14 +2394,7 @@ body.is-panning .mind-map-text-overlay-v2-card.is-passive .mind-map-text-overlay
2456
2394
  /* Template launcher and Multi Desktop Monitor own their visual chrome on the
2457
2395
  card itself. Keep the CSS3D wrapper neutral so motion CSS3D and the passive
2458
2396
  DOM overlay do not stack different selection chrome. */
2459
- .css3d-resolution-wrapper.node-type-templatelauncher.selected,
2460
- body.is-panning .css3d-resolution-wrapper.node-type-templatelauncher.selected,
2461
- body.is-zooming .css3d-resolution-wrapper.node-type-templatelauncher.selected,
2462
- body.mindcanvas-is-moving .css3d-resolution-wrapper.node-type-templatelauncher.selected,
2463
- #css3d-dom-wrapper.is-panning .css3d-resolution-wrapper.node-type-templatelauncher.selected,
2464
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper.node-type-templatelauncher.selected,
2465
- #css3d-dom-wrapper.mindcanvas-is-moving .css3d-resolution-wrapper.node-type-templatelauncher.selected,
2466
- body.mindcanvas-is-dense .css3d-resolution-wrapper.node-type-templatelauncher.selected {
2397
+ .css3d-resolution-wrapper.node-type-templatelauncher.selected {
2467
2398
  outline: none !important;
2468
2399
  -webkit-box-shadow: none !important;
2469
2400
  box-shadow: none !important;
@@ -3348,59 +3279,6 @@ body.mindcanvas-is-dense .css3d-resolution-wrapper.node-type-templatelauncher.se
3348
3279
  }
3349
3280
  }
3350
3281
 
3351
- /* Lightweight CSS3D motion path.
3352
- Reference graph tools are useful here because they keep node DOM cheap:
3353
- while the camera is moving, retain the canonical CSS3D surface but remove
3354
- decorative compositor-heavy effects from every CSS3D node, including
3355
- selected and agent-style nodes. */
3356
- body.is-zooming .css3d-resolution-wrapper,
3357
- body.is-panning .css3d-resolution-wrapper,
3358
- body.is-node-dragging .css3d-resolution-wrapper,
3359
- body.is-resizing .css3d-resolution-wrapper,
3360
- body.mindcanvas-is-moving .css3d-resolution-wrapper,
3361
- body.mindcanvas-is-dense .css3d-resolution-wrapper,
3362
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper,
3363
- #css3d-dom-wrapper.is-panning .css3d-resolution-wrapper,
3364
- #css3d-dom-wrapper.mindcanvas-is-moving .css3d-resolution-wrapper,
3365
- #css3d-dom-wrapper.mindcanvas-is-dense .css3d-resolution-wrapper {
3366
- -webkit-transition: none !important;
3367
- transition: none !important;
3368
- -webkit-filter: none !important;
3369
- filter: none !important;
3370
- -webkit-box-shadow: none !important;
3371
- box-shadow: none !important;
3372
- }
3373
-
3374
- body.is-zooming .css3d-resolution-wrapper .css3d-dynamic-node,
3375
- body.is-panning .css3d-resolution-wrapper .css3d-dynamic-node,
3376
- body.is-node-dragging .css3d-resolution-wrapper .css3d-dynamic-node,
3377
- body.is-resizing .css3d-resolution-wrapper .css3d-dynamic-node,
3378
- body.mindcanvas-is-moving .css3d-resolution-wrapper .css3d-dynamic-node,
3379
- body.mindcanvas-is-dense .css3d-resolution-wrapper .css3d-dynamic-node,
3380
- body.is-zooming .css3d-resolution-wrapper [id^="css3d-node-"],
3381
- body.is-panning .css3d-resolution-wrapper [id^="css3d-node-"],
3382
- body.is-node-dragging .css3d-resolution-wrapper [id^="css3d-node-"],
3383
- body.is-resizing .css3d-resolution-wrapper [id^="css3d-node-"],
3384
- body.mindcanvas-is-moving .css3d-resolution-wrapper [id^="css3d-node-"],
3385
- body.mindcanvas-is-dense .css3d-resolution-wrapper [id^="css3d-node-"],
3386
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper .css3d-dynamic-node,
3387
- #css3d-dom-wrapper.is-panning .css3d-resolution-wrapper .css3d-dynamic-node,
3388
- #css3d-dom-wrapper.mindcanvas-is-moving .css3d-resolution-wrapper .css3d-dynamic-node,
3389
- #css3d-dom-wrapper.mindcanvas-is-dense .css3d-resolution-wrapper .css3d-dynamic-node,
3390
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper [id^="css3d-node-"],
3391
- #css3d-dom-wrapper.is-panning .css3d-resolution-wrapper [id^="css3d-node-"],
3392
- #css3d-dom-wrapper.mindcanvas-is-moving .css3d-resolution-wrapper [id^="css3d-node-"],
3393
- #css3d-dom-wrapper.mindcanvas-is-dense .css3d-resolution-wrapper [id^="css3d-node-"] {
3394
- -webkit-transition: none !important;
3395
- transition: none !important;
3396
- -webkit-filter: none !important;
3397
- filter: none !important;
3398
- -webkit-backdrop-filter: none !important;
3399
- backdrop-filter: none !important;
3400
- -webkit-box-shadow: none !important;
3401
- box-shadow: none !important;
3402
- }
3403
-
3404
3282
  /* Temporary lightweight node visuals.
3405
3283
  CSS3D and passive overlay node surfaces intentionally avoid shadows/glow.
3406
3284
  Keep outlines, borders, and status color so selection and node state remain readable. */
@@ -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 = '20260620-css3d-flat-wheel-v737';
8
+ const MINDMAP_CORE_BUILD_ID = '20260620-css3d-flat-pan-v749';
9
9
  const CanvasPhase = Object.freeze({
10
10
  Booting: 'booting',
11
11
  BoardFileLoading: 'board-file-loading',
@@ -114,9 +114,9 @@
114
114
  const LOCAL_SNAP_GRID_DEFAULT_OPACITY = 0.42;
115
115
  const CAMERA_MOTION_PARTIAL_FRAME_SKIP_ENABLED = false;
116
116
  const CAMERA_MOTION_VISUAL_ONLY_FAST_PATH_ENABLED = true;
117
- const CAMERA_MOTION_WEBGL_CANVAS_TRANSFORM_ENABLED = true;
118
- const CAMERA_MOTION_WEBGL_TRANSFORM_TRANSLATE_BUCKET_PX = 0.25;
119
- const CAMERA_MOTION_WEBGL_TRANSFORM_SCALE_BUCKET = 0.0005;
117
+ const CAMERA_MOTION_WEBGL_CANVAS_TRANSFORM_ENABLED = false;
118
+ const CAMERA_MOTION_WEBGL_TRANSFORM_TRANSLATE_BUCKET_PX = 0.001;
119
+ const CAMERA_MOTION_WEBGL_TRANSFORM_SCALE_BUCKET = 0.000001;
120
120
  const CSS3D_FLAT_CAMERA_MOTION_ENABLED = true;
121
121
  const CAMERA_STATIONARY_INTERACTION_FRAME_DELAY_MS = 48;
122
122
  const CAMERA_ZOOM_END_DELAY_MS = 150;
@@ -606,6 +606,40 @@
606
606
  return false;
607
607
  }
608
608
 
609
+ function renderCss3dSceneForCurrentCamera(module, scene, camera, reason = 'css3d-render') {
610
+ const renderer = module?.cssRenderer || null;
611
+ if (!renderer || !scene || !camera) {
612
+ return false;
613
+ }
614
+
615
+ const renderDebugFlags = module.renderDebugFlags || getRenderDebugFlagsSnapshot();
616
+ if (renderDebugFlags.enableCss3d === false) {
617
+ return false;
618
+ }
619
+
620
+ if (typeof renderer.renderFlat === 'function' &&
621
+ canUseFlatCss3dCameraMotion(module, renderer, camera, renderDebugFlags) === true) {
622
+ const usedFlatSceneRender = renderer.renderFlat(scene, camera, {
623
+ translateBucketPx: CAMERA_MOTION_WEBGL_TRANSFORM_TRANSLATE_BUCKET_PX,
624
+ scaleBucket: CAMERA_MOTION_WEBGL_TRANSFORM_SCALE_BUCKET
625
+ });
626
+ if (usedFlatSceneRender === true) {
627
+ module._lastCss3dCameraMotionMode = 'flat';
628
+ module._lastCss3dCameraMotionReason = reason;
629
+ return true;
630
+ }
631
+ }
632
+
633
+ if (typeof renderer.render === 'function') {
634
+ renderer.render(scene, camera);
635
+ module._lastCss3dCameraMotionMode = 'matrix3d';
636
+ module._lastCss3dCameraMotionReason = reason;
637
+ return true;
638
+ }
639
+
640
+ return false;
641
+ }
642
+
609
643
  function markWebglSurfaceFrozenForZoom(module, mode) {
610
644
  if (!module) {
611
645
  return false;
@@ -1943,7 +1977,10 @@
1943
1977
  !module.lodRenderer ||
1944
1978
  module.lodRenderer.isInLODMode !== true ||
1945
1979
  typeof module.lodRenderer.canUseUltraResidentCameraOnlyFrame !== 'function' ||
1946
- module.lodRenderer.canUseUltraResidentCameraOnlyFrame(module.camera, module) !== true) {
1980
+ module.lodRenderer.canUseUltraResidentCameraOnlyFrame(module.camera, module, {
1981
+ isCameraMoving,
1982
+ isCameraNavigationActive
1983
+ }) !== true) {
1947
1984
  return false;
1948
1985
  }
1949
1986
 
@@ -4019,6 +4056,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4019
4056
  this._webglCameraMotionDeferred = false;
4020
4057
  this._themeCameraMotionDeferred = false;
4021
4058
  this._css3dVisualDeferredForMotion = false;
4059
+ this._backgroundGridLayerHost = null;
4022
4060
  this._motionGridLayer = null;
4023
4061
  this._motionGridVisible = false;
4024
4062
  this._motionGridHideTimer = null;
@@ -4100,6 +4138,13 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4100
4138
 
4101
4139
  const roundedX = Math.round(nextX * 2) / 2;
4102
4140
  const roundedY = Math.round(nextY * 2) / 2;
4141
+ if (this.isZooming === true &&
4142
+ this._lastLocalSnapGridPointerSnapKey &&
4143
+ Math.abs(Number(this._lastSnapGridClientX || 0) - roundedX) < 0.001 &&
4144
+ Math.abs(Number(this._lastSnapGridClientY || 0) - roundedY) < 0.001) {
4145
+ this._localSnapGridStyleDeferredForZoom = true;
4146
+ return false;
4147
+ }
4103
4148
  const fallbackGrid = Math.max(1, Number(this.GRID_SIZE || 10));
4104
4149
  let pointerSnapKey = `screen:${Math.round(roundedX / fallbackGrid)}|${Math.round(roundedY / fallbackGrid)}`;
4105
4150
  if (this.container && this.camera?.isPerspectiveCamera) {
@@ -4173,9 +4218,9 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4173
4218
  };
4174
4219
  }
4175
4220
 
4176
- _placeBackgroundGridLayer(layer) {
4177
- if (!layer || !this.container) {
4178
- return false;
4221
+ _ensureBackgroundGridLayerHost() {
4222
+ if (!this.container || typeof document === 'undefined') {
4223
+ return null;
4179
4224
  }
4180
4225
 
4181
4226
  const webglElement = this.renderer?.domElement;
@@ -4187,16 +4232,64 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4187
4232
  ? cssElement
4188
4233
  : null;
4189
4234
 
4235
+ let host = this._backgroundGridLayerHost;
4236
+ if (!host || !host.isConnected) {
4237
+ host = document.createElement('div');
4238
+ host.className = 'mindcanvas-background-hint-layer-host';
4239
+ host.setAttribute('aria-hidden', 'true');
4240
+ host.style.position = 'absolute';
4241
+ host.style.inset = '0';
4242
+ host.style.zIndex = '0';
4243
+ host.style.pointerEvents = 'none';
4244
+ host.style.overflow = 'visible';
4245
+ host.style.background = 'transparent';
4246
+ host.style.contain = 'layout style paint';
4247
+ host.style.transform = 'translate3d(0, 0, 0)';
4248
+ host.style.transition = 'none';
4249
+ this._backgroundGridLayerHost = host;
4250
+ }
4251
+
4252
+ if (this.container.style.isolation !== 'isolate') {
4253
+ this.container.style.isolation = 'isolate';
4254
+ }
4255
+
4190
4256
  if (anchorElement) {
4191
- if (layer.parentNode !== this.container ||
4192
- layer.nextSibling !== anchorElement) {
4193
- this.container.insertBefore(layer, anchorElement);
4257
+ if (host.parentNode !== this.container ||
4258
+ host.nextSibling !== anchorElement) {
4259
+ this.container.insertBefore(host, anchorElement);
4194
4260
  }
4195
- } else if (layer.parentNode !== this.container) {
4196
- this.container.insertBefore(layer, this.container.firstChild || null);
4261
+ } else if (host.parentNode !== this.container) {
4262
+ this.container.insertBefore(host, this.container.firstChild || null);
4263
+ }
4264
+
4265
+ if (webglElement?.style && webglElement.style.zIndex !== '1') {
4266
+ webglElement.style.zIndex = '1';
4267
+ }
4268
+ if (cssElement?.style && cssElement.style.zIndex !== '2') {
4269
+ cssElement.style.zIndex = '2';
4270
+ }
4271
+
4272
+ return host.parentNode === this.container ? host : null;
4273
+ }
4274
+
4275
+ _placeBackgroundGridLayer(layer) {
4276
+ if (!layer || !this.container) {
4277
+ return false;
4278
+ }
4279
+
4280
+ const host = this._ensureBackgroundGridLayerHost();
4281
+ if (!host) {
4282
+ return false;
4197
4283
  }
4198
4284
 
4199
- return layer.parentNode === this.container;
4285
+ if (layer.parentNode !== host) {
4286
+ host.appendChild(layer);
4287
+ }
4288
+ if (layer.style.zIndex !== '0') {
4289
+ layer.style.zIndex = '0';
4290
+ }
4291
+
4292
+ return layer.parentNode === host;
4200
4293
  }
4201
4294
 
4202
4295
  _ensureLocalSnapGridLayer() {
@@ -4478,10 +4571,12 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4478
4571
  const layerTop = snappedCenter.centerY - LOCAL_SNAP_GRID_RADIUS_PX;
4479
4572
 
4480
4573
  if (this.isZooming === true && !!this._lastLocalSnapGridKey) {
4481
- const transformKey = `${Math.round(layerLeft * 2)}|${Math.round(layerTop * 2)}`;
4574
+ const snappedLayerLeft = Math.round(layerLeft);
4575
+ const snappedLayerTop = Math.round(layerTop);
4576
+ const transformKey = `${snappedLayerLeft}|${snappedLayerTop}`;
4482
4577
  if (transformKey !== this._lastLocalSnapGridTransformKey) {
4483
4578
  this._lastLocalSnapGridTransformKey = transformKey;
4484
- layer.style.transform = `translate3d(${layerLeft.toFixed(2)}px, ${layerTop.toFixed(2)}px, 0)`;
4579
+ layer.style.transform = `translate3d(${snappedLayerLeft}px, ${snappedLayerTop}px, 0)`;
4485
4580
  }
4486
4581
  this._lastLocalSnapGridPositionRevision = Math.max(0, Number(this._localSnapGridPointerRevision || 0));
4487
4582
  this._localSnapGridStyleDeferredForZoom = true;
@@ -4524,10 +4619,12 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4524
4619
  this._localSnapGridStyleDeferredForZoom = true;
4525
4620
  }
4526
4621
 
4527
- const transformKey = `${Math.round(layerLeft * 2)}|${Math.round(layerTop * 2)}`;
4622
+ const snappedLayerLeft = Math.round(layerLeft);
4623
+ const snappedLayerTop = Math.round(layerTop);
4624
+ const transformKey = `${snappedLayerLeft}|${snappedLayerTop}`;
4528
4625
  if (transformKey !== this._lastLocalSnapGridTransformKey) {
4529
4626
  this._lastLocalSnapGridTransformKey = transformKey;
4530
- layer.style.transform = `translate3d(${layerLeft.toFixed(2)}px, ${layerTop.toFixed(2)}px, 0)`;
4627
+ layer.style.transform = `translate3d(${snappedLayerLeft}px, ${snappedLayerTop}px, 0)`;
4531
4628
  }
4532
4629
  this._lastLocalSnapGridPositionRevision = Math.max(0, Number(this._localSnapGridPointerRevision || 0));
4533
4630
 
@@ -4586,11 +4683,13 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4586
4683
  const snappedCenter = this._getLocalSnapGridSnappedScreenCenter(centerX, centerY, width, height, this.camera);
4587
4684
  const layerLeft = snappedCenter.centerX - LOCAL_SNAP_GRID_RADIUS_PX;
4588
4685
  const layerTop = snappedCenter.centerY - LOCAL_SNAP_GRID_RADIUS_PX;
4589
- const transformKey = `${Math.round(layerLeft * 2)}|${Math.round(layerTop * 2)}`;
4686
+ const snappedLayerLeft = Math.round(layerLeft);
4687
+ const snappedLayerTop = Math.round(layerTop);
4688
+ const transformKey = `${snappedLayerLeft}|${snappedLayerTop}`;
4590
4689
 
4591
4690
  if (transformKey !== this._lastLocalSnapGridTransformKey) {
4592
4691
  this._lastLocalSnapGridTransformKey = transformKey;
4593
- layer.style.transform = `translate3d(${layerLeft.toFixed(2)}px, ${layerTop.toFixed(2)}px, 0)`;
4692
+ layer.style.transform = `translate3d(${snappedLayerLeft}px, ${snappedLayerTop}px, 0)`;
4594
4693
  }
4595
4694
  this._lastLocalSnapGridPositionRevision = pointerRevision;
4596
4695
 
@@ -4796,8 +4895,15 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4796
4895
  get: () => this._forceUpdateFrameCount || 0,
4797
4896
  set: (value) => {
4798
4897
  const nextValue = Math.max(0, Math.floor(Number(value) || 0));
4898
+ const previousValue = Math.max(0, Math.floor(Number(this._forceUpdateFrameCount || 0)));
4799
4899
  this._forceUpdateFrameCount = nextValue;
4800
- if (nextValue > 0) {
4900
+ const needsWake =
4901
+ nextValue > 0 &&
4902
+ (
4903
+ nextValue > previousValue ||
4904
+ (!this.animationFrameId && !this._animationIdleTimerId)
4905
+ );
4906
+ if (needsWake) {
4801
4907
  this.requestAnimationWake('force-update-frames');
4802
4908
  }
4803
4909
  }
@@ -5454,7 +5560,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5454
5560
  this.renderer.domElement.style.left = '0';
5455
5561
  this.renderer.domElement.style.width = '100%';
5456
5562
  this.renderer.domElement.style.height = '100%';
5457
- this.renderer.domElement.style.zIndex = '0';
5563
+ this.renderer.domElement.style.zIndex = '1';
5458
5564
  this.renderer.domElement.style.outline = 'none';
5459
5565
 
5460
5566
  if (this.container) this.container.appendChild(this.renderer.domElement);
@@ -5474,7 +5580,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5474
5580
  cssEl.style.padding = '0';
5475
5581
  cssEl.style.width = '100%';
5476
5582
  cssEl.style.height = '100%';
5477
- cssEl.style.zIndex = '1';
5583
+ cssEl.style.zIndex = '2';
5478
5584
  cssEl.style.pointerEvents = 'auto'; // 마우스/휠 이벤트 수신
5479
5585
  cssEl.id = 'css3d-dom-wrapper';
5480
5586
 
@@ -5722,6 +5828,10 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5722
5828
  const transformTranslateX = Math.round(translateX / translateBucket) * translateBucket;
5723
5829
  const transformTranslateY = Math.round(translateY / translateBucket) * translateBucket;
5724
5830
  const transformScale = Math.round(scale / scaleBucket) * scaleBucket;
5831
+ const originX = width * 0.5;
5832
+ const originY = height * 0.5;
5833
+ const matrixTranslateX = ((1 - transformScale) * originX) + transformTranslateX;
5834
+ const matrixTranslateY = ((1 - transformScale) * originY) + transformTranslateY;
5725
5835
  const transformKey = [
5726
5836
  Math.round(width),
5727
5837
  Math.round(height),
@@ -5732,9 +5842,11 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
5732
5842
 
5733
5843
  if (transformKey !== this._lastWebglCanvasMotionTransformKey) {
5734
5844
  this._lastWebglCanvasMotionTransformKey = transformKey;
5735
- element.style.transformOrigin = '50% 50%';
5736
- element.style.willChange = 'transform';
5737
- element.style.transform = `translate3d(${transformTranslateX.toFixed(3)}px, ${transformTranslateY.toFixed(3)}px, 0) scale(${transformScale.toFixed(5)})`;
5845
+ if (this._webglCanvasMotionTransformActive !== true) {
5846
+ element.style.transformOrigin = '0 0';
5847
+ element.style.willChange = 'transform';
5848
+ }
5849
+ element.style.transform = `matrix(${transformScale.toFixed(6)}, 0, 0, ${transformScale.toFixed(6)}, ${matrixTranslateX.toFixed(3)}, ${matrixTranslateY.toFixed(3)})`;
5738
5850
  }
5739
5851
 
5740
5852
  this._webglCanvasMotionTransformActive = true;
@@ -6835,7 +6947,6 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
6835
6947
 
6836
6948
  if (this.isZooming && !isCameraMoving && timeSinceLastInput > CAMERA_ZOOM_END_DELAY_MS) {
6837
6949
  this.isZooming = false;
6838
- this.cssRenderer?.domElement?.classList?.remove?.('is-zooming');
6839
6950
  if (FRAME_PERF_DEBUG) {
6840
6951
  console.log(`[Perf] Zoom ended (${timeSinceLastInput.toFixed(0)}ms since last input)`);
6841
6952
  }
@@ -8818,8 +8929,20 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
8818
8929
  isCameraNavigationOnlyInteractiveFrame === true
8819
8930
  ? false
8820
8931
  : (shouldUseAnimation60Fps === true || shouldUseAnimationLowFpsFrame === true);
8932
+ const shouldHoldWebglCanvasTransformDuringCameraNavigation = !!(
8933
+ isWebglRenderEnabled &&
8934
+ isCameraNavigationOnlyInteractiveFrame === true &&
8935
+ canUseWebglCanvasTransformForCameraMotion(this) === true &&
8936
+ !isBoardLoading &&
8937
+ this._lastWebglRenderedCameraState &&
8938
+ this.renderer?.domElement &&
8939
+ !isNodeInteracting &&
8940
+ !hasNonCameraNavigationForcedUpdate &&
8941
+ this.isWindowResizing !== true
8942
+ );
8821
8943
  const shouldRenderWebglFrame =
8822
8944
  isWebglRenderEnabled &&
8945
+ shouldHoldWebglCanvasTransformDuringCameraNavigation !== true &&
8823
8946
  (
8824
8947
  isBoardLoading === true ||
8825
8948
  shouldUpdate === true ||
@@ -8968,7 +9091,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
8968
9091
  cssEl.scrollLeft = 0;
8969
9092
  }
8970
9093
 
8971
- this.cssRenderer.render(this.cssScene || this.scene, this.camera);
9094
+ renderCss3dSceneForCurrentCamera(this, this.cssScene || this.scene, this.camera, 'css3d-full-render');
8972
9095
  this._css3dVisualChangedThisFrame = false;
8973
9096
  this._css3dVisualChangedSinceLastRender = false;
8974
9097
  }
@@ -9634,6 +9757,10 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
9634
9757
  this._lastLocalSnapGridKey = '';
9635
9758
  this._lastLocalSnapGridTransformKey = '';
9636
9759
  }
9760
+ if (this._backgroundGridLayerHost) {
9761
+ this._backgroundGridLayerHost.remove();
9762
+ this._backgroundGridLayerHost = null;
9763
+ }
9637
9764
  removeCursorDomElement(this);
9638
9765
  if (this._viewStatePersistTimer) {
9639
9766
  clearTimeout(this._viewStatePersistTimer);
@@ -9844,7 +9971,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
9844
9971
  this.renderer.render(this.scene, this.camera);
9845
9972
  this._recordWebglRenderedCameraState();
9846
9973
  if (this.cssRenderer && typeof this.cssRenderer.render === 'function') {
9847
- this.cssRenderer.render(this.cssScene || this.scene, this.camera);
9974
+ renderCss3dSceneForCurrentCamera(this, this.cssScene || this.scene, this.camera, 'project-thumbnail-pre-capture');
9848
9975
  }
9849
9976
  } catch (error) {
9850
9977
  console.warn('[ProjectThumbnail] render before capture failed:', error);
@@ -828,63 +828,18 @@
828
828
  max-height: none !important;
829
829
  }
830
830
 
831
- /* Keep CSS3D nodes readable during camera motion, but drop expensive
832
- decorative effects consistently across selected and agent-style nodes. */
833
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper,
834
- #css3d-dom-wrapper.is-panning .css3d-resolution-wrapper,
835
- #css3d-dom-wrapper.mindcanvas-is-moving .css3d-resolution-wrapper,
836
- #css3d-dom-wrapper.mindcanvas-is-dense .css3d-resolution-wrapper {
837
- transition: none !important;
838
- -webkit-transition: none !important;
839
- filter: none !important;
840
- -webkit-filter: none !important;
841
- }
842
-
843
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper .css3d-dynamic-node,
844
- #css3d-dom-wrapper.is-panning .css3d-resolution-wrapper .css3d-dynamic-node,
845
- #css3d-dom-wrapper.mindcanvas-is-moving .css3d-resolution-wrapper .css3d-dynamic-node,
846
- #css3d-dom-wrapper.mindcanvas-is-dense .css3d-resolution-wrapper .css3d-dynamic-node,
847
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper [id^="css3d-node-"],
848
- #css3d-dom-wrapper.is-panning .css3d-resolution-wrapper [id^="css3d-node-"],
849
- #css3d-dom-wrapper.mindcanvas-is-moving .css3d-resolution-wrapper [id^="css3d-node-"],
850
- #css3d-dom-wrapper.mindcanvas-is-dense .css3d-resolution-wrapper [id^="css3d-node-"] {
851
- box-shadow: none !important;
852
- filter: none !important;
853
- -webkit-filter: none !important;
854
- backdrop-filter: none !important;
855
- -webkit-backdrop-filter: none !important;
856
- }
857
-
858
- #css3d-dom-wrapper.is-zooming .embed-content iframe,
859
- #css3d-dom-wrapper.is-zooming .embed-iframe-host iframe,
860
- #css3d-dom-wrapper.is-zooming .embed-card,
861
- #css3d-dom-wrapper.is-zooming .embed-card *,
862
- #css3d-dom-wrapper.is-zooming .embed-action,
863
- #css3d-dom-wrapper.is-zooming .embed-play-button,
864
831
  body.is-node-dragging .embed-content iframe,
865
832
  body.is-node-dragging .embed-iframe-host iframe,
866
833
  body.is-node-dragging .embed-card,
867
834
  body.is-node-dragging .embed-card *,
868
835
  body.is-node-dragging .embed-action,
869
836
  body.is-node-dragging .embed-play-button,
870
- #css3d-dom-wrapper.mindcanvas-is-moving .embed-content iframe,
871
- #css3d-dom-wrapper.mindcanvas-is-moving .embed-iframe-host iframe,
872
- #css3d-dom-wrapper.mindcanvas-is-moving .embed-card,
873
- #css3d-dom-wrapper.mindcanvas-is-moving .embed-card *,
874
- #css3d-dom-wrapper.mindcanvas-is-moving .embed-action,
875
- #css3d-dom-wrapper.mindcanvas-is-moving .embed-play-button,
876
837
  .is-dragging .embed-content iframe,
877
838
  .is-dragging .embed-iframe-host iframe,
878
839
  .is-dragging .embed-card,
879
840
  .is-dragging .embed-card *,
880
841
  .is-dragging .embed-action,
881
- .is-dragging .embed-play-button,
882
- .is-panning .embed-content iframe,
883
- .is-panning .embed-iframe-host iframe,
884
- .is-panning .embed-card,
885
- .is-panning .embed-card *,
886
- .is-panning .embed-action,
887
- .is-panning .embed-play-button {
842
+ .is-dragging .embed-play-button {
888
843
  pointer-events: none !important;
889
844
  }
890
845
 
@@ -903,32 +858,13 @@
903
858
  pointer-events: auto !important;
904
859
  }
905
860
 
906
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper.node-type-embed .embed-content[data-iframe-interacting="true"] .embed-iframe-host,
907
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper.node-type-embed .embed-content[data-iframe-interacting="true"] .embed-iframe-host iframe,
908
861
  body.is-node-dragging .css3d-resolution-wrapper.node-type-embed .embed-content[data-iframe-interacting="true"] .embed-iframe-host,
909
862
  body.is-node-dragging .css3d-resolution-wrapper.node-type-embed .embed-content[data-iframe-interacting="true"] .embed-iframe-host iframe,
910
- #css3d-dom-wrapper.mindcanvas-is-moving .css3d-resolution-wrapper.node-type-embed .embed-content[data-iframe-interacting="true"] .embed-iframe-host,
911
- #css3d-dom-wrapper.mindcanvas-is-moving .css3d-resolution-wrapper.node-type-embed .embed-content[data-iframe-interacting="true"] .embed-iframe-host iframe,
912
863
  .is-dragging .css3d-resolution-wrapper.node-type-embed .embed-content[data-iframe-interacting="true"] .embed-iframe-host,
913
- .is-dragging .css3d-resolution-wrapper.node-type-embed .embed-content[data-iframe-interacting="true"] .embed-iframe-host iframe,
914
- .is-panning .css3d-resolution-wrapper.node-type-embed .embed-content[data-iframe-interacting="true"] .embed-iframe-host,
915
- .is-panning .css3d-resolution-wrapper.node-type-embed .embed-content[data-iframe-interacting="true"] .embed-iframe-host iframe {
864
+ .is-dragging .css3d-resolution-wrapper.node-type-embed .embed-content[data-iframe-interacting="true"] .embed-iframe-host iframe {
916
865
  pointer-events: none !important;
917
866
  }
918
867
 
919
- #css3d-dom-wrapper.is-zooming .ai-loading-row.ai-loading-row-media,
920
- #css3d-dom-wrapper.is-panning .ai-loading-row.ai-loading-row-media,
921
- #css3d-dom-wrapper.mindcanvas-is-moving .ai-loading-row.ai-loading-row-media,
922
- #css3d-dom-wrapper.mindcanvas-is-dense .ai-loading-row.ai-loading-row-media,
923
- #css3d-dom-wrapper.is-zooming .css3d-resolution-wrapper .map-node-memo__agent-console-panel,
924
- #css3d-dom-wrapper.is-panning .css3d-resolution-wrapper .map-node-memo__agent-console-panel,
925
- #css3d-dom-wrapper.mindcanvas-is-moving .css3d-resolution-wrapper .map-node-memo__agent-console-panel,
926
- #css3d-dom-wrapper.mindcanvas-is-dense .css3d-resolution-wrapper .map-node-memo__agent-console-panel {
927
- box-shadow: none !important;
928
- backdrop-filter: none !important;
929
- -webkit-backdrop-filter: none !important;
930
- }
931
-
932
868
  .ai-loading-row {
933
869
  display: flex;
934
870
  align-items: center;
@@ -9832,8 +9768,11 @@
9832
9768
  const transform = `translate3d(${translateX.toFixed(3)}px, ${translateY.toFixed(3)}px, 0) scale(${scale.toFixed(5)})`;
9833
9769
  for (const layer of [visualLayer, hitLayer]) {
9834
9770
  if (!layer?.style) continue;
9835
- layer.style.transformOrigin = '50% 50%';
9836
- layer.style.willChange = 'transform';
9771
+ if (module._businessAutomationEdgeCameraTransformActive !== true ||
9772
+ layer.style.willChange !== 'transform') {
9773
+ layer.style.transformOrigin = '50% 50%';
9774
+ layer.style.willChange = 'transform';
9775
+ }
9837
9776
  layer.style.transform = transform;
9838
9777
  }
9839
9778
  module._lastBusinessAutomationEdgeTransformKey = transformKey;
@@ -2,7 +2,6 @@ window.MindMapInteractions = (function () {
2
2
  const WHEEL_DEBUG = false;
3
3
  const WHEEL_PERF_DEBUG = false;
4
4
  const CTRL_WHEEL_SCROLL_EDGE_EPSILON = 2;
5
- const PAN_LIVE_REFRESH_FORCE_FRAMES = 2;
6
5
  const MOTION_DOM_FANOUT_DELAY_MS = 180;
7
6
  const MOTION_DOM_FANOUT_ENABLED = false;
8
7
  const MOTION_WILL_CHANGE_ENABLE_DELAY_MS = 240;
@@ -26,11 +25,7 @@ window.MindMapInteractions = (function () {
26
25
  }
27
26
 
28
27
  function isBrowserSelectionLocked() {
29
- const css3dWrapper = document?.getElementById?.('css3d-dom-wrapper');
30
- return browserSelectionLockReasons.size > 0 ||
31
- css3dWrapper?.classList?.contains?.('is-panning') === true ||
32
- css3dWrapper?.classList?.contains?.('is-zooming') === true ||
33
- css3dWrapper?.classList?.contains?.('is-dragging') === true;
28
+ return browserSelectionLockReasons.size > 0;
34
29
  }
35
30
 
36
31
  function handleBrowserSelectStart(event) {
@@ -97,11 +92,9 @@ window.MindMapInteractions = (function () {
97
92
  }
98
93
 
99
94
  function scheduleZoomMotionChrome(module) {
100
- if (!module || typeof document === 'undefined') {
101
- return;
102
- }
103
- if (MOTION_DOM_FANOUT_ENABLED !== true) {
104
- clearZoomMotionChrome(module);
95
+ if (MOTION_DOM_FANOUT_ENABLED !== true ||
96
+ !module ||
97
+ typeof document === 'undefined') {
105
98
  return;
106
99
  }
107
100
 
@@ -111,13 +104,17 @@ window.MindMapInteractions = (function () {
111
104
  const targetElement = module?.cssRenderer?.domElement || null;
112
105
  if (module.isZooming === true) {
113
106
  targetElement?.classList?.add?.('is-zooming');
107
+ module._zoomMotionChromeApplied = true;
114
108
  }
115
109
  }, MOTION_DOM_FANOUT_DELAY_MS);
116
110
  }
117
111
 
118
112
  function clearZoomMotionChrome(module) {
119
113
  clearModuleTimer(module, '_zoomMotionChromeTimer');
120
- module?.cssRenderer?.domElement?.classList?.remove?.('is-zooming');
114
+ if (module?._zoomMotionChromeApplied === true) {
115
+ module.cssRenderer?.domElement?.classList?.remove?.('is-zooming');
116
+ module._zoomMotionChromeApplied = false;
117
+ }
121
118
  }
122
119
 
123
120
  function getPanMotionClassElement(module) {
@@ -125,12 +122,11 @@ window.MindMapInteractions = (function () {
125
122
  }
126
123
 
127
124
  function schedulePanMotionChrome(module) {
128
- const element = getPanMotionClassElement(module);
129
- if (!module || !element) {
125
+ if (MOTION_DOM_FANOUT_ENABLED !== true) {
130
126
  return;
131
127
  }
132
- if (MOTION_DOM_FANOUT_ENABLED !== true) {
133
- clearPanMotionChrome(module);
128
+ const element = getPanMotionClassElement(module);
129
+ if (!module || !element) {
134
130
  return;
135
131
  }
136
132
 
@@ -140,13 +136,17 @@ window.MindMapInteractions = (function () {
140
136
  const targetElement = getPanMotionClassElement(module);
141
137
  if (module.isPanning === true && targetElement) {
142
138
  targetElement.classList.add('is-panning');
139
+ module._panMotionChromeApplied = true;
143
140
  }
144
141
  }, MOTION_DOM_FANOUT_DELAY_MS);
145
142
  }
146
143
 
147
144
  function clearPanMotionChrome(module) {
148
145
  clearModuleTimer(module, '_panMotionChromeTimer');
149
- module?.cssRenderer?.domElement?.classList?.remove?.('is-panning');
146
+ if (module?._panMotionChromeApplied === true) {
147
+ module.cssRenderer?.domElement?.classList?.remove?.('is-panning');
148
+ module._panMotionChromeApplied = false;
149
+ }
150
150
  }
151
151
 
152
152
  function scheduleMotionWillChangeEnable(module) {
@@ -572,22 +572,9 @@ window.MindMapInteractions = (function () {
572
572
  }
573
573
 
574
574
  function requestPanLiveRefreshFrames(module) {
575
- if (!module || PAN_LIVE_REFRESH_FORCE_FRAMES <= 0) {
575
+ if (!module) {
576
576
  return;
577
577
  }
578
-
579
- module._forceUpdateFrames = Math.max(
580
- Number(module._forceUpdateFrames || 0),
581
- PAN_LIVE_REFRESH_FORCE_FRAMES
582
- );
583
- module._panLiveRefreshForceFrames = Math.max(
584
- Number(module._panLiveRefreshForceFrames || 0),
585
- PAN_LIVE_REFRESH_FORCE_FRAMES
586
- );
587
- module._cameraNavigationForceFrames = Math.max(
588
- Number(module._cameraNavigationForceFrames || 0),
589
- PAN_LIVE_REFRESH_FORCE_FRAMES
590
- );
591
578
  wakeCameraNavigationAnimation(module, 'pan-input');
592
579
  }
593
580
 
@@ -6320,8 +6307,6 @@ window.MindMapInteractions = (function () {
6320
6307
  module.targetY -= normalizedDeltaY * panFactor;
6321
6308
  const panInputAt = performance.now();
6322
6309
  module._lastPanInputAt = panInputAt;
6323
- module._forceUpdateFrames = Math.max(module._forceUpdateFrames || 0, 2);
6324
- module._cameraNavigationForceFrames = Math.max(module._cameraNavigationForceFrames || 0, 2);
6325
6310
  module._deferPassiveOverlayRefresh = true;
6326
6311
  module._cameraNavigationOverlayInputTime = panInputAt;
6327
6312
  wakeCameraNavigationAnimation(module, 'trackpad-pan-input');
@@ -6372,18 +6357,18 @@ window.MindMapInteractions = (function () {
6372
6357
  * - 기존 performCameraZoom과 동일하지만 event 객체 대신 누적된 값 사용
6373
6358
  */
6374
6359
  function performCameraZoomBuffered(module, deltaY, clientX, clientY, options = {}) {
6360
+ if (!module || !module.camera || !module.container) {
6361
+ /*
6375
6362
  // ▼▼▼ [Fix] 안전 검사 추가 ▼▼▼
6376
6363
  if (!module || !module.camera || !module.container) {
6364
+ */
6377
6365
  console.warn('[Wheel Buffer] Module, camera, or container not ready');
6378
6366
  return;
6379
6367
  }
6368
+ /*
6380
6369
  // ▲▲▲ [Fix] ▲▲▲
6381
6370
  if (module._isLocalSnapGridThemeEnabled?.() === true ||
6382
- module._localSnapGridVisible === true ||
6383
- module._localSnapGridLayer?.isConnected === true) {
6384
- module.setLocalSnapGridClientPoint?.(clientX, clientY);
6385
- }
6386
-
6371
+ */
6387
6372
  let t0 = 0;
6388
6373
  let cameraZ = '?';
6389
6374
  if (WHEEL_PERF_DEBUG) {
@@ -6402,6 +6387,13 @@ window.MindMapInteractions = (function () {
6402
6387
  }
6403
6388
  }
6404
6389
  // ▼▼▼ [Fix] 휠 입력 시간 기록 (줌 종료 타이밍 계산용) ▼▼▼
6390
+ // Keep wheel zoom in the cheap active-zoom local grid branch when the
6391
+ // pointer has not moved. Exact grid style is reconciled after settle.
6392
+ if (module._isLocalSnapGridThemeEnabled?.() === true ||
6393
+ module._localSnapGridVisible === true ||
6394
+ module._localSnapGridLayer?.isConnected === true) {
6395
+ module.setLocalSnapGridClientPoint?.(clientX, clientY);
6396
+ }
6405
6397
  module._zoomInputTime = Number(options?.inputTime || 0) > 0
6406
6398
  ? Number(options.inputTime)
6407
6399
  : performance.now();
@@ -51,7 +51,7 @@
51
51
  // ▲▲▲ [Usage] ▲▲▲
52
52
 
53
53
  // Procedural line rendering settings
54
- const LOD_RENDERER_BUILD_ID = '20260614-remote-fleet-lod-css-v200';
54
+ const LOD_RENDERER_BUILD_ID = '20260620-css3d-flat-pan-v749';
55
55
  const DirtyKind = Object.freeze({
56
56
  ResidentFullRebuild: 'resident-full-rebuild',
57
57
  ResidentPatch: 'resident-patch',
@@ -2903,7 +2903,7 @@
2903
2903
  (this.imageInstanceData?.nodeIds?.length || 0) > 0;
2904
2904
  }
2905
2905
 
2906
- canUseUltraResidentCameraOnlyFrame(camera = this._module?.camera, module = this._module) {
2906
+ canUseUltraResidentCameraOnlyFrame(camera = this._module?.camera, module = this._module, options = {}) {
2907
2907
  if (FORCE_RESIDENT_INSTANCES_IN_MID_FAR !== true || !camera || !module) {
2908
2908
  return false;
2909
2909
  }
@@ -2931,7 +2931,15 @@
2931
2931
  const multiSelectedIds = module?.multiSelectedNodeIds || new Set();
2932
2932
  const selectedNodeId = module?.selectedNodeIdJs || null;
2933
2933
  const selectionKey = buildLodSelectionKey(multiSelectedIds, selectedNodeId);
2934
+ const isCameraNavigationActive = options?.isCameraNavigationActive === true ||
2935
+ options?.isCameraMoving === true ||
2936
+ module?.isPanning === true ||
2937
+ module?.isZooming === true;
2934
2938
  if (this._lastAppliedLodSelectionKey !== selectionKey) {
2939
+ if (isCameraNavigationActive === true) {
2940
+ this._residentCameraOnlyDeferredVisualWork = true;
2941
+ return true;
2942
+ }
2935
2943
  return false;
2936
2944
  }
2937
2945
 
@@ -2955,6 +2963,24 @@
2955
2963
  this._residentCameraOnlyDeferredVisualWork = true;
2956
2964
  }
2957
2965
 
2966
+ if (isCameraNavigationActive === true) {
2967
+ const hasOnlyDeferrableVisualWork = !!(
2968
+ pendingSelectionCount > 0 ||
2969
+ this._residentRebuildPending === true
2970
+ );
2971
+ const hasStructuralResidentDirtyWork = !!(
2972
+ this._instancesDirty === true ||
2973
+ this._imageInstancesDirty === true ||
2974
+ this._positionsOnlyDirty === true ||
2975
+ this._isNodeArrayDirty === true ||
2976
+ dirtyPositionCount > 0
2977
+ );
2978
+ if (hasOnlyDeferrableVisualWork === true && hasStructuralResidentDirtyWork !== true) {
2979
+ this._residentCameraOnlyDeferredVisualWork = true;
2980
+ return true;
2981
+ }
2982
+ }
2983
+
2958
2984
  return !(
2959
2985
  this._instancesDirty === true ||
2960
2986
  this._imageInstancesDirty === true ||
@@ -55,12 +55,10 @@
55
55
  var cameraZ = Math.max(1, Number(position.z || 1));
56
56
  var cameraX = Number(position.x || 0);
57
57
  var cameraY = Number(position.y || 0);
58
- var fov = Number(camera.fov || 45);
59
- if (!Number.isFinite(cameraX) || !Number.isFinite(cameraY) || !Number.isFinite(cameraZ) || !Number.isFinite(fov)) return false;
58
+ var projectionScale = Number(camera.projectionMatrix && camera.projectionMatrix.elements ? camera.projectionMatrix.elements[5] : 0);
59
+ if (!Number.isFinite(cameraX) || !Number.isFinite(cameraY) || !Number.isFinite(cameraZ) || !Number.isFinite(projectionScale) || projectionScale <= 0) return false;
60
60
  if (Math.abs(Number(rotation.x || 0)) > 1e-5 || Math.abs(Number(rotation.y || 0)) > 1e-5 || Math.abs(Number(rotation.z || 0)) > 1e-5) return false;
61
- var vfov = fov * Math.PI / 180;
62
- var viewHeight = 2 * Math.tan(vfov / 2) * cameraZ;
63
- var scale = _height / Math.max(1, viewHeight);
61
+ var scale = (projectionScale * _heightHalf) / cameraZ;
64
62
  var translateX = _widthHalf - cameraX * scale;
65
63
  var translateY = _heightHalf + cameraY * scale;
66
64
  var translateBucket = Math.max(0.001, Number(options && options.translateBucketPx || 0.25));
@@ -82,6 +80,7 @@
82
80
  }
83
81
  this.renderCamera = function(camera){ renderCameraOnly(this, camera); };
84
82
  this.renderFlatCamera = function(camera, options){ return renderFlatCameraOnly(this, camera, options); };
83
+ this.renderFlat = function(scene, camera, options){ if (scene.autoUpdate===true) scene.updateMatrixWorld(); if (renderFlatCameraOnly(this, camera, options)!==true) return false; renderObject(scene, scene, camera, null); return true; };
85
84
  this.render = function(scene, camera){ if (scene.autoUpdate===true) scene.updateMatrixWorld(); var cameraCSSMatrix=renderCameraOnly(this, camera); renderObject(scene, scene, camera, cameraCSSMatrix); };
86
85
  }
87
86
 
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html>
1
+ <!DOCTYPE html>
2
2
  <html lang="en">
3
3
 
4
4
  <head>
@@ -7,11 +7,11 @@
7
7
  <title>MindExec | Business Execution OS for solo builders</title>
8
8
  <meta name="description" content="MindExec is an AI business execution OS for solo builders who want to turn notes, research, assets, and repeatable execution Skills into revenue-producing work." />
9
9
  <base href="/" />
10
- <link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260620-css3d-flat-wheel-v733" />
11
- <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260620-css3d-flat-wheel-v733" />
12
- <!-- ?�▼??Font Awesome (local) ?�▼??-->
10
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260620-css3d-flat-pan-v749" />
11
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260620-css3d-flat-pan-v749" />
12
+ <!-- ?占썩뼹??Font Awesome (local) ?占썩뼹??-->
13
13
  <link rel="stylesheet" href="_content/MindExecution.Shared/lib/font-awesome/css/all.min.css" />
14
- <!-- ?�▲??-->
14
+ <!-- ?占썩뼯??-->
15
15
  <style>
16
16
  #app {
17
17
  min-height: 100vh;
@@ -180,7 +180,7 @@
180
180
  function adjustTextareaHeight(element) {
181
181
  if (!element) return;
182
182
 
183
- // ?�▼??[Change] Add jitter-prevention logic ?�▼??
183
+ // ?占썩뼹??[Change] Add jitter-prevention logic ?占썩뼹??
184
184
  // Compare the previous text length. When content grows, skip the 'auto' reset.
185
185
  // Only reset to 'auto' when shrinking (during deletion).
186
186
  const currentLength = element.value.length;
@@ -191,7 +191,7 @@
191
191
  }
192
192
 
193
193
  element.dataset.prevLength = currentLength; // store current length
194
- // ?�▲??[Change] ?�▲??
194
+ // ?占썩뼯??[Change] ?占썩뼯??
195
195
 
196
196
  const computedStyles = window.getComputedStyle(element);
197
197
  const lineHeight = parseFloat(computedStyles.lineHeight || '24') || 24;
@@ -284,11 +284,11 @@
284
284
  // Prevent default page-level DnD behavior
285
285
  document.addEventListener('dragover', (e) => {
286
286
  e.preventDefault();
287
- // ?�▼??[Change] Remove/avoid stopPropagation (do not disrupt event flow) ?�▼??
287
+ // ?占썩뼹??[Change] Remove/avoid stopPropagation (do not disrupt event flow) ?占썩뼹??
288
288
  // e.stopPropagation();
289
- // ?�▲??[Change] ?�▲??
289
+ // ?占썩뼯??[Change] ?占썩뼯??
290
290
 
291
- // ?�▼??[Change] Default is 'none', but only apply when not handled by children ?�▼??
291
+ // ?占썩뼹??[Change] Default is 'none', but only apply when not handled by children ?占썩뼹??
292
292
  // (If MindMapDnD handles it, this code won't run or will be ignored)
293
293
  // e.dataTransfer.dropEffect = 'none';
294
294
  }, false);
@@ -428,7 +428,7 @@
428
428
  window.__matchesMarketingToolHost(host, location.host || '')
429
429
  );
430
430
  </script>
431
- <!-- ?�▼??[Upgrade] Three.js r168 via ESM ?�▼??-->
431
+ <!-- ?占썩뼹??[Upgrade] Three.js r168 via ESM ?占썩뼹??-->
432
432
  <script type="importmap">
433
433
  {
434
434
  "imports": {
@@ -579,7 +579,7 @@
579
579
  }
580
580
 
581
581
  const base = '_content/MindExecution.Shared/js/';
582
- const scriptVersion = '20260620-css3d-flat-wheel-v733';
582
+ const scriptVersion = '20260620-css3d-flat-pan-v749';
583
583
  const scriptUrl = (script) => `${base}${script}?v=${scriptVersion}`;
584
584
  console.log(`[Script Loader] Shared JS version: ${scriptVersion}`);
585
585
  const criticalScripts = [
@@ -590,7 +590,7 @@
590
590
  'background-themes.js',
591
591
  'mind-map-logic-workers.js',
592
592
  // Troika removed
593
- 'texture-worker-manager.js', // ??Web Worker ?�스�??�성
593
+ 'texture-worker-manager.js', // ??Web Worker ?占쎌뒪占??占쎌꽦
594
594
  'mind-map-texture-factory.js',
595
595
  'mind-map-glow-shader.js', // ??SDF Glow (no textures, GPU only)
596
596
  'mind-map-css3d-manager.js',
@@ -603,7 +603,7 @@
603
603
  'mind-map-interactions.js',
604
604
  'mind-map-dnd.js',
605
605
  'mind-map-lod-renderer.js',
606
- 'mind-map-text-lod-system.js', // ??Text LOD ?�스??(?�적 ?�스�??�질)
606
+ 'mind-map-text-lod-system.js', // ??Text LOD ?占쎌뒪??(?占쎌쟻 ?占쎌뒪占??占쎌쭏)
607
607
  'mind-map-core.js',
608
608
  'mindmap-toolbar.js',
609
609
  'token-manager.js',
@@ -722,7 +722,7 @@
722
722
  window.showBlazorBootError('The page could not finish loading. Reload and try again.');
723
723
  });
724
724
  </script>
725
- <!-- ?�▲??[Upgrade] ?�▲??-->
725
+ <!-- ?占썩뼯??[Upgrade] ?占썩뼯??-->
726
726
  </body>
727
727
 
728
728
  </html>
@@ -732,3 +732,5 @@
732
732
 
733
733
 
734
734
 
735
+
736
+
@@ -1,5 +1,5 @@
1
1
  self.assetsManifest = {
2
- "version": "++e3HCEL",
2
+ "version": "34tfuA2r",
3
3
  "assets": [
4
4
  {
5
5
  "hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
@@ -42,7 +42,7 @@
42
42
  "url": "_content/MindExecution.Shared/css/app.css"
43
43
  },
44
44
  {
45
- "hash": "sha256-IsoaitVgDvGMpqVgxyFAW22yVRgrOb0iXaxNi3Sa6s4=",
45
+ "hash": "sha256-zsNb+3FjEoGDJ9DgMdmdUDcqtZZutCCxWLOD11C0c00=",
46
46
  "url": "_content/MindExecution.Shared/css/mind-map-overrides.css"
47
47
  },
48
48
  {
@@ -78,7 +78,7 @@
78
78
  "url": "_content/MindExecution.Shared/js/marked.min.js"
79
79
  },
80
80
  {
81
- "hash": "sha256-bABeX8wANAaPRLG1o7ZxQkUkfqWUzChfMUTxUgLiyAA=",
81
+ "hash": "sha256-9dRYqTrnhDg0IZsxQn903CNm9U677BGsYRQp5vmq1fM=",
82
82
  "url": "_content/MindExecution.Shared/js/mind-map-core.js"
83
83
  },
84
84
  {
@@ -86,7 +86,7 @@
86
86
  "url": "_content/MindExecution.Shared/js/mind-map-core.js.backup"
87
87
  },
88
88
  {
89
- "hash": "sha256-apogDKTIevZkwck7bwJk6IdZUOmdroyMFIXuT/Lu7M0=",
89
+ "hash": "sha256-EhlES5jLPO7cMczX09TDrcP1PC/KgkF86AYsA+Oap4I=",
90
90
  "url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
91
91
  },
92
92
  {
@@ -106,7 +106,7 @@
106
106
  "url": "_content/MindExecution.Shared/js/mind-map-glow-shader.js"
107
107
  },
108
108
  {
109
- "hash": "sha256-AmkV3no3WRkpMZ4GO+XHpIMuWgixgiTmZjuR1FpgTA8=",
109
+ "hash": "sha256-zEddte5cBTtErNAKwTTpVx4/HQVQpkkF460AYsYe/28=",
110
110
  "url": "_content/MindExecution.Shared/js/mind-map-interactions.js"
111
111
  },
112
112
  {
@@ -114,7 +114,7 @@
114
114
  "url": "_content/MindExecution.Shared/js/mind-map-lod-plan-worker.js"
115
115
  },
116
116
  {
117
- "hash": "sha256-GV55pbSXnKE79f7Fvjh995B6wrzgPmh6BUWD26/RlCA=",
117
+ "hash": "sha256-r7TMeUwMfWBuxJ6MhK0klNJY4GEc0L/8xirnfXQWafs=",
118
118
  "url": "_content/MindExecution.Shared/js/mind-map-lod-renderer.js"
119
119
  },
120
120
  {
@@ -182,7 +182,7 @@
182
182
  "url": "_content/MindExecution.Shared/js/plan-master.js"
183
183
  },
184
184
  {
185
- "hash": "sha256-C7LU7nkx5+E0CFEAx0GNaL5EaiG8g8Cvg5QtDaWTuyo=",
185
+ "hash": "sha256-uNsEEhPWF3LWiI7eGK5Fft6Rw/XSu+PrxGcu5ZcK1MA=",
186
186
  "url": "_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js"
187
187
  },
188
188
  {
@@ -834,7 +834,7 @@
834
834
  "url": "image-manifest.json"
835
835
  },
836
836
  {
837
- "hash": "sha256-f4OkG0Hf42cuV7+AZwsQiqZTLsxdeJjkvo1cJbRNTMs=",
837
+ "hash": "sha256-XyPjNU9Tot83XqrvufSilMaCVa6UO70xgl90gwyfCS0=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: ++e3HCEL */
1
+ /* Manifest version: 34tfuA2r */
2
2
  // Hosted deployments should prefer the network over stale offline caches.
3
3
  // This service worker immediately clears old Blazor offline caches and unregisters itself.
4
4