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.
- package/BILLING-IMPLEMENTATION-SUMMARY.md +425 -0
- package/BILLING-INDEX.md +293 -0
- package/BILLING-INTEGRATION-GUIDE.md +414 -0
- package/COLLABORATION-INDEX.md +440 -0
- package/COLLABORATION-SYSTEM-SUMMARY.md +548 -0
- package/DOCKER-BUILD-MANIFEST.txt +483 -0
- package/DOCKER-FILES-REFERENCE.md +440 -0
- package/DOCKER-INFRASTRUCTURE.md +475 -0
- package/DOCKER-README.md +435 -0
- package/Dockerfile +33 -55
- package/PWA-FILES-CREATED.txt +350 -0
- package/QUICK-START-TESTING.md +126 -0
- package/STEP-IMPORT-QUICKSTART.md +347 -0
- package/STEP-IMPORT-SYSTEM-SUMMARY.md +502 -0
- package/app/css/mobile.css +1074 -0
- package/app/icons/generate-icons.js +203 -0
- package/app/index.html +93 -0
- package/app/js/billing-ui.js +990 -0
- package/app/js/brep-kernel.js +933 -981
- package/app/js/collab-client.js +750 -0
- package/app/js/mobile-nav.js +623 -0
- package/app/js/mobile-toolbar.js +476 -0
- package/app/js/modules/billing-module.js +724 -0
- package/app/js/modules/step-module-enhanced.js +938 -0
- package/app/js/offline-manager.js +705 -0
- package/app/js/responsive-init.js +360 -0
- package/app/js/touch-handler.js +429 -0
- package/app/manifest.json +211 -0
- package/app/offline.html +508 -0
- package/app/sw.js +571 -0
- package/app/tests/billing-tests.html +779 -0
- package/app/tests/brep-tests.html +980 -0
- package/app/tests/collab-tests.html +743 -0
- package/app/tests/mobile-tests.html +1299 -0
- package/app/tests/pwa-tests.html +1134 -0
- package/app/tests/step-tests.html +1042 -0
- package/app/tests/test-agent-v3.html +719 -0
- package/docker-compose.yml +225 -0
- package/docs/BILLING-HELP.json +260 -0
- package/docs/BILLING-README.md +639 -0
- package/docs/BILLING-TUTORIAL.md +736 -0
- package/docs/BREP-HELP.json +326 -0
- package/docs/BREP-TUTORIAL.md +802 -0
- package/docs/COLLABORATION-HELP.json +228 -0
- package/docs/COLLABORATION-TUTORIAL.md +818 -0
- package/docs/DOCKER-HELP.json +224 -0
- package/docs/DOCKER-TUTORIAL.md +974 -0
- package/docs/MOBILE-HELP.json +243 -0
- package/docs/MOBILE-RESPONSIVE-README.md +378 -0
- package/docs/MOBILE-TUTORIAL.md +747 -0
- package/docs/PWA-HELP.json +228 -0
- package/docs/PWA-README.md +662 -0
- package/docs/PWA-TUTORIAL.md +757 -0
- package/docs/STEP-HELP.json +481 -0
- package/docs/STEP-IMPORT-TUTORIAL.md +824 -0
- package/docs/TESTING-GUIDE.md +528 -0
- package/docs/TESTING-HELP.json +182 -0
- package/fusion-vs-cyclecad.html +1771 -0
- package/nginx.conf +237 -0
- package/package.json +1 -1
- package/server/Dockerfile.converter +51 -0
- package/server/Dockerfile.signaling +28 -0
- package/server/billing-server.js +487 -0
- package/server/converter-enhanced.py +528 -0
- package/server/requirements-converter.txt +29 -0
- package/server/signaling-server.js +801 -0
- 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;
|