@nasser-sw/fabric 7.0.1-beta3 → 7.0.1-beta4

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 (105) hide show
  1. package/0 +0 -0
  2. package/dist/index.js +323 -155
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.min.js +1 -1
  5. package/dist/index.min.js.map +1 -1
  6. package/dist/index.min.mjs +1 -1
  7. package/dist/index.min.mjs.map +1 -1
  8. package/dist/index.mjs +323 -155
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/index.node.cjs +323 -155
  11. package/dist/index.node.cjs.map +1 -1
  12. package/dist/index.node.mjs +323 -155
  13. package/dist/index.node.mjs.map +1 -1
  14. package/dist/package.json.min.mjs +1 -1
  15. package/dist/package.json.mjs +1 -1
  16. package/dist/src/shapes/Line.d.ts +31 -86
  17. package/dist/src/shapes/Line.d.ts.map +1 -1
  18. package/dist/src/shapes/Line.min.mjs +1 -1
  19. package/dist/src/shapes/Line.min.mjs.map +1 -1
  20. package/dist/src/shapes/Line.mjs +323 -154
  21. package/dist/src/shapes/Line.mjs.map +1 -1
  22. package/dist-extensions/src/shapes/CustomLine.d.ts +10 -0
  23. package/dist-extensions/src/shapes/CustomLine.d.ts.map +1 -0
  24. package/dist-extensions/src/shapes/Line.d.ts +31 -86
  25. package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
  26. package/fabric-test-editor.html +157 -8
  27. package/fabric-test2.html +513 -0
  28. package/fabric.ts +182 -182
  29. package/package.json +1 -1
  30. package/src/shapes/Line.ts +372 -158
  31. package/debug/konva/CHANGELOG.md +0 -1474
  32. package/debug/konva/LICENSE +0 -22
  33. package/debug/konva/README.md +0 -205
  34. package/debug/konva/gulpfile.mjs +0 -110
  35. package/debug/konva/package.json +0 -139
  36. package/debug/konva/release.sh +0 -65
  37. package/debug/konva/resources/doc-includes/ContainerParams.txt +0 -6
  38. package/debug/konva/resources/doc-includes/NodeParams.txt +0 -20
  39. package/debug/konva/resources/doc-includes/ShapeParams.txt +0 -53
  40. package/debug/konva/resources/jsdoc.conf.json +0 -28
  41. package/debug/konva/rollup.config.mjs +0 -32
  42. package/debug/konva/src/Animation.ts +0 -237
  43. package/debug/konva/src/BezierFunctions.ts +0 -826
  44. package/debug/konva/src/Canvas.ts +0 -193
  45. package/debug/konva/src/Container.ts +0 -649
  46. package/debug/konva/src/Context.ts +0 -1017
  47. package/debug/konva/src/Core.ts +0 -5
  48. package/debug/konva/src/DragAndDrop.ts +0 -173
  49. package/debug/konva/src/Factory.ts +0 -246
  50. package/debug/konva/src/FastLayer.ts +0 -29
  51. package/debug/konva/src/Global.ts +0 -210
  52. package/debug/konva/src/Group.ts +0 -31
  53. package/debug/konva/src/Layer.ts +0 -546
  54. package/debug/konva/src/Node.ts +0 -3477
  55. package/debug/konva/src/PointerEvents.ts +0 -67
  56. package/debug/konva/src/Shape.ts +0 -2081
  57. package/debug/konva/src/Stage.ts +0 -1000
  58. package/debug/konva/src/Tween.ts +0 -811
  59. package/debug/konva/src/Util.ts +0 -1123
  60. package/debug/konva/src/Validators.ts +0 -210
  61. package/debug/konva/src/_CoreInternals.ts +0 -85
  62. package/debug/konva/src/_FullInternals.ts +0 -171
  63. package/debug/konva/src/canvas-backend.ts +0 -36
  64. package/debug/konva/src/filters/Blur.ts +0 -388
  65. package/debug/konva/src/filters/Brighten.ts +0 -48
  66. package/debug/konva/src/filters/Brightness.ts +0 -30
  67. package/debug/konva/src/filters/Contrast.ts +0 -75
  68. package/debug/konva/src/filters/Emboss.ts +0 -207
  69. package/debug/konva/src/filters/Enhance.ts +0 -154
  70. package/debug/konva/src/filters/Grayscale.ts +0 -25
  71. package/debug/konva/src/filters/HSL.ts +0 -108
  72. package/debug/konva/src/filters/HSV.ts +0 -106
  73. package/debug/konva/src/filters/Invert.ts +0 -23
  74. package/debug/konva/src/filters/Kaleidoscope.ts +0 -274
  75. package/debug/konva/src/filters/Mask.ts +0 -220
  76. package/debug/konva/src/filters/Noise.ts +0 -44
  77. package/debug/konva/src/filters/Pixelate.ts +0 -107
  78. package/debug/konva/src/filters/Posterize.ts +0 -46
  79. package/debug/konva/src/filters/RGB.ts +0 -82
  80. package/debug/konva/src/filters/RGBA.ts +0 -103
  81. package/debug/konva/src/filters/Sepia.ts +0 -27
  82. package/debug/konva/src/filters/Solarize.ts +0 -29
  83. package/debug/konva/src/filters/Threshold.ts +0 -44
  84. package/debug/konva/src/index.ts +0 -3
  85. package/debug/konva/src/shapes/Arc.ts +0 -176
  86. package/debug/konva/src/shapes/Arrow.ts +0 -231
  87. package/debug/konva/src/shapes/Circle.ts +0 -76
  88. package/debug/konva/src/shapes/Ellipse.ts +0 -121
  89. package/debug/konva/src/shapes/Image.ts +0 -319
  90. package/debug/konva/src/shapes/Label.ts +0 -386
  91. package/debug/konva/src/shapes/Line.ts +0 -364
  92. package/debug/konva/src/shapes/Path.ts +0 -1013
  93. package/debug/konva/src/shapes/Rect.ts +0 -79
  94. package/debug/konva/src/shapes/RegularPolygon.ts +0 -167
  95. package/debug/konva/src/shapes/Ring.ts +0 -94
  96. package/debug/konva/src/shapes/Sprite.ts +0 -370
  97. package/debug/konva/src/shapes/Star.ts +0 -125
  98. package/debug/konva/src/shapes/Text.ts +0 -1065
  99. package/debug/konva/src/shapes/TextPath.ts +0 -583
  100. package/debug/konva/src/shapes/Transformer.ts +0 -1889
  101. package/debug/konva/src/shapes/Wedge.ts +0 -129
  102. package/debug/konva/src/skia-backend.ts +0 -35
  103. package/debug/konva/src/types.ts +0 -84
  104. package/debug/konva/tsconfig.json +0 -31
  105. package/debug/konva/tsconfig.test.json +0 -7
