cyclecad 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/BILLING-IMPLEMENTATION-SUMMARY.md +425 -0
  2. package/BILLING-INDEX.md +293 -0
  3. package/BILLING-INTEGRATION-GUIDE.md +414 -0
  4. package/COLLABORATION-INDEX.md +440 -0
  5. package/COLLABORATION-SYSTEM-SUMMARY.md +548 -0
  6. package/DOCKER-BUILD-MANIFEST.txt +483 -0
  7. package/DOCKER-FILES-REFERENCE.md +440 -0
  8. package/DOCKER-INFRASTRUCTURE.md +475 -0
  9. package/DOCKER-README.md +435 -0
  10. package/Dockerfile +33 -55
  11. package/PWA-FILES-CREATED.txt +350 -0
  12. package/QUICK-START-TESTING.md +126 -0
  13. package/STEP-IMPORT-QUICKSTART.md +347 -0
  14. package/STEP-IMPORT-SYSTEM-SUMMARY.md +502 -0
  15. package/app/css/mobile.css +1074 -0
  16. package/app/icons/generate-icons.js +203 -0
  17. package/app/js/billing-ui.js +990 -0
  18. package/app/js/brep-kernel.js +933 -981
  19. package/app/js/collab-client.js +750 -0
  20. package/app/js/mobile-nav.js +623 -0
  21. package/app/js/mobile-toolbar.js +476 -0
  22. package/app/js/modules/billing-module.js +724 -0
  23. package/app/js/modules/step-module-enhanced.js +938 -0
  24. package/app/js/offline-manager.js +705 -0
  25. package/app/js/responsive-init.js +360 -0
  26. package/app/js/touch-handler.js +429 -0
  27. package/app/manifest.json +211 -0
  28. package/app/offline.html +508 -0
  29. package/app/sw.js +571 -0
  30. package/app/tests/billing-tests.html +779 -0
  31. package/app/tests/brep-tests.html +980 -0
  32. package/app/tests/collab-tests.html +743 -0
  33. package/app/tests/mobile-tests.html +1299 -0
  34. package/app/tests/pwa-tests.html +1134 -0
  35. package/app/tests/step-tests.html +1042 -0
  36. package/app/tests/test-agent-v3.html +719 -0
  37. package/docker-compose.yml +225 -0
  38. package/docs/BILLING-HELP.json +260 -0
  39. package/docs/BILLING-README.md +639 -0
  40. package/docs/BILLING-TUTORIAL.md +736 -0
  41. package/docs/BREP-HELP.json +326 -0
  42. package/docs/BREP-TUTORIAL.md +802 -0
  43. package/docs/COLLABORATION-HELP.json +228 -0
  44. package/docs/COLLABORATION-TUTORIAL.md +818 -0
  45. package/docs/DOCKER-HELP.json +224 -0
  46. package/docs/DOCKER-TUTORIAL.md +974 -0
  47. package/docs/MOBILE-HELP.json +243 -0
  48. package/docs/MOBILE-RESPONSIVE-README.md +378 -0
  49. package/docs/MOBILE-TUTORIAL.md +747 -0
  50. package/docs/PWA-HELP.json +228 -0
  51. package/docs/PWA-README.md +662 -0
  52. package/docs/PWA-TUTORIAL.md +757 -0
  53. package/docs/STEP-HELP.json +481 -0
  54. package/docs/STEP-IMPORT-TUTORIAL.md +824 -0
  55. package/docs/TESTING-GUIDE.md +528 -0
  56. package/docs/TESTING-HELP.json +182 -0
  57. package/fusion-vs-cyclecad.html +1771 -0
  58. package/nginx.conf +237 -0
  59. package/package.json +1 -1
  60. package/server/Dockerfile.converter +51 -0
  61. package/server/Dockerfile.signaling +28 -0
  62. package/server/billing-server.js +487 -0
  63. package/server/converter-enhanced.py +528 -0
  64. package/server/requirements-converter.txt +29 -0
  65. package/server/signaling-server.js +801 -0
  66. package/tests/docker-tests.sh +389 -0
