cyclecad 3.0.0 → 3.2.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 (67) 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/index.html +93 -0
  18. package/app/js/billing-ui.js +990 -0
  19. package/app/js/brep-kernel.js +933 -981
  20. package/app/js/collab-client.js +750 -0
  21. package/app/js/mobile-nav.js +623 -0
  22. package/app/js/mobile-toolbar.js +476 -0
  23. package/app/js/modules/billing-module.js +724 -0
  24. package/app/js/modules/step-module-enhanced.js +938 -0
  25. package/app/js/offline-manager.js +705 -0
  26. package/app/js/responsive-init.js +360 -0
  27. package/app/js/touch-handler.js +429 -0
  28. package/app/manifest.json +211 -0
  29. package/app/offline.html +508 -0
  30. package/app/sw.js +571 -0
  31. package/app/tests/billing-tests.html +779 -0
  32. package/app/tests/brep-tests.html +980 -0
  33. package/app/tests/collab-tests.html +743 -0
  34. package/app/tests/mobile-tests.html +1299 -0
  35. package/app/tests/pwa-tests.html +1134 -0
  36. package/app/tests/step-tests.html +1042 -0
  37. package/app/tests/test-agent-v3.html +719 -0
  38. package/docker-compose.yml +225 -0
  39. package/docs/BILLING-HELP.json +260 -0
  40. package/docs/BILLING-README.md +639 -0
  41. package/docs/BILLING-TUTORIAL.md +736 -0
  42. package/docs/BREP-HELP.json +326 -0
  43. package/docs/BREP-TUTORIAL.md +802 -0
  44. package/docs/COLLABORATION-HELP.json +228 -0
  45. package/docs/COLLABORATION-TUTORIAL.md +818 -0
  46. package/docs/DOCKER-HELP.json +224 -0
  47. package/docs/DOCKER-TUTORIAL.md +974 -0
  48. package/docs/MOBILE-HELP.json +243 -0
  49. package/docs/MOBILE-RESPONSIVE-README.md +378 -0
  50. package/docs/MOBILE-TUTORIAL.md +747 -0
  51. package/docs/PWA-HELP.json +228 -0
  52. package/docs/PWA-README.md +662 -0
  53. package/docs/PWA-TUTORIAL.md +757 -0
  54. package/docs/STEP-HELP.json +481 -0
  55. package/docs/STEP-IMPORT-TUTORIAL.md +824 -0
  56. package/docs/TESTING-GUIDE.md +528 -0
  57. package/docs/TESTING-HELP.json +182 -0
  58. package/fusion-vs-cyclecad.html +1771 -0
  59. package/nginx.conf +237 -0
  60. package/package.json +1 -1
  61. package/server/Dockerfile.converter +51 -0
  62. package/server/Dockerfile.signaling +28 -0
  63. package/server/billing-server.js +487 -0
  64. package/server/converter-enhanced.py +528 -0
  65. package/server/requirements-converter.txt +29 -0
  66. package/server/signaling-server.js +801 -0
  67. package/tests/docker-tests.sh +389 -0
