@mindexec/cli 0.2.458 → 0.2.460

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 (41) hide show
  1. package/desktop-workspace-state.cjs +120 -0
  2. package/electron/main.cjs +17 -6
  3. package/electron/source-smoke.mjs +21 -0
  4. package/electron/windows-package-smoke.mjs +8 -2
  5. package/package.json +6 -5
  6. package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
  7. package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
  8. package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
  9. package/scripts/desktop-workspace-state-smoke.mjs +81 -0
  10. package/server.js +36 -16
  11. package/wwwroot/_content/MindExecution.Shared/js/mind-map-animated-image-preview.js +270 -0
  12. package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +106 -60
  13. package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +30 -26
  14. package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +94 -124
  15. package/wwwroot/_content/MindExecution.Shared/js/mind-map-menu-manager.js +43 -21
  16. package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +45 -13
  17. package/wwwroot/_content/MindExecution.Shared/js/mind-map-render-plan.js +117 -1
  18. package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js +4 -1
  19. package/wwwroot/_framework/{MindExecution.Core.2ch68iyy8o.dll → MindExecution.Core.oqju650dkd.dll} +0 -0
  20. package/wwwroot/_framework/{MindExecution.Kernel.dh617xfv36.dll → MindExecution.Kernel.7zjugdfmfg.dll} +0 -0
  21. package/wwwroot/_framework/{MindExecution.Plugins.Admin.0sm50hbae9.dll → MindExecution.Plugins.Admin.qln7lkmsnn.dll} +0 -0
  22. package/wwwroot/_framework/{MindExecution.Plugins.Business.3wq01orrbu.dll → MindExecution.Plugins.Business.rd6flxuebm.dll} +0 -0
  23. package/wwwroot/_framework/{MindExecution.Plugins.Concept.z8yl28fa2a.dll → MindExecution.Plugins.Concept.0qrgx3epss.dll} +0 -0
  24. package/wwwroot/_framework/{MindExecution.Plugins.Directory.m7murp5oes.dll → MindExecution.Plugins.Directory.4prauy9d1z.dll} +0 -0
  25. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.yuyrbpf0vh.dll → MindExecution.Plugins.PlanMaster.cobfta1p3l.dll} +0 -0
  26. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.rwxvn00rm2.dll → MindExecution.Plugins.YouTube.99bahbgkkr.dll} +0 -0
  27. package/wwwroot/_framework/{MindExecution.Shared.r9k48iyijb.dll → MindExecution.Shared.sevaa4rgkp.dll} +0 -0
  28. package/wwwroot/_framework/{MindExecution.Web.742aribkxm.dll → MindExecution.Web.coqh2ccnuk.dll} +0 -0
  29. package/wwwroot/_framework/blazor.boot.json +21 -21
  30. package/wwwroot/app-icon-1024.png +0 -0
  31. package/wwwroot/apple-touch-icon.png +0 -0
  32. package/wwwroot/appsettings.json +81 -81
  33. package/wwwroot/favicon-32x32.png +0 -0
  34. package/wwwroot/favicon.ico +0 -0
  35. package/wwwroot/icon-192.png +0 -0
  36. package/wwwroot/icon-512.png +0 -0
  37. package/wwwroot/index.html +70 -46
  38. package/wwwroot/manifest.webmanifest +4 -4
  39. package/wwwroot/mindexec-favicon-v3.png +0 -0
  40. package/wwwroot/service-worker-assets.js +888 -880
  41. package/wwwroot/service-worker.js +1 -1
