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,1437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Help System Module for cycleCAD
|
|
3
|
+
* Interactive tutorials, tooltips, searchable help panel, keyboard shortcut reference
|
|
4
|
+
* Version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const HelpModule = {
|
|
8
|
+
id: 'help',
|
|
9
|
+
name: 'Help System',
|
|
10
|
+
version: '1.0.0',
|
|
11
|
+
category: 'service',
|
|
12
|
+
dependencies: [],
|
|
13
|
+
memoryEstimate: 5,
|
|
14
|
+
|
|
15
|
+
init() {
|
|
16
|
+
this.isOpen = false;
|
|
17
|
+
this.activeTutorial = null;
|
|
18
|
+
this.currentStep = 0;
|
|
19
|
+
this.tooltips = new Map();
|
|
20
|
+
this.helpEntries = this.buildHelpEntries();
|
|
21
|
+
this.tutorials = this.buildTutorials();
|
|
22
|
+
this.shortcuts = this.buildShortcuts();
|
|
23
|
+
|
|
24
|
+
this.setupUI();
|
|
25
|
+
this.setupEventListeners();
|
|
26
|
+
this.registerDefaultTooltips();
|
|
27
|
+
|
|
28
|
+
window.dispatchEvent(new CustomEvent('help:initialized', { detail: this }));
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
// ========== HELP ENTRIES DATABASE ==========
|
|
32
|
+
buildHelpEntries() {
|
|
33
|
+
return {
|
|
34
|
+
'getting-started': [
|
|
35
|
+
{
|
|
36
|
+
id: 'first-part',
|
|
37
|
+
title: 'Create Your First Part',
|
|
38
|
+
desc: 'Learn how to sketch a 2D profile and extrude it into 3D. This is the foundation of CAD modeling.',
|
|
39
|
+
shortcut: null,
|
|
40
|
+
tutorial: 'first-part',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'ai-copilot',
|
|
44
|
+
title: 'Use the AI Copilot',
|
|
45
|
+
desc: 'Ask the AI to generate parts, modify designs, and answer CAD questions in natural language.',
|
|
46
|
+
shortcut: 'Ctrl+/',
|
|
47
|
+
tutorial: 'ai-copilot',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'keyboard-basics',
|
|
51
|
+
title: 'Keyboard Shortcuts (Basics)',
|
|
52
|
+
desc: 'Essential shortcuts: Sketch (K), Extrude (E), Undo (Z), Pan (Middle-click), Zoom (Scroll).',
|
|
53
|
+
shortcut: '?',
|
|
54
|
+
tutorial: null,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 'saving',
|
|
58
|
+
title: 'Save and Load Your Work',
|
|
59
|
+
desc: 'Auto-save to browser storage. Export to STL, OBJ, glTF. Import STEP files from CAD software.',
|
|
60
|
+
shortcut: 'Ctrl+S',
|
|
61
|
+
tutorial: null,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'export-basics',
|
|
65
|
+
title: 'Export Your Model',
|
|
66
|
+
desc: 'Export to STL for 3D printing, OBJ for rendering, or glTF for web. DXF for drawings.',
|
|
67
|
+
shortcut: null,
|
|
68
|
+
tutorial: null,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
'sketch-tools': [
|
|
72
|
+
{
|
|
73
|
+
id: 'sketch-line',
|
|
74
|
+
title: 'Line Tool (L)',
|
|
75
|
+
desc: 'Draw straight lines. Click to set points, right-click or Escape to finish.',
|
|
76
|
+
shortcut: 'L',
|
|
77
|
+
tutorial: null,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'sketch-rect',
|
|
81
|
+
title: 'Rectangle Tool (R)',
|
|
82
|
+
desc: 'Draw axis-aligned rectangles by clicking two opposite corners.',
|
|
83
|
+
shortcut: 'R',
|
|
84
|
+
tutorial: null,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 'sketch-circle',
|
|
88
|
+
title: 'Circle Tool (C)',
|
|
89
|
+
desc: 'Click center, then click rim. Or drag to set radius. Shift+click to create concentric circles.',
|
|
90
|
+
shortcut: 'C',
|
|
91
|
+
tutorial: null,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 'sketch-arc',
|
|
95
|
+
title: 'Arc Tool (A)',
|
|
96
|
+
desc: 'Draw circular arcs. 3-point arc: center, start, end. Tangent arc from existing geometry.',
|
|
97
|
+
shortcut: 'A',
|
|
98
|
+
tutorial: null,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'sketch-polyline',
|
|
102
|
+
title: 'Polyline Tool (P)',
|
|
103
|
+
desc: 'Draw connected line segments. Click to add vertices, right-click to finish.',
|
|
104
|
+
shortcut: 'P',
|
|
105
|
+
tutorial: null,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'sketch-constraints',
|
|
109
|
+
title: 'Sketch Constraints',
|
|
110
|
+
desc: 'Constrain geometry: horizontal, vertical, parallel, perpendicular, tangent, coincident, distance, angle.',
|
|
111
|
+
shortcut: null,
|
|
112
|
+
tutorial: null,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: 'sketch-constrain-h',
|
|
116
|
+
title: 'Constrain Horizontal (H)',
|
|
117
|
+
desc: 'Make a line horizontal. Select line, press H.',
|
|
118
|
+
shortcut: 'H',
|
|
119
|
+
tutorial: null,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: 'sketch-constrain-v',
|
|
123
|
+
title: 'Constrain Vertical (V)',
|
|
124
|
+
desc: 'Make a line vertical. Select line, press V.',
|
|
125
|
+
shortcut: 'V',
|
|
126
|
+
tutorial: null,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'sketch-constrain-d',
|
|
130
|
+
title: 'Constrain Distance (D)',
|
|
131
|
+
desc: 'Set distance between points or from origin. Select geometry, press D, enter value.',
|
|
132
|
+
shortcut: 'D',
|
|
133
|
+
tutorial: null,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: 'sketch-constrain-angle',
|
|
137
|
+
title: 'Constrain Angle',
|
|
138
|
+
desc: 'Set angle between two lines. Select both, right-click → Angle.',
|
|
139
|
+
shortcut: null,
|
|
140
|
+
tutorial: null,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: 'sketch-mirror',
|
|
144
|
+
title: 'Mirror in Sketch',
|
|
145
|
+
desc: 'Mirror geometry across a centerline. Select geometry, select mirror line, right-click → Mirror.',
|
|
146
|
+
shortcut: null,
|
|
147
|
+
tutorial: null,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: 'sketch-offset',
|
|
151
|
+
title: 'Offset Tool',
|
|
152
|
+
desc: 'Create parallel copies of edges at a distance. Useful for walls, chamfers.',
|
|
153
|
+
shortcut: null,
|
|
154
|
+
tutorial: null,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: 'sketch-trim',
|
|
158
|
+
title: 'Trim/Extend Tool',
|
|
159
|
+
desc: 'Trim edges where they intersect, or extend them to intersect with other geometry.',
|
|
160
|
+
shortcut: null,
|
|
161
|
+
tutorial: null,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'sketch-construction',
|
|
165
|
+
title: 'Construction Geometry',
|
|
166
|
+
desc: 'Convert lines to construction mode (dashed) — they won\'t be used in extrudes/revolves.',
|
|
167
|
+
shortcut: null,
|
|
168
|
+
tutorial: null,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'sketch-dimensions',
|
|
172
|
+
title: 'Add Dimensions to Sketch',
|
|
173
|
+
desc: 'Double-click a line or distance to add a dimension. Modify the value to update geometry.',
|
|
174
|
+
shortcut: null,
|
|
175
|
+
tutorial: null,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: 'sketch-fully-constrained',
|
|
179
|
+
title: 'Fully Constrained Sketches',
|
|
180
|
+
desc: 'A sketch is fully constrained when it has no degrees of freedom. The profile turns white/green.',
|
|
181
|
+
shortcut: null,
|
|
182
|
+
tutorial: null,
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: 'sketch-exit',
|
|
186
|
+
title: 'Exit Sketch Mode',
|
|
187
|
+
desc: 'Press Escape or click "Exit Sketch" button to return to 3D view.',
|
|
188
|
+
shortcut: 'Escape',
|
|
189
|
+
tutorial: null,
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
'3d-operations': [
|
|
193
|
+
{
|
|
194
|
+
id: 'op-extrude',
|
|
195
|
+
title: 'Extrude (E)',
|
|
196
|
+
desc: 'Push a sketch profile into 3D. Select a sketch, press E, set depth, click confirm.',
|
|
197
|
+
shortcut: 'E',
|
|
198
|
+
tutorial: 'first-part',
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: 'op-revolve',
|
|
202
|
+
title: 'Revolve (Rev)',
|
|
203
|
+
desc: 'Rotate a sketch profile around an axis to create shapes like cylinders, cones, domes.',
|
|
204
|
+
shortcut: null,
|
|
205
|
+
tutorial: null,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
id: 'op-fillet',
|
|
209
|
+
title: 'Fillet (F)',
|
|
210
|
+
desc: 'Round sharp edges. Select edge(s), press F, set radius (1-10mm typical).',
|
|
211
|
+
shortcut: 'F',
|
|
212
|
+
tutorial: 'first-part',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: 'op-chamfer',
|
|
216
|
+
title: 'Chamfer (Ch)',
|
|
217
|
+
desc: 'Bevel edges at 45° or custom angle. Select edge(s), right-click → Chamfer.',
|
|
218
|
+
shortcut: null,
|
|
219
|
+
tutorial: null,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: 'op-hole',
|
|
223
|
+
title: 'Add Hole (H)',
|
|
224
|
+
desc: 'Create a hole through a body. Select face, press H, set diameter.',
|
|
225
|
+
shortcut: 'H',
|
|
226
|
+
tutorial: null,
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
id: 'op-pocket',
|
|
230
|
+
title: 'Pocket (Shallow Cut)',
|
|
231
|
+
desc: 'Cut a shallow depression (not through). Define sketch, select depth, confirm.',
|
|
232
|
+
shortcut: null,
|
|
233
|
+
tutorial: null,
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
id: 'op-boolean-union',
|
|
237
|
+
title: 'Boolean Union',
|
|
238
|
+
desc: 'Combine two bodies into one. Select both bodies, right-click → Union.',
|
|
239
|
+
shortcut: null,
|
|
240
|
+
tutorial: null,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
id: 'op-boolean-cut',
|
|
244
|
+
title: 'Boolean Cut',
|
|
245
|
+
desc: 'Subtract one body from another. Select target, select tool, right-click → Cut.',
|
|
246
|
+
shortcut: null,
|
|
247
|
+
tutorial: null,
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: 'op-boolean-intersect',
|
|
251
|
+
title: 'Boolean Intersect',
|
|
252
|
+
desc: 'Keep only the overlapping volume of two bodies.',
|
|
253
|
+
shortcut: null,
|
|
254
|
+
tutorial: null,
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: 'op-shell',
|
|
258
|
+
title: 'Shell (Hollow Out)',
|
|
259
|
+
desc: 'Make a solid body hollow with uniform wall thickness. Select body, set wall thickness.',
|
|
260
|
+
shortcut: null,
|
|
261
|
+
tutorial: null,
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
id: 'op-pattern-rect',
|
|
265
|
+
title: 'Rectangular Pattern',
|
|
266
|
+
desc: 'Array features in a grid. Set columns, rows, spacing.',
|
|
267
|
+
shortcut: null,
|
|
268
|
+
tutorial: null,
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
id: 'op-pattern-circ',
|
|
272
|
+
title: 'Circular Pattern',
|
|
273
|
+
desc: 'Array features around a center point. Set count and radius.',
|
|
274
|
+
shortcut: null,
|
|
275
|
+
tutorial: null,
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: 'op-mirror-3d',
|
|
279
|
+
title: 'Mirror (3D)',
|
|
280
|
+
desc: 'Mirror a feature across a plane. Select feature, select mirror plane.',
|
|
281
|
+
shortcut: null,
|
|
282
|
+
tutorial: null,
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
id: 'op-sweep',
|
|
286
|
+
title: 'Sweep (Profile Along Path)',
|
|
287
|
+
desc: 'Move a profile along a path. Defines complex shapes like hoses, tubing, structural members.',
|
|
288
|
+
shortcut: null,
|
|
289
|
+
tutorial: null,
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
id: 'op-loft',
|
|
293
|
+
title: 'Loft (Morph Between Profiles)',
|
|
294
|
+
desc: 'Smoothly transition between multiple profiles. Creates organic shapes.',
|
|
295
|
+
shortcut: null,
|
|
296
|
+
tutorial: null,
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
'assembly': [
|
|
300
|
+
{
|
|
301
|
+
id: 'asm-insert-part',
|
|
302
|
+
title: 'Insert Component',
|
|
303
|
+
desc: 'Add existing parts to an assembly. Right-click in tree → Insert, select .step or .obj file.',
|
|
304
|
+
shortcut: null,
|
|
305
|
+
tutorial: 'first-assembly',
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
id: 'asm-mate-coincident',
|
|
309
|
+
title: 'Coincident Mate',
|
|
310
|
+
desc: 'Align two faces, edges, or planes. Select geometry on each part, right-click → Mate.',
|
|
311
|
+
shortcut: null,
|
|
312
|
+
tutorial: null,
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
id: 'asm-mate-concentric',
|
|
316
|
+
title: 'Concentric Mate',
|
|
317
|
+
desc: 'Align two cylinders or circles (same axis, different radii for gears/bearings).',
|
|
318
|
+
shortcut: null,
|
|
319
|
+
tutorial: null,
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
id: 'asm-mate-distance',
|
|
323
|
+
title: 'Distance Mate',
|
|
324
|
+
desc: 'Set distance between two planes or faces. Useful for clearances, spacing.',
|
|
325
|
+
shortcut: null,
|
|
326
|
+
tutorial: null,
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
id: 'asm-mate-angle',
|
|
330
|
+
title: 'Angle Mate',
|
|
331
|
+
desc: 'Set angle between two planes. For hinges, bent assemblies.',
|
|
332
|
+
shortcut: null,
|
|
333
|
+
tutorial: null,
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
id: 'asm-explode',
|
|
337
|
+
title: 'Explode View',
|
|
338
|
+
desc: 'Separate parts to show assembly structure. Drag slider or press "Explode" button.',
|
|
339
|
+
shortcut: null,
|
|
340
|
+
tutorial: null,
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
id: 'asm-bom',
|
|
344
|
+
title: 'Generate BOM (Bill of Materials)',
|
|
345
|
+
desc: 'Auto-generate parts list with quantities. Export to CSV.',
|
|
346
|
+
shortcut: null,
|
|
347
|
+
tutorial: null,
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
id: 'asm-interference',
|
|
351
|
+
title: 'Check Interference',
|
|
352
|
+
desc: 'Detect parts that overlap (shouldn\'t). Helps catch design errors.',
|
|
353
|
+
shortcut: null,
|
|
354
|
+
tutorial: null,
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
'drawing': [
|
|
358
|
+
{
|
|
359
|
+
id: 'draw-new',
|
|
360
|
+
title: 'Create Drawing',
|
|
361
|
+
desc: 'Create a 2D engineering drawing from a 3D model. Auto-generate orthogonal views.',
|
|
362
|
+
shortcut: null,
|
|
363
|
+
tutorial: null,
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
id: 'draw-views',
|
|
367
|
+
title: 'Add Views',
|
|
368
|
+
desc: 'Front, Top, Right, Isometric, custom angle views. Drag to position.',
|
|
369
|
+
shortcut: null,
|
|
370
|
+
tutorial: null,
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
id: 'draw-dimension',
|
|
374
|
+
title: 'Add Dimension',
|
|
375
|
+
desc: 'Measure distance, radius, angle, diameter in drawing. Click two edges/vertices.',
|
|
376
|
+
shortcut: null,
|
|
377
|
+
tutorial: null,
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
id: 'draw-gdt',
|
|
381
|
+
title: 'GD&T (Geometric Dimensioning & Tolerancing)',
|
|
382
|
+
desc: 'Add form/profile/runout tolerance symbols. ISO 1101 standard callouts.',
|
|
383
|
+
shortcut: null,
|
|
384
|
+
tutorial: null,
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
id: 'draw-tolerance',
|
|
388
|
+
title: 'Add Tolerance',
|
|
389
|
+
desc: 'Specify +/- limits on dimensions (e.g., 25.0 +0.05/-0.1).',
|
|
390
|
+
shortcut: null,
|
|
391
|
+
tutorial: null,
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
id: 'draw-annotation',
|
|
395
|
+
title: 'Add Annotation',
|
|
396
|
+
desc: 'Add text notes, material callouts, surface finish (Ra, Rz values).',
|
|
397
|
+
shortcut: null,
|
|
398
|
+
tutorial: null,
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
id: 'draw-title-block',
|
|
402
|
+
title: 'Title Block & Border',
|
|
403
|
+
desc: 'Standard drawing frame with company logo, scale, revision, date.',
|
|
404
|
+
shortcut: null,
|
|
405
|
+
tutorial: null,
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
id: 'draw-export-pdf',
|
|
409
|
+
title: 'Export to PDF',
|
|
410
|
+
desc: 'Generate PDF drawing for printing or sharing. Maintains dimensions and view layout.',
|
|
411
|
+
shortcut: null,
|
|
412
|
+
tutorial: null,
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
id: 'draw-section',
|
|
416
|
+
title: 'Section View',
|
|
417
|
+
desc: 'Create cut-away views showing internal structure.',
|
|
418
|
+
shortcut: null,
|
|
419
|
+
tutorial: null,
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
id: 'draw-detail',
|
|
423
|
+
title: 'Detail View',
|
|
424
|
+
desc: 'Zoomed inset showing high-detail area of main view.',
|
|
425
|
+
shortcut: null,
|
|
426
|
+
tutorial: null,
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
'simulation': [
|
|
430
|
+
{
|
|
431
|
+
id: 'sim-setup',
|
|
432
|
+
title: 'Setup Simulation',
|
|
433
|
+
desc: 'Choose analysis type: Structural, Thermal, Modal, Flow. Set boundary conditions.',
|
|
434
|
+
shortcut: null,
|
|
435
|
+
tutorial: null,
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
id: 'sim-material',
|
|
439
|
+
title: 'Assign Material',
|
|
440
|
+
desc: 'Select material from library (Steel, Aluminum, Plastic). View properties.',
|
|
441
|
+
shortcut: null,
|
|
442
|
+
tutorial: null,
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
id: 'sim-loads',
|
|
446
|
+
title: 'Apply Loads',
|
|
447
|
+
desc: 'Force, pressure, temperature, velocity. Click face, set magnitude and direction.',
|
|
448
|
+
shortcut: null,
|
|
449
|
+
tutorial: null,
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
id: 'sim-mesh',
|
|
453
|
+
title: 'Mesh',
|
|
454
|
+
desc: 'Generate finite element mesh. Fine mesh = accurate but slower. Coarse = fast.',
|
|
455
|
+
shortcut: null,
|
|
456
|
+
tutorial: null,
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
id: 'sim-solve',
|
|
460
|
+
title: 'Solve',
|
|
461
|
+
desc: 'Run simulation. Iterative solver calculates stress, strain, temperature, displacement.',
|
|
462
|
+
shortcut: null,
|
|
463
|
+
tutorial: null,
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
id: 'sim-results',
|
|
467
|
+
title: 'View Results',
|
|
468
|
+
desc: 'Color-coded stress/strain maps, deformation animations, data table export.',
|
|
469
|
+
shortcut: null,
|
|
470
|
+
tutorial: null,
|
|
471
|
+
},
|
|
472
|
+
],
|
|
473
|
+
'import-export': [
|
|
474
|
+
{
|
|
475
|
+
id: 'io-import-step',
|
|
476
|
+
title: 'Import STEP File',
|
|
477
|
+
desc: 'Load 3D models from CAD software (SolidWorks, Fusion 360, AutoCAD). File → Import.',
|
|
478
|
+
shortcut: null,
|
|
479
|
+
tutorial: 'import-step',
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
id: 'io-import-stl',
|
|
483
|
+
title: 'Import STL File',
|
|
484
|
+
desc: 'Load 3D scanned/printed parts as mesh geometry. Useful for reverse engineering.',
|
|
485
|
+
shortcut: null,
|
|
486
|
+
tutorial: null,
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
id: 'io-import-obj',
|
|
490
|
+
title: 'Import OBJ File',
|
|
491
|
+
desc: 'Load polygon meshes. Good for artwork, scanned data, CAM output.',
|
|
492
|
+
shortcut: null,
|
|
493
|
+
tutorial: null,
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
id: 'io-export-step',
|
|
497
|
+
title: 'Export STEP File',
|
|
498
|
+
desc: 'Save as STEP for sharing with other CAD software (SolidWorks, AutoCAD, FreeCAD).',
|
|
499
|
+
shortcut: null,
|
|
500
|
+
tutorial: null,
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
id: 'io-export-stl',
|
|
504
|
+
title: 'Export STL for 3D Printing',
|
|
505
|
+
desc: 'Binary STL format for slicers (Cura, PrusaSlicer). Binary is smaller.',
|
|
506
|
+
shortcut: null,
|
|
507
|
+
tutorial: null,
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
id: 'io-export-obj',
|
|
511
|
+
title: 'Export OBJ File',
|
|
512
|
+
desc: 'Mesh format for rendering, animation, CAM. Keep textures in separate files.',
|
|
513
|
+
shortcut: null,
|
|
514
|
+
tutorial: null,
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
id: 'io-export-gltf',
|
|
518
|
+
title: 'Export glTF/GLTB (Web 3D)',
|
|
519
|
+
desc: 'Modern 3D format for web viewing, AR, VR. Includes materials and animations.',
|
|
520
|
+
shortcut: null,
|
|
521
|
+
tutorial: null,
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
id: 'io-export-dxf',
|
|
525
|
+
title: 'Export DXF (2D Drawing)',
|
|
526
|
+
desc: 'AutoCAD 2D format. Flattens 3D model to orthogonal views.',
|
|
527
|
+
shortcut: null,
|
|
528
|
+
tutorial: null,
|
|
529
|
+
},
|
|
530
|
+
],
|
|
531
|
+
'ai-features': [
|
|
532
|
+
{
|
|
533
|
+
id: 'ai-text-to-cad',
|
|
534
|
+
title: 'Text-to-CAD',
|
|
535
|
+
desc: 'Describe a part in English, AI generates a 3D model. "Create a 50mm hex bolt with 20mm height"',
|
|
536
|
+
shortcut: 'Ctrl+/',
|
|
537
|
+
tutorial: 'ai-copilot',
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
id: 'ai-part-id',
|
|
541
|
+
title: 'AI Part Identifier',
|
|
542
|
+
desc: 'Take a photo or 3D scan of a part, AI identifies it (fastener, bearing, spring, etc) and suggests sources.',
|
|
543
|
+
shortcut: null,
|
|
544
|
+
tutorial: null,
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
id: 'ai-batch-scan',
|
|
548
|
+
title: 'Batch AI Scan',
|
|
549
|
+
desc: 'Scan all parts in an assembly at once. AI auto-identifies and color-codes by type.',
|
|
550
|
+
shortcut: null,
|
|
551
|
+
tutorial: null,
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
id: 'ai-chatbot',
|
|
555
|
+
title: 'AI Chatbot',
|
|
556
|
+
desc: 'Ask questions about your design: "What\'s the weight?" "Can this handle 50N force?" "Suggest fasteners."',
|
|
557
|
+
shortcut: 'Ctrl+?',
|
|
558
|
+
tutorial: null,
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
id: 'ai-design-review',
|
|
562
|
+
title: 'AI Design Review',
|
|
563
|
+
desc: 'AI analyzes model for: manufacturability, cost, weight, fit issues. Suggests improvements.',
|
|
564
|
+
shortcut: null,
|
|
565
|
+
tutorial: null,
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
'agent-api': [
|
|
569
|
+
{
|
|
570
|
+
id: 'agent-execute',
|
|
571
|
+
title: 'cycleCAD.execute()',
|
|
572
|
+
desc: 'Main API entry point. Call with { method, params }. Returns promise. Async operations safe.',
|
|
573
|
+
shortcut: null,
|
|
574
|
+
tutorial: null,
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
id: 'agent-shape',
|
|
578
|
+
title: 'shape.* Namespace',
|
|
579
|
+
desc: 'Create geometry: shape.cylinder, shape.box, shape.sphere, shape.cone, shape.torus',
|
|
580
|
+
shortcut: null,
|
|
581
|
+
tutorial: null,
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
id: 'agent-feature',
|
|
585
|
+
title: 'feature.* Namespace',
|
|
586
|
+
desc: 'Modify geometry: feature.fillet, feature.chamfer, feature.hole, feature.pattern',
|
|
587
|
+
shortcut: null,
|
|
588
|
+
tutorial: null,
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
id: 'agent-render',
|
|
592
|
+
title: 'render.* Namespace',
|
|
593
|
+
desc: 'View control: render.snapshot, render.fitToAll, render.setView, render.color',
|
|
594
|
+
shortcut: null,
|
|
595
|
+
tutorial: null,
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
id: 'agent-validate',
|
|
599
|
+
title: 'validate.* Namespace',
|
|
600
|
+
desc: 'Check design: validate.designReview, validate.weight, validate.manufacturability',
|
|
601
|
+
shortcut: null,
|
|
602
|
+
tutorial: null,
|
|
603
|
+
},
|
|
604
|
+
],
|
|
605
|
+
};
|
|
606
|
+
},
|
|
607
|
+
|
|
608
|
+
// ========== TUTORIALS DATABASE ==========
|
|
609
|
+
buildTutorials() {
|
|
610
|
+
return {
|
|
611
|
+
'first-part': {
|
|
612
|
+
id: 'first-part',
|
|
613
|
+
name: 'Create Your First Part',
|
|
614
|
+
desc: 'Learn CAD fundamentals: sketch a 2D profile and extrude it into 3D.',
|
|
615
|
+
duration: '5 min',
|
|
616
|
+
steps: [
|
|
617
|
+
{
|
|
618
|
+
target: '#sketch-btn',
|
|
619
|
+
title: 'Step 1: Start a Sketch',
|
|
620
|
+
text: 'Click the Sketch button (or press K). This enters 2D sketch mode on the XY plane. You\'ll see a top-down view with a grid.',
|
|
621
|
+
position: 'bottom',
|
|
622
|
+
action: 'click',
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
target: '#rect-tool',
|
|
626
|
+
title: 'Step 2: Draw a Rectangle',
|
|
627
|
+
text: 'Click the Rectangle tool (or press R). Then click two opposite corners on the grid to create a rectangle, like 50mm × 30mm. Keep it simple!',
|
|
628
|
+
position: 'right',
|
|
629
|
+
action: 'click',
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
target: null,
|
|
633
|
+
title: 'Step 3: Add Dimensions',
|
|
634
|
+
text: 'Double-click one edge of your rectangle to add a dimension. Type "50" and press Enter. Repeat for the other edge: "30". This makes your sketch parametric.',
|
|
635
|
+
position: 'center',
|
|
636
|
+
action: 'none',
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
target: '#sketch-exit-btn',
|
|
640
|
+
title: 'Step 4: Exit Sketch Mode',
|
|
641
|
+
text: 'Press Escape or click the "Exit Sketch" button. You\'ll return to 3D view with your 2D profile outlined.',
|
|
642
|
+
position: 'bottom',
|
|
643
|
+
action: 'press-key',
|
|
644
|
+
key: 'Escape',
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
target: '#extrude-btn',
|
|
648
|
+
title: 'Step 5: Extrude to 3D',
|
|
649
|
+
text: 'Click the Extrude button (or press E). A 3D preview appears. Set the height to 20mm by typing in the dialog or moving the slider. Your 2D rectangle becomes a 3D box!',
|
|
650
|
+
position: 'right',
|
|
651
|
+
action: 'click',
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
target: '#extrude-confirm',
|
|
655
|
+
title: 'Step 6: Confirm',
|
|
656
|
+
text: 'Click "Confirm" to finish the extrude. Your first 3D part is born!',
|
|
657
|
+
position: 'bottom',
|
|
658
|
+
action: 'click',
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
target: '#fillet-btn',
|
|
662
|
+
title: 'Step 7: Add Fillet (Optional)',
|
|
663
|
+
text: 'To make the edges smooth, click the Fillet button and select an edge. Set radius to 2mm. This gives your part a professional look.',
|
|
664
|
+
position: 'right',
|
|
665
|
+
action: 'click',
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
target: '#export-menu',
|
|
669
|
+
title: 'Step 8: Export Your Part',
|
|
670
|
+
text: 'Click Export and choose STL format. Save the file. You now have a 3D-printable part! Send to a printer, or share with colleagues.',
|
|
671
|
+
position: 'left',
|
|
672
|
+
action: 'click',
|
|
673
|
+
},
|
|
674
|
+
],
|
|
675
|
+
},
|
|
676
|
+
'ai-copilot': {
|
|
677
|
+
id: 'ai-copilot',
|
|
678
|
+
name: 'Use the AI Copilot',
|
|
679
|
+
desc: 'Let AI generate parts from text descriptions.',
|
|
680
|
+
duration: '3 min',
|
|
681
|
+
steps: [
|
|
682
|
+
{
|
|
683
|
+
target: '#ai-chat-btn',
|
|
684
|
+
title: 'Open AI Copilot',
|
|
685
|
+
text: 'Click the AI Chat button (or press Ctrl+/). The chat panel opens on the right.',
|
|
686
|
+
position: 'left',
|
|
687
|
+
action: 'click',
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
target: '#ai-input',
|
|
691
|
+
title: 'Type a Part Request',
|
|
692
|
+
text: 'In the chat box, type: "Create a 50mm diameter cylinder with 80mm height." The AI understands natural language.',
|
|
693
|
+
position: 'bottom',
|
|
694
|
+
action: 'type',
|
|
695
|
+
text_input: 'Create a 50mm diameter cylinder with 80mm height',
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
target: null,
|
|
699
|
+
title: 'AI Generates 3D',
|
|
700
|
+
text: 'Press Enter. The AI parses your text, calls cycleCAD API, and renders the cylinder in the 3D viewport. Instant CAD!',
|
|
701
|
+
position: 'center',
|
|
702
|
+
action: 'none',
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
target: '#ai-input',
|
|
706
|
+
title: 'Refine with Follow-up',
|
|
707
|
+
text: 'Type: "Add a 20mm hole in the center." AI modifies the existing part.',
|
|
708
|
+
position: 'bottom',
|
|
709
|
+
action: 'type',
|
|
710
|
+
text_input: 'Add a 20mm hole in the center',
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
target: '#export-menu',
|
|
714
|
+
title: 'Export Result',
|
|
715
|
+
text: 'When satisfied, click Export → STL. The AI-generated part is ready to manufacture.',
|
|
716
|
+
position: 'left',
|
|
717
|
+
action: 'click',
|
|
718
|
+
},
|
|
719
|
+
],
|
|
720
|
+
},
|
|
721
|
+
'first-assembly': {
|
|
722
|
+
id: 'first-assembly',
|
|
723
|
+
name: 'Build an Assembly',
|
|
724
|
+
desc: 'Insert components and add mates to create an assembly.',
|
|
725
|
+
duration: '7 min',
|
|
726
|
+
steps: [
|
|
727
|
+
{
|
|
728
|
+
target: '#asm-new-btn',
|
|
729
|
+
title: 'Create Assembly',
|
|
730
|
+
text: 'Right-click in the tree and select "New Assembly". This creates an empty assembly document.',
|
|
731
|
+
position: 'left',
|
|
732
|
+
action: 'right-click',
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
target: '#asm-insert-btn',
|
|
736
|
+
title: 'Insert First Component',
|
|
737
|
+
text: 'Click "Insert Component" and select a STEP file or OBJ. This adds the first part to the assembly.',
|
|
738
|
+
position: 'bottom',
|
|
739
|
+
action: 'click',
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
target: null,
|
|
743
|
+
title: 'Position First Part',
|
|
744
|
+
text: 'The part appears at the origin. You can drag it to move. Use Ctrl+mouse to rotate.',
|
|
745
|
+
position: 'center',
|
|
746
|
+
action: 'none',
|
|
747
|
+
},
|
|
748
|
+
{
|
|
749
|
+
target: '#asm-insert-btn',
|
|
750
|
+
title: 'Insert Second Component',
|
|
751
|
+
text: 'Click "Insert Component" again and add another part.',
|
|
752
|
+
position: 'bottom',
|
|
753
|
+
action: 'click',
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
target: null,
|
|
757
|
+
title: 'Add a Mate',
|
|
758
|
+
text: 'Select a face on part 1, hold Ctrl and select the mating face on part 2. Right-click → "Coincident Mate". The parts snap together.',
|
|
759
|
+
position: 'center',
|
|
760
|
+
action: 'none',
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
target: '#asm-explode-slider',
|
|
764
|
+
title: 'Explode View',
|
|
765
|
+
text: 'Drag the "Explode" slider to separate parts and see the assembly structure. Great for showing how it assembles.',
|
|
766
|
+
position: 'right',
|
|
767
|
+
action: 'drag',
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
target: '#asm-bom-btn',
|
|
771
|
+
title: 'Generate BOM',
|
|
772
|
+
text: 'Click "Generate BOM" to create a parts list. Export to CSV for procurement.',
|
|
773
|
+
position: 'bottom',
|
|
774
|
+
action: 'click',
|
|
775
|
+
},
|
|
776
|
+
],
|
|
777
|
+
},
|
|
778
|
+
'import-step': {
|
|
779
|
+
id: 'import-step',
|
|
780
|
+
name: 'Import STEP File',
|
|
781
|
+
desc: 'Load a 3D CAD model from desktop software.',
|
|
782
|
+
duration: '2 min',
|
|
783
|
+
steps: [
|
|
784
|
+
{
|
|
785
|
+
target: '#file-import-btn',
|
|
786
|
+
title: 'Open File Import',
|
|
787
|
+
text: 'Click File → Import (or drag a .step file onto the viewport).',
|
|
788
|
+
position: 'top',
|
|
789
|
+
action: 'click',
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
target: null,
|
|
793
|
+
title: 'Select STEP File',
|
|
794
|
+
text: 'Browse your computer and select a .stp or .step file. These come from SolidWorks, Fusion 360, AutoCAD, etc.',
|
|
795
|
+
position: 'center',
|
|
796
|
+
action: 'none',
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
target: null,
|
|
800
|
+
title: 'AI Parses It',
|
|
801
|
+
text: 'cycleCAD uses OpenCascade.js to parse the STEP file. Large files may take a few seconds. Progress bar shows status.',
|
|
802
|
+
position: 'center',
|
|
803
|
+
action: 'none',
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
target: null,
|
|
807
|
+
title: 'Explore Model',
|
|
808
|
+
text: 'Once loaded, rotate (middle-click drag), zoom (scroll), pan (right-click drag). Click parts to select them. The tree shows assembly structure.',
|
|
809
|
+
position: 'center',
|
|
810
|
+
action: 'none',
|
|
811
|
+
},
|
|
812
|
+
],
|
|
813
|
+
},
|
|
814
|
+
};
|
|
815
|
+
},
|
|
816
|
+
|
|
817
|
+
// ========== KEYBOARD SHORTCUTS DATABASE ==========
|
|
818
|
+
buildShortcuts() {
|
|
819
|
+
return {
|
|
820
|
+
'sketch-mode': [
|
|
821
|
+
{ key: 'K', desc: 'Enter Sketch mode' },
|
|
822
|
+
{ key: 'L', desc: 'Line tool' },
|
|
823
|
+
{ key: 'R', desc: 'Rectangle tool' },
|
|
824
|
+
{ key: 'C', desc: 'Circle tool' },
|
|
825
|
+
{ key: 'A', desc: 'Arc tool' },
|
|
826
|
+
{ key: 'P', desc: 'Polyline tool' },
|
|
827
|
+
{ key: 'H', desc: 'Constrain Horizontal' },
|
|
828
|
+
{ key: 'V', desc: 'Constrain Vertical' },
|
|
829
|
+
{ key: 'D', desc: 'Constrain Distance' },
|
|
830
|
+
{ key: 'Escape', desc: 'Exit Sketch mode' },
|
|
831
|
+
],
|
|
832
|
+
'3d-operations': [
|
|
833
|
+
{ key: 'E', desc: 'Extrude' },
|
|
834
|
+
{ key: 'F', desc: 'Fillet' },
|
|
835
|
+
{ key: 'Z', desc: 'Undo' },
|
|
836
|
+
{ key: 'Shift+Z', desc: 'Redo' },
|
|
837
|
+
{ key: 'Delete', desc: 'Delete selected' },
|
|
838
|
+
],
|
|
839
|
+
'viewport': [
|
|
840
|
+
{ key: 'Middle-click drag', desc: 'Rotate view' },
|
|
841
|
+
{ key: 'Right-click drag', desc: 'Pan view' },
|
|
842
|
+
{ key: 'Scroll', desc: 'Zoom in/out' },
|
|
843
|
+
{ key: 'G', desc: 'Toggle grid' },
|
|
844
|
+
{ key: 'V', desc: 'Toggle wireframe' },
|
|
845
|
+
{ key: 'Ctrl+0', desc: 'Fit all' },
|
|
846
|
+
],
|
|
847
|
+
'general': [
|
|
848
|
+
{ key: '?', desc: 'Show keyboard shortcuts' },
|
|
849
|
+
{ key: 'Ctrl+/', desc: 'Open AI Copilot' },
|
|
850
|
+
{ key: 'Ctrl+S', desc: 'Save' },
|
|
851
|
+
{ key: 'Ctrl+O', desc: 'Open file' },
|
|
852
|
+
],
|
|
853
|
+
};
|
|
854
|
+
},
|
|
855
|
+
|
|
856
|
+
// ========== UI SETUP ==========
|
|
857
|
+
setupUI() {
|
|
858
|
+
// Help button in top bar
|
|
859
|
+
const helpBtn = document.createElement('button');
|
|
860
|
+
helpBtn.id = 'help-btn';
|
|
861
|
+
helpBtn.innerHTML = '?';
|
|
862
|
+
helpBtn.title = 'Help (Press ?)';
|
|
863
|
+
helpBtn.style.cssText = `
|
|
864
|
+
position: fixed;
|
|
865
|
+
top: 10px;
|
|
866
|
+
right: 60px;
|
|
867
|
+
width: 40px;
|
|
868
|
+
height: 40px;
|
|
869
|
+
border-radius: 50%;
|
|
870
|
+
background: #0284C7;
|
|
871
|
+
color: white;
|
|
872
|
+
border: none;
|
|
873
|
+
font-size: 20px;
|
|
874
|
+
font-weight: bold;
|
|
875
|
+
cursor: pointer;
|
|
876
|
+
z-index: 9998;
|
|
877
|
+
transition: all 0.2s;
|
|
878
|
+
`;
|
|
879
|
+
helpBtn.onmouseover = () => helpBtn.style.background = '#0369A1';
|
|
880
|
+
helpBtn.onmouseout = () => helpBtn.style.background = '#0284C7';
|
|
881
|
+
document.body.appendChild(helpBtn);
|
|
882
|
+
|
|
883
|
+
// Help panel HTML
|
|
884
|
+
const panelHTML = `
|
|
885
|
+
<div id="help-panel" style="
|
|
886
|
+
position: fixed;
|
|
887
|
+
right: -400px;
|
|
888
|
+
top: 0;
|
|
889
|
+
width: 400px;
|
|
890
|
+
height: 100vh;
|
|
891
|
+
background: white;
|
|
892
|
+
border-left: 1px solid #ddd;
|
|
893
|
+
box-shadow: -2px 0 8px rgba(0,0,0,0.1);
|
|
894
|
+
z-index: 9997;
|
|
895
|
+
transition: right 0.3s;
|
|
896
|
+
display: flex;
|
|
897
|
+
flex-direction: column;
|
|
898
|
+
font-family: Calibri, sans-serif;
|
|
899
|
+
">
|
|
900
|
+
<div style="padding: 15px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center;">
|
|
901
|
+
<h2 style="margin: 0; font-size: 18px;">Help</h2>
|
|
902
|
+
<button id="help-close-btn" style="background: none; border: none; font-size: 20px; cursor: pointer; padding: 0;">×</button>
|
|
903
|
+
</div>
|
|
904
|
+
|
|
905
|
+
<input type="text" id="help-search" placeholder="Search help..." style="
|
|
906
|
+
margin: 10px;
|
|
907
|
+
padding: 8px;
|
|
908
|
+
border: 1px solid #ddd;
|
|
909
|
+
border-radius: 4px;
|
|
910
|
+
font-size: 14px;
|
|
911
|
+
" />
|
|
912
|
+
|
|
913
|
+
<div id="help-content" style="
|
|
914
|
+
flex: 1;
|
|
915
|
+
overflow-y: auto;
|
|
916
|
+
padding: 10px;
|
|
917
|
+
"></div>
|
|
918
|
+
</div>
|
|
919
|
+
|
|
920
|
+
<div id="help-tutorial-overlay" style="
|
|
921
|
+
position: fixed;
|
|
922
|
+
top: 0;
|
|
923
|
+
left: 0;
|
|
924
|
+
width: 100%;
|
|
925
|
+
height: 100%;
|
|
926
|
+
background: rgba(0,0,0,0.7);
|
|
927
|
+
z-index: 9996;
|
|
928
|
+
display: none;
|
|
929
|
+
"></div>
|
|
930
|
+
|
|
931
|
+
<div id="help-tooltip" style="
|
|
932
|
+
position: fixed;
|
|
933
|
+
background: #333;
|
|
934
|
+
color: white;
|
|
935
|
+
padding: 8px 12px;
|
|
936
|
+
border-radius: 4px;
|
|
937
|
+
font-size: 12px;
|
|
938
|
+
z-index: 9999;
|
|
939
|
+
display: none;
|
|
940
|
+
max-width: 250px;
|
|
941
|
+
pointer-events: none;
|
|
942
|
+
"></div>
|
|
943
|
+
|
|
944
|
+
<div id="help-shortcuts-overlay" style="
|
|
945
|
+
position: fixed;
|
|
946
|
+
top: 0;
|
|
947
|
+
left: 0;
|
|
948
|
+
width: 100%;
|
|
949
|
+
height: 100%;
|
|
950
|
+
background: rgba(0,0,0,0.8);
|
|
951
|
+
z-index: 9999;
|
|
952
|
+
display: none;
|
|
953
|
+
overflow-y: auto;
|
|
954
|
+
padding: 40px;
|
|
955
|
+
"></div>
|
|
956
|
+
`;
|
|
957
|
+
document.body.insertAdjacentHTML('beforeend', panelHTML);
|
|
958
|
+
|
|
959
|
+
// Initialize content
|
|
960
|
+
this.renderHelpContent();
|
|
961
|
+
},
|
|
962
|
+
|
|
963
|
+
renderHelpContent() {
|
|
964
|
+
const content = document.getElementById('help-content');
|
|
965
|
+
if (!content) return;
|
|
966
|
+
|
|
967
|
+
content.innerHTML = '';
|
|
968
|
+
for (const [category, entries] of Object.entries(this.helpEntries)) {
|
|
969
|
+
const categoryDiv = document.createElement('div');
|
|
970
|
+
categoryDiv.style.marginBottom = '15px';
|
|
971
|
+
|
|
972
|
+
const catTitle = document.createElement('h3');
|
|
973
|
+
catTitle.style.cssText = `
|
|
974
|
+
margin: 10px 0 5px 0;
|
|
975
|
+
font-size: 13px;
|
|
976
|
+
color: #0284C7;
|
|
977
|
+
font-weight: bold;
|
|
978
|
+
cursor: pointer;
|
|
979
|
+
padding: 5px;
|
|
980
|
+
border-radius: 3px;
|
|
981
|
+
`;
|
|
982
|
+
catTitle.textContent = this.formatCategoryName(category);
|
|
983
|
+
catTitle.onmouseover = () => catTitle.style.background = '#f0f0f0';
|
|
984
|
+
catTitle.onmouseout = () => catTitle.style.background = 'transparent';
|
|
985
|
+
|
|
986
|
+
const entriesList = document.createElement('div');
|
|
987
|
+
entriesList.style.marginLeft = '10px';
|
|
988
|
+
entriesList.className = 'help-category-entries';
|
|
989
|
+
|
|
990
|
+
entries.forEach(entry => {
|
|
991
|
+
const entryDiv = document.createElement('div');
|
|
992
|
+
entryDiv.style.cssText = `
|
|
993
|
+
margin-bottom: 8px;
|
|
994
|
+
padding: 8px;
|
|
995
|
+
border-radius: 3px;
|
|
996
|
+
background: #f9f9f9;
|
|
997
|
+
cursor: pointer;
|
|
998
|
+
transition: all 0.2s;
|
|
999
|
+
`;
|
|
1000
|
+
entryDiv.onmouseover = () => {
|
|
1001
|
+
entryDiv.style.background = '#e8f4f8';
|
|
1002
|
+
entryDiv.style.borderLeft = '3px solid #0284C7';
|
|
1003
|
+
};
|
|
1004
|
+
entryDiv.onmouseout = () => {
|
|
1005
|
+
entryDiv.style.background = '#f9f9f9';
|
|
1006
|
+
entryDiv.style.borderLeft = 'none';
|
|
1007
|
+
};
|
|
1008
|
+
|
|
1009
|
+
const titleSpan = document.createElement('div');
|
|
1010
|
+
titleSpan.style.cssText = 'font-size: 12px; font-weight: bold; margin-bottom: 3px;';
|
|
1011
|
+
titleSpan.textContent = entry.title;
|
|
1012
|
+
|
|
1013
|
+
const descSpan = document.createElement('div');
|
|
1014
|
+
descSpan.style.cssText = 'font-size: 11px; color: #666; line-height: 1.4;';
|
|
1015
|
+
descSpan.textContent = entry.desc;
|
|
1016
|
+
|
|
1017
|
+
if (entry.shortcut) {
|
|
1018
|
+
const shortcutSpan = document.createElement('div');
|
|
1019
|
+
shortcutSpan.style.cssText = `
|
|
1020
|
+
font-size: 10px;
|
|
1021
|
+
color: #999;
|
|
1022
|
+
margin-top: 3px;
|
|
1023
|
+
font-family: monospace;
|
|
1024
|
+
`;
|
|
1025
|
+
shortcutSpan.textContent = `Shortcut: ${entry.shortcut}`;
|
|
1026
|
+
entryDiv.appendChild(shortcutSpan);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
entryDiv.appendChild(titleSpan);
|
|
1030
|
+
entryDiv.appendChild(descSpan);
|
|
1031
|
+
|
|
1032
|
+
if (entry.tutorial) {
|
|
1033
|
+
const tutorialBtn = document.createElement('button');
|
|
1034
|
+
tutorialBtn.textContent = 'Start Tutorial';
|
|
1035
|
+
tutorialBtn.style.cssText = `
|
|
1036
|
+
margin-top: 5px;
|
|
1037
|
+
padding: 4px 8px;
|
|
1038
|
+
font-size: 10px;
|
|
1039
|
+
background: #0284C7;
|
|
1040
|
+
color: white;
|
|
1041
|
+
border: none;
|
|
1042
|
+
border-radius: 3px;
|
|
1043
|
+
cursor: pointer;
|
|
1044
|
+
`;
|
|
1045
|
+
tutorialBtn.onclick = (e) => {
|
|
1046
|
+
e.stopPropagation();
|
|
1047
|
+
this.startTutorial(entry.tutorial);
|
|
1048
|
+
};
|
|
1049
|
+
entryDiv.appendChild(tutorialBtn);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
entriesList.appendChild(entryDiv);
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
catTitle.onclick = () => {
|
|
1056
|
+
const isVisible = entriesList.style.display !== 'none';
|
|
1057
|
+
entriesList.style.display = isVisible ? 'none' : 'block';
|
|
1058
|
+
catTitle.style.fontWeight = isVisible ? 'normal' : 'bold';
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
categoryDiv.appendChild(catTitle);
|
|
1062
|
+
categoryDiv.appendChild(entriesList);
|
|
1063
|
+
content.appendChild(categoryDiv);
|
|
1064
|
+
}
|
|
1065
|
+
},
|
|
1066
|
+
|
|
1067
|
+
formatCategoryName(category) {
|
|
1068
|
+
const names = {
|
|
1069
|
+
'getting-started': 'Getting Started',
|
|
1070
|
+
'sketch-tools': 'Sketch Tools',
|
|
1071
|
+
'3d-operations': '3D Operations',
|
|
1072
|
+
'assembly': 'Assembly',
|
|
1073
|
+
'drawing': 'Drawing',
|
|
1074
|
+
'simulation': 'Simulation',
|
|
1075
|
+
'import-export': 'Import/Export',
|
|
1076
|
+
'ai-features': 'AI Features',
|
|
1077
|
+
'agent-api': 'Agent API',
|
|
1078
|
+
};
|
|
1079
|
+
return names[category] || category;
|
|
1080
|
+
},
|
|
1081
|
+
|
|
1082
|
+
setupEventListeners() {
|
|
1083
|
+
const helpBtn = document.getElementById('help-btn');
|
|
1084
|
+
const closeBtn = document.getElementById('help-close-btn');
|
|
1085
|
+
const panel = document.getElementById('help-panel');
|
|
1086
|
+
const searchInput = document.getElementById('help-search');
|
|
1087
|
+
|
|
1088
|
+
helpBtn.onclick = () => this.toggle();
|
|
1089
|
+
closeBtn.onclick = () => this.close();
|
|
1090
|
+
|
|
1091
|
+
searchInput.oninput = (e) => this.search(e.target.value);
|
|
1092
|
+
|
|
1093
|
+
// Keyboard shortcuts
|
|
1094
|
+
document.addEventListener('keydown', (e) => {
|
|
1095
|
+
if (e.key === '?') {
|
|
1096
|
+
e.preventDefault();
|
|
1097
|
+
this.showShortcutOverlay();
|
|
1098
|
+
}
|
|
1099
|
+
if (e.key === 'Escape' && this.isOpen) {
|
|
1100
|
+
this.close();
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
window.addEventListener('help:startTutorial', (e) => {
|
|
1105
|
+
this.startTutorial(e.detail.tutorialId);
|
|
1106
|
+
});
|
|
1107
|
+
},
|
|
1108
|
+
|
|
1109
|
+
registerDefaultTooltips() {
|
|
1110
|
+
const tooltips = [
|
|
1111
|
+
{ id: '#sketch-btn', title: 'Sketch (K)', text: 'Enter 2D sketch mode. Draw profiles to extrude or revolve.' },
|
|
1112
|
+
{ id: '#extrude-btn', title: 'Extrude (E)', text: 'Push a sketch profile into 3D. Creates solid features.' },
|
|
1113
|
+
{ id: '#fillet-btn', title: 'Fillet (F)', text: 'Round sharp edges. Set radius in mm.' },
|
|
1114
|
+
{ id: '#ai-chat-btn', title: 'AI Copilot', text: 'Generate parts from text. "Create a cylinder 50mm diameter"' },
|
|
1115
|
+
{ id: '#export-menu', title: 'Export', text: 'Save as STL (3D print), OBJ, glTF, STEP, DXF' },
|
|
1116
|
+
];
|
|
1117
|
+
|
|
1118
|
+
tooltips.forEach(t => {
|
|
1119
|
+
this.registerTooltip(t.id, { title: t.title, text: t.text });
|
|
1120
|
+
});
|
|
1121
|
+
},
|
|
1122
|
+
|
|
1123
|
+
registerTooltip(elementId, config) {
|
|
1124
|
+
this.tooltips.set(elementId, config);
|
|
1125
|
+
const el = document.querySelector(elementId);
|
|
1126
|
+
if (el) {
|
|
1127
|
+
el.addEventListener('mouseenter', () => this.showTooltip(elementId));
|
|
1128
|
+
el.addEventListener('mouseleave', () => this.hideTooltip());
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
|
|
1132
|
+
showTooltip(elementId) {
|
|
1133
|
+
const el = document.querySelector(elementId);
|
|
1134
|
+
const config = this.tooltips.get(elementId);
|
|
1135
|
+
if (!el || !config) return;
|
|
1136
|
+
|
|
1137
|
+
const tooltip = document.getElementById('help-tooltip');
|
|
1138
|
+
tooltip.innerHTML = `<strong>${config.title}</strong><br>${config.text}`;
|
|
1139
|
+
tooltip.style.display = 'block';
|
|
1140
|
+
|
|
1141
|
+
const rect = el.getBoundingClientRect();
|
|
1142
|
+
tooltip.style.left = (rect.left + rect.width / 2) + 'px';
|
|
1143
|
+
tooltip.style.top = (rect.bottom + 10) + 'px';
|
|
1144
|
+
tooltip.style.transform = 'translateX(-50%)';
|
|
1145
|
+
},
|
|
1146
|
+
|
|
1147
|
+
hideTooltip() {
|
|
1148
|
+
document.getElementById('help-tooltip').style.display = 'none';
|
|
1149
|
+
},
|
|
1150
|
+
|
|
1151
|
+
toggle() {
|
|
1152
|
+
if (this.isOpen) this.close();
|
|
1153
|
+
else this.open();
|
|
1154
|
+
},
|
|
1155
|
+
|
|
1156
|
+
open() {
|
|
1157
|
+
this.isOpen = true;
|
|
1158
|
+
document.getElementById('help-panel').style.right = '0';
|
|
1159
|
+
window.dispatchEvent(new CustomEvent('help:opened'));
|
|
1160
|
+
},
|
|
1161
|
+
|
|
1162
|
+
close() {
|
|
1163
|
+
this.isOpen = false;
|
|
1164
|
+
document.getElementById('help-panel').style.right = '-400px';
|
|
1165
|
+
this.stopTutorial();
|
|
1166
|
+
window.dispatchEvent(new CustomEvent('help:closed'));
|
|
1167
|
+
},
|
|
1168
|
+
|
|
1169
|
+
search(query) {
|
|
1170
|
+
const content = document.getElementById('help-content');
|
|
1171
|
+
if (!query.trim()) {
|
|
1172
|
+
this.renderHelpContent();
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
const q = query.toLowerCase();
|
|
1177
|
+
content.innerHTML = '';
|
|
1178
|
+
|
|
1179
|
+
let found = 0;
|
|
1180
|
+
for (const [category, entries] of Object.entries(this.helpEntries)) {
|
|
1181
|
+
const filtered = entries.filter(e =>
|
|
1182
|
+
e.title.toLowerCase().includes(q) || e.desc.toLowerCase().includes(q)
|
|
1183
|
+
);
|
|
1184
|
+
|
|
1185
|
+
if (filtered.length === 0) continue;
|
|
1186
|
+
found++;
|
|
1187
|
+
|
|
1188
|
+
const catDiv = document.createElement('div');
|
|
1189
|
+
catDiv.style.marginBottom = '15px';
|
|
1190
|
+
|
|
1191
|
+
const catTitle = document.createElement('h3');
|
|
1192
|
+
catTitle.style.cssText = 'margin: 10px 0 5px 0; font-size: 13px; color: #0284C7; font-weight: bold;';
|
|
1193
|
+
catTitle.textContent = this.formatCategoryName(category);
|
|
1194
|
+
|
|
1195
|
+
catDiv.appendChild(catTitle);
|
|
1196
|
+
|
|
1197
|
+
filtered.forEach(entry => {
|
|
1198
|
+
const entryDiv = document.createElement('div');
|
|
1199
|
+
entryDiv.style.cssText = 'margin-bottom: 8px; padding: 8px; border-radius: 3px; background: #f9f9f9;';
|
|
1200
|
+
entryDiv.innerHTML = `
|
|
1201
|
+
<div style="font-size: 12px; font-weight: bold;">${entry.title}</div>
|
|
1202
|
+
<div style="font-size: 11px; color: #666;">${entry.desc}</div>
|
|
1203
|
+
`;
|
|
1204
|
+
catDiv.appendChild(entryDiv);
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
content.appendChild(catDiv);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
if (found === 0) {
|
|
1211
|
+
content.innerHTML = '<p style="padding: 20px; text-align: center; color: #999;">No results found.</p>';
|
|
1212
|
+
}
|
|
1213
|
+
},
|
|
1214
|
+
|
|
1215
|
+
startTutorial(tutorialId) {
|
|
1216
|
+
const tutorial = this.tutorials[tutorialId];
|
|
1217
|
+
if (!tutorial) return;
|
|
1218
|
+
|
|
1219
|
+
this.activeTutorial = tutorial;
|
|
1220
|
+
this.currentStep = 0;
|
|
1221
|
+
|
|
1222
|
+
this.showTutorialStep();
|
|
1223
|
+
window.dispatchEvent(new CustomEvent('help:tutorialStarted', { detail: { tutorialId } }));
|
|
1224
|
+
},
|
|
1225
|
+
|
|
1226
|
+
showTutorialStep() {
|
|
1227
|
+
const tutorial = this.activeTutorial;
|
|
1228
|
+
if (!tutorial) return;
|
|
1229
|
+
|
|
1230
|
+
const step = tutorial.steps[this.currentStep];
|
|
1231
|
+
if (!step) {
|
|
1232
|
+
this.tutorialComplete();
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// Show overlay
|
|
1237
|
+
const overlay = document.getElementById('help-tutorial-overlay');
|
|
1238
|
+
overlay.style.display = 'block';
|
|
1239
|
+
|
|
1240
|
+
// Highlight target element
|
|
1241
|
+
if (step.target) {
|
|
1242
|
+
const target = document.querySelector(step.target);
|
|
1243
|
+
if (target) {
|
|
1244
|
+
const rect = target.getBoundingClientRect();
|
|
1245
|
+
const highlightBox = document.createElement('div');
|
|
1246
|
+
highlightBox.id = 'tutorial-highlight';
|
|
1247
|
+
highlightBox.style.cssText = `
|
|
1248
|
+
position: fixed;
|
|
1249
|
+
left: ${rect.left - 5}px;
|
|
1250
|
+
top: ${rect.top - 5}px;
|
|
1251
|
+
width: ${rect.width + 10}px;
|
|
1252
|
+
height: ${rect.height + 10}px;
|
|
1253
|
+
border: 3px solid #FFD700;
|
|
1254
|
+
border-radius: 8px;
|
|
1255
|
+
box-shadow: 0 0 0 9999px rgba(0,0,0,0.7);
|
|
1256
|
+
z-index: 9998;
|
|
1257
|
+
pointer-events: none;
|
|
1258
|
+
`;
|
|
1259
|
+
|
|
1260
|
+
const existing = document.getElementById('tutorial-highlight');
|
|
1261
|
+
if (existing) existing.remove();
|
|
1262
|
+
document.body.appendChild(highlightBox);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// Show step content
|
|
1267
|
+
const stepPanel = document.createElement('div');
|
|
1268
|
+
stepPanel.id = 'tutorial-step-panel';
|
|
1269
|
+
stepPanel.style.cssText = `
|
|
1270
|
+
position: fixed;
|
|
1271
|
+
bottom: 40px;
|
|
1272
|
+
left: 50%;
|
|
1273
|
+
transform: translateX(-50%);
|
|
1274
|
+
background: white;
|
|
1275
|
+
padding: 20px;
|
|
1276
|
+
border-radius: 8px;
|
|
1277
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
|
1278
|
+
z-index: 9999;
|
|
1279
|
+
max-width: 400px;
|
|
1280
|
+
font-family: Calibri, sans-serif;
|
|
1281
|
+
`;
|
|
1282
|
+
|
|
1283
|
+
const progress = document.createElement('div');
|
|
1284
|
+
progress.style.cssText = 'font-size: 12px; color: #999; margin-bottom: 8px;';
|
|
1285
|
+
progress.textContent = `Step ${this.currentStep + 1} of ${tutorial.steps.length}`;
|
|
1286
|
+
|
|
1287
|
+
const title = document.createElement('h3');
|
|
1288
|
+
title.style.cssText = 'margin: 0 0 8px 0; font-size: 16px;';
|
|
1289
|
+
title.textContent = step.title;
|
|
1290
|
+
|
|
1291
|
+
const text = document.createElement('p');
|
|
1292
|
+
text.style.cssText = 'margin: 0 0 15px 0; font-size: 14px; line-height: 1.5;';
|
|
1293
|
+
text.textContent = step.text;
|
|
1294
|
+
|
|
1295
|
+
const buttons = document.createElement('div');
|
|
1296
|
+
buttons.style.cssText = 'display: flex; gap: 10px; justify-content: space-between;';
|
|
1297
|
+
|
|
1298
|
+
if (this.currentStep > 0) {
|
|
1299
|
+
const backBtn = document.createElement('button');
|
|
1300
|
+
backBtn.textContent = 'Back';
|
|
1301
|
+
backBtn.style.cssText = 'padding: 8px 16px; background: #999; color: white; border: none; border-radius: 4px; cursor: pointer;';
|
|
1302
|
+
backBtn.onclick = () => this.previousStep();
|
|
1303
|
+
buttons.appendChild(backBtn);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
const nextBtn = document.createElement('button');
|
|
1307
|
+
nextBtn.textContent = this.currentStep === tutorial.steps.length - 1 ? 'Complete' : 'Next';
|
|
1308
|
+
nextBtn.style.cssText = 'padding: 8px 16px; background: #0284C7; color: white; border: none; border-radius: 4px; cursor: pointer; flex: 1;';
|
|
1309
|
+
nextBtn.onclick = () => this.nextStep();
|
|
1310
|
+
buttons.appendChild(nextBtn);
|
|
1311
|
+
|
|
1312
|
+
const exitBtn = document.createElement('button');
|
|
1313
|
+
exitBtn.textContent = 'Exit';
|
|
1314
|
+
exitBtn.style.cssText = 'padding: 8px 16px; background: #ddd; color: #333; border: none; border-radius: 4px; cursor: pointer;';
|
|
1315
|
+
exitBtn.onclick = () => this.stopTutorial();
|
|
1316
|
+
buttons.appendChild(exitBtn);
|
|
1317
|
+
|
|
1318
|
+
stepPanel.appendChild(progress);
|
|
1319
|
+
stepPanel.appendChild(title);
|
|
1320
|
+
stepPanel.appendChild(text);
|
|
1321
|
+
stepPanel.appendChild(buttons);
|
|
1322
|
+
|
|
1323
|
+
const existing = document.getElementById('tutorial-step-panel');
|
|
1324
|
+
if (existing) existing.remove();
|
|
1325
|
+
document.body.appendChild(stepPanel);
|
|
1326
|
+
},
|
|
1327
|
+
|
|
1328
|
+
nextStep() {
|
|
1329
|
+
const tutorial = this.activeTutorial;
|
|
1330
|
+
if (this.currentStep < tutorial.steps.length - 1) {
|
|
1331
|
+
this.currentStep++;
|
|
1332
|
+
this.showTutorialStep();
|
|
1333
|
+
} else {
|
|
1334
|
+
this.tutorialComplete();
|
|
1335
|
+
}
|
|
1336
|
+
},
|
|
1337
|
+
|
|
1338
|
+
previousStep() {
|
|
1339
|
+
if (this.currentStep > 0) {
|
|
1340
|
+
this.currentStep--;
|
|
1341
|
+
this.showTutorialStep();
|
|
1342
|
+
}
|
|
1343
|
+
},
|
|
1344
|
+
|
|
1345
|
+
tutorialComplete() {
|
|
1346
|
+
this.stopTutorial();
|
|
1347
|
+
window.dispatchEvent(new CustomEvent('help:tutorialCompleted', {
|
|
1348
|
+
detail: { tutorialId: this.activeTutorial.id }
|
|
1349
|
+
}));
|
|
1350
|
+
|
|
1351
|
+
alert(`Great! You've completed the "${this.activeTutorial.name}" tutorial.`);
|
|
1352
|
+
},
|
|
1353
|
+
|
|
1354
|
+
stopTutorial() {
|
|
1355
|
+
if (!this.activeTutorial) return;
|
|
1356
|
+
|
|
1357
|
+
document.getElementById('help-tutorial-overlay').style.display = 'none';
|
|
1358
|
+
const highlight = document.getElementById('tutorial-highlight');
|
|
1359
|
+
if (highlight) highlight.remove();
|
|
1360
|
+
const panel = document.getElementById('tutorial-step-panel');
|
|
1361
|
+
if (panel) panel.remove();
|
|
1362
|
+
|
|
1363
|
+
window.dispatchEvent(new CustomEvent('help:tutorialExited', {
|
|
1364
|
+
detail: { tutorialId: this.activeTutorial.id }
|
|
1365
|
+
}));
|
|
1366
|
+
|
|
1367
|
+
this.activeTutorial = null;
|
|
1368
|
+
},
|
|
1369
|
+
|
|
1370
|
+
showShortcutOverlay() {
|
|
1371
|
+
const overlay = document.getElementById('help-shortcuts-overlay');
|
|
1372
|
+
overlay.innerHTML = `
|
|
1373
|
+
<div style="max-width: 900px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px;">
|
|
1374
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
|
1375
|
+
<h1 style="margin: 0;">Keyboard Shortcuts</h1>
|
|
1376
|
+
<button onclick="document.getElementById('help-shortcuts-overlay').style.display='none'" style="background: none; border: none; font-size: 24px; cursor: pointer;">×</button>
|
|
1377
|
+
</div>
|
|
1378
|
+
`;
|
|
1379
|
+
|
|
1380
|
+
for (const [category, shortcuts] of Object.entries(this.shortcuts)) {
|
|
1381
|
+
overlay.innerHTML += `<h2 style="font-size: 16px; margin-top: 20px; border-bottom: 2px solid #0284C7; padding-bottom: 8px;">${this.formatCategoryName(category)}</h2>`;
|
|
1382
|
+
overlay.innerHTML += '<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-bottom: 15px;">';
|
|
1383
|
+
|
|
1384
|
+
shortcuts.forEach(s => {
|
|
1385
|
+
overlay.innerHTML += `
|
|
1386
|
+
<div style="padding: 10px; background: #f9f9f9; border-radius: 4px;">
|
|
1387
|
+
<div style="font-family: monospace; font-weight: bold; color: #0284C7;">${s.key}</div>
|
|
1388
|
+
<div style="font-size: 12px; color: #666;">${s.desc}</div>
|
|
1389
|
+
</div>
|
|
1390
|
+
`;
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
overlay.innerHTML += '</div>';
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
overlay.innerHTML += '</div>';
|
|
1397
|
+
overlay.style.display = 'block';
|
|
1398
|
+
|
|
1399
|
+
overlay.onclick = (e) => {
|
|
1400
|
+
if (e.target === overlay) overlay.style.display = 'none';
|
|
1401
|
+
};
|
|
1402
|
+
},
|
|
1403
|
+
|
|
1404
|
+
getUI() {
|
|
1405
|
+
return document.getElementById('help-panel');
|
|
1406
|
+
},
|
|
1407
|
+
|
|
1408
|
+
execute(command, params) {
|
|
1409
|
+
switch (command) {
|
|
1410
|
+
case 'open':
|
|
1411
|
+
this.open();
|
|
1412
|
+
break;
|
|
1413
|
+
case 'close':
|
|
1414
|
+
this.close();
|
|
1415
|
+
break;
|
|
1416
|
+
case 'search':
|
|
1417
|
+
this.search(params.query || '');
|
|
1418
|
+
break;
|
|
1419
|
+
case 'startTutorial':
|
|
1420
|
+
this.startTutorial(params.tutorialId);
|
|
1421
|
+
break;
|
|
1422
|
+
case 'showShortcuts':
|
|
1423
|
+
this.showShortcutOverlay();
|
|
1424
|
+
break;
|
|
1425
|
+
case 'registerTooltip':
|
|
1426
|
+
this.registerTooltip(params.elementId, params.config);
|
|
1427
|
+
break;
|
|
1428
|
+
default:
|
|
1429
|
+
console.warn(`[HelpModule] Unknown command: ${command}`);
|
|
1430
|
+
}
|
|
1431
|
+
},
|
|
1432
|
+
};
|
|
1433
|
+
|
|
1434
|
+
// Export
|
|
1435
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
1436
|
+
module.exports = HelpModule;
|
|
1437
|
+
}
|