@@ -0,0 +1,508 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="theme-color" content="#0284C7">
7
+ <title>Offline — cycleCAD</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
+ background: linear-gradient(135deg, #1e1e1e 0%, #2d2d2d 100%);
18
+ color: #fff;
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ min-height: 100vh;
23
+ padding: 20px;
24
+ }
25
+
26
+ .offline-container {
27
+ background: rgba(255, 255, 255, 0.05);
28
+ backdrop-filter: blur(10px);
29
+ border: 1px solid rgba(255, 255, 255, 0.1);
30
+ border-radius: 16px;
31
+ padding: 40px;
32
+ max-width: 600px;
33
+ text-align: center;
34
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
35
+ }
36
+
37
+ .offline-icon {
38
+ width: 80px;
39
+ height: 80px;
40
+ margin: 0 auto 30px;
41
+ background: linear-gradient(135deg, #0284C7 0%, #0369a1 100%);
42
+ border-radius: 50%;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ font-size: 40px;
47
+ box-shadow: 0 8px 24px rgba(2, 132, 199, 0.3);
48
+ }
49
+
50
+ h1 {
51
+ font-size: 32px;
52
+ margin-bottom: 12px;
53
+ font-weight: 700;
54
+ color: #fff;
55
+ }
56
+
57
+ .status-text {
58
+ font-size: 18px;
59
+ color: #e5e7eb;
60
+ margin-bottom: 8px;
61
+ }
62
+
63
+ .subtitle {
64
+ font-size: 14px;
65
+ color: #9ca3af;
66
+ margin-bottom: 40px;
67
+ line-height: 1.6;
68
+ }
69
+
70
+ .offline-tips {
71
+ background: rgba(255, 255, 255, 0.05);
72
+ border: 1px solid rgba(255, 255, 255, 0.1);
73
+ border-radius: 12px;
74
+ padding: 24px;
75
+ margin-bottom: 30px;
76
+ text-align: left;
77
+ }
78
+
79
+ .tips-title {
80
+ font-weight: 600;
81
+ color: #0284C7;
82
+ font-size: 14px;
83
+ text-transform: uppercase;
84
+ letter-spacing: 0.5px;
85
+ margin-bottom: 16px;
86
+ }
87
+
88
+ .tip-item {
89
+ display: flex;
90
+ gap: 12px;
91
+ margin-bottom: 12px;
92
+ font-size: 14px;
93
+ color: #d1d5db;
94
+ align-items: flex-start;
95
+ }
96
+
97
+ .tip-item:last-child {
98
+ margin-bottom: 0;
99
+ }
100
+
101
+ .tip-icon {
102
+ flex-shrink: 0;
103
+ width: 20px;
104
+ height: 20px;
105
+ background: rgba(2, 132, 199, 0.2);
106
+ border-radius: 50%;
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: center;
110
+ color: #0284C7;
111
+ font-size: 12px;
112
+ font-weight: bold;
113
+ }
114
+
115
+ .cached-projects {
116
+ background: rgba(255, 255, 255, 0.05);
117
+ border: 1px solid rgba(255, 255, 255, 0.1);
118
+ border-radius: 12px;
119
+ padding: 24px;
120
+ margin-bottom: 30px;
121
+ text-align: left;
122
+ }
123
+
124
+ .projects-title {
125
+ font-weight: 600;
126
+ color: #0284C7;
127
+ font-size: 14px;
128
+ text-transform: uppercase;
129
+ letter-spacing: 0.5px;
130
+ margin-bottom: 16px;
131
+ }
132
+
133
+ .project-list {
134
+ list-style: none;
135
+ }
136
+
137
+ .project-item {
138
+ padding: 12px;
139
+ background: rgba(2, 132, 199, 0.1);
140
+ border: 1px solid rgba(2, 132, 199, 0.2);
141
+ border-radius: 8px;
142
+ margin-bottom: 8px;
143
+ cursor: pointer;
144
+ transition: all 0.2s ease;
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: space-between;
148
+ }
149
+
150
+ .project-item:hover {
151
+ background: rgba(2, 132, 199, 0.15);
152
+ border-color: rgba(2, 132, 199, 0.3);
153
+ }
154
+
155
+ .project-item:last-child {
156
+ margin-bottom: 0;
157
+ }
158
+
159
+ .project-name {
160
+ font-weight: 500;
161
+ color: #fff;
162
+ font-size: 14px;
163
+ }
164
+
165
+ .project-date {
166
+ font-size: 12px;
167
+ color: #9ca3af;
168
+ }
169
+
170
+ .project-arrow {
171
+ color: #0284C7;
172
+ font-size: 16px;
173
+ }
174
+
175
+ .empty-state {
176
+ text-align: center;
177
+ padding: 20px 0;
178
+ color: #6b7280;
179
+ font-size: 14px;
180
+ }
181
+
182
+ .button-group {
183
+ display: flex;
184
+ gap: 12px;
185
+ flex-wrap: wrap;
186
+ justify-content: center;
187
+ }
188
+
189
+ .btn {
190
+ padding: 12px 24px;
191
+ border-radius: 8px;
192
+ border: none;
193
+ font-weight: 600;
194
+ font-size: 14px;
195
+ cursor: pointer;
196
+ transition: all 0.2s ease;
197
+ display: inline-flex;
198
+ align-items: center;
199
+ gap: 8px;
200
+ }
201
+
202
+ .btn-primary {
203
+ background: linear-gradient(135deg, #0284C7 0%, #0369a1 100%);
204
+ color: white;
205
+ box-shadow: 0 4px 12px rgba(2, 132, 199, 0.3);
206
+ }
207
+
208
+ .btn-primary:hover {
209
+ transform: translateY(-2px);
210
+ box-shadow: 0 8px 20px rgba(2, 132, 199, 0.4);
211
+ }
212
+
213
+ .btn-secondary {
214
+ background: rgba(255, 255, 255, 0.1);
215
+ color: #e5e7eb;
216
+ border: 1px solid rgba(255, 255, 255, 0.2);
217
+ }
218
+
219
+ .btn-secondary:hover {
220
+ background: rgba(255, 255, 255, 0.15);
221
+ border-color: rgba(255, 255, 255, 0.3);
222
+ }
223
+
224
+ .spinner {
225
+ width: 16px;
226
+ height: 16px;
227
+ border: 2px solid rgba(255, 255, 255, 0.2);
228
+ border-top-color: #0284C7;
229
+ border-radius: 50%;
230
+ animation: spin 0.8s linear infinite;
231
+ }
232
+
233
+ @keyframes spin {
234
+ to { transform: rotate(360deg); }
235
+ }
236
+
237
+ .status-badge {
238
+ display: inline-block;
239
+ background: rgba(239, 68, 68, 0.2);
240
+ color: #fca5a5;
241
+ padding: 4px 12px;
242
+ border-radius: 20px;
243
+ font-size: 12px;
244
+ font-weight: 500;
245
+ margin-top: 12px;
246
+ }
247
+
248
+ .connection-indicator {
249
+ position: fixed;
250
+ top: 20px;
251
+ right: 20px;
252
+ display: flex;
253
+ align-items: center;
254
+ gap: 8px;
255
+ padding: 8px 16px;
256
+ background: rgba(239, 68, 68, 0.1);
257
+ border: 1px solid rgba(239, 68, 68, 0.3);
258
+ border-radius: 8px;
259
+ font-size: 12px;
260
+ color: #fca5a5;
261
+ font-weight: 500;
262
+ }
263
+
264
+ .indicator-dot {
265
+ width: 8px;
266
+ height: 8px;
267
+ background: #ef4444;
268
+ border-radius: 50%;
269
+ animation: pulse 2s ease-in-out infinite;
270
+ }
271
+
272
+ @keyframes pulse {
273
+ 0%, 100% { opacity: 1; }
274
+ 50% { opacity: 0.5; }
275
+ }
276
+
277
+ @media (max-width: 640px) {
278
+ .offline-container {
279
+ padding: 30px 20px;
280
+ }
281
+
282
+ h1 {
283
+ font-size: 24px;
284
+ }
285
+
286
+ .status-text {
287
+ font-size: 16px;
288
+ }
289
+
290
+ .offline-icon {
291
+ width: 60px;
292
+ height: 60px;
293
+ font-size: 32px;
294
+ margin-bottom: 20px;
295
+ }
296
+
297
+ .button-group {
298
+ flex-direction: column;
299
+ }
300
+
301
+ .btn {
302
+ width: 100%;
303
+ justify-content: center;
304
+ }
305
+ }
306
+ </style>
307
+ </head>
308
+ <body>
309
+ <div class="connection-indicator">
310
+ <div class="indicator-dot"></div>
311
+ <span>Offline Mode</span>
312
+ </div>
313
+
314
+ <div class="offline-container">
315
+ <div class="offline-icon">🔌</div>
316
+
317
+ <h1>You're Offline</h1>
318
+
319
+ <p class="status-text">
320
+ cycleCAD is ready to work without internet
321
+ </p>
322
+
323
+ <p class="subtitle">
324
+ You can continue working on cached projects. Your changes will automatically sync when you reconnect.
325
+ </p>
326
+
327
+ <div class="cached-projects">
328
+ <div class="projects-title">📦 Available Offline</div>
329
+ <ul class="project-list" id="project-list">
330
+ <li class="empty-state">
331
+ No cached projects. Create one online first, then work offline.
332
+ </li>
333
+ </ul>
334
+ </div>
335
+
336
+ <div class="offline-tips">
337
+ <div class="tips-title">✓ What You Can Do Offline</div>
338
+ <div class="tip-item">
339
+ <div class="tip-icon">✓</div>
340
+ <div>
341
+ <strong>Create & Edit</strong> — Full modeling, sketching, and operations work offline
342
+ </div>
343
+ </div>
344
+ <div class="tip-item">
345
+ <div class="tip-icon">✓</div>
346
+ <div>
347
+ <strong>Export & View</strong> — Export to STL, OBJ, DXF, and view your projects
348
+ </div>
349
+ </div>
350
+ <div class="tip-item">
351
+ <div class="tip-icon">✓</div>
352
+ <div>
353
+ <strong>Auto-Save</strong> — All work is saved locally in your browser
354
+ </div>
355
+ </div>
356
+ <div class="tip-item">
357
+ <div class="tip-icon">✓</div>
358
+ <div>
359
+ <strong>Auto-Sync</strong> — Changes sync automatically when you come back online
360
+ </div>
361
+ </div>
362
+ <div class="tip-item">
363
+ <div class="tip-icon">✓</div>
364
+ <div>
365
+ <strong>No Data Loss</strong> — Everything is stored securely in your browser
366
+ </div>
367
+ </div>
368
+ </div>
369
+
370
+ <div class="button-group">
371
+ <button class="btn btn-primary" onclick="retryConnection()">
372
+ <span id="retry-icon">⟲</span>
373
+ <span id="retry-text">Check Connection</span>
374
+ </button>
375
+ <button class="btn btn-secondary" onclick="goToApp()">
376
+ Continue Offline
377
+ </button>
378
+ </div>
379
+
380
+ <div class="status-badge">
381
+ Checking connection <span style="display: inline-block; animation: spin 0.8s linear infinite;">⟳</span>
382
+ </div>
383
+ </div>
384
+
385
+ <script>
386
+ let retrying = false;
387
+
388
+ async function retryConnection() {
389
+ if (retrying) return;
390
+ retrying = true;
391
+
392
+ const btn = document.querySelector('.btn-primary');
393
+ const icon = document.getElementById('retry-icon');
394
+ const text = document.getElementById('retry-text');
395
+
396
+ icon.innerHTML = '<span class="spinner"></span>';
397
+ text.textContent = 'Checking...';
398
+ btn.disabled = true;
399
+
400
+ try {
401
+ // Try to fetch something from the server
402
+ const response = await fetch('/app/index.html', { method: 'HEAD' });
403
+
404
+ if (response.ok) {
405
+ // Connected!
406
+ icon.innerHTML = '✓';
407
+ text.textContent = 'Connected!';
408
+ setTimeout(() => {
409
+ window.location.replace('/app/');
410
+ }, 1000);
411
+ return;
412
+ }
413
+ } catch (err) {
414
+ // Still offline
415
+ }
416
+
417
+ // Still offline
418
+ icon.innerHTML = '⟲';
419
+ text.textContent = 'Check Connection';
420
+ btn.disabled = false;
421
+ retrying = false;
422
+
423
+ alert('Still offline. Please check your internet connection.');
424
+ }
425
+
426
+ function goToApp() {
427
+ window.location.replace('/app/');
428
+ }
429
+
430
+ // Load cached projects from IndexedDB
431
+ async function loadCachedProjects() {
432
+ try {
433
+ const db = await new Promise((resolve, reject) => {
434
+ const req = indexedDB.open('cyclecad', 1);
435
+ req.onerror = () => reject(req.error);
436
+ req.onsuccess = () => resolve(req.result);
437
+ });
438
+
439
+ const tx = db.transaction('projects', 'readonly');
440
+ const projects = await new Promise((resolve, reject) => {
441
+ const req = tx.objectStore('projects').getAll();
442
+ req.onerror = () => reject(req.error);
443
+ req.onsuccess = () => resolve(req.result);
444
+ });
445
+
446
+ const list = document.getElementById('project-list');
447
+ list.innerHTML = '';
448
+
449
+ if (projects.length === 0) {
450
+ list.innerHTML = '<li class="empty-state">No cached projects available offline.</li>';
451
+ return;
452
+ }
453
+
454
+ projects.forEach((project) => {
455
+ const item = document.createElement('li');
456
+ item.className = 'project-item';
457
+ item.onclick = () => {
458
+ // Navigate with project ID
459
+ window.location.href = `/app/?project=${project.id}`;
460
+ };
461
+
462
+ const date = new Date(project.lastModified || project.created);
463
+ const dateStr = date.toLocaleDateString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
464
+
465
+ item.innerHTML = `
466
+ <div>
467
+ <div class="project-name">${project.name || 'Untitled Project'}</div>
468
+ <div class="project-date">Modified ${dateStr}</div>
469
+ </div>
470
+ <div class="project-arrow">→</div>
471
+ `;
472
+
473
+ list.appendChild(item);
474
+ });
475
+
476
+ } catch (err) {
477
+ console.error('Failed to load cached projects:', err);
478
+ }
479
+ }
480
+
481
+ // Check connection periodically
482
+ function checkConnectionPeriodically() {
483
+ setInterval(async () => {
484
+ try {
485
+ const response = await fetch('/app/manifest.json', { method: 'HEAD' });
486
+ if (response.ok) {
487
+ // Connected! Redirect to app
488
+ window.location.replace('/app/');
489
+ }
490
+ } catch (err) {
491
+ // Still offline, do nothing
492
+ }
493
+ }, 5000);
494
+ }
495
+
496
+ // Initialize on load
497
+ window.addEventListener('load', () => {
498
+ loadCachedProjects();
499
+ checkConnectionPeriodically();
500
+ });
501
+
502
+ // Also check on online event
503
+ window.addEventListener('online', () => {
504
+ window.location.replace('/app/');
505
+ });
506
+ </script>
507
+ </body>
508
+ </html>