cyclecad 0.9.7 → 1.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/app/index.html CHANGED
@@ -1419,7 +1419,7 @@
1419
1419
  <span class="splash-logo-cycle">cycle</span><span class="splash-logo-cad">CAD</span>
1420
1420
  </div>
1421
1421
  <p class="splash-subtitle">Parametric 3D CAD Modeler for the Mechanical Designer</p>
1422
- <p style="display:inline-block; color:#0066cc; font-size:1rem; margin:12px 0 0 0; letter-spacing:2px; font-family:monospace; font-weight:700; background:rgba(0,102,204,0.08); border:1.5px solid rgba(0,102,204,0.25); border-radius:8px; padding:5px 20px;">v0.9.6</p>
1422
+ <p style="display:inline-block; color:#0066cc; font-size:1rem; margin:12px 0 0 0; letter-spacing:2px; font-family:monospace; font-weight:700; background:rgba(0,102,204,0.08); border:1.5px solid rgba(0,102,204,0.25); border-radius:8px; padding:5px 20px;">v1.0.1</p>
1423
1423
  </div>
1424
1424
  <div class="splash-options">
1425
1425
  <button class="splash-button splash-button-primary" id="btn-empty-project" style="grid-column: 1 / -1;">
@@ -4396,35 +4396,305 @@
4396
4396
  if (panel) { panel.style.display = panel.style.display === 'none' ? 'flex' : 'none'; return; }
4397
4397
  panel = document.createElement('div');
4398
4398
  panel.id = 'help-tutorials-panel';