@@ -0,0 +1,270 @@
1
+ // Bounded resident previews for animation-capable image formats.
2
+ (function () {
3
+ 'use strict';
4
+
5
+ function normalizeAssetUrl(url) {
6
+ if (typeof url !== 'string') return '';
7
+ return url.trim()
8
+ .replace(/^http:\/\/localhost(?=:\d+\/assets\/)/i, 'http://127.0.0.1')
9
+ .replace(/^http:\/\/\[::1\](?=:\d+\/assets\/)/i, 'http://127.0.0.1');
10
+ }
11
+
12
+ function getContentType(model) {
13
+ return String(model?.contentType || model?.ContentType || '').trim().toLowerCase();
14
+ }
15
+
16
+ function getMetadata(model) {
17
+ return model?.metadata || model?.Metadata || {};
18
+ }
19
+
20
+ function getMetadataValue(metadata, ...keys) {
21
+ for (const key of keys) {
22
+ const direct = metadata?.[key];
23
+ if (direct !== undefined && direct !== null && String(direct).trim()) {
24
+ return String(direct).trim();
25
+ }
26
+ }
27
+
28
+ const wanted = new Set(keys.map(key => String(key).toLowerCase()));
29
+ for (const [key, value] of Object.entries(metadata || {})) {
30
+ if (wanted.has(String(key).toLowerCase()) && value !== undefined && value !== null && String(value).trim()) {
31
+ return String(value).trim();
32
+ }
33
+ }
34
+
35
+ return '';
36
+ }
37
+
38
+ function hasExtension(url, extension) {
39
+ const pathname = normalizeAssetUrl(url).split(/[?#]/)[0].toLowerCase();
40
+ return !!pathname && pathname.endsWith(extension);
41
+ }
42
+
43
+ function getImageReferenceCandidates(model) {
44
+ const metadata = getMetadata(model);
45
+ return [
46
+ model?.response,
47
+ model?.Response,
48
+ metadata?.OriginalPath,
49
+ metadata?.OriginalUrl,
50
+ metadata?.originalUrl,
51
+ metadata?.ImageUrl,
52
+ metadata?.imageUrl
53
+ ];
54
+ }
55
+
56
+ function isAnimatedGifImageModel(model) {
57
+ if (!model || getContentType(model) !== 'image') return false;
58
+ const metadata = getMetadata(model);
59
+ const mimeType = getMetadataValue(metadata, 'fileMime', 'FileMime', 'mimeType', 'MimeType').toLowerCase();
60
+ return mimeType === 'image/gif' || getImageReferenceCandidates(model).some(url => hasExtension(url, '.gif'));
61
+ }
62
+
63
+ function isWebpImageModel(model) {
64
+ if (!model || getContentType(model) !== 'image') return false;
65
+ const metadata = getMetadata(model);
66
+ const mimeType = getMetadataValue(metadata, 'fileMime', 'FileMime', 'mimeType', 'MimeType').toLowerCase();
67
+ return mimeType === 'image/webp' || getImageReferenceCandidates(model).some(url => hasExtension(url, '.webp'));
68
+ }
69
+
70
+ function isSafeResidentStaticPreviewUrl(url) {
71
+ const normalized = normalizeAssetUrl(url).toLowerCase();
72
+ return normalized.includes('/assets/thumbs/') &&
73
+ !hasExtension(normalized, '.gif') &&
74
+ !hasExtension(normalized, '.webp');
75
+ }
76
+
77
+ function getCanonicalAssetKey(url) {
78
+ const normalized = normalizeAssetUrl(url);
79
+ if (!normalized) return '';
80
+ const withoutQuery = normalized.split(/[?#]/)[0];
81
+ const lower = withoutQuery.toLowerCase();
82
+ for (const marker of ['/assets/thumbs/', '/assets/']) {
83
+ if (!lower.includes(marker)) continue;
84
+ const assetName = withoutQuery.slice(lower.lastIndexOf(marker) + marker.length).split('/').pop() || '';
85
+ const dotIndex = assetName.lastIndexOf('.');
86
+ return (dotIndex > 0 ? assetName.slice(0, dotIndex) : assetName).toLowerCase();
87
+ }
88
+ return withoutQuery.toLowerCase();
89
+ }
90
+
91
+ function areAssetUrlsEquivalent(leftUrl, rightUrl) {
92
+ const leftKey = getCanonicalAssetKey(leftUrl);
93
+ const rightKey = getCanonicalAssetKey(rightUrl);
94
+ return leftKey && rightKey
95
+ ? leftKey === rightKey
96
+ : normalizeAssetUrl(leftUrl) === normalizeAssetUrl(rightUrl);
97
+ }
98
+
99
+ function getLocalWebpAssetFilename(...urls) {
100
+ for (const candidate of urls) {
101
+ const normalized = normalizeAssetUrl(candidate);
102
+ if (!normalized || !hasExtension(normalized, '.webp')) continue;
103
+
104
+ let pathname = normalized.split(/[?#]/)[0];
105
+ try {
106
+ pathname = new URL(normalized, window.location?.origin || 'http://127.0.0.1').pathname;
107
+ } catch { }
108
+
109
+ const normalizedPath = pathname.replace(/\\/g, '/');
110
+ if (!/(^|\/)assets\//i.test(normalizedPath)) continue;
111
+
112
+ const rawFilename = normalizedPath.split('/').pop() || '';
113
+ let filename = rawFilename;
114
+ try { filename = decodeURIComponent(rawFilename); } catch { }
115
+ if (/^[^/\\]+\.webp$/i.test(filename)) return filename;
116
+ }
117
+
118
+ return '';
119
+ }
120
+
121
+ function getBridgeOrigin() {
122
+ const configuredOrigin = String(window.MindExecLocalBridge?.activeBridgeUrl || '').trim();
123
+ return (configuredOrigin || window.location?.origin || 'http://127.0.0.1:5147').replace(/\/$/, '');
124
+ }
125
+
126
+ async function getBridgeAuthHeaders(bridgeOrigin) {
127
+ if (window.__mindExecutionBridgeAuthPromise) {
128
+ try { return await window.__mindExecutionBridgeAuthPromise; } catch { }
129
+ }
130
+
131
+ const authPromise = (async () => {
132
+ const nonce = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
133
+ const response = await fetch(`${bridgeOrigin}/api/status?cb=${encodeURIComponent(nonce)}`, {
134
+ cache: 'no-store',
135
+ targetAddressSpace: 'loopback'
136
+ });
137
+ if (!response.ok) return {};
138
+ const status = await response.json();
139
+ const token = String(status?.bridgeToken || '').trim();
140
+ const header = String(status?.bridgeTokenHeader || 'X-Bridge-Token').trim();
141
+ return token && header ? { [header]: token } : {};
142
+ })();
143
+ window.__mindExecutionBridgeAuthPromise = authPromise;
144
+
145
+ try {
146
+ return await authPromise;
147
+ } catch {
148
+ if (window.__mindExecutionBridgeAuthPromise === authPromise) {
149
+ window.__mindExecutionBridgeAuthPromise = null;
150
+ }
151
+ return {};
152
+ }
153
+ }
154
+
155
+ async function resolveLocalWebpResidentPreview(item, assetUrls) {
156
+ const sourceUrl = normalizeAssetUrl(
157
+ item?.originalUrl ||
158
+ assetUrls?.fullResUrl ||
159
+ assetUrls?.originalUrl ||
160
+ assetUrls?.originalPath ||
161
+ item?.previewUrl || ''
162
+ );
163
+ const filename = getLocalWebpAssetFilename(
164
+ sourceUrl,
165
+ assetUrls?.originalPath,
166
+ assetUrls?.originalUrl,
167
+ item?.previewUrl
168
+ );
169
+ if (!filename || typeof fetch !== 'function') {
170
+ return { sourceUrl, previewUrl: '' };
171
+ }
172
+
173
+ const bridgeOrigin = getBridgeOrigin();
174
+ const headers = await getBridgeAuthHeaders(bridgeOrigin);
175
+ const endpoint = `${bridgeOrigin}/api/assets/generate-thumbnail/${encodeURIComponent(filename)}`;
176
+ const response = await fetch(endpoint, {
177
+ method: 'POST',
178
+ headers,
179
+ cache: 'no-store',
180
+ targetAddressSpace: 'loopback'
181
+ });
182
+ if (!response.ok) {
183
+ throw new Error(`resident-static-preview-http-${response.status}`);
184
+ }
185
+
186
+ const payload = await response.json();
187
+ const previewUrl = normalizeAssetUrl(payload?.thumbnailUrl || '');
188
+ if (!isSafeResidentStaticPreviewUrl(previewUrl)) {
189
+ throw new Error('resident-static-preview-invalid-url');
190
+ }
191
+
192
+ return { sourceUrl, previewUrl };
193
+ }
194
+
195
+ function getResidentStaticPreviewUrl(renderer, nodeId, model, assetUrls) {
196
+ const normalizedNodeId = String(nodeId || '').trim();
197
+ const previewUrl = normalizeAssetUrl(assetUrls?.previewUrl || '');
198
+ if (!isWebpImageModel(model)) return previewUrl;
199
+
200
+ const sourceUrl = normalizeAssetUrl(
201
+ assetUrls?.fullResUrl || assetUrls?.originalUrl || assetUrls?.originalPath || previewUrl
202
+ );
203
+ const record = normalizedNodeId ? renderer?._residentStaticPreviewUrls?.get(normalizedNodeId) : null;
204
+ if (record?.sourceUrl === sourceUrl && isSafeResidentStaticPreviewUrl(record.previewUrl)) {
205
+ return normalizeAssetUrl(record.previewUrl);
206
+ }
207
+
208
+ if (normalizedNodeId && record) renderer._residentStaticPreviewUrls.delete(normalizedNodeId);
209
+ const existingThumbnailUrl = normalizeAssetUrl(assetUrls?.thumbnailUrl || '');
210
+ if (normalizedNodeId && sourceUrl && isSafeResidentStaticPreviewUrl(existingThumbnailUrl)) {
211
+ renderer._residentStaticPreviewUrls.set(normalizedNodeId, {
212
+ sourceUrl,
213
+ previewUrl: existingThumbnailUrl
214
+ });
215
+ return existingThumbnailUrl;
216
+ }
217
+
218
+ return '';
219
+ }
220
+
221
+ async function resolveSafeResidentPreviewUrl(renderer, item, assetUrls, getCurrentAssetUrls, onReady) {
222
+ if (!isWebpImageModel(item?.model)) {
223
+ return normalizeAssetUrl(item?.previewUrl || assetUrls?.previewUrl || '');
224
+ }
225
+
226
+ const nodeId = String(item?.nodeId || '').trim();
227
+ const existingPreviewUrl = getResidentStaticPreviewUrl(renderer, nodeId, item.model, {
228
+ ...assetUrls,
229
+ thumbnailUrl: item?.thumbnailUrl || assetUrls?.thumbnailUrl || ''
230
+ });
231
+ if (existingPreviewUrl) return existingPreviewUrl;
232
+
233
+ const resolved = await resolveLocalWebpResidentPreview(item, assetUrls);
234
+ const sourceUrl = normalizeAssetUrl(resolved?.sourceUrl || '');
235
+ const previewUrl = normalizeAssetUrl(resolved?.previewUrl || '');
236
+ if (!isSafeResidentStaticPreviewUrl(previewUrl)) return '';
237
+
238
+ if (
239
+ renderer?._disposed === true ||
240
+ item?.previewEpoch !== renderer?._residentStaticPreviewEpoch ||
241
+ renderer?.isInLODMode !== true
242
+ ) {
243
+ return '';
244
+ }
245
+
246
+ const currentEntry = renderer?._module?.nodeObjectsById?.get?.(nodeId) || null;
247
+ const currentAssetUrls = getCurrentAssetUrls?.(currentEntry?.model) || {};
248
+ const currentSourceUrl = normalizeAssetUrl(
249
+ currentAssetUrls?.fullResUrl ||
250
+ currentAssetUrls?.originalUrl ||
251
+ currentAssetUrls?.originalPath ||
252
+ currentAssetUrls?.previewUrl || ''
253
+ );
254
+ if (!currentEntry?.model || currentSourceUrl !== sourceUrl) return '';
255
+
256
+ renderer._residentStaticPreviewUrls.set(nodeId, { sourceUrl, previewUrl });
257
+ onReady?.({ nodeId, sourceKind: 'webp', previewUrl });
258
+ return previewUrl;
259
+ }
260
+
261
+ window.MindMapAnimatedImagePreview = Object.freeze({
262
+ normalizeAssetUrl,
263
+ isAnimatedGifImageModel,
264
+ isWebpImageModel,
265
+ isSafeResidentStaticPreviewUrl,
266
+ areAssetUrlsEquivalent,
267
+ getResidentStaticPreviewUrl,
268
+ resolveSafeResidentPreviewUrl
269
+ });
270
+ })();
@@ -85,6 +85,8 @@
85
85
  const PASSIVE_OVERLAY_IDLE_DELAY_MS = 500;
86
86
  const PASSIVE_OVERLAY_PAN_IDLE_DELAY_MS = 500;
87
87
  const CURSOR_DOM_BLINK_DURATION_MS = 1000;
88
+ const CURSOR_WORLD_SIZE = 20;
89
+ const CURSOR_WORLD_THICKNESS = 4;
88
90
  const RENDER_DEBUG_METRICS_CACHE_MS = 250;
89
91
  const ANIMATION_IDLE_LOW_FPS_DELAY_MS = 500;
90
92
  const WHEEL_ZOOM_BURST_QUIET_FRAME_LIMIT = 12;
@@ -108,13 +110,13 @@
108
110
  const LOCAL_SNAP_GRID_DIAMETER_PX = LOCAL_SNAP_GRID_RADIUS_PX * 2;
109
111
  const LOCAL_SNAP_GRID_FADE_PX = 100;
110
112
  const LOCAL_SNAP_GRID_SOLID_RADIUS_PX = LOCAL_SNAP_GRID_RADIUS_PX - LOCAL_SNAP_GRID_FADE_PX;
111
- const LOCAL_SNAP_GRID_MIN_STEP_PX = 8;
112
- const LOCAL_SNAP_GRID_MAX_STEP_PX = 56;
113
+ const LOCAL_SNAP_GRID_RASTER_MIN_STEP_PX = 0.5;
114
+ const LOCAL_SNAP_GRID_RASTER_MAX_PERIOD_PX = 2048;
113
115
  const LOCAL_SNAP_GRID_OPACITY_BUCKET = 0.04;
114
- const LOCAL_SNAP_GRID_DEFAULT_STEP_PX = 24;
115
- const LOCAL_SNAP_GRID_DEFAULT_MAJOR_STEP_PX = 240;
116
- const LOCAL_SNAP_GRID_DEFAULT_OPACITY = 0.42;
117
- const LOCAL_SNAP_GRID_TILE_OVERSCAN_PX = LOCAL_SNAP_GRID_MAX_STEP_PX * 10;
116
+ const LOCAL_SNAP_GRID_DEFAULT_STEP_PX = 10;
117
+ const LOCAL_SNAP_GRID_DEFAULT_MAJOR_STEP_PX = 100;
118
+ const LOCAL_SNAP_GRID_DEFAULT_OPACITY = 0.58;
119
+ const LOCAL_SNAP_GRID_TILE_OVERSCAN_PX = 560;
118
120
  const LOCAL_SNAP_GRID_COMPOSITOR_SCALE_MIN = 0.8;
119
121
  const LOCAL_SNAP_GRID_COMPOSITOR_SCALE_MAX = 1.5;
120
122
  const CAMERA_STATIONARY_INTERACTION_FRAME_DELAY_MS = 48;
@@ -289,11 +291,11 @@
289
291
  position: absolute;
290
292
  left: 0;
291
293
  top: 0;
292
- width: 20px;
293
- height: 20px;
294
+ width: ${CURSOR_WORLD_SIZE}px;
295
+ height: ${CURSOR_WORLD_SIZE}px;
294
296
  box-sizing: border-box;
295
- border-top: 4px solid #000;
296
- border-left: 4px solid #000;
297
+ border-top: ${CURSOR_WORLD_THICKNESS}px solid #000;
298
+ border-left: ${CURSOR_WORLD_THICKNESS}px solid #000;
297
299
  pointer-events: none;
298
300
  z-index: 2147483000;
299
301
  animation: mindMapCursorDomBlink ${CURSOR_DOM_BLINK_DURATION_MS}ms steps(1, end) infinite;
@@ -347,8 +349,11 @@
347
349
  const ndcY = ((cursor.y - camera.position.y) * 2) / viewHeight;
348
350
  const x = ((ndcX + 1) / 2) * width;
349
351
  const y = ((1 - ndcY) / 2) * height;
352
+ const pxPerWorld = height / Math.max(1, viewHeight);
353
+ const cursorSizePx = Math.max(0.5, CURSOR_WORLD_SIZE * pxPerWorld);
354
+ const cursorThicknessPx = Math.max(0.5, CURSOR_WORLD_THICKNESS * pxPerWorld);
350
355
  const visible = ndcX >= -1.1 && ndcX <= 1.1 && ndcY >= -1.1 && ndcY <= 1.1;
351
- const positionKey = `${visible ? 1 : 0}|${Math.round(x)}|${Math.round(y)}|${width}|${height}`;
356
+ const positionKey = `${visible ? 1 : 0}|${Math.round(x)}|${Math.round(y)}|${cursorSizePx.toFixed(2)}|${cursorThicknessPx.toFixed(2)}|${width}|${height}`;
352
357
 
353
358
  if (positionKey === module._lastCursorDomPositionKey) {
354
359
  return false;
@@ -360,6 +365,10 @@
360
365
  element.style.display = visible ? 'block' : 'none';
361
366
  }
362
367
  if (visible) {
368
+ element.style.width = `${cursorSizePx.toFixed(2)}px`;
369
+ element.style.height = `${cursorSizePx.toFixed(2)}px`;
370
+ element.style.borderTopWidth = `${cursorThicknessPx.toFixed(2)}px`;
371
+ element.style.borderLeftWidth = `${cursorThicknessPx.toFixed(2)}px`;
363
372
  element.style.transform = `translate3d(${x.toFixed(2)}px, ${y.toFixed(2)}px, 0)`;
364
373
  }
365
374
  return true;
@@ -3669,23 +3678,8 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3669
3678
  _resolveLocalSnapGridWorldStep(pxPerWorld) {
3670
3679
  const safePxPerWorld = Math.max(Number.EPSILON, Number(pxPerWorld) || Number.EPSILON);
3671
3680
  const baseWorldStep = Math.max(Number.EPSILON, Number(this.GRID_SIZE || 10));
3672
- let worldStep = Math.max(
3673
- Number.EPSILON,
3674
- Number(this._lastLocalSnapGridWorldStep || baseWorldStep)
3675
- );
3676
- let smallStepPx = worldStep * safePxPerWorld;
3677
- let guard = 0;
3678
-
3679
- while (smallStepPx < LOCAL_SNAP_GRID_MIN_STEP_PX && guard < 24) {
3680
- worldStep *= 2;
3681
- smallStepPx *= 2;
3682
- guard += 1;
3683
- }
3684
- while (smallStepPx > LOCAL_SNAP_GRID_MAX_STEP_PX && guard < 48) {
3685
- worldStep /= 2;
3686
- smallStepPx /= 2;
3687
- guard += 1;
3688
- }
3681
+ const worldStep = baseWorldStep;
3682
+ const smallStepPx = worldStep * safePxPerWorld;
3689
3683
 
3690
3684
  return {
3691
3685
  worldStep,
@@ -3709,37 +3703,43 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3709
3703
  return false;
3710
3704
  }
3711
3705
 
3712
- const smallStep = Math.max(LOCAL_SNAP_GRID_MIN_STEP_PX, Number(smallStepPx) || LOCAL_SNAP_GRID_DEFAULT_STEP_PX);
3706
+ const smallStep = Math.max(LOCAL_SNAP_GRID_RASTER_MIN_STEP_PX, Number(smallStepPx) || LOCAL_SNAP_GRID_DEFAULT_STEP_PX);
3713
3707
  const majorStep = Math.max(
3714
3708
  smallStep,
3715
3709
  Math.min(
3716
- LOCAL_SNAP_GRID_TILE_OVERSCAN_PX,
3710
+ LOCAL_SNAP_GRID_RASTER_MAX_PERIOD_PX,
3717
3711
  Number(majorStepPx) || LOCAL_SNAP_GRID_DEFAULT_MAJOR_STEP_PX
3718
3712
  )
3719
3713
  );
3720
- const tileSize = Math.max(1, Math.round(majorStep));
3721
- if (canvas.width !== tileSize) {
3722
- canvas.width = tileSize;
3714
+ const tileSize = Math.max(1, majorStep);
3715
+ const viewportDpr = Math.max(1, Math.min(3, Number(this._lastViewportDpr || window.devicePixelRatio || 1)));
3716
+ const bitmapSize = Math.max(1, Math.ceil(tileSize * viewportDpr));
3717
+ if (canvas.width !== bitmapSize) {
3718
+ canvas.width = bitmapSize;
3723
3719
  }
3724
- if (canvas.height !== tileSize) {
3725
- canvas.height = tileSize;
3720
+ if (canvas.height !== bitmapSize) {
3721
+ canvas.height = bitmapSize;
3726
3722
  }
3723
+ const rasterScale = bitmapSize / tileSize;
3724
+ ctx.setTransform(rasterScale, 0, 0, rasterScale, 0, 0);
3727
3725
  ctx.clearRect(0, 0, tileSize, tileSize);
3728
3726
 
3729
3727
  if (styleKind === 'dot') {
3730
- ctx.fillStyle = 'rgba(58, 69, 88, 0.46)';
3731
- const dotRadius = 1.15;
3732
- for (let y = 0; y < tileSize; y += smallStep) {
3733
- for (let x = 0; x < tileSize; x += smallStep) {
3734
- ctx.beginPath();
3735
- ctx.arc(x, y, dotRadius, 0, Math.PI * 2);
3736
- ctx.fill();
3728
+ if (smallStep >= 2.25) {
3729
+ ctx.fillStyle = 'rgba(58, 69, 88, 0.68)';
3730
+ const dotRadius = 1.2;
3731
+ for (let y = 0; y < tileSize; y += smallStep) {
3732
+ for (let x = 0; x < tileSize; x += smallStep) {
3733
+ ctx.beginPath();
3734
+ ctx.arc(x, y, dotRadius, 0, Math.PI * 2);
3735
+ ctx.fill();
3736
+ }
3737
3737
  }
3738
3738
  }
3739
3739
 
3740
- ctx.fillStyle = 'rgba(15, 23, 42, 0.62)';
3740
+ ctx.fillStyle = 'rgba(15, 23, 42, 0.82)';
3741
3741
  ctx.beginPath();
3742
- ctx.arc(0, 0, 1.85, 0, Math.PI * 2);
3742
+ ctx.arc(0, 0, 1.9, 0, Math.PI * 2);
3743
3743
  ctx.fill();
3744
3744
  } else {
3745
3745
  const drawLines = (step, color, width) => {
@@ -3779,17 +3779,17 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3779
3779
 
3780
3780
  const safeStyleKind = styleKind === 'dot' ? 'dot' : 'line';
3781
3781
  const safeSmallStep = Math.max(
3782
- LOCAL_SNAP_GRID_MIN_STEP_PX,
3783
- Math.min(LOCAL_SNAP_GRID_MAX_STEP_PX, Number(smallStepPx) || LOCAL_SNAP_GRID_DEFAULT_STEP_PX)
3782
+ LOCAL_SNAP_GRID_RASTER_MIN_STEP_PX,
3783
+ Number(smallStepPx) || LOCAL_SNAP_GRID_DEFAULT_STEP_PX
3784
3784
  );
3785
3785
  const safeMajorStep = Math.max(
3786
3786
  safeSmallStep,
3787
3787
  Math.min(
3788
- LOCAL_SNAP_GRID_TILE_OVERSCAN_PX,
3788
+ LOCAL_SNAP_GRID_RASTER_MAX_PERIOD_PX,
3789
3789
  Number(majorStepPx) || LOCAL_SNAP_GRID_DEFAULT_MAJOR_STEP_PX
3790
3790
  )
3791
3791
  );
3792
- const safeOpacity = Math.max(0.2, Math.min(0.56, Number(opacity) || LOCAL_SNAP_GRID_DEFAULT_OPACITY));
3792
+ const safeOpacity = Math.max(0.28, Math.min(0.68, Number(opacity) || LOCAL_SNAP_GRID_DEFAULT_OPACITY));
3793
3793
  const localGridKey = [
3794
3794
  keyPrefix,
3795
3795
  safeStyleKind,
@@ -3797,13 +3797,21 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3797
3797
  Math.round(safeMajorStep * 100),
3798
3798
  Math.round(safeOpacity * 100)
3799
3799
  ].join('|');
3800
- const backgroundSizeKey = safeMajorStep.toFixed(3);
3800
+ const smallStepValue = safeSmallStep.toFixed(3);
3801
+ const majorStepValue = safeMajorStep.toFixed(3);
3802
+ const showMinorDots = safeSmallStep >= 2.25;
3803
+ const backgroundSizeValue = safeStyleKind === 'dot'
3804
+ ? (showMinorDots
3805
+ ? `${majorStepValue}px ${majorStepValue}px, ${smallStepValue}px ${smallStepValue}px`
3806
+ : `${majorStepValue}px ${majorStepValue}px`)
3807
+ : `${majorStepValue}px ${majorStepValue}px`;
3808
+ const backgroundSizeKey = backgroundSizeValue;
3801
3809
 
3802
3810
  if (localGridKey === this._lastLocalSnapGridKey) {
3803
3811
  this._localSnapGridRasterPeriodPx = safeMajorStep;
3804
3812
  if (backgroundSizeKey !== this._lastLocalSnapGridBackgroundSizeKey) {
3805
3813
  this._lastLocalSnapGridBackgroundSizeKey = backgroundSizeKey;
3806
- layer.style.backgroundSize = `${backgroundSizeKey}px ${backgroundSizeKey}px`;
3814
+ layer.style.backgroundSize = backgroundSizeValue;
3807
3815
  this._lastLocalSnapGridTransformKey = '';
3808
3816
  }
3809
3817
  return false;
@@ -3811,6 +3819,20 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3811
3819
 
3812
3820
  const opacityValue = safeOpacity.toFixed(3);
3813
3821
  this._lastLocalSnapGridOpacity = opacityValue;
3822
+ if (safeStyleKind === 'dot') {
3823
+ const majorDot = 'radial-gradient(circle at 0 0, rgba(15, 23, 42, 0.82) 0 1.9px, transparent 2px)';
3824
+ const minorDot = 'radial-gradient(circle at 0 0, rgba(58, 69, 88, 0.68) 0 1.2px, transparent 1.3px)';
3825
+ const backgroundImage = showMinorDots ? `${majorDot}, ${minorDot}` : majorDot;
3826
+ this._lastLocalSnapGridKey = localGridKey;
3827
+ this._lastLocalSnapGridPatternPeriodPx = safeMajorStep;
3828
+ this._localSnapGridRasterPeriodPx = safeMajorStep;
3829
+ this._lastLocalSnapGridTransformKey = '';
3830
+ if (layer.style.backgroundImage !== backgroundImage) layer.style.backgroundImage = backgroundImage;
3831
+ this._lastLocalSnapGridBackgroundSizeKey = backgroundSizeKey;
3832
+ if (layer.style.backgroundSize !== backgroundSizeValue) layer.style.backgroundSize = backgroundSizeValue;
3833
+ if (layer.style.opacity !== opacityValue) layer.style.opacity = opacityValue;
3834
+ return true;
3835
+ }
3814
3836
  const tileCanvas = this._localSnapGridTileCanvas || document.createElement('canvas');
3815
3837
  this._localSnapGridTileCanvas = tileCanvas;
3816
3838
  const tileSize = this._drawLocalSnapGridTile(
@@ -3828,7 +3850,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3828
3850
  this._lastLocalSnapGridTransformKey = '';
3829
3851
  layer.style.backgroundImage = `url("${tileCanvas.toDataURL('image/png')}")`;
3830
3852
  this._lastLocalSnapGridBackgroundSizeKey = backgroundSizeKey;
3831
- layer.style.backgroundSize = `${backgroundSizeKey}px ${backgroundSizeKey}px`;
3853
+ layer.style.backgroundSize = backgroundSizeValue;
3832
3854
  if (layer.style.opacity !== opacityValue) {
3833
3855
  layer.style.opacity = opacityValue;
3834
3856
  }
@@ -3936,7 +3958,9 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3936
3958
  const top = Number(viewportMetrics.top || 0);
3937
3959
  const pointerRevision = Math.max(0, Number(this._localSnapGridPointerRevision || 0));
3938
3960
 
3939
- if (this.isZooming === true && !!this._lastLocalSnapGridKey) {
3961
+ if (this.isZooming === true &&
3962
+ !!this._lastLocalSnapGridKey &&
3963
+ this._getLocalSnapGridStyleKind() !== 'dot') {
3940
3964
  this._syncLocalSnapGridSpotlightPosition(width, height, left, top);
3941
3965
  this._lastLocalSnapGridPositionRevision = pointerRevision;
3942
3966
  this._localSnapGridStyleDeferredForZoom = true;
@@ -3955,13 +3979,14 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
3955
3979
  this._lastLocalSnapGridWorldStep = gridProjection.worldStep;
3956
3980
  this._lastLocalSnapGridWorldPeriod = gridProjection.worldStep * 10;
3957
3981
  const styleKind = this._getLocalSnapGridStyleKind();
3958
- const rawOpacity = Math.max(0.24, Math.min(0.54, cameraZ <= 1800 ? 0.48 : 0.48 - ((cameraZ - 1800) / 36000)));
3982
+ const rawOpacity = Math.max(0.38, Math.min(0.64, cameraZ <= 1800 ? 0.60 : 0.60 - ((cameraZ - 1800) / 36000)));
3959
3983
  const opacity = Math.max(
3960
- 0.24,
3961
- Math.min(0.54, Math.round(rawOpacity / LOCAL_SNAP_GRID_OPACITY_BUCKET) * LOCAL_SNAP_GRID_OPACITY_BUCKET)
3984
+ 0.38,
3985
+ Math.min(0.64, Math.round(rawOpacity / LOCAL_SNAP_GRID_OPACITY_BUCKET) * LOCAL_SNAP_GRID_OPACITY_BUCKET)
3962
3986
  );
3963
3987
  const shouldDeferLocalGridStyleForZoom =
3964
3988
  this.isZooming === true &&
3989
+ styleKind !== 'dot' &&
3965
3990
  !!this._lastLocalSnapGridKey;
3966
3991
 
3967
3992
  if (shouldDeferLocalGridStyleForZoom !== true) {
@@ -4013,10 +4038,24 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
4013
4038
  const height = Math.max(1, Number(viewportMetrics.height || 0));
4014
4039
  const left = Number(viewportMetrics.left || 0);
4015
4040
  const top = Number(viewportMetrics.top || 0);
4041
+ const styleKind = this._getLocalSnapGridStyleKind();
4042
+ if (this.isZooming === true && styleKind === 'dot') {
4043
+ const cameraZ = Math.max(1, Number(this.camera?.position?.z || 1));
4044
+ const vfov = (Number(this.camera?.fov || 45) * Math.PI) / 180;
4045
+ const pxPerWorld = height / Math.max(1, 2 * Math.tan(vfov / 2) * cameraZ);
4046
+ const projection = this._resolveLocalSnapGridWorldStep(pxPerWorld);
4047
+ const rawOpacity = Math.max(0.38, Math.min(0.64,
4048
+ cameraZ <= 1800 ? 0.60 : 0.60 - ((cameraZ - 1800) / 36000)));
4049
+ const opacity = Math.max(0.38, Math.min(0.64,
4050
+ Math.round(rawOpacity / LOCAL_SNAP_GRID_OPACITY_BUCKET) * LOCAL_SNAP_GRID_OPACITY_BUCKET));
4051
+ this._applyLocalSnapGridStyle(
4052
+ layer, styleKind, projection.smallStepPx, projection.majorStepPx, opacity);
4053
+ this._localSnapGridStyleDeferredForZoom = false;
4054
+ }
4016
4055
  this._syncLocalSnapGridSpotlightPosition(width, height, left, top);
4017
4056
  this._lastLocalSnapGridPositionRevision = pointerRevision;
4018
4057
 
4019
- if (this.isZooming === true) {
4058
+ if (this.isZooming === true && styleKind !== 'dot') {
4020
4059
  this._localSnapGridStyleDeferredForZoom = true;
4021
4060
  }
4022
4061
  this._setLocalSnapGridVisible(true, this._lastLocalSnapGridOpacity || null);
@@ -6196,7 +6235,10 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
6196
6235
  this.lodRenderer?.hasPendingNearCssWarmup?.(this) === true;
6197
6236
  const isResidentMidFarLodFrame = this.useLODRendering &&
6198
6237
  this.lodRenderer?.usesResidentMidFarInstances?.(this.camera) === true;
6199
- const isFullResMode = this.lodRenderer?.isInLODMode !== true;
6238
+ // The current camera band owns the boundary frame. isInLODMode
6239
+ // still describes the prior frame until updateLOD applies the
6240
+ // resident/full-res handoff.
6241
+ const isFullResMode = isResidentMidFarLodFrame !== true;
6200
6242
 
6201
6243
  // Event-driven viewport sync handles the common case. Keep the
6202
6244
  // fallback poll slow, and skip it entirely while resident MID/FAR
@@ -6232,7 +6274,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
6232
6274
  const isLodModeActive = this.lodRenderer?.isInLODMode === true;
6233
6275
  const residentDirty = isResidentMidFarLodFrame &&
6234
6276
  this.lodRenderer?.hasResidentDirtyWork?.(this.camera, this) === true;
6235
- const residentBandChanging = isResidentMidFarLodFrame &&
6277
+ const residentBandChanging = this.useLODRendering === true &&
6236
6278
  this.lodRenderer?.willLodBandChange?.(this.camera) === true;
6237
6279
  let canDeferResidentDirtyIdleFrame = false;
6238
6280
  let canDeferResidentDirtyMotionFrame = false;
@@ -7332,8 +7374,12 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
7332
7374
  !hasNonCameraNavigationForcedUpdate &&
7333
7375
  !hasMotionBlockingOverlayFocus &&
7334
7376
  !this.isWindowResizing;
7335
- if (isMenuOverlayUiEnabled && window.MindMapMenuManager && shouldDeferFloatingUiDuringMotion !== true) {
7336
- window.MindMapMenuManager.update();
7377
+ if (isMenuOverlayUiEnabled && window.MindMapMenuManager) {
7378
+ if (shouldDeferFloatingUiDuringMotion === true) {
7379
+ window.MindMapMenuManager.updateForCameraMotion?.();
7380
+ } else {
7381
+ window.MindMapMenuManager.update();
7382
+ }
7337
7383
  } else if (!isMenuOverlayUiEnabled && window.MindMapMenuManager?.hideMenu) {
7338
7384
  window.MindMapMenuManager.hideMenu();
7339
7385
  }