@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.
- package/CHANGELOG.md +118 -0
- package/DOCS/BLUEPRINT_EXECUTION_PLAN_2026-01-07.md +34 -0
- package/DOCS/CI_TESTING.md +38 -0
- package/DOCS/CLI_ONBOARDING.md +75 -0
- package/DOCS/CONTROL_REFERENCE.md +64 -0
- package/DOCS/DEV_TRACK_ANALYSIS.md +77 -0
- package/DOCS/DEV_TRACK_PLAN_2026-01-07.md +42 -0
- package/DOCS/DEV_TRACK_SESSION_2026-01-31.md +220 -0
- package/DOCS/ENV_SETUP.md +189 -0
- package/DOCS/EXPORT_FORMATS.md +417 -0
- package/DOCS/GPU_DISPOSAL_GUIDE.md +21 -0
- package/DOCS/LICENSING_TIERS.md +275 -0
- package/DOCS/MASTER_PLAN_2026-01-31.md +570 -0
- package/DOCS/OBS_SETUP_GUIDE.md +98 -0
- package/DOCS/PROJECT_SETUP.md +66 -0
- package/DOCS/RENDERER_LIFECYCLE.md +40 -0
- package/DOCS/REPO_MANIFEST.md +121 -0
- package/DOCS/SESSION_014_PLAN.md +195 -0
- package/DOCS/SESSION_LOG_2026-01-07.md +56 -0
- package/DOCS/STRATEGIC_BLUEPRINT_2026-01-07.md +72 -0
- package/DOCS/SYSTEM_AUDIT_2026-01-30.md +738 -0
- package/DOCS/SYSTEM_INVENTORY.md +520 -0
- package/DOCS/TELEMETRY_EXPORTS.md +34 -0
- package/DOCS/WEBGPU_STATUS.md +38 -0
- package/DOCS/XR_BENCHMARKS.md +608 -0
- package/LICENSE +21 -0
- package/README.md +426 -0
- package/docs/.nojekyll +0 -0
- package/docs/01-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/02-hyperspatial_ego_death.html +346 -0
- package/docs/03-post_cartesian_sublime.html +346 -0
- package/docs/04-crystalline_void_meditation.html +346 -0
- package/docs/05-quantum_decoherence_ballet.html +346 -0
- package/docs/06-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/07-hyperspatial_ego_death.html +346 -0
- package/docs/08-post_cartesian_sublime.html +346 -0
- package/docs/09-crystalline_void_meditation.html +346 -0
- package/docs/10-quantum_decoherence_ballet.html +346 -0
- package/docs/11-dissolution_of_euclidean_hegemony.html +346 -0
- package/docs/12-hyperspatial_ego_death.html +346 -0
- package/docs/13-post_cartesian_sublime.html +346 -0
- package/docs/index.html +794 -0
- package/docs/test-hub.html +441 -0
- package/docs/url-state.js +102 -0
- package/docs/vib3-exports/01-quantum-quantum-tetrahedron-lattice.html +489 -0
- package/docs/vib3-exports/02-quantum-quantum-hypersphere-matrix.html +489 -0
- package/docs/vib3-exports/03-quantum-quantum-hypertetra-fractal.html +489 -0
- package/docs/vib3-exports/04-faceted-faceted-crystal-structure.html +407 -0
- package/docs/vib3-exports/05-faceted-faceted-klein-bottle.html +407 -0
- package/docs/vib3-exports/06-faceted-faceted-hypertetra-torus.html +407 -0
- package/docs/vib3-exports/07-holographic-holographic-wave-field.html +457 -0
- package/docs/vib3-exports/08-holographic-holographic-hypersphere-sphere.html +457 -0
- package/docs/vib3-exports/09-holographic-holographic-hypertetra-crystal.html +457 -0
- package/docs/vib3-exports/index.html +238 -0
- package/docs/webgpu-live.html +702 -0
- package/package.json +367 -0
- package/src/advanced/AIPresetGenerator.js +777 -0
- package/src/advanced/MIDIController.js +703 -0
- package/src/advanced/OffscreenWorker.js +1051 -0
- package/src/advanced/WebGPUCompute.js +1051 -0
- package/src/advanced/WebXRRenderer.js +680 -0
- package/src/agent/cli/AgentCLI.js +615 -0
- package/src/agent/cli/index.js +14 -0
- package/src/agent/index.js +73 -0
- package/src/agent/mcp/MCPServer.js +950 -0
- package/src/agent/mcp/index.js +9 -0
- package/src/agent/mcp/tools.js +548 -0
- package/src/agent/telemetry/EventStream.js +669 -0
- package/src/agent/telemetry/Instrumentation.js +618 -0
- package/src/agent/telemetry/TelemetryExporters.js +427 -0
- package/src/agent/telemetry/TelemetryService.js +464 -0
- package/src/agent/telemetry/index.js +52 -0
- package/src/benchmarks/BenchmarkRunner.js +381 -0
- package/src/benchmarks/MetricsCollector.js +299 -0
- package/src/benchmarks/index.js +9 -0
- package/src/benchmarks/scenes.js +259 -0
- package/src/cli/index.js +675 -0
- package/src/config/ApiConfig.js +88 -0
- package/src/core/CanvasManager.js +217 -0
- package/src/core/ErrorReporter.js +117 -0
- package/src/core/ParameterMapper.js +333 -0
- package/src/core/Parameters.js +396 -0
- package/src/core/RendererContracts.js +200 -0
- package/src/core/UnifiedResourceManager.js +370 -0
- package/src/core/VIB3Engine.js +636 -0
- package/src/core/renderers/FacetedRendererAdapter.js +32 -0
- package/src/core/renderers/HolographicRendererAdapter.js +29 -0
- package/src/core/renderers/QuantumRendererAdapter.js +29 -0
- package/src/core/renderers/RendererLifecycleManager.js +63 -0
- package/src/creative/ColorPresetsSystem.js +980 -0
- package/src/creative/ParameterTimeline.js +1061 -0
- package/src/creative/PostProcessingPipeline.js +1113 -0
- package/src/creative/TransitionAnimator.js +683 -0
- package/src/export/CSSExporter.js +226 -0
- package/src/export/CardGeneratorBase.js +279 -0
- package/src/export/ExportManager.js +580 -0
- package/src/export/FacetedCardGenerator.js +279 -0
- package/src/export/HolographicCardGenerator.js +543 -0
- package/src/export/LottieExporter.js +552 -0
- package/src/export/QuantumCardGenerator.js +315 -0
- package/src/export/SVGExporter.js +519 -0
- package/src/export/ShaderExporter.js +903 -0
- package/src/export/TradingCardGenerator.js +3055 -0
- package/src/export/TradingCardManager.js +181 -0
- package/src/export/VIB3PackageExporter.js +559 -0
- package/src/export/index.js +14 -0
- package/src/export/systems/TradingCardSystemFaceted.js +494 -0
- package/src/export/systems/TradingCardSystemHolographic.js +452 -0
- package/src/export/systems/TradingCardSystemQuantum.js +411 -0
- package/src/faceted/FacetedSystem.js +963 -0
- package/src/features/CollectionManager.js +433 -0
- package/src/gallery/CollectionManager.js +240 -0
- package/src/gallery/GallerySystem.js +485 -0
- package/src/geometry/GeometryFactory.js +314 -0
- package/src/geometry/GeometryLibrary.js +72 -0
- package/src/geometry/buffers/BufferBuilder.js +338 -0
- package/src/geometry/buffers/index.js +18 -0
- package/src/geometry/generators/Crystal.js +420 -0
- package/src/geometry/generators/Fractal.js +298 -0
- package/src/geometry/generators/KleinBottle.js +197 -0
- package/src/geometry/generators/Sphere.js +192 -0
- package/src/geometry/generators/Tesseract.js +160 -0
- package/src/geometry/generators/Tetrahedron.js +225 -0
- package/src/geometry/generators/Torus.js +304 -0
- package/src/geometry/generators/Wave.js +341 -0
- package/src/geometry/index.js +142 -0
- package/src/geometry/warp/HypersphereCore.js +211 -0
- package/src/geometry/warp/HypertetraCore.js +386 -0
- package/src/geometry/warp/index.js +57 -0
- package/src/holograms/HolographicVisualizer.js +1073 -0
- package/src/holograms/RealHolographicSystem.js +966 -0
- package/src/holograms/variantRegistry.js +69 -0
- package/src/integrations/FigmaPlugin.js +854 -0
- package/src/integrations/OBSMode.js +754 -0
- package/src/integrations/ThreeJsPackage.js +660 -0
- package/src/integrations/TouchDesignerExport.js +552 -0
- package/src/integrations/frameworks/Vib3React.js +591 -0
- package/src/integrations/frameworks/Vib3Svelte.js +654 -0
- package/src/integrations/frameworks/Vib3Vue.js +628 -0
- package/src/llm/LLMParameterInterface.js +240 -0
- package/src/llm/LLMParameterUI.js +577 -0
- package/src/math/Mat4x4.js +708 -0
- package/src/math/Projection.js +341 -0
- package/src/math/Rotor4D.js +637 -0
- package/src/math/Vec4.js +476 -0
- package/src/math/constants.js +164 -0
- package/src/math/index.js +68 -0
- package/src/math/projections.js +54 -0
- package/src/math/rotations.js +196 -0
- package/src/quantum/QuantumEngine.js +906 -0
- package/src/quantum/QuantumVisualizer.js +1103 -0
- package/src/reactivity/ReactivityConfig.js +499 -0
- package/src/reactivity/ReactivityManager.js +586 -0
- package/src/reactivity/SpatialInputSystem.js +1783 -0
- package/src/reactivity/index.js +93 -0
- package/src/render/CommandBuffer.js +465 -0
- package/src/render/MultiCanvasBridge.js +340 -0
- package/src/render/RenderCommand.js +514 -0
- package/src/render/RenderResourceRegistry.js +523 -0
- package/src/render/RenderState.js +552 -0
- package/src/render/RenderTarget.js +512 -0
- package/src/render/ShaderLoader.js +253 -0
- package/src/render/ShaderProgram.js +599 -0
- package/src/render/UnifiedRenderBridge.js +496 -0
- package/src/render/backends/WebGLBackend.js +1108 -0
- package/src/render/backends/WebGPUBackend.js +1409 -0
- package/src/render/commands/CommandBufferExecutor.js +607 -0
- package/src/render/commands/RenderCommandBuffer.js +661 -0
- package/src/render/commands/index.js +17 -0
- package/src/render/index.js +367 -0
- package/src/scene/Disposable.js +498 -0
- package/src/scene/MemoryPool.js +618 -0
- package/src/scene/Node4D.js +697 -0
- package/src/scene/ResourceManager.js +599 -0
- package/src/scene/Scene4D.js +540 -0
- package/src/scene/index.js +98 -0
- package/src/schemas/error.schema.json +84 -0
- package/src/schemas/extension.schema.json +88 -0
- package/src/schemas/index.js +214 -0
- package/src/schemas/parameters.schema.json +142 -0
- package/src/schemas/tool-pack.schema.json +44 -0
- package/src/schemas/tool-response.schema.json +127 -0
- package/src/shaders/common/fullscreen.vert.glsl +5 -0
- package/src/shaders/common/fullscreen.vert.wgsl +17 -0
- package/src/shaders/common/geometry24.glsl +65 -0
- package/src/shaders/common/geometry24.wgsl +54 -0
- package/src/shaders/common/rotation4d.glsl +85 -0
- package/src/shaders/common/rotation4d.wgsl +86 -0
- package/src/shaders/common/uniforms.glsl +44 -0
- package/src/shaders/common/uniforms.wgsl +48 -0
- package/src/shaders/faceted/faceted.frag.glsl +129 -0
- package/src/shaders/faceted/faceted.frag.wgsl +164 -0
- package/src/shaders/holographic/holographic.frag.glsl +406 -0
- package/src/shaders/holographic/holographic.frag.wgsl +185 -0
- package/src/shaders/quantum/quantum.frag.glsl +513 -0
- package/src/shaders/quantum/quantum.frag.wgsl +361 -0
- package/src/testing/ParallelTestFramework.js +519 -0
- package/src/testing/__snapshots__/exportFormats.test.js.snap +24 -0
- package/src/testing/exportFormats.test.js +8 -0
- package/src/testing/projections.test.js +14 -0
- package/src/testing/rotations.test.js +37 -0
- package/src/ui/InteractivityMenu.js +516 -0
- package/src/ui/StatusManager.js +96 -0
- package/src/ui/adaptive/renderers/webgpu/BufferLayout.ts +252 -0
- package/src/ui/adaptive/renderers/webgpu/PolytopeInstanceBuffer.ts +144 -0
- package/src/ui/adaptive/renderers/webgpu/TripleBufferedUniform.ts +170 -0
- package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +735 -0
- package/src/ui/adaptive/renderers/webgpu/index.ts +112 -0
- package/src/variations/VariationManager.js +431 -0
- package/src/viewer/AudioReactivity.js +505 -0
- package/src/viewer/CardBending.js +481 -0
- package/src/viewer/GalleryUI.js +832 -0
- package/src/viewer/ReactivityManager.js +590 -0
- package/src/viewer/TradingCardExporter.js +600 -0
- package/src/viewer/ViewerPortal.js +374 -0
- package/src/viewer/index.js +12 -0
- package/src/wasm/WasmLoader.js +296 -0
- package/src/wasm/index.js +132 -0
- package/tools/agentic/mcpTools.js +88 -0
- package/tools/cli/agent-cli.js +92 -0
- package/tools/export/formats.js +24 -0
- package/tools/math/rotation-baseline.mjs +64 -0
- package/tools/shader-sync-verify.js +937 -0
- package/tools/telemetry/manifestPipeline.js +141 -0
- package/tools/telemetry/telemetryEvents.js +35 -0
- package/types/adaptive-sdk.d.ts +185 -0
- package/types/advanced/AIPresetGenerator.d.ts +81 -0
- package/types/advanced/MIDIController.d.ts +100 -0
- package/types/advanced/OffscreenWorker.d.ts +82 -0
- package/types/advanced/WebGPUCompute.d.ts +52 -0
- package/types/advanced/WebXRRenderer.d.ts +77 -0
- package/types/advanced/index.d.ts +46 -0
- package/types/core/ErrorReporter.d.ts +50 -0
- package/types/core/VIB3Engine.d.ts +204 -0
- package/types/creative/ColorPresetsSystem.d.ts +91 -0
- package/types/creative/ParameterTimeline.d.ts +74 -0
- package/types/creative/PostProcessingPipeline.d.ts +109 -0
- package/types/creative/TransitionAnimator.d.ts +71 -0
- package/types/creative/index.d.ts +35 -0
- package/types/integrations/FigmaPlugin.d.ts +46 -0
- package/types/integrations/OBSMode.d.ts +74 -0
- package/types/integrations/ThreeJsPackage.d.ts +62 -0
- package/types/integrations/TouchDesignerExport.d.ts +36 -0
- package/types/integrations/Vib3React.d.ts +74 -0
- package/types/integrations/Vib3Svelte.d.ts +63 -0
- package/types/integrations/Vib3Vue.d.ts +55 -0
- package/types/integrations/index.d.ts +52 -0
- package/types/reactivity/SpatialInputSystem.d.ts +173 -0
- package/types/reactivity/index.d.ts +394 -0
- package/types/render/CommandBuffer.d.ts +169 -0
- package/types/render/RenderCommand.d.ts +312 -0
- package/types/render/RenderState.d.ts +279 -0
- package/types/render/RenderTarget.d.ts +254 -0
- package/types/render/ShaderProgram.d.ts +277 -0
- package/types/render/UnifiedRenderBridge.d.ts +143 -0
- package/types/render/WebGLBackend.d.ts +168 -0
- package/types/render/WebGPUBackend.d.ts +186 -0
- package/types/render/index.d.ts +141 -0
|
@@ -0,0 +1,980 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ColorPresetsSystem.js - VIB3+ Color Presets & Themes System
|
|
3
|
+
*
|
|
4
|
+
* Provides 20+ built-in color presets organized by mood/theme, with support
|
|
5
|
+
* for smooth transitions, custom presets, and serialization. Integrates with
|
|
6
|
+
* the VIB3+ parameter system via the parameterUpdateFn callback pattern.
|
|
7
|
+
*
|
|
8
|
+
* @module creative/ColorPresetsSystem
|
|
9
|
+
* @version 1.0.0
|
|
10
|
+
* @author VIB3+ Creative Tooling - Phase B
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} ColorPresetConfig
|
|
15
|
+
* @property {number} hue - Primary hue value (0-360)
|
|
16
|
+
* @property {number} saturation - Color saturation (0-1)
|
|
17
|
+
* @property {number} intensity - Visual intensity/brightness (0-1)
|
|
18
|
+
* @property {number} [hueShift=0] - Secondary hue offset applied over time (degrees)
|
|
19
|
+
* @property {number} [saturationRange=0] - Range of saturation oscillation (0-1)
|
|
20
|
+
* @property {number} [intensityRange=0] - Range of intensity oscillation (0-1)
|
|
21
|
+
* @property {number} [speed] - Optional speed override (0.1-3)
|
|
22
|
+
* @property {number} [chaos] - Optional chaos override (0-1)
|
|
23
|
+
* @property {number} [morphFactor] - Optional morph factor override (0-2)
|
|
24
|
+
* @property {string} [category] - Category grouping for UI display
|
|
25
|
+
* @property {string} [description] - Human-readable description
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {Object} TransitionState
|
|
30
|
+
* @property {ColorPresetConfig} from - Starting preset configuration
|
|
31
|
+
* @property {ColorPresetConfig} to - Target preset configuration
|
|
32
|
+
* @property {number} startTime - Transition start timestamp (ms)
|
|
33
|
+
* @property {number} duration - Transition duration (ms)
|
|
34
|
+
* @property {number} progress - Current progress (0-1)
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Color presets and themes system for VIB3+ visualization engine.
|
|
39
|
+
*
|
|
40
|
+
* Manages a library of built-in and custom color presets, each defining
|
|
41
|
+
* hue, saturation, intensity and optional secondary color shift parameters.
|
|
42
|
+
* Supports smooth animated transitions between presets.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* const colors = new ColorPresetsSystem((name, value) => {
|
|
46
|
+
* engine.setParameter(name, value);
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* // Apply a preset with smooth transition
|
|
50
|
+
* colors.applyPreset('Cyberpunk Neon');
|
|
51
|
+
*
|
|
52
|
+
* // List all presets
|
|
53
|
+
* const presets = colors.getPresets();
|
|
54
|
+
*
|
|
55
|
+
* // Create and apply a custom preset
|
|
56
|
+
* colors.createCustomPreset('My Theme', { hue: 42, saturation: 0.9, intensity: 0.7 });
|
|
57
|
+
* colors.applyPreset('My Theme');
|
|
58
|
+
*/
|
|
59
|
+
export class ColorPresetsSystem {
|
|
60
|
+
/**
|
|
61
|
+
* Create a new ColorPresetsSystem.
|
|
62
|
+
*
|
|
63
|
+
* @param {Function} parameterUpdateFn - Callback invoked as (paramName, value)
|
|
64
|
+
* whenever a VIB3+ parameter should be updated. This follows the standard
|
|
65
|
+
* VIB3+ parameter callback pattern.
|
|
66
|
+
*/
|
|
67
|
+
constructor(parameterUpdateFn) {
|
|
68
|
+
if (typeof parameterUpdateFn !== 'function') {
|
|
69
|
+
throw new Error('ColorPresetsSystem requires a parameterUpdateFn callback');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** @type {Function} */
|
|
73
|
+
this.updateParameter = parameterUpdateFn;
|
|
74
|
+
|
|
75
|
+
/** @type {Map<string, ColorPresetConfig>} Built-in presets */
|
|
76
|
+
this.presets = new Map();
|
|
77
|
+
|
|
78
|
+
/** @type {Map<string, ColorPresetConfig>} User-created presets */
|
|
79
|
+
this.customPresets = new Map();
|
|
80
|
+
|
|
81
|
+
/** @type {string|null} Name of the currently active preset */
|
|
82
|
+
this.currentPreset = null;
|
|
83
|
+
|
|
84
|
+
/** @type {TransitionState|null} Active transition state */
|
|
85
|
+
this.transitionState = null;
|
|
86
|
+
|
|
87
|
+
/** @type {number|null} requestAnimationFrame ID for transitions */
|
|
88
|
+
this._frameId = null;
|
|
89
|
+
|
|
90
|
+
/** @type {Function|null} Callback invoked when a transition completes */
|
|
91
|
+
this._onTransitionComplete = null;
|
|
92
|
+
|
|
93
|
+
this._initBuiltInPresets();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// -------------------------------------------------------------------------
|
|
97
|
+
// Initialization
|
|
98
|
+
// -------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Initialize all built-in color presets.
|
|
102
|
+
* @private
|
|
103
|
+
*/
|
|
104
|
+
_initBuiltInPresets() {
|
|
105
|
+
const builtIn = [
|
|
106
|
+
{
|
|
107
|
+
name: 'Cyberpunk Neon',
|
|
108
|
+
config: {
|
|
109
|
+
hue: 290,
|
|
110
|
+
saturation: 0.95,
|
|
111
|
+
intensity: 0.85,
|
|
112
|
+
hueShift: 30,
|
|
113
|
+
saturationRange: 0.1,
|
|
114
|
+
intensityRange: 0.15,
|
|
115
|
+
speed: 1.4,
|
|
116
|
+
chaos: 0.35,
|
|
117
|
+
category: 'Futuristic',
|
|
118
|
+
description: 'Hot magenta and electric blue neon glow'
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'Ocean Deep',
|
|
123
|
+
config: {
|
|
124
|
+
hue: 200,
|
|
125
|
+
saturation: 0.75,
|
|
126
|
+
intensity: 0.55,
|
|
127
|
+
hueShift: 15,
|
|
128
|
+
saturationRange: 0.1,
|
|
129
|
+
intensityRange: 0.1,
|
|
130
|
+
speed: 0.6,
|
|
131
|
+
chaos: 0.1,
|
|
132
|
+
category: 'Nature',
|
|
133
|
+
description: 'Deep ocean blues and teals with gentle motion'
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: 'Solar Flare',
|
|
138
|
+
config: {
|
|
139
|
+
hue: 30,
|
|
140
|
+
saturation: 0.9,
|
|
141
|
+
intensity: 0.9,
|
|
142
|
+
hueShift: 25,
|
|
143
|
+
saturationRange: 0.1,
|
|
144
|
+
intensityRange: 0.2,
|
|
145
|
+
speed: 2.0,
|
|
146
|
+
chaos: 0.5,
|
|
147
|
+
category: 'Cosmic',
|
|
148
|
+
description: 'Explosive orange and yellow solar eruption'
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'Midnight Purple',
|
|
153
|
+
config: {
|
|
154
|
+
hue: 270,
|
|
155
|
+
saturation: 0.7,
|
|
156
|
+
intensity: 0.4,
|
|
157
|
+
hueShift: 10,
|
|
158
|
+
saturationRange: 0.05,
|
|
159
|
+
intensityRange: 0.08,
|
|
160
|
+
speed: 0.5,
|
|
161
|
+
chaos: 0.05,
|
|
162
|
+
category: 'Dark',
|
|
163
|
+
description: 'Subdued deep purple with subtle shimmer'
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: 'Arctic Aurora',
|
|
168
|
+
config: {
|
|
169
|
+
hue: 160,
|
|
170
|
+
saturation: 0.8,
|
|
171
|
+
intensity: 0.65,
|
|
172
|
+
hueShift: 50,
|
|
173
|
+
saturationRange: 0.15,
|
|
174
|
+
intensityRange: 0.2,
|
|
175
|
+
speed: 0.8,
|
|
176
|
+
chaos: 0.2,
|
|
177
|
+
category: 'Nature',
|
|
178
|
+
description: 'Northern lights green-to-violet sweeps'
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: 'Volcanic',
|
|
183
|
+
config: {
|
|
184
|
+
hue: 10,
|
|
185
|
+
saturation: 0.85,
|
|
186
|
+
intensity: 0.75,
|
|
187
|
+
hueShift: 15,
|
|
188
|
+
saturationRange: 0.1,
|
|
189
|
+
intensityRange: 0.25,
|
|
190
|
+
speed: 1.2,
|
|
191
|
+
chaos: 0.45,
|
|
192
|
+
category: 'Elemental',
|
|
193
|
+
description: 'Molten reds and oranges with dark undertones'
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: 'Forest Mystic',
|
|
198
|
+
config: {
|
|
199
|
+
hue: 130,
|
|
200
|
+
saturation: 0.6,
|
|
201
|
+
intensity: 0.5,
|
|
202
|
+
hueShift: 20,
|
|
203
|
+
saturationRange: 0.1,
|
|
204
|
+
intensityRange: 0.1,
|
|
205
|
+
speed: 0.7,
|
|
206
|
+
chaos: 0.15,
|
|
207
|
+
category: 'Nature',
|
|
208
|
+
description: 'Enchanted forest greens with mossy accents'
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: 'Retro Wave',
|
|
213
|
+
config: {
|
|
214
|
+
hue: 310,
|
|
215
|
+
saturation: 0.9,
|
|
216
|
+
intensity: 0.8,
|
|
217
|
+
hueShift: 40,
|
|
218
|
+
saturationRange: 0.1,
|
|
219
|
+
intensityRange: 0.15,
|
|
220
|
+
speed: 1.5,
|
|
221
|
+
chaos: 0.3,
|
|
222
|
+
category: 'Retro',
|
|
223
|
+
description: 'Synthwave pink and cyan retrowave palette'
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: 'Quantum Void',
|
|
228
|
+
config: {
|
|
229
|
+
hue: 240,
|
|
230
|
+
saturation: 0.5,
|
|
231
|
+
intensity: 0.3,
|
|
232
|
+
hueShift: 5,
|
|
233
|
+
saturationRange: 0.05,
|
|
234
|
+
intensityRange: 0.15,
|
|
235
|
+
speed: 0.4,
|
|
236
|
+
chaos: 0.6,
|
|
237
|
+
category: 'Cosmic',
|
|
238
|
+
description: 'Near-black indigo with sporadic quantum flickers'
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: 'Golden Hour',
|
|
243
|
+
config: {
|
|
244
|
+
hue: 45,
|
|
245
|
+
saturation: 0.8,
|
|
246
|
+
intensity: 0.7,
|
|
247
|
+
hueShift: 10,
|
|
248
|
+
saturationRange: 0.05,
|
|
249
|
+
intensityRange: 0.1,
|
|
250
|
+
speed: 0.5,
|
|
251
|
+
chaos: 0.05,
|
|
252
|
+
category: 'Warm',
|
|
253
|
+
description: 'Warm golden light of late afternoon'
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: 'Ice Crystal',
|
|
258
|
+
config: {
|
|
259
|
+
hue: 190,
|
|
260
|
+
saturation: 0.55,
|
|
261
|
+
intensity: 0.8,
|
|
262
|
+
hueShift: 8,
|
|
263
|
+
saturationRange: 0.1,
|
|
264
|
+
intensityRange: 0.1,
|
|
265
|
+
speed: 0.6,
|
|
266
|
+
chaos: 0.08,
|
|
267
|
+
category: 'Elemental',
|
|
268
|
+
description: 'Crisp icy blue-white crystalline shimmer'
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: 'Blood Moon',
|
|
273
|
+
config: {
|
|
274
|
+
hue: 0,
|
|
275
|
+
saturation: 0.85,
|
|
276
|
+
intensity: 0.5,
|
|
277
|
+
hueShift: 10,
|
|
278
|
+
saturationRange: 0.1,
|
|
279
|
+
intensityRange: 0.15,
|
|
280
|
+
speed: 0.7,
|
|
281
|
+
chaos: 0.25,
|
|
282
|
+
category: 'Dark',
|
|
283
|
+
description: 'Deep crimson with dark blood-red pulsation'
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: 'Digital Rain',
|
|
288
|
+
config: {
|
|
289
|
+
hue: 120,
|
|
290
|
+
saturation: 0.9,
|
|
291
|
+
intensity: 0.7,
|
|
292
|
+
hueShift: 5,
|
|
293
|
+
saturationRange: 0.05,
|
|
294
|
+
intensityRange: 0.2,
|
|
295
|
+
speed: 1.8,
|
|
296
|
+
chaos: 0.4,
|
|
297
|
+
category: 'Futuristic',
|
|
298
|
+
description: 'Matrix-green cascading digital streams'
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: 'Sunset Gradient',
|
|
303
|
+
config: {
|
|
304
|
+
hue: 20,
|
|
305
|
+
saturation: 0.85,
|
|
306
|
+
intensity: 0.75,
|
|
307
|
+
hueShift: 35,
|
|
308
|
+
saturationRange: 0.1,
|
|
309
|
+
intensityRange: 0.1,
|
|
310
|
+
speed: 0.5,
|
|
311
|
+
chaos: 0.1,
|
|
312
|
+
category: 'Warm',
|
|
313
|
+
description: 'Orange-to-purple sunset sweep across the sky'
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: 'Deep Space',
|
|
318
|
+
config: {
|
|
319
|
+
hue: 250,
|
|
320
|
+
saturation: 0.6,
|
|
321
|
+
intensity: 0.35,
|
|
322
|
+
hueShift: 20,
|
|
323
|
+
saturationRange: 0.1,
|
|
324
|
+
intensityRange: 0.2,
|
|
325
|
+
speed: 0.3,
|
|
326
|
+
chaos: 0.15,
|
|
327
|
+
category: 'Cosmic',
|
|
328
|
+
description: 'Dark nebula blues with distant starlight'
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: 'Toxic Green',
|
|
333
|
+
config: {
|
|
334
|
+
hue: 100,
|
|
335
|
+
saturation: 0.95,
|
|
336
|
+
intensity: 0.8,
|
|
337
|
+
hueShift: 10,
|
|
338
|
+
saturationRange: 0.05,
|
|
339
|
+
intensityRange: 0.15,
|
|
340
|
+
speed: 1.6,
|
|
341
|
+
chaos: 0.5,
|
|
342
|
+
category: 'Futuristic',
|
|
343
|
+
description: 'Radioactive green glow with toxic highlights'
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: 'Royal Purple',
|
|
348
|
+
config: {
|
|
349
|
+
hue: 280,
|
|
350
|
+
saturation: 0.75,
|
|
351
|
+
intensity: 0.6,
|
|
352
|
+
hueShift: 12,
|
|
353
|
+
saturationRange: 0.08,
|
|
354
|
+
intensityRange: 0.1,
|
|
355
|
+
speed: 0.8,
|
|
356
|
+
chaos: 0.12,
|
|
357
|
+
category: 'Elegant',
|
|
358
|
+
description: 'Rich regal purple with velvet depth'
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: 'Coral Reef',
|
|
363
|
+
config: {
|
|
364
|
+
hue: 350,
|
|
365
|
+
saturation: 0.7,
|
|
366
|
+
intensity: 0.65,
|
|
367
|
+
hueShift: 25,
|
|
368
|
+
saturationRange: 0.1,
|
|
369
|
+
intensityRange: 0.1,
|
|
370
|
+
speed: 0.7,
|
|
371
|
+
chaos: 0.2,
|
|
372
|
+
category: 'Nature',
|
|
373
|
+
description: 'Coral pink and aqua reef ecosystem'
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
name: 'Thunderstorm',
|
|
378
|
+
config: {
|
|
379
|
+
hue: 220,
|
|
380
|
+
saturation: 0.6,
|
|
381
|
+
intensity: 0.5,
|
|
382
|
+
hueShift: 15,
|
|
383
|
+
saturationRange: 0.15,
|
|
384
|
+
intensityRange: 0.35,
|
|
385
|
+
speed: 1.3,
|
|
386
|
+
chaos: 0.65,
|
|
387
|
+
category: 'Elemental',
|
|
388
|
+
description: 'Electric blues and greys with lightning flashes'
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: 'Holographic Rainbow',
|
|
393
|
+
config: {
|
|
394
|
+
hue: 0,
|
|
395
|
+
saturation: 0.85,
|
|
396
|
+
intensity: 0.75,
|
|
397
|
+
hueShift: 120,
|
|
398
|
+
saturationRange: 0.15,
|
|
399
|
+
intensityRange: 0.2,
|
|
400
|
+
speed: 1.0,
|
|
401
|
+
chaos: 0.2,
|
|
402
|
+
category: 'Special',
|
|
403
|
+
description: 'Full-spectrum holographic rainbow sweep'
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
name: 'Lavender Dream',
|
|
408
|
+
config: {
|
|
409
|
+
hue: 260,
|
|
410
|
+
saturation: 0.5,
|
|
411
|
+
intensity: 0.6,
|
|
412
|
+
hueShift: 8,
|
|
413
|
+
saturationRange: 0.05,
|
|
414
|
+
intensityRange: 0.05,
|
|
415
|
+
speed: 0.4,
|
|
416
|
+
chaos: 0.03,
|
|
417
|
+
category: 'Elegant',
|
|
418
|
+
description: 'Soft pastel lavender with dreamlike calm'
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
name: 'Amber Glow',
|
|
423
|
+
config: {
|
|
424
|
+
hue: 38,
|
|
425
|
+
saturation: 0.85,
|
|
426
|
+
intensity: 0.65,
|
|
427
|
+
hueShift: 8,
|
|
428
|
+
saturationRange: 0.05,
|
|
429
|
+
intensityRange: 0.1,
|
|
430
|
+
speed: 0.6,
|
|
431
|
+
chaos: 0.08,
|
|
432
|
+
category: 'Warm',
|
|
433
|
+
description: 'Warm amber candlelight radiance'
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
];
|
|
437
|
+
|
|
438
|
+
for (const { name, config } of builtIn) {
|
|
439
|
+
this.presets.set(name, { ...config });
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// -------------------------------------------------------------------------
|
|
444
|
+
// Public API - Querying
|
|
445
|
+
// -------------------------------------------------------------------------
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Get a flat list of all available presets (built-in + custom).
|
|
449
|
+
*
|
|
450
|
+
* @returns {Array<{name: string, config: ColorPresetConfig, isCustom: boolean}>}
|
|
451
|
+
*/
|
|
452
|
+
getPresets() {
|
|
453
|
+
const list = [];
|
|
454
|
+
|
|
455
|
+
for (const [name, config] of this.presets) {
|
|
456
|
+
list.push({ name, config: { ...config }, isCustom: false });
|
|
457
|
+
}
|
|
458
|
+
for (const [name, config] of this.customPresets) {
|
|
459
|
+
list.push({ name, config: { ...config }, isCustom: true });
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return list;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Get presets grouped by their category.
|
|
467
|
+
*
|
|
468
|
+
* @returns {Object<string, Array<{name: string, config: ColorPresetConfig, isCustom: boolean}>>}
|
|
469
|
+
*/
|
|
470
|
+
getPresetsByCategory() {
|
|
471
|
+
const categories = {};
|
|
472
|
+
const all = this.getPresets();
|
|
473
|
+
|
|
474
|
+
for (const preset of all) {
|
|
475
|
+
const cat = preset.config.category || 'Uncategorized';
|
|
476
|
+
if (!categories[cat]) {
|
|
477
|
+
categories[cat] = [];
|
|
478
|
+
}
|
|
479
|
+
categories[cat].push(preset);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return categories;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Get a single preset configuration by name.
|
|
487
|
+
*
|
|
488
|
+
* @param {string} name - Preset name
|
|
489
|
+
* @returns {ColorPresetConfig|null} The preset config, or null if not found
|
|
490
|
+
*/
|
|
491
|
+
getPreset(name) {
|
|
492
|
+
const config = this.customPresets.get(name) || this.presets.get(name);
|
|
493
|
+
return config ? { ...config } : null;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Get the name of the currently active preset.
|
|
498
|
+
*
|
|
499
|
+
* @returns {string|null}
|
|
500
|
+
*/
|
|
501
|
+
getCurrentPreset() {
|
|
502
|
+
return this.currentPreset;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Check whether a transition animation is currently active.
|
|
507
|
+
*
|
|
508
|
+
* @returns {boolean}
|
|
509
|
+
*/
|
|
510
|
+
isTransitioning() {
|
|
511
|
+
return this.transitionState !== null;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// -------------------------------------------------------------------------
|
|
515
|
+
// Public API - Applying Presets
|
|
516
|
+
// -------------------------------------------------------------------------
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Apply a color preset by name.
|
|
520
|
+
*
|
|
521
|
+
* @param {string} name - Name of the preset to apply
|
|
522
|
+
* @param {boolean} [transition=true] - Whether to smoothly animate to the preset
|
|
523
|
+
* @param {number} [duration=800] - Transition duration in milliseconds
|
|
524
|
+
* @param {Function} [onComplete] - Optional callback when transition finishes
|
|
525
|
+
* @returns {boolean} true if the preset was found and applied
|
|
526
|
+
*/
|
|
527
|
+
applyPreset(name, transition = true, duration = 800, onComplete = null) {
|
|
528
|
+
const config = this.customPresets.get(name) || this.presets.get(name);
|
|
529
|
+
if (!config) {
|
|
530
|
+
console.warn(`ColorPresetsSystem: Unknown preset "${name}"`);
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (transition && duration > 0) {
|
|
535
|
+
this._startTransition(config, duration, onComplete);
|
|
536
|
+
} else {
|
|
537
|
+
this._applyConfigImmediate(config);
|
|
538
|
+
if (typeof onComplete === 'function') {
|
|
539
|
+
onComplete();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
this.currentPreset = name;
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Apply a raw preset configuration object directly (without looking up by name).
|
|
549
|
+
*
|
|
550
|
+
* @param {ColorPresetConfig} config - The configuration to apply
|
|
551
|
+
* @param {boolean} [transition=true] - Whether to animate
|
|
552
|
+
* @param {number} [duration=800] - Transition duration in ms
|
|
553
|
+
*/
|
|
554
|
+
applyConfig(config, transition = true, duration = 800) {
|
|
555
|
+
if (!config || typeof config.hue !== 'number') {
|
|
556
|
+
console.warn('ColorPresetsSystem: Invalid config object');
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (transition && duration > 0) {
|
|
561
|
+
this._startTransition(config, duration);
|
|
562
|
+
} else {
|
|
563
|
+
this._applyConfigImmediate(config);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
this.currentPreset = null;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// -------------------------------------------------------------------------
|
|
570
|
+
// Public API - Custom Presets
|
|
571
|
+
// -------------------------------------------------------------------------
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Create a new custom preset.
|
|
575
|
+
*
|
|
576
|
+
* @param {string} name - Name for the custom preset
|
|
577
|
+
* @param {ColorPresetConfig} config - Preset configuration
|
|
578
|
+
* @returns {boolean} true if created, false if name conflicts with built-in
|
|
579
|
+
*/
|
|
580
|
+
createCustomPreset(name, config) {
|
|
581
|
+
if (typeof name !== 'string' || name.trim().length === 0) {
|
|
582
|
+
console.warn('ColorPresetsSystem: Preset name must be a non-empty string');
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (this.presets.has(name)) {
|
|
587
|
+
console.warn(`ColorPresetsSystem: Cannot overwrite built-in preset "${name}"`);
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const validated = this._validateConfig(config);
|
|
592
|
+
this.customPresets.set(name.trim(), validated);
|
|
593
|
+
return true;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Update an existing custom preset.
|
|
598
|
+
*
|
|
599
|
+
* @param {string} name - Name of the custom preset to update
|
|
600
|
+
* @param {ColorPresetConfig} config - New configuration
|
|
601
|
+
* @returns {boolean} true if updated
|
|
602
|
+
*/
|
|
603
|
+
updateCustomPreset(name, config) {
|
|
604
|
+
if (!this.customPresets.has(name)) {
|
|
605
|
+
console.warn(`ColorPresetsSystem: Custom preset "${name}" not found`);
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const validated = this._validateConfig(config);
|
|
610
|
+
this.customPresets.set(name, validated);
|
|
611
|
+
return true;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Delete a custom preset.
|
|
616
|
+
*
|
|
617
|
+
* @param {string} name - Name of the custom preset to remove
|
|
618
|
+
* @returns {boolean} true if deleted
|
|
619
|
+
*/
|
|
620
|
+
deleteCustomPreset(name) {
|
|
621
|
+
return this.customPresets.delete(name);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// -------------------------------------------------------------------------
|
|
625
|
+
// Public API - Serialization
|
|
626
|
+
// -------------------------------------------------------------------------
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Export a preset for external storage or sharing.
|
|
630
|
+
*
|
|
631
|
+
* @param {string} [name] - Preset name to export. If omitted, exports current state.
|
|
632
|
+
* @returns {Object} Serializable preset data
|
|
633
|
+
*/
|
|
634
|
+
exportPreset(name) {
|
|
635
|
+
let config;
|
|
636
|
+
let presetName;
|
|
637
|
+
|
|
638
|
+
if (name) {
|
|
639
|
+
config = this.getPreset(name);
|
|
640
|
+
presetName = name;
|
|
641
|
+
} else if (this.currentPreset) {
|
|
642
|
+
config = this.getPreset(this.currentPreset);
|
|
643
|
+
presetName = this.currentPreset;
|
|
644
|
+
} else {
|
|
645
|
+
console.warn('ColorPresetsSystem: No preset specified or active for export');
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (!config) {
|
|
650
|
+
console.warn(`ColorPresetsSystem: Preset "${name}" not found for export`);
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return {
|
|
655
|
+
type: 'vib3-color-preset',
|
|
656
|
+
version: '1.0.0',
|
|
657
|
+
name: presetName,
|
|
658
|
+
timestamp: new Date().toISOString(),
|
|
659
|
+
config: { ...config }
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Export all custom presets for backup.
|
|
665
|
+
*
|
|
666
|
+
* @returns {Object} Serializable object containing all custom presets
|
|
667
|
+
*/
|
|
668
|
+
exportAllCustomPresets() {
|
|
669
|
+
const presets = {};
|
|
670
|
+
for (const [name, config] of this.customPresets) {
|
|
671
|
+
presets[name] = { ...config };
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
return {
|
|
675
|
+
type: 'vib3-color-preset-collection',
|
|
676
|
+
version: '1.0.0',
|
|
677
|
+
timestamp: new Date().toISOString(),
|
|
678
|
+
presets
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Import a preset from serialized data.
|
|
684
|
+
*
|
|
685
|
+
* @param {Object} data - Preset data as returned by exportPreset()
|
|
686
|
+
* @returns {boolean} true if imported successfully
|
|
687
|
+
*/
|
|
688
|
+
importPreset(data) {
|
|
689
|
+
if (!data || typeof data !== 'object') {
|
|
690
|
+
console.warn('ColorPresetsSystem: Invalid import data');
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (data.type === 'vib3-color-preset' && data.config) {
|
|
695
|
+
const name = data.name || `Imported ${Date.now()}`;
|
|
696
|
+
return this.createCustomPreset(name, data.config);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (data.type === 'vib3-color-preset-collection' && data.presets) {
|
|
700
|
+
let count = 0;
|
|
701
|
+
for (const [name, config] of Object.entries(data.presets)) {
|
|
702
|
+
if (this.createCustomPreset(name, config)) {
|
|
703
|
+
count++;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return count > 0;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
console.warn('ColorPresetsSystem: Unrecognized import format');
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// -------------------------------------------------------------------------
|
|
714
|
+
// Public API - Transition Control
|
|
715
|
+
// -------------------------------------------------------------------------
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Cancel any in-progress transition and keep current values.
|
|
719
|
+
*/
|
|
720
|
+
cancelTransition() {
|
|
721
|
+
if (this._frameId !== null) {
|
|
722
|
+
cancelAnimationFrame(this._frameId);
|
|
723
|
+
this._frameId = null;
|
|
724
|
+
}
|
|
725
|
+
this.transitionState = null;
|
|
726
|
+
this._onTransitionComplete = null;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// -------------------------------------------------------------------------
|
|
730
|
+
// Public API - Lifecycle
|
|
731
|
+
// -------------------------------------------------------------------------
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Clean up resources and cancel any active animation.
|
|
735
|
+
*/
|
|
736
|
+
dispose() {
|
|
737
|
+
this.cancelTransition();
|
|
738
|
+
this.presets.clear();
|
|
739
|
+
this.customPresets.clear();
|
|
740
|
+
this.currentPreset = null;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// -------------------------------------------------------------------------
|
|
744
|
+
// Private - Transition Engine
|
|
745
|
+
// -------------------------------------------------------------------------
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Begin a smooth transition to the target configuration.
|
|
749
|
+
*
|
|
750
|
+
* @param {ColorPresetConfig} targetConfig - Target color configuration
|
|
751
|
+
* @param {number} duration - Duration in milliseconds
|
|
752
|
+
* @param {Function} [onComplete] - Completion callback
|
|
753
|
+
* @private
|
|
754
|
+
*/
|
|
755
|
+
_startTransition(targetConfig, duration, onComplete = null) {
|
|
756
|
+
// Cancel any existing transition
|
|
757
|
+
this.cancelTransition();
|
|
758
|
+
|
|
759
|
+
// Capture the current parameter values as the starting point
|
|
760
|
+
const fromConfig = this._captureCurrentState();
|
|
761
|
+
|
|
762
|
+
this.transitionState = {
|
|
763
|
+
from: fromConfig,
|
|
764
|
+
to: { ...targetConfig },
|
|
765
|
+
startTime: performance.now(),
|
|
766
|
+
duration,
|
|
767
|
+
progress: 0
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
this._onTransitionComplete = onComplete;
|
|
771
|
+
this._tickTransition();
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Animation frame tick for the transition.
|
|
776
|
+
* @private
|
|
777
|
+
*/
|
|
778
|
+
_tickTransition() {
|
|
779
|
+
if (!this.transitionState) return;
|
|
780
|
+
|
|
781
|
+
const now = performance.now();
|
|
782
|
+
const elapsed = now - this.transitionState.startTime;
|
|
783
|
+
const progress = Math.min(elapsed / this.transitionState.duration, 1);
|
|
784
|
+
|
|
785
|
+
// Smooth ease-in-out curve
|
|
786
|
+
const eased = this._easeInOutCubic(progress);
|
|
787
|
+
|
|
788
|
+
this.transitionState.progress = progress;
|
|
789
|
+
|
|
790
|
+
// Interpolate and apply each animatable parameter
|
|
791
|
+
const from = this.transitionState.from;
|
|
792
|
+
const to = this.transitionState.to;
|
|
793
|
+
|
|
794
|
+
// Hue needs special circular interpolation
|
|
795
|
+
const hue = this._lerpHue(from.hue, to.hue, eased);
|
|
796
|
+
this.updateParameter('hue', Math.round(hue));
|
|
797
|
+
|
|
798
|
+
// Linear interpolation for saturation and intensity
|
|
799
|
+
if (typeof to.saturation === 'number') {
|
|
800
|
+
this.updateParameter('saturation', this._lerp(from.saturation, to.saturation, eased));
|
|
801
|
+
}
|
|
802
|
+
if (typeof to.intensity === 'number') {
|
|
803
|
+
this.updateParameter('intensity', this._lerp(from.intensity, to.intensity, eased));
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// Optional parameter overrides
|
|
807
|
+
if (typeof to.speed === 'number' && typeof from.speed === 'number') {
|
|
808
|
+
this.updateParameter('speed', this._lerp(from.speed, to.speed, eased));
|
|
809
|
+
}
|
|
810
|
+
if (typeof to.chaos === 'number' && typeof from.chaos === 'number') {
|
|
811
|
+
this.updateParameter('chaos', this._lerp(from.chaos, to.chaos, eased));
|
|
812
|
+
}
|
|
813
|
+
if (typeof to.morphFactor === 'number' && typeof from.morphFactor === 'number') {
|
|
814
|
+
this.updateParameter('morphFactor', this._lerp(from.morphFactor, to.morphFactor, eased));
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// Continue or finish
|
|
818
|
+
if (progress < 1) {
|
|
819
|
+
this._frameId = requestAnimationFrame(() => this._tickTransition());
|
|
820
|
+
} else {
|
|
821
|
+
this.transitionState = null;
|
|
822
|
+
this._frameId = null;
|
|
823
|
+
if (typeof this._onTransitionComplete === 'function') {
|
|
824
|
+
const cb = this._onTransitionComplete;
|
|
825
|
+
this._onTransitionComplete = null;
|
|
826
|
+
cb();
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Apply a configuration immediately without transition.
|
|
833
|
+
*
|
|
834
|
+
* @param {ColorPresetConfig} config
|
|
835
|
+
* @private
|
|
836
|
+
*/
|
|
837
|
+
_applyConfigImmediate(config) {
|
|
838
|
+
if (typeof config.hue === 'number') {
|
|
839
|
+
this.updateParameter('hue', Math.round(config.hue));
|
|
840
|
+
}
|
|
841
|
+
if (typeof config.saturation === 'number') {
|
|
842
|
+
this.updateParameter('saturation', config.saturation);
|
|
843
|
+
}
|
|
844
|
+
if (typeof config.intensity === 'number') {
|
|
845
|
+
this.updateParameter('intensity', config.intensity);
|
|
846
|
+
}
|
|
847
|
+
if (typeof config.speed === 'number') {
|
|
848
|
+
this.updateParameter('speed', config.speed);
|
|
849
|
+
}
|
|
850
|
+
if (typeof config.chaos === 'number') {
|
|
851
|
+
this.updateParameter('chaos', config.chaos);
|
|
852
|
+
}
|
|
853
|
+
if (typeof config.morphFactor === 'number') {
|
|
854
|
+
this.updateParameter('morphFactor', config.morphFactor);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Capture the current state of animatable parameters as a snapshot.
|
|
860
|
+
* Uses sensible defaults for any parameter the callback system cannot
|
|
861
|
+
* report back (since the callback is write-only).
|
|
862
|
+
*
|
|
863
|
+
* @returns {ColorPresetConfig}
|
|
864
|
+
* @private
|
|
865
|
+
*/
|
|
866
|
+
_captureCurrentState() {
|
|
867
|
+
// If we have a current preset, use its values as the starting state
|
|
868
|
+
if (this.currentPreset) {
|
|
869
|
+
const preset = this.customPresets.get(this.currentPreset) || this.presets.get(this.currentPreset);
|
|
870
|
+
if (preset) {
|
|
871
|
+
return { ...preset };
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// Fallback: use VIB3+ defaults from Parameters.js
|
|
876
|
+
return {
|
|
877
|
+
hue: 200,
|
|
878
|
+
saturation: 0.8,
|
|
879
|
+
intensity: 0.5,
|
|
880
|
+
speed: 1.0,
|
|
881
|
+
chaos: 0.2,
|
|
882
|
+
morphFactor: 1.0
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// -------------------------------------------------------------------------
|
|
887
|
+
// Private - Validation
|
|
888
|
+
// -------------------------------------------------------------------------
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Validate and sanitize a preset config, filling defaults for missing fields.
|
|
892
|
+
*
|
|
893
|
+
* @param {Object} config - Raw config object
|
|
894
|
+
* @returns {ColorPresetConfig} Sanitized config
|
|
895
|
+
* @private
|
|
896
|
+
*/
|
|
897
|
+
_validateConfig(config) {
|
|
898
|
+
return {
|
|
899
|
+
hue: this._clamp(Number(config.hue) || 0, 0, 360),
|
|
900
|
+
saturation: this._clamp(Number(config.saturation) ?? 0.8, 0, 1),
|
|
901
|
+
intensity: this._clamp(Number(config.intensity) ?? 0.5, 0, 1),
|
|
902
|
+
hueShift: Number(config.hueShift) || 0,
|
|
903
|
+
saturationRange: this._clamp(Number(config.saturationRange) || 0, 0, 1),
|
|
904
|
+
intensityRange: this._clamp(Number(config.intensityRange) || 0, 0, 1),
|
|
905
|
+
speed: config.speed != null ? this._clamp(Number(config.speed), 0.1, 3) : undefined,
|
|
906
|
+
chaos: config.chaos != null ? this._clamp(Number(config.chaos), 0, 1) : undefined,
|
|
907
|
+
morphFactor: config.morphFactor != null ? this._clamp(Number(config.morphFactor), 0, 2) : undefined,
|
|
908
|
+
category: typeof config.category === 'string' ? config.category : 'Custom',
|
|
909
|
+
description: typeof config.description === 'string' ? config.description : ''
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// -------------------------------------------------------------------------
|
|
914
|
+
// Private - Math Utilities
|
|
915
|
+
// -------------------------------------------------------------------------
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* Linear interpolation.
|
|
919
|
+
*
|
|
920
|
+
* @param {number} a - Start value
|
|
921
|
+
* @param {number} b - End value
|
|
922
|
+
* @param {number} t - Progress (0-1)
|
|
923
|
+
* @returns {number}
|
|
924
|
+
* @private
|
|
925
|
+
*/
|
|
926
|
+
_lerp(a, b, t) {
|
|
927
|
+
return a + (b - a) * t;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* Circular hue interpolation that takes the shortest path around the
|
|
932
|
+
* 360-degree hue wheel.
|
|
933
|
+
*
|
|
934
|
+
* @param {number} a - Start hue (0-360)
|
|
935
|
+
* @param {number} b - End hue (0-360)
|
|
936
|
+
* @param {number} t - Progress (0-1)
|
|
937
|
+
* @returns {number} Interpolated hue (0-360)
|
|
938
|
+
* @private
|
|
939
|
+
*/
|
|
940
|
+
_lerpHue(a, b, t) {
|
|
941
|
+
let diff = b - a;
|
|
942
|
+
|
|
943
|
+
// Take the shortest arc around the wheel
|
|
944
|
+
if (diff > 180) diff -= 360;
|
|
945
|
+
if (diff < -180) diff += 360;
|
|
946
|
+
|
|
947
|
+
let result = a + diff * t;
|
|
948
|
+
if (result < 0) result += 360;
|
|
949
|
+
if (result >= 360) result -= 360;
|
|
950
|
+
|
|
951
|
+
return result;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Clamp a value to a range.
|
|
956
|
+
*
|
|
957
|
+
* @param {number} value
|
|
958
|
+
* @param {number} min
|
|
959
|
+
* @param {number} max
|
|
960
|
+
* @returns {number}
|
|
961
|
+
* @private
|
|
962
|
+
*/
|
|
963
|
+
_clamp(value, min, max) {
|
|
964
|
+
if (!Number.isFinite(value)) return min;
|
|
965
|
+
return Math.max(min, Math.min(max, value));
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* Cubic ease-in-out for smooth transitions.
|
|
970
|
+
*
|
|
971
|
+
* @param {number} t - Progress (0-1)
|
|
972
|
+
* @returns {number} Eased value
|
|
973
|
+
* @private
|
|
974
|
+
*/
|
|
975
|
+
_easeInOutCubic(t) {
|
|
976
|
+
return t < 0.5
|
|
977
|
+
? 4 * t * t * t
|
|
978
|
+
: 1 - Math.pow(-2 * t + 2, 3) / 2;
|
|
979
|
+
}
|
|
980
|
+
}
|