Modular SDK for integrating the Atletica R8 Power Rack configurator into your application. Each module is self-contained and can be used independently.
<script src="atletica-sdk.js"></script>
const sdk = AtleticaSDK.init({ containerId: 'player' });
await sdk.threekit.loadPlayer();
sdk.configurator.setAttribute('crossbar', '78');
// Access configuration data const config = AtleticaSDK.getConfig(); // Get all attributes const attrs = config.attributes; // Get rack size definitions const rackSizes = config.rackSizes; // Get validation rules const rules = config.rules; // Get Threekit mapping const mapping = config.threekit.attributeMap;
// Initialize the 3D player await sdk.threekit.loadPlayer('#player-container'); // Get discovered attributes const tkAttrs = sdk.threekit.getDiscoveredAttributes(); // Sync current configuration to 3D model await sdk.threekit.syncConfiguration(currentState); // Resolve a text value to Threekit format const resolved = sdk.threekit.resolveValue('Uprights_Color', 'Black'); // → { assetId: "abc-123-..." } // Take a snapshot const imageUrl = await sdk.threekit.snapshot({ width: 1920, height: 1080 }); // Get the player instance for advanced usage const player = sdk.threekit.getPlayer(); const configurator = sdk.threekit.getConfigurator(); // Listen for Threekit events sdk.threekit.on('playerReady', () => console.log('Player loaded!')); sdk.threekit.on('attributesDiscovered', (attrs) => { /* ... */ });
// Set an attribute value sdk.configurator.setAttribute('crossbar', '78'); // Toggle an attribute (Yes/No, 0/1) sdk.configurator.toggleAttribute('smith_machine'); // Step a stepper attribute (respects step size, e.g. step:2 for cable pairs) sdk.configurator.stepAttribute('weight_stack_amount', +1); // → jumps by 2 (0→2→4) because weight stacks come in pairs // Get current state const state = sdk.configurator.getState(); // → { crossbar: '78', uprights_color: 'Black', ... } // Get current rack definition const rackDef = sdk.configurator.getCurrentRackDef(); // → { id: '2x2', uprights: 4, crossbarPositions: [...], ... } // Check if an attribute is available for current rack const available = sdk.configurator.isAttributeAvailable('j_hooks_type3'); // → false (only on 2x3+) // Validate current configuration const errors = sdk.configurator.validate(); // → [] or [{message: '...', rule: '...'}] // Get max cable systems for current rack size const maxSystems = sdk.configurator.getMaxCableSystems(); // → 2 (for 2x2) or 4 (for 2x3/2x4) // Get the text value for Threekit (resolves stepper/option → text) const textVal = sdk.configurator.getTextValue('crossbar'); // → '78' // inactiveValue: returns 'N/A' when equipment is off const posVal = sdk.configurator.getTextValue('weight_stack1_position'); // → 'N/A' (when weight_stack_amount < 2) // → 'DCL2' (when weight_stack_amount ≥ 2) // Subscribe to state changes sdk.configurator.on('stateChange', (newState, changedKey) => { console.log('State updated:', changedKey, newState[changedKey]); }); sdk.configurator.on('validationError', (errors) => { errors.forEach(e => showToast(e.message)); });
// ═══════════════════════════════════════════════════════ // 1. POSITION PAIRING (L↔R Mirror) // ═══════════════════════════════════════════════════════ // When Slot 1 (left) is set, Slot 2 (right) auto-mirrors: // DCL2 ↔ DCR2, DCL3B ↔ DCR3B, DL2 ↔ DR2 // Applies to: Weight_Stack, Plate_Loaded // Mirror regex: pos.replace(/([D]C?)([LR])/, swap L↔R) // ═══════════════════════════════════════════════════════ // 2. CROSSBAR SIZE → POSITION HEIGHT FILTERING (NEW) // ═══════════════════════════════════════════════════════ // The crossbar size controls which height levels are valid: // 42cm → level 2 only // 78cm → levels 2, 3 // 108cm → levels 2, 3, 4 // Applies to ALL position attributes (WS, PL, Smith). // On 2x3, front and back crossbars can differ — front CB // controls front positions, back CB controls back positions. // Enforced in: _clampPositionsForCrossbar(), renderButtonGroup(). // ═══════════════════════════════════════════════════════ // 3. RACK-SIZE POSITION FILTERING (optionsByRack) // ═══════════════════════════════════════════════════════ // Different rack sizes offer different positions: // 2x2: DCL2/3/4, DCR2/3/4 (center only) // 2x3: + DCL2B/3B/4B, DCR2B/3B/4B (adds back row) // 2x4: DL2, DR2, DCL2, DCR2 (outer + inner) // Smith: 2x2→DC2/3/4, 2x3→+DC2B/3B/4B, 2x4→DC3 only // ═══════════════════════════════════════════════════════ // 4. RACK-SIZE MAX (rackSizeMax) // ═══════════════════════════════════════════════════════ // Weight Stack: 2x2=2, 2x3=2, 2x4=4 (supports 4 units) // Plate Loaded: 2x2=2, 2x3=2, 2x4=2 (always max 2) // Combined cable systems: 2x2=2, 2x3=4, 2x4=4 // ═══════════════════════════════════════════════════════ // 5. STEP SIZE // ═══════════════════════════════════════════════════════ // Cable systems increment by 2 (pairs): 0 → 2 → 4 // Other steppers increment by 1: 0 → 1 → 2 → 3 → 4 // ═══════════════════════════════════════════════════════ // 6. DEPENDENCY + INACTIVE VALUE (dependsOn) // ═══════════════════════════════════════════════════════ // Position attributes send N/A when their parent is off: // weight_stack1_position → dependsOn: weight_stack_amount ≥ 2 // smith_position → dependsOn: smith_machine = Yes // spotter_arms_position → dependsOn: spotter_arms = Yes // ═══════════════════════════════════════════════════════ // 7. FIXED POSITIONS // ═══════════════════════════════════════════════════════ // Spotter Arms, J-Hooks → always D1 (no user choice) // Weight Pins, Dip Station → always N/A (no position data) // ═══════════════════════════════════════════════════════ // 8. WEIGHT_SYSTEM AUTO-SWITCH // ═══════════════════════════════════════════════════════ // plate_loaded_amount > 0 → Weight_System = "Plate Loaded" // else → Weight_System = "95kg" // ═══════════════════════════════════════════════════════ // 9. 2x4 FOUR-UNIT POSITION LAYOUT // ═══════════════════════════════════════════════════════ // WS amount=2: WS1→DL2, WS2→DR2 (outer slots) // WS amount=4: WS1→DL2, WS2→DCL2, WS3→DCR2, WS4→DR2 // All 2x4 positions are fixed (single option per slot) // ═══════════════════════════════════════════════════════ // 10. MUTUAL EXCLUSION // ═══════════════════════════════════════════════════════ // J-Hooks Type 1 and Type 4 cannot both be active // ═══════════════════════════════════════════════════════ // 11. WS ↔ PL MUTUAL EXCLUSIVITY // ═══════════════════════════════════════════════════════ // Weight Stacks and Plate Loaded are NEVER both active. // They represent different cable system types occupying // the same physical positions on the rack. // Excel data: 0 rows across ALL sheets have both WS>0 AND PL>0. // Enforced in: stepAttribute() auto-resets the other to 0. // ═══════════════════════════════════════════════════════ // 12. SMITH ↔ CABLE POSITION CONFLICT // ═══════════════════════════════════════════════════════ // Cable systems cannot occupy the same slot (height + bay) // as the Smith Machine. The slot = height number + B suffix. // Smith=DC2 → exclude DCL2/DCR2 from cable options // Smith=DC3 → exclude DCL3/DCR3 // Smith=DC2B → exclude DCL2B/DCR2B (2x3 back bay) // Slot extraction: pos.match(/(\d+B?)$/) → "2", "3B", etc. // Enforced in: _applyAutoRules(), validate(), UI filtering. // ═══════════════════════════════════════════════════════ // All rules are enforced in ConfiguratorEngine: // - setAttribute() → position pairing // - stepAttribute() → rackSizeMax, step, combined max, WS/PL exclusivity // - _applyAutoRules() → Weight_System, 2x4 positions, smith conflict, CB height // - _clampPositionsForCrossbar() → crossbar height filtering // - getTextValue() → inactiveValue, optionsByRack // - validate() → all CONFIG.rules (incl. position_conflict, crossbar_height) // ═══════════════════════════════════════════════════════
// ═══ CONFIGURATOR EVENTS ═══ sdk.on('stateChange', (state, changedKey) => { }); sdk.on('validationError', (errors) => { }); sdk.on('rackSizeChange', (newRackDef) => { }); // ═══ THREEKIT EVENTS ═══ sdk.on('playerLoading', () => { }); sdk.on('playerReady', () => { }); sdk.on('playerError', (error) => { }); sdk.on('attributesDiscovered', (attrs) => { }); sdk.on('configurationSynced', (tkConfig) => { }); // ═══ UNSUBSCRIBE ═══ const handler = (state) => console.log(state); sdk.on('stateChange', handler); sdk.off('stateChange', handler);
// Render a button group for an attribute AtleticaUI.renderButtonGroup('crossbar', document.getElementById('crossbar-el')); // Render color swatches AtleticaUI.renderColorSwatches('uprights_color', container); // Render a stepper control AtleticaUI.renderStepper('weight_stack_amount', container); // Render toggle cards AtleticaUI.renderToggle('smith_machine', container); // Render configuration summary AtleticaUI.renderSummary(container); // Render technical specs AtleticaUI.renderSpecs(container); // Auto-render all configurator controls AtleticaUI.renderAll(document.getElementById('config-panel'));
// Create a new chat session. Returns a session_id to use in subsequent calls. const res = await fetch('/api/atletica/atlas/sessions', { method: 'POST' }); const { session_id } = await res.json(); // Response: // { "session_id": "atlas_m3k9x2_a7b4c1" }
// Send a user message. Returns an SSE stream with text deltas, // tool activity events, configuration updates, and a done event. const res = await fetch('/api/atletica/atlas/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_id: 'atlas_m3k9x2_a7b4c1', message: 'I want to build muscle', configurator_state: { rack_size: '2x2', crossbar: '108', uprights_color: 'Black', // ... all current attribute values _rackSize: '2x2', _crossbar: '108', _attributesMeta: { /* per-attribute type, options, min/max */ } } }) }); // SSE Events: // event: delta → { "text": "chunk of text" } // event: tool_activity → { "tools": ["update_configuration"] } // event: updates → [{ "attribute": "weight_stack_amount", "value": "2" }] // event: done → { "session_id": "...", "message": "full text", "updates": [...] } // event: error → { "error": "description" }
// GET /api/atletica/atlas/system-prompt — Read current prompt // PUT /api/atletica/atlas/system-prompt — Update prompt { "prompt": "..." } // DELETE /api/atletica/atlas/system-prompt — Reset to default // Read const { prompt, is_custom } = await ( await fetch('/api/atletica/atlas/system-prompt') ).json(); // Update await fetch('/api/atletica/atlas/system-prompt', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'You are ATLAS...' }) }); // Reset to default await fetch('/api/atletica/atlas/system-prompt', { method: 'DELETE' });
// DELETE /api/atletica/atlas/sessions/:id — End a session await fetch('/api/atletica/atlas/sessions/atlas_m3k9x2_a7b4c1', { method: 'DELETE' }); // GET /api/atletica/atlas/health — Service health check const health = await ( await fetch('/api/atletica/atlas/health') ).json(); // { "status": "ok", "active_sessions": 3, "api_key_configured": true }