@mindexec/cli 0.2.0

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 (232) hide show
  1. package/README.md +275 -0
  2. package/codex-runtime.js +960 -0
  3. package/launch-bridge.cjs +162 -0
  4. package/package.json +61 -0
  5. package/port-guard.cjs +232 -0
  6. package/scripts/setup-tree-sitter-grammars.mjs +59 -0
  7. package/server.js +8422 -0
  8. package/start-bridge.bat +32 -0
  9. package/start-bridge.sh +81 -0
  10. package/tree-sitter-grammars/README.md +18 -0
  11. package/tree-sitter-grammars/tree-sitter-c_sharp.wasm +0 -0
  12. package/tree-sitter-grammars/tree-sitter-go.wasm +0 -0
  13. package/tree-sitter-grammars/tree-sitter-java.wasm +0 -0
  14. package/tree-sitter-grammars/tree-sitter-javascript.wasm +0 -0
  15. package/tree-sitter-grammars/tree-sitter-python.wasm +0 -0
  16. package/tree-sitter-grammars/tree-sitter-rust.wasm +0 -0
  17. package/tree-sitter-grammars/tree-sitter-tsx.wasm +0 -0
  18. package/tree-sitter-grammars/tree-sitter-typescript.wasm +0 -0
  19. package/wwwroot/MindExecution.Web.styles.css +3 -0
  20. package/wwwroot/_content/MindExecution.Plugins.Admin/css/admin-dashboard.css +546 -0
  21. package/wwwroot/_content/MindExecution.Plugins.Directory/MindExecution.Plugins.Directory.u7utcng611.bundle.scp.css +7 -0
  22. package/wwwroot/_content/MindExecution.Plugins.Directory/background.png +0 -0
  23. package/wwwroot/_content/MindExecution.Plugins.Directory/directory-manager.js +202 -0
  24. package/wwwroot/_content/MindExecution.Plugins.Directory/exampleJsInterop.js +6 -0
  25. package/wwwroot/_content/MindExecution.Plugins.YouTube/css/youtube-search.css +251 -0
  26. package/wwwroot/_content/MindExecution.Shared/MindExecution.Shared.wsano1j4wp.bundle.scp.css +4 -0
  27. package/wwwroot/_content/MindExecution.Shared/css/admin-dashboard.css +559 -0
  28. package/wwwroot/_content/MindExecution.Shared/css/app.css +1 -0
  29. package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +2936 -0
  30. package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Bold.ttf +0 -0
  31. package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Regular.ttf +0 -0
  32. package/wwwroot/_content/MindExecution.Shared/js/agent-visualization.js +359 -0
  33. package/wwwroot/_content/MindExecution.Shared/js/background-themes.js +1721 -0
  34. package/wwwroot/_content/MindExecution.Shared/js/code-master.js +8316 -0
  35. package/wwwroot/_content/MindExecution.Shared/js/file-system-helper.js +639 -0
  36. package/wwwroot/_content/MindExecution.Shared/js/helpers/InfiniteGridHelper.js +109 -0
  37. package/wwwroot/_content/MindExecution.Shared/js/marked.min.js +69 -0
  38. package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +7982 -0
  39. package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js.backup +1059 -0
  40. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +15803 -0
  41. package/wwwroot/_content/MindExecution.Shared/js/mind-map-dev-guards.js +325 -0
  42. package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js +1430 -0
  43. package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js.bak +434 -0
  44. package/wwwroot/_content/MindExecution.Shared/js/mind-map-glow-shader.js +260 -0
  45. package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +7640 -0
  46. package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-plan-worker.js +160 -0
  47. package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +9923 -0
  48. package/wwwroot/_content/MindExecution.Shared/js/mind-map-logic-workers.js +977 -0
  49. package/wwwroot/_content/MindExecution.Shared/js/mind-map-menu-manager.js +1431 -0
  50. package/wwwroot/_content/MindExecution.Shared/js/mind-map-multi-select.js +1716 -0
  51. package/wwwroot/_content/MindExecution.Shared/js/mind-map-node-search-worker.js +553 -0
  52. package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +4541 -0
  53. package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js +489 -0
  54. package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js.backup +372 -0
  55. package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +2075 -0
  56. package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-lod-system.js +646 -0
  57. package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-overlay-v2.js +4323 -0
  58. package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js +2260 -0
  59. package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js.backup +1258 -0
  60. package/wwwroot/_content/MindExecution.Shared/js/mind-map-visibility-worker.js +890 -0
  61. package/wwwroot/_content/MindExecution.Shared/js/mindmap-toolbar.js +594 -0
  62. package/wwwroot/_content/MindExecution.Shared/js/native-drop-handler.js +170 -0
  63. package/wwwroot/_content/MindExecution.Shared/js/plan-master.js +788 -0
  64. package/wwwroot/_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js +50 -0
  65. package/wwwroot/_content/MindExecution.Shared/js/texture-worker-manager.js +188 -0
  66. package/wwwroot/_content/MindExecution.Shared/js/texture-worker.js +208 -0
  67. package/wwwroot/_content/MindExecution.Shared/js/three.min.js +6 -0
  68. package/wwwroot/_content/MindExecution.Shared/js/titlebar-handler.js +191 -0
  69. package/wwwroot/_content/MindExecution.Shared/js/token-manager.js +37 -0
  70. package/wwwroot/_content/MindExecution.Shared/js/token-worker.js +28 -0
  71. package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js +5626 -0
  72. package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js.map +7 -0
  73. package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/css/all.min.css +9 -0
  74. package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-brands-400.ttf +0 -0
  75. package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-brands-400.woff2 +0 -0
  76. package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-regular-400.ttf +0 -0
  77. package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-regular-400.woff2 +0 -0
  78. package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-solid-900.ttf +0 -0
  79. package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-solid-900.woff2 +0 -0
  80. package/wwwroot/_content/MindExecution.Shared/models/all-MiniLM-L6-v2-quantized.onnx +0 -0
  81. package/wwwroot/_content/MindExecution.Shared/models/vocab.txt +30522 -0
  82. package/wwwroot/_framework/Google.Protobuf.9h59ukbel7.dll +0 -0
  83. package/wwwroot/_framework/Markdig.d1j7v41cl1.dll +0 -0
  84. package/wwwroot/_framework/MessagePack.Annotations.l6qv48kgpt.dll +0 -0
  85. package/wwwroot/_framework/MessagePack.eqoptzx9d5.dll +0 -0
  86. package/wwwroot/_framework/Microsoft.AspNetCore.Authorization.k7dsih5y5g.dll +0 -0
  87. package/wwwroot/_framework/Microsoft.AspNetCore.Components.6nyje9sa0g.dll +0 -0
  88. package/wwwroot/_framework/Microsoft.AspNetCore.Components.Authorization.iycd6unprw.dll +0 -0
  89. package/wwwroot/_framework/Microsoft.AspNetCore.Components.Web.487u3twia4.dll +0 -0
  90. package/wwwroot/_framework/Microsoft.AspNetCore.Components.WebAssembly.d0gcnmlxxz.dll +0 -0
  91. package/wwwroot/_framework/Microsoft.AspNetCore.Metadata.h4yevl9adi.dll +0 -0
  92. package/wwwroot/_framework/Microsoft.CSharp.qrvp77qmhs.dll +0 -0
  93. package/wwwroot/_framework/Microsoft.Data.Sqlite.jdlxgv2jtg.dll +0 -0
  94. package/wwwroot/_framework/Microsoft.EntityFrameworkCore.4gjazp7kjf.dll +0 -0
  95. package/wwwroot/_framework/Microsoft.EntityFrameworkCore.Abstractions.gocudnvz7b.dll +0 -0
  96. package/wwwroot/_framework/Microsoft.EntityFrameworkCore.Relational.lt4rsvinuo.dll +0 -0
  97. package/wwwroot/_framework/Microsoft.EntityFrameworkCore.Sqlite.69luj0fa9r.dll +0 -0
  98. package/wwwroot/_framework/Microsoft.Extensions.Caching.Abstractions.364t4jh3zz.dll +0 -0
  99. package/wwwroot/_framework/Microsoft.Extensions.Caching.Memory.izlxhpzosu.dll +0 -0
  100. package/wwwroot/_framework/Microsoft.Extensions.Configuration.8zq7hh41o7.dll +0 -0
  101. package/wwwroot/_framework/Microsoft.Extensions.Configuration.Abstractions.8if74zs6ea.dll +0 -0
  102. package/wwwroot/_framework/Microsoft.Extensions.Configuration.Json.duvlngw8i0.dll +0 -0
  103. package/wwwroot/_framework/Microsoft.Extensions.DependencyInjection.Abstractions.t2hh9kvx0o.dll +0 -0
  104. package/wwwroot/_framework/Microsoft.Extensions.DependencyInjection.n4tg99oy8l.dll +0 -0
  105. package/wwwroot/_framework/Microsoft.Extensions.DependencyModel.h0d06ixk3e.dll +0 -0
  106. package/wwwroot/_framework/Microsoft.Extensions.Logging.Abstractions.rl32bkx2sd.dll +0 -0
  107. package/wwwroot/_framework/Microsoft.Extensions.Logging.dlht1xei0t.dll +0 -0
  108. package/wwwroot/_framework/Microsoft.Extensions.Options.qeunebioml.dll +0 -0
  109. package/wwwroot/_framework/Microsoft.Extensions.Primitives.18cr6vnuuz.dll +0 -0
  110. package/wwwroot/_framework/Microsoft.IO.RecyclableMemoryStream.r915vovvw4.dll +0 -0
  111. package/wwwroot/_framework/Microsoft.IdentityModel.Abstractions.1ejljk3erv.dll +0 -0
  112. package/wwwroot/_framework/Microsoft.IdentityModel.JsonWebTokens.1596zr8gne.dll +0 -0
  113. package/wwwroot/_framework/Microsoft.IdentityModel.Logging.229uyvpgio.dll +0 -0
  114. package/wwwroot/_framework/Microsoft.IdentityModel.Tokens.9sibtajc9f.dll +0 -0
  115. package/wwwroot/_framework/Microsoft.JSInterop.17lq4j1j7g.dll +0 -0
  116. package/wwwroot/_framework/Microsoft.JSInterop.WebAssembly.ryia5gxiad.dll +0 -0
  117. package/wwwroot/_framework/Microsoft.ML.OnnxRuntime.w9deo1m5ss.dll +0 -0
  118. package/wwwroot/_framework/Microsoft.ML.Tokenizers.cm2vuv2z61.dll +0 -0
  119. package/wwwroot/_framework/Microsoft.NET.StringTools.3qbrf4v2ki.dll +0 -0
  120. package/wwwroot/_framework/MimeMapping.og9ys58ylm.dll +0 -0
  121. package/wwwroot/_framework/MindExecution.Core.1q1trifbuu.dll +0 -0
  122. package/wwwroot/_framework/MindExecution.Kernel.gwwc40sc45.dll +0 -0
  123. package/wwwroot/_framework/MindExecution.Plugins.Admin.0jgrn1sckv.dll +0 -0
  124. package/wwwroot/_framework/MindExecution.Plugins.Business.13mme2qcag.dll +0 -0
  125. package/wwwroot/_framework/MindExecution.Plugins.Concept.dfp2mdt45q.dll +0 -0
  126. package/wwwroot/_framework/MindExecution.Plugins.Directory.3w4t6n3se0.dll +0 -0
  127. package/wwwroot/_framework/MindExecution.Plugins.PlanMaster.s0qpntz420.dll +0 -0
  128. package/wwwroot/_framework/MindExecution.Plugins.YouTube.iu11fq8d16.dll +0 -0
  129. package/wwwroot/_framework/MindExecution.Shared.7j27dcqnrc.dll +0 -0
  130. package/wwwroot/_framework/MindExecution.Web.pq1ty8ov2v.dll +0 -0
  131. package/wwwroot/_framework/Newtonsoft.Json.a56zs13vug.dll +0 -0
  132. package/wwwroot/_framework/SQLitePCLRaw.batteries_v2.rrd1nzawpp.dll +0 -0
  133. package/wwwroot/_framework/SQLitePCLRaw.core.1dxloztpfz.dll +0 -0
  134. package/wwwroot/_framework/SQLitePCLRaw.provider.e_sqlite3.oekyzl53i1.dll +0 -0
  135. package/wwwroot/_framework/Supabase.Core.s1pkj4aj0l.dll +0 -0
  136. package/wwwroot/_framework/Supabase.Functions.qz4nu782sg.dll +0 -0
  137. package/wwwroot/_framework/Supabase.Gotrue.twah27pkik.dll +0 -0
  138. package/wwwroot/_framework/Supabase.Postgrest.gmuuv369ih.dll +0 -0
  139. package/wwwroot/_framework/Supabase.Realtime.ox3kchdy3w.dll +0 -0
  140. package/wwwroot/_framework/Supabase.Storage.fnjnepaowr.dll +0 -0
  141. package/wwwroot/_framework/Supabase.azmaw5pgcz.dll +0 -0
  142. package/wwwroot/_framework/System.Collections.Concurrent.y1zmvuyipi.dll +0 -0
  143. package/wwwroot/_framework/System.Collections.Immutable.ug3j698qms.dll +0 -0
  144. package/wwwroot/_framework/System.Collections.NonGeneric.h66hj3863h.dll +0 -0
  145. package/wwwroot/_framework/System.Collections.Specialized.umr3y27ntj.dll +0 -0
  146. package/wwwroot/_framework/System.Collections.x53e19vfsj.dll +0 -0
  147. package/wwwroot/_framework/System.ComponentModel.Annotations.tz6gnt4ebt.dll +0 -0
  148. package/wwwroot/_framework/System.ComponentModel.Primitives.j7tiphu4rg.dll +0 -0
  149. package/wwwroot/_framework/System.ComponentModel.TypeConverter.ujlztox1gx.dll +0 -0
  150. package/wwwroot/_framework/System.ComponentModel.x9xz0ojfb6.dll +0 -0
  151. package/wwwroot/_framework/System.Console.ijzpqmj7ne.dll +0 -0
  152. package/wwwroot/_framework/System.Data.Common.1r0sqffq1p.dll +0 -0
  153. package/wwwroot/_framework/System.Diagnostics.DiagnosticSource.9upoqwq09o.dll +0 -0
  154. package/wwwroot/_framework/System.Diagnostics.Process.m99azzntjm.dll +0 -0
  155. package/wwwroot/_framework/System.Diagnostics.TraceSource.pl7wv26myr.dll +0 -0
  156. package/wwwroot/_framework/System.Diagnostics.Tracing.crlhfx6tut.dll +0 -0
  157. package/wwwroot/_framework/System.Drawing.Primitives.22e4y9ikq9.dll +0 -0
  158. package/wwwroot/_framework/System.Drawing.mi7d8hwowb.dll +0 -0
  159. package/wwwroot/_framework/System.Formats.Asn1.jx23sjiqnn.dll +0 -0
  160. package/wwwroot/_framework/System.IO.Compression.6fyoii3uej.dll +0 -0
  161. package/wwwroot/_framework/System.IO.Pipelines.vg77t4cd4d.dll +0 -0
  162. package/wwwroot/_framework/System.IdentityModel.Tokens.Jwt.t67es60z5b.dll +0 -0
  163. package/wwwroot/_framework/System.Linq.1bkoxlqgmq.dll +0 -0
  164. package/wwwroot/_framework/System.Linq.Expressions.24xqiypwdt.dll +0 -0
  165. package/wwwroot/_framework/System.Linq.Queryable.hvd01d6rsa.dll +0 -0
  166. package/wwwroot/_framework/System.Memory.8dx3lwgym4.dll +0 -0
  167. package/wwwroot/_framework/System.Net.Http.Json.3mhdm9l1rf.dll +0 -0
  168. package/wwwroot/_framework/System.Net.Http.eitrz660my.dll +0 -0
  169. package/wwwroot/_framework/System.Net.NetworkInformation.3pkuofcv9r.dll +0 -0
  170. package/wwwroot/_framework/System.Net.Ping.8clj5pklrp.dll +0 -0
  171. package/wwwroot/_framework/System.Net.Primitives.qrp4wcjz1p.dll +0 -0
  172. package/wwwroot/_framework/System.Net.WebSockets.Client.2u6pv01g69.dll +0 -0
  173. package/wwwroot/_framework/System.Net.WebSockets.qp6u31zvm5.dll +0 -0
  174. package/wwwroot/_framework/System.Numerics.Tensors.0c7z4mt3on.dll +0 -0
  175. package/wwwroot/_framework/System.Numerics.Vectors.kc7ufp2j4l.dll +0 -0
  176. package/wwwroot/_framework/System.ObjectModel.qv82fot1ib.dll +0 -0
  177. package/wwwroot/_framework/System.Private.CoreLib.rkafq04oma.dll +0 -0
  178. package/wwwroot/_framework/System.Private.Uri.t9542hmr6j.dll +0 -0
  179. package/wwwroot/_framework/System.Private.Xml.Linq.n8n3ptrbwu.dll +0 -0
  180. package/wwwroot/_framework/System.Private.Xml.rxd3tytisn.dll +0 -0
  181. package/wwwroot/_framework/System.Reactive.t3fuon548l.dll +0 -0
  182. package/wwwroot/_framework/System.Reflection.Emit.9tjhp6y0j3.dll +0 -0
  183. package/wwwroot/_framework/System.Reflection.Emit.ILGeneration.stxyk8zoo1.dll +0 -0
  184. package/wwwroot/_framework/System.Reflection.Emit.Lightweight.6xrd5v8vg0.dll +0 -0
  185. package/wwwroot/_framework/System.Reflection.Primitives.wgn8fpwwvv.dll +0 -0
  186. package/wwwroot/_framework/System.Runtime.InteropServices.JavaScript.sliym526xh.dll +0 -0
  187. package/wwwroot/_framework/System.Runtime.InteropServices.RuntimeInformation.oji7zut14z.dll +0 -0
  188. package/wwwroot/_framework/System.Runtime.InteropServices.te07xr2we9.dll +0 -0
  189. package/wwwroot/_framework/System.Runtime.Intrinsics.507y4h8nzq.dll +0 -0
  190. package/wwwroot/_framework/System.Runtime.Loader.v7gk4bse0k.dll +0 -0
  191. package/wwwroot/_framework/System.Runtime.Numerics.eqy5xjv3nd.dll +0 -0
  192. package/wwwroot/_framework/System.Runtime.Serialization.Formatters.zpkrub8lab.dll +0 -0
  193. package/wwwroot/_framework/System.Runtime.Serialization.Primitives.vhkpnbxjip.dll +0 -0
  194. package/wwwroot/_framework/System.Runtime.jn319d5nyg.dll +0 -0
  195. package/wwwroot/_framework/System.Security.Claims.0ztig1q9vo.dll +0 -0
  196. package/wwwroot/_framework/System.Security.Cryptography.vttizqc9ho.dll +0 -0
  197. package/wwwroot/_framework/System.Text.Encoding.Extensions.utdd47ny8f.dll +0 -0
  198. package/wwwroot/_framework/System.Text.Encodings.Web.wah8r1zoe0.dll +0 -0
  199. package/wwwroot/_framework/System.Text.Json.kxlfxj0wrs.dll +0 -0
  200. package/wwwroot/_framework/System.Text.RegularExpressions.dbqn58klox.dll +0 -0
  201. package/wwwroot/_framework/System.Threading.42ao9vi047.dll +0 -0
  202. package/wwwroot/_framework/System.Threading.Channels.hfa7j0uv2w.dll +0 -0
  203. package/wwwroot/_framework/System.Threading.Thread.caul0pdqul.dll +0 -0
  204. package/wwwroot/_framework/System.Transactions.Local.fimi2hamzo.dll +0 -0
  205. package/wwwroot/_framework/System.Web.HttpUtility.gq8yz50p2e.dll +0 -0
  206. package/wwwroot/_framework/System.Xml.Linq.kitin4zjoj.dll +0 -0
  207. package/wwwroot/_framework/System.Xml.ReaderWriter.kzvw3qgxb0.dll +0 -0
  208. package/wwwroot/_framework/System.Xml.XDocument.c539ki6cuq.dll +0 -0
  209. package/wwwroot/_framework/System.m05i39uvk9.dll +0 -0
  210. package/wwwroot/_framework/Websocket.Client.vapounvmnl.dll +0 -0
  211. package/wwwroot/_framework/blazor.boot.json +305 -0
  212. package/wwwroot/_framework/blazor.webassembly.js +1 -0
  213. package/wwwroot/_framework/dotnet.js +4 -0
  214. package/wwwroot/_framework/dotnet.native.vz0adxojrz.wasm +0 -0
  215. package/wwwroot/_framework/dotnet.native.xsn1d6x2kd.js +16 -0
  216. package/wwwroot/_framework/dotnet.runtime.dstopyvqzi.js +4 -0
  217. package/wwwroot/_framework/icudt_CJK.tjcz0u77k5.dat +0 -0
  218. package/wwwroot/_framework/icudt_EFIGS.tptq2av103.dat +0 -0
  219. package/wwwroot/_framework/icudt_no_CJK.lfu7j35m59.dat +0 -0
  220. package/wwwroot/_framework/netstandard.0xet7jg7ky.dll +0 -0
  221. package/wwwroot/_headers +40 -0
  222. package/wwwroot/_redirects +1 -0
  223. package/wwwroot/appsettings.json +71 -0
  224. package/wwwroot/icon-192.png +0 -0
  225. package/wwwroot/icon-512.png +0 -0
  226. package/wwwroot/index.html +710 -0
  227. package/wwwroot/js/marketing-tool.js +180 -0
  228. package/wwwroot/manifest.webmanifest +22 -0
  229. package/wwwroot/robots.txt +4 -0
  230. package/wwwroot/service-worker-assets.js +857 -0
  231. package/wwwroot/service-worker.js +33 -0
  232. package/wwwroot/sitemap.xml +27 -0
