@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,854 @@
1
+ /**
2
+ * VIB3+ Figma Plugin Integration
3
+ *
4
+ * Generates a complete Figma plugin package that renders VIB3+ 4D visualizations
5
+ * and exports them as image fills, effects, or standalone frames within Figma.
6
+ *
7
+ * The plugin uses an off-screen canvas with WebGL to render VIB3 shaders,
8
+ * then transfers the pixel data to Figma via the Plugin API.
9
+ *
10
+ * Usage:
11
+ * const pkg = Vib3FigmaPlugin.generatePackage();
12
+ * // Write all files to disk, then publish via Figma Developer Console
13
+ *
14
+ * @module Vib3FigmaPlugin
15
+ * @version 1.0.0
16
+ * @license All Rights Reserved - Clear Seas Solutions LLC
17
+ */
18
+
19
+ /**
20
+ * Default render dimensions for plugin output.
21
+ * @constant {Object}
22
+ */
23
+ const DEFAULT_RENDER_CONFIG = {
24
+ width: 1024,
25
+ height: 1024,
26
+ pixelRatio: 2,
27
+ format: 'PNG'
28
+ };
29
+
30
+ /**
31
+ * VIB3+ systems available in the plugin.
32
+ * @constant {string[]}
33
+ */
34
+ const AVAILABLE_SYSTEMS = ['quantum', 'faceted', 'holographic'];
35
+
36
+ /**
37
+ * Base geometry names for the UI selector.
38
+ * @constant {string[]}
39
+ */
40
+ const BASE_GEOMETRIES = [
41
+ 'Tetrahedron', 'Hypercube', 'Sphere', 'Torus',
42
+ 'Klein Bottle', 'Fractal', 'Wave', 'Crystal'
43
+ ];
44
+
45
+ /**
46
+ * Core type names for the UI selector.
47
+ * @constant {string[]}
48
+ */
49
+ const CORE_TYPES = ['Base', 'Hypersphere', 'Hypertetrahedron'];
50
+
51
+ /**
52
+ * Figma plugin integration for VIB3+ visualization engine.
53
+ *
54
+ * Generates all necessary files for a Figma plugin:
55
+ * - manifest.json (plugin metadata and permissions)
56
+ * - code.js (plugin sandbox code using Figma Plugin API)
57
+ * - ui.html (plugin UI with parameter controls and preview)
58
+ *
59
+ * @class
60
+ */
61
+ export class Vib3FigmaPlugin {
62
+ /**
63
+ * Generates the Figma plugin manifest.json content.
64
+ *
65
+ * Configures the plugin with:
66
+ * - Plugin name, ID, and API version
67
+ * - Edit permission for creating/modifying fills
68
+ * - UI entry point and dimensions
69
+ * - Network access for loading VIB3 core assets
70
+ *
71
+ * @param {object} [options={}] - Manifest customization options
72
+ * @param {string} [options.name='VIB3+ 4D Visualizer'] - Plugin display name
73
+ * @param {string} [options.id='vib3-figma-plugin'] - Plugin identifier
74
+ * @param {string} [options.description] - Plugin description text
75
+ * @returns {object} Figma plugin manifest object
76
+ * @example
77
+ * const manifest = Vib3FigmaPlugin.generatePluginManifest({ name: 'My VIB3 Plugin' });
78
+ * fs.writeFileSync('manifest.json', JSON.stringify(manifest, null, 2));
79
+ */
80
+ static generatePluginManifest(options = {}) {
81
+ return {
82
+ name: options.name || 'VIB3+ 4D Visualizer',
83
+ id: options.id || 'vib3-figma-plugin',
84
+ api: '1.0.0',
85
+ main: 'code.js',
86
+ ui: 'ui.html',
87
+ editorType: ['figma'],
88
+ capabilities: [],
89
+ enableProposedApi: false,
90
+ permissions: ['currentuser'],
91
+ networkAccess: {
92
+ allowedDomains: ['none'],
93
+ reasoning: 'VIB3 renders entirely client-side using embedded shaders.'
94
+ },
95
+ description: options.description ||
96
+ 'Generate stunning 4D visualizations with quantum, faceted, and holographic rendering systems. ' +
97
+ 'Supports 24 geometry variants with full 6D rotation control.',
98
+ documentAccess: 'dynamic-page',
99
+ containsWidget: false
100
+ };
101
+ }
102
+
103
+ /**
104
+ * Generates the main plugin sandbox code (code.js).
105
+ *
106
+ * This code runs in the Figma plugin sandbox and:
107
+ * - Opens the plugin UI at a configurable size
108
+ * - Receives rendered pixel data from the UI iframe
109
+ * - Creates Figma rectangles with the visualization as an image fill
110
+ * - Supports batch generation of multiple geometry variants
111
+ * - Handles error reporting back to the UI
112
+ *
113
+ * @param {object} [options={}] - Code generation options
114
+ * @param {number} [options.uiWidth=480] - Plugin UI panel width
115
+ * @param {number} [options.uiHeight=640] - Plugin UI panel height
116
+ * @returns {string} Complete plugin sandbox code
117
+ */
118
+ static generatePluginCode(options = {}) {
119
+ const uiWidth = options.uiWidth || 480;
120
+ const uiHeight = options.uiHeight || 640;
121
+
122
+ return `/**
123
+ * VIB3+ Figma Plugin - Sandbox Code
124
+ * Runs in the Figma plugin sandbox (no DOM access).
125
+ * Communicates with ui.html via figma.ui.postMessage / figma.ui.onmessage.
126
+ */
127
+
128
+ figma.showUI(__html__, {
129
+ width: ${uiWidth},
130
+ height: ${uiHeight},
131
+ title: 'VIB3+ 4D Visualizer'
132
+ });
133
+
134
+ /**
135
+ * Handle messages from the plugin UI.
136
+ */
137
+ figma.ui.onmessage = async (msg) => {
138
+ try {
139
+ switch (msg.type) {
140
+ case 'render-complete':
141
+ await handleRenderComplete(msg);
142
+ break;
143
+
144
+ case 'batch-render':
145
+ await handleBatchRender(msg);
146
+ break;
147
+
148
+ case 'apply-fill':
149
+ await handleApplyFill(msg);
150
+ break;
151
+
152
+ case 'cancel':
153
+ figma.closePlugin();
154
+ break;
155
+
156
+ default:
157
+ console.warn('Unknown message type:', msg.type);
158
+ }
159
+ } catch (err) {
160
+ figma.ui.postMessage({
161
+ type: 'error',
162
+ message: err.message || 'An unknown error occurred'
163
+ });
164
+ figma.notify('VIB3+ Error: ' + (err.message || 'Unknown error'), { error: true });
165
+ }
166
+ };
167
+
168
+ /**
169
+ * Create a Figma rectangle with the rendered VIB3 visualization as an image fill.
170
+ *
171
+ * @param {object} msg - Message containing imageData (Uint8Array), width, height, name
172
+ */
173
+ async function handleRenderComplete(msg) {
174
+ const { imageData, width, height, name } = msg;
175
+
176
+ if (!imageData || !width || !height) {
177
+ throw new Error('Invalid render data: missing imageData, width, or height');
178
+ }
179
+
180
+ // Create image from raw pixel data
181
+ const image = figma.createImage(new Uint8Array(imageData));
182
+
183
+ // Create rectangle node
184
+ const rect = figma.createRectangle();
185
+ rect.name = name || 'VIB3+ Visualization';
186
+ rect.resize(width, height);
187
+
188
+ // Apply image as fill
189
+ rect.fills = [{
190
+ type: 'IMAGE',
191
+ scaleMode: 'FILL',
192
+ imageHash: image.hash
193
+ }];
194
+
195
+ // Position in viewport center
196
+ const viewCenter = figma.viewport.center;
197
+ rect.x = viewCenter.x - width / 2;
198
+ rect.y = viewCenter.y - height / 2;
199
+
200
+ // Select the new node
201
+ figma.currentPage.selection = [rect];
202
+ figma.viewport.scrollAndZoomIntoView([rect]);
203
+
204
+ figma.notify('VIB3+ visualization created!');
205
+ figma.ui.postMessage({ type: 'render-inserted', nodeId: rect.id });
206
+ }
207
+
208
+ /**
209
+ * Generate multiple geometry variants and arrange them in a grid.
210
+ *
211
+ * @param {object} msg - Message containing params, count, columns, cellSize
212
+ */
213
+ async function handleBatchRender(msg) {
214
+ const { renders, columns, cellSize, name } = msg;
215
+
216
+ if (!renders || !Array.isArray(renders) || renders.length === 0) {
217
+ throw new Error('Invalid batch render data');
218
+ }
219
+
220
+ const cols = columns || 4;
221
+ const size = cellSize || 512;
222
+ const groupName = name || 'VIB3+ Batch';
223
+
224
+ const nodes = [];
225
+ const viewCenter = figma.viewport.center;
226
+ const totalCols = Math.min(cols, renders.length);
227
+ const totalRows = Math.ceil(renders.length / cols);
228
+ const startX = viewCenter.x - (totalCols * (size + 20)) / 2;
229
+ const startY = viewCenter.y - (totalRows * (size + 20)) / 2;
230
+
231
+ for (let i = 0; i < renders.length; i++) {
232
+ const render = renders[i];
233
+ const col = i % cols;
234
+ const row = Math.floor(i / cols);
235
+
236
+ const image = figma.createImage(new Uint8Array(render.imageData));
237
+
238
+ const rect = figma.createRectangle();
239
+ rect.name = render.name || 'VIB3+ Variant ' + i;
240
+ rect.resize(size, size);
241
+ rect.fills = [{
242
+ type: 'IMAGE',
243
+ scaleMode: 'FILL',
244
+ imageHash: image.hash
245
+ }];
246
+
247
+ rect.x = startX + col * (size + 20);
248
+ rect.y = startY + row * (size + 20);
249
+
250
+ // Add rounded corners
251
+ rect.cornerRadius = 8;
252
+
253
+ nodes.push(rect);
254
+ }
255
+
256
+ // Group all nodes
257
+ if (nodes.length > 1) {
258
+ const group = figma.group(nodes, figma.currentPage);
259
+ group.name = groupName;
260
+ figma.currentPage.selection = [group];
261
+ figma.viewport.scrollAndZoomIntoView([group]);
262
+ } else if (nodes.length === 1) {
263
+ figma.currentPage.selection = nodes;
264
+ figma.viewport.scrollAndZoomIntoView(nodes);
265
+ }
266
+
267
+ figma.notify('VIB3+ batch: ' + renders.length + ' visualizations created!');
268
+ figma.ui.postMessage({ type: 'batch-complete', count: renders.length });
269
+ }
270
+
271
+ /**
272
+ * Apply a VIB3 visualization as fill to the currently selected node.
273
+ *
274
+ * @param {object} msg - Message containing imageData, width, height
275
+ */
276
+ async function handleApplyFill(msg) {
277
+ const { imageData } = msg;
278
+ const selection = figma.currentPage.selection;
279
+
280
+ if (selection.length === 0) {
281
+ figma.notify('Please select a shape first.', { error: true });
282
+ return;
283
+ }
284
+
285
+ const image = figma.createImage(new Uint8Array(imageData));
286
+
287
+ let appliedCount = 0;
288
+ for (const node of selection) {
289
+ if ('fills' in node) {
290
+ node.fills = [{
291
+ type: 'IMAGE',
292
+ scaleMode: 'FILL',
293
+ imageHash: image.hash
294
+ }];
295
+ appliedCount++;
296
+ }
297
+ }
298
+
299
+ if (appliedCount > 0) {
300
+ figma.notify('VIB3+ fill applied to ' + appliedCount + ' shape(s)!');
301
+ } else {
302
+ figma.notify('Selected nodes do not support fills.', { error: true });
303
+ }
304
+
305
+ figma.ui.postMessage({ type: 'fill-applied', count: appliedCount });
306
+ }
307
+ `;
308
+ }
309
+
310
+ /**
311
+ * Generates the plugin UI HTML file.
312
+ *
313
+ * The UI provides:
314
+ * - System selector (Quantum / Faceted / Holographic)
315
+ * - Geometry variant grid (24 options)
316
+ * - Parameter sliders (hue, saturation, intensity, speed, etc.)
317
+ * - 6D rotation controls
318
+ * - Live WebGL preview canvas
319
+ * - Export buttons (Create New, Apply to Selection, Batch Generate)
320
+ * - Render size configuration
321
+ *
322
+ * The UI renders VIB3 shaders on an off-screen WebGL canvas and
323
+ * sends pixel data to the plugin sandbox via parent.postMessage.
324
+ *
325
+ * @param {object} [options={}] - UI customization options
326
+ * @param {number} [options.previewSize=256] - Preview canvas size in pixels
327
+ * @returns {string} Complete HTML string for the plugin UI
328
+ */
329
+ static generatePluginUI(options = {}) {
330
+ const previewSize = options.previewSize || 256;
331
+
332
+ return `<!DOCTYPE html>
333
+ <html lang="en">
334
+ <head>
335
+ <meta charset="UTF-8">
336
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
337
+ <title>VIB3+ 4D Visualizer</title>
338
+ <style>
339
+ * { margin: 0; padding: 0; box-sizing: border-box; }
340
+ body {
341
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
342
+ font-size: 11px;
343
+ color: #e0e0e0;
344
+ background: #1e1e2e;
345
+ overflow-y: auto;
346
+ padding: 12px;
347
+ }
348
+ h2 { font-size: 13px; margin-bottom: 8px; color: #64ffb4; text-transform: uppercase; letter-spacing: 1px; }
349
+ h3 { font-size: 11px; margin-bottom: 6px; color: #aaa; text-transform: uppercase; letter-spacing: 0.5px; }
350
+ .section { margin-bottom: 16px; }
351
+ .preview-container {
352
+ display: flex; justify-content: center; margin-bottom: 12px;
353
+ }
354
+ #preview-canvas {
355
+ width: ${previewSize}px; height: ${previewSize}px;
356
+ border-radius: 8px; border: 1px solid #333;
357
+ background: #000;
358
+ }
359
+ .system-tabs {
360
+ display: flex; gap: 4px; margin-bottom: 12px;
361
+ }
362
+ .system-tab {
363
+ flex: 1; padding: 6px 0; text-align: center;
364
+ background: #2a2a3e; border: 1px solid #3a3a5e; border-radius: 4px;
365
+ color: #888; cursor: pointer; font-size: 10px; text-transform: uppercase;
366
+ transition: all 0.2s;
367
+ }
368
+ .system-tab.active { background: #3a3a6e; color: #64ffb4; border-color: #64ffb4; }
369
+ .system-tab:hover:not(.active) { background: #333358; color: #ccc; }
370
+ .geometry-grid {
371
+ display: grid; grid-template-columns: repeat(8, 1fr); gap: 3px; margin-bottom: 12px;
372
+ }
373
+ .geom-btn {
374
+ padding: 4px 0; text-align: center; font-size: 9px;
375
+ background: #2a2a3e; border: 1px solid #3a3a5e; border-radius: 3px;
376
+ color: #888; cursor: pointer; transition: all 0.15s;
377
+ }
378
+ .geom-btn.active { background: #3a3a6e; color: #64ffb4; border-color: #64ffb4; }
379
+ .geom-btn:hover:not(.active) { background: #333358; color: #ccc; }
380
+ .slider-row {
381
+ display: flex; align-items: center; margin-bottom: 6px;
382
+ }
383
+ .slider-label { width: 80px; color: #999; font-size: 10px; }
384
+ .slider-input {
385
+ flex: 1; -webkit-appearance: none; appearance: none;
386
+ height: 4px; background: #3a3a5e; border-radius: 2px; outline: none;
387
+ }
388
+ .slider-input::-webkit-slider-thumb {
389
+ -webkit-appearance: none; width: 12px; height: 12px;
390
+ background: #64ffb4; border-radius: 50%; cursor: pointer;
391
+ }
392
+ .slider-value { width: 40px; text-align: right; color: #64ffb4; font-size: 10px; font-family: monospace; }
393
+ .action-buttons {
394
+ display: flex; gap: 6px; margin-top: 12px;
395
+ }
396
+ .btn {
397
+ flex: 1; padding: 8px 0; text-align: center;
398
+ border: 1px solid #64ffb4; border-radius: 4px;
399
+ color: #64ffb4; background: transparent;
400
+ cursor: pointer; font-size: 10px; text-transform: uppercase;
401
+ letter-spacing: 0.5px; transition: all 0.2s;
402
+ }
403
+ .btn:hover { background: #64ffb420; }
404
+ .btn.primary { background: #64ffb4; color: #1e1e2e; font-weight: 600; }
405
+ .btn.primary:hover { background: #7affcc; }
406
+ .size-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
407
+ .size-input {
408
+ width: 60px; padding: 4px 6px; background: #2a2a3e; border: 1px solid #3a3a5e;
409
+ border-radius: 3px; color: #e0e0e0; font-size: 10px; text-align: center;
410
+ }
411
+ .status { color: #888; font-size: 10px; text-align: center; margin-top: 8px; min-height: 14px; }
412
+ .core-label {
413
+ grid-column: span 8; font-size: 9px; color: #666;
414
+ text-transform: uppercase; letter-spacing: 0.5px; padding: 2px 0;
415
+ }
416
+ </style>
417
+ </head>
418
+ <body>
419
+ <h2>VIB3+ 4D Visualizer</h2>
420
+
421
+ <!-- Preview -->
422
+ <div class="preview-container">
423
+ <canvas id="preview-canvas" width="${previewSize * 2}" height="${previewSize * 2}"></canvas>
424
+ </div>
425
+
426
+ <!-- System Selection -->
427
+ <div class="section">
428
+ <div class="system-tabs">
429
+ <div class="system-tab active" data-system="quantum">Quantum</div>
430
+ <div class="system-tab" data-system="faceted">Faceted</div>
431
+ <div class="system-tab" data-system="holographic">Holographic</div>
432
+ </div>
433
+ </div>
434
+
435
+ <!-- Geometry Selection -->
436
+ <div class="section">
437
+ <h3>Geometry</h3>
438
+ <div class="geometry-grid" id="geometry-grid"></div>
439
+ </div>
440
+
441
+ <!-- Parameters -->
442
+ <div class="section">
443
+ <h3>Parameters</h3>
444
+ <div id="param-sliders"></div>
445
+ </div>
446
+
447
+ <!-- 6D Rotation -->
448
+ <div class="section">
449
+ <h3>6D Rotation</h3>
450
+ <div id="rotation-sliders"></div>
451
+ </div>
452
+
453
+ <!-- Render Size -->
454
+ <div class="section">
455
+ <h3>Output Size</h3>
456
+ <div class="size-row">
457
+ <span class="slider-label">Width</span>
458
+ <input class="size-input" id="render-width" type="number" value="1024" min="64" max="4096" step="64">
459
+ <span style="color:#666">x</span>
460
+ <input class="size-input" id="render-height" type="number" value="1024" min="64" max="4096" step="64">
461
+ <span class="slider-label">Height</span>
462
+ </div>
463
+ </div>
464
+
465
+ <!-- Action Buttons -->
466
+ <div class="action-buttons">
467
+ <button class="btn primary" id="btn-create">Create New</button>
468
+ <button class="btn" id="btn-apply">Apply Fill</button>
469
+ <button class="btn" id="btn-batch">Batch (24)</button>
470
+ </div>
471
+ <div class="status" id="status"></div>
472
+
473
+ <script>
474
+ (function() {
475
+ // ---- State ----
476
+ const state = {
477
+ system: 'quantum',
478
+ geometry: 0,
479
+ hue: 200, saturation: 0.8, intensity: 0.5, speed: 1.0,
480
+ gridDensity: 15, morphFactor: 1.0, chaos: 0.2, dimension: 3.5,
481
+ rot4dXY: 0, rot4dXZ: 0, rot4dYZ: 0, rot4dXW: 0, rot4dYW: 0, rot4dZW: 0
482
+ };
483
+
484
+ // ---- Parameter Definitions ----
485
+ const paramDefs = [
486
+ { key: 'hue', label: 'Hue', min: 0, max: 360, step: 1, format: (v) => v + '\\u00b0' },
487
+ { key: 'saturation', label: 'Saturation', min: 0, max: 1, step: 0.01, format: (v) => (v * 100).toFixed(0) + '%' },
488
+ { key: 'intensity', label: 'Intensity', min: 0, max: 1, step: 0.01, format: (v) => (v * 100).toFixed(0) + '%' },
489
+ { key: 'speed', label: 'Speed', min: 0.1, max: 3, step: 0.1, format: (v) => v.toFixed(1) + 'x' },
490
+ { key: 'gridDensity', label: 'Density', min: 4, max: 100, step: 1, format: (v) => v.toFixed(0) },
491
+ { key: 'morphFactor', label: 'Morph', min: 0, max: 2, step: 0.01, format: (v) => v.toFixed(2) },
492
+ { key: 'chaos', label: 'Chaos', min: 0, max: 1, step: 0.01, format: (v) => (v * 100).toFixed(0) + '%' },
493
+ { key: 'dimension', label: 'Dimension', min: 3.0, max: 4.5, step: 0.01, format: (v) => v.toFixed(2) + 'D' }
494
+ ];
495
+
496
+ const rotDefs = [
497
+ { key: 'rot4dXY', label: 'XY Plane', min: -6.28, max: 6.28, step: 0.01 },
498
+ { key: 'rot4dXZ', label: 'XZ Plane', min: -6.28, max: 6.28, step: 0.01 },
499
+ { key: 'rot4dYZ', label: 'YZ Plane', min: -6.28, max: 6.28, step: 0.01 },
500
+ { key: 'rot4dXW', label: 'XW Hyper', min: -2, max: 2, step: 0.01 },
501
+ { key: 'rot4dYW', label: 'YW Hyper', min: -2, max: 2, step: 0.01 },
502
+ { key: 'rot4dZW', label: 'ZW Hyper', min: -2, max: 2, step: 0.01 }
503
+ ];
504
+
505
+ const baseNames = ['Tet', 'Cube', 'Sph', 'Tor', 'Kln', 'Frc', 'Wav', 'Cry'];
506
+ const coreLabels = ['Base', 'Hypersphere Core', 'Hypertetrahedron Core'];
507
+
508
+ // ---- Build UI ----
509
+ function buildGeometryGrid() {
510
+ const grid = document.getElementById('geometry-grid');
511
+ grid.innerHTML = '';
512
+ for (let core = 0; core < 3; core++) {
513
+ const label = document.createElement('div');
514
+ label.className = 'core-label';
515
+ label.textContent = coreLabels[core];
516
+ grid.appendChild(label);
517
+ for (let base = 0; base < 8; base++) {
518
+ const idx = core * 8 + base;
519
+ const btn = document.createElement('div');
520
+ btn.className = 'geom-btn' + (idx === state.geometry ? ' active' : '');
521
+ btn.textContent = baseNames[base];
522
+ btn.title = coreLabels[core] + ' - ' + baseNames[base] + ' (' + idx + ')';
523
+ btn.addEventListener('click', () => {
524
+ state.geometry = idx;
525
+ document.querySelectorAll('.geom-btn').forEach((b, i) => {
526
+ b.classList.toggle('active', i === idx);
527
+ });
528
+ });
529
+ grid.appendChild(btn);
530
+ }
531
+ }
532
+ }
533
+
534
+ function buildSliders(container, defs) {
535
+ const el = document.getElementById(container);
536
+ el.innerHTML = '';
537
+ defs.forEach(def => {
538
+ const row = document.createElement('div');
539
+ row.className = 'slider-row';
540
+ const fmt = def.format || ((v) => v.toFixed(2));
541
+ row.innerHTML =
542
+ '<span class="slider-label">' + def.label + '</span>' +
543
+ '<input class="slider-input" type="range" min="' + def.min + '" max="' + def.max +
544
+ '" step="' + def.step + '" value="' + state[def.key] + '" data-key="' + def.key + '">' +
545
+ '<span class="slider-value" id="val-' + def.key + '">' + fmt(state[def.key]) + '</span>';
546
+ const slider = row.querySelector('input');
547
+ slider.addEventListener('input', () => {
548
+ const v = parseFloat(slider.value);
549
+ state[def.key] = v;
550
+ document.getElementById('val-' + def.key).textContent = fmt(v);
551
+ });
552
+ el.appendChild(row);
553
+ });
554
+ }
555
+
556
+ buildGeometryGrid();
557
+ buildSliders('param-sliders', paramDefs);
558
+ buildSliders('rotation-sliders', rotDefs);
559
+
560
+ // ---- System Tabs ----
561
+ document.querySelectorAll('.system-tab').forEach(tab => {
562
+ tab.addEventListener('click', () => {
563
+ state.system = tab.dataset.system;
564
+ document.querySelectorAll('.system-tab').forEach(t => t.classList.remove('active'));
565
+ tab.classList.add('active');
566
+ });
567
+ });
568
+
569
+ // ---- Render to Canvas ----
570
+ function renderToCanvas(canvas, width, height) {
571
+ canvas.width = width;
572
+ canvas.height = height;
573
+
574
+ const gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
575
+ if (!gl) {
576
+ setStatus('WebGL not available');
577
+ return null;
578
+ }
579
+
580
+ // Minimal full-screen quad vertex shader
581
+ const vsSource = 'attribute vec2 a_position;void main(){gl_Position=vec4(a_position,0.0,1.0);}';
582
+
583
+ // Faceted-style fragment shader (works for all systems as preview)
584
+ const fsSource = \`precision highp float;
585
+ uniform float u_time;uniform vec2 u_resolution;uniform float u_geometry;
586
+ uniform float u_rot4dXY;uniform float u_rot4dXZ;uniform float u_rot4dYZ;
587
+ uniform float u_rot4dXW;uniform float u_rot4dYW;uniform float u_rot4dZW;
588
+ uniform float u_dimension;uniform float u_gridDensity;uniform float u_morphFactor;
589
+ uniform float u_chaos;uniform float u_hue;uniform float u_intensity;
590
+ uniform float u_saturation;uniform float u_speed;
591
+
592
+ mat4 rotXY(float a){float c=cos(a),s=sin(a);return mat4(c,-s,0,0,s,c,0,0,0,0,1,0,0,0,0,1);}
593
+ mat4 rotXW(float a){float c=cos(a),s=sin(a);return mat4(c,0,0,-s,0,1,0,0,0,0,1,0,s,0,0,c);}
594
+ mat4 rotYW(float a){float c=cos(a),s=sin(a);return mat4(1,0,0,0,0,c,0,-s,0,0,1,0,0,s,0,c);}
595
+ mat4 rotZW(float a){float c=cos(a),s=sin(a);return mat4(1,0,0,0,0,1,0,0,0,0,c,-s,0,0,s,c);}
596
+
597
+ vec3 hsl2rgb(float h,float s,float l){
598
+ float c=(1.0-abs(2.0*l-1.0))*s;float hp=h*6.0;
599
+ float x=c*(1.0-abs(mod(hp,2.0)-1.0));float m=l-c*0.5;vec3 rgb;
600
+ if(hp<1.0)rgb=vec3(c,x,0);else if(hp<2.0)rgb=vec3(x,c,0);
601
+ else if(hp<3.0)rgb=vec3(0,c,x);else if(hp<4.0)rgb=vec3(0,x,c);
602
+ else if(hp<5.0)rgb=vec3(x,0,c);else rgb=vec3(c,0,x);
603
+ return rgb+m;
604
+ }
605
+
606
+ void main(){
607
+ vec2 uv=(gl_FragCoord.xy-u_resolution*0.5)/min(u_resolution.x,u_resolution.y);
608
+ float t=u_time*0.001*u_speed;
609
+ vec4 p=vec4(uv*3.0,sin(t),cos(t*0.7));
610
+ p=rotXY(u_rot4dXY)*p;p=rotXW(u_rot4dXW)*p;p=rotYW(u_rot4dYW)*p;p=rotZW(u_rot4dZW)*p;
611
+ float w=1.0/(u_dimension-p.w);vec3 proj=p.xyz*w;
612
+ float gs=u_gridDensity*0.1;vec3 g=fract(proj*gs);
613
+ float base=mod(u_geometry,8.0);float core=floor(u_geometry/8.0);
614
+ float pattern=0.0;
615
+ if(base<1.0){vec3 q=g-0.5;pattern=1.0-smoothstep(0.0,0.15,length(q));}
616
+ else if(base<2.0){vec3 e=min(g,1.0-g);pattern=1.0-smoothstep(0.0,0.04,min(min(e.x,e.y),e.z));}
617
+ else if(base<3.0){pattern=1.0-smoothstep(0.1,0.3,length(g-0.5));}
618
+ else if(base<4.0){float r=length(g.xy-0.5);pattern=1.0-smoothstep(0.0,0.05,abs(r-0.3));}
619
+ else if(base<5.0){float u2=atan(g.y-0.5,g.x-0.5);pattern=sin(u2*4.0+t)*0.5+0.5;}
620
+ else if(base<6.0){vec3 c2=abs(g*2.0-1.0);float d=length(max(c2-0.3,0.0));for(int i=0;i<3;i++){c2=abs(c2*2.0-1.0);d=min(d,length(max(c2-0.3,0.0))/pow(2.0,float(i+1)));}pattern=1.0-smoothstep(0.0,0.05,d);}
621
+ else if(base<7.0){pattern=(sin(proj.x*gs*2.0+t*2.0)+sin(proj.y*gs*1.8+t*1.5))*0.25+0.5;}
622
+ else{vec3 c2=g-0.5;float oct=max(max(abs(c2.x)+abs(c2.y),abs(c2.y)+abs(c2.z)),abs(c2.x)+abs(c2.z));pattern=1.0-smoothstep(0.3,0.4,oct);}
623
+
624
+ if(core>0.5){pattern*=(1.0+sin(length(proj)*3.0+t)*0.3);}
625
+ pattern*=u_morphFactor;
626
+ pattern+=sin(proj.x*7.0)*cos(proj.y*11.0)*u_chaos*0.3;
627
+ float pi2=pow(clamp(pattern,0.0,1.0),1.5)*u_intensity;
628
+ vec3 col=hsl2rgb(u_hue,u_saturation,0.3+pi2*0.5);
629
+ gl_FragColor=vec4(col*pi2*2.0,1.0);
630
+ }\`;
631
+
632
+ // Compile shaders
633
+ function createShader(type, source) {
634
+ const shader = gl.createShader(type);
635
+ gl.shaderSource(shader, source);
636
+ gl.compileShader(shader);
637
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
638
+ console.error('Shader error:', gl.getShaderInfoLog(shader));
639
+ gl.deleteShader(shader);
640
+ return null;
641
+ }
642
+ return shader;
643
+ }
644
+
645
+ const vs = createShader(gl.VERTEX_SHADER, vsSource);
646
+ const fs = createShader(gl.FRAGMENT_SHADER, fsSource);
647
+ if (!vs || !fs) return null;
648
+
649
+ const prog = gl.createProgram();
650
+ gl.attachShader(prog, vs);
651
+ gl.attachShader(prog, fs);
652
+ gl.linkProgram(prog);
653
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
654
+ console.error('Program link error');
655
+ return null;
656
+ }
657
+
658
+ // Fullscreen quad
659
+ const buf = gl.createBuffer();
660
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
661
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1,1,-1,-1,1,1,1]), gl.STATIC_DRAW);
662
+ const posLoc = gl.getAttribLocation(prog, 'a_position');
663
+ gl.enableVertexAttribArray(posLoc);
664
+ gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
665
+
666
+ gl.useProgram(prog);
667
+
668
+ // Set uniforms
669
+ gl.uniform2f(gl.getUniformLocation(prog, 'u_resolution'), width, height);
670
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_time'), performance.now());
671
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_geometry'), state.geometry);
672
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_gridDensity'), state.gridDensity);
673
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_morphFactor'), state.morphFactor);
674
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_chaos'), state.chaos);
675
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_speed'), state.speed);
676
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_hue'), state.hue / 360.0);
677
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_intensity'), state.intensity);
678
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_saturation'), state.saturation);
679
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_dimension'), state.dimension);
680
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dXY'), state.rot4dXY);
681
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dXZ'), state.rot4dXZ);
682
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dYZ'), state.rot4dYZ);
683
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dXW'), state.rot4dXW);
684
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dYW'), state.rot4dYW);
685
+ gl.uniform1f(gl.getUniformLocation(prog, 'u_rot4dZW'), state.rot4dZW);
686
+
687
+ gl.viewport(0, 0, width, height);
688
+ gl.clearColor(0, 0, 0, 1);
689
+ gl.clear(gl.COLOR_BUFFER_BIT);
690
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
691
+
692
+ // Read pixels
693
+ const pixels = new Uint8Array(width * height * 4);
694
+ gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
695
+
696
+ // Cleanup
697
+ gl.deleteProgram(prog);
698
+ gl.deleteShader(vs);
699
+ gl.deleteShader(fs);
700
+ gl.deleteBuffer(buf);
701
+
702
+ return pixels;
703
+ }
704
+
705
+ // ---- Preview Loop ----
706
+ const previewCanvas = document.getElementById('preview-canvas');
707
+ let animFrame;
708
+ function previewLoop() {
709
+ renderToCanvas(previewCanvas, previewCanvas.width, previewCanvas.height);
710
+ animFrame = requestAnimationFrame(previewLoop);
711
+ }
712
+ previewLoop();
713
+
714
+ // ---- Status ----
715
+ function setStatus(msg) {
716
+ document.getElementById('status').textContent = msg;
717
+ }
718
+
719
+ // ---- Actions ----
720
+ document.getElementById('btn-create').addEventListener('click', () => {
721
+ setStatus('Rendering...');
722
+ const w = parseInt(document.getElementById('render-width').value) || 1024;
723
+ const h = parseInt(document.getElementById('render-height').value) || 1024;
724
+ const offscreen = document.createElement('canvas');
725
+ const pixels = renderToCanvas(offscreen, w, h);
726
+ if (pixels) {
727
+ parent.postMessage({
728
+ pluginMessage: {
729
+ type: 'render-complete',
730
+ imageData: Array.from(pixels),
731
+ width: w, height: h,
732
+ name: 'VIB3+ ' + state.system + ' G' + state.geometry
733
+ }
734
+ }, '*');
735
+ setStatus('Sent to Figma!');
736
+ } else {
737
+ setStatus('Render failed.');
738
+ }
739
+ });
740
+
741
+ document.getElementById('btn-apply').addEventListener('click', () => {
742
+ setStatus('Rendering for fill...');
743
+ const w = parseInt(document.getElementById('render-width').value) || 1024;
744
+ const h = parseInt(document.getElementById('render-height').value) || 1024;
745
+ const offscreen = document.createElement('canvas');
746
+ const pixels = renderToCanvas(offscreen, w, h);
747
+ if (pixels) {
748
+ parent.postMessage({
749
+ pluginMessage: {
750
+ type: 'apply-fill',
751
+ imageData: Array.from(pixels),
752
+ width: w, height: h
753
+ }
754
+ }, '*');
755
+ setStatus('Fill applied!');
756
+ }
757
+ });
758
+
759
+ document.getElementById('btn-batch').addEventListener('click', () => {
760
+ setStatus('Batch rendering 24 variants...');
761
+ const size = 512;
762
+ const offscreen = document.createElement('canvas');
763
+ const renders = [];
764
+ const baseNames = ['Tetrahedron','Hypercube','Sphere','Torus','Klein','Fractal','Wave','Crystal'];
765
+ const coreNames = ['Base','Hyper-S','Hyper-T'];
766
+ const origGeom = state.geometry;
767
+
768
+ for (let i = 0; i < 24; i++) {
769
+ state.geometry = i;
770
+ const pixels = renderToCanvas(offscreen, size, size);
771
+ if (pixels) {
772
+ renders.push({
773
+ imageData: Array.from(pixels),
774
+ name: coreNames[Math.floor(i / 8)] + ' ' + baseNames[i % 8]
775
+ });
776
+ }
777
+ }
778
+ state.geometry = origGeom;
779
+
780
+ if (renders.length > 0) {
781
+ parent.postMessage({
782
+ pluginMessage: {
783
+ type: 'batch-render',
784
+ renders: renders,
785
+ columns: 8,
786
+ cellSize: size,
787
+ name: 'VIB3+ ' + state.system + ' All Geometries'
788
+ }
789
+ }, '*');
790
+ setStatus('Batch sent! ' + renders.length + ' variants.');
791
+ }
792
+ });
793
+
794
+ // ---- Messages from plugin code ----
795
+ window.onmessage = (event) => {
796
+ const msg = event.data.pluginMessage;
797
+ if (!msg) return;
798
+ if (msg.type === 'error') setStatus('Error: ' + msg.message);
799
+ if (msg.type === 'render-inserted') setStatus('Visualization created!');
800
+ if (msg.type === 'batch-complete') setStatus('Batch complete: ' + msg.count + ' items.');
801
+ if (msg.type === 'fill-applied') setStatus('Fill applied to ' + msg.count + ' shape(s).');
802
+ };
803
+ })();
804
+ <\/script>
805
+ </body>
806
+ </html>`;
807
+ }
808
+
809
+ /**
810
+ * Generates a complete Figma plugin package ready for deployment.
811
+ *
812
+ * @param {object} [options={}] - Package customization options
813
+ * @returns {Object.<string, string|object>} Map of file path to file content
814
+ * @example
815
+ * const pkg = Vib3FigmaPlugin.generatePackage();
816
+ * // Write manifest.json, code.js, and ui.html to your plugin directory
817
+ */
818
+ static generatePackage(options = {}) {
819
+ return {
820
+ 'manifest.json': Vib3FigmaPlugin.generatePluginManifest(options),
821
+ 'code.js': Vib3FigmaPlugin.generatePluginCode(options),
822
+ 'ui.html': Vib3FigmaPlugin.generatePluginUI(options)
823
+ };
824
+ }
825
+
826
+ /**
827
+ * Returns the default render configuration.
828
+ * @returns {Object} Default width, height, pixelRatio, format
829
+ */
830
+ static getDefaultRenderConfig() {
831
+ return { ...DEFAULT_RENDER_CONFIG };
832
+ }
833
+
834
+ /**
835
+ * Returns available system names.
836
+ * @returns {string[]} Array of system names
837
+ */
838
+ static getAvailableSystems() {
839
+ return [...AVAILABLE_SYSTEMS];
840
+ }
841
+
842
+ /**
843
+ * Returns geometry name for a given index.
844
+ * @param {number} index - Geometry index (0-23)
845
+ * @returns {string} Human-readable geometry name
846
+ */
847
+ static getGeometryName(index) {
848
+ const coreIndex = Math.floor(index / 8);
849
+ const baseIndex = index % 8;
850
+ const coreName = CORE_TYPES[coreIndex] || 'Unknown';
851
+ const baseName = BASE_GEOMETRIES[baseIndex] || 'Unknown';
852
+ return coreIndex === 0 ? baseName : `${coreName} (${baseName})`;
853
+ }
854
+ }