@vib3code/sdk 2.0.3-canary.0e9a1ac → 2.0.3-canary.45332e3

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.
@@ -9,6 +9,8 @@ import { telemetry, EventType, withTelemetry } from '../telemetry/index.js';
9
9
  import { AestheticMapper } from '../../creative/AestheticMapper.js';
10
10
  import { ChoreographyPlayer } from '../../creative/ChoreographyPlayer.js';
11
11
  import { ParameterTimeline } from '../../creative/ParameterTimeline.js';
12
+ import { ColorPresetsSystem } from '../../creative/ColorPresetsSystem.js';
13
+ import { TransitionAnimator } from '../../creative/TransitionAnimator.js';
12
14
  import { PRESET_REGISTRY } from '../../render/LayerRelationshipGraph.js';
13
15
 
14
16
  /**
@@ -42,6 +44,36 @@ export class MCPServer {
42
44
  this.engine = engine;
43
45
  this.sceneId = null;
44
46
  this.initialized = false;
47
+ this._gallerySlots = new Map();
48
+ }
49
+
50
+ /**
51
+ * Get or lazily create ColorPresetsSystem instance.
52
+ * Requires engine for the parameter update callback.
53
+ * @returns {ColorPresetsSystem|null}
54
+ */
55
+ _getColorPresets() {
56
+ if (this._colorPresets) return this._colorPresets;
57
+ if (!this.engine) return null;
58
+ this._colorPresets = new ColorPresetsSystem(
59
+ (name, value) => this.engine.setParameter(name, value)
60
+ );
61
+ return this._colorPresets;
62
+ }
63
+
64
+ /**
65
+ * Get or lazily create TransitionAnimator instance.
66
+ * Requires engine for the parameter update/get callbacks.
67
+ * @returns {TransitionAnimator|null}
68
+ */
69
+ _getTransitionAnimator() {
70
+ if (this._transitionAnimator) return this._transitionAnimator;
71
+ if (!this.engine) return null;
72
+ this._transitionAnimator = new TransitionAnimator(
73
+ (name, value) => this.engine.setParameter(name, value),
74
+ (name) => this.engine.getParameter(name)
75
+ );
76
+ return this._transitionAnimator;
45
77
  }
46
78
 
