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,360 @@
1
+ /**
2
+ * Responsive Design Initialization for cycleCAD
3
+ * Detects device type, loads mobile CSS, initializes touch handlers
4
+ */
5
+
6
+ class ResponsiveInit {
7
+ constructor() {
8
+ this.device = null;
9
+ this.isMobile = false;
10
+ this.isTablet = false;
11
+ this.isDesktop = false;
12
+ this.isTouch = false;
13
+ this.viewport = {
14
+ width: window.innerWidth,
15
+ height: window.innerHeight,
16
+ orientation: window.innerHeight > window.innerWidth ? 'portrait' : 'landscape'
17
+ };
18
+
19
+ this.init();
20
+ }
21
+
22
+ init() {
23
+ this.detectDevice();
24
+ this.loadMobileCSS();
25
+ this.setViewportMeta();
26
+ this.initTouchHandlers();
27
+ this.initOrientationHandler();
28
+ this.reportDeviceInfo();
29
+ this.setupSafeAreas();
30
+ }
31
+
32
+ detectDevice() {
33
+ const width = this.viewport.width;
34
+ const height = this.viewport.height;
35
+ const userAgent = navigator.userAgent;
36
+
37
+ // Detect touch capability
38
+ this.isTouch = () => {
39
+ try {
40
+ document.createEvent('TouchEvent');
41
+ return true;
42
+ } catch (e) {
43
+ return false;
44
+ }
45
+ }() || navigator.maxTouchPoints > 0;
46
+
47
+ // Categorize by screen size
48
+ if (width < 600) {
49
+ this.isMobile = true;
50
+ this.device = 'phone';
51
+ } else if (width < 1200) {
52
+ this.isTablet = true;
53
+ this.device = 'tablet';
54
+ } else {
55
+ this.isDesktop = true;
56
+ this.device = 'desktop';
57
+ }
58
+
59
+ // Detect specific device
60
+ if (/iPad/.test(userAgent)) {
61
+ this.device = 'ipad';
62
+ } else if (/iPhone/.test(userAgent)) {
63
+ this.device = 'iphone';
64
+ } else if (/Android/.test(userAgent)) {
65
+ this.device = /Tablet|iPad/.test(userAgent) ? 'android-tablet' : 'android-phone';
66
+ } else if (/Windows NT/.test(userAgent) && /Touch/.test(userAgent)) {
67
+ this.device = 'surface';
68
+ }
69
+
70
+ console.log('[Responsive] Device detected:', {
71
+ device: this.device,
72
+ isMobile: this.isMobile,
73
+ isTablet: this.isTablet,
74
+ isDesktop: this.isDesktop,
75
+ isTouch: this.isTouch,
76
+ viewport: this.viewport
77
+ });
78
+ }
79
+
80
+ loadMobileCSS() {
81
+ // Load mobile.css if not already present
82
+ if (!document.getElementById('mobile-css')) {
83
+ const link = document.createElement('link');
84
+ link.id = 'mobile-css';
85
+ link.rel = 'stylesheet';
86
+ link.href = '/app/css/mobile.css';
87
+ link.media = 'all';
88
+ document.head.appendChild(link);
89
+
90
+ link.onload = () => {
91
+ console.log('[Responsive] mobile.css loaded successfully');
92
+ };
93
+
94
+ link.onerror = () => {
95
+ console.warn('[Responsive] Failed to load mobile.css');
96
+ };
97
+ }
98
+ }
99
+
100
+ setViewportMeta() {
101
+ // Check if viewport meta exists
102
+ let viewportMeta = document.querySelector('meta[name="viewport"]');
103
+
104
+ if (!viewportMeta) {
105
+ viewportMeta = document.createElement('meta');
106
+ viewportMeta.name = 'viewport';
107
+ document.head.appendChild(viewportMeta);
108
+ }
109
+
110
+ // Set optimal viewport settings for mobile
111
+ viewportMeta.content = [
112
+ 'width=device-width',
113
+ 'initial-scale=1.0',
114
+ 'viewport-fit=cover',
115
+ 'user-scalable=yes',
116
+ 'maximum-scale=5.0',
117
+ 'minimum-scale=1.0'
118
+ ].join(', ');
119
+
120
+ // Add theme-color meta for mobile browsers
121
+ let themeMeta = document.querySelector('meta[name="theme-color"]');
122
+ if (!themeMeta) {
123
+ themeMeta = document.createElement('meta');
124
+ themeMeta.name = 'theme-color';
125
+ themeMeta.content = '#ffffff';
126
+ document.head.appendChild(themeMeta);
127
+ }
128
+ }
129
+
130
+ setupSafeAreas() {
131
+ // Set CSS custom properties for safe area insets
132
+ const style = document.documentElement.style;
133
+
134
+ // iOS notch support
135
+ const topInset = parseInt(
136
+ getComputedStyle(document.documentElement).getPropertyValue('--safe-area-top') || '0'
137
+ );
138
+ const rightInset = parseInt(
139
+ getComputedStyle(document.documentElement).getPropertyValue('--safe-area-right') || '0'
140
+ );
141
+ const bottomInset = parseInt(
142
+ getComputedStyle(document.documentElement).getPropertyValue('--safe-area-bottom') || '0'
143
+ );
144
+ const leftInset = parseInt(
145
+ getComputedStyle(document.documentElement).getPropertyValue('--safe-area-left') || '0'
146
+ );
147
+
148
+ console.log('[Responsive] Safe areas:', { topInset, rightInset, bottomInset, leftInset });
149
+
150
+ // Add viewport-fit for proper notch handling
151
+ setViewportMeta();
152
+
153
+ function setViewportMeta() {
154
+ const meta = document.querySelector('meta[name="viewport"]');
155
+ if (meta && !meta.content.includes('viewport-fit')) {
156
+ meta.content += ', viewport-fit=cover';
157
+ }
158
+ }
159
+ }
160
+
161
+ initTouchHandlers() {
162
+ if (!this.isTouch) {
163
+ console.log('[Responsive] No touch capability detected');
164
+ return;
165
+ }
166
+
167
+ // Prevent default touch behaviors that interfere with app
168
+ document.addEventListener('touchmove', (e) => {
169
+ // Allow scrolling in specific elements
170
+ const scrollableSelectors = ['#left-panel', '#right-panel', '.bottom-sheet', '.results-log'];
171
+ const target = e.target;
172
+
173
+ const isScrollable = scrollableSelectors.some(selector => {
174
+ return target.closest(selector);
175
+ });
176
+
177
+ if (!isScrollable && e.cancelable) {
178
+ e.preventDefault();
179
+ }
180
+ }, { passive: false });
181
+
182
+ // Prevent iOS tap highlight
183
+ document.addEventListener('touchstart', () => {
184
+ // iOS tap-to-focus handling
185
+ }, false);
186
+
187
+ // Initialize TouchHandler if available
188
+ if (window.TouchHandler) {
189
+ const viewport = document.getElementById('viewport');
190
+ if (viewport) {
191
+ window.touchHandler = new TouchHandler(viewport, {
192
+ onTap: (touch) => this.handleTap(touch),
193
+ onDoubleTap: (touch) => this.handleDoubleTap(touch),
194
+ onLongPress: (touch) => this.handleLongPress(touch),
195
+ onSwipeLeft: () => this.openRightPanel(),
196
+ onSwipeRight: () => this.openLeftPanel(),
197
+ onSwipeUp: () => this.expandTimeline(),
198
+ onSwipeDown: () => this.collapseTimeline(),
199
+ onPinch: (data) => this.handlePinch(data),
200
+ onRotate: (data) => this.handleRotate(data),
201
+ onContextMenu: (pos) => this.showContextMenu(pos)
202
+ });
203
+
204
+ console.log('[Responsive] TouchHandler initialized');
205
+ }
206
+ }
207
+
208
+ console.log('[Responsive] Touch handlers initialized');
209
+ }
210
+
211
+ handleTap(touch) {
212
+ console.log('[Responsive] Tap at', touch);
213
+ }
214
+
215
+ handleDoubleTap(touch) {
216
+ console.log('[Responsive] Double-tap at', touch);
217
+ }
218
+
219
+ handleLongPress(touch) {
220
+ console.log('[Responsive] Long-press at', touch);
221
+ }
222
+
223
+ handlePinch(data) {
224
+ console.log('[Responsive] Pinch with scale', data.scale);
225
+ }
226
+
227
+ handleRotate(data) {
228
+ console.log('[Responsive] Rotate with delta', data.delta);
229
+ }
230
+
231
+ openRightPanel() {
232
+ const panel = document.getElementById('right-panel');
233
+ const backdrop = document.querySelector('.right-panel-backdrop');
234
+ if (panel) {
235
+ panel.classList.add('open');
236
+ if (backdrop) backdrop.classList.add('open');
237
+ }
238
+ }
239
+
240
+ openLeftPanel() {
241
+ const panel = document.getElementById('left-panel');
242
+ const backdrop = document.querySelector('.left-panel-backdrop');
243
+ if (panel) {
244
+ panel.classList.add('open');
245
+ if (backdrop) backdrop.classList.add('open');
246
+ }
247
+ }
248
+
249
+ expandTimeline() {
250
+ const timeline = document.querySelector('.timeline-container');
251
+ if (timeline) {
252
+ timeline.classList.add('open');
253
+ }
254
+ }
255
+
256
+ collapseTimeline() {
257
+ const timeline = document.querySelector('.timeline-container');
258
+ if (timeline) {
259
+ timeline.classList.remove('open');
260
+ }
261
+ }
262
+
263
+ showContextMenu(pos) {
264
+ console.log('[Responsive] Context menu at', pos);
265
+ }
266
+
267
+ initOrientationHandler() {
268
+ window.addEventListener('orientationchange', () => {
269
+ this.handleOrientationChange();
270
+ });
271
+
272
+ window.addEventListener('resize', () => {
273
+ this.handleResize();
274
+ });
275
+ }
276
+
277
+ handleOrientationChange() {
278
+ const orientation = window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
279
+
280
+ if (orientation !== this.viewport.orientation) {
281
+ this.viewport.orientation = orientation;
282
+
283
+ console.log('[Responsive] Orientation changed to', orientation);
284
+
285
+ // Close open panels on orientation change
286
+ document.getElementById('left-panel')?.classList.remove('open');
287
+ document.getElementById('right-panel')?.classList.remove('open');
288
+ document.querySelector('.left-panel-backdrop')?.classList.remove('open');
289
+ document.querySelector('.right-panel-backdrop')?.classList.remove('open');
290
+
291
+ // Re-layout as needed
292
+ this.handleResize();
293
+ }
294
+ }
295
+
296
+ handleResize() {
297
+ const newWidth = window.innerWidth;
298
+ const newHeight = window.innerHeight;
299
+
300
+ if (newWidth !== this.viewport.width || newHeight !== this.viewport.height) {
301
+ this.viewport.width = newWidth;
302
+ this.viewport.height = newHeight;
303
+
304
+ // Re-detect device type on resize
305
+ const oldDevice = this.device;
306
+ this.detectDevice();
307
+
308
+ if (oldDevice !== this.device) {
309
+ console.log('[Responsive] Device type changed from', oldDevice, 'to', this.device);
310
+ }
311
+
312
+ // Trigger layout recalculation
313
+ window.dispatchEvent(new CustomEvent('mobileLayoutChange', {
314
+ detail: {
315
+ device: this.device,
316
+ viewport: this.viewport,
317
+ isMobile: this.isMobile,
318
+ isTablet: this.isTablet,
319
+ isDesktop: this.isDesktop
320
+ }
321
+ }));
322
+ }
323
+ }
324
+
325
+ reportDeviceInfo() {
326
+ // Add device info to status bar if available
327
+ const statusBar = document.querySelector('.status-bar');
328
+ if (statusBar) {
329
+ const deviceInfo = document.createElement('span');
330
+ deviceInfo.className = 'device-info';
331
+ deviceInfo.style.marginLeft = 'auto';
332
+ deviceInfo.textContent = `${this.device} • ${this.viewport.width}×${this.viewport.height}`;
333
+ statusBar.appendChild(deviceInfo);
334
+ }
335
+
336
+ // Expose device info globally
337
+ window.deviceInfo = {
338
+ device: this.device,
339
+ isMobile: this.isMobile,
340
+ isTablet: this.isTablet,
341
+ isDesktop: this.isDesktop,
342
+ isTouch: this.isTouch,
343
+ viewport: this.viewport
344
+ };
345
+
346
+ console.log('[Responsive] Device info exposed to window.deviceInfo');
347
+ }
348
+ }
349
+
350
+ // Initialize on DOM ready
351
+ if (document.readyState === 'loading') {
352
+ document.addEventListener('DOMContentLoaded', () => {
353
+ window.responsiveInit = new ResponsiveInit();
354
+ });
355
+ } else {
356
+ window.responsiveInit = new ResponsiveInit();
357
+ }
358
+
359
+ // Also expose class globally for reference
360
+ window.ResponsiveInit = ResponsiveInit;