@@ -0,0 +1,513 @@
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
+ <title>Fabric.js 7 Line Test</title>
7
+ <style>
8
+ body {
9
+ margin: 20px;
10
+ font-family: Arial, sans-serif;
11
+ }
12
+
13
+ #canvas-container {
14
+ border: 2px solid #ddd;
15
+ display: inline-block;
16
+ margin: 10px 0;
17
+ }
18
+
19
+ .controls {
20
+ margin: 10px 0;
21
+ }
22
+
23
+ button {
24
+ margin: 5px;
25
+ padding: 8px 16px;
26
+ font-size: 14px;
27
+ }
28
+
29
+ .info {
30
+ background: #f5f5f5;
31
+ padding: 10px;
32
+ margin: 10px 0;
33
+ border-radius: 4px;
34
+ }
35
+ </style>
36
+ </head>
37
+ <body>
38
+ <h1>Fabric.js 7 Line Rendering Test</h1>
39
+
40
+ <div class="info">
41
+ <strong>Test Instructions:</strong>
42
+ <ol>
43
+ <li>Click "Add Line" to create a line</li>
44
+ <li>Select the line to see endpoint controls</li>
45
+ <li>Drag the endpoint controls (blue circles)</li>
46
+ <li>Observe if the line renders correctly during drag preview</li>
47
+ <li>Check if the line renders correctly after releasing the drag</li>
48
+ </ol>
49
+ </div>
50
+
51
+ <div class="controls">
52
+ <button id="addLineBtn">Add Line</button>
53
+ <button id="clearCanvasBtn">Clear Canvas</button>
54
+ <button id="toggleDebugBtn">Toggle Debug</button>
55
+ <button id="exportPngBtn">Export as PNG</button>
56
+ <button id="exportSvgBtn">Export as SVG</button>
57
+ </div>
58
+
59
+ <div style="display: flex; gap: 20px;">
60
+ <div>
61
+ <h3>Fabric.js 7 Implementation</h3>
62
+ <div id="canvas-container">
63
+ <canvas id="canvas" width="800" height="600"></canvas>
64
+ </div>
65
+ </div>
66
+
67
+ <div>
68
+ <h3>Native Canvas 2D (CFX) Implementation</h3>
69
+ <div style="border: 2px solid #ddd; display: inline-block;">
70
+ <canvas id="cfx-canvas" width="800" height="600"></canvas>
71
+ </div>
72
+ <div class="controls">
73
+ <button id="addCfxLineBtn">Add CFX Line</button>
74
+ <button id="clearCfxCanvasBtn">Clear CFX Canvas</button>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
79
+ <div class="info">
80
+ <strong>Expected Behavior:</strong> Line should render smoothly during endpoint dragging without clipping or positioning issues.
81
+ <br><strong>Problem:</strong> Line may not render correctly in drag preview but appears fine after drag completion.
82
+ </div>
83
+
84
+ <!-- Load Fabric.js 7 from local build -->
85
+ <script src="./dist/index.js"></script>
86
+
87
+ <script>
88
+ // Initialize canvas
89
+ const canvas = new fabric.Canvas('canvas', {
90
+ backgroundColor: 'white',
91
+ selection: true
92
+ });
93
+
94
+ let debugMode = false;
95
+ let lineCounter = 1;
96
+
97
+ // Add Line button
98
+ document.getElementById('addLineBtn').addEventListener('click', () => {
99
+ // Create a line with some default coordinates
100
+ const line = new fabric.Line([100, 100, 300, 200], {
101
+ stroke: '#ff0000',
102
+ strokeWidth: 3,
103
+ strokeLineCap: 'round',
104
+ selectable: true,
105
+ evented: true
106
+ });
107
+
108
+ canvas.add(line);
109
+ canvas.setActiveObject(line);
110
+ canvas.renderAll();
111
+
112
+ console.log('Added line:', line);
113
+ lineCounter++;
114
+ });
115
+
116
+ // Clear Canvas button
117
+ document.getElementById('clearCanvasBtn').addEventListener('click', () => {
118
+ canvas.clear();
119
+ canvas.backgroundColor = 'white';
120
+ canvas.renderAll();
121
+ lineCounter = 1;
122
+ });
123
+
124
+ // Toggle Debug button
125
+ document.getElementById('toggleDebugBtn').addEventListener('click', () => {
126
+ debugMode = !debugMode;
127
+ const button = document.getElementById('toggleDebugBtn');
128
+ button.textContent = debugMode ? 'Disable Debug' : 'Enable Debug';
129
+
130
+ // Toggle debug visualization for all lines
131
+ canvas.getObjects('line').forEach(line => {
132
+ if (line._debugBoundingBox) {
133
+ // Enable/disable debug rendering
134
+ line._showDebug = debugMode;
135
+ }
136
+ });
137
+
138
+ canvas.renderAll();
139
+ });
140
+
141
+ // Export as PNG button
142
+ document.getElementById('exportPngBtn').addEventListener('click', () => {
143
+ try {
144
+ // Ensure canvas is rendered before export
145
+ canvas.renderAll();
146
+
147
+ // Get the canvas data URL
148
+ const dataURL = canvas.toDataURL({
149
+ format: 'png',
150
+ quality: 1.0,
151
+ multiplier: 1
152
+ });
153
+
154
+ // Create download link
155
+ const link = document.createElement('a');
156
+ link.download = `fabric-line-export-${Date.now()}.png`;
157
+ link.href = dataURL;
158
+
159
+ // Trigger download
160
+ document.body.appendChild(link);
161
+ link.click();
162
+ document.body.removeChild(link);
163
+
164
+ console.log('PNG export completed successfully');
165
+ } catch (error) {
166
+ console.error('PNG export failed:', error);
167
+ alert('Export failed: ' + error.message);
168
+ }
169
+ });
170
+
171
+ // Export as SVG button
172
+ document.getElementById('exportSvgBtn').addEventListener('click', () => {
173
+ try {
174
+ // Ensure canvas is rendered before export
175
+ canvas.renderAll();
176
+
177
+ // Get the SVG string
178
+ const svgString = canvas.toSVG({
179
+ viewBox: {
180
+ x: 0,
181
+ y: 0,
182
+ width: canvas.getWidth(),
183
+ height: canvas.getHeight()
184
+ },
185
+ width: canvas.getWidth(),
186
+ height: canvas.getHeight()
187
+ });
188
+
189
+ // Create blob and download link
190
+ const blob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });
191
+ const url = URL.createObjectURL(blob);
192
+
193
+ const link = document.createElement('a');
194
+ link.download = `fabric-line-export-${Date.now()}.svg`;
195
+ link.href = url;
196
+
197
+ // Trigger download
198
+ document.body.appendChild(link);
199
+ link.click();
200
+ document.body.removeChild(link);
201
+
202
+ // Clean up
203
+ URL.revokeObjectURL(url);
204
+
205
+ console.log('SVG export completed successfully');
206
+ console.log('SVG content preview:', svgString.substring(0, 200) + '...');
207
+ } catch (error) {
208
+ console.error('SVG export failed:', error);
209
+ alert('SVG export failed: ' + error.message);
210
+ }
211
+ });
212
+
213
+ // Canvas event listeners for debugging
214
+ canvas.on('selection:created', (e) => {
215
+ console.log('Selection created:', e.target);
216
+ });
217
+
218
+ canvas.on('selection:updated', (e) => {
219
+ console.log('Selection updated:', e.target);
220
+ });
221
+
222
+ canvas.on('object:moving', (e) => {
223
+ if (debugMode) {
224
+ console.log('Object moving:', e.target.type, {
225
+ left: e.target.left,
226
+ top: e.target.top,
227
+ x1: e.target.x1,
228
+ y1: e.target.y1,
229
+ x2: e.target.x2,
230
+ y2: e.target.y2
231
+ });
232
+ }
233
+ });
234
+
235
+ canvas.on('object:modified', (e) => {
236
+ console.log('Object modified:', e.target.type, {
237
+ left: e.target.left,
238
+ top: e.target.top,
239
+ x1: e.target.x1,
240
+ y1: e.target.y1,
241
+ x2: e.target.x2,
242
+ y2: e.target.y2
243
+ });
244
+ });
245
+
246
+ // Add initial line for testing
247
+ const initialLine = new fabric.Line([150, 150, 350, 250], {
248
+ stroke: '#2196F3',
249
+ strokeWidth: 4,
250
+ strokeLineCap: 'round',
251
+ selectable: true,
252
+ evented: true
253
+ });
254
+
255
+ canvas.add(initialLine);
256
+ canvas.renderAll();
257
+
258
+ console.log('Fabric.js version:', fabric.version);
259
+ console.log('Initial line added:', initialLine);
260
+ </script>
261
+
262
+ <script>
263
+ // CFX (Native Canvas 2D) Implementation
264
+ class CFXLine {
265
+ constructor(x1, y1, x2, y2, options = {}) {
266
+ this.x1 = x1;
267
+ this.y1 = y1;
268
+ this.x2 = x2;
269
+ this.y2 = y2;
270
+ this.stroke = options.stroke || '#000000';
271
+ this.strokeWidth = options.strokeWidth || 1;
272
+ this.strokeLineCap = options.strokeLineCap || 'butt';
273
+ this.selected = false;
274
+ this.dragging = null; // null, 'p1', 'p2', or 'line'
275
+ this.controlSize = 12;
276
+ }
277
+
278
+ draw(ctx) {
279
+ ctx.save();
280
+ ctx.strokeStyle = this.stroke;
281
+ ctx.lineWidth = this.strokeWidth;
282
+ ctx.lineCap = this.strokeLineCap;
283
+
284
+ ctx.beginPath();
285
+ ctx.moveTo(this.x1, this.y1);
286
+ ctx.lineTo(this.x2, this.y2);
287
+ ctx.stroke();
288
+
289
+ // Draw endpoint controls if selected
290
+ if (this.selected) {
291
+ this.drawControls(ctx);
292
+ }
293
+
294
+ ctx.restore();
295
+ }
296
+
297
+ drawControls(ctx) {
298
+ ctx.save();
299
+ ctx.fillStyle = '#007bff';
300
+ ctx.strokeStyle = '#ffffff';
301
+ ctx.lineWidth = 2;
302
+
303
+ // Draw p1 control
304
+ ctx.beginPath();
305
+ ctx.arc(this.x1, this.y1, this.controlSize / 2, 0, 2 * Math.PI);
306
+ ctx.fill();
307
+ ctx.stroke();
308
+
309
+ // Draw p2 control
310
+ ctx.beginPath();
311
+ ctx.arc(this.x2, this.y2, this.controlSize / 2, 0, 2 * Math.PI);
312
+ ctx.fill();
313
+ ctx.stroke();
314
+
315
+ ctx.restore();
316
+ }
317
+
318
+ hitTest(x, y) {
319
+ // Test endpoint controls first
320
+ if (this.selected) {
321
+ const dist1 = Math.sqrt((x - this.x1) ** 2 + (y - this.y1) ** 2);
322
+ if (dist1 <= this.controlSize) return 'p1';
323
+
324
+ const dist2 = Math.sqrt((x - this.x2) ** 2 + (y - this.y2) ** 2);
325
+ if (dist2 <= this.controlSize) return 'p2';
326
+ }
327
+
328
+ // Test line itself (using distance from point to line)
329
+ const A = this.y2 - this.y1;
330
+ const B = this.x1 - this.x2;
331
+ const C = this.x2 * this.y1 - this.x1 * this.y2;
332
+ const distance = Math.abs(A * x + B * y + C) / Math.sqrt(A * A + B * B);
333
+
334
+ if (distance <= this.strokeWidth / 2 + 3) {
335
+ // Check if point is within line segment bounds
336
+ const minX = Math.min(this.x1, this.x2) - 5;
337
+ const maxX = Math.max(this.x1, this.x2) + 5;
338
+ const minY = Math.min(this.y1, this.y2) - 5;
339
+ const maxY = Math.max(this.y1, this.y2) + 5;
340
+
341
+ if (x >= minX && x <= maxX && y >= minY && y <= maxY) {
342
+ return 'line';
343
+ }
344
+ }
345
+
346
+ return null;
347
+ }
348
+
349
+ updateEndpoint(endpoint, x, y, shiftKey = false) {
350
+ if (shiftKey) {
351
+ // Snap to 15-degree angles
352
+ const fixedX = endpoint === 'p1' ? this.x2 : this.x1;
353
+ const fixedY = endpoint === 'p1' ? this.y2 : this.y1;
354
+
355
+ const deltaX = x - fixedX;
356
+ const deltaY = y - fixedY;
357
+ const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
358
+
359
+ if (distance > 0) {
360
+ let angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
361
+ const snapIncrement = 15;
362
+ const snappedAngle = Math.round(angle / snapIncrement) * snapIncrement;
363
+ const snappedRadians = snappedAngle * (Math.PI / 180);
364
+
365
+ x = fixedX + Math.cos(snappedRadians) * distance;
366
+ y = fixedY + Math.sin(snappedRadians) * distance;
367
+ }
368
+ }
369
+
370
+ if (endpoint === 'p1') {
371
+ this.x1 = x;
372
+ this.y1 = y;
373
+ } else if (endpoint === 'p2') {
374
+ this.x2 = x;
375
+ this.y2 = y;
376
+ }
377
+ }
378
+
379
+ move(deltaX, deltaY) {
380
+ this.x1 += deltaX;
381
+ this.y1 += deltaY;
382
+ this.x2 += deltaX;
383
+ this.y2 += deltaY;
384
+ }
385
+ }
386
+
387
+ // CFX Canvas Setup
388
+ const cfxCanvas = document.getElementById('cfx-canvas');
389
+ const cfxCtx = cfxCanvas.getContext('2d');
390
+ let cfxLines = [];
391
+ let cfxSelectedLine = null;
392
+ let cfxDragStart = null;
393
+ let cfxDragType = null;
394
+
395
+ function cfxRender() {
396
+ cfxCtx.clearRect(0, 0, cfxCanvas.width, cfxCanvas.height);
397
+ cfxCtx.fillStyle = 'white';
398
+ cfxCtx.fillRect(0, 0, cfxCanvas.width, cfxCanvas.height);
399
+
400
+ cfxLines.forEach(line => line.draw(cfxCtx));
401
+ }
402
+
403
+ function cfxGetMousePos(e) {
404
+ const rect = cfxCanvas.getBoundingClientRect();
405
+ return {
406
+ x: e.clientX - rect.left,
407
+ y: e.clientY - rect.top
408
+ };
409
+ }
410
+
411
+ // CFX Mouse Events
412
+ cfxCanvas.addEventListener('mousedown', (e) => {
413
+ const pos = cfxGetMousePos(e);
414
+
415
+ // Test all lines for hit
416
+ for (let line of cfxLines) {
417
+ const hit = line.hitTest(pos.x, pos.y);
418
+ if (hit) {
419
+ cfxSelectedLine = line;
420
+ cfxDragType = hit;
421
+ cfxDragStart = pos;
422
+
423
+ // Update selection
424
+ cfxLines.forEach(l => l.selected = (l === line));
425
+ cfxRender();
426
+ return;
427
+ }
428
+ }
429
+
430
+ // No hit - deselect all
431
+ cfxSelectedLine = null;
432
+ cfxLines.forEach(l => l.selected = false);
433
+ cfxRender();
434
+ });
435
+
436
+ cfxCanvas.addEventListener('mousemove', (e) => {
437
+ if (!cfxSelectedLine || !cfxDragStart) return;
438
+
439
+ const pos = cfxGetMousePos(e);
440
+ const deltaX = pos.x - cfxDragStart.x;
441
+ const deltaY = pos.y - cfxDragStart.y;
442
+
443
+ if (cfxDragType === 'p1') {
444
+ cfxSelectedLine.updateEndpoint('p1', pos.x, pos.y, e.shiftKey);
445
+ } else if (cfxDragType === 'p2') {
446
+ cfxSelectedLine.updateEndpoint('p2', pos.x, pos.y, e.shiftKey);
447
+ } else if (cfxDragType === 'line') {
448
+ cfxSelectedLine.move(deltaX, deltaY);
449
+ cfxDragStart = pos;
450
+ }
451
+
452
+ cfxRender();
453
+ });
454
+
455
+ cfxCanvas.addEventListener('mouseup', (e) => {
456
+ cfxDragStart = null;
457
+ cfxDragType = null;
458
+ });
459
+
460
+ // CFX Cursor handling
461
+ cfxCanvas.addEventListener('mousemove', (e) => {
462
+ if (cfxDragStart) return; // Don't change cursor while dragging
463
+
464
+ const pos = cfxGetMousePos(e);
465
+ let cursor = 'default';
466
+
467
+ for (let line of cfxLines) {
468
+ const hit = line.hitTest(pos.x, pos.y);
469
+ if (hit === 'p1' || hit === 'p2') {
470
+ cursor = 'move';
471
+ break;
472
+ } else if (hit === 'line') {
473
+ cursor = 'move';
474
+ break;
475
+ }
476
+ }
477
+
478
+ cfxCanvas.style.cursor = cursor;
479
+ });
480
+
481
+ // CFX Controls
482
+ document.getElementById('addCfxLineBtn').addEventListener('click', () => {
483
+ const line = new CFXLine(150, 150, 350, 250, {
484
+ stroke: '#ff0000',
485
+ strokeWidth: 3,
486
+ strokeLineCap: 'round'
487
+ });
488
+
489
+ cfxLines.push(line);
490
+ cfxRender();
491
+ console.log('Added CFX line:', line);
492
+ });
493
+
494
+ document.getElementById('clearCfxCanvasBtn').addEventListener('click', () => {
495
+ cfxLines = [];
496
+ cfxSelectedLine = null;
497
+ cfxRender();
498
+ });
499
+
500
+ // Add initial CFX line
501
+ const initialCfxLine = new CFXLine(150, 150, 350, 250, {
502
+ stroke: '#2196F3',
503
+ strokeWidth: 4,
504
+ strokeLineCap: 'round'
505
+ });
506
+
507
+ cfxLines.push(initialCfxLine);
508
+ cfxRender();
509
+
510
+ console.log('Initial CFX line added:', initialCfxLine);
511
+ </script>
512
+ </body>
513
+ </html>