cyclecad 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/DELIVERABLES.txt +296 -445
  2. package/ENHANCEMENT_COMPLETION_REPORT.md +383 -0
  3. package/ENHANCEMENT_SUMMARY.txt +308 -0
  4. package/FEATURE_INVENTORY.md +235 -0
  5. package/FUSION360_FEATURES_SUMMARY.md +452 -0
  6. package/FUSION360_PARITY_ENHANCEMENTS.md +461 -0
  7. package/FUSION360_PARITY_SUMMARY.md +520 -0
  8. package/FUSION360_QUICK_REFERENCE.md +351 -0
  9. package/IMPLEMENTATION_GUIDE.md +502 -0
  10. package/INTEGRATION-GUIDE.md +377 -0
  11. package/MODULES_PHASES_6_7.md +780 -0
  12. package/MODULE_API_REFERENCE.md +712 -0
  13. package/MODULE_INVENTORY.txt +264 -0
  14. package/app/index.html +1345 -4930
  15. package/app/js/app.js +1312 -514
  16. package/app/js/brep-kernel.js +1353 -455
  17. package/app/js/help-module.js +1437 -0
  18. package/app/js/kernel.js +364 -40
  19. package/app/js/modules/animation-module.js +1461 -0
  20. package/app/js/modules/assembly-module.js +47 -3
  21. package/app/js/modules/cam-module.js +1572 -0
  22. package/app/js/modules/collaboration-module.js +1615 -0
  23. package/app/js/modules/constraint-module.js +1266 -0
  24. package/app/js/modules/data-module.js +1054 -0
  25. package/app/js/modules/drawing-module.js +54 -8
  26. package/app/js/modules/formats-module.js +873 -0
  27. package/app/js/modules/inspection-module.js +1330 -0
  28. package/app/js/modules/mesh-module-enhanced.js +880 -0
  29. package/app/js/modules/mesh-module.js +968 -0
  30. package/app/js/modules/operations-module.js +40 -7
  31. package/app/js/modules/plugin-module.js +1554 -0
  32. package/app/js/modules/rendering-module.js +1766 -0
  33. package/app/js/modules/scripting-module.js +1073 -0
  34. package/app/js/modules/simulation-module.js +60 -3
  35. package/app/js/modules/sketch-module.js +2029 -91
  36. package/app/js/modules/step-module.js +47 -6
  37. package/app/js/modules/surface-module.js +1040 -0
  38. package/app/js/modules/version-module.js +1830 -0
  39. package/app/js/modules/viewport-module.js +95 -8
  40. package/app/test-agent-v2.html +881 -1316
  41. package/cycleCAD-Architecture-v2.pptx +0 -0
  42. package/docs/ARCHITECTURE.html +838 -1408
  43. package/docs/DEVELOPER-GUIDE.md +1504 -0
  44. package/docs/TUTORIAL.md +740 -0
  45. package/package.json +1 -1
  46. package/~$cycleCAD-Architecture-v2.pptx +0 -0
  47. package/.github/scripts/cad-diff.js +0 -590
  48. package/.github/workflows/cad-diff.yml +0 -117