@@ -0,0 +1,476 @@
1
+ /**
2
+ * Mobile Toolbar Component for cycleCAD
3
+ * Bottom floating toolbar optimized for mobile touch interaction
4
+ */
5
+
6
+ class MobileToolbar {
7
+ constructor(options = {}) {
8
+ this.options = {
9
+ position: 'bottom', // bottom, left, right
10
+ autoHide: true,
11
+ autoHideDelay: 3000,
12
+ tools: [],
13
+ onToolClick: () => {},
14
+ onMoreClick: () => {},
15
+ ...options
16
+ };
17
+
18
+ this.container = null;
19
+ this.isVisible = true;
20
+ this.autoHideTimer = null;
21
+ this.isExpanded = false;
22
+ this.init();
23
+ }
24
+
25
+ init() {
26
+ this.createContainer();
27
+ this.renderTools();
28
+ this.attachEventListeners();
29
+ }
30
+
31
+ createContainer() {
32
+ this.container = document.createElement('div');
33
+ this.container.className = 'mobile-toolbar';
34
+ this.container.id = 'mobile-toolbar';
35
+ this.container.innerHTML = `
36
+ <div class="toolbar-content">
37
+ <div class="toolbar-pills"></div>
38
+ </div>
39
+ `;
40
+
41
+ document.body.appendChild(this.container);
42
+ }
43
+
44
+ renderTools() {
45
+ const pillsContainer = this.container.querySelector('.toolbar-pills');
46
+ pillsContainer.innerHTML = '';
47
+
48
+ // Determine which tools to show (up to 6)
49
+ const visibleTools = this.options.tools.slice(0, 6);
50
+ const hasMore = this.options.tools.length > 6;
51
+
52
+ visibleTools.forEach((tool, index) => {
53
+ const button = this.createToolButton(tool);
54
+ pillsContainer.appendChild(button);
55
+ });
56
+
57
+ // Add "More" button if tools exceed 6
58
+ if (hasMore) {
59
+ const moreButton = document.createElement('button');
60
+ moreButton.className = 'tool-button more-button';
61
+ moreButton.innerHTML = `
62
+ <span class="tool-icon">⋯</span>
63
+ <span class="tool-label">More</span>
64
+ `;
65
+ moreButton.addEventListener('click', () => this.toggleGrid());
66
+ pillsContainer.appendChild(moreButton);
67
+ }
68
+ }
69
+
70
+ createToolButton(tool) {
71
+ const button = document.createElement('button');
72
+ button.className = `tool-button ${tool.active ? 'active' : ''}`;
73
+ button.dataset.toolId = tool.id;
74
+ button.dataset.category = tool.category || 'view';
75
+
76
+ const icon = tool.icon || '🔧';
77
+ const label = tool.label || tool.id;
78
+
79
+ button.innerHTML = `
80
+ <span class="tool-icon">${icon}</span>
81
+ <span class="tool-label">${label}</span>
82
+ `;
83
+
84
+ button.addEventListener('click', (e) => {
85
+ e.stopPropagation();
86
+ this.selectTool(tool);
87
+ });
88
+
89
+ return button;
90
+ }
91
+
92
+ selectTool(tool) {
93
+ // Deselect previous tool
94
+ this.container.querySelectorAll('.tool-button').forEach(btn => {
95
+ btn.classList.remove('active');
96
+ });
97
+
98
+ // Select new tool
99
+ const button = this.container.querySelector(`[data-tool-id="${tool.id}"]`);
100
+ if (button) {
101
+ button.classList.add('active');
102
+ }
103
+
104
+ // Call callback
105
+ this.options.onToolClick(tool);
106
+
107
+ // Reset auto-hide timer
108
+ if (this.options.autoHide) {
109
+ this.resetAutoHideTimer();
110
+ }
111
+ }
112
+
113
+ toggleGrid() {
114
+ if (this.isExpanded) {
115
+ this.closeGrid();
116
+ } else {
117
+ this.openGrid();
118
+ }
119
+ }
120
+
121
+ openGrid() {
122
+ if (this.isExpanded) return;
123
+
124
+ this.isExpanded = true;
125
+
126
+ // Create bottom sheet grid
127
+ const grid = document.createElement('div');
128
+ grid.className = 'toolbar-grid-sheet';
129
+ grid.id = 'toolbar-grid-sheet';
130
+
131
+ // Group tools by category
132
+ const categories = {
133
+ sketch: [],
134
+ solid: [],
135
+ view: [],
136
+ measure: [],
137
+ other: []
138
+ };
139
+
140
+ this.options.tools.forEach(tool => {
141
+ const cat = tool.category || 'other';
142
+ if (categories[cat]) {
143
+ categories[cat].push(tool);
144
+ } else {
145
+ categories.other.push(tool);
146
+ }
147
+ });
148
+
149
+ // Build grid HTML
150
+ let gridHTML = `
151
+ <div class="grid-sheet-handle"></div>
152
+ <div class="grid-sheet-content">
153
+ `;
154
+
155
+ Object.entries(categories).forEach(([category, tools]) => {
156
+ if (tools.length === 0) return;
157
+
158
+ const categoryLabel = this.getCategoryLabel(category);
159
+ gridHTML += `<div class="grid-category">
160
+ <div class="category-label">${categoryLabel}</div>
161
+ <div class="category-tools">`;
162
+
163
+ tools.forEach(tool => {
164
+ const icon = tool.icon || '🔧';
165
+ const label = tool.label || tool.id;
166
+ gridHTML += `
167
+ <button class="grid-tool-button" data-tool-id="${tool.id}">
168
+ <span class="tool-icon">${icon}</span>
169
+ <span class="tool-label">${label}</span>
170
+ </button>
171
+ `;
172
+ });
173
+
174
+ gridHTML += `</div></div>`;
175
+ });
176
+
177
+ gridHTML += '</div>';
178
+ grid.innerHTML = gridHTML;
179
+
180
+ // Attach event listeners
181
+ grid.querySelectorAll('.grid-tool-button').forEach(button => {
182
+ button.addEventListener('click', (e) => {
183
+ const toolId = e.currentTarget.dataset.toolId;
184
+ const tool = this.options.tools.find(t => t.id === toolId);
185
+ if (tool) {
186
+ this.selectTool(tool);
187
+ this.closeGrid();
188
+ }
189
+ });
190
+ });
191
+
192
+ // Close on backdrop click
193
+ const backdrop = document.createElement('div');
194
+ backdrop.className = 'grid-sheet-backdrop';
195
+ backdrop.addEventListener('click', () => this.closeGrid());
196
+
197
+ document.body.appendChild(backdrop);
198
+ document.body.appendChild(grid);
199
+
200
+ // Animate in
201
+ setTimeout(() => {
202
+ grid.classList.add('open');
203
+ backdrop.classList.add('open');
204
+ }, 10);
205
+ }
206
+
207
+ closeGrid() {
208
+ if (!this.isExpanded) return;
209
+
210
+ this.isExpanded = false;
211
+
212
+ const grid = document.getElementById('toolbar-grid-sheet');
213
+ const backdrop = document.querySelector('.grid-sheet-backdrop');
214
+
215
+ if (grid) {
216
+ grid.classList.remove('open');
217
+ setTimeout(() => grid.remove(), 300);
218
+ }
219
+
220
+ if (backdrop) {
221
+ backdrop.classList.remove('open');
222
+ setTimeout(() => backdrop.remove(), 300);
223
+ }
224
+ }
225
+
226
+ getCategoryLabel(category) {
227
+ const labels = {
228
+ sketch: '✏️ Sketch Tools',
229
+ solid: '🧊 Solid Tools',
230
+ view: '👁️ View',
231
+ measure: '📏 Measure',
232
+ other: '🔧 Other'
233
+ };
234
+ return labels[category] || category;
235
+ }
236
+
237
+ attachEventListeners() {
238
+ // Show toolbar on viewport tap
239
+ const viewport = document.getElementById('viewport');
240
+ if (viewport) {
241
+ viewport.addEventListener('pointerdown', () => {
242
+ if (!this.isVisible) {
243
+ this.show();
244
+ }
245
+ if (this.options.autoHide) {
246
+ this.resetAutoHideTimer();
247
+ }
248
+ });
249
+ }
250
+
251
+ // Make toolbar draggable
252
+ this.makeToolbarDraggable();
253
+ }
254
+
255
+ makeToolbarDraggable() {
256
+ let isDragging = false;
257
+ let startX = 0;
258
+ let startY = 0;
259
+ let offsetX = 0;
260
+ let offsetY = 0;
261
+
262
+ this.container.addEventListener('pointerdown', (e) => {
263
+ if (e.target.closest('.tool-button')) return;
264
+
265
+ isDragging = true;
266
+ startX = e.clientX;
267
+ startY = e.clientY;
268
+ offsetX = this.container.offsetLeft;
269
+ offsetY = this.container.offsetTop;
270
+ });
271
+
272
+ document.addEventListener('pointermove', (e) => {
273
+ if (!isDragging) return;
274
+
275
+ const dx = e.clientX - startX;
276
+ const dy = e.clientY - startY;
277
+
278
+ const newX = offsetX + dx;
279
+ const newY = offsetY + dy;
280
+
281
+ // Constrain to viewport
282
+ const maxX = window.innerWidth - this.container.offsetWidth;
283
+ const maxY = window.innerHeight - this.container.offsetHeight;
284
+
285
+ this.container.style.left = Math.max(0, Math.min(newX, maxX)) + 'px';
286
+ this.container.style.top = Math.max(0, Math.min(newY, maxY)) + 'px';
287
+ });
288
+
289
+ document.addEventListener('pointerup', () => {
290
+ isDragging = false;
291
+ });
292
+ }
293
+
294
+ resetAutoHideTimer() {
295
+ if (this.autoHideTimer) {
296
+ clearTimeout(this.autoHideTimer);
297
+ }
298
+
299
+ this.autoHideTimer = setTimeout(() => {
300
+ this.hide();
301
+ }, this.options.autoHideDelay);
302
+ }
303
+
304
+ show() {
305
+ this.isVisible = true;
306
+ this.container.style.opacity = '1';
307
+ this.container.style.pointerEvents = 'auto';
308
+ }
309
+
310
+ hide() {
311
+ this.isVisible = false;
312
+ this.container.style.opacity = '0.3';
313
+ this.container.style.pointerEvents = 'none';
314
+ }
315
+
316
+ updateTools(tools) {
317
+ this.options.tools = tools;
318
+ this.renderTools();
319
+ }
320
+
321
+ addTool(tool) {
322
+ this.options.tools.push(tool);
323
+ this.renderTools();
324
+ }
325
+
326
+ removeTool(toolId) {
327
+ this.options.tools = this.options.tools.filter(t => t.id !== toolId);
328
+ this.renderTools();
329
+ }
330
+
331
+ setActiveTool(toolId) {
332
+ this.options.tools.forEach(t => t.active = t.id === toolId);
333
+ this.renderTools();
334
+ }
335
+
336
+ destroy() {
337
+ if (this.autoHideTimer) {
338
+ clearTimeout(this.autoHideTimer);
339
+ }
340
+
341
+ if (this.container) {
342
+ this.container.remove();
343
+ }
344
+
345
+ const grid = document.getElementById('toolbar-grid-sheet');
346
+ if (grid) grid.remove();
347
+
348
+ const backdrop = document.querySelector('.grid-sheet-backdrop');
349
+ if (backdrop) backdrop.remove();
350
+ }
351
+ }
352
+
353
+ // Add CSS styles for mobile toolbar if not already present
354
+ if (!document.getElementById('mobile-toolbar-styles')) {
355
+ const style = document.createElement('style');
356
+ style.id = 'mobile-toolbar-styles';
357
+ style.textContent = `
358
+ .toolbar-grid-sheet {
359
+ position: fixed;
360
+ bottom: 0;
361
+ left: 0;
362
+ right: 0;
363
+ background: var(--bg-primary);
364
+ border-top: 1px solid var(--border-color);
365
+ z-index: 160;
366
+ max-height: 80vh;
367
+ overflow-y: auto;
368
+ border-radius: 12px 12px 0 0;
369
+ transform: translateY(100%);
370
+ transition: transform 0.3s ease-out;
371
+ padding-bottom: var(--safe-area-bottom);
372
+ }
373
+
374
+ .toolbar-grid-sheet.open {
375
+ transform: translateY(0);
376
+ }
377
+
378
+ .grid-sheet-handle {
379
+ width: 40px;
380
+ height: 4px;
381
+ background: var(--border-color);
382
+ border-radius: 2px;
383
+ margin: 8px auto;
384
+ cursor: grab;
385
+ }
386
+
387
+ .grid-sheet-content {
388
+ padding: 16px;
389
+ }
390
+
391
+ .grid-category {
392
+ margin-bottom: 24px;
393
+ }
394
+
395
+ .category-label {
396
+ font-size: 12px;
397
+ font-weight: 600;
398
+ color: var(--text-secondary);
399
+ text-transform: uppercase;
400
+ letter-spacing: 0.5px;
401
+ margin-bottom: 8px;
402
+ padding: 0 8px;
403
+ }
404
+
405
+ .category-tools {
406
+ display: grid;
407
+ grid-template-columns: repeat(4, 1fr);
408
+ gap: 8px;
409
+ }
410
+
411
+ .grid-tool-button {
412
+ display: flex;
413
+ flex-direction: column;
414
+ align-items: center;
415
+ justify-content: center;
416
+ gap: 8px;
417
+ padding: 12px;
418
+ border: 1px solid var(--border-color);
419
+ background: var(--bg-secondary);
420
+ border-radius: 8px;
421
+ cursor: pointer;
422
+ font-size: 11px;
423
+ color: var(--text-primary);
424
+ transition: all 0.2s;
425
+ min-height: 80px;
426
+ }
427
+
428
+ .grid-tool-button:active {
429
+ background: var(--accent-color);
430
+ color: white;
431
+ transform: scale(0.95);
432
+ }
433
+
434
+ .grid-tool-button .tool-icon {
435
+ font-size: 28px;
436
+ }
437
+
438
+ .grid-tool-button .tool-label {
439
+ text-align: center;
440
+ line-height: 1.2;
441
+ }
442
+
443
+ .grid-sheet-backdrop {
444
+ position: fixed;
445
+ top: 0;
446
+ left: 0;
447
+ width: 100%;
448
+ height: 100%;
449
+ background: rgba(0, 0, 0, 0);
450
+ z-index: 150;
451
+ transition: background 0.3s;
452
+ }
453
+
454
+ .grid-sheet-backdrop.open {
455
+ background: rgba(0, 0, 0, 0.4);
456
+ }
457
+
458
+ @media (min-width: 600px) {
459
+ .category-tools {
460
+ grid-template-columns: repeat(5, 1fr);
461
+ }
462
+ }
463
+
464
+ @media (min-width: 900px) {
465
+ .category-tools {
466
+ grid-template-columns: repeat(6, 1fr);
467
+ }
468
+ }
469
+ `;
470
+ document.head.appendChild(style);
471
+ }
472
+
473
+ // Export for use in modules
474
+ if (typeof module !== 'undefined' && module.exports) {
475
+ module.exports = MobileToolbar;
476
+ }