@vib3code/sdk 2.0.1

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 (258) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/DOCS/BLUEPRINT_EXECUTION_PLAN_2026-01-07.md +34 -0
  3. package/DOCS/CI_TESTING.md +38 -0
  4. package/DOCS/CLI_ONBOARDING.md +75 -0
  5. package/DOCS/CONTROL_REFERENCE.md +64 -0
  6. package/DOCS/DEV_TRACK_ANALYSIS.md +77 -0
  7. package/DOCS/DEV_TRACK_PLAN_2026-01-07.md +42 -0
  8. package/DOCS/DEV_TRACK_SESSION_2026-01-31.md +220 -0
  9. package/DOCS/ENV_SETUP.md +189 -0
  10. package/DOCS/EXPORT_FORMATS.md +417 -0
  11. package/DOCS/GPU_DISPOSAL_GUIDE.md +21 -0
  12. package/DOCS/LICENSING_TIERS.md +275 -0
  13. package/DOCS/MASTER_PLAN_2026-01-31.md +570 -0
  14. package/DOCS/OBS_SETUP_GUIDE.md +98 -0
  15. package/DOCS/PROJECT_SETUP.md +66 -0
  16. package/DOCS/RENDERER_LIFECYCLE.md +40 -0
  17. package/DOCS/REPO_MANIFEST.md +121 -0
  18. package/DOCS/SESSION_014_PLAN.md +195 -0
  19. package/DOCS/SESSION_LOG_2026-01-07.md +56 -0
  20. package/DOCS/STRATEGIC_BLUEPRINT_2026-01-07.md +72 -0
  21. package/DOCS/SYSTEM_AUDIT_2026-01-30.md +738 -0
  22. package/DOCS/SYSTEM_INVENTORY.md +520 -0
  23. package/DOCS/TELEMETRY_EXPORTS.md +34 -0
  24. package/DOCS/WEBGPU_STATUS.md +38 -0
  25. package/DOCS/XR_BENCHMARKS.md +608 -0
  26. package/LICENSE +21 -0
  27. package/README.md +426 -0
  28. package/docs/.nojekyll +0 -0
  29. package/docs/01-dissolution_of_euclidean_hegemony.html +346 -0
  30. package/docs/02-hyperspatial_ego_death.html +346 -0
  31. package/docs/03-post_cartesian_sublime.html +346 -0
  32. package/docs/04-crystalline_void_meditation.html +346 -0
  33. package/docs/05-quantum_decoherence_ballet.html +346 -0
  34. package/docs/06-dissolution_of_euclidean_hegemony.html +346 -0
  35. package/docs/07-hyperspatial_ego_death.html +346 -0
  36. package/docs/08-post_cartesian_sublime.html +346 -0
  37. package/docs/09-crystalline_void_meditation.html +346 -0
  38. package/docs/10-quantum_decoherence_ballet.html +346 -0
  39. package/docs/11-dissolution_of_euclidean_hegemony.html +346 -0
  40. package/docs/12-hyperspatial_ego_death.html +346 -0
  41. package/docs/13-post_cartesian_sublime.html +346 -0
  42. package/docs/index.html +794 -0
  43. package/docs/test-hub.html +441 -0
  44. package/docs/url-state.js +102 -0
  45. package/docs/vib3-exports/01-quantum-quantum-tetrahedron-lattice.html +489 -0
  46. package/docs/vib3-exports/02-quantum-quantum-hypersphere-matrix.html +489 -0
  47. package/docs/vib3-exports/03-quantum-quantum-hypertetra-fractal.html +489 -0
  48. package/docs/vib3-exports/04-faceted-faceted-crystal-structure.html +407 -0
  49. package/docs/vib3-exports/05-faceted-faceted-klein-bottle.html +407 -0
  50. package/docs/vib3-exports/06-faceted-faceted-hypertetra-torus.html +407 -0
  51. package/docs/vib3-exports/07-holographic-holographic-wave-field.html +457 -0
  52. package/docs/vib3-exports/08-holographic-holographic-hypersphere-sphere.html +457 -0
  53. package/docs/vib3-exports/09-holographic-holographic-hypertetra-crystal.html +457 -0
  54. package/docs/vib3-exports/index.html +238 -0
  55. package/docs/webgpu-live.html +702 -0
  56. package/package.json +367 -0
  57. package/src/advanced/AIPresetGenerator.js +777 -0
  58. package/src/advanced/MIDIController.js +703 -0
  59. package/src/advanced/OffscreenWorker.js +1051 -0
  60. package/src/advanced/WebGPUCompute.js +1051 -0
  61. package/src/advanced/WebXRRenderer.js +680 -0
  62. package/src/agent/cli/AgentCLI.js +615 -0
  63. package/src/agent/cli/index.js +14 -0
  64. package/src/agent/index.js +73 -0
  65. package/src/agent/mcp/MCPServer.js +950 -0
  66. package/src/agent/mcp/index.js +9 -0
  67. package/src/agent/mcp/tools.js +548 -0
  68. package/src/agent/telemetry/EventStream.js +669 -0
  69. package/src/agent/telemetry/Instrumentation.js +618 -0
  70. package/src/agent/telemetry/TelemetryExporters.js +427 -0
  71. package/src/agent/telemetry/TelemetryService.js +464 -0
  72. package/src/agent/telemetry/index.js +52 -0
  73. package/src/benchmarks/BenchmarkRunner.js +381 -0
  74. package/src/benchmarks/MetricsCollector.js +299 -0
  75. package/src/benchmarks/index.js +9 -0
  76. package/src/benchmarks/scenes.js +259 -0
  77. package/src/cli/index.js +675 -0
  78. package/src/config/ApiConfig.js +88 -0
  79. package/src/core/CanvasManager.js +217 -0
  80. package/src/core/ErrorReporter.js +117 -0
  81. package/src/core/ParameterMapper.js +333 -0
  82. package/src/core/Parameters.js +396 -0
  83. package/src/core/RendererContracts.js +200 -0
  84. package/src/core/UnifiedResourceManager.js +370 -0
  85. package/src/core/VIB3Engine.js +636 -0
  86. package/src/core/renderers/FacetedRendererAdapter.js +32 -0
  87. package/src/core/renderers/HolographicRendererAdapter.js +29 -0
  88. package/src/core/renderers/QuantumRendererAdapter.js +29 -0
  89. package/src/core/renderers/RendererLifecycleManager.js +63 -0
  90. package/src/creative/ColorPresetsSystem.js +980 -0
  91. package/src/creative/ParameterTimeline.js +1061 -0
  92. package/src/creative/PostProcessingPipeline.js +1113 -0
  93. package/src/creative/TransitionAnimator.js +683 -0
  94. package/src/export/CSSExporter.js +226 -0
  95. package/src/export/CardGeneratorBase.js +279 -0
  96. package/src/export/ExportManager.js +580 -0
  97. package/src/export/FacetedCardGenerator.js +279 -0
  98. package/src/export/HolographicCardGenerator.js +543 -0
  99. package/src/export/LottieExporter.js +552 -0
  100. package/src/export/QuantumCardGenerator.js +315 -0
  101. package/src/export/SVGExporter.js +519 -0
  102. package/src/export/ShaderExporter.js +903 -0
  103. package/src/export/TradingCardGenerator.js +3055 -0
  104. package/src/export/TradingCardManager.js +181 -0
  105. package/src/export/VIB3PackageExporter.js +559 -0
  106. package/src/export/index.js +14 -0
  107. package/src/export/systems/TradingCardSystemFaceted.js +494 -0
  108. package/src/export/systems/TradingCardSystemHolographic.js +452 -0
  109. package/src/export/systems/TradingCardSystemQuantum.js +411 -0
  110. package/src/faceted/FacetedSystem.js +963 -0
  111. package/src/features/CollectionManager.js +433 -0
  112. package/src/gallery/CollectionManager.js +240 -0
  113. package/src/gallery/GallerySystem.js +485 -0
  114. package/src/geometry/GeometryFactory.js +314 -0
  115. package/src/geometry/GeometryLibrary.js +72 -0
  116. package/src/geometry/buffers/BufferBuilder.js +338 -0
  117. package/src/geometry/buffers/index.js +18 -0
  118. package/src/geometry/generators/Crystal.js +420 -0
  119. package/src/geometry/generators/Fractal.js +298 -0
  120. package/src/geometry/generators/KleinBottle.js +197 -0
  121. package/src/geometry/generators/Sphere.js +192 -0
  122. package/src/geometry/generators/Tesseract.js +160 -0
  123. package/src/geometry/generators/Tetrahedron.js +225 -0
  124. package/src/geometry/generators/Torus.js +304 -0
  125. package/src/geometry/generators/Wave.js +341 -0
  126. package/src/geometry/index.js +142 -0
  127. package/src/geometry/warp/HypersphereCore.js +211 -0
  128. package/src/geometry/warp/HypertetraCore.js +386 -0
  129. package/src/geometry/warp/index.js +57 -0
  130. package/src/holograms/HolographicVisualizer.js +1073 -0
  131. package/src/holograms/RealHolographicSystem.js +966 -0
  132. package/src/holograms/variantRegistry.js +69 -0
  133. package/src/integrations/FigmaPlugin.js +854 -0
  134. package/src/integrations/OBSMode.js +754 -0
  135. package/src/integrations/ThreeJsPackage.js +660 -0
  136. package/src/integrations/TouchDesignerExport.js +552 -0
  137. package/src/integrations/frameworks/Vib3React.js +591 -0
  138. package/src/integrations/frameworks/Vib3Svelte.js +654 -0
  139. package/src/integrations/frameworks/Vib3Vue.js +628 -0
  140. package/src/llm/LLMParameterInterface.js +240 -0
  141. package/src/llm/LLMParameterUI.js +577 -0
  142. package/src/math/Mat4x4.js +708 -0
  143. package/src/math/Projection.js +341 -0
  144. package/src/math/Rotor4D.js +637 -0
  145. package/src/math/Vec4.js +476 -0
  146. package/src/math/constants.js +164 -0
  147. package/src/math/index.js +68 -0
  148. package/src/math/projections.js +54 -0
  149. package/src/math/rotations.js +196 -0
  150. package/src/quantum/QuantumEngine.js +906 -0
  151. package/src/quantum/QuantumVisualizer.js +1103 -0
  152. package/src/reactivity/ReactivityConfig.js +499 -0
  153. package/src/reactivity/ReactivityManager.js +586 -0
  154. package/src/reactivity/SpatialInputSystem.js +1783 -0
  155. package/src/reactivity/index.js +93 -0
  156. package/src/render/CommandBuffer.js +465 -0
  157. package/src/render/MultiCanvasBridge.js +340 -0
  158. package/src/render/RenderCommand.js +514 -0
  159. package/src/render/RenderResourceRegistry.js +523 -0
  160. package/src/render/RenderState.js +552 -0
  161. package/src/render/RenderTarget.js +512 -0
  162. package/src/render/ShaderLoader.js +253 -0
  163. package/src/render/ShaderProgram.js +599 -0
  164. package/src/render/UnifiedRenderBridge.js +496 -0
  165. package/src/render/backends/WebGLBackend.js +1108 -0
  166. package/src/render/backends/WebGPUBackend.js +1409 -0
  167. package/src/render/commands/CommandBufferExecutor.js +607 -0
  168. package/src/render/commands/RenderCommandBuffer.js +661 -0
  169. package/src/render/commands/index.js +17 -0
  170. package/src/render/index.js +367 -0
  171. package/src/scene/Disposable.js +498 -0
  172. package/src/scene/MemoryPool.js +618 -0
  173. package/src/scene/Node4D.js +697 -0
  174. package/src/scene/ResourceManager.js +599 -0
  175. package/src/scene/Scene4D.js +540 -0
  176. package/src/scene/index.js +98 -0
  177. package/src/schemas/error.schema.json +84 -0
  178. package/src/schemas/extension.schema.json +88 -0
  179. package/src/schemas/index.js +214 -0
  180. package/src/schemas/parameters.schema.json +142 -0
  181. package/src/schemas/tool-pack.schema.json +44 -0
  182. package/src/schemas/tool-response.schema.json +127 -0
  183. package/src/shaders/common/fullscreen.vert.glsl +5 -0
  184. package/src/shaders/common/fullscreen.vert.wgsl +17 -0
  185. package/src/shaders/common/geometry24.glsl +65 -0
  186. package/src/shaders/common/geometry24.wgsl +54 -0
  187. package/src/shaders/common/rotation4d.glsl +85 -0
  188. package/src/shaders/common/rotation4d.wgsl +86 -0
  189. package/src/shaders/common/uniforms.glsl +44 -0
  190. package/src/shaders/common/uniforms.wgsl +48 -0
  191. package/src/shaders/faceted/faceted.frag.glsl +129 -0
  192. package/src/shaders/faceted/faceted.frag.wgsl +164 -0
  193. package/src/shaders/holographic/holographic.frag.glsl +406 -0
  194. package/src/shaders/holographic/holographic.frag.wgsl +185 -0
  195. package/src/shaders/quantum/quantum.frag.glsl +513 -0
  196. package/src/shaders/quantum/quantum.frag.wgsl +361 -0
  197. package/src/testing/ParallelTestFramework.js +519 -0
  198. package/src/testing/__snapshots__/exportFormats.test.js.snap +24 -0
  199. package/src/testing/exportFormats.test.js +8 -0
  200. package/src/testing/projections.test.js +14 -0
  201. package/src/testing/rotations.test.js +37 -0
  202. package/src/ui/InteractivityMenu.js +516 -0
  203. package/src/ui/StatusManager.js +96 -0
  204. package/src/ui/adaptive/renderers/webgpu/BufferLayout.ts +252 -0
  205. package/src/ui/adaptive/renderers/webgpu/PolytopeInstanceBuffer.ts +144 -0
  206. package/src/ui/adaptive/renderers/webgpu/TripleBufferedUniform.ts +170 -0
  207. package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +735 -0
  208. package/src/ui/adaptive/renderers/webgpu/index.ts +112 -0
  209. package/src/variations/VariationManager.js +431 -0
  210. package/src/viewer/AudioReactivity.js +505 -0
  211. package/src/viewer/CardBending.js +481 -0
  212. package/src/viewer/GalleryUI.js +832 -0
  213. package/src/viewer/ReactivityManager.js +590 -0
  214. package/src/viewer/TradingCardExporter.js +600 -0
  215. package/src/viewer/ViewerPortal.js +374 -0
  216. package/src/viewer/index.js +12 -0
  217. package/src/wasm/WasmLoader.js +296 -0
  218. package/src/wasm/index.js +132 -0
  219. package/tools/agentic/mcpTools.js +88 -0
  220. package/tools/cli/agent-cli.js +92 -0
  221. package/tools/export/formats.js +24 -0
  222. package/tools/math/rotation-baseline.mjs +64 -0
  223. package/tools/shader-sync-verify.js +937 -0
  224. package/tools/telemetry/manifestPipeline.js +141 -0
  225. package/tools/telemetry/telemetryEvents.js +35 -0
  226. package/types/adaptive-sdk.d.ts +185 -0
  227. package/types/advanced/AIPresetGenerator.d.ts +81 -0
  228. package/types/advanced/MIDIController.d.ts +100 -0
  229. package/types/advanced/OffscreenWorker.d.ts +82 -0
  230. package/types/advanced/WebGPUCompute.d.ts +52 -0
  231. package/types/advanced/WebXRRenderer.d.ts +77 -0
  232. package/types/advanced/index.d.ts +46 -0
  233. package/types/core/ErrorReporter.d.ts +50 -0
  234. package/types/core/VIB3Engine.d.ts +204 -0
  235. package/types/creative/ColorPresetsSystem.d.ts +91 -0
  236. package/types/creative/ParameterTimeline.d.ts +74 -0
  237. package/types/creative/PostProcessingPipeline.d.ts +109 -0
  238. package/types/creative/TransitionAnimator.d.ts +71 -0
  239. package/types/creative/index.d.ts +35 -0
  240. package/types/integrations/FigmaPlugin.d.ts +46 -0
  241. package/types/integrations/OBSMode.d.ts +74 -0
  242. package/types/integrations/ThreeJsPackage.d.ts +62 -0
  243. package/types/integrations/TouchDesignerExport.d.ts +36 -0
  244. package/types/integrations/Vib3React.d.ts +74 -0
  245. package/types/integrations/Vib3Svelte.d.ts +63 -0
  246. package/types/integrations/Vib3Vue.d.ts +55 -0
  247. package/types/integrations/index.d.ts +52 -0
  248. package/types/reactivity/SpatialInputSystem.d.ts +173 -0
  249. package/types/reactivity/index.d.ts +394 -0
  250. package/types/render/CommandBuffer.d.ts +169 -0
  251. package/types/render/RenderCommand.d.ts +312 -0
  252. package/types/render/RenderState.d.ts +279 -0
  253. package/types/render/RenderTarget.d.ts +254 -0
  254. package/types/render/ShaderProgram.d.ts +277 -0
  255. package/types/render/UnifiedRenderBridge.d.ts +143 -0
  256. package/types/render/WebGLBackend.d.ts +168 -0
  257. package/types/render/WebGPUBackend.d.ts +186 -0
  258. package/types/render/index.d.ts +141 -0