@@ -0,0 +1,646 @@
1
+ // mind-map-text-lod-system.js
2
+ // [NEW] 텍스트 LOD 시스템 - 카메라 Z 기반 동적 텍스처 품질 관리
3
+ (function () {
4
+ 'use strict';
5
+
6
+ // LOD 설정 - 카메라 Z 기반
7
+ const CAMERA_Z_THRESHOLD = 4000; // z < 4000이면 고해상도, 이상이면 저해상도
8
+ const ENABLE_WEBGL_GLOW = false;
9
+
10
+ const QUALITY_SCALES = {
11
+ HIGH: 1.0,
12
+ LOW: 1.0
13
+ };
14
+
15
+ // ★ LOD 텍스처 캐시 (노드별로 LOW/HIGH 분리 저장)
16
+ // 저해상도: 항상 유지 (재생성 없음)
17
+ // 고해상도: 필요시 생성, 화면 밖으로 나가면 dispose
18
+ const lodTextureCache = new Map(); // nodeId -> { low: texture, high: texture|null }
19
+
20
+ // 업데이트 큐 (프레임당 여러 개 처리 가능)
21
+ const updateQueue = [];
22
+ let isProcessing = false;
23
+ let module = null;
24
+ let lastCameraZ = null;
25
+ let lastDesiredLevel = null;
26
+ function isDocumentHidden() {
27
+ return typeof document !== 'undefined' && document.visibilityState === 'hidden';
28
+ }
29
+
30
+ function scheduleNonVisualFrame(callback, hiddenDelay = 16) {
31
+ if (typeof requestAnimationFrame === 'function' && !isDocumentHidden()) {
32
+ return requestAnimationFrame(callback);
33
+ }
34
+
35
+ return setTimeout(() => callback(typeof performance !== 'undefined' ? performance.now() : Date.now()), hiddenDelay);
36
+ }
37
+
38
+ // ▼▼▼ [Perf] Yield heavy work to idle time ▼▼▼
39
+ function yieldToIdle() {
40
+ return new Promise(resolve => {
41
+ if (typeof requestIdleCallback === 'function') {
42
+ requestIdleCallback(() => resolve());
43
+ } else {
44
+ setTimeout(resolve, 0);
45
+ }
46
+ });
47
+ }
48
+
49
+ function deferDispose(tex) {
50
+ if (!tex || typeof tex.dispose !== 'function') return;
51
+ if (typeof requestIdleCallback === 'function') {
52
+ requestIdleCallback(() => { try { tex.dispose(); } catch { } });
53
+ } else {
54
+ setTimeout(() => { try { tex.dispose(); } catch { } }, 0);
55
+ }
56
+ }
57
+
58
+ function removeGlowMesh(group) {
59
+ if (!group?.getObjectByName) return null;
60
+ const glowMesh = group.getObjectByName('glow');
61
+ if (!glowMesh) return null;
62
+
63
+ group.remove(glowMesh);
64
+ deferDispose(glowMesh.geometry);
65
+ deferDispose(glowMesh.material);
66
+ return null;
67
+ }
68
+ // ▲▲▲ [Perf] ▲▲▲
69
+
70
+ /**
71
+ * LOD 시스템 초기화
72
+ */
73
+ function init(moduleInstance) {
74
+ module = moduleInstance;
75
+ console.log('[TextLOD] System initialized (Camera Z threshold: ' + CAMERA_Z_THRESHOLD + ')');
76
+ }
77
+
78
+ /**
79
+ * 노드가 화면에 보이는지 확인 (frustum culling 기반)
80
+ */
81
+ function isNodeVisible(nodeEntry, frustum) {
82
+ // 기본적으로 glObject가 visible이어야 함 (수동 제어)
83
+ if (!nodeEntry?.glObject?.visible) return false;
84
+
85
+ // ★ [Fix] 실제 카메라 절두체 검사 (화면 밖 노드는 LOW로 처리하여 성능 확보)
86
+ if (frustum && nodeEntry.glObject) {
87
+ // body mesh 기준으로 체크 (가장 큰 요소)
88
+ const body = nodeEntry.glObject.getObjectByName('body');
89
+ if (body) {
90
+ return frustum.intersectsObject(body);
91
+ }
92
+ return frustum.intersectsObject(nodeEntry.glObject);
93
+ }
94
+ return true;
95
+ }
96
+
97
+ /**
98
+ * 모든 노드의 LOD 상태 검사 (카메라 Z 기반 + 100ms마다 visible 노드 재검사)
99
+ */
100
+ let lastCheckTime = 0;
101
+ const CHECK_INTERVAL_MS = 100; // 100ms마다 visible 노드 재검사 (캐시 사용하므로 부하 적음)
102
+
103
+ function update() {
104
+ if (!module || !module.camera) return;
105
+
106
+ // CSS3D/TextureArray 전환은 LODRenderer가 담당함
107
+ // TextLOD의 품질/텍스처 전환 로직은 비활성화
108
+ return;
109
+
110
+ // (unreachable)
111
+
112
+ const now = performance.now();
113
+ const cameraZ = module.camera.position.z;
114
+ const desiredLevel = cameraZ < CAMERA_Z_THRESHOLD ? 'HIGH' : 'LOW';
115
+
116
+ // ★ 레벨 변경 OR 1초 경과 OR 강제 업데이트 요청 시 visible 노드 재검사
117
+ const levelChanged = desiredLevel !== lastDesiredLevel;
118
+ const intervalPassed = (now - lastCheckTime) > CHECK_INTERVAL_MS;
119
+ const forceUpdate = window.MindMapTextLOD._forceUpdate === true;
120
+
121
+ // ▼▼▼ [Perf] 줌 중에는 스킵 (단, 강제 업데이트 요청이 있으면 처리) ▼▼▼
122
+ if (!levelChanged && !intervalPassed && !forceUpdate) {
123
+ return; // 변경 없고 1초 안 지났으면 스킵
124
+ }
125
+
126
+ // 강제 업데이트 플래그 리셋
127
+ if (forceUpdate) {
128
+ window.MindMapTextLOD._forceUpdate = false;
129
+ console.log('[TextLOD] Force update triggered after zoom end');
130
+ }
131
+ // ▲▲▲ [Perf] ▲▲▲
132
+
133
+ if (levelChanged) {
134
+ console.log(`[TextLOD] Camera Z=${cameraZ.toFixed(0)}, switching all visible nodes to ${desiredLevel}`);
135
+ }
136
+
137
+ lastDesiredLevel = desiredLevel;
138
+ lastCheckTime = now;
139
+
140
+ if (ENABLE_WEBGL_GLOW) {
141
+ // ★ 모든 노드의 glow 가시성을 카메라 거리에 따라 일괄 업데이트
142
+ const showGlow = desiredLevel === 'HIGH';
143
+ module.nodeObjectsById.forEach((nodeEntry) => {
144
+ const glowMesh = nodeEntry.glObject?.getObjectByName('glow');
145
+ if (glowMesh && glowMesh.visible !== showGlow) {
146
+ glowMesh.visible = showGlow;
147
+ }
148
+ });
149
+ } else {
150
+ module.nodeObjectsById.forEach((nodeEntry) => removeGlowMesh(nodeEntry.glObject));
151
+ }
152
+
153
+ // ★ [Perf] Frustum 생성 (한 번만 생성해서 재사용)
154
+ const frustum = new THREE.Frustum();
155
+ const matrix = new THREE.Matrix4().multiplyMatrices(module.camera.projectionMatrix, module.camera.matrixWorldInverse);
156
+ frustum.setFromProjectionMatrix(matrix);
157
+
158
+ // 화면에 보이는 노드 → 현재 desiredLevel로
159
+ // 화면 밖 노드 → 저해상도로 다운그레이드 (메모리 절약)
160
+ module.nodeObjectsById.forEach((nodeEntry, nodeId) => {
161
+ const model = nodeEntry.model;
162
+ // ▼▼▼ [Fix] markdown, image 타입도 포함 ▼▼▼
163
+ if (!model || (model.contentType !== 'text' && model.contentType !== 'note' && model.contentType !== 'memo' && model.contentType !== 'markdown' && model.contentType !== 'image')) {
164
+ return;
165
+ }
166
+ // ▲▲▲ [Fix] ▲▲▲
167
+
168
+ const currentLevel = nodeEntry.glObject?.userData?.lodLevel;
169
+ // ★ [Fix] Frustum 전달
170
+ const visible = isNodeVisible(nodeEntry, frustum);
171
+
172
+ let targetLevel;
173
+ if (visible) {
174
+ // 보이는 노드 → desiredLevel
175
+ targetLevel = desiredLevel;
176
+ } else {
177
+ // 안 보이는 노드
178
+ // ★ [Fix] HIGH 모드에서 CSS3D 지원 노드는 화면 밖이라도 LOW(WebGL)로 내리지 않고 HIGH(CSS3D) 유지
179
+ // 이렇게 해야 화면 안으로 들어올 때 WebGL -> CSS3D 전환 깜빡임이 없고, 사용자가 원하는 "무조건 CSS3D"가 됨.
180
+ // (성능 주의: DOM 요소가 많아질 수 있음)
181
+ const supportsCss3d = model.contentType === 'text' ||
182
+ model.contentType === 'note' ||
183
+ model.contentType === 'memo' ||
184
+ model.contentType === 'markdown';
185
+ if (desiredLevel === 'HIGH' && supportsCss3d) {
186
+ targetLevel = 'HIGH';
187
+ } else {
188
+ targetLevel = 'LOW';
189
+ }
190
+ }
191
+
192
+ // ★ [Fix] 불필요한 반복 업데이트 차단
193
+ // 1. 레벨이 이미 같고
194
+ // 2. 현재 렌더링 타입(CSS/GL)도 목표와 일치하면 업데이트 불필요
195
+ if (currentLevel === targetLevel) {
196
+ const isCorrectType = (targetLevel === 'HIGH' && nodeEntry.currentType === 'CSS') ||
197
+ (targetLevel === 'LOW' && nodeEntry.currentType === 'GL');
198
+
199
+ if (isCorrectType) return; // 이미 완벽한 상태
200
+ }
201
+
202
+ // 'NEEDS_UPDATE'는 항상 업데이트 트리거
203
+ // 레벨이 다르거나, 'NEEDS_UPDATE'이거나, null이면 큐에 추가
204
+ const needsUpdate = currentLevel === 'NEEDS_UPDATE' ||
205
+ currentLevel === null ||
206
+ currentLevel === undefined ||
207
+ currentLevel !== targetLevel;
208
+
209
+ if (needsUpdate) {
210
+ if (!updateQueue.find(item => item.nodeId === nodeId)) {
211
+ updateQueue.push({
212
+ nodeId: nodeId,
213
+ nodeEntry: nodeEntry,
214
+ level: targetLevel
215
+ });
216
+ }
217
+ }
218
+
219
+ // ★ [Safety] CSS3D 모드(HIGH)라면 WebGL Body/Tail 숨김 강제 (외부 간섭 방지)
220
+ // 다른 로직(선택, 인터랙션 등)에서 실수로 body.visible = true로 되돌리는 경우를 방지
221
+ const supportsCss3d = model.contentType === 'text' ||
222
+ model.contentType === 'note' ||
223
+ model.contentType === 'memo' ||
224
+ model.contentType === 'markdown';
225
+ if (supportsCss3d && targetLevel === 'HIGH' && nodeEntry.currentType === 'CSS') {
226
+ const body = nodeEntry.glObject?.getObjectByName('body');
227
+ if (body && body.visible) body.visible = false;
228
+
229
+ const tail = nodeEntry.glObject?.getObjectByName('tail');
230
+ if (tail && tail.visible) tail.visible = false;
231
+ }
232
+ });
233
+
234
+ if (updateQueue.length > 0) {
235
+ console.log(`[TextLOD] Queued ${updateQueue.length} nodes for LOD update`);
236
+ }
237
+ }
238
+
239
+ /**
240
+ * 큐에서 처리 (프레임당 3개씩 - 속도와 프레임 드랍 균형)
241
+ */
242
+ async function processQueue() {
243
+ if (isProcessing || updateQueue.length === 0) return;
244
+
245
+ isProcessing = true;
246
+
247
+ // 타임슬라이스: 프레임 예산 내에서만 처리해 UI 스터터 최소화
248
+ const frameBudgetMs = 8; // ~0.5 frame @60fps
249
+ const start = performance.now();
250
+
251
+ while (updateQueue.length > 0) {
252
+ const item = updateQueue.shift();
253
+ if (!item) break;
254
+
255
+ try {
256
+ await applyTextureQuality(item.nodeEntry, item.level);
257
+ } catch (e) {
258
+ console.error('[TextLOD] Error processing queue:', e);
259
+ }
260
+
261
+ if ((performance.now() - start) >= frameBudgetMs) {
262
+ break; // 다음 프레임으로 이월
263
+ }
264
+ }
265
+
266
+ isProcessing = false;
267
+
268
+ // 남은 작업은 다음 프레임에 처리
269
+ if (updateQueue.length > 0) {
270
+ scheduleNonVisualFrame(() => processQueue());
271
+ }
272
+ }
273
+
274
+ async function applyTextureQuality(nodeEntry, level) {
275
+ if (!nodeEntry?.glObject || !nodeEntry?.model) return;
276
+
277
+ const nodeModel = nodeEntry.model;
278
+ const nodeId = nodeModel.id;
279
+
280
+ // 이미 같은 레벨이면 스킵
281
+ if (nodeEntry.glObject.userData.lodLevel === level) return;
282
+
283
+ // ▼▼▼ [Perf] 줌 중에는 업데이트 스킵 (줌 멈출 때만 업데이트) ▼▼▼
284
+ if (module.isZooming && level === 'HIGH') {
285
+ console.log(`[TextLOD] Skipping HIGH update for ${nodeId} (zooming in progress)`);
286
+ return;
287
+ }
288
+ // ▲▲▲ [Perf] ▲▲▲
289
+
290
+ // ★ 현재 선택 상태 확인
291
+ const isSelected = module.multiSelectedNodeIds?.has(nodeId) || module.selectedNodeIdJs === nodeId;
292
+
293
+ try {
294
+ if (level === 'LOW') {
295
+ // ★ LOW 모드: WebGL 인스턴싱 사용 (현행 유지)
296
+ // ▼▼▼ [Fix] currentType을 'GL'로 설정 ▼▼▼
297
+ nodeEntry.currentType = 'GL';
298
+ // ▲▲▲ [Fix] ▲▲▲
299
+
300
+ // CSS3D 숨기고 WebGL 보이기
301
+ if (nodeEntry.cssObject) {
302
+ nodeEntry.cssObject.visible = false;
303
+ }
304
+
305
+ // WebGL body/tail 보이기
306
+ const bodyMesh = nodeEntry.glObject.getObjectByName('body');
307
+ const tailMesh = nodeEntry.glObject.getObjectByName('tail');
308
+ if (bodyMesh) bodyMesh.visible = true;
309
+ if (tailMesh) tailMesh.visible = true;
310
+
311
+ // LOD 캐시 가져오기 또는 생성
312
+ let cache = lodTextureCache.get(nodeId);
313
+ if (!cache) {
314
+ cache = { low: null, lowSelected: null, high: null, highSelected: null };
315
+ lodTextureCache.set(nodeId, cache);
316
+ }
317
+
318
+ // ★ [Image Node Exception] Image nodes do not generate low-res textures (yet).
319
+ // They just hide the CSS3D object. The hitBody remains (invisible).
320
+ if (nodeEntry.model.contentType === 'image') {
321
+ // Do nothing for texture generation.
322
+ // Just ensure bodyMesh material is basic/invisible if needed.
323
+ // (Our implementation of image node uses invisible request hitBody, so we are good)
324
+ return;
325
+ }
326
+
327
+ let targetTextures = null;
328
+ const cacheKey = isSelected ? 'lowSelected' : 'low';
329
+
330
+ if (cache[cacheKey]) {
331
+ targetTextures = { body: cache[cacheKey] };
332
+ } else {
333
+ // ★ [Optimization] First check if MindMapNodes.textureCache already has this texture
334
+ const existingTextureEntry = window.MindMapNodes?.textureCache?.get(nodeId);
335
+ const existingTexture = isSelected
336
+ ? existingTextureEntry?.selected?.body
337
+ : existingTextureEntry?.default?.body;
338
+
339
+ // Reuse if it exists and was created with LOW quality scale
340
+ if (existingTexture && existingTexture._qualityScale === QUALITY_SCALES.LOW) {
341
+ cache[cacheKey] = existingTexture;
342
+ targetTextures = { body: existingTexture };
343
+ console.log(`[TextLOD] Reusing existing low-res texture for ${nodeId}`);
344
+ } else {
345
+ await yieldToIdle();
346
+ // ★ 저해상도 텍스처 새로 생성 (skipBorder=true 옵션 전달)
347
+ const { textures } = await window.MindMapTextureFactory.generateAndCacheTextures(
348
+ module,
349
+ nodeModel,
350
+ window.MindMapNodes.imageCache,
351
+ { skipMeasure: true, skipSelected: !isSelected, skipBorder: true },
352
+ QUALITY_SCALES.LOW
353
+ );
354
+ const textureData = isSelected ? textures?.selected : textures?.default;
355
+ if (textureData?.body) {
356
+ cache[cacheKey] = textureData.body;
357
+ targetTextures = { body: cache[cacheKey] };
358
+ }
359
+ }
360
+ }
361
+
362
+ // 메시에 적용
363
+ if (targetTextures?.body && bodyMesh) {
364
+ bodyMesh.material.map = targetTextures.body;
365
+ bodyMesh.material.needsUpdate = true;
366
+ }
367
+
368
+ } else if (level === 'HIGH') {
369
+ // ▼▼▼ [Changed] HIGH 모드: CSS3D 사용 (텍스처 메모리 절약) ▼▼▼
370
+ // text, note, markdown 타입만 CSS3D 전환
371
+ const supportsCss3d = nodeModel.contentType === 'text' ||
372
+ nodeModel.contentType === 'note' ||
373
+ nodeModel.contentType === 'memo' ||
374
+ nodeModel.contentType === 'markdown';
375
+
376
+ // ▼▼▼ [Fix] cssObject가 없으면 동적 생성 시도 ▼▼▼
377
+ if (supportsCss3d && !nodeEntry.cssObject) {
378
+ console.log(`[TextLOD] Creating cssObject for ${nodeId} (contentType=${nodeModel.contentType})`);
379
+ if (window.MindMapCss3DManager) {
380
+ nodeEntry.cssObject = window.MindMapCss3DManager.createCss3dObject(module, nodeModel);
381
+ if (nodeEntry.cssObject) {
382
+ const glPos = nodeEntry.glObject.position;
383
+ nodeEntry.cssObject.position.set(glPos.x, glPos.y, glPos.z);
384
+ (module.cssScene || module.scene).add(nodeEntry.cssObject);
385
+ console.log(`[TextLOD] ✅ cssObject created and added to scene for ${nodeId}`);
386
+ } else {
387
+ console.warn(`[TextLOD] ⚠️ Failed to create cssObject for ${nodeId}`);
388
+ }
389
+ }
390
+ }
391
+ // ▲▲▲ [Fix] ▲▲▲
392
+
393
+ if (supportsCss3d && nodeEntry.cssObject) {
394
+ console.log(`[TextLOD] Switching ${nodeId} to CSS3D mode (HIGH)`);
395
+
396
+ // ▼▼▼ [Fix] currentType을 'CSS'로 설정하여 cullAndLOD()에서 올바르게 처리되도록 ▼▼▼
397
+ nodeEntry.currentType = 'CSS';
398
+ // ▲▲▲ [Fix] ▲▲▲
399
+
400
+ // CSS3D 위치 동기화 (WebGL과 동일하게)
401
+ const glPos = nodeEntry.glObject.position;
402
+ nodeEntry.cssObject.position.copy(glPos);
403
+
404
+ // CSS3D 크기 동기화
405
+ const wrapper = nodeEntry.cssObject.element;
406
+ if (wrapper) {
407
+ wrapper.style.width = (nodeModel.width || 400) + 'px';
408
+ wrapper.style.height = (nodeModel.height || 200) + 'px';
409
+
410
+ const innerContent = wrapper.firstElementChild;
411
+ if (innerContent) {
412
+ innerContent.style.width = (nodeModel.width || 400) + 'px';
413
+ innerContent.style.height = (nodeModel.height || 200) + 'px';
414
+ }
415
+ }
416
+
417
+ // CSS3D 보이기
418
+ nodeEntry.cssObject.visible = true;
419
+
420
+ // WebGL body/tail 숨기기 (glow와 outline은 유지)
421
+ const bodyMesh = nodeEntry.glObject.getObjectByName('body');
422
+ const tailMesh = nodeEntry.glObject.getObjectByName('tail');
423
+ if (bodyMesh) bodyMesh.visible = false;
424
+ if (tailMesh) tailMesh.visible = false;
425
+
426
+ // ▼▼▼ [Memory Optimization] Dispose WebGL textures when in CSS3D mode ▼▼▼
427
+ if (bodyMesh && bodyMesh.material && bodyMesh.material.map) {
428
+ // Don't dispose if it's the shared placeholder or if another node is using it (unlikely unless shared)
429
+ if (!bodyMesh.material.map._isShared) {
430
+ try { bodyMesh.material.map.dispose(); } catch (e) { }
431
+ }
432
+ bodyMesh.material.map = null;
433
+ bodyMesh.material.needsUpdate = true;
434
+ }
435
+ if (tailMesh && tailMesh.material && tailMesh.material.map) {
436
+ try { tailMesh.material.map.dispose(); } catch (e) { }
437
+ tailMesh.material.map = null;
438
+ tailMesh.material.needsUpdate = true;
439
+ }
440
+
441
+ // Clear caches to force regeneration when returning to LOW mode
442
+ lodTextureCache.delete(nodeId);
443
+ if (window.MindMapNodes && window.MindMapNodes.textureCache) {
444
+ // 선택된 상태라면 선택 텍스처도 날아갈 수 있으므로 주의.
445
+ // 하지만 CSS3D에서는 선택 표시도 CSS(Glow)로 하거나 별도 처리를 하므로
446
+ // WebGL 텍스처는 날려도 됩니다.
447
+ window.MindMapNodes.textureCache.delete(nodeId);
448
+ }
449
+ // ▲▲▲ [Memory Optimization] ▲▲▲
450
+
451
+ // glObject 자체는 visible 유지 (glow 렌더링용)
452
+ nodeEntry.glObject.visible = true;
453
+
454
+ } else if (!supportsCss3d) {
455
+ // CSS3D 미지원 노드
456
+ console.log(`[TextLOD] ${nodeId} does not support CSS3D, using WebGL texture`);
457
+
458
+ // Same exception for image checks if logic flows here (redundant but safe)
459
+ if (nodeModel.contentType === 'image') return;
460
+
461
+ // LOD 캐시 가져오기 또는 생성
462
+ let cache = lodTextureCache.get(nodeId);
463
+ if (!cache) {
464
+ cache = { low: null, lowSelected: null, high: null, highSelected: null };
465
+ lodTextureCache.set(nodeId, cache);
466
+ }
467
+
468
+ let targetTextures = null;
469
+ const cacheKey = isSelected ? 'highSelected' : 'high';
470
+
471
+ if (cache[cacheKey]) {
472
+ targetTextures = { body: cache[cacheKey] };
473
+ } else {
474
+ const existingTextureEntry = window.MindMapNodes?.textureCache?.get(nodeId);
475
+ const existingTexture = isSelected
476
+ ? existingTextureEntry?.selected?.body
477
+ : existingTextureEntry?.default?.body;
478
+
479
+ if (existingTexture && existingTexture._qualityScale === QUALITY_SCALES.HIGH) {
480
+ cache[cacheKey] = existingTexture;
481
+ targetTextures = { body: existingTexture };
482
+ } else {
483
+ await yieldToIdle();
484
+ const { textures } = await window.MindMapTextureFactory.generateAndCacheTextures(
485
+ module,
486
+ nodeModel,
487
+ window.MindMapNodes.imageCache,
488
+ { skipMeasure: true, skipSelected: !isSelected },
489
+ QUALITY_SCALES.HIGH
490
+ );
491
+ const textureData = isSelected ? textures?.selected : textures?.default;
492
+ if (textureData?.body) {
493
+ cache[cacheKey] = textureData.body;
494
+ targetTextures = { body: cache[cacheKey] };
495
+ }
496
+ }
497
+ }
498
+
499
+ // 메시에 적용
500
+ if (targetTextures?.body) {
501
+ const bodyMesh = nodeEntry.glObject.getObjectByName('body');
502
+ if (bodyMesh) {
503
+ bodyMesh.material.map = targetTextures.body;
504
+ bodyMesh.material.needsUpdate = true;
505
+ }
506
+ }
507
+ }
508
+ // ▲▲▲ [Changed] ▲▲▲
509
+ }
510
+
511
+ if (ENABLE_WEBGL_GLOW) {
512
+ // ★ glow 가시성은 level에 따라 설정
513
+ const glowMesh = nodeEntry.glObject.getObjectByName('glow');
514
+ if (glowMesh) {
515
+ glowMesh.visible = (level === 'HIGH');
516
+ }
517
+ } else {
518
+ removeGlowMesh(nodeEntry.glObject);
519
+ }
520
+
521
+ // LOD 레벨 저장
522
+ nodeEntry.glObject.userData.lodLevel = level;
523
+
524
+ } catch (e) {
525
+ console.error(`[TextLOD] Failed to apply texture for ${nodeId}:`, e);
526
+ }
527
+ }
528
+
529
+ /**
530
+ * 초기 로딩용 품질 스케일 반환
531
+ */
532
+ function getInitialQualityScale() {
533
+ return 1.0;
534
+ }
535
+
536
+ /**
537
+ * 강제로 모든 노드를 특정 품질로 변경
538
+ */
539
+ function setAllNodesQuality(level) {
540
+ if (!module) return;
541
+
542
+ module.nodeObjectsById.forEach((nodeEntry, nodeId) => {
543
+ if (nodeEntry.model?.contentType === 'text' || nodeEntry.model?.contentType === 'note' || nodeEntry.model?.contentType === 'memo') {
544
+ updateQueue.push({
545
+ nodeId: nodeId,
546
+ nodeEntry: nodeEntry,
547
+ level: level
548
+ });
549
+ }
550
+ });
551
+
552
+ console.log(`[TextLOD] Queued ${updateQueue.length} nodes for ${level} quality`);
553
+ }
554
+
555
+ /**
556
+ * 특정 노드의 LOD 캐시 무효화 (콘텐츠 변경 시 호출)
557
+ */
558
+ function invalidateNodeCache(nodeId, regenerate = false) {
559
+ const cache = lodTextureCache.get(nodeId);
560
+ if (cache) {
561
+ // Dispose cached textures
562
+ deferDispose(cache.low);
563
+ deferDispose(cache.lowSelected);
564
+ deferDispose(cache.high);
565
+ deferDispose(cache.highSelected);
566
+ lodTextureCache.delete(nodeId);
567
+ console.log(`[TextLOD] Cache invalidated for node ${nodeId}`);
568
+ }
569
+
570
+ // ★ [New] Optionally queue for immediate regeneration
571
+ if (regenerate && module) {
572
+ const nodeEntry = module.nodeObjectsById.get(nodeId);
573
+ if (nodeEntry && (nodeEntry.model?.contentType === 'text' || nodeEntry.model?.contentType === 'note' || nodeEntry.model?.contentType === 'memo')) {
574
+ const cameraZ = module.camera?.position?.z || 0;
575
+ const desiredLevel = cameraZ < CAMERA_Z_THRESHOLD ? 'HIGH' : 'LOW';
576
+
577
+ // Reset LOD level to force regeneration
578
+ if (nodeEntry.glObject) {
579
+ nodeEntry.glObject.userData.lodLevel = null;
580
+ }
581
+
582
+ // Add to queue if not already there
583
+ if (!updateQueue.find(item => item.nodeId === nodeId)) {
584
+ updateQueue.push({
585
+ nodeId: nodeId,
586
+ nodeEntry: nodeEntry,
587
+ level: desiredLevel
588
+ });
589
+ }
590
+ }
591
+ }
592
+ }
593
+
594
+ /**
595
+ * 여러 노드의 LOD 캐시 무효화 및 재생성
596
+ */
597
+ function invalidateMultipleNodeCaches(nodeIds) {
598
+ if (!module || !nodeIds || nodeIds.length === 0) return;
599
+
600
+ const cameraZ = module.camera?.position?.z || 0;
601
+ const desiredLevel = cameraZ < CAMERA_Z_THRESHOLD ? 'HIGH' : 'LOW';
602
+
603
+ let count = 0;
604
+ for (const nodeId of nodeIds) {
605
+ const cache = lodTextureCache.get(nodeId);
606
+ if (cache) {
607
+ deferDispose(cache.low);
608
+ deferDispose(cache.lowSelected);
609
+ deferDispose(cache.high);
610
+ deferDispose(cache.highSelected);
611
+ lodTextureCache.delete(nodeId);
612
+ }
613
+
614
+ const nodeEntry = module.nodeObjectsById.get(nodeId);
615
+ if (nodeEntry && (nodeEntry.model?.contentType === 'text' || nodeEntry.model?.contentType === 'note' || nodeEntry.model?.contentType === 'memo')) {
616
+ if (nodeEntry.glObject) {
617
+ nodeEntry.glObject.userData.lodLevel = null;
618
+ }
619
+
620
+ if (!updateQueue.find(item => item.nodeId === nodeId)) {
621
+ updateQueue.push({
622
+ nodeId: nodeId,
623
+ nodeEntry: nodeEntry,
624
+ level: desiredLevel
625
+ });
626
+ count++;
627
+ }
628
+ }
629
+ }
630
+
631
+ console.log(`[TextLOD] Invalidated and queued ${count} nodes for ${desiredLevel} regeneration`);
632
+ }
633
+
634
+ // 전역 노출
635
+ window.MindMapTextLOD = {
636
+ init,
637
+ update,
638
+ getInitialQualityScale,
639
+ setAllNodesQuality,
640
+ invalidateNodeCache,
641
+ invalidateMultipleNodeCaches, // ★ [New] Batch invalidation
642
+ QUALITY_SCALES,
643
+ CAMERA_Z_THRESHOLD
644
+ };
645
+
646
+ })();