4399
- panel.style.cssText = 'position:fixed;top:60px;left:50%;transform:translateX(-50%);width:680px;max-height:85vh;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:10px;z-index:600;display:flex;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,0.6);';
4399
+ panel.style.cssText = 'position:fixed;top:0;left:0;width:100vw;height:100vh;background:var(--bg-secondary);z-index:600;display:flex;flex-direction:column;';
4400
+
4401
+ // Tutorial step definitions — each step has title, detailed instructions, command, and category
4402
+ const _tutDefs = {
4403
+ basic: [
4404
+ {t:'Create a Box',d:'We start by creating a rectangular box. The AI Chat understands natural language — type dimensions like "80x40x25" and it builds the 3D geometry instantly. Watch the viewport on the right as the box appears.',cmd:'chat:box 80x40x25',cat:'Create'},
4405
+ {t:'Add a Cylinder',d:'Now we add a cylinder on top. In cycleCAD, you can stack multiple shapes to build complex parts. The cylinder will appear at the origin — you can move it later with the Move tool (keyboard shortcut: T).',cmd:'chat:cylinder 20mm radius 15mm tall',cat:'Create'},
4406
+ {t:'Add a Sphere',d:'Spheres are useful for rounded caps, ball joints, or decorative elements. Notice how each shape gets added to the Model Tree on the left panel — you can click any item in the tree to select it.',cmd:'chat:sphere 15mm radius',cat:'Create'},
4407
+ {t:'Toggle Wireframe View',d:'Press W to toggle wireframe mode. This shows the mesh edges of all geometry, letting you inspect the triangulation. Wireframe is great for checking mesh quality before 3D printing or CNC machining.',cmd:'key:W',cat:'View'},
4408
+ {t:'Toggle Wireframe Off',d:'Press W again to return to solid shading. cycleCAD uses Three.js PBR (Physically Based Rendering) with metalness and roughness maps for realistic material appearance.',cmd:'key:W',cat:'View'},
4409
+ {t:'Show Grid Floor',d:'The grid provides a reference plane at Y=0. It auto-sizes to your model and helps with spatial orientation. Useful when positioning parts or checking alignment.',cmd:'key:G',cat:'View'},
4410
+ {t:'Fit Everything in View',d:'Press V to auto-zoom the camera so all geometry fits in the viewport. The camera smoothly animates to the optimal position. Use this whenever you lose track of your model.',cmd:'key:V',cat:'View'},
4411
+ {t:'Create a Gear',d:'cycleCAD can generate parametric gears with accurate involute tooth profiles. Specify the pitch diameter and tooth count — the generator calculates module, pressure angle, and root/tip diameters automatically.',cmd:'chat:gear 30mm 12 teeth',cat:'Create'},
4412
+ {t:'Add a Spring',d:'Helical springs are generated with precise coil geometry. Specify radius and height — the generator creates a helix path and sweeps a circular profile along it. Great for mechanical assemblies.',cmd:'chat:spring 8mm radius 25mm tall',cat:'Create'},
4413
+ {t:'Reset View',d:'Press Escape to cancel any active operation and return to the default selection mode. This clears any active tool (measure, annotate, section cut) and deselects all parts.',cmd:'key:Escape',cat:'View'}
4414
+ ],
4415
+ intermediate: [
4416
+ {t:'Build a Bracket Base',d:'Start with a flat rectangular plate that will serve as the mounting bracket. 100mm wide, 60mm deep, 10mm thick. This is a common starting shape for machined brackets.',cmd:'chat:box 100x60x10',cat:'Model'},
4417
+ {t:'Add a Mounting Boss',d:'A cylindrical boss rises from the bracket surface. In real manufacturing, this would be a turned feature or a cast boss. 12mm radius, 30mm tall — sized for an M10 bolt clearance hole.',cmd:'chat:cylinder 12mm radius 30mm tall',cat:'Model'},
4418
+ {t:'Add a Stiffening Rib',d:'Ribs prevent flexing under load. This 10x30x60mm plate acts as a vertical stiffener. In injection molding, ribs should be 60% of wall thickness to avoid sink marks.',cmd:'chat:box 10x30x60',cat:'Model'},
4419
+ {t:'Create a Parametric Gear',d:'40mm pitch diameter with 18 teeth gives a module of 2.22mm. The tooth profile follows involute geometry — the same math used in real gear design. Tooth count affects load distribution and noise.',cmd:'chat:gear 40mm 18 teeth',cat:'Model'},
4420
+ {t:'Add a Return Spring',d:'This helical spring has 8mm coil radius and 25mm free height. In mechanism design, springs store energy and provide return force. Wire diameter and coil count determine spring rate.',cmd:'chat:spring 8mm radius 25mm tall',cat:'Model'},
4421
+ {t:'Inspect with Wireframe',d:'Toggle wireframe to see mesh density. High-poly areas (gears, springs) have more triangles for smooth curves. Low-poly areas (boxes) are minimal. Mesh density affects file size and render performance.',cmd:'key:W',cat:'Analyze'},
4422
+ {t:'Enable Grid Reference',d:'The grid shows the XZ ground plane. Parts sitting below the grid (negative Y) would be underground. Use this to verify your parts are correctly positioned for assembly.',cmd:'key:G',cat:'Analyze'},
4423
+ {t:'Activate Section Cut',d:'Press C to enable the section cut tool. This creates a clipping plane that slices through your model, revealing internal geometry. Drag the slider to move the cut plane. Essential for inspecting internal features.',cmd:'key:C',cat:'Analyze'},
4424
+ {t:'Measure Distances',d:'Press M to activate the measurement tool. Click two points in 3D space to measure the distance between them. Click a third point to also measure the angle. Measurements show in millimeters.',cmd:'key:M',cat:'Analyze'},
4425
+ {t:'Assembly Mode',d:'Click the Assembly button to switch to the assembly workspace. This mode lets you add components, define mate constraints (coincident, concentric, fixed), and create exploded views.',cmd:'click:[data-action="assembly"]',cat:'Assembly'},
4426
+ {t:'Explode the Assembly',d:'Press E to explode — all components spread outward from the center, showing how the assembly comes apart. This is the same technology that powers ExplodeView, our standalone 3D viewer.',cmd:'key:E',cat:'Assembly'},
4427
+ {t:'Fit All to Viewport',d:'After exploding, parts may be outside the view. Press V to refit the camera to show everything. The fit algorithm calculates the bounding box of all visible geometry.',cmd:'key:V',cat:'View'},
4428
+ {t:'Capture Screenshot',d:'Press S to capture a high-resolution PNG screenshot of the current viewport. The image is rendered at 2x resolution for retina displays. Great for documentation and presentations.',cmd:'key:S',cat:'Export'},
4429
+ {t:'View All Shortcuts',d:'Press ? to open the Help panel showing all 25+ keyboard shortcuts organized by category. This is the panel you are in right now — but in normal use, it is a quick reference overlay.',cmd:'key:?',cat:'Help'}
4430
+ ],
4431
+ advanced: [
4432
+ {t:'Build a Machine Base',d:'Start with a 50mm cube. In the Agent API, this would be: window.cycleCAD.execute({method:"shape.box", params:{width:50,depth:50,height:50}}). The Chat AI maps natural language to the same geometry engine.',cmd:'chat:box 50x50x50',cat:'Agent API'},
4433
+ {t:'Add a Shaft via Agent',d:'A 25mm radius, 60mm tall cylinder. The Agent API supports 55 commands across 10 namespaces: shape.*, feature.*, assembly.*, render.*, validate.*, token.*, export.*, import.*, scene.*, config.*.',cmd:'chat:cylinder 25mm radius 60mm tall',cat:'Agent API'},
4434
+ {t:'Create a Bearing Housing',d:'This sphere represents a ball bearing housing. Real agents use compound commands — create sphere, then shell it (hollow out), then add mounting holes. The API supports chaining.',cmd:'chat:sphere 30mm radius',cat:'Agent API'},
4435
+ {t:'Generate Drive Gear',d:'50mm, 24 teeth. The Agent API returns JSON with the mesh data, bounding box, and mass properties. AI agents can use this data for automated design review and optimization loops.',cmd:'chat:gear 50mm 24 teeth',cat:'Agent API'},
4436
+ {t:'Clear Active Tools',d:'Escape resets the app state. In agent mode, this maps to scene.reset(). The API tracks undo history — every operation pushes a state snapshot that agents can roll back with scene.undo().',cmd:'key:Escape',cat:'Agent API'},
4437
+ {t:'Token System',d:'The $CYCLE token engine tracks usage per operation. Creating a box costs 50 tokens, AI analysis costs 200 tokens. Free tier gets 10,000 tokens/month. Pro tier is unlimited at EUR 49/month.',cmd:'click:#token-btn',cat:'Tokens'},
4438
+ {t:'Model Marketplace',d:'Browse and publish parametric models. Creators earn 70-90% royalties in $CYCLE tokens. 7 access tiers from Free to Enterprise. Every model has a live 3D preview and version history.',cmd:'click:#marketplace-btn',cat:'Marketplace'},
4439
+ {t:'Feature Tree',d:'The Model Tree shows every operation in chronological order. Click to select, right-click to rename/suppress/delete. Suppressed features are greyed out but preserved — toggle them back anytime.',cmd:'click:.tree-tab',cat:'Platform'},
4440
+ {t:'Theme Toggle',d:'Switch between dark (default) and light themes. The theme affects the entire UI including the 3D viewport background. Dark theme reduces eye strain; light theme is better for screenshots.',cmd:'click:#theme-toggle',cat:'Settings'},
4441
+ {t:'Performance Monitor',d:'Ctrl+Shift+F shows real-time FPS, memory usage, triangle count, and draw calls. Useful for optimizing complex models. Target: 60 FPS with <100K triangles for smooth interaction.',cmd:'key:Control+Shift+F',cat:'Debug'}
4442
+ ],
4443
+ explodeview: [
4444
+ {t:'Create Box Component',d:'Build the first component — an 80x40x25mm rectangular body. In ExplodeView, each mesh in the scene becomes a selectable, hideable, exportable part with its own info card.',cmd:'chat:box 80x40x25',cat:'Setup'},
4445
+ {t:'Add Cylinder Component',d:'Add a second component. When you switch to Viewer Mode, each distinct mesh becomes an independent part that can be selected, hidden, measured, and annotated individually.',cmd:'chat:cylinder 10mm radius 35mm tall',cat:'Setup'},
4446
+ {t:'Add Gear Component',d:'Third component completes our test assembly. ExplodeView supports up to 400+ parts with real-time interaction. The cycleWASH bike washing machine model has 399 components across 6 sub-assemblies.',cmd:'chat:gear 30mm 12 teeth',cat:'Setup'},
4447
+ {t:'Enter Viewer Mode',d:'Press V to switch from Modeler to Viewer mode. This activates ExplodeView — the same 3D viewer used on explodeview.com. All 40+ analysis tools become available in the toolbar.',cmd:'key:V',cat:'ExplodeView'},
4448
+ {t:'Explode the Assembly',d:'Press E to explode. Parts fly outward from the center along their radial direction. Use the slider to control explosion distance. This helps visualize how components fit together.',cmd:'key:E',cat:'ExplodeView'},
4449
+ {t:'Cross-Section Cut',d:'Press C to activate the section cut plane. Choose X, Y, or Z axis and drag the slider to move the cut position. The clipping plane uses per-material settings with double-sided rendering.',cmd:'key:C',cat:'ExplodeView'},
4450
+ {t:'Wireframe Overlay',d:'Press W to show wireframe edges over the solid model. This reveals the mesh structure — useful for verifying CAD quality before manufacturing or detecting degenerate triangles.',cmd:'key:W',cat:'ExplodeView'},
4451
+ {t:'Reference Grid',d:'Press G to toggle the ground plane grid. The grid auto-sizes based on model dimensions and includes a shadow plane for visual grounding. Grid spacing is in millimeters.',cmd:'key:G',cat:'ExplodeView'},
4452
+ {t:'Fit to Scene',d:'Press F to auto-fit the camera to show all visible geometry. Uses a FOV-based calculation: distance = maxDim / (2 * tan(fov/2)) with smooth animated transition.',cmd:'key:F',cat:'ExplodeView'},
4453
+ {t:'High-Res Screenshot',d:'Press S to capture a screenshot at 2x resolution. The renderer temporarily upscales, captures the canvas, then restores. Output is a PNG file ready for documentation.',cmd:'key:S',cat:'Export'},
4454
+ {t:'Distance Measurement',d:'Press M to start measuring. Click two points for distance, click a third for angle. The tool uses raycasting against all visible meshes to find exact 3D intersection points.',cmd:'key:M',cat:'Analyze'},
4455
+ {t:'Place Annotations',d:'Press A to enter annotation mode. Click any surface to place a pin with custom text. Pins track their 3D position and reproject to screen space in the render loop. Drag to reposition.',cmd:'key:A',cat:'Create'},
4456
+ {t:'Keyboard Reference',d:'Press ? to see all shortcuts. ExplodeView has 25+ shortcuts: T=translate, E=explode, R=reset, W=wireframe, S=screenshot, G=grid, H=hide, M=measure, C=section, A=annotate, F=fit.',cmd:'key:?',cat:'Help'},
4457
+ {t:'Blueprint Mode',d:'Blueprint theme switches to white background with blue wireframe overlay — mimicking traditional engineering drawings. Great for technical documentation and print-ready screenshots.',cmd:'click:#blueprint-btn',cat:'View'},
4458
+ {t:'Export Bill of Materials',d:'The BOM export generates a CSV with all parts: index, name, dimensions, bounding box, file size, and assembly membership. Import into Excel or ERP systems for procurement.',cmd:'click:#bom-btn',cat:'Export'}
4459
+ ],
4460
+ assembly: [
4461
+ {t:'Create Caliper Body',d:'The caliper housing is the main structural part — an 80x40x25mm aluminum block. In real brake calipers, this is typically cast or CNC machined from 6061-T6 aluminum for strength and light weight.',cmd:'chat:box 80x40x25',cat:'Build'},
4462
+ {t:'Add Mounting Holes',d:'Two M5 mounting holes (5mm radius, 20mm deep) attach the caliper to the fork. In manufacturing, these would be drilled and tapped. The through-holes allow bolt-on mounting with M5x20 socket heads.',cmd:'chat:cylinder 5mm radius 20mm tall',cat:'Build'},
4463
+ {t:'Create Brake Pad',d:'The friction pad is 60x30x8mm — a flat rectangular component that presses against the rotor. Real brake pads use sintered metal or organic compounds bonded to a steel backing plate.',cmd:'chat:box 60x30x8',cat:'Build'},
4464
+ {t:'Create Pivot Pin',d:'The pivot pin (8mm radius, 40mm long) is the axis around which the caliper arm swings. Made from hardened steel (HRC 58-62) for wear resistance. The pin is press-fit into the caliper body.',cmd:'chat:cylinder 8mm radius 40mm tall',cat:'Build'},
4465
+ {t:'Create Return Spring',d:'The helical spring (10mm radius, 30mm height) returns the caliper arm to the open position when brake pressure is released. Spring rate is calculated from wire diameter, coil count, and material (music wire).',cmd:'chat:spring 10mm radius 30mm tall',cat:'Build'},
4466
+ {t:'Create Adjustment Bolt',d:'The pad adjustment bolt (4mm radius, 25mm long) sets the resting gap between pad and rotor. Fine-pitch M8x1.0 thread allows precise adjustment in 1mm increments. Locknut prevents vibration loosening.',cmd:'chat:cylinder 4mm radius 25mm tall',cat:'Build'},
4467
+ {t:'Create Retaining Clip',d:'The E-clip (20x5x2mm) locks the pivot pin in place. In production, this is a stamped spring steel clip (DIN 6799). The clip snaps into a groove machined in the pin.',cmd:'chat:box 20x5x2',cat:'Build'},
4468
+ {t:'Create Drive Gear',d:'The final component — a 30mm gear with 12 teeth for the cable-actuated brake mechanism. The gear converts cable pull into caliper squeeze force. Module 2.5, pressure angle 20 degrees.',cmd:'chat:gear 30mm 12 teeth',cat:'Build'},
4469
+ {t:'Switch to ExplodeView',d:'Now that all 8 components are built, switch to Viewer Mode (press V). The modeler hands off the entire scene to ExplodeView — every mesh becomes an interactive, selectable part.',cmd:'key:V',cat:'Inspect'},
4470
+ {t:'Explode the Assembly',d:'Press E to see how all brake caliper parts separate. The explosion reveals the assembly order — body first, then pin, spring, pad, bolt, clip, and gear. Each part moves along its radial vector.',cmd:'key:E',cat:'Inspect'},
4471
+ {t:'Section Through Center',d:'Press C to slice the assembly with a section plane. Move the slider to cut through the caliper body and reveal the internal spring cavity, bolt channel, and pin bore.',cmd:'key:C',cat:'Inspect'},
4472
+ {t:'Wireframe Analysis',d:'Toggle wireframe (W) to inspect mesh quality. The gear teeth should have smooth involute curves. The spring coils should be uniform. Check for degenerate triangles or mesh artifacts.',cmd:'key:W',cat:'Inspect'},
4473
+ {t:'Show Ground Plane',d:'Enable the grid (G) to see the assembly orientation relative to the ground. The caliper should sit upright — if parts are below the grid, they need repositioning.',cmd:'key:G',cat:'Inspect'},
4474
+ {t:'Fit All Components',d:'After exploding, press F to refit the camera. The algorithm finds the bounding box of all 8 components (including their exploded positions) and frames them with 10% padding.',cmd:'key:F',cat:'Inspect'},
4475
+ {t:'Capture Assembly Photo',d:'Take a screenshot (S) of the exploded view. This is the classic exploded-view diagram used in repair manuals, assembly instructions, and patent drawings.',cmd:'key:S',cat:'Document'},
4476
+ {t:'Measure Bolt Length',d:'Use the measure tool (M) to verify the adjustment bolt length. Click the bolt top and bottom — the displayed distance should read approximately 50mm (25mm radius * 2).',cmd:'key:M',cat:'Document'},
4477
+ {t:'Annotate Components',d:'Place annotation pins (A) on each component — label them: "1. Body", "2. Pad", "3. Pin", "4. Spring", etc. These annotations export with screenshots for documentation.',cmd:'key:A',cat:'Document'},
4478
+ {t:'View All Shortcuts',d:'Press ? for the complete keyboard shortcut reference. You have now completed the full Assembly + ExplodeView tutorial — from CAD modeling to assembly inspection to documentation.',cmd:'key:?',cat:'Document'}
4479
+ ],
4480
+ testCycleCAD: [{t:'Run cycleCAD Tests',d:'115 automated UI tests with live visualization',cmd:'iframe:./test-agent-v2.html',cat:'Test Agent'}],
4481
+ testExplodeView: [{t:'Run ExplodeView Tests',d:'100+ tests for 3D viewer, panels, shortcuts',cmd:'iframe:./explodeview-test-agent.html',cat:'Test Agent'}]
4482
+ };
4483
+
4484
+ // Helper to run a step command against the live app
4485
+ window._htRunStep = function(cmd) {
4486
+ try {
4487
+ if (cmd.startsWith('chat:')) {
4488
+ const text = cmd.slice(5);
4489
+ const chatInput = document.querySelector('#chat-input') || document.querySelector('input[placeholder*="hat"]') || document.querySelector('textarea[placeholder*="hat"]');
4490
+ if (chatInput) { chatInput.value = text; chatInput.dispatchEvent(new Event('input',{bubbles:true})); const btn = chatInput.closest('div')?.querySelector('button') || document.querySelector('#chat-send'); if(btn) btn.click(); }
4491
+ } else if (cmd.startsWith('key:')) {
4492
+ const k = cmd.slice(4);
4493
+ if (k.includes('+')) { const parts = k.split('+'); document.dispatchEvent(new KeyboardEvent('keydown',{key:parts[parts.length-1],ctrlKey:parts.includes('Control'),shiftKey:parts.includes('Shift'),bubbles:true})); }
4494
+ else { document.dispatchEvent(new KeyboardEvent('keydown',{key:k,bubbles:true})); }
4495
+ } else if (cmd.startsWith('click:')) {
4496
+ const el = document.querySelector(cmd.slice(6)); if(el) el.click();
4497
+ }
4498
+ } catch(e) { console.warn('[HelpRunner]', e); }
4499
+ };
4500
+
4501
+ // Build the runner view — panel becomes left sidebar, 3D viewport visible on right
4502
+ window._htShowRunner = function(key, title) {
4503
+ const steps = _tutDefs[key];
4504
+ if (!steps) return;
4505
+ const panel = document.getElementById('help-tutorials-panel');
4506
+ const runnerArea = document.getElementById('ht-runner-area');
4507
+ const homeArea = document.getElementById('ht-home-area');
4508
+ homeArea.style.display = 'none';
4509
+ runnerArea.style.display = 'flex';
4510
+
4511
+ // Check if it's a test agent (loads iframe) — stays full screen
4512
+ if (steps.length === 1 && steps[0].cmd.startsWith('iframe:')) {
4513
+ runnerArea.innerHTML = '<div style="display:flex;flex-direction:column;width:100%;height:100%;"><div style="padding:8px 14px;background:var(--bg-tertiary);border-bottom:1px solid var(--border-color);display:flex;align-items:center;gap:10px;"><button onclick="_htBackHome()" style="padding:4px 10px;border-radius:4px;border:1px solid var(--border-color);background:var(--bg-secondary);color:var(--text-primary);cursor:pointer;font-size:12px;">← Back</button><span style="font-weight:600;font-size:14px;">'+title+'</span></div><iframe src="'+steps[0].cmd.slice(7)+'" style="flex:1;border:none;width:100%;"></iframe></div>';
4514
+ return;
4515
+ }
4516
+
4517
+ // SPLIT MODE: panel becomes left sidebar, 3D viewport visible on the right
4518
+ panel.style.width = '420px';
4519
+ panel.style.borderRight = '2px solid var(--accent-blue)';
4520
+ panel.style.boxShadow = '4px 0 20px rgba(0,0,0,0.3)';
4521
+
4522
+ let completed = new Set();
4523
+ let isRunning = false;
4524
+ let stopFlag = false;
4525
+ let activeStep = -1;
4526
+
4527
+ runnerArea.innerHTML = `
4528
+ <div style="display:flex;flex-direction:column;height:100%;">
4529
+ <div style="padding:10px 14px;background:var(--bg-tertiary);border-bottom:1px solid var(--border-color);display:flex;align-items:center;gap:8px;flex-shrink:0;">
4530
+ <button onclick="_htBackHome()" style="padding:4px 10px;border-radius:4px;border:1px solid var(--border-color);background:var(--bg-secondary);color:var(--text-primary);cursor:pointer;font-size:12px;">← Back</button>
4531
+ <span style="font-weight:600;font-size:13px;flex:1;">${title}</span>
4532
+ <button id="ht-run-all" style="padding:5px 14px;border-radius:4px;border:1px solid rgba(88,166,255,0.4);background:rgba(88,166,255,0.15);color:#58a6ff;cursor:pointer;font-size:11px;font-weight:600;">▶ Run All</button>
4533
+ <button id="ht-stop" style="padding:5px 14px;border-radius:4px;border:1px solid rgba(248,81,73,0.4);background:rgba(248,81,73,0.15);color:#f85149;cursor:pointer;font-size:11px;font-weight:600;display:none;">Stop</button>
4534
+ </div>
4535
+ <div style="padding:6px 14px;background:var(--bg-tertiary);border-bottom:1px solid var(--border-color);flex-shrink:0;">
4536
+ <div style="height:5px;background:var(--bg-primary);border-radius:3px;overflow:hidden;"><div id="ht-progress" style="height:100%;width:0%;background:linear-gradient(90deg,#58a6ff,#3fb950);transition:width 0.4s;"></div></div>
4537
+ <div id="ht-step-counter" style="font-size:10px;color:var(--text-secondary);margin-top:4px;">0 / ${steps.length} steps completed</div>
4538
+ </div>
4539
+ <div id="ht-step-list" style="flex:1;overflow-y:auto;padding:10px;min-height:0;"></div>
4540
+ </div>
4541
+ `;
4542
+
4543
+ // Render detailed step cards with instructions
4544
+ const stepList = document.getElementById('ht-step-list');
4545
+ steps.forEach((s, i) => {
4546
+ const card = document.createElement('div');
4547
+ card.id = 'ht-card-'+i;
4548
+ card.className = 'ht-step-card';
4549
+ card.style.cssText = 'padding:12px;margin-bottom:8px;border:1px solid var(--border-color);border-radius:8px;background:var(--bg-secondary);transition:all 0.3s;';
4550
+ card.innerHTML = '<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;">'
4551
+ +'<span id="ht-badge-'+i+'" style="width:26px;height:26px;border-radius:50%;background:#58a6ff;color:#fff;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;flex-shrink:0;">'+(i+1)+'</span>'
4552
+ +'<span style="font-size:13px;font-weight:600;flex:1;line-height:1.3;">'+s.t+'</span>'
4553
+ +'<span style="font-size:9px;padding:2px 8px;border-radius:10px;background:var(--bg-tertiary);color:var(--text-secondary);white-space:nowrap;">'+s.cat+'</span>'
4554
+ +'</div>'
4555
+ +'<div style="font-size:12px;color:var(--text-secondary);line-height:1.6;padding:0 0 10px 36px;">'+s.d+'</div>'
4556
+ +'<div style="padding-left:36px;display:flex;gap:8px;align-items:center;">'
4557
+ +'<button class="ht-run-btn" data-idx="'+i+'" style="padding:5px 16px;border-radius:5px;border:1px solid rgba(88,166,255,0.4);background:rgba(88,166,255,0.15);color:#58a6ff;cursor:pointer;font-size:11px;font-weight:600;">▶ Run Step</button>'
4558
+ +'<span class="ht-cmd-hint" style="font-size:10px;color:var(--text-secondary);font-family:monospace;opacity:0.6;">'+s.cmd.replace('chat:','AI: ').replace('key:','Key: ').replace('click:','Click: ')+'</span>'
4559
+ +'</div>';
4560
+ stepList.appendChild(card);
4561
+ });
4562
+
4563
+ function markDone(i) {
4564
+ completed.add(i);
4565
+ const card = document.getElementById('ht-card-'+i);
4566
+ const badge = document.getElementById('ht-badge-'+i);
4567
+ if(card) { card.style.borderColor='#238636'; card.style.background='rgba(35,134,54,0.08)'; }
4568
+ if(badge) { badge.style.background='#238636'; badge.textContent='✓'; }
4569
+ const btn = card?.querySelector('.ht-run-btn');
4570
+ if(btn) { btn.textContent='✓ Done'; btn.style.background='rgba(35,134,54,0.15)'; btn.style.borderColor='rgba(35,134,54,0.4)'; btn.style.color='#3fb950'; }
4571
+ document.getElementById('ht-progress').style.width = (completed.size/steps.length*100)+'%';
4572
+ document.getElementById('ht-step-counter').textContent = completed.size+' / '+steps.length+' steps completed';
4573
+ }
4574
+
4575
+ function highlightStep(i) {
4576
+ // Remove previous highlight
4577
+ document.querySelectorAll('.ht-step-card').forEach(c => {
4578
+ if(!completed.has(parseInt(c.id.replace('ht-card-','')))) {
4579
+ c.style.borderColor='var(--border-color)'; c.style.background='var(--bg-secondary)';
4580
+ }
4581
+ });
4582
+ const card = document.getElementById('ht-card-'+i);
4583
+ if(card && !completed.has(i)) { card.style.borderColor='#58a6ff'; card.style.background='rgba(88,166,255,0.08)'; card.style.boxShadow='0 0 12px rgba(88,166,255,0.15)'; }
4584
+ card?.scrollIntoView({behavior:'smooth',block:'center'});
4585
+ activeStep = i;
4586
+ }
4587
+
4588
+ async function runStep(i) {
4589
+ highlightStep(i);
4590
+ await new Promise(r=>setTimeout(r,300));
4591
+ window._htRunStep(steps[i].cmd);
4592
+ await new Promise(r=>setTimeout(r,1800));
4593
+ markDone(i);
4594
+ }
4595
+
4596
+ // Individual run buttons
4597
+ stepList.addEventListener('click', async (e) => {
4598
+ const btn = e.target.closest('.ht-run-btn');
4599
+ if (!btn || btn.textContent.includes('Done')) return;
4600
+ const idx = parseInt(btn.dataset.idx);
4601
+ btn.textContent = '⏳ Running...';
4602
+ btn.style.color = '#d29922';
4603
+ await runStep(idx);
4604
+ });
4605
+
4606
+ // Run All
4607
+ document.getElementById('ht-run-all').addEventListener('click', async () => {
4608
+ isRunning = true; stopFlag = false;
4609
+ document.getElementById('ht-run-all').style.display='none';
4610
+ document.getElementById('ht-stop').style.display='inline-block';
4611
+ for (let i=0; i<steps.length; i++) {
4612
+ if (stopFlag) break;
4613
+ if (completed.has(i)) continue;
4614
+ const btn = document.querySelector('#ht-card-'+i+' .ht-run-btn');
4615
+ if(btn) { btn.textContent='⏳ Running...'; btn.style.color='#d29922'; }
4616
+ await runStep(i);
4617
+ if (i < steps.length-1 && !stopFlag) await new Promise(r=>setTimeout(r,2000));
4618
+ }
4619
+ isRunning = false;
4620
+ document.getElementById('ht-run-all').style.display='inline-block';
4621
+ document.getElementById('ht-stop').style.display='none';
4622
+ });
4623
+
4624
+ // Stop
4625
+ document.getElementById('ht-stop').addEventListener('click', () => { stopFlag = true; });
4626
+ };
4627
+
4628
+ // Back to home from runner — restore full-width panel
4629
+ window._htBackHome = function() {
4630
+ const panel = document.getElementById('help-tutorials-panel');
4631
+ panel.style.width = '100vw';
4632
+ panel.style.borderRight = 'none';
4633
+ panel.style.boxShadow = 'none';
4634
+ document.getElementById('ht-runner-area').style.display='none';
4635
+ document.getElementById('ht-home-area').style.display='block';
4636
+ };
4637
+
4400
4638
  panel.innerHTML = `
4401
- <div style="display:flex;justify-content:space-between;align-items:center;padding:14px 18px;border-bottom:1px solid var(--border-color);background:var(--bg-tertiary);border-radius:10px 10px 0 0;">
4402
- <span style="font-weight:700;font-size:15px;">? Help & Tutorials</span>
4403
- <button onclick="document.getElementById('help-tutorials-panel').style.display='none'" style="width:30px;height:30px;display:flex;align-items:center;justify-content:center;background:rgba(255,80,80,0.15);border:1px solid rgba(255,80,80,0.3);color:#f88;cursor:pointer;font-size:16px;border-radius:6px;font-weight:700;">✕</button>
4404
- </div>
4405
- <div style="padding:10px 18px;border-bottom:1px solid var(--border-color);">
4406
- <input id="help-search-input" type="text" placeholder="Search features, shortcuts, tutorials..." style="width:100%;padding:8px 12px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:6px;color:var(--text-primary);font-size:13px;outline:none;box-sizing:border-box;">
4639
+ <div style="display:flex;justify-content:space-between;align-items:center;padding:12px 18px;border-bottom:1px solid var(--border-color);background:var(--bg-tertiary);flex-shrink:0;">
4640
+ <span style="font-weight:700;font-size:15px;">Help & Tutorials</span>
4641
+ <div style="display:flex;gap:8px;align-items:center;">
4642
+ <span style="font-size:11px;color:var(--text-secondary);">Press ? to toggle</span>
4643
+ <button onclick="document.getElementById('help-tutorials-panel').style.display='none'" style="width:28px;height:28px;display:flex;align-items:center;justify-content:center;background:rgba(255,80,80,0.15);border:1px solid rgba(255,80,80,0.3);color:#f88;cursor:pointer;font-size:14px;border-radius:6px;font-weight:700;">✕</button>
4644
+ </div>
4407
4645
  </div>
4408
- <div style="padding:18px;overflow-y:auto;flex:1;min-height:0;">
4409
- <!-- Tutorials Section -->
4646
+ <div id="ht-home-area" style="flex:1;overflow-y:auto;padding:18px;min-height:0;">
4647
+ <!-- Tutorials with integrated runners -->
4410
4648
  <div style="margin-bottom:20px;">
4411
- <h3 style="font-size:14px;font-weight:600;margin-bottom:12px;color:var(--accent-blue);">Tutorials</h3>
4649
+ <h3 style="font-size:14px;font-weight:600;margin-bottom:12px;color:var(--accent-blue);">Tutorials <span style="font-size:11px;font-weight:400;color:var(--text-secondary);">— click to run live</span></h3>
4412
4650
  <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;">
4413
- <a href="./tutorials/basic.html" style="text-decoration:none;display:block;padding:16px;background:linear-gradient(135deg,rgba(63,185,80,0.12),rgba(63,185,80,0.04));border:1px solid rgba(63,185,80,0.3);border-radius:8px;text-align:center;">
4414
- <div style="font-size:28px;margin-bottom:8px;">&#x1F331;</div>
4415
- <div style="font-weight:600;color:#3fb950;font-size:13px;">Basic</div>
4416
- <div style="font-size:11px;color:var(--text-secondary);margin-top:4px;">First 3D part, sketches, extrude, export</div>
4417
- </a>
4418
- <a href="./tutorials/intermediate.html" style="text-decoration:none;display:block;padding:16px;background:linear-gradient(135deg,rgba(88,166,255,0.12),rgba(88,166,255,0.04));border:1px solid rgba(88,166,255,0.3);border-radius:8px;text-align:center;">
4419
- <div style="font-size:28px;margin-bottom:8px;">&#x1F680;</div>
4420
- <div style="font-weight:600;color:#58a6ff;font-size:13px;">Intermediate</div>
4421
- <div style="font-size:11px;color:var(--text-secondary);margin-top:4px;">CAM, Text-to-CAD, assemblies, DFM</div>
4422
- </a>
4423
- <a href="./tutorials/advanced.html" style="text-decoration:none;display:block;padding:16px;background:linear-gradient(135deg,rgba(210,153,34,0.12),rgba(210,153,34,0.04));border:1px solid rgba(210,153,34,0.3);border-radius:8px;text-align:center;">
4424
- <div style="font-size:28px;margin-bottom:8px;">&#x26A1;</div>
4425
- <div style="font-weight:600;color:#d29922;font-size:13px;">Advanced</div>
4426
- <div style="font-size:11px;color:var(--text-secondary);margin-top:4px;">Agent API, MCP, tokens, Docker, plugins</div>
4427
- </a>
4651
+ <div onclick="_htShowRunner('basic','Basic Tutorial')" style="cursor:pointer;padding:14px;background:linear-gradient(135deg,rgba(63,185,80,0.12),rgba(63,185,80,0.04));border:1px solid rgba(63,185,80,0.3);border-radius:8px;text-align:center;transition:transform 0.15s;">
4652
+ <div style="font-size:24px;margin-bottom:6px;">🌱</div>
4653
+ <div style="font-weight:600;color:#3fb950;font-size:12px;">Basic</div>
4654
+ <div style="font-size:10px;color:var(--text-secondary);margin-top:3px;">Sketches, extrude, export</div>
4655
+ <div style="margin-top:6px;font-size:9px;padding:2px 8px;background:rgba(63,185,80,0.15);border-radius:10px;color:#3fb950;display:inline-block;">10 steps</div>
4656
+ </div>
4657
+ <div onclick="_htShowRunner('intermediate','Intermediate Tutorial')" style="cursor:pointer;padding:14px;background:linear-gradient(135deg,rgba(88,166,255,0.12),rgba(88,166,255,0.04));border:1px solid rgba(88,166,255,0.3);border-radius:8px;text-align:center;transition:transform 0.15s;">
4658
+ <div style="font-size:24px;margin-bottom:6px;">🚀</div>
4659
+ <div style="font-weight:600;color:#58a6ff;font-size:12px;">Intermediate</div>
4660
+ <div style="font-size:10px;color:var(--text-secondary);margin-top:3px;">CAM, assemblies, DFM</div>
4661
+ <div style="margin-top:6px;font-size:9px;padding:2px 8px;background:rgba(88,166,255,0.15);border-radius:10px;color:#58a6ff;display:inline-block;">14 steps</div>
4662
+ </div>
4663
+ <div onclick="_htShowRunner('advanced','Advanced Tutorial')" style="cursor:pointer;padding:14px;background:linear-gradient(135deg,rgba(210,153,34,0.12),rgba(210,153,34,0.04));border:1px solid rgba(210,153,34,0.3);border-radius:8px;text-align:center;transition:transform 0.15s;">
4664
+ <div style="font-size:24px;margin-bottom:6px;">⚡</div>
4665
+ <div style="font-weight:600;color:#d29922;font-size:12px;">Advanced</div>
4666
+ <div style="font-size:10px;color:var(--text-secondary);margin-top:3px;">Agent API, MCP, tokens</div>
4667
+ <div style="margin-top:6px;font-size:9px;padding:2px 8px;background:rgba(210,153,34,0.15);border-radius:10px;color:#d29922;display:inline-block;">10 steps</div>
4668
+ </div>
4669
+ <div onclick="_htShowRunner('explodeview','ExplodeView Tutorial')" style="cursor:pointer;padding:14px;background:linear-gradient(135deg,rgba(0,212,170,0.12),rgba(0,212,170,0.04));border:1px solid rgba(0,212,170,0.3);border-radius:8px;text-align:center;transition:transform 0.15s;">
4670
+ <div style="font-size:24px;margin-bottom:6px;">🔍</div>
4671
+ <div style="font-weight:600;color:#00d4aa;font-size:12px;">ExplodeView</div>
4672
+ <div style="font-size:10px;color:var(--text-secondary);margin-top:3px;">Viewer, section cuts, BOM</div>
4673
+ <div style="margin-top:6px;font-size:9px;padding:2px 8px;background:rgba(0,212,170,0.15);border-radius:10px;color:#00d4aa;display:inline-block;">15 steps</div>
4674
+ </div>
4675
+ <div onclick="_htShowRunner('assembly','Assembly + ExplodeView')" style="cursor:pointer;padding:14px;background:linear-gradient(135deg,rgba(248,81,73,0.12),rgba(248,81,73,0.04));border:1px solid rgba(248,81,73,0.3);border-radius:8px;text-align:center;transition:transform 0.15s;">
4676
+ <div style="font-size:24px;margin-bottom:6px;">🔩</div>
4677
+ <div style="font-weight:600;color:#f85149;font-size:12px;">Assembly + Viewer</div>
4678
+ <div style="font-size:10px;color:var(--text-secondary);margin-top:3px;">Build then explore all 47 features</div>
4679
+ <div style="margin-top:6px;font-size:9px;padding:2px 8px;background:rgba(248,81,73,0.15);border-radius:10px;color:#f85149;display:inline-block;">18 steps</div>
4680
+ </div>
4681
+ </div>
4682
+ </div>
4683
+
4684
+ <!-- Test Agents -->
4685
+ <div style="margin-bottom:20px;">
4686
+ <h3 style="font-size:14px;font-weight:600;margin-bottom:12px;color:var(--accent-blue);">Test Agents <span style="font-size:11px;font-weight:400;color:var(--text-secondary);">— automated UI testing</span></h3>
4687
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
4688
+ <div onclick="_htShowRunner('testCycleCAD','cycleCAD Test Agent')" style="cursor:pointer;padding:14px;background:linear-gradient(135deg,rgba(246,185,59,0.12),rgba(246,185,59,0.04));border:1px solid rgba(246,185,59,0.3);border-radius:8px;text-align:center;">
4689
+ <div style="font-size:24px;margin-bottom:6px;">🤖</div>
4690
+ <div style="font-weight:600;color:#f6b93b;font-size:12px;">cycleCAD Tests</div>
4691
+ <div style="font-size:10px;color:var(--text-secondary);margin-top:3px;">115 automated UI tests, live viz</div>
4692
+ </div>
4693
+ <div onclick="_htShowRunner('testExplodeView','ExplodeView Test Agent')" style="cursor:pointer;padding:14px;background:linear-gradient(135deg,rgba(56,211,159,0.12),rgba(56,211,159,0.04));border:1px solid rgba(56,211,159,0.3);border-radius:8px;text-align:center;">
4694
+ <div style="font-size:24px;margin-bottom:6px;">🧪</div>
4695
+ <div style="font-weight:600;color:#38d39f;font-size:12px;">ExplodeView Tests</div>
4696
+ <div style="font-size:10px;color:var(--text-secondary);margin-top:3px;">100+ viewer, panel, shortcut tests</div>
4697
+ </div>
4428
4698
  </div>
4429
4699
  </div>
4430
4700
 
@@ -4435,22 +4705,16 @@
4435
4705
  <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Undo</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">Ctrl+Z</kbd></div>
4436
4706
  <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Redo</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">Ctrl+Y</kbd></div>
4437
4707
  <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Extrude</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">E</kbd></div>
4438
- <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Revolve</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">R</kbd></div>
4439
- <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Toggle Grid</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">G</kbd></div>
4440
4708
  <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Wireframe</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">W</kbd></div>
4441
- <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Fit All</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">V</kbd></div>
4442
- <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Delete</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">Del</kbd></div>
4443
- <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Cancel</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">Esc</kbd></div>
4444
- <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Save</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">Ctrl+S</kbd></div>
4709
+ <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Grid</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">G</kbd></div>
4710
+ <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Viewer Mode</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">V</kbd></div>
4711
+ <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Section Cut</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">C</kbd></div>
4712
+ <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Measure</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">M</kbd></div>
4713
+ <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Annotate</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">A</kbd></div>
4714
+ <div style="display:flex;justify-content:space-between;padding:4px 8px;background:var(--bg-tertiary);border-radius:4px;"><span>Help</span><kbd style="background:var(--bg-primary);padding:1px 6px;border-radius:3px;font-family:monospace;font-size:10px;border:1px solid var(--border-color);">?</kbd></div>
4445
4715
  </div>
4446
4716
  </div>
4447
4717
 
4448
- <!-- Feature Catalog -->
4449
- <div style="margin-bottom:16px;">
4450
- <h3 style="font-size:14px;font-weight:600;margin-bottom:10px;color:var(--accent-blue);">All Features (${Object.keys(window.cycleCAD || {}).length}+ modules)</h3>
4451
- <div id="help-feature-list" style="max-height:280px;overflow-y:auto;border:1px solid var(--border-color);border-radius:6px;"></div>
4452
- </div>
4453
-
4454
4718
  <!-- Links -->
4455
4719
  <div style="display:flex;gap:10px;flex-wrap:wrap;">
4456
4720
  <a href="https://github.com/vvlars-cmd/cyclecad" target="_blank" style="text-decoration:none;padding:8px 14px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:6px;color:var(--text-primary);font-size:12px;">GitHub Repo</a>
@@ -4458,6 +4722,7 @@
4458
4722
  <a href="../" style="text-decoration:none;padding:8px 14px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:6px;color:var(--text-primary);font-size:12px;">cyclecad.com</a>
4459
4723
  </div>
4460
4724
  </div>
4725
+ <div id="ht-runner-area" style="display:none;flex:1;overflow:hidden;min-height:0;"></div>
4461
4726
  `;