@@ -0,0 +1,777 @@
1
+ /**
2
+ * VIB3+ AI Preset Generator
3
+ * Uses rule-based NLP keyword extraction and optional MCP/LLM integration
4
+ * to generate creative parameter presets from natural-language descriptions.
5
+ *
6
+ * The generator works in three tiers:
7
+ * 1. Built-in keyword-to-parameter vocabulary (no external API needed)
8
+ * 2. Algorithmic random generation with optional theme constraints
9
+ * 3. MCP protocol integration for full LLM-powered generation
10
+ *
11
+ * Also provides preset mutation and crossbreeding for evolutionary exploration.
12
+ *
13
+ * @module advanced/AIPresetGenerator
14
+ */
15
+
16
+ /**
17
+ * A VIB3 preset -- a complete set of visualization parameters.
18
+ * @typedef {Object} VIB3Preset
19
+ * @property {string} name - Human-readable name
20
+ * @property {string} description - How this preset was generated
21
+ * @property {number} geometry - Geometry index (0-23)
22
+ * @property {number} rot4dXY - XY rotation (0 - 2*PI)
23
+ * @property {number} rot4dXZ - XZ rotation
24
+ * @property {number} rot4dYZ - YZ rotation
25
+ * @property {number} rot4dXW - XW rotation
26
+ * @property {number} rot4dYW - YW rotation
27
+ * @property {number} rot4dZW - ZW rotation
28
+ * @property {number} gridDensity - Grid density (4-100)
29
+ * @property {number} morphFactor - Morph factor (0-2)
30
+ * @property {number} chaos - Chaos level (0-1)
31
+ * @property {number} speed - Animation speed (0.1-3)
32
+ * @property {number} hue - Color hue (0-360)
33
+ * @property {number} intensity - Brightness (0-1)
34
+ * @property {number} saturation - Color saturation (0-1)
35
+ * @property {number} dimension - Projection distance (3.0-4.5)
36
+ * @property {string} [system] - Optional target system (quantum|faceted|holographic)
37
+ * @property {number} timestamp - Creation timestamp (ms)
38
+ */
39
+
40
+ /** Parameter ranges used by the generator. */
41
+ const PARAM_RANGES = {
42
+ geometry: { min: 0, max: 23, step: 1, type: 'int' },
43
+ rot4dXY: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
44
+ rot4dXZ: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
45
+ rot4dYZ: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
46
+ rot4dXW: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
47
+ rot4dYW: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
48
+ rot4dZW: { min: 0, max: Math.PI * 2, step: null, type: 'float' },
49
+ gridDensity: { min: 4, max: 100, step: 1, type: 'float' },
50
+ morphFactor: { min: 0, max: 2, step: null, type: 'float' },
51
+ chaos: { min: 0, max: 1, step: null, type: 'float' },
52
+ speed: { min: 0.1, max: 3, step: null, type: 'float' },
53
+ hue: { min: 0, max: 360, step: null, type: 'float' },
54
+ intensity: { min: 0, max: 1, step: null, type: 'float' },
55
+ saturation: { min: 0, max: 1, step: null, type: 'float' },
56
+ dimension: { min: 3.0, max: 4.5, step: null, type: 'float' }
57
+ };
58
+
59
+ /**
60
+ * Style vocabulary: maps descriptive keywords to partial parameter overrides.
61
+ * Values are expressed as fractions of the parameter range (0-1) unless
62
+ * marked as absolute.
63
+ */
64
+ const STYLE_VOCABULARY = {
65
+ // Energy / Motion
66
+ energetic: { speed: [0.7, 1.0], chaos: [0.4, 0.7], intensity: [0.7, 1.0], hue: [0, 60] },
67
+ calm: { speed: [0.05, 0.25], chaos: [0.0, 0.15], intensity: [0.3, 0.6], hue: [180, 240] },
68
+ frantic: { speed: [0.85, 1.0], chaos: [0.7, 1.0], morphFactor: [0.6, 1.0], gridDensity: [0.5, 0.8] },
69
+ slow: { speed: [0.0, 0.15], chaos: [0.0, 0.1] },
70
+ fast: { speed: [0.7, 1.0], chaos: [0.2, 0.5] },
71
+ gentle: { speed: [0.05, 0.2], chaos: [0.0, 0.1], intensity: [0.2, 0.5] },
72
+ aggressive: { speed: [0.8, 1.0], chaos: [0.6, 1.0], intensity: [0.8, 1.0] },
73
+ pulsing: { speed: [0.4, 0.6], morphFactor: [0.5, 1.0], chaos: [0.2, 0.4] },
74
+ flowing: { speed: [0.2, 0.45], chaos: [0.1, 0.3], morphFactor: [0.3, 0.7] },
75
+ chaotic: { chaos: [0.7, 1.0], speed: [0.5, 0.8] },
76
+ orderly: { chaos: [0.0, 0.1], gridDensity: [0.4, 0.7] },
77
+ vibrating: { speed: [0.6, 0.8], morphFactor: [0.4, 0.8], chaos: [0.3, 0.5] },
78
+
79
+ // Color / Light
80
+ dark: { intensity: [0.05, 0.25], saturation: [0.2, 0.5], hue: [240, 300] },
81
+ bright: { intensity: [0.7, 1.0], saturation: [0.6, 1.0] },
82
+ neon: { intensity: [0.8, 1.0], saturation: [0.9, 1.0], hue: [280, 340] },
83
+ pastel: { intensity: [0.5, 0.7], saturation: [0.2, 0.4] },
84
+ monochrome: { saturation: [0.0, 0.05], intensity: [0.4, 0.8] },
85
+ warm: { hue: [0, 60], saturation: [0.5, 0.8], intensity: [0.5, 0.8] },
86
+ cool: { hue: [180, 270], saturation: [0.4, 0.7], intensity: [0.4, 0.7] },
87
+ fiery: { hue: [0, 30], saturation: [0.8, 1.0], intensity: [0.7, 1.0], speed: [0.5, 0.8] },
88
+ icy: { hue: [190, 220], saturation: [0.3, 0.6], intensity: [0.5, 0.8], speed: [0.1, 0.3] },
89
+ golden: { hue: [40, 55], saturation: [0.7, 0.9], intensity: [0.6, 0.85] },
90
+ sunset: { hue: [10, 45], saturation: [0.6, 0.9], intensity: [0.5, 0.8] },
91
+ ocean: { hue: [180, 220], saturation: [0.5, 0.8], speed: [0.2, 0.4] },
92
+ forest: { hue: [100, 150], saturation: [0.4, 0.7], intensity: [0.3, 0.6] },
93
+ electric: { intensity: [0.8, 1.0], saturation: [0.8, 1.0], hue: [220, 280] },
94
+ muted: { saturation: [0.1, 0.3], intensity: [0.3, 0.5] },
95
+ vivid: { saturation: [0.8, 1.0], intensity: [0.7, 1.0] },
96
+ purple: { hue: [270, 310], saturation: [0.6, 0.9] },
97
+ red: { hue: [350, 370], saturation: [0.7, 1.0] },
98
+ blue: { hue: [200, 250], saturation: [0.5, 0.8] },
99
+ green: { hue: [100, 150], saturation: [0.5, 0.8] },
100
+ pink: { hue: [320, 350], saturation: [0.5, 0.8], intensity: [0.6, 0.9] },
101
+
102
+ // Mood / Theme
103
+ psychedelic: { chaos: [0.5, 0.9], saturation: [0.8, 1.0], speed: [0.4, 0.7], morphFactor: [0.6, 1.0] },
104
+ zen: { chaos: [0.0, 0.05], speed: [0.05, 0.15], intensity: [0.3, 0.5], saturation: [0.2, 0.4] },
105
+ dreamy: { chaos: [0.1, 0.3], speed: [0.1, 0.3], intensity: [0.4, 0.6], morphFactor: [0.3, 0.6] },
106
+ nightmare: { chaos: [0.6, 0.9], intensity: [0.1, 0.3], hue: [280, 340], speed: [0.3, 0.6] },
107
+ ethereal: { intensity: [0.3, 0.5], saturation: [0.3, 0.6], chaos: [0.1, 0.25], speed: [0.1, 0.25] },
108
+ cosmic: { dimension: [0.6, 1.0], chaos: [0.3, 0.6], hue: [240, 300], intensity: [0.5, 0.8] },
109
+ organic: { morphFactor: [0.4, 0.8], chaos: [0.2, 0.4], speed: [0.2, 0.4] },
110
+ mechanical: { chaos: [0.0, 0.1], gridDensity: [0.6, 0.9], morphFactor: [0.0, 0.2] },
111
+ abstract: { chaos: [0.3, 0.6], morphFactor: [0.4, 0.8], dimension: [0.3, 0.7] },
112
+ minimal: { gridDensity: [0.0, 0.2], chaos: [0.0, 0.1], intensity: [0.3, 0.5] },
113
+ complex: { gridDensity: [0.7, 1.0], chaos: [0.3, 0.6], morphFactor: [0.5, 0.8] },
114
+ retro: { saturation: [0.4, 0.6], hue: [20, 60], gridDensity: [0.3, 0.5] },
115
+ futuristic: { saturation: [0.6, 0.9], hue: [180, 260], gridDensity: [0.5, 0.8] },
116
+ alien: { hue: [100, 160], chaos: [0.4, 0.7], morphFactor: [0.5, 0.9], dimension: [0.5, 0.9] },
117
+ underwater: { hue: [170, 210], speed: [0.15, 0.35], chaos: [0.15, 0.35] },
118
+ space: { hue: [230, 280], intensity: [0.3, 0.6], dimension: [0.7, 1.0] },
119
+ hypnotic: { speed: [0.3, 0.5], morphFactor: [0.5, 0.8], chaos: [0.15, 0.3] },
120
+ glitch: { chaos: [0.7, 1.0], speed: [0.6, 0.9], gridDensity: [0.5, 0.8] },
121
+ serene: { speed: [0.05, 0.2], chaos: [0.0, 0.1], intensity: [0.3, 0.5], hue: [160, 220] },
122
+
123
+ // Geometry hints
124
+ crystal: { _geometryBase: 7, gridDensity: [0.4, 0.7], chaos: [0.0, 0.15] },
125
+ fractal: { _geometryBase: 5, chaos: [0.3, 0.6], gridDensity: [0.5, 0.8] },
126
+ toroidal: { _geometryBase: 3, morphFactor: [0.3, 0.6] },
127
+ torus: { _geometryBase: 3 },
128
+ cube: { _geometryBase: 1 },
129
+ tesseract: { _geometryBase: 1, dimension: [0.5, 0.8] },
130
+ sphere: { _geometryBase: 2 },
131
+ wave: { _geometryBase: 6, speed: [0.3, 0.6] },
132
+ klein: { _geometryBase: 4, dimension: [0.4, 0.7] },
133
+ simplex: { _geometryBase: 0 },
134
+ tetrahedron: { _geometryBase: 0 },
135
+
136
+ // Core type hints
137
+ hypersphere: { _coreType: 1 },
138
+ hypertetra: { _coreType: 2 },
139
+ base: { _coreType: 0 },
140
+
141
+ // System hints
142
+ quantum: { _system: 'quantum' },
143
+ faceted: { _system: 'faceted' },
144
+ holographic: { _system: 'holographic' },
145
+ hologram: { _system: 'holographic' }
146
+ };
147
+
148
+ /**
149
+ * Named theme presets for the random generator.
150
+ */
151
+ const THEMES = {
152
+ deepSpace: {
153
+ hue: [220, 280], intensity: [0.2, 0.5], saturation: [0.4, 0.7],
154
+ speed: [0.1, 0.3], chaos: [0.1, 0.3], dimension: [0.6, 1.0],
155
+ gridDensity: [0.3, 0.6]
156
+ },
157
+ lavaLamp: {
158
+ hue: [0, 40], intensity: [0.6, 0.9], saturation: [0.7, 1.0],
159
+ speed: [0.15, 0.35], chaos: [0.2, 0.4], morphFactor: [0.5, 0.9]
160
+ },
161
+ arctic: {
162
+ hue: [190, 220], intensity: [0.5, 0.8], saturation: [0.2, 0.5],
163
+ speed: [0.05, 0.2], chaos: [0.0, 0.1], gridDensity: [0.2, 0.4]
164
+ },
165
+ acid: {
166
+ hue: [80, 160], intensity: [0.7, 1.0], saturation: [0.8, 1.0],
167
+ speed: [0.5, 0.9], chaos: [0.5, 0.9], morphFactor: [0.6, 1.0]
168
+ },
169
+ meditation: {
170
+ hue: [240, 280], intensity: [0.2, 0.4], saturation: [0.3, 0.5],
171
+ speed: [0.03, 0.12], chaos: [0.0, 0.05], morphFactor: [0.1, 0.3]
172
+ },
173
+ storm: {
174
+ hue: [200, 260], intensity: [0.3, 0.7], saturation: [0.4, 0.7],
175
+ speed: [0.6, 1.0], chaos: [0.5, 0.8], gridDensity: [0.5, 0.8]
176
+ },
177
+ synthwave: {
178
+ hue: [280, 340], intensity: [0.6, 0.9], saturation: [0.7, 1.0],
179
+ speed: [0.3, 0.5], chaos: [0.1, 0.3], gridDensity: [0.4, 0.7]
180
+ },
181
+ nature: {
182
+ hue: [80, 150], intensity: [0.4, 0.7], saturation: [0.4, 0.7],
183
+ speed: [0.1, 0.3], chaos: [0.1, 0.3], morphFactor: [0.2, 0.5]
184
+ }
185
+ };
186
+
187
+ export class AIPresetGenerator {
188
+ /**
189
+ * @param {Object} engine - VIB3Engine instance (optional, used for reading current state)
190
+ */
191
+ constructor(engine) {
192
+ /** @type {Object|null} */
193
+ this.engine = engine;
194
+
195
+ /** @type {string|null} MCP server endpoint URL */
196
+ this.mcpEndpoint = null;
197
+
198
+ /** @type {VIB3Preset[]} History of generated presets */
199
+ this.presetHistory = [];
200
+
201
+ /** @type {number} Maximum history entries */
202
+ this.maxHistory = 100;
203
+ }
204
+
205
+ // -----------------------------------------------------------------------
206
+ // Text Description -> Preset
207
+ // -----------------------------------------------------------------------
208
+
209
+ /**
210
+ * Generate a VIB3 preset from a natural-language text description.
211
+ * Uses the built-in style vocabulary to extract keywords and map them
212
+ * to parameter ranges. No external API required.
213
+ *
214
+ * @param {string} description - e.g. "calm ocean with gentle waves"
215
+ * @returns {Promise<VIB3Preset>}
216
+ */
217
+ async generateFromDescription(description) {
218
+ if (!description || typeof description !== 'string') {
219
+ throw new Error('Description must be a non-empty string.');
220
+ }
221
+
222
+ const normalized = description.toLowerCase().trim();
223
+ const tokens = this._tokenize(normalized);
224
+ const matchedStyles = this._matchKeywords(tokens);
225
+
226
+ // Build parameter constraints from matched styles
227
+ const constraints = {};
228
+ let geometryBase = null;
229
+ let coreType = null;
230
+ let system = null;
231
+
232
+ for (const style of matchedStyles) {
233
+ const mapping = STYLE_VOCABULARY[style];
234
+ if (!mapping) continue;
235
+
236
+ // Handle special keys
237
+ if (mapping._geometryBase !== undefined && geometryBase === null) {
238
+ geometryBase = mapping._geometryBase;
239
+ }
240
+ if (mapping._coreType !== undefined && coreType === null) {
241
+ coreType = mapping._coreType;
242
+ }
243
+ if (mapping._system !== undefined && system === null) {
244
+ system = mapping._system;
245
+ }
246
+
247
+ // Merge parameter constraints (later matches override earlier)
248
+ for (const [param, range] of Object.entries(mapping)) {
249
+ if (param.startsWith('_')) continue;
250
+ constraints[param] = range;
251
+ }
252
+ }
253
+
254
+ // Generate parameter values from constraints
255
+ const preset = this._generateFromConstraints(constraints, geometryBase, coreType);
256
+
257
+ preset.name = this._generatePresetName(matchedStyles, description);
258
+ preset.description = `Generated from: "${description}"`;
259
+ if (system) {
260
+ preset.system = system;
261
+ }
262
+ preset.timestamp = Date.now();
263
+
264
+ // Store in history
265
+ this._addToHistory(preset);
266
+
267
+ return preset;
268
+ }
269
+
270
+ /**
271
+ * Get the comprehensive style vocabulary mapping.
272
+ * @returns {Object} Keyword-to-parameter mapping dictionary
273
+ */
274
+ getStyleMapping() {
275
+ return { ...STYLE_VOCABULARY };
276
+ }
277
+
278
+ /**
279
+ * Get available theme names for random generation.
280
+ * @returns {string[]}
281
+ */
282
+ getAvailableThemes() {
283
+ return Object.keys(THEMES);
284
+ }
285
+
286
+ // -----------------------------------------------------------------------
287
+ // MCP / LLM Integration
288
+ // -----------------------------------------------------------------------
289
+
290
+ /**
291
+ * Set the MCP endpoint for LLM-based generation.
292
+ * @param {string} endpoint - URL of the MCP server
293
+ */
294
+ setMCPEndpoint(endpoint) {
295
+ this.mcpEndpoint = endpoint;
296
+ }
297
+
298
+ /**
299
+ * Generate a preset via MCP protocol communicating with an LLM.
300
+ * Sends the current visualization state plus the user prompt,
301
+ * and expects a structured parameter response.
302
+ *
303
+ * @param {string} prompt - User's creative prompt
304
+ * @returns {Promise<VIB3Preset>}
305
+ * @throws {Error} If MCP endpoint is not configured or request fails
306
+ */
307
+ async generateViaMCP(prompt) {
308
+ if (!this.mcpEndpoint) {
309
+ throw new Error('MCP endpoint not configured. Call setMCPEndpoint() first.');
310
+ }
311
+
312
+ // Gather current state
313
+ const currentState = this._getCurrentState();
314
+
315
+ const requestBody = {
316
+ jsonrpc: '2.0',
317
+ id: Date.now(),
318
+ method: 'tools/call',
319
+ params: {
320
+ name: 'generate_preset',
321
+ arguments: {
322
+ prompt,
323
+ currentState,
324
+ parameterRanges: PARAM_RANGES,
325
+ availableStyles: Object.keys(STYLE_VOCABULARY)
326
+ }
327
+ }
328
+ };
329
+
330
+ const response = await fetch(this.mcpEndpoint, {
331
+ method: 'POST',
332
+ headers: { 'Content-Type': 'application/json' },
333
+ body: JSON.stringify(requestBody)
334
+ });
335
+
336
+ if (!response.ok) {
337
+ throw new Error(`MCP request failed: ${response.status} ${response.statusText}`);
338
+ }
339
+
340
+ const result = await response.json();
341
+
342
+ if (result.error) {
343
+ throw new Error(`MCP error: ${result.error.message || JSON.stringify(result.error)}`);
344
+ }
345
+
346
+ // Parse LLM response into a preset
347
+ const llmOutput = result.result;
348
+ const preset = this._parseLLMResponse(llmOutput);
349
+
350
+ preset.name = preset.name || `LLM: ${prompt.substring(0, 30)}`;
351
+ preset.description = `LLM generated from: "${prompt}"`;
352
+ preset.timestamp = Date.now();
353
+
354
+ this._addToHistory(preset);
355
+
356
+ return preset;
357
+ }
358
+
359
+ // -----------------------------------------------------------------------
360
+ // Random Generation
361
+ // -----------------------------------------------------------------------
362
+
363
+ /**
364
+ * Generate a random creative preset with optional theme constraint.
365
+ *
366
+ * @param {string|null} [theme=null] - Theme name (e.g. 'deepSpace', 'lavaLamp')
367
+ * or null for fully random.
368
+ * @returns {VIB3Preset}
369
+ */
370
+ generateRandom(theme = null) {
371
+ let constraints = {};
372
+
373
+ if (theme && THEMES[theme]) {
374
+ constraints = { ...THEMES[theme] };
375
+ }
376
+
377
+ // Random geometry
378
+ const geometryBase = Math.floor(Math.random() * 8);
379
+ const coreType = Math.floor(Math.random() * 3);
380
+
381
+ const preset = this._generateFromConstraints(constraints, geometryBase, coreType);
382
+
383
+ preset.name = theme
384
+ ? `${this._capitalize(theme)} #${Math.floor(Math.random() * 1000)}`
385
+ : `Random #${Math.floor(Math.random() * 10000)}`;
386
+ preset.description = theme ? `Random preset with ${theme} theme` : 'Fully random preset';
387
+ preset.timestamp = Date.now();
388
+
389
+ this._addToHistory(preset);
390
+
391
+ return preset;
392
+ }
393
+
394
+ // -----------------------------------------------------------------------
395
+ // Mutation & Crossbreeding
396
+ // -----------------------------------------------------------------------
397
+
398
+ /**
399
+ * Mutate an existing preset by randomly varying parameters while
400
+ * maintaining overall coherence.
401
+ *
402
+ * @param {VIB3Preset} preset - Source preset
403
+ * @param {number} [intensity=0.3] - Mutation intensity (0 = no change, 1 = fully random)
404
+ * @returns {VIB3Preset} New mutated preset
405
+ */
406
+ mutate(preset, intensity = 0.3) {
407
+ const clamped = Math.max(0, Math.min(1, intensity));
408
+ const result = { ...preset };
409
+
410
+ for (const [param, range] of Object.entries(PARAM_RANGES)) {
411
+ if (preset[param] === undefined) continue;
412
+
413
+ const currentNorm = (preset[param] - range.min) / (range.max - range.min);
414
+
415
+ // Apply gaussian-like mutation
416
+ const mutation = (Math.random() - 0.5) * 2.0 * clamped;
417
+ let newNorm = currentNorm + mutation;
418
+ newNorm = Math.max(0, Math.min(1, newNorm));
419
+
420
+ let newValue = range.min + newNorm * (range.max - range.min);
421
+
422
+ if (range.type === 'int') {
423
+ newValue = Math.round(newValue);
424
+ }
425
+
426
+ result[param] = newValue;
427
+ }
428
+
429
+ result.name = `Mutated: ${preset.name || 'Unnamed'} (${Math.round(clamped * 100)}%)`;
430
+ result.description = `Mutated from "${preset.name || 'unnamed'}" at ${Math.round(clamped * 100)}% intensity`;
431
+ result.timestamp = Date.now();
432
+
433
+ this._addToHistory(result);
434
+
435
+ return result;
436
+ }
437
+
438
+ /**
439
+ * Crossbreed two presets by blending their parameters.
440
+ *
441
+ * @param {VIB3Preset} presetA - Parent A
442
+ * @param {VIB3Preset} presetB - Parent B
443
+ * @param {number} [ratio=0.5] - Blend ratio (0 = all A, 1 = all B)
444
+ * @returns {VIB3Preset} Offspring preset
445
+ */
446
+ crossbreed(presetA, presetB, ratio = 0.5) {
447
+ const r = Math.max(0, Math.min(1, ratio));
448
+ const result = {};
449
+
450
+ for (const [param, range] of Object.entries(PARAM_RANGES)) {
451
+ const valA = presetA[param] !== undefined ? presetA[param] : this._randomInRange(range);
452
+ const valB = presetB[param] !== undefined ? presetB[param] : this._randomInRange(range);
453
+
454
+ // For geometry, pick one parent based on ratio
455
+ if (range.type === 'int') {
456
+ result[param] = Math.random() < r
457
+ ? Math.round(valB)
458
+ : Math.round(valA);
459
+ } else {
460
+ // Linear interpolation
461
+ result[param] = valA * (1.0 - r) + valB * r;
462
+ }
463
+ }
464
+
465
+ result.name = `Crossbred: ${(presetA.name || 'A').substring(0, 15)} x ${(presetB.name || 'B').substring(0, 15)}`;
466
+ result.description = `Crossbred at ${Math.round(r * 100)}% ratio`;
467
+ result.timestamp = Date.now();
468
+
469
+ this._addToHistory(result);
470
+
471
+ return result;
472
+ }
473
+
474
+ // -----------------------------------------------------------------------
475
+ // History
476
+ // -----------------------------------------------------------------------
477
+
478
+ /**
479
+ * Export the full preset generation history.
480
+ * @returns {VIB3Preset[]}
481
+ */
482
+ exportHistory() {
483
+ return [...this.presetHistory];
484
+ }
485
+
486
+ /**
487
+ * Clear the preset history.
488
+ */
489
+ clearHistory() {
490
+ this.presetHistory = [];
491
+ }
492
+
493
+ /**
494
+ * Get the most recently generated preset.
495
+ * @returns {VIB3Preset|null}
496
+ */
497
+ getLastPreset() {
498
+ return this.presetHistory.length > 0
499
+ ? this.presetHistory[this.presetHistory.length - 1]
500
+ : null;
501
+ }
502
+
503
+ // -----------------------------------------------------------------------
504
+ // Internal Helpers
505
+ // -----------------------------------------------------------------------
506
+
507
+ /**
508
+ * Tokenize a description string into individual keywords.
509
+ * @param {string} text - Lowercased description
510
+ * @returns {string[]}
511
+ * @private
512
+ */
513
+ _tokenize(text) {
514
+ // Remove punctuation and split on whitespace
515
+ return text
516
+ .replace(/[^a-z0-9\s-]/g, ' ')
517
+ .split(/\s+/)
518
+ .filter(t => t.length > 1);
519
+ }
520
+
521
+ /**
522
+ * Match tokens against the style vocabulary.
523
+ * Also attempts partial/fuzzy matching for common suffixes.
524
+ *
525
+ * @param {string[]} tokens
526
+ * @returns {string[]} Matched style keywords
527
+ * @private
528
+ */
529
+ _matchKeywords(tokens) {
530
+ const matched = [];
531
+ const vocabKeys = Object.keys(STYLE_VOCABULARY);
532
+
533
+ for (const token of tokens) {
534
+ // Direct match
535
+ if (STYLE_VOCABULARY[token]) {
536
+ matched.push(token);
537
+ continue;
538
+ }
539
+
540
+ // Try common variations
541
+ const variations = [
542
+ token,
543
+ token.replace(/s$/, ''), // plurals
544
+ token.replace(/ing$/, ''), // gerunds
545
+ token.replace(/ed$/, ''), // past tense
546
+ token.replace(/ly$/, ''), // adverbs -> adjectives
547
+ token.replace(/ish$/, ''), // bluish -> blue
548
+ token.replace(/ness$/, ''), // darkness -> dark
549
+ token.replace(/y$/, ''), // wavy -> wav
550
+ token + 'al', // cosmic -> cosmical
551
+ ];
552
+
553
+ for (const variant of variations) {
554
+ if (STYLE_VOCABULARY[variant]) {
555
+ matched.push(variant);
556
+ break;
557
+ }
558
+ }
559
+
560
+ // Substring match (e.g. "psychedelia" matches "psychedelic")
561
+ if (!matched.includes(token)) {
562
+ for (const key of vocabKeys) {
563
+ if (key.startsWith(token) || token.startsWith(key)) {
564
+ if (!matched.includes(key)) {
565
+ matched.push(key);
566
+ }
567
+ break;
568
+ }
569
+ }
570
+ }
571
+ }
572
+
573
+ return matched;
574
+ }
575
+
576
+ /**
577
+ * Generate a preset from parameter constraints.
578
+ * Constraints are expressed as [low, high] normalized fractions of the
579
+ * parameter range.
580
+ *
581
+ * @param {Object} constraints - Param name -> [low, high] (0-1 fractions)
582
+ * @param {number|null} geometryBase - Forced base geometry (0-7) or null
583
+ * @param {number|null} coreType - Forced core type (0-2) or null
584
+ * @returns {VIB3Preset}
585
+ * @private
586
+ */
587
+ _generateFromConstraints(constraints, geometryBase, coreType) {
588
+ const preset = {};
589
+
590
+ for (const [param, range] of Object.entries(PARAM_RANGES)) {
591
+ if (param === 'geometry') continue; // Handle separately
592
+
593
+ if (constraints[param]) {
594
+ const [lo, hi] = constraints[param];
595
+ const norm = lo + Math.random() * (hi - lo);
596
+ preset[param] = range.min + norm * (range.max - range.min);
597
+ } else {
598
+ preset[param] = this._randomInRange(range);
599
+ }
600
+
601
+ if (range.type === 'int') {
602
+ preset[param] = Math.round(preset[param]);
603
+ }
604
+ }
605
+
606
+ // Geometry index = coreType * 8 + baseGeometry
607
+ const base = geometryBase !== null ? geometryBase : Math.floor(Math.random() * 8);
608
+ const core = coreType !== null ? coreType : Math.floor(Math.random() * 3);
609
+ preset.geometry = core * 8 + base;
610
+
611
+ // Handle hue wrapping (values like [350, 370] should wrap around 360)
612
+ if (preset.hue > 360) {
613
+ preset.hue = preset.hue - 360;
614
+ }
615
+
616
+ return preset;
617
+ }
618
+
619
+ /**
620
+ * Generate a random value within a parameter range.
621
+ * @param {{min: number, max: number, type: string}} range
622
+ * @returns {number}
623
+ * @private
624
+ */
625
+ _randomInRange(range) {
626
+ const val = range.min + Math.random() * (range.max - range.min);
627
+ return range.type === 'int' ? Math.round(val) : val;
628
+ }
629
+
630
+ /**
631
+ * Generate a human-readable name from matched styles.
632
+ * @param {string[]} styles
633
+ * @param {string} description
634
+ * @returns {string}
635
+ * @private
636
+ */
637
+ _generatePresetName(styles, description) {
638
+ if (styles.length === 0) {
639
+ // Take first few words of description
640
+ const words = description.split(/\s+/).slice(0, 3);
641
+ return words.map(w => this._capitalize(w)).join(' ');
642
+ }
643
+
644
+ // Combine up to 3 matched styles into a name
645
+ const nameStyles = styles.slice(0, 3);
646
+ return nameStyles.map(s => this._capitalize(s)).join(' ');
647
+ }
648
+
649
+ /**
650
+ * Capitalize first letter of a string.
651
+ * @param {string} str
652
+ * @returns {string}
653
+ * @private
654
+ */
655
+ _capitalize(str) {
656
+ if (!str) return '';
657
+ return str.charAt(0).toUpperCase() + str.slice(1);
658
+ }
659
+
660
+ /**
661
+ * Get the current engine state (if engine is available).
662
+ * @returns {Object}
663
+ * @private
664
+ */
665
+ _getCurrentState() {
666
+ if (!this.engine || typeof this.engine.getParameter !== 'function') {
667
+ return {};
668
+ }
669
+
670
+ const state = {};
671
+ for (const param of Object.keys(PARAM_RANGES)) {
672
+ try {
673
+ state[param] = this.engine.getParameter(param);
674
+ } catch (_e) {
675
+ // Parameter may not exist
676
+ }
677
+ }
678
+
679
+ return state;
680
+ }
681
+
682
+ /**
683
+ * Parse an LLM response into a VIB3Preset. The LLM is expected to return
684
+ * either a JSON object with parameter keys or a content array with text.
685
+ *
686
+ * @param {Object} llmOutput
687
+ * @returns {VIB3Preset}
688
+ * @private
689
+ */
690
+ _parseLLMResponse(llmOutput) {
691
+ const preset = {};
692
+
693
+ // Try direct parameter extraction
694
+ let paramSource = llmOutput;
695
+
696
+ // Handle MCP content array format
697
+ if (llmOutput && llmOutput.content && Array.isArray(llmOutput.content)) {
698
+ for (const block of llmOutput.content) {
699
+ if (block.type === 'text') {
700
+ try {
701
+ // Try to parse JSON from text
702
+ const jsonMatch = block.text.match(/\{[\s\S]*\}/);
703
+ if (jsonMatch) {
704
+ paramSource = JSON.parse(jsonMatch[0]);
705
+ }
706
+ } catch (_e) {
707
+ // Not valid JSON, try keyword extraction
708
+ return this._generateFromConstraints(
709
+ this._extractConstraintsFromText(block.text),
710
+ null,
711
+ null
712
+ );
713
+ }
714
+ }
715
+ }
716
+ }
717
+
718
+ // Map known parameters from the source
719
+ for (const [param, range] of Object.entries(PARAM_RANGES)) {
720
+ if (paramSource[param] !== undefined) {
721
+ let val = parseFloat(paramSource[param]);
722
+ if (!isNaN(val)) {
723
+ val = Math.max(range.min, Math.min(range.max, val));
724
+ preset[param] = range.type === 'int' ? Math.round(val) : val;
725
+ }
726
+ }
727
+ }
728
+
729
+ // Fill any missing parameters with defaults
730
+ for (const [param, range] of Object.entries(PARAM_RANGES)) {
731
+ if (preset[param] === undefined) {
732
+ preset[param] = this._randomInRange(range);
733
+ }
734
+ }
735
+
736
+ if (paramSource.name) preset.name = String(paramSource.name);
737
+ if (paramSource.system) preset.system = String(paramSource.system);
738
+
739
+ return preset;
740
+ }
741
+
742
+ /**
743
+ * Extract parameter constraints from unstructured text (fallback).
744
+ * @param {string} text
745
+ * @returns {Object}
746
+ * @private
747
+ */
748
+ _extractConstraintsFromText(text) {
749
+ const tokens = this._tokenize(text.toLowerCase());
750
+ const matched = this._matchKeywords(tokens);
751
+ const constraints = {};
752
+
753
+ for (const style of matched) {
754
+ const mapping = STYLE_VOCABULARY[style];
755
+ if (!mapping) continue;
756
+ for (const [param, range] of Object.entries(mapping)) {
757
+ if (!param.startsWith('_')) {
758
+ constraints[param] = range;
759
+ }
760
+ }
761
+ }
762
+
763
+ return constraints;
764
+ }
765
+
766
+ /**
767
+ * Add a preset to history, trimming if needed.
768
+ * @param {VIB3Preset} preset
769
+ * @private
770
+ */
771
+ _addToHistory(preset) {
772
+ this.presetHistory.push(preset);
773
+ if (this.presetHistory.length > this.maxHistory) {
774
+ this.presetHistory = this.presetHistory.slice(-this.maxHistory);
775
+ }
776
+ }
777
+ }