@@ -0,0 +1,1504 @@
1
+ # cycleCAD Developer Guide
2
+
3
+ Complete reference for building with and extending cycleCAD's LEGO microkernel architecture.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Architecture Overview](#architecture-overview)
8
+ 2. [Getting Started for Developers](#getting-started-for-developers)
9
+ 3. [Module Lifecycle](#module-lifecycle)
10
+ 4. [Creating a Custom Module](#creating-a-custom-module)
11
+ 5. [Kernel API Reference](#kernel-api-reference)
12
+ 6. [Core Modules Reference](#core-modules-reference)
13
+ 7. [Event Catalog](#event-catalog)
14
+ 8. [Best Practices](#best-practices)
15
+ 9. [Troubleshooting](#troubleshooting)
16
+
17
+ ---
18
+
19
+ ## Architecture Overview
20
+
21
+ ### The LEGO Microkernel Concept
22
+
23
+ cycleCAD is built on a **LEGO microkernel architecture** — a tiny immutable core with pluggable, swappable modules. Unlike monolithic CAD systems, every feature (sketching, 3D operations, drawings, simulations) is a separate module that can be:
24
+
25
+ - **Loaded** on demand (lazy-loading for fast startup)
26
+ - **Activated** to inject UI and start processing
27
+ - **Deactivated** to pause (state preserved in memory)
28
+ - **Unloaded** to free memory
29
+ - **Hot-swapped** to replace with alternative implementations
30
+
31
+ ### Why LEGO Modules?
32
+
33
+ | Problem | LEGO Solution |
34
+ |---------|---------------|
35
+ | Monolithic 5MB+ bundle | Modules load only when needed |
36
+ | Feature coupling | Events, not imports — no direct dependencies |
37
+ | Multiple backends (B-Rep vs mesh) | Smart dispatch: try B-Rep, fall back to mesh |
38
+ | Memory on low-end devices | Auto-eviction: unload least-used modules when budget exceeded |
39
+ | Plugin developers stuck | Standard interface: anyone can write modules |
40
+
41
+ ### Core Architecture
42
+
43
+ ```
44
+ ┌─────────────────────────────────────────────────────────┐
45
+ │ The Immutable Kernel (~50KB) │
46
+ ├─────────────────────────────────────────────────────────┤
47
+ │ Module Registry • Event Bus • Command Dispatcher │
48
+ │ State Manager • Memory Manager • Smart Dispatch │
49
+ └─────────────────────────────────────────────────────────┘
50
+ ↕ (events only, no direct calls)
51
+ ┌─────────────────────────────────────────────────────────┐
52
+ │ LEGO Modules (pluggable) │
53
+ ├─────────────────────────────────────────────────────────┤
54
+ │ Viewport │ Sketch │ Ops │ Drawing │ Sim │ │
55
+ │ Assembly │ AI Chat │ STEP │ B-Rep Core│... │ │
56
+ └─────────────────────────────────────────────────────────┘
57
+ ```
58
+
59
+ ### Smart Dispatch Pattern
60
+
61
+ When a user requests a feature (e.g., "fillet"), the kernel:
62
+
63
+ 1. **Checks availability:** Is B-Rep initialized? → Yes
64
+ 2. **Routes to preferred:** Call `brep.fillet(edges, radius)`
65
+ 3. **Falls back gracefully:** If B-Rep unavailable → Call `mesh.fillet()` (visual approximation)
66
+ 4. **Loads on-demand:** If module not loaded → `kernel.load('brep')` then retry
67
+
68
+ This ensures the app **never breaks** — users always get something, even if the premium backend is unavailable.
69
+
70
+ ---
71
+
72
+ ## Getting Started for Developers
73
+
74
+ ### Prerequisites
75
+
76
+ - Node.js 18+
77
+ - Git
78
+ - A text editor (VS Code recommended)
79
+ - Chrome or Chromium for testing
80
+
81
+ ### Clone and Install
82
+
83
+ ```bash
84
+ git clone https://github.com/vvlars-cmd/cyclecad.git
85
+ cd cyclecad
86
+ npm install
87
+ ```
88
+
89
+ ### Directory Structure
90
+
91
+ ```
92
+ cyclecad/
93
+ ├── app/
94
+ │ ├── index.html # Main app entry point
95
+ │ ├── js/
96
+ │ │ ├── kernel.js # Immutable microkernel (~50KB)
97
+ │ │ ├── app.js # App state and initialization
98
+ │ │ ├── modules/
99
+ │ │ │ ├── viewport.js # Core: 3D scene, camera, rendering
100
+ │ │ │ ├── sketch.js # Core: 2D sketcher
101
+ │ │ │ ├── ops.js # Core: extrude, revolve, fillet, etc.
102
+ │ │ │ ├── drawing.js # Engineering drawings + DXF export
103
+ │ │ │ ├── assembly.js # Components + mates + explosions
104
+ │ │ │ ├── simulation.js # FEA, kinematics, thermal
105
+ │ │ │ ├── step.js # STEP import/export
106
+ │ │ │ ├── brep-core.js # B-Rep geometry engine (OCCT WASM)
107
+ │ │ │ ├── ai-chat.js # AI copilot (Gemini + Groq)
108
+ │ │ │ ├── token-engine.js # $CYCLE token accounting
109
+ │ │ │ └── marketplace.js # Model marketplace
110
+ │ │ ├── agent-api.js # Public API for agents
111
+ │ │ ├── token-engine.js # Token ledger system
112
+ │ │ └── shared/
113
+ │ │ ├── three-utils.js # Three.js helpers
114
+ │ │ ├── geometry.js # Math: vectors, planes, intersections
115
+ │ │ └── storage.js # localStorage helpers
116
+ │ └── styles/
117
+ │ └── app.css
118
+ ├── docs/
119
+ │ ├── DEVELOPER-GUIDE.md # This file
120
+ │ ├── TUTORIAL.md # Getting-started tutorial
121
+ │ ├── ARCHITECTURE.md # Detailed architecture
122
+ │ └── KERNEL-API.md # Kernel API reference
123
+ ├── server/
124
+ │ ├── mcp-server.js # MCP protocol server
125
+ │ ├── api-server.js # REST API (HTTP + WebSocket)
126
+ │ └── converter.py # STEP→GLB server (FastAPI)
127
+ ├── bin/
128
+ │ └── cyclecad-cli.js # Command-line interface
129
+ ├── test/
130
+ │ ├── kernel.test.js
131
+ │ ├── modules/
132
+ │ │ ├── sketch.test.js
133
+ │ │ ├── ops.test.js
134
+ │ │ └── ...
135
+ │ └── agent-api.test.js
136
+ ├── package.json
137
+ ├── kernel.config.js # Kernel configuration
138
+ └── README.md
139
+ ```
140
+
141
+ ### Run the App Locally
142
+
143
+ ```bash
144
+ # Development server with live reload
145
+ npm run dev
146
+ # Opens http://localhost:5173
147
+
148
+ # Production build
149
+ npm run build
150
+
151
+ # Run tests
152
+ npm test
153
+
154
+ # Run with debug logging
155
+ npm run dev -- --debug
156
+ ```
157
+
158
+ ### View the Architecture
159
+
160
+ The microkernel structure is visualized at:
161
+
162
+ ```
163
+ http://localhost:5173/app/?view=architecture
164
+ ```
165
+
166
+ This shows:
167
+ - All 20 modules with status (loaded/active/dormant/unloaded)
168
+ - Event connections between modules
169
+ - Memory usage per module
170
+ - Auto-eviction history
171
+
172
+ ### Run the Visual Test Agent
173
+
174
+ ```bash
175
+ # In the app, press Ctrl+Shift+T
176
+ # Or navigate to:
177
+ http://localhost:5173/app/test-agent.html
178
+ ```
179
+
180
+ This launches an interactive test suite with 113 tests across 15 categories. The test agent runs in split-screen mode (app on left, test log on right) and provides live visualization of every click.
181
+
182
+ ---
183
+
184
+ ## Module Lifecycle
185
+
186
+ Every module follows this state machine:
187
+
188
+ ```
189
+ ┌─────────────────────────────────────────┐
190
+ │ Registered (definition only) │
191
+ │ ← kernel.register(moduleDef) │
192
+ └────────────────────┬────────────────────┘
193
+
194
+ kernel.load()
195
+
196
+ ┌─────────────────────────────────────────┐
197
+ │ Loaded (assets downloaded) │
198
+ │ → load(kernel) called │
199
+ │ → Module can load files, WASM, etc. │
200
+ └────────────────────┬────────────────────┘
201
+
202
+ kernel.activate()
203
+
204
+ ┌─────────────────────────────────────────┐
205
+ │ Active (running) │
206
+ │ → activate(kernel) called │
207
+ │ → UI injected, events wired │
208
+ │ → Memory: ~50% of estimate │
209
+ └────────────────────┬────────────────────┘
210
+
211
+ kernel.deactivate()
212
+
213
+ ┌─────────────────────────────────────────┐
214
+ │ Dormant (suspended, memory kept) │
215
+ │ → deactivate(kernel) called │
216
+ │ → UI hidden, events unsubscribed │
217
+ │ → Memory: ~10% of estimate │
218
+ └────────────────────┬────────────────────┘
219
+
220
+ kernel.unload()
221
+
222
+ ┌─────────────────────────────────────────┐
223
+ │ Unloaded (freed) │
224
+ │ → unload(kernel) called │
225
+ │ → All memory released │
226
+ └─────────────────────────────────────────┘
227
+ ```
228
+
229
+ ### Example Lifecycle
230
+
231
+ ```javascript
232
+ // Module definition
233
+ const MyModule = {
234
+ id: 'my-module',
235
+ async load(kernel) {
236
+ console.log('[MyModule] Loading (downloading resources...)');
237
+ // Download WASM, models, textures, etc.
238
+ // Called once per app session
239
+ },
240
+ async activate(kernel) {
241
+ console.log('[MyModule] Activating (injecting UI...)');
242
+ // Show UI, register commands, subscribe to events
243
+ kernel.on('viewport:ready', this.onViewportReady.bind(this));
244
+ },
245
+ async deactivate(kernel) {
246
+ console.log('[MyModule] Deactivating (hiding UI...)');
247
+ // Hide UI, unsubscribe from events
248
+ // State is preserved in memory
249
+ kernel.off('viewport:ready', this.onViewportReady);
250
+ },
251
+ async unload(kernel) {
252
+ console.log('[MyModule] Unloading (freeing memory)...');
253
+ // Dispose geometries, clear arrays, etc.
254
+ // This is the only state loss point
255
+ },
256
+ };
257
+
258
+ // Usage
259
+ await kernel.register(MyModule); // Registered
260
+ await kernel.load('my-module'); // Loaded
261
+ await kernel.activate('my-module'); // Active
262
+ await kernel.deactivate('my-module'); // Dormant
263
+ await kernel.unload('my-module'); // Unloaded
264
+ ```
265
+
266
+ ---
267
+
268
+ ## Creating a Custom Module
269
+
270
+ This tutorial walks through creating a **Part Counter** module that counts parts in the scene and displays a badge.
271
+
272
+ ### Step 1: Create the Module File
273
+
274
+ Create `app/js/modules/part-counter.js`:
275
+
276
+ ```javascript
277
+ const PartCounterModule = {
278
+ // ========== METADATA ==========
279
+ id: 'part-counter',
280
+ name: 'Part Counter',
281
+ version: '1.0.0',
282
+ category: 'tool',
283
+
284
+ // Dependencies: only loads when these are already active
285
+ dependencies: ['viewport'],
286
+
287
+ // Estimated memory (MB): used for auto-eviction decisions
288
+ memoryEstimate: 2,
289
+
290
+ // ========== LIFECYCLE ==========
291
+
292
+ async load(kernel) {
293
+ // Called once when module is first loaded
294
+ // Good place to download external resources
295
+ console.log('[PartCounter] Loading...');
296
+ this.partCount = 0;
297
+ this.mesh = null;
298
+ },
299
+
300
+ async activate(kernel) {
301
+ // Called when module should start running
302
+ // Inject UI, subscribe to events, register commands
303
+ console.log('[PartCounter] Activating...');
304
+
305
+ // Subscribe to events
306
+ kernel.on('viewport:ready', this.onViewportReady.bind(this));
307
+ kernel.on('feature:created', this.onFeatureCreated.bind(this));
308
+ kernel.on('part:selected', this.onPartSelected.bind(this));
309
+
310
+ // Inject UI into the toolbar
311
+ const toolbar = document.querySelector('#ce-buttons');
312
+ if (toolbar) {
313
+ const button = document.createElement('button');
314
+ button.id = 'pc-toggle';
315
+ button.textContent = '📊 Parts';
316
+ button.addEventListener('click', () => kernel.exec('part-counter.toggle'));
317
+ toolbar.appendChild(button);
318
+ }
319
+
320
+ // Create badge element (hidden initially)
321
+ const badge = document.createElement('div');
322
+ badge.id = 'pc-badge';
323
+ badge.style.cssText = `
324
+ position: fixed;
325
+ top: 20px;
326
+ right: 20px;
327
+ background: #2563eb;
328
+ color: white;
329
+ padding: 12px 16px;
330
+ border-radius: 8px;
331
+ font-weight: bold;
332
+ z-index: 1000;
333
+ display: none;
334
+ `;
335
+ document.body.appendChild(badge);
336
+ },
337
+
338
+ async deactivate(kernel) {
339
+ // Called when module should pause
340
+ // Remove UI, unsubscribe from events
341
+ console.log('[PartCounter] Deactivating...');
342
+
343
+ kernel.off('viewport:ready', this.onViewportReady);
344
+ kernel.off('feature:created', this.onFeatureCreated);
345
+ kernel.off('part:selected', this.onPartSelected);
346
+
347
+ const button = document.querySelector('#pc-toggle');
348
+ if (button) button.remove();
349
+ },
350
+
351
+ async unload(kernel) {
352
+ // Called when module is being removed entirely
353
+ // Free all memory, dispose resources
354
+ console.log('[PartCounter] Unloading...');
355
+
356
+ const badge = document.querySelector('#pc-badge');
357
+ if (badge) badge.remove();
358
+
359
+ this.partCount = 0;
360
+ this.mesh = null;
361
+ },
362
+
363
+ // ========== EVENT HANDLERS ==========
364
+
365
+ onViewportReady(data) {
366
+ console.log('[PartCounter] Viewport ready, initializing counter');
367
+ this.updateCount();
368
+ },
369
+
370
+ onFeatureCreated(data) {
371
+ console.log('[PartCounter] Feature created:', data.featureName);
372
+ this.updateCount();
373
+ },
374
+
375
+ onPartSelected(data) {
376
+ console.log('[PartCounter] Part selected:', data.meshId);
377
+ this.updateCount();
378
+ },
379
+
380
+ updateCount() {
381
+ // Count meshes in the scene
382
+ if (!window._scene) return;
383
+
384
+ const meshes = window._scene.getObjectsByProperty('isMesh', true);
385
+ this.partCount = meshes.filter(m => m.userData.isGeometry).length;
386
+
387
+ // Update badge
388
+ const badge = document.querySelector('#pc-badge');
389
+ if (badge && badge.style.display !== 'none') {
390
+ badge.textContent = `📊 ${this.partCount} Parts`;
391
+ }
392
+ },
393
+
394
+ // ========== COMMANDS ==========
395
+
396
+ provides: {
397
+ commands: {
398
+ 'part-counter.toggle': async (params) => {
399
+ const badge = document.querySelector('#pc-badge');
400
+ const isVisible = badge.style.display !== 'none';
401
+
402
+ badge.style.display = isVisible ? 'none' : 'block';
403
+
404
+ if (!isVisible) {
405
+ this.updateCount();
406
+ }
407
+
408
+ return {
409
+ success: true,
410
+ visible: !isVisible,
411
+ count: this.partCount,
412
+ };
413
+ },
414
+
415
+ 'part-counter.getCount': async (params) => {
416
+ return {
417
+ count: this.partCount,
418
+ };
419
+ },
420
+
421
+ 'part-counter.export': async (params) => {
422
+ // Export count to JSON
423
+ const data = {
424
+ timestamp: new Date().toISOString(),
425
+ partCount: this.partCount,
426
+ };
427
+
428
+ const json = JSON.stringify(data, null, 2);
429
+ const blob = new Blob([json], { type: 'application/json' });
430
+ const url = URL.createObjectURL(blob);
431
+ const link = document.createElement('a');
432
+ link.href = url;
433
+ link.download = 'part-count.json';
434
+ link.click();
435
+ URL.revokeObjectURL(url);
436
+
437
+ return { success: true };
438
+ },
439
+ },
440
+
441
+ // UI provided by this module
442
+ ui: {
443
+ // Buttons for toolbar
444
+ toolbar: [
445
+ {
446
+ id: 'pc-toggle',
447
+ label: 'Toggle Counter',
448
+ icon: '📊',
449
+ command: 'part-counter.toggle',
450
+ },
451
+ ],
452
+
453
+ // Right-side panel
454
+ panel: `
455
+ <div id="pc-panel" style="padding: 16px;">
456
+ <h3>Part Counter</h3>
457
+ <p>Total parts: <strong id="pc-count">0</strong></p>
458
+ <button onclick="kernel.exec('part-counter.export')">
459
+ Export JSON
460
+ </button>
461
+ </div>
462
+ `,
463
+ },
464
+ },
465
+ };
466
+
467
+ export default PartCounterModule;
468
+ ```
469
+
470
+ ### Step 2: Register the Module
471
+
472
+ In `app/js/app.js`, add to the module registration list:
473
+
474
+ ```javascript
475
+ import PartCounterModule from './modules/part-counter.js';
476
+
477
+ async function initializeApp() {
478
+ // ... existing initialization code ...
479
+
480
+ // Register modules (must be done before load/activate)
481
+ await kernel.register(PartCounterModule);
482
+ await kernel.register(ViewportModule);
483
+ // ... other modules ...
484
+
485
+ // Load and activate core modules
486
+ await kernel.activate('viewport');
487
+
488
+ // Lazy-load optional modules on demand
489
+ // Part Counter will activate when user clicks its button
490
+ }
491
+ ```
492
+
493
+ ### Step 3: Update index.html
494
+
495
+ Add the import to `app/index.html`:
496
+
497
+ ```html
498
+ <script type="module">
499
+ import PartCounterModule from './js/modules/part-counter.js';
500
+ // Rest of initialization...
501
+ </script>
502
+ ```
503
+
504
+ ### Step 4: Test Your Module
505
+
506
+ In the browser console:
507
+
508
+ ```javascript
509
+ // Check registration
510
+ kernel.list(); // Should show 'part-counter' as Registered
511
+
512
+ // Load it
513
+ await kernel.activate('part-counter');
514
+
515
+ // Test commands
516
+ await kernel.exec('part-counter.getCount');
517
+ // { count: 0 }
518
+
519
+ await kernel.exec('part-counter.toggle');
520
+ // { success: true, visible: true, count: 0 }
521
+
522
+ // Export
523
+ await kernel.exec('part-counter.export');
524
+ // Downloads part-count.json
525
+ ```
526
+
527
+ ---
528
+
529
+ ## Kernel API Reference
530
+
531
+ ### Module Registry
532
+
533
+ #### `kernel.register(moduleDef)`
534
+
535
+ Register a module definition (doesn't load it yet).
536
+
537
+ ```javascript
538
+ await kernel.register({
539
+ id: 'my-module',
540
+ name: 'My Module',
541
+ // ... rest of definition
542
+ });
543
+ ```
544
+
545
+ **Parameters:**
546
+ - `moduleDef` (object) — Module definition with id, name, version, category, etc.
547
+
548
+ **Returns:** Promise<void>
549
+
550
+ **Throws:** Error if module ID already registered
551
+
552
+ ---
553
+
554
+ #### `kernel.get(moduleId)`
555
+
556
+ Get current status and info about a module.
557
+
558
+ ```javascript
559
+ const info = kernel.get('viewport');
560
+ console.log(info);
561
+ // {
562
+ // id: 'viewport',
563
+ // name: 'Viewport',
564
+ // status: 'active', // 'registered' | 'loading' | 'loaded' | 'activating' | 'active' | 'deactivating' | 'dormant' | 'unloading' | 'unloaded'
565
+ // memoryUsage: 12.5, // MB
566
+ // dependencies: ['THREE'],
567
+ // loadTime: 450, // ms
568
+ // }
569
+ ```
570
+
571
+ **Returns:** object with metadata and status
572
+
573
+ ---
574
+
575
+ #### `kernel.list()`
576
+
577
+ List all registered modules with status.
578
+
579
+ ```javascript
580
+ const modules = kernel.list();
581
+ modules.forEach(m => {
582
+ console.log(`${m.id}: ${m.status} (${m.memoryUsage}MB)`);
583
+ });
584
+ ```
585
+
586
+ **Returns:** Array of module info objects
587
+
588
+ ---
589
+
590
+ #### `kernel.listByCategory(category)`
591
+
592
+ Filter modules by category.
593
+
594
+ ```javascript
595
+ const tools = kernel.listByCategory('tool');
596
+ const cores = kernel.listByCategory('core');
597
+ ```
598
+
599
+ **Returns:** Array of module info objects
600
+
601
+ ---
602
+
603
+ ### Module Lifecycle
604
+
605
+ #### `kernel.load(moduleId)`
606
+
607
+ Load a module (download resources, run `load()` hook).
608
+
609
+ - Automatically resolves dependencies first
610
+ - Resolves immediately if already loaded
611
+ - Emits `module:loaded` event
612
+
613
+ ```javascript
614
+ await kernel.load('brep-core');
615
+ ```
616
+
617
+ **Returns:** Promise<void>
618
+
619
+ ---
620
+
621
+ #### `kernel.activate(moduleId)`
622
+
623
+ Load + activate a module (inject UI, start processing).
624
+
625
+ - Calls `load()` first if needed
626
+ - Calls `activate()` hook
627
+ - Emits `module:activated` event
628
+
629
+ ```javascript
630
+ await kernel.activate('viewport');
631
+ ```
632
+
633
+ **Returns:** Promise<void>
634
+
635
+ ---
636
+
637
+ #### `kernel.deactivate(moduleId)`
638
+
639
+ Pause a module (suspend but keep in memory).
640
+
641
+ - Calls `deactivate()` hook
642
+ - Removes UI, unsubscribes from events
643
+ - Emits `module:deactivated` event
644
+ - Memory stays allocated (faster reactivation)
645
+
646
+ ```javascript
647
+ await kernel.deactivate('drawing');
648
+ ```
649
+
650
+ **Returns:** Promise<void>
651
+
652
+ ---
653
+
654
+ #### `kernel.unload(moduleId)`
655
+
656
+ Unload a module completely (free all memory).
657
+
658
+ - Calls `unload()` hook
659
+ - Calls `deactivate()` first if active
660
+ - Emits `module:unloaded` event
661
+
662
+ ```javascript
663
+ await kernel.unload('simulation');
664
+ ```
665
+
666
+ **Returns:** Promise<void>
667
+
668
+ ---
669
+
670
+ #### `kernel.swap(oldId, newId)`
671
+
672
+ Hot-swap modules (unload old, load new, preserve state).
673
+
674
+ Useful for switching between B-Rep and mesh backends without losing user work.
675
+
676
+ ```javascript
677
+ await kernel.swap('ops-mesh', 'ops-brep');
678
+ ```
679
+
680
+ **Returns:** Promise<void>
681
+
682
+ ---
683
+
684
+ ### Event Bus
685
+
686
+ #### `kernel.on(event, handler, options?)`
687
+
688
+ Subscribe to an event.
689
+
690
+ ```javascript
691
+ kernel.on('part:selected', (data) => {
692
+ console.log('Selected:', data.meshId);
693
+ });
694
+
695
+ // Subscribe once then auto-unsubscribe
696
+ kernel.on('viewport:ready', handler, { once: true });
697
+ ```
698
+
699
+ **Parameters:**
700
+ - `event` (string) — Event name (e.g., 'part:selected')
701
+ - `handler` (function) — Callback receiving event data
702
+ - `options` (object) — { once: true } for one-time subscription
703
+
704
+ **Returns:** void
705
+
706
+ ---
707
+
708
+ #### `kernel.off(event, handler)`
709
+
710
+ Unsubscribe from an event.
711
+
712
+ ```javascript
713
+ kernel.off('part:selected', myHandler);
714
+ ```
715
+
716
+ **Parameters:**
717
+ - `event` (string) — Event name
718
+ - `handler` (function) — Same handler function passed to `on()`
719
+
720
+ **Returns:** void
721
+
722
+ ---
723
+
724
+ #### `kernel.emit(event, data?)`
725
+
726
+ Publish an event (usually called by modules, not consumers).
727
+
728
+ ```javascript
729
+ kernel.emit('part:selected', { meshId: 42, position: [0, 0, 0] });
730
+ ```
731
+
732
+ **Parameters:**
733
+ - `event` (string) — Event name
734
+ - `data` (object) — Payload to send to subscribers
735
+
736
+ **Returns:** void
737
+
738
+ ---
739
+
740
+ #### `kernel.once(event, handler)`
741
+
742
+ Subscribe to an event, auto-unsubscribe after first call.
743
+
744
+ ```javascript
745
+ await new Promise(resolve => {
746
+ kernel.once('step:importComplete', (data) => {
747
+ resolve(data);
748
+ });
749
+ });
750
+ ```
751
+
752
+ **Returns:** void
753
+
754
+ ---
755
+
756
+ ### Command System
757
+
758
+ #### `kernel.exec(command, params?)`
759
+
760
+ Execute a command (auto-loads module if needed).
761
+
762
+ ```javascript
763
+ const result = await kernel.exec('ops.extrude', {
764
+ profile: 'active-sketch',
765
+ distance: 50,
766
+ reverse: false,
767
+ });
768
+
769
+ console.log(result);
770
+ // { success: true, featureId: 'feat_123' }
771
+ ```
772
+
773
+ **Parameters:**
774
+ - `command` (string) — Command name (e.g., 'ops.extrude')
775
+ - `params` (object) — Command parameters
776
+
777
+ **Returns:** Promise resolving to command result (shape varies by command)
778
+
779
+ ---
780
+
781
+ #### `kernel.hasCommand(name)`
782
+
783
+ Check if a command is available.
784
+
785
+ ```javascript
786
+ if (kernel.hasCommand('brep.fillet')) {
787
+ await kernel.exec('brep.fillet', { ... });
788
+ } else {
789
+ console.log('B-Rep not available, using mesh approximation');
790
+ }
791
+ ```
792
+
793
+ **Returns:** boolean
794
+
795
+ ---
796
+
797
+ #### `kernel.listCommands()`
798
+
799
+ List all available commands across all modules.
800
+
801
+ ```javascript
802
+ const commands = kernel.listCommands();
803
+ commands.forEach(cmd => {
804
+ console.log(`${cmd.namespace}.${cmd.name}: ${cmd.description}`);
805
+ });
806
+ ```
807
+
808
+ **Returns:** Array of command info objects
809
+
810
+ ---
811
+
812
+ ### Shared State
813
+
814
+ #### `kernel.state.set(key, value)`
815
+
816
+ Set a global state value.
817
+
818
+ ```javascript
819
+ kernel.state.set('current-part', { id: 42, name: 'Part 1' });
820
+ ```
821
+
822
+ **Returns:** void
823
+
824
+ ---
825
+
826
+ #### `kernel.state.get(key)`
827
+
828
+ Get a state value.
829
+
830
+ ```javascript
831
+ const part = kernel.state.get('current-part');
832
+ ```
833
+
834
+ **Returns:** any
835
+
836
+ ---
837
+
838
+ #### `kernel.state.watch(key, handler)`
839
+
840
+ Watch a state key for changes.
841
+
842
+ ```javascript
843
+ kernel.state.watch('current-part', (newValue, oldValue) => {
844
+ console.log(`Part changed from ${oldValue.id} to ${newValue.id}`);
845
+ });
846
+ ```
847
+
848
+ **Parameters:**
849
+ - `key` (string) — State key
850
+ - `handler` (function) — Called with (newValue, oldValue)
851
+
852
+ **Returns:** unwatch function
853
+
854
+ ---
855
+
856
+ ### Memory Manager
857
+
858
+ #### `kernel.memory.usage()`
859
+
860
+ Get total memory in use (MB).
861
+
862
+ ```javascript
863
+ const used = kernel.memory.usage();
864
+ console.log(`Using ${used.toFixed(1)}MB / ${kernel.memory.budget}MB`);
865
+ ```
866
+
867
+ **Returns:** number (MB)
868
+
869
+ ---
870
+
871
+ #### `kernel.memory.pressure()`
872
+
873
+ Get memory pressure ratio (0.0 = empty, 1.0 = full).
874
+
875
+ ```javascript
876
+ const ratio = kernel.memory.pressure();
877
+ if (ratio > 0.8) {
878
+ console.log('Warning: Memory pressure high, unloading dormant modules');
879
+ await kernel.memory.gc();
880
+ }
881
+ ```
882
+
883
+ **Returns:** number (0-1)
884
+
885
+ ---
886
+
887
+ #### `kernel.memory.budget`
888
+
889
+ Max memory allowed (MB, default 512).
890
+
891
+ ```javascript
892
+ kernel.memory.budget = 1024; // Increase to 1GB
893
+ ```
894
+
895
+ ---
896
+
897
+ #### `kernel.memory.gc()`
898
+
899
+ Force garbage collection (unload least-used dormant modules).
900
+
901
+ Automatically called when pressure > 0.9. Manual calls for testing.
902
+
903
+ ```javascript
904
+ await kernel.memory.gc();
905
+ console.log(`After GC: ${kernel.memory.usage().toFixed(1)}MB`);
906
+ ```
907
+
908
+ **Returns:** Promise<{ unloaded: string[], freed: number }>
909
+
910
+ ---
911
+
912
+ ## Core Modules Reference
913
+
914
+ This section documents the 10 core modules and all their commands.
915
+
916
+ ### Module: Viewport
917
+
918
+ **ID:** `viewport`
919
+ **Category:** `core`
920
+ **Memory:** 45 MB
921
+ **Dependencies:** `THREE`
922
+
923
+ The 3D viewport — scene, camera, lights, rendering.
924
+
925
+ **Commands:**
926
+
927
+ - **viewport.fitAll()** — Frame all parts in view
928
+ - Returns: `{ success: true }`
929
+
930
+ - **viewport.fitSelection()** — Frame selected parts
931
+ - Returns: `{ success: true }`
932
+
933
+ - **viewport.setView(name)** — Set preset view (front, top, right, isometric, etc.)
934
+ - Parameters: `{ name: 'front' | 'top' | 'right' | 'back' | 'bottom' | 'left' | 'isometric' }`
935
+ - Returns: `{ success: true, view: string }`
936
+
937
+ - **viewport.toggleWireframe()** — Toggle wireframe rendering
938
+ - Returns: `{ success: true, wireframe: boolean }`
939
+
940
+ - **viewport.toggleGrid()** — Toggle grid floor plane
941
+ - Returns: `{ success: true, gridVisible: boolean }`
942
+
943
+ - **viewport.snapshot(options)** — Capture 3D view as PNG/JPG
944
+ - Parameters: `{ format: 'png' | 'jpg', width: number, height: number }`
945
+ - Returns: `{ success: true, dataUrl: string }`
946
+
947
+ - **viewport.multiview(columns, rows)** — Create split viewport
948
+ - Parameters: `{ columns: number, rows: number }`
949
+ - Returns: `{ success: true, viewCount: number }`
950
+
951
+ **Events:**
952
+
953
+ - `viewport:ready` — Viewport initialized and rendered once
954
+ - `viewport:resize` — Window resized
955
+ - `viewport:viewChanged` — Preset view changed
956
+ - `viewport:cameraMoved` — User rotated/panned/zoomed
957
+ - `viewport:rendered` — Frame rendered (fires every frame)
958
+
959
+ ---
960
+
961
+ ### Module: Sketch
962
+
963
+ **ID:** `sketch`
964
+ **Category:** `core`
965
+ **Memory:** 25 MB
966
+ **Dependencies:** `viewport`
967
+
968
+ 2D sketcher with constraints.
969
+
970
+ **Commands:**
971
+
972
+ - **sketch.start(plane)** — Start a sketch
973
+ - Parameters: `{ plane: 'XY' | 'YZ' | 'XZ' | { normal: [x,y,z], origin: [x,y,z] } }`
974
+ - Returns: `{ success: true, sketchId: string }`
975
+
976
+ - **sketch.line(p1, p2)** — Draw line
977
+ - Parameters: `{ start: [x, y], end: [x, y] }`
978
+
979
+ - **sketch.circle(center, radius)** — Draw circle
980
+ - Parameters: `{ center: [x, y], radius: number }`
981
+
982
+ - **sketch.rectangle(corner1, corner2)** — Draw rectangle
983
+ - Parameters: `{ p1: [x, y], p2: [x, y] }`
984
+
985
+ - **sketch.arc(center, start, end, radius)** — Draw arc
986
+ - Parameters: `{ center: [x, y], start: [x, y], end: [x, y], radius: number }`
987
+
988
+ - **sketch.constraint(type, entityIds, params)** — Add constraint
989
+ - Parameters: `{ type: 'coincident' | 'horizontal' | 'vertical' | 'parallel' | 'perpendicular' | 'tangent' | 'equal' | 'fixed' | 'concentric' | 'symmetric' | 'distance' | 'angle', entities: [id1, id2], params: {...} }`
990
+
991
+ - **sketch.finish()** — End sketch and create profile
992
+ - Returns: `{ success: true, profileId: string }`
993
+
994
+ **Events:**
995
+
996
+ - `sketch:started` — Sketch mode activated
997
+ - `sketch:entityAdded` — Line/circle/arc/rect drawn
998
+ - `sketch:constraintAdded` — Constraint applied
999
+ - `sketch:finished` — Sketch exited
1000
+ - `sketch:toolChanged` — Active sketch tool changed
1001
+
1002
+ ---
1003
+
1004
+ ### Module: Operations
1005
+
1006
+ **ID:** `ops`
1007
+ **Category:** `core`
1008
+ **Memory:** 60 MB
1009
+ **Dependencies:** `viewport`, `sketch`
1010
+
1011
+ Parametric modeling: extrude, revolve, fillet, chamfer, boolean, shell, pattern.
1012
+
1013
+ **Commands:**
1014
+
1015
+ - **ops.extrude(profile, distance, direction?)** — Extrude sketch profile
1016
+ - Parameters: `{ profileId: string, distance: number, direction?: [x,y,z], taper?: number }`
1017
+ - Returns: `{ success: true, featureId: string }`
1018
+
1019
+ - **ops.revolve(profile, axis, angle?)** — Revolve sketch around axis
1020
+ - Parameters: `{ profileId: string, axis: [x,y,z], angle?: number }`
1021
+
1022
+ - **ops.fillet(edges, radius)** — Fillet edges
1023
+ - Parameters: `{ edgeIds: [id1, id2, ...] | 'all', radius: number }`
1024
+
1025
+ - **ops.chamfer(edges, size)** — Chamfer edges
1026
+ - Parameters: `{ edgeIds: [id1, id2, ...], size: number }`
1027
+
1028
+ - **ops.hole(point, radius, depth?)** — Create hole
1029
+ - Parameters: `{ center: [x, y, z], radius: number, depth?: number }`
1030
+
1031
+ - **ops.cut(tool)** — Boolean cut
1032
+ - Parameters: `{ toolId: string }`
1033
+
1034
+ - **ops.union(body)** — Boolean union
1035
+ - Parameters: `{ bodyId: string }`
1036
+
1037
+ - **ops.intersect(body)** — Boolean intersect
1038
+ - Parameters: `{ bodyId: string }`
1039
+
1040
+ - **ops.shell(thickness, removeFaces?)** — Create hollow shell
1041
+ - Parameters: `{ thickness: number, removeFaceIds?: [id1, id2, ...] }`
1042
+
1043
+ - **ops.pattern(type, count, spacing)** — Array pattern
1044
+ - Parameters: `{ type: 'rectangular' | 'circular', count: number, spacing: number }`
1045
+
1046
+ **Events:**
1047
+
1048
+ - `feature:created` — Feature added
1049
+ - `feature:edited` — Feature parameters changed
1050
+ - `feature:deleted` — Feature removed
1051
+ - `rebuild:start` — Geometry rebuild starting
1052
+ - `rebuild:complete` — Rebuild finished
1053
+ - `rebuild:error` — Error during rebuild
1054
+
1055
+ ---
1056
+
1057
+ ### Module: Drawing
1058
+
1059
+ **ID:** `drawing`
1060
+ **Category:** `feature`
1061
+ **Memory:** 35 MB
1062
+ **Dependencies:** `viewport`
1063
+
1064
+ Engineering drawings with orthographic views, sections, dimensions.
1065
+
1066
+ **Commands:**
1067
+
1068
+ - **drawing.new()** — Create new drawing
1069
+ - Returns: `{ success: true, drawingId: string }`
1070
+
1071
+ - **drawing.addView(type, position)** — Add view to drawing
1072
+ - Parameters: `{ type: 'front' | 'top' | 'right' | 'section' | 'detail', position: [x, y] }`
1073
+
1074
+ - **drawing.addDimension(type, geometry)** — Add dimension
1075
+ - Parameters: `{ type: 'distance' | 'diameter' | 'radius' | 'angle', geometryId: string }`
1076
+
1077
+ - **drawing.addBalloon(number, position)** — Add part balloon
1078
+ - Parameters: `{ partNumber: number, position: [x, y] }`
1079
+
1080
+ - **drawing.export(format)** — Export drawing
1081
+ - Parameters: `{ format: 'pdf' | 'dwg' | 'dxf' | 'png' | 'svg' }`
1082
+ - Returns: `{ success: true, dataUrl: string }`
1083
+
1084
+ **Events:**
1085
+
1086
+ - `drawing:created` — Drawing initialized
1087
+ - `drawing:viewAdded` — View added
1088
+ - `drawing:dimensionAdded` — Dimension added
1089
+ - `drawing:exported` — Export complete
1090
+
1091
+ ---
1092
+
1093
+ ### Module: Assembly
1094
+
1095
+ **ID:** `assembly`
1096
+ **Category:** `feature`
1097
+ **Memory:** 40 MB
1098
+ **Dependencies:** `viewport`
1099
+
1100
+ Component management, mates, constraints, explosions.
1101
+
1102
+ **Commands:**
1103
+
1104
+ - **assembly.insertComponent(modelPath, position?)** — Insert part
1105
+ - Parameters: `{ path: string, position?: [x, y, z] }`
1106
+ - Returns: `{ success: true, componentId: string }`
1107
+
1108
+ - **assembly.createMate(type, comp1, comp2, params)** — Create mate
1109
+ - Parameters: `{ type: 'revolute' | 'prismatic' | 'planar' | 'spherical' | 'distance' | 'angle' | 'parallel' | 'perpendicular' | 'tangent', comp1Id: string, comp2Id: string, params: {...} }`
1110
+
1111
+ - **assembly.explode(intensity)** — Explode assembly
1112
+ - Parameters: `{ intensity: 0-1 }`
1113
+
1114
+ - **assembly.collapse()** — Collapse assembly
1115
+ - Returns: `{ success: true }`
1116
+
1117
+ - **assembly.generateBOM()** — Generate bill of materials
1118
+ - Returns: `{ success: true, bom: [...] }`
1119
+
1120
+ **Events:**
1121
+
1122
+ - `assembly:componentInserted` — Component added
1123
+ - `assembly:jointCreated` — Mate created
1124
+ - `assembly:interfereFound` — Collision detected
1125
+ - `assembly:exploded` — Assembly exploded
1126
+ - `assembly:collapsed` — Assembly collapsed
1127
+
1128
+ ---
1129
+
1130
+ ### Module: STEP
1131
+
1132
+ **ID:** `step`
1133
+ **Category:** `io`
1134
+ **Memory:** 80 MB
1135
+ **Dependencies:** `viewport`, `brep-core` (optional)
1136
+
1137
+ STEP import/export via OpenCascade.js or server converter.
1138
+
1139
+ **Commands:**
1140
+
1141
+ - **step.import(file)** — Import STEP file
1142
+ - Parameters: `{ file: File | Blob | ArrayBuffer | string (URL) }`
1143
+ - Returns: `{ success: true, parts: number, modelId: string }`
1144
+
1145
+ - **step.export()** — Export current model
1146
+ - Returns: `{ success: true, dataUrl: string }`
1147
+
1148
+ **Events:**
1149
+
1150
+ - `step:importStart` — Import beginning
1151
+ - `step:importProgress` — Bytes loaded (payload: { loaded, total })
1152
+ - `step:importComplete` — Import succeeded
1153
+ - `step:importError` — Import failed
1154
+ - `step:exportComplete` — Export finished
1155
+
1156
+ ---
1157
+
1158
+ ### Module: B-Rep Core
1159
+
1160
+ **ID:** `brep-core`
1161
+ **Category:** `engine`
1162
+ **Memory:** 120 MB
1163
+ **Dependencies:** `viewport`
1164
+
1165
+ B-Rep geometry engine (OpenCascade.js WASM). Enables:
1166
+ - Real boolean operations (not visual)
1167
+ - Exact fillet/chamfer (not approximation)
1168
+ - STEP native support
1169
+ - Tolerance checking
1170
+
1171
+ **Commands:**
1172
+
1173
+ - **brep.fillet(edges, radius)** — Real edge fillet
1174
+ - Parameters: `{ edgeIds: [id1, id2, ...] | 'all', radius: number }`
1175
+
1176
+ - **brep.chamfer(edges, size)** — Real edge chamfer
1177
+ - Parameters: `{ edgeIds: [id1, id2, ...], size: number }`
1178
+
1179
+ - **brep.shell(thickness, removeFaces?)** — Hollow solid
1180
+ - Parameters: `{ thickness: number, removeFaceIds?: [...] }`
1181
+
1182
+ - **brep.split(plane)** — Cut with plane
1183
+ - Parameters: `{ normal: [x, y, z], origin: [x, y, z] }`
1184
+
1185
+ **Events:**
1186
+
1187
+ - `brep:initialized` — B-Rep engine ready
1188
+ - `brep:wasmLoaded` — OpenCascade.js loaded
1189
+ - `brep:operationComplete` — B-Rep operation finished
1190
+
1191
+ ---
1192
+
1193
+ ### Module: AI Chat
1194
+
1195
+ **ID:** `ai-chat`
1196
+ **Category:** `ai`
1197
+ **Memory:** 15 MB (models loaded on-demand)
1198
+ **Dependencies:** `viewport`, `ops`
1199
+
1200
+ AI copilot (Gemini Flash + Groq LLama 3.1 + offline NLP).
1201
+
1202
+ **Commands:**
1203
+
1204
+ - **ai.message(text)** — Send message to AI
1205
+ - Parameters: `{ text: string }`
1206
+ - Returns: `{ success: true, response: string, action?: { command: string, params: any } }`
1207
+
1208
+ **Events:**
1209
+
1210
+ - `ai:messageReceived` — AI responded
1211
+ - `ai:commandExecuted` — AI issued command
1212
+
1213
+ ---
1214
+
1215
+ ## Event Catalog
1216
+
1217
+ Complete table of all events emitted across all modules.
1218
+
1219
+ | Event | Module | Payload | Description |
1220
+ |-------|--------|---------|-------------|
1221
+ | `part:selected` | viewport | `{ meshId, position, normal }` | User clicked 3D object |
1222
+ | `part:deselected` | viewport | `{ meshId }` | User deselected |
1223
+ | `viewport:ready` | viewport | `{}` | Viewport initialized |
1224
+ | `viewport:resize` | viewport | `{ width, height }` | Window resized |
1225
+ | `viewport:viewChanged` | viewport | `{ view: string }` | Preset view changed |
1226
+ | `viewport:cameraMoved` | viewport | `{ position, target }` | Camera moved |
1227
+ | `viewport:rendered` | viewport | `{}` | Frame rendered (every frame) |
1228
+ | `sketch:started` | sketch | `{ sketchId, plane }` | Sketch mode activated |
1229
+ | `sketch:finished` | sketch | `{ profileId, entityCount }` | Sketch exited |
1230
+ | `sketch:entityAdded` | sketch | `{ type, vertices }` | Line/circle/arc drawn |
1231
+ | `sketch:toolChanged` | sketch | `{ tool: string }` | Active tool changed |
1232
+ | `sketch:dimensionAdded` | sketch | `{ type, value }` | Dimension applied |
1233
+ | `feature:created` | ops | `{ featureId, featureName, type }` | Feature added |
1234
+ | `feature:edited` | ops | `{ featureId, params }` | Parameters changed |
1235
+ | `feature:deleted` | ops | `{ featureId }` | Feature removed |
1236
+ | `rebuild:start` | ops | `{ featureId }` | Rebuild starting |
1237
+ | `rebuild:complete` | ops | `{ duration, success }` | Rebuild finished |
1238
+ | `rebuild:error` | ops | `{ featureId, error }` | Rebuild failed |
1239
+ | `drawing:created` | drawing | `{ drawingId }` | Drawing initialized |
1240
+ | `drawing:viewAdded` | drawing | `{ type, position }` | View added |
1241
+ | `drawing:dimensionAdded` | drawing | `{ type, value }` | Dimension added |
1242
+ | `drawing:exported` | drawing | `{ format, dataUrl }` | Export complete |
1243
+ | `assembly:componentInserted` | assembly | `{ componentId, path }` | Component inserted |
1244
+ | `assembly:jointCreated` | assembly | `{ jointId, type }` | Mate created |
1245
+ | `assembly:interferenceFound` | assembly | `{ comp1Id, comp2Id }` | Collision detected |
1246
+ | `assembly:exploded` | assembly | `{ intensity }` | Assembly exploded |
1247
+ | `assembly:collapsed` | assembly | `{}` | Assembly collapsed |
1248
+ | `step:importStart` | step | `{ filename, fileSize }` | Import starting |
1249
+ | `step:importProgress` | step | `{ loaded, total, percent }` | Download progress |
1250
+ | `step:importComplete` | step | `{ parts, duration }` | Import succeeded |
1251
+ | `step:importError` | step | `{ error, details }` | Import failed |
1252
+ | `step:exportComplete` | step | `{ dataUrl, size }` | Export finished |
1253
+ | `brep:initialized` | brep-core | `{}` | B-Rep ready |
1254
+ | `brep:wasmLoaded` | brep-core | `{ size }` | WASM loaded |
1255
+ | `brep:operationComplete` | brep-core | `{ type, duration }` | Operation finished |
1256
+ | `ai:messageReceived` | ai-chat | `{ response, action }` | AI responded |
1257
+ | `ai:commandExecuted` | ai-chat | `{ command, params }` | AI issued command |
1258
+ | `module:loaded` | kernel | `{ moduleId }` | Module loaded |
1259
+ | `module:activated` | kernel | `{ moduleId }` | Module activated |
1260
+ | `module:deactivated` | kernel | `{ moduleId }` | Module deactivated |
1261
+ | `module:unloaded` | kernel | `{ moduleId }` | Module unloaded |
1262
+ | `module:error` | kernel | `{ moduleId, error }` | Module error |
1263
+ | `module:swapped` | kernel | `{ oldId, newId }` | Hot-swap completed |
1264
+
1265
+ ---
1266
+
1267
+ ## Best Practices
1268
+
1269
+ ### 1. Always Use Events, Never Import Between Modules
1270
+
1271
+ **Bad:**
1272
+ ```javascript
1273
+ // Don't do this
1274
+ import { ops } from './ops.js';
1275
+ import { viewport } from './viewport.js';
1276
+
1277
+ ops.extrude(); // Direct call, tight coupling
1278
+ ```
1279
+
1280
+ **Good:**
1281
+ ```javascript
1282
+ // Do this instead
1283
+ kernel.emit('sketch:finished', { profileId });
1284
+ // Operations module listens and acts
1285
+ kernel.on('feature:created', (data) => {
1286
+ // React to changes
1287
+ });
1288
+ ```
1289
+
1290
+ ### 2. Declare Accurate Memory Estimates
1291
+
1292
+ The memory manager uses `memoryEstimate` to decide when to auto-evict modules.
1293
+
1294
+ ```javascript
1295
+ const MyModule = {
1296
+ // Realistic estimate helps kernel make smart decisions
1297
+ memoryEstimate: 45, // 45 MB if fully active
1298
+ };
1299
+ ```
1300
+
1301
+ ### 3. Always Clean Up in unload()
1302
+
1303
+ Memory leaks destroy the "LEGO" benefit.
1304
+
1305
+ ```javascript
1306
+ async unload(kernel) {
1307
+ // Dispose Three.js objects
1308
+ this.geometry.dispose();
1309
+ this.material.dispose();
1310
+ this.mesh.parent.remove(this.mesh);
1311
+
1312
+ // Clear arrays
1313
+ this.cache = [];
1314
+
1315
+ // Remove DOM elements
1316
+ this.element.remove();
1317
+
1318
+ // Cancel pending requests
1319
+ if (this.xhr) this.xhr.abort();
1320
+ }
1321
+ ```
1322
+
1323
+ ### 4. Use kernel.state, Not Global Variables
1324
+
1325
+ ```javascript
1326
+ // Bad
1327
+ window.currentPart = 42;
1328
+
1329
+ // Good
1330
+ kernel.state.set('current-part', 42);
1331
+ const part = kernel.state.get('current-part');
1332
+
1333
+ // Watch for changes
1334
+ kernel.state.watch('current-part', (newValue, oldValue) => {
1335
+ console.log(`Changed from ${oldValue} to ${newValue}`);
1336
+ });
1337
+ ```
1338
+
1339
+ ### 5. Handle Missing Dependencies Gracefully
1340
+
1341
+ ```javascript
1342
+ async activate(kernel) {
1343
+ // Check if B-Rep is available
1344
+ if (kernel.hasCommand('brep.fillet')) {
1345
+ // Use exact fillet
1346
+ } else {
1347
+ // Fall back to mesh approximation
1348
+ console.warn('B-Rep not available, using mesh fillet');
1349
+ }
1350
+ }
1351
+ ```
1352
+
1353
+ ### 6. Implement Progressive Enhancement
1354
+
1355
+ Load basic features immediately, premium features on-demand.
1356
+
1357
+ ```javascript
1358
+ // Module definition
1359
+ const MyModule = {
1360
+ dependencies: ['viewport'], // Core only
1361
+
1362
+ async activate(kernel) {
1363
+ // Show basic UI immediately
1364
+
1365
+ // Load premium features on-demand
1366
+ document.querySelector('#premium-button').addEventListener('click', async () => {
1367
+ await kernel.load('premium-feature');
1368
+ // Now available
1369
+ });
1370
+ },
1371
+ };
1372
+ ```
1373
+
1374
+ ### 7. Emit Contextual Events
1375
+
1376
+ Events should carry rich context so subscribers don't need to query state.
1377
+
1378
+ ```javascript
1379
+ // Bad
1380
+ kernel.emit('feature:created');
1381
+
1382
+ // Good
1383
+ kernel.emit('feature:created', {
1384
+ featureId: 'feat_123',
1385
+ featureName: 'Extrude',
1386
+ type: 'extrude',
1387
+ parameters: { distance: 50, direction: [0, 0, 1] },
1388
+ duration: 234, // ms
1389
+ timestamp: Date.now(),
1390
+ });
1391
+ ```
1392
+
1393
+ ---
1394
+
1395
+ ## Troubleshooting
1396
+
1397
+ ### Module Won't Load
1398
+
1399
+ **Symptom:** `await kernel.load('my-module')` times out
1400
+
1401
+ **Causes:**
1402
+ 1. Circular dependency
1403
+ 2. Network error loading assets
1404
+ 3. WASM initialization failure
1405
+
1406
+ **Debug:**
1407
+ ```javascript
1408
+ try {
1409
+ await kernel.load('my-module');
1410
+ } catch (error) {
1411
+ console.error('Load error:', error);
1412
+ const info = kernel.get('my-module');
1413
+ console.log('Module status:', info.status);
1414
+ }
1415
+ ```
1416
+
1417
+ **Fix:**
1418
+ - Check `dependencies` in module definition
1419
+ - Verify all assets load (open DevTools Network tab)
1420
+ - Test WASM in isolation: `npm run test`
1421
+
1422
+ ---
1423
+
1424
+ ### Module Activated But UI Not Showing
1425
+
1426
+ **Symptom:** Module shows as active but buttons/panels are invisible
1427
+
1428
+ **Causes:**
1429
+ 1. `provides.ui` is empty
1430
+ 2. DOM element selectors don't match
1431
+ 3. CSS display is hidden
1432
+
1433
+ **Debug:**
1434
+ ```javascript
1435
+ const info = kernel.get('my-module');
1436
+ console.log(info.provides); // Should show ui
1437
+
1438
+ const button = document.querySelector('#my-button');
1439
+ console.log('Button found:', button);
1440
+ console.log('Computed style:', window.getComputedStyle(button));
1441
+ ```
1442
+
1443
+ **Fix:**
1444
+ - Add UI in `activate()`: `document.querySelector('#ce-buttons').appendChild(button)`
1445
+ - Ensure selector matches your HTML
1446
+ - Check CSS for `display: none`
1447
+
1448
+ ---
1449
+
1450
+ ### Memory Pressure High
1451
+
1452
+ **Symptom:** `kernel.memory.pressure() > 0.8`
1453
+
1454
+ **Causes:**
1455
+ 1. Modules not unloaded when deactivated
1456
+ 2. Inaccurate `memoryEstimate` values
1457
+ 3. Memory leaks in module `unload()`
1458
+
1459
+ **Debug:**
1460
+ ```javascript
1461
+ console.log('Memory usage:', kernel.memory.usage(), 'MB');
1462
+ console.log('Modules:', kernel.list().map(m => `${m.id}: ${m.memoryUsage}MB`));
1463
+ ```
1464
+
1465
+ **Fix:**
1466
+ - Manually unload dormant modules: `await kernel.unload('drawing')`
1467
+ - Update `memoryEstimate` values to match reality
1468
+ - Add cleanup code to `unload()` hooks
1469
+
1470
+ ---
1471
+
1472
+ ### Commands Not Available
1473
+
1474
+ **Symptom:** `kernel.hasCommand('ops.extrude')` returns false
1475
+
1476
+ **Causes:**
1477
+ 1. Module not loaded/activated
1478
+ 2. Command typo in provides definition
1479
+ 3. Module registration failed
1480
+
1481
+ **Debug:**
1482
+ ```javascript
1483
+ const commands = kernel.listCommands();
1484
+ commands.forEach(cmd => {
1485
+ if (cmd.name.includes('extrude')) {
1486
+ console.log(cmd); // Details
1487
+ }
1488
+ });
1489
+ ```
1490
+
1491
+ **Fix:**
1492
+ - Activate module: `await kernel.activate('ops')`
1493
+ - Check command name: `kernel.listCommands()`
1494
+ - Verify `provides.commands` in module definition
1495
+
1496
+ ---
1497
+
1498
+ ## Next Steps
1499
+
1500
+ - Read [TUTORIAL.md](./TUTORIAL.md) for hands-on getting started
1501
+ - Check [KERNEL-API.md](./KERNEL-API.md) for detailed API reference
1502
+ - Review [ARCHITECTURE.md](./ARCHITECTURE.md) for system design details
1503
+ - Run tests: `npm test`
1504
+ - Join the community at [github.com/vvlars-cmd/cyclecad](https://github.com/vvlars-cmd/cyclecad)