4462
4727
  document.body.appendChild(panel);
4463
4728
 
@@ -4956,6 +5221,6 @@
4956
5221
  </div>
4957
5222
  </div>
4958
5223
 
4959
- <span id="version-badge" style="position:fixed;bottom:42px;left:50%;transform:translateX(-50%);z-index:999;font-size:0.9rem;color:rgba(255,255,255,0.9);letter-spacing:0.1em;white-space:nowrap;padding:6px 16px;user-select:all;pointer-events:auto;font-family:monospace;font-weight:700;background:rgba(0,0,0,0.7);border:1px solid rgba(88,166,255,0.4);border-radius:6px;text-shadow:0 1px 3px rgba(0,0,0,0.5);" title="cycleCAD version">cycleCAD v0.9.6</span>
5224
+ <span id="version-badge" style="position:fixed;bottom:42px;left:50%;transform:translateX(-50%);z-index:999;font-size:0.9rem;color:rgba(255,255,255,0.9);letter-spacing:0.1em;white-space:nowrap;padding:6px 16px;user-select:all;pointer-events:auto;font-family:monospace;font-weight:700;background:rgba(0,0,0,0.7);border:1px solid rgba(88,166,255,0.4);border-radius:6px;text-shadow:0 1px 3px rgba(0,0,0,0.5);" title="cycleCAD version">cycleCAD v1.0.1</span>
4960
5225
  </body>
4961
5226
  </html>
@@ -381,10 +381,10 @@
381
381
  const TESTS = {
382
382
  'Splash Screen': [
383
383
  {
384
- name: 'Version badge shows v0.9.6',
384
+ name: 'Version badge shows v0.9.9',
385
385
  fn: async () => {
386
386
  const badge = appWindow.document.querySelector('[data-version]') ||
387
- appWindow.document.body.textContent.includes('0.9.6');
387
+ appWindow.document.body.textContent.includes('0.9.9');
388
388
  return badge ? 'Version badge found' : 'No version badge';
389
389
  }
390
390
  },