47
79
  buildResponse(operation, data, options = {}) {
@@ -141,10 +173,13 @@ export class MCPServer {
141
173
  result = this.getParameterSchema();
142
174
  break;
143
175
  case 'get_sdk_context':
144
- result = this.getSDKContext();
176
+ result = this.getSDKContext(args);
177
+ break;
178
+ case 'inspect_layers':
179
+ result = this.inspectLayers(args);
145
180
  break;
146
- case 'verify_knowledge':
147
- result = this.verifyKnowledge(args);
181
+ case 'set_holographic_layer':
182
+ result = this.setHolographicLayer(args);
148
183
  break;
149
184
  // Reactivity tools (Phase 6.5)
150
185
  case 'set_reactivity_config':
@@ -484,32 +519,68 @@ export class MCPServer {
484
519
 
485
520
  telemetry.recordEvent(EventType.GALLERY_SAVE, { slot });
486
521
 
522
+ // Persist actual engine state if available
523
+ if (this.engine) {
524
+ const state = this.engine.exportState();
525
+ this._gallerySlots.set(slot, {
526
+ name: name || `Variation ${slot}`,
527
+ saved_at: new Date().toISOString(),
528
+ state
529
+ });
530
+ }
531
+
487
532
  return {
488
533
  slot,
489
534
  name: name || `Variation ${slot}`,
490
535
  saved_at: new Date().toISOString(),
536
+ persisted: !!this.engine,
537
+ gallery_size: this._gallerySlots.size,
491
538
  suggested_next_actions: ['load_from_gallery', 'randomize_parameters']
492
539
  };
493
540
  }
494
541
 
495
542
  /**
496
- * Load from gallery
543
+ * Load from gallery — restores previously saved state
497
544
  */
498
545
  loadFromGallery(args) {
499
546
  const { slot } = args;
500
547
 
501
- if (this.engine) {
502
- // Apply variation
503
- const params = this.engine.parameters?.generateVariationParameters?.(slot) || {};
504
- this.engine.setParameters(params);
548
+ telemetry.recordEvent(EventType.GALLERY_LOAD, { slot });
549
+
550
+ const saved = this._gallerySlots.get(slot);
551
+ if (saved && this.engine) {
552
+ // Restore saved state
553
+ this.engine.importState(saved.state);
554
+ return {
555
+ slot,
556
+ name: saved.name,
557
+ saved_at: saved.saved_at,
558
+ loaded_at: new Date().toISOString(),
559
+ restored: true,
560
+ ...this.getState()
561
+ };
505
562
  }
506
563
 
507
- telemetry.recordEvent(EventType.GALLERY_LOAD, { slot });
564
+ if (!saved) {
565
+ // No saved state — fall back to random variation
566
+ if (this.engine) {
567
+ const params = this.engine.parameters?.generateVariationParameters?.(slot) || {};
568
+ this.engine.setParameters(params);
569
+ }
570
+ return {
571
+ slot,
572
+ loaded_at: new Date().toISOString(),
573
+ restored: false,
574
+ note: 'No saved state in this slot — generated random variation',
575
+ ...this.getState()
576
+ };
577
+ }
508
578
 
509
579
  return {
510
580
  slot,
511
581
  loaded_at: new Date().toISOString(),
512
- ...this.getState()
582
+ restored: false,
583
+ note: 'Engine not initialized — cannot apply state'
513
584
  };
514
585
  }
515
586
 
@@ -582,162 +653,218 @@ export class MCPServer {
582
653
  /**
583
654
  * Get SDK context for agent onboarding
584
655
  */
585
- getSDKContext() {
586
- return {
587
- sdk_name: 'VIB3+ SDK',
588
- version: '1.9.0',
589
- purpose: 'General-purpose 4D rotation visualization SDK for plugins, extensions, wearables, and agentic use',
590
-
591
- quick_reference: {
592
- active_visualization_systems: 3,
593
- placeholder_systems: 1,
594
- rotation_planes: 6,
595
- base_geometries: 8,
596
- core_warp_types: 3,
597
- total_geometries: 24,
598
- canvas_layers_per_system: 5
599
- },
600
-
601
- systems: {
602
- ACTIVE: [
603
- { name: 'quantum', description: 'Complex quantum lattice visualizations with 24 geometries' },
604
- { name: 'faceted', description: 'Clean 2D geometric patterns with 4D rotation' },
605
- { name: 'holographic', description: '5-layer audio-reactive holographic effects' }
606
- ],
607
- PLACEHOLDER_TBD: [
608
- { name: 'polychora', status: 'TBD', description: '4D polytopes - placeholder, not production ready' }
609
- ]
656
+ getSDKContext(args = {}) {
657
+ const { include_state = true, include_tools = false } = args;
658
+
659
+ const context = {
660
+ sdk: 'VIB3+ 4D Visualization Engine',
661
+ version: '2.0.3',
662
+
663
+ // Capability manifest — what this engine can do
664
+ capabilities: {
665
+ systems: ['quantum', 'faceted', 'holographic'],
666
+ geometries: { count: 24, formula: 'core_type * 8 + base_geometry', base: 8, warps: 3 },
667
+ rotation: { planes: 6, '3D': ['XY', 'XZ', 'YZ'], '4D': ['XW', 'YW', 'ZW'], range: '±6.28 rad' },
668
+ layers: { count: 5, roles: ['background', 'shadow', 'content', 'highlight', 'accent'], addressable: true },
669
+ audio: { bands: ['bass', 'mid', 'high'], modes: ['add', 'multiply', 'replace', 'max', 'min'] },
670
+ input: { sources: ['deviceTilt', 'mousePosition', 'gyroscope', 'gamepad', 'perspective', 'programmatic', 'audio', 'midi'] },
671
+ creative: {
672
+ color_presets: 22,
673
+ easing_functions: 14,
674
+ post_effects: 14,
675
+ aesthetic_keywords: '130+',
676
+ choreography: true,
677
+ timeline_bpm_sync: true
678
+ },
679
+ environment: {
680
+ browser: typeof document !== 'undefined',
681
+ screenshot: typeof document !== 'undefined',
682
+ webgpu: typeof navigator !== 'undefined' && !!navigator.gpu,
683
+ wasm: typeof WebAssembly !== 'undefined'
684
+ }
610
685
  },
611
686
 
612
- geometry_encoding: {
613
- formula: 'geometry_index = core_index * 8 + base_index',
614
- base_geometries: ['tetrahedron', 'hypercube', 'sphere', 'torus', 'klein_bottle', 'fractal', 'wave', 'crystal'],
615
- core_types: ['base (0)', 'hypersphere (1)', 'hypertetrahedron (2)'],
616
- example: 'geometry 10 = hypersphere(sphere) because 1*8+2=10'
687
+ // Parameter ranges — the agent needs these to generate valid values
688
+ parameter_ranges: {
689
+ geometry: { min: 0, max: 23, type: 'integer' },
690
+ hue: { min: 0, max: 360, type: 'integer' },
691
+ saturation: { min: 0, max: 1 },
692
+ intensity: { min: 0, max: 1 },
693
+ speed: { min: 0.1, max: 3 },
694
+ chaos: { min: 0, max: 1 },
695
+ morphFactor: { min: 0, max: 2 },
696
+ gridDensity: { min: 4, max: 100 },
697
+ dimension: { min: 3.0, max: 4.5 },
698
+ rot4dXY: { min: -6.28, max: 6.28 },
699
+ rot4dXW: { min: -6.28, max: 6.28 }
617
700
  },
618
701
 
619
- rotation_planes: {
620
- total: 6,
621
- '3D_space': ['XY', 'XZ', 'YZ'],
622
- '4D_hyperspace': ['XW', 'YW', 'ZW'],
623
- range: '-6.28 to 6.28 radians'
702
+ // Workflow hints — what tool sequences accomplish creative goals
703
+ workflows: {
704
+ quick_design: 'design_from_description describe_visual_state → batch_set_parameters',
705
+ choreography: 'create_choreography play_choreography → describe_visual_state',
706
+ evolve: 'batch_set_parameters describe_visual_state → batch_set_parameters (iterate)',
707
+ layer_control: 'inspect_layers → set_holographic_layer → inspect_layers',
708
+ audio_reactive: 'configure_audio_band → apply_behavior_preset → describe_visual_state'
624
709
  },
625
710
 
626
- canvas_layers: {
627
- count: 5,
628
- names: ['background', 'shadow', 'content', 'highlight', 'accent']
629
- },
711
+ // Geometry quick-reference
712
+ geometry_map: {
713
+ 'sphere': 2, 'hypersphere+sphere': 10, 'hypertetra+sphere': 18,
714
+ 'torus': 3, 'hypersphere+torus': 11, 'hypertetra+torus': 19,
715
+ 'fractal': 5, 'hypersphere+fractal': 13, 'hypertetra+fractal': 21,
716
+ 'crystal': 7, 'wave': 6, 'klein_bottle': 4, 'hypercube': 1, 'tetrahedron': 0
717
+ }
718
+ };
630
719
 
631
- knowledge_quiz: {
632
- IMPORTANT: 'Call verify_knowledge with multiple choice answers (a/b/c/d) to confirm understanding',
633
- questions: [
634
- 'Q1: How many rotation planes? a)3 b)4 c)6 d)8',
635
- 'Q2: Geometry encoding formula? a)base*3+core b)core*8+base c)base+core d)core*base',
636
- 'Q3: Canvas layers per system? a)3 b)4 c)5 d)6',
637
- 'Q4: Which are the 3 ACTIVE systems? a)quantum,faceted,holographic b)quantum,faceted,polychora c)all four d)none',
638
- 'Q5: How many base geometry types? a)6 b)8 c)10 d)24',
639
- 'Q6: Core warp types? a)base,sphere,cube b)base,hypersphere,hypertetrahedron c)2D,3D,4D d)none'
640
- ]
641
- },
720
+ if (include_state) {
721
+ context.current_state = this.getState();
722
+ }
642
723
 
643
- documentation: {
644
- primary: 'DOCS/SYSTEM_INVENTORY.md',
645
- geometry: '24-GEOMETRY-6D-ROTATION-SUMMARY.md',
646
- controls: 'DOCS/CONTROL_REFERENCE.md',
647
- cli: 'DOCS/CLI_ONBOARDING.md'
648
- },
724
+ if (include_tools) {
725
+ context.tool_summary = Object.entries(toolDefinitions).map(([name, def]) => ({
726
+ name,
727
+ description: def.description.split('.')[0] // First sentence only
728
+ }));
729
+ }
649
730
 
650
- suggested_next_actions: ['verify_knowledge', 'create_4d_visualization', 'search_geometries']
651
- };
731
+ return context;
652
732
  }
653
733
 
654
734
  /**
655
- * Verify agent knowledge of SDK (multiple choice)
735
+ * Inspect holographic layer state returns per-layer metadata in JSON format.
736
+ * This replaces the old verify_knowledge quiz with something actually useful.
656
737
  */
657
- verifyKnowledge(answers) {
658
- const correctAnswers = {
659
- q1_rotation_planes: 'c', // 6 rotation planes
660
- q2_geometry_formula: 'b', // core*8+base
661
- q3_canvas_layers: 'c', // 5 layers
662
- q4_active_systems: 'a', // quantum, faceted, holographic (polychora is TBD)
663
- q5_base_geometries: 'b', // 8 base geometries
664
- q6_core_types: 'b' // base, hypersphere, hypertetrahedron
665
- };
738
+ inspectLayers(args = {}) {
739
+ const { layer = 'all' } = args;
666
740
 
667
- const docReferences = {
668
- q1_rotation_planes: {
669
- topic: '6D ROTATION SYSTEM',
670
- doc: 'DOCS/SYSTEM_INVENTORY.md#the-6d-rotation-system',
671
- reason: '6 planes: XY, XZ, YZ (3D) + XW, YW, ZW (4D hyperspace)'
672
- },
673
- q2_geometry_formula: {
674
- topic: 'GEOMETRY ENCODING',
675
- doc: '24-GEOMETRY-6D-ROTATION-SUMMARY.md',
676
- reason: 'geometry = coreIndex * 8 + baseIndex. Example: 10 = 1*8+2 = hypersphere+sphere'
677
- },
678
- q3_canvas_layers: {
679
- topic: 'CANVAS LAYER SYSTEM',
680
- doc: 'DOCS/SYSTEM_INVENTORY.md#the-4-visualization-systems',
681
- reason: '5 layers: background, shadow, content, highlight, accent'
682
- },
683
- q4_active_systems: {
684
- topic: 'ACTIVE VS PLACEHOLDER SYSTEMS',
685
- doc: 'DOCS/SYSTEM_INVENTORY.md',
686
- reason: 'Only 3 ACTIVE: quantum, faceted, holographic. Polychora is TBD/placeholder!'
687
- },
688
- q5_base_geometries: {
689
- topic: 'BASE GEOMETRY TYPES',
690
- doc: '24-GEOMETRY-6D-ROTATION-SUMMARY.md',
691
- reason: '8 base: tetrahedron, hypercube, sphere, torus, klein, fractal, wave, crystal'
692
- },
693
- q6_core_types: {
694
- topic: 'CORE WARP TYPES',
695
- doc: '24-GEOMETRY-6D-ROTATION-SUMMARY.md',
696
- reason: '3 cores: base (no warp), hypersphere, hypertetrahedron'
697
- }
741
+ const systemName = this.engine?.currentSystemName || 'unknown';
742
+ const isHolographic = systemName === 'holographic';
743
+
744
+ if (!isHolographic) {
745
+ return {
746
+ system: systemName,
747
+ note: 'Layer inspection is most detailed for the holographic system (5 independent canvas layers). Switch with switch_system("holographic").',
748
+ layers: [{
749
+ role: 'main',
750
+ system: systemName,
751
+ opacity: 1.0,
752
+ enabled: true,
753
+ description: `Single canvas for ${systemName} system`
754
+ }],
755
+ suggested_next_actions: ['switch_system', 'describe_visual_state']
756
+ };
757
+ }
758
+
759
+ // Build layer metadata from the holographic system
760
+ const system = this.engine?.currentSystem;
761
+ const layerRoles = ['background', 'shadow', 'content', 'highlight', 'accent'];
762
+ const defaultConfigs = {
763
+ background: { densityMult: 0.4, speedMult: 0.2, colorShift: 0, intensity: 0.2, reactivity: 0.5 },
764
+ shadow: { densityMult: 0.8, speedMult: 0.3, colorShift: 180, intensity: 0.4, reactivity: 0.7 },
765
+ content: { densityMult: 1.0, speedMult: 1.0, colorShift: 0, intensity: 1.0, reactivity: 0.9 },
766
+ highlight: { densityMult: 1.5, speedMult: 0.8, colorShift: 60, intensity: 0.6, reactivity: 1.1 },
767
+ accent: { densityMult: 2.5, speedMult: 0.4, colorShift: 300, intensity: 0.3, reactivity: 1.5 }
698
768
  };
699
769
 
700
- const results = {
701
- score: 0,
702
- max_score: 6,
703
- details: [],
704
- REVIEW_REQUIRED: []
770
+ const buildLayerInfo = (role) => {
771
+ const config = defaultConfigs[role] || {};
772
+ const visualizer = system?.visualizers?.find?.(v => v?.role === role);
773
+ const overrides = this._layerOverrides?.get(role) || {};
774
+
775
+ return {
776
+ role,
777
+ enabled: overrides.enabled !== undefined ? overrides.enabled : true,
778
+ opacity: overrides.opacity !== undefined ? overrides.opacity : 1.0,
779
+ blendMode: overrides.blendMode || 'normal',
780
+ densityMult: overrides.densityMult ?? config.densityMult,
781
+ speedMult: overrides.speedMult ?? config.speedMult,
782
+ colorShift: overrides.colorShift ?? config.colorShift,
783
+ intensity: overrides.intensity ?? config.intensity,
784
+ reactivity: overrides.reactivity ?? config.reactivity,
785
+ has_visualizer: !!visualizer
786
+ };
705
787
  };
706
788
 
707
- // Check each answer
708
- for (const [question, correct] of Object.entries(correctAnswers)) {
709
- const given = answers[question]?.toLowerCase?.() || answers[question];
710
- if (given === correct) {
711
- results.score++;
712
- results.details.push({ question, status: '✓ CORRECT' });
713
- } else if (given !== undefined) {
714
- results.details.push({
715
- question,
716
- status: '✗ WRONG',
717
- your_answer: given,
718
- correct_answer: correct
719
- });
720
- results.REVIEW_REQUIRED.push(docReferences[question]);
721
- }
722
- }
789
+ const layers = layer === 'all'
790
+ ? layerRoles.map(buildLayerInfo)
791
+ : [buildLayerInfo(layer)];
792
+
793
+ return {
794
+ system: 'holographic',
795
+ layer_count: layerRoles.length,
796
+ layers,
797
+ suggested_next_actions: ['set_holographic_layer', 'batch_set_parameters', 'describe_visual_state']
798
+ };
799
+ }
723
800
 
724
- results.percentage = Math.round((results.score / results.max_score) * 100);
801
+ /**
802
+ * Set properties on an individual holographic layer.
803
+ * Stores overrides in a layer override map and applies them to the visualizer.
804
+ */
805
+ setHolographicLayer(args) {
806
+ const { layer, opacity, blendMode, enabled, colorShift, densityMult, speedMult, reactivity } = args;
725
807
 
726
- // Build response
727
- if (results.REVIEW_REQUIRED.length > 0) {
728
- results.MESSAGE = `Score: ${results.score}/${results.max_score}. YOU MAY PROCEED but PLEASE review the topics below to avoid errors.`;
729
- results.URGENT = results.REVIEW_REQUIRED.map(ref => ({
730
- TOPIC: ref.topic,
731
- READ: ref.doc,
732
- WHY: ref.reason
733
- }));
734
- } else {
735
- results.MESSAGE = `PERFECT SCORE! You understand the VIB3+ SDK architecture.`;
808
+ const systemName = this.engine?.currentSystemName || 'unknown';
809
+ if (systemName !== 'holographic') {
810
+ return {
811
+ error: {
812
+ type: 'SystemError',
813
+ code: 'NOT_HOLOGRAPHIC',
814
+ message: `set_holographic_layer requires holographic system (current: ${systemName})`,
815
+ suggestion: 'Call switch_system("holographic") first'
816
+ }
817
+ };
736
818
  }
737
819
 
738
- results.suggested_next_actions = ['create_4d_visualization', 'get_state', 'search_geometries'];
820
+ // Initialize layer override storage
821
+ if (!this._layerOverrides) this._layerOverrides = new Map();
822
+ const existing = this._layerOverrides.get(layer) || {};
823
+ const updates = {};
824
+
825
+ if (opacity !== undefined) { existing.opacity = opacity; updates.opacity = opacity; }
826
+ if (blendMode !== undefined) { existing.blendMode = blendMode; updates.blendMode = blendMode; }
827
+ if (enabled !== undefined) { existing.enabled = enabled; updates.enabled = enabled; }
828
+ if (colorShift !== undefined) { existing.colorShift = colorShift; updates.colorShift = colorShift; }
829
+ if (densityMult !== undefined) { existing.densityMult = densityMult; updates.densityMult = densityMult; }
830
+ if (speedMult !== undefined) { existing.speedMult = speedMult; updates.speedMult = speedMult; }
831
+ if (reactivity !== undefined) { existing.reactivity = reactivity; updates.reactivity = reactivity; }
832
+
833
+ this._layerOverrides.set(layer, existing);
834
+
835
+ // Apply to visualizer if available
836
+ const system = this.engine?.currentSystem;
837
+ const visualizer = system?.visualizers?.find?.(v => v?.role === layer);
838
+ if (visualizer) {
839
+ if (opacity !== undefined && visualizer.canvas) {
840
+ visualizer.canvas.style.opacity = String(opacity);
841
+ }
842
+ if (blendMode !== undefined && visualizer.canvas) {
843
+ visualizer.canvas.style.mixBlendMode = blendMode;
844
+ }
845
+ if (enabled !== undefined && visualizer.canvas) {
846
+ visualizer.canvas.style.display = enabled ? '' : 'none';
847
+ }
848
+ if (colorShift !== undefined && visualizer.roleParams) {
849
+ visualizer.roleParams.colorShift = colorShift;
850
+ }
851
+ if (densityMult !== undefined && visualizer.roleParams) {
852
+ visualizer.roleParams.densityMult = densityMult;
853
+ }
854
+ if (speedMult !== undefined && visualizer.roleParams) {
855
+ visualizer.roleParams.speedMult = speedMult;
856
+ }
857
+ if (reactivity !== undefined) {
858
+ visualizer.reactivity = reactivity;
859
+ }
860
+ }
739
861
 
740
- return results;
862
+ return {
863
+ layer,
864
+ applied: updates,
865
+ current_state: this.inspectLayers({ layer }).layers[0],
866
+ suggested_next_actions: ['inspect_layers', 'set_holographic_layer', 'describe_visual_state']
867
+ };
741
868
  }
742
869
 
743
870
  /**
@@ -1217,8 +1344,17 @@ export class MCPServer {
1217
1344
  (sum, step) => sum + step.duration + step.delay, 0
1218
1345
  );
1219
1346
 
1347
+ // Execute live if engine available
1348
+ let executing = false;
1349
+ const animator = this._getTransitionAnimator();
1350
+ if (animator) {
1351
+ const seqId = animator.sequence(normalizedSequence);
1352
+ executing = !!seqId;
1353
+ }
1354
+
1220
1355
  return {
1221
1356
  transition_id: transitionId,
1357
+ executing,
1222
1358
  step_count: normalizedSequence.length,
1223
1359
  total_duration_ms: totalDuration,
1224
1360
  steps: normalizedSequence.map((step, i) => ({
@@ -1228,7 +1364,7 @@ export class MCPServer {
1228
1364
  easing: step.easing,
1229
1365
  delay: step.delay
1230
1366
  })),
1231
- load_code: `const animator = new TransitionAnimator(\n (n, v) => engine.setParameter(n, v),\n (n) => engine.getParameter(n)\n);\nanimator.sequence(${JSON.stringify(normalizedSequence)});`,
1367
+ load_code: executing ? null : `const animator = new TransitionAnimator(\n (n, v) => engine.setParameter(n, v),\n (n) => engine.getParameter(n)\n);\nanimator.sequence(${JSON.stringify(normalizedSequence)});`,
1232
1368
  suggested_next_actions: ['describe_visual_state', 'create_timeline', 'save_to_gallery']
1233
1369
  };
1234
1370
  }
@@ -1237,55 +1373,41 @@ export class MCPServer {
1237
1373
  * Apply a named color preset
1238
1374
  */
1239
1375
  applyColorPreset(args) {
1240
- const { preset } = args;
1376
+ const { preset, transition = true, duration = 800 } = args;
1241
1377
 
1242
- // Color preset hue/saturation mappings (subset — full list in ColorPresetsSystem)
1243
- const COLOR_PRESETS = {
1244
- Ocean: { hue: 200, saturation: 0.8, intensity: 0.6 },
1245
- Lava: { hue: 15, saturation: 0.9, intensity: 0.8 },
1246
- Neon: { hue: 300, saturation: 1.0, intensity: 0.9 },
1247
- Monochrome: { hue: 0, saturation: 0.0, intensity: 0.6 },
1248
- Sunset: { hue: 30, saturation: 0.85, intensity: 0.7 },
1249
- Aurora: { hue: 140, saturation: 0.7, intensity: 0.6 },
1250
- Cyberpunk: { hue: 280, saturation: 0.9, intensity: 0.8 },
1251
- Forest: { hue: 120, saturation: 0.6, intensity: 0.5 },
1252
- Desert: { hue: 40, saturation: 0.5, intensity: 0.7 },
1253
- Galaxy: { hue: 260, saturation: 0.8, intensity: 0.4 },
1254
- Ice: { hue: 190, saturation: 0.5, intensity: 0.8 },
1255
- Fire: { hue: 10, saturation: 1.0, intensity: 0.9 },
1256
- Toxic: { hue: 100, saturation: 0.9, intensity: 0.7 },
1257
- Royal: { hue: 270, saturation: 0.7, intensity: 0.5 },
1258
- Pastel: { hue: 330, saturation: 0.3, intensity: 0.8 },
1259
- Retro: { hue: 50, saturation: 0.7, intensity: 0.6 },
1260
- Midnight: { hue: 240, saturation: 0.6, intensity: 0.3 },
1261
- Tropical: { hue: 160, saturation: 0.8, intensity: 0.7 },
1262
- Ethereal: { hue: 220, saturation: 0.4, intensity: 0.7 },
1263
- Volcanic: { hue: 5, saturation: 0.95, intensity: 0.6 },
1264
- Holographic: { hue: 180, saturation: 0.6, intensity: 0.8 },
1265
- Vaporwave: { hue: 310, saturation: 0.7, intensity: 0.7 }
1266
- };
1378
+ const colorSystem = this._getColorPresets();
1379
+
1380
+ if (colorSystem) {
1381
+ // Use real ColorPresetsSystem full preset library with transitions
1382
+ const config = colorSystem.getPreset(preset);
1383
+ if (!config) {
1384
+ const allPresets = colorSystem.getPresets().map(p => p.name);
1385
+ return {
1386
+ error: {
1387
+ type: 'ValidationError',
1388
+ code: 'INVALID_COLOR_PRESET',
1389
+ message: `Unknown color preset: ${preset}`,
1390
+ valid_options: allPresets
1391
+ }
1392
+ };
1393
+ }
1394
+
1395
+ colorSystem.applyPreset(preset, transition, duration);
1267
1396
 
1268
- const presetData = COLOR_PRESETS[preset];
1269
- if (!presetData) {
1270
1397
  return {
1271
- error: {
1272
- type: 'ValidationError',
1273
- code: 'INVALID_COLOR_PRESET',
1274
- message: `Unknown color preset: ${preset}`,
1275
- valid_options: Object.keys(COLOR_PRESETS)
1276
- }
1398
+ preset,
1399
+ applied: { hue: config.hue, saturation: config.saturation, intensity: config.intensity },
1400
+ transition: transition ? { enabled: true, duration } : { enabled: false },
1401
+ full_config: config,
1402
+ suggested_next_actions: ['set_post_processing', 'describe_visual_state', 'set_visual_parameters']
1277
1403
  };
1278
1404
  }
1279
1405
 
1280
- if (this.engine) {
1281
- this.engine.setParameter('hue', presetData.hue);
1282
- this.engine.setParameter('saturation', presetData.saturation);
1283
- this.engine.setParameter('intensity', presetData.intensity);
1284
- }
1285
-
1406
+ // Fallback: no engine, return preset metadata for artifact mode
1286
1407
  return {
1287
1408
  preset,
1288
- applied: presetData,
1409
+ applied: null,
1410
+ load_code: `const colors = new ColorPresetsSystem((n, v) => engine.setParameter(n, v));\ncolors.applyPreset('${preset}', ${transition}, ${duration});`,
1289
1411
  suggested_next_actions: ['set_post_processing', 'describe_visual_state', 'set_visual_parameters']
1290
1412
  };
1291
1413
  }
@@ -1296,14 +1418,50 @@ export class MCPServer {
1296
1418
  setPostProcessing(args) {
1297
1419
  const { effects, chain_preset, clear_first = true } = args;
1298
1420
 
1421
+ // Try to execute live in browser context
1422
+ let executing = false;
1423
+ if (typeof document !== 'undefined') {
1424
+ try {
1425
+ const target = document.getElementById('viz-container')
1426
+ || document.querySelector('.vib3-container')
1427
+ || document.querySelector('canvas')?.parentElement;
1428
+
1429
+ if (target) {
1430
+ // Lazy-init pipeline, importing dynamically to avoid Node.js issues
1431
+ if (!this._postPipeline) {
1432
+ // PostProcessingPipeline imported statically would fail in Node;
1433
+ // it's already a known browser-only module, so guard at runtime
1434
+ const { PostProcessingPipeline: PPP } = { PostProcessingPipeline: globalThis.PostProcessingPipeline };
1435
+ if (PPP) {
1436
+ this._postPipeline = new PPP(target);
1437
+ }
1438
+ }
1439
+
1440
+ if (this._postPipeline) {
1441
+ if (clear_first) this._postPipeline.clearChain?.();
1442
+ if (chain_preset) {
1443
+ this._postPipeline.loadPresetChain(chain_preset);
1444
+ } else if (effects) {
1445
+ for (const e of effects) {
1446
+ this._postPipeline.addEffect(e.name, { intensity: e.intensity || 0.5, ...e });
1447
+ }
1448
+ }
1449
+ this._postPipeline.apply();
1450
+ executing = true;
1451
+ }
1452
+ }
1453
+ } catch { /* fall through to code generation */ }
1454
+ }
1455
+
1299
1456
  return {
1300
1457
  applied: true,
1458
+ executing,
1301
1459
  effects: effects || [],
1302
1460
  chain_preset: chain_preset || null,
1303
1461
  cleared_previous: clear_first,
1304
- load_code: effects ?
1305
- `const pipeline = new PostProcessingPipeline(gl, canvas);\n${effects.map(e => `pipeline.addEffect('${e.name}', { intensity: ${e.intensity || 0.5} });`).join('\n')}` :
1306
- `pipeline.applyChain('${chain_preset}');`,
1462
+ load_code: executing ? null : (effects ?
1463
+ `const pipeline = new PostProcessingPipeline(document.getElementById('viz-container'));\n${effects.map(e => `pipeline.addEffect('${e.name}', { intensity: ${e.intensity || 0.5} });`).join('\n')}\npipeline.apply();` :
1464
+ `const pipeline = new PostProcessingPipeline(document.getElementById('viz-container'));\npipeline.loadPresetChain('${chain_preset}');\npipeline.apply();`),
1307
1465
  suggested_next_actions: ['describe_visual_state', 'apply_color_preset', 'create_choreography']
1308
1466
  };
1309
1467
  }