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.
- package/DELIVERABLES.txt +296 -445
- package/ENHANCEMENT_COMPLETION_REPORT.md +383 -0
- package/ENHANCEMENT_SUMMARY.txt +308 -0
- package/FEATURE_INVENTORY.md +235 -0
- package/FUSION360_FEATURES_SUMMARY.md +452 -0
- package/FUSION360_PARITY_ENHANCEMENTS.md +461 -0
- package/FUSION360_PARITY_SUMMARY.md +520 -0
- package/FUSION360_QUICK_REFERENCE.md +351 -0
- package/IMPLEMENTATION_GUIDE.md +502 -0
- package/INTEGRATION-GUIDE.md +377 -0
- package/MODULES_PHASES_6_7.md +780 -0
- package/MODULE_API_REFERENCE.md +712 -0
- package/MODULE_INVENTORY.txt +264 -0
- package/app/index.html +1345 -4930
- package/app/js/app.js +1312 -514
- package/app/js/brep-kernel.js +1353 -455
- package/app/js/help-module.js +1437 -0
- package/app/js/kernel.js +364 -40
- package/app/js/modules/animation-module.js +1461 -0
- package/app/js/modules/assembly-module.js +47 -3
- package/app/js/modules/cam-module.js +1572 -0
- package/app/js/modules/collaboration-module.js +1615 -0
- package/app/js/modules/constraint-module.js +1266 -0
- package/app/js/modules/data-module.js +1054 -0
- package/app/js/modules/drawing-module.js +54 -8
- package/app/js/modules/formats-module.js +873 -0
- package/app/js/modules/inspection-module.js +1330 -0
- package/app/js/modules/mesh-module-enhanced.js +880 -0
- package/app/js/modules/mesh-module.js +968 -0
- package/app/js/modules/operations-module.js +40 -7
- package/app/js/modules/plugin-module.js +1554 -0
- package/app/js/modules/rendering-module.js +1766 -0
- package/app/js/modules/scripting-module.js +1073 -0
- package/app/js/modules/simulation-module.js +60 -3
- package/app/js/modules/sketch-module.js +2029 -91
- package/app/js/modules/step-module.js +47 -6
- package/app/js/modules/surface-module.js +1040 -0
- package/app/js/modules/version-module.js +1830 -0
- package/app/js/modules/viewport-module.js +95 -8
- package/app/test-agent-v2.html +881 -1316
- package/cycleCAD-Architecture-v2.pptx +0 -0
- package/docs/ARCHITECTURE.html +838 -1408
- package/docs/DEVELOPER-GUIDE.md +1504 -0
- package/docs/TUTORIAL.md +740 -0
- package/package.json +1 -1
- package/~$cycleCAD-Architecture-v2.pptx +0 -0
- package/.github/scripts/cad-diff.js +0 -590
- 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)
|