pushfeedback 0.1.69 → 0.1.71

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 (27) hide show
  1. package/dist/cjs/{feedback-button_2.cjs.entry.js → canvas-editor_3.cjs.entry.js} +883 -718
  2. package/dist/cjs/loader.cjs.js +1 -1
  3. package/dist/cjs/pushfeedback.cjs.js +1 -1
  4. package/dist/collection/collection-manifest.json +1 -0
  5. package/dist/collection/components/canvas-editor/canvas-editor.css +404 -0
  6. package/dist/collection/components/canvas-editor/canvas-editor.js +1282 -0
  7. package/dist/collection/components/feedback-button/feedback-button.js +220 -0
  8. package/dist/collection/components/feedback-modal/feedback-modal.css +2 -458
  9. package/dist/collection/components/feedback-modal/feedback-modal.js +247 -782
  10. package/dist/components/canvas-editor.d.ts +11 -0
  11. package/dist/components/canvas-editor.js +6 -0
  12. package/dist/components/canvas-editor2.js +917 -0
  13. package/dist/components/feedback-button.js +40 -1
  14. package/dist/components/feedback-modal2.js +68 -784
  15. package/dist/components/index.d.ts +1 -0
  16. package/dist/components/index.js +1 -0
  17. package/dist/esm/{feedback-button_2.entry.js → canvas-editor_3.entry.js} +883 -719
  18. package/dist/esm/loader.js +1 -1
  19. package/dist/esm/pushfeedback.js +1 -1
  20. package/dist/pushfeedback/p-2c39091c.entry.js +1 -0
  21. package/dist/pushfeedback/pushfeedback.esm.js +1 -1
  22. package/dist/types/components/canvas-editor/canvas-editor.d.ts +108 -0
  23. package/dist/types/components/feedback-button/feedback-button.d.ts +11 -0
  24. package/dist/types/components/feedback-modal/feedback-modal.d.ts +22 -79
  25. package/dist/types/components.d.ts +102 -0
  26. package/package.json +3 -4
  27. package/dist/pushfeedback/p-e7f48090.entry.js +0 -1
@@ -85,26 +85,6 @@ export class FeedbackModal {
85
85
  document.querySelectorAll('.feedback-modal-element-selected').forEach(el => {
86
86
  el.classList.remove('feedback-modal-element-selected');
87
87
  });
88
- // Reset canvas editor states
89
- this.takingScreenshot = false;
90
- this.showPreviewModal = false;
91
- this.showCanvasEditor = false;
92
- this.annotations = [];
93
- this.currentAnnotation = null;
94
- this.isDrawing = false;
95
- this.canvasRef = null;
96
- this.canvasContext = null;
97
- this.originalImageData = null;
98
- // Reset error states
99
- this.showScreenshotError = false;
100
- this.screenshotError = '';
101
- // Reset resizing states
102
- this.isResizing = false;
103
- this.resizingAnnotation = null;
104
- this.resizeStartSize = 16;
105
- this.resizeStartDimensions = null;
106
- this.hoveredAnnotation = null;
107
- this.resizeHandle = false;
108
88
  // Reset form states
109
89
  this.formSuccess = false;
110
90
  this.formError = false;
@@ -114,681 +94,50 @@ export class FeedbackModal {
114
94
  this.resetOverflow();
115
95
  }, 200);
116
96
  };
117
- this.openScreenShot = async () => {
118
- // Show loading state immediately
119
- this.takingScreenshot = true;
120
- this.showScreenshotError = false;
121
- // Clear any previous annotations when taking a new screenshot
122
- this.annotations = [];
123
- this.currentAnnotation = null;
124
- this.isDrawing = false;
125
- this.hoveredAnnotation = null;
126
- // Hide the feedback modal temporarily to exclude it from screenshot
127
- const wasModalVisible = this.showModal;
128
- this.showModal = false;
129
- // Also hide any feedback buttons on the page
130
- this.hideAllFeedbackElements();
131
- try {
132
- // Wait a moment for UI to update before capturing
133
- await new Promise(resolve => setTimeout(resolve, 100));
134
- // Capture viewport screenshot using browser API
135
- const dataUrl = await this.captureViewportScreenshot();
136
- this.encodedScreenshot = dataUrl;
137
- this.originalImageData = dataUrl;
138
- // Reset loading state
139
- this.takingScreenshot = false;
140
- // Go directly to canvas editor (don't restore modal)
141
- this.showCanvasEditor = true;
142
- // Restore feedback elements visibility
143
- this.showAllFeedbackElements();
144
- // Initialize canvas after a short delay to ensure DOM is ready
145
- setTimeout(() => {
146
- this.initializeCanvas();
147
- }, 100);
148
- }
149
- catch (error) {
150
- console.error('Failed to capture screenshot:', error);
151
- // Reset loading state on error
152
- this.takingScreenshot = false;
153
- // Restore modal and feedback elements on error
154
- this.showModal = wasModalVisible;
155
- this.showAllFeedbackElements();
156
- // Show error message to user
157
- this.handleScreenshotError(error);
158
- }
159
- };
160
- this.hideAllFeedbackElements = () => {
161
- // Hide all feedback buttons and modals on the page
162
- const feedbackElements = document.querySelectorAll('feedback-button, feedback-modal');
163
- feedbackElements.forEach(element => {
164
- element.style.visibility = 'hidden';
165
- });
97
+ // Handle screenshot events from canvas editor
98
+ this.handleScreenshotReady = (event) => {
99
+ this.encodedScreenshot = event.detail.screenshot;
100
+ this.showModal = true;
101
+ this.takingScreenshot = false;
102
+ this.showCanvasEditor = false;
103
+ this.autoStartCapture = false;
166
104
  };
167
- this.showAllFeedbackElements = () => {
168
- // Show all feedback buttons and modals on the page
169
- const feedbackElements = document.querySelectorAll('feedback-button, feedback-modal');
170
- feedbackElements.forEach(element => {
171
- element.style.visibility = 'visible';
172
- });
105
+ this.handleScreenshotCancelled = () => {
106
+ this.showModal = true;
107
+ this.takingScreenshot = false;
108
+ this.showCanvasEditor = false;
109
+ this.autoStartCapture = false;
173
110
  };
174
- this.handleScreenshotError = (error) => {
175
- let errorMessage = 'Failed to capture screenshot. ';
176
- if (error.name === 'NotAllowedError') {
177
- errorMessage += 'Permission denied. Please allow screen sharing to take screenshots.';
178
- }
179
- else if (error.name === 'NotSupportedError') {
180
- errorMessage += 'Screen capture is not supported in this browser.';
181
- }
182
- else if (error.name === 'NotFoundError') {
183
- errorMessage += 'No screen sources available for capture.';
184
- }
185
- else if (error.name === 'AbortError') {
186
- errorMessage += 'Screenshot capture was cancelled.';
187
- }
188
- else if (error.message && error.message.includes('not supported')) {
189
- errorMessage += 'Your browser does not support screen capture. Please use a browser like Chrome, Firefox, or Safari.';
190
- }
191
- else {
192
- errorMessage += 'An unexpected error occurred. Please try again.';
193
- }
194
- this.screenshotError = errorMessage;
111
+ this.handleScreenshotError = (event) => {
112
+ console.error('Screenshot error:', event.detail.error);
113
+ // Store error message to display in feedback modal
114
+ this.screenshotError = event.detail.error;
195
115
  this.showScreenshotError = true;
196
- // Auto-hide error after 5 seconds
116
+ // Close canvas editor and return to feedback modal
117
+ this.showModal = true;
118
+ this.takingScreenshot = false;
119
+ this.showCanvasEditor = false;
120
+ this.autoStartCapture = false;
121
+ // Auto-hide error after 8 seconds
197
122
  setTimeout(() => {
198
123
  this.showScreenshotError = false;
199
- }, 5000);
124
+ }, 8000);
125
+ };
126
+ // Trigger screenshot capture
127
+ this.openScreenShot = () => {
128
+ this.showModal = false;
129
+ this.takingScreenshot = true;
130
+ this.autoStartCapture = true; // Auto-start new screenshot
131
+ this.showCanvasEditor = true;
200
132
  };
133
+ // Open canvas editor for existing screenshot
201
134
  this.openCanvasEditor = (event) => {
202
135
  if (event) {
203
136
  event.stopPropagation();
204
137
  }
205
138
  this.showModal = false;
139
+ this.autoStartCapture = false; // Don't auto-start, just edit existing
206
140
  this.showCanvasEditor = true;
207
- // Initialize canvas after a short delay to ensure DOM is ready
208
- setTimeout(() => {
209
- this.initializeCanvas();
210
- }, 100);
211
- };
212
- this.closeCanvasEditor = () => {
213
- this.showCanvasEditor = false;
214
- this.showModal = true;
215
- };
216
- this.saveAnnotations = () => {
217
- if (this.canvasRef) {
218
- // Create final image with annotations
219
- const finalDataUrl = this.canvasRef.toDataURL('image/png');
220
- this.encodedScreenshot = finalDataUrl;
221
- }
222
- this.showCanvasEditor = false;
223
- this.showModal = true;
224
- };
225
- this.initializeCanvas = () => {
226
- if (!this.canvasRef || !this.originalImageData)
227
- return;
228
- this.canvasContext = this.canvasRef.getContext('2d');
229
- const img = new Image();
230
- img.onload = () => {
231
- // Set canvas to original image dimensions
232
- this.canvasRef.width = img.width;
233
- this.canvasRef.height = img.height;
234
- // Get available container dimensions
235
- const containerWidth = this.canvasRef.parentElement.clientWidth - 32; // Account for reduced padding (16px * 2)
236
- const containerHeight = this.canvasRef.parentElement.clientHeight - 32;
237
- // Calculate scale factors for both dimensions
238
- const scaleX = containerWidth / img.width;
239
- const scaleY = containerHeight / img.height;
240
- // Use the smaller scale to ensure complete image fits
241
- const scale = Math.min(scaleX, scaleY, 1); // Never scale up, only down
242
- // Calculate final display dimensions
243
- const displayWidth = img.width * scale;
244
- const displayHeight = img.height * scale;
245
- // Set CSS size for display (this scales the canvas visually)
246
- this.canvasRef.style.width = `${displayWidth}px`;
247
- this.canvasRef.style.height = `${displayHeight}px`;
248
- console.log('Canvas initialized with complete image fit:', {
249
- originalWidth: img.width,
250
- originalHeight: img.height,
251
- displayWidth,
252
- displayHeight,
253
- scale,
254
- scaleX,
255
- scaleY,
256
- containerWidth,
257
- containerHeight,
258
- usingScale: scale === scaleX ? 'width-limited' : 'height-limited'
259
- });
260
- // Draw the original image at full resolution
261
- this.canvasContext.drawImage(img, 0, 0);
262
- // Redraw existing annotations
263
- this.redrawAnnotations();
264
- };
265
- img.src = this.originalImageData;
266
- };
267
- this.redrawAnnotations = () => {
268
- if (!this.canvasContext)
269
- return;
270
- // Clear and redraw background image
271
- const img = new Image();
272
- img.onload = () => {
273
- this.canvasContext.clearRect(0, 0, this.canvasRef.width, this.canvasRef.height);
274
- this.canvasContext.drawImage(img, 0, 0);
275
- // Draw all annotations
276
- this.annotations.forEach(annotation => {
277
- this.drawAnnotation(annotation);
278
- });
279
- };
280
- img.src = this.originalImageData;
281
- };
282
- this.drawAnnotation = (annotation) => {
283
- if (!this.canvasContext)
284
- return;
285
- this.canvasContext.strokeStyle = annotation.color;
286
- this.canvasContext.lineWidth = annotation.lineWidth;
287
- this.canvasContext.lineCap = 'round';
288
- this.canvasContext.lineJoin = 'round';
289
- switch (annotation.type) {
290
- case 'rectangle':
291
- this.canvasContext.strokeRect(annotation.startX, annotation.startY, annotation.width, annotation.height);
292
- // Draw resize handle if this annotation is hovered
293
- if (this.hoveredAnnotation === annotation) {
294
- this.drawRectangleResizeHandles(annotation);
295
- }
296
- break;
297
- case 'line':
298
- this.canvasContext.beginPath();
299
- this.canvasContext.moveTo(annotation.startX, annotation.startY);
300
- this.canvasContext.lineTo(annotation.endX, annotation.endY);
301
- this.canvasContext.stroke();
302
- // Draw resize handles if this annotation is hovered
303
- if (this.hoveredAnnotation === annotation) {
304
- this.drawLineResizeHandles(annotation);
305
- }
306
- break;
307
- case 'arrow':
308
- this.drawArrow(annotation.startX, annotation.startY, annotation.endX, annotation.endY);
309
- // Draw resize handles if this annotation is hovered
310
- if (this.hoveredAnnotation === annotation) {
311
- this.drawLineResizeHandles(annotation); // Same as line
312
- }
313
- break;
314
- case 'text':
315
- const fontSize = annotation.fontSize || 16;
316
- this.canvasContext.fillStyle = annotation.color;
317
- this.canvasContext.font = `${fontSize}px Arial`;
318
- this.canvasContext.fillText(annotation.text, annotation.x, annotation.y);
319
- // Draw resize handle if this annotation is hovered
320
- if (this.hoveredAnnotation === annotation) {
321
- this.drawTextResizeHandle(annotation);
322
- }
323
- break;
324
- }
325
- };
326
- // Draw resize handle for text annotation
327
- this.drawTextResizeHandle = (annotation) => {
328
- if (!this.canvasContext || annotation.type !== 'text')
329
- return;
330
- const fontSize = annotation.fontSize || 16;
331
- const textWidth = this.getTextWidth(annotation.text, fontSize);
332
- const handleSize = 8;
333
- const handleX = annotation.x + textWidth;
334
- const handleY = annotation.y;
335
- // Draw resize handle (small square) - using widget primary color
336
- this.canvasContext.fillStyle = '#0070F4'; // var(--feedback-primary-color)
337
- this.canvasContext.strokeStyle = '#ffffff';
338
- this.canvasContext.lineWidth = 2;
339
- this.canvasContext.fillRect(handleX - handleSize / 2, handleY - handleSize / 2, handleSize, handleSize);
340
- this.canvasContext.strokeRect(handleX - handleSize / 2, handleY - handleSize / 2, handleSize, handleSize);
341
- };
342
- this.drawArrow = (fromX, fromY, toX, toY) => {
343
- const headlen = 15; // Arrow head length
344
- const angle = Math.atan2(toY - fromY, toX - fromX);
345
- // Draw line
346
- this.canvasContext.beginPath();
347
- this.canvasContext.moveTo(fromX, fromY);
348
- this.canvasContext.lineTo(toX, toY);
349
- this.canvasContext.stroke();
350
- // Draw arrow head
351
- this.canvasContext.beginPath();
352
- this.canvasContext.moveTo(toX, toY);
353
- this.canvasContext.lineTo(toX - headlen * Math.cos(angle - Math.PI / 6), toY - headlen * Math.sin(angle - Math.PI / 6));
354
- this.canvasContext.moveTo(toX, toY);
355
- this.canvasContext.lineTo(toX - headlen * Math.cos(angle + Math.PI / 6), toY - headlen * Math.sin(angle + Math.PI / 6));
356
- this.canvasContext.stroke();
357
- };
358
- this.undoLastAnnotation = () => {
359
- this.annotations = this.annotations.slice(0, -1);
360
- this.redrawAnnotations();
361
- };
362
- // Handle color slot editing
363
- this.handleColorSlotClick = (colorIndex) => {
364
- if (this.editingColorIndex === colorIndex) {
365
- // If already editing this slot, just select the color
366
- this.canvasDrawingColor = this.defaultColors[colorIndex];
367
- this.showColorPicker = false;
368
- this.editingColorIndex = -1;
369
- }
370
- else {
371
- // Start editing this color slot
372
- this.editingColorIndex = colorIndex;
373
- this.showColorPicker = true;
374
- this.canvasDrawingColor = this.defaultColors[colorIndex];
375
- }
376
- };
377
- // Update color in slot
378
- this.updateColorSlot = (newColor) => {
379
- if (this.editingColorIndex >= 0 && this.editingColorIndex < this.defaultColors.length) {
380
- this.defaultColors[this.editingColorIndex] = newColor;
381
- this.canvasDrawingColor = newColor;
382
- this.showColorPicker = false;
383
- this.editingColorIndex = -1;
384
- // Force reactivity
385
- this.defaultColors = [...this.defaultColors];
386
- }
387
- };
388
- // Handle color picker input without closing
389
- this.handleColorPickerInput = (event) => {
390
- event.stopPropagation();
391
- const newColor = event.target.value;
392
- if (this.editingColorIndex >= 0 && this.editingColorIndex < this.defaultColors.length) {
393
- this.defaultColors[this.editingColorIndex] = newColor;
394
- this.canvasDrawingColor = newColor;
395
- // Force reactivity
396
- this.defaultColors = [...this.defaultColors];
397
- }
398
- };
399
- // Handle color picker click to prevent closing
400
- this.handleColorPickerClick = (event) => {
401
- event.stopPropagation();
402
- };
403
- // Close color picker
404
- this.closeColorPicker = () => {
405
- this.showColorPicker = false;
406
- this.editingColorIndex = -1;
407
- };
408
- // Check if point is in resize handle for any annotation type
409
- this.isPointInResizeHandle = (x, y, annotation) => {
410
- const handleSize = 8;
411
- switch (annotation.type) {
412
- case 'text':
413
- const textWidth = this.getTextWidth(annotation.text, annotation.fontSize || 16);
414
- const handleX = annotation.x + textWidth;
415
- const handleY = annotation.y;
416
- return x >= handleX - handleSize / 2 && x <= handleX + handleSize / 2 &&
417
- y >= handleY - handleSize / 2 && y <= handleY + handleSize / 2;
418
- case 'rectangle':
419
- const right = annotation.startX + annotation.width;
420
- const bottom = annotation.startY + annotation.height;
421
- // Only check bottom-right corner handle
422
- return x >= right - handleSize / 2 && x <= right + handleSize / 2 &&
423
- y >= bottom - handleSize / 2 && y <= bottom + handleSize / 2;
424
- case 'line':
425
- case 'arrow':
426
- // Check both endpoint handles
427
- const lineHandles = [
428
- { x: annotation.startX, y: annotation.startY, point: 'start' },
429
- { x: annotation.endX, y: annotation.endY, point: 'end' }
430
- ];
431
- for (const handle of lineHandles) {
432
- if (x >= handle.x - handleSize / 2 && x <= handle.x + handleSize / 2 &&
433
- y >= handle.y - handleSize / 2 && y <= handle.y + handleSize / 2) {
434
- return handle.point; // Return which endpoint was clicked
435
- }
436
- }
437
- return false;
438
- default:
439
- return false;
440
- }
441
- };
442
- // Get text width for resize handle positioning
443
- this.getTextWidth = (text, fontSize) => {
444
- // Approximate text width calculation
445
- return text.length * fontSize * 0.6;
446
- };
447
- // Start text resize
448
- this.startTextResize = (annotation, startPos) => {
449
- this.isResizing = true;
450
- this.resizingAnnotation = annotation;
451
- this.resizeStartSize = annotation.fontSize || 16;
452
- this.dragStartPos = startPos;
453
- };
454
- // Handle text resize
455
- this.handleTextResize = (currentPos) => {
456
- if (!this.resizingAnnotation || !this.dragStartPos)
457
- return;
458
- const deltaX = currentPos.x - this.dragStartPos.x;
459
- const deltaY = currentPos.y - this.dragStartPos.y;
460
- const avgDelta = (deltaX + deltaY) / 2;
461
- // Calculate new font size (minimum 8px, maximum 72px)
462
- const newSize = Math.max(8, Math.min(72, this.resizeStartSize + avgDelta * 0.5));
463
- // Update annotation font size
464
- const index = this.annotations.findIndex(a => a === this.resizingAnnotation);
465
- if (index !== -1) {
466
- this.annotations[index] = Object.assign(Object.assign({}, this.resizingAnnotation), { fontSize: Math.round(newSize) });
467
- this.resizingAnnotation = this.annotations[index];
468
- }
469
- this.redrawAnnotations();
470
- };
471
- // Start resize for any annotation type
472
- this.startResize = (annotation, handle, startPos) => {
473
- this.isResizing = true;
474
- this.resizingAnnotation = annotation;
475
- this.resizeHandle = handle;
476
- this.dragStartPos = startPos;
477
- // Store original values for different annotation types
478
- if (annotation.type === 'text') {
479
- this.resizeStartSize = annotation.fontSize || 16;
480
- }
481
- else if (annotation.type === 'rectangle') {
482
- this.resizeStartDimensions = { width: annotation.width, height: annotation.height };
483
- }
484
- };
485
- // Enhanced mouse down handler with resize detection for all annotation types
486
- this.handleCanvasMouseDown = (event) => {
487
- if (!this.canvasRef)
488
- return;
489
- // Disable drawing on mobile devices
490
- if (window.innerWidth <= 768)
491
- return;
492
- // Close color picker if open
493
- if (this.showColorPicker) {
494
- this.closeColorPicker();
495
- }
496
- const coords = this.getCanvasCoordinates(event);
497
- // Check if clicking on existing annotation first
498
- const found = this.findAnnotationAt(coords.x, coords.y);
499
- if (found) {
500
- // Check if clicking on resize handle for any annotation type
501
- const handle = this.isPointInResizeHandle(coords.x, coords.y, found.annotation);
502
- if (handle) {
503
- this.startResize(found.annotation, handle, coords);
504
- this.canvasRef.style.cursor = 'nw-resize';
505
- return;
506
- }
507
- // Start dragging existing annotation
508
- if (!this.isDrawing) {
509
- this.isDragging = true;
510
- this.draggedAnnotation = found.annotation;
511
- this.dragStartPos = coords;
512
- this.canvasRef.style.cursor = 'grabbing';
513
- return;
514
- }
515
- }
516
- // Original drawing logic
517
- this.isDrawing = true;
518
- if (this.canvasDrawingTool === 'text') {
519
- const text = prompt('Enter text:');
520
- if (text) {
521
- const annotation = {
522
- type: 'text',
523
- x: coords.x,
524
- y: coords.y,
525
- text,
526
- color: this.canvasDrawingColor,
527
- fontSize: 16
528
- };
529
- this.annotations = [...this.annotations, annotation];
530
- this.redrawAnnotations();
531
- }
532
- this.isDrawing = false;
533
- }
534
- else {
535
- this.currentAnnotation = {
536
- type: this.canvasDrawingTool,
537
- startX: coords.x,
538
- startY: coords.y,
539
- color: this.canvasDrawingColor,
540
- lineWidth: this.canvasLineWidth
541
- };
542
- }
543
- };
544
- this.handleCanvasMouseMove = (event) => {
545
- if (!this.canvasRef)
546
- return;
547
- // Disable drawing on mobile devices
548
- if (window.innerWidth <= 768)
549
- return;
550
- const coords = this.getCanvasCoordinates(event);
551
- // Handle resizing for any annotation type
552
- if (this.isResizing && this.resizingAnnotation) {
553
- this.handleResize(coords);
554
- return;
555
- }
556
- // Handle dragging existing annotation
557
- if (this.isDragging && this.draggedAnnotation && this.dragStartPos) {
558
- const deltaX = coords.x - this.dragStartPos.x;
559
- const deltaY = coords.y - this.dragStartPos.y;
560
- // Update annotation position
561
- const updatedAnnotation = Object.assign({}, this.draggedAnnotation);
562
- switch (updatedAnnotation.type) {
563
- case 'rectangle':
564
- updatedAnnotation.startX += deltaX;
565
- updatedAnnotation.startY += deltaY;
566
- break;
567
- case 'line':
568
- case 'arrow':
569
- updatedAnnotation.startX += deltaX;
570
- updatedAnnotation.startY += deltaY;
571
- updatedAnnotation.endX += deltaX;
572
- updatedAnnotation.endY += deltaY;
573
- break;
574
- case 'text':
575
- updatedAnnotation.x += deltaX;
576
- updatedAnnotation.y += deltaY;
577
- break;
578
- }
579
- // Update annotation in array
580
- const index = this.annotations.findIndex(a => a === this.draggedAnnotation);
581
- if (index !== -1) {
582
- this.annotations[index] = updatedAnnotation;
583
- this.draggedAnnotation = updatedAnnotation;
584
- }
585
- this.dragStartPos = coords;
586
- this.redrawAnnotations();
587
- return;
588
- }
589
- // Handle drawing new annotation
590
- if (this.isDrawing && this.currentAnnotation) {
591
- if (this.canvasDrawingTool === 'rectangle') {
592
- this.currentAnnotation.width = coords.x - this.currentAnnotation.startX;
593
- this.currentAnnotation.height = coords.y - this.currentAnnotation.startY;
594
- }
595
- else {
596
- this.currentAnnotation.endX = coords.x;
597
- this.currentAnnotation.endY = coords.y;
598
- }
599
- this.redrawAnnotations();
600
- this.drawAnnotation(this.currentAnnotation);
601
- return;
602
- }
603
- // Handle hover states and cursor changes
604
- const found = this.findAnnotationAt(coords.x, coords.y);
605
- if (found) {
606
- // Check if hovering over resize handle for any annotation type
607
- const handle = this.isPointInResizeHandle(coords.x, coords.y, found.annotation);
608
- if (handle) {
609
- this.canvasRef.style.cursor = 'nw-resize';
610
- this.hoveredAnnotation = found.annotation;
611
- this.redrawAnnotations();
612
- return;
613
- }
614
- // Regular hover over annotation
615
- this.canvasRef.style.cursor = 'grab';
616
- if (this.hoveredAnnotation !== found.annotation) {
617
- this.hoveredAnnotation = found.annotation;
618
- this.redrawAnnotations();
619
- }
620
- }
621
- else {
622
- // No annotation under cursor
623
- this.canvasRef.style.cursor = 'crosshair';
624
- if (this.hoveredAnnotation) {
625
- this.hoveredAnnotation = null;
626
- this.redrawAnnotations();
627
- }
628
- }
629
- };
630
- this.handleCanvasMouseUp = () => {
631
- // Disable drawing on mobile devices
632
- if (window.innerWidth <= 768)
633
- return;
634
- // Handle end of resizing
635
- if (this.isResizing) {
636
- this.isResizing = false;
637
- this.resizingAnnotation = null;
638
- this.dragStartPos = null;
639
- this.resizeHandle = false;
640
- this.resizeStartDimensions = null;
641
- if (this.canvasRef) {
642
- this.canvasRef.style.cursor = 'crosshair';
643
- }
644
- return;
645
- }
646
- // Handle end of dragging
647
- if (this.isDragging) {
648
- this.isDragging = false;
649
- this.draggedAnnotation = null;
650
- this.dragStartPos = null;
651
- if (this.canvasRef) {
652
- this.canvasRef.style.cursor = 'crosshair';
653
- }
654
- return;
655
- }
656
- // Handle end of drawing
657
- if (!this.isDrawing || !this.currentAnnotation)
658
- return;
659
- this.isDrawing = false;
660
- this.annotations = [...this.annotations, this.currentAnnotation];
661
- this.currentAnnotation = null;
662
- this.redrawAnnotations();
663
- };
664
- // Draw resize handles for rectangle annotation (only bottom-right corner)
665
- this.drawRectangleResizeHandles = (annotation) => {
666
- if (!this.canvasContext || annotation.type !== 'rectangle')
667
- return;
668
- const handleSize = 8;
669
- const right = annotation.startX + annotation.width;
670
- const bottom = annotation.startY + annotation.height;
671
- // Only draw bottom-right corner handle
672
- const handle = { x: right, y: bottom };
673
- // Draw the handle
674
- this.canvasContext.fillStyle = '#0070F4'; // Primary color
675
- this.canvasContext.strokeStyle = '#ffffff';
676
- this.canvasContext.lineWidth = 2;
677
- this.canvasContext.fillRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
678
- this.canvasContext.strokeRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
679
- };
680
- // Draw resize handles for line/arrow annotation
681
- this.drawLineResizeHandles = (annotation) => {
682
- if (!this.canvasContext || (annotation.type !== 'line' && annotation.type !== 'arrow'))
683
- return;
684
- const handleSize = 8;
685
- // Define handle positions (2 endpoints)
686
- const handles = [
687
- { x: annotation.startX, y: annotation.startY },
688
- { x: annotation.endX, y: annotation.endY } // End point
689
- ];
690
- // Draw each handle
691
- this.canvasContext.fillStyle = '#0070F4'; // Primary color
692
- this.canvasContext.strokeStyle = '#ffffff';
693
- this.canvasContext.lineWidth = 2;
694
- handles.forEach(handle => {
695
- this.canvasContext.fillRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
696
- this.canvasContext.strokeRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
697
- });
698
- };
699
- // Convert screen coordinates to canvas coordinates
700
- this.getCanvasCoordinates = (event) => {
701
- if (!this.canvasRef)
702
- return { x: 0, y: 0 };
703
- const rect = this.canvasRef.getBoundingClientRect();
704
- // Calculate the scale factor between display size and actual canvas size
705
- const scaleX = this.canvasRef.width / rect.width;
706
- const scaleY = this.canvasRef.height / rect.height;
707
- const x = (event.clientX - rect.left) * scaleX;
708
- const y = (event.clientY - rect.top) * scaleY;
709
- return { x, y };
710
- };
711
- // Find annotation under mouse cursor
712
- this.findAnnotationAt = (x, y) => {
713
- // Check in reverse order (top to bottom)
714
- for (let i = this.annotations.length - 1; i >= 0; i--) {
715
- const annotation = this.annotations[i];
716
- if (this.isPointInAnnotation(x, y, annotation)) {
717
- return { annotation, index: i };
718
- }
719
- }
720
- return null;
721
- };
722
- // Check if point is within annotation bounds
723
- this.isPointInAnnotation = (x, y, annotation) => {
724
- const tolerance = 10; // Click tolerance
725
- switch (annotation.type) {
726
- case 'rectangle':
727
- const left = Math.min(annotation.startX, annotation.startX + annotation.width);
728
- const right = Math.max(annotation.startX, annotation.startX + annotation.width);
729
- const top = Math.min(annotation.startY, annotation.startY + annotation.height);
730
- const bottom = Math.max(annotation.startY, annotation.startY + annotation.height);
731
- return x >= left - tolerance && x <= right + tolerance &&
732
- y >= top - tolerance && y <= bottom + tolerance;
733
- case 'line':
734
- case 'arrow':
735
- // Distance from point to line
736
- const A = annotation.endY - annotation.startY;
737
- const B = annotation.startX - annotation.endX;
738
- const C = annotation.endX * annotation.startY - annotation.startX * annotation.endY;
739
- const distance = Math.abs(A * x + B * y + C) / Math.sqrt(A * A + B * B);
740
- return distance <= tolerance;
741
- case 'text':
742
- // Simple bounding box for text
743
- return x >= annotation.x - tolerance && x <= annotation.x + 100 &&
744
- y >= annotation.y - 20 && y <= annotation.y + tolerance;
745
- default:
746
- return false;
747
- }
748
- };
749
- // Handle resize for different annotation types
750
- this.handleResize = (currentPos) => {
751
- if (!this.resizingAnnotation || !this.dragStartPos)
752
- return;
753
- const annotation = this.resizingAnnotation;
754
- const index = this.annotations.findIndex(a => a === annotation);
755
- if (index === -1)
756
- return;
757
- let updatedAnnotation = Object.assign({}, annotation);
758
- switch (annotation.type) {
759
- case 'text':
760
- // Text resize logic (existing)
761
- const deltaX = currentPos.x - this.dragStartPos.x;
762
- const deltaY = currentPos.y - this.dragStartPos.y;
763
- const avgDelta = (deltaX + deltaY) / 2;
764
- const newSize = Math.max(8, Math.min(72, this.resizeStartSize + avgDelta * 0.5));
765
- updatedAnnotation.fontSize = Math.round(newSize);
766
- break;
767
- case 'rectangle':
768
- // Rectangle resize logic - only bottom-right corner
769
- const rectDeltaX = currentPos.x - this.dragStartPos.x;
770
- const rectDeltaY = currentPos.y - this.dragStartPos.y;
771
- // Update width and height based on original dimensions plus delta
772
- updatedAnnotation.width = Math.max(10, this.resizeStartDimensions.width + rectDeltaX); // Minimum width of 10px
773
- updatedAnnotation.height = Math.max(10, this.resizeStartDimensions.height + rectDeltaY); // Minimum height of 10px
774
- break;
775
- case 'line':
776
- case 'arrow':
777
- // Line/arrow resize logic - move endpoints
778
- if (this.resizeHandle === 'start') {
779
- updatedAnnotation.startX = currentPos.x;
780
- updatedAnnotation.startY = currentPos.y;
781
- }
782
- else if (this.resizeHandle === 'end') {
783
- updatedAnnotation.endX = currentPos.x;
784
- updatedAnnotation.endY = currentPos.y;
785
- }
786
- break;
787
- }
788
- // Update annotation in array
789
- this.annotations[index] = updatedAnnotation;
790
- this.resizingAnnotation = updatedAnnotation;
791
- this.redrawAnnotations();
792
141
  };
793
142
  this.sending = false;
794
143
  this.formMessage = '';
@@ -804,28 +153,10 @@ export class FeedbackModal {
804
153
  this.overlayVisible = false;
805
154
  this.isAnimating = false;
806
155
  this.takingScreenshot = false;
807
- this.showPreviewModal = false;
808
- this.screenshotError = '';
809
156
  this.showScreenshotError = false;
157
+ this.screenshotError = '';
810
158
  this.showCanvasEditor = false;
811
- this.canvasDrawingTool = 'rectangle';
812
- this.canvasDrawingColor = '#ff0000';
813
- this.canvasLineWidth = 3;
814
- this.isDrawing = false;
815
- this.annotations = [];
816
- this.currentAnnotation = null;
817
- this.isDragging = false;
818
- this.draggedAnnotation = null;
819
- this.dragStartPos = null;
820
- this.showColorPicker = false;
821
- this.editingColorIndex = -1;
822
- this.isResizing = false;
823
- this.resizingAnnotation = null;
824
- this.resizeStartSize = 16;
825
- this.resizeStartDimensions = null;
826
- this.hoveredAnnotation = null;
827
- this.resizeHandle = false;
828
- this.defaultColors = ['#ff0000', '#00ff00', '#0000ff', '#000000'];
159
+ this.autoStartCapture = false;
829
160
  this.customFont = false;
830
161
  this.emailAddress = '';
831
162
  this.hideEmail = false;
@@ -864,6 +195,17 @@ export class FeedbackModal {
864
195
  this.canvasEditorTitle = 'Edit screenshot';
865
196
  this.canvasEditorCancelText = 'Cancel';
866
197
  this.canvasEditorSaveText = 'Save';
198
+ this.editTextButtonText = 'Edit Text';
199
+ this.sizeLabelText = 'Size:';
200
+ this.borderLabelText = 'Border:';
201
+ this.editTextPromptText = 'Edit text:';
202
+ this.screenshotErrorGeneral = 'Failed to capture screenshot.';
203
+ this.screenshotErrorPermission = 'Permission denied. Please allow screen sharing to take screenshots.';
204
+ this.screenshotErrorNotSupported = 'Screen capture is not supported in this browser.';
205
+ this.screenshotErrorNotFound = 'No screen sources available for capture.';
206
+ this.screenshotErrorCancelled = 'Screenshot capture was cancelled.';
207
+ this.screenshotErrorBrowserNotSupported = 'Your browser does not support screen capture. Please use a browser like Chrome, Firefox, or Safari.';
208
+ this.screenshotErrorUnexpected = 'An unexpected error occurred. Please try again.';
867
209
  }
868
210
  componentWillLoad() {
869
211
  if (this.fetchData)
@@ -898,63 +240,6 @@ export class FeedbackModal {
898
240
  handleEmailInput(event) {
899
241
  this.formEmail = event.target.value;
900
242
  }
901
- async captureViewportScreenshot() {
902
- try {
903
- // Check if Screen Capture API is supported
904
- if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) {
905
- throw new Error('Screen Capture API is not supported in this browser');
906
- }
907
- // Request screen capture with preference for current tab
908
- const stream = await navigator.mediaDevices.getDisplayMedia({
909
- video: {
910
- mediaSource: 'screen',
911
- width: { ideal: window.innerWidth },
912
- height: { ideal: window.innerHeight }
913
- },
914
- audio: false,
915
- preferCurrentTab: true
916
- });
917
- // Create video element to capture frame
918
- const video = document.createElement('video');
919
- video.srcObject = stream;
920
- video.autoplay = true;
921
- video.muted = true;
922
- return new Promise((resolve, reject) => {
923
- video.onloadedmetadata = () => {
924
- video.play();
925
- // Wait a moment for video to stabilize
926
- setTimeout(() => {
927
- try {
928
- // Create canvas to capture frame
929
- const canvas = document.createElement('canvas');
930
- canvas.width = video.videoWidth;
931
- canvas.height = video.videoHeight;
932
- const ctx = canvas.getContext('2d');
933
- ctx.drawImage(video, 0, 0);
934
- // Stop the stream
935
- stream.getTracks().forEach(track => track.stop());
936
- // Convert to data URL
937
- const dataUrl = canvas.toDataURL('image/png');
938
- console.log('Screenshot captured successfully using Screen Capture API');
939
- resolve(dataUrl);
940
- }
941
- catch (error) {
942
- stream.getTracks().forEach(track => track.stop());
943
- reject(error);
944
- }
945
- }, 100);
946
- };
947
- video.onerror = (_) => {
948
- stream.getTracks().forEach(track => track.stop());
949
- reject(new Error('Failed to load video for screenshot capture'));
950
- };
951
- });
952
- }
953
- catch (error) {
954
- console.error('Screen capture failed:', error);
955
- throw error;
956
- }
957
- }
958
243
  handleCheckboxChange(event) {
959
244
  this.isPrivacyChecked = event.target.checked;
960
245
  }
@@ -965,7 +250,7 @@ export class FeedbackModal {
965
250
  this.selectedRating = newRating;
966
251
  }
967
252
  render() {
968
- return (h("div", { class: `feedback-modal-wrapper ${this.customFont ? 'feedback-modal-wrapper--custom-font' : ''}` }, this.showScreenshotError && (h("div", { class: "screenshot-error-notification" }, h("div", { class: "screenshot-error-content" }, h("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("circle", { cx: "12", cy: "12", r: "10" }), h("line", { x1: "15", y1: "9", x2: "9", y2: "15" }), h("line", { x1: "9", y1: "9", x2: "15", y2: "15" })), h("span", null, this.screenshotError), h("button", { class: "error-close-btn", onClick: () => this.showScreenshotError = false, title: "Close" }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), h("line", { x1: "6", y1: "6", x2: "18", y2: "18" })))))), this.showModal && (h("div", { class: `feedback-overlay ${this.isAnimating ? 'feedback-overlay--visible' : ''}` })), this.showModal && (h("div", { class: `feedback-modal-content feedback-modal-content--${this.modalPosition} ${this.isAnimating ? 'feedback-modal-content--open' : ''}`, ref: (el) => (this.modalContent = el) }, h("div", { class: "feedback-modal-header" }, !this.formSuccess && !this.formError ? (h("span", null, this.modalTitle)) : this.formSuccess ? (h("span", null, this.modalTitleSuccess)) : (h("span", null, this.modalTitleError)), h("button", { class: "feedback-modal-close", onClick: this.close }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "#191919", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "feather feather-x" }, h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), h("line", { x1: "6", y1: "6", x2: "18", y2: "18" })))), h("div", { class: "feedback-modal-body" }, !this.formSuccess && !this.formError ? (h("form", { onSubmit: this.handleSubmit }, !this.hideRating && (h("div", { class: "feedback-modal-rating" }, this.ratingMode === 'thumbs' ? (h("div", { class: "feedback-modal-rating-content" }, h("span", { class: "feedback-modal-input-heading" }, this.ratingPlaceholder), h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--thumbs" }, h("button", { title: "Yes", class: `feedback-modal-rating-button ${this.selectedRating === 1
253
+ return (h("div", { class: `feedback-modal-wrapper ${this.customFont ? 'feedback-modal-wrapper--custom-font' : ''}` }, this.showCanvasEditor && (h("canvas-editor", { ref: (el) => this.canvasEditorRef = el, "canvas-editor-title": this.canvasEditorTitle, "canvas-editor-cancel-text": this.canvasEditorCancelText, "canvas-editor-save-text": this.canvasEditorSaveText, "screenshot-taking-text": this.screenshotTakingText, "screenshot-attached-text": this.screenshotAttachedText, "screenshot-button-text": this.screenshotButtonText, "auto-start-screenshot": this.autoStartCapture, "existing-screenshot": this.encodedScreenshot || '', "edit-text-button-text": this.editTextButtonText, "size-label-text": this.sizeLabelText, "border-label-text": this.borderLabelText, "edit-text-prompt-text": this.editTextPromptText, "screenshot-error-general": this.screenshotErrorGeneral, "screenshot-error-permission": this.screenshotErrorPermission, "screenshot-error-not-supported": this.screenshotErrorNotSupported, "screenshot-error-not-found": this.screenshotErrorNotFound, "screenshot-error-cancelled": this.screenshotErrorCancelled, "screenshot-error-browser-not-supported": this.screenshotErrorBrowserNotSupported, "screenshot-error-unexpected": this.screenshotErrorUnexpected, onScreenshotReady: this.handleScreenshotReady, onScreenshotCancelled: this.handleScreenshotCancelled, onScreenshotFailed: this.handleScreenshotError })), this.showScreenshotError && (h("div", { class: "screenshot-error-notification" }, h("div", { class: "screenshot-error-content" }, h("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("circle", { cx: "12", cy: "12", r: "10" }), h("line", { x1: "15", y1: "9", x2: "9", y2: "15" }), h("line", { x1: "9", y1: "9", x2: "15", y2: "15" })), h("span", null, this.screenshotError), h("button", { class: "error-close-btn", onClick: () => this.showScreenshotError = false, title: "Close" }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), h("line", { x1: "6", y1: "6", x2: "18", y2: "18" })))))), this.showModal && (h("div", { class: `feedback-overlay ${this.isAnimating ? 'feedback-overlay--visible' : ''}` })), this.showModal && (h("div", { class: `feedback-modal-content feedback-modal-content--${this.modalPosition} ${this.isAnimating ? 'feedback-modal-content--open' : ''}`, ref: (el) => (this.modalContent = el) }, h("div", { class: "feedback-modal-header" }, !this.formSuccess && !this.formError ? (h("span", null, this.modalTitle)) : this.formSuccess ? (h("span", null, this.modalTitleSuccess)) : (h("span", null, this.modalTitleError)), h("button", { class: "feedback-modal-close", onClick: this.close }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "#191919", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "feather feather-x" }, h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), h("line", { x1: "6", y1: "6", x2: "18", y2: "18" })))), h("div", { class: "feedback-modal-body" }, !this.formSuccess && !this.formError ? (h("form", { onSubmit: this.handleSubmit }, !this.hideRating && (h("div", { class: "feedback-modal-rating" }, this.ratingMode === 'thumbs' ? (h("div", { class: "feedback-modal-rating-content" }, h("span", { class: "feedback-modal-input-heading" }, this.ratingPlaceholder), h("div", { class: "feedback-modal-rating-buttons feedback-modal-rating-buttons--thumbs" }, h("button", { title: "Yes", class: `feedback-modal-rating-button ${this.selectedRating === 1
969
254
  ? 'feedback-modal-rating-button--selected'
970
255
  : ''}`, onClick: (event) => {
971
256
  event.preventDefault();
@@ -981,7 +266,7 @@ export class FeedbackModal {
981
266
  event.preventDefault();
982
267
  this.handleRatingChange(rating);
983
268
  } }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "#5F6368", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("polygon", { points: "12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" })))))))))), h("div", { class: "feedback-modal-text" }, h("textarea", { placeholder: this.messagePlaceholder, value: this.formMessage, onInput: (event) => this.handleMessageInput(event) })), !this.hideEmail && (h("div", { class: "feedback-modal-email" }, h("input", { placeholder: this.emailPlaceholder, type: "email", onInput: (event) => this.handleEmailInput(event), value: this.formEmail, required: this.isEmailRequired }))), h("div", { class: "feedback-verification" }, h("input", { type: "text", name: "verification", style: { display: 'none' }, onInput: (event) => this.handleVerification(event), value: this.formVerification })), !this.hidePrivacyPolicy && (h("div", { class: "feedback-modal-privacy" }, h("input", { type: "checkbox", id: "privacyPolicy", onChange: (ev) => this.handleCheckboxChange(ev), required: true }), h("span", { innerHTML: this.privacyPolicyText }))), h("div", { class: `feedback-modal-buttons ${this.hideScreenshotButton ? 'single' : ''}` }, !this.hideScreenshotButton && (h("button", { type: "button", class: `feedback-modal-button feedback-modal-button--screenshot ${this.encodedScreenshot ? 'feedback-modal-button--active' : ''}`, onClick: this.openScreenShot, disabled: this.sending || this.takingScreenshot }, this.encodedScreenshot && (h("div", { class: "screenshot-preview", onClick: this.openCanvasEditor }, h("img", { src: this.encodedScreenshot, alt: "Screenshot Preview" }))), !this.encodedScreenshot && !this.takingScreenshot && (h("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24", viewBox: "0 -960 960 960", width: "24" }, h("path", { d: "M680-80v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM200-200v-200h80v120h120v80H200Zm0-360v-200h200v80H280v120h-80Zm480 0v-120H560v-80h200v200h-80Z" }))), this.takingScreenshot && (h("div", { class: "screenshot-loading" }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#666", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "feather-loader" }, h("line", { x1: "12", y1: "2", x2: "12", y2: "6" }), h("line", { x1: "12", y1: "18", x2: "12", y2: "22" }), h("line", { x1: "4.93", y1: "4.93", x2: "7.76", y2: "7.76" }), h("line", { x1: "16.24", y1: "16.24", x2: "19.07", y2: "19.07" }), h("line", { x1: "2", y1: "12", x2: "6", y2: "12" }), h("line", { x1: "18", y1: "12", x2: "22", y2: "12" }), h("line", { x1: "4.93", y1: "19.07", x2: "7.76", y2: "16.24" }), h("line", { x1: "16.24", y1: "7.76", x2: "19.07", y2: "4.93" })))), this.takingScreenshot ? this.screenshotTakingText :
984
- this.encodedScreenshot ? this.screenshotAttachedText : this.screenshotButtonText)), h("button", { class: "feedback-modal-button feedback-modal-button--submit", type: "submit", disabled: this.sending }, this.sendButtonText)))) : this.formSuccess && !this.formError ? (h("div", { class: "feedback-modal-success" }, h("p", { class: "feedback-modal-message" }, this.successMessage))) : this.formError && this.formErrorStatus == 404 ? (h("p", { class: "feedback-modal-message" }, this.errorMessage404)) : this.formError && this.formErrorStatus == 403 ? (h("p", { class: "feedback-modal-message" }, this.errorMessage403)) : this.formError ? (h("p", { class: "feedback-modal-message" }, this.errorMessage)) : (h("span", null))), h("div", { class: "feedback-modal-footer" }, h("div", { class: "feedback-logo", style: { display: this.whitelabel ? 'none' : 'block' } }, "Powered by", ' ', h("a", { target: "_blank", href: "https://pushfeedback.com" }, "PushFeedback.com")), this.footerText && (h("div", { class: "feedback-footer-text" }, h("span", { innerHTML: this.footerText })))))), this.showCanvasEditor && (h("div", { class: "canvas-editor-overlay" }, h("div", { class: "canvas-editor-modal" }, h("div", { class: "canvas-editor-header" }, h("div", { class: "canvas-editor-title" }, h("h3", null, this.canvasEditorTitle)), h("div", { class: "canvas-editor-toolbar" }, h("div", { class: "toolbar-section" }, h("div", { class: "tool-group" }, h("button", { class: `tool-btn ${this.canvasDrawingTool === 'rectangle' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'rectangle', title: "Rectangle" }, h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'line' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'line', title: "Line" }, h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("line", { x1: "5", y1: "12", x2: "19", y2: "12" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'arrow' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'arrow', title: "Arrow" }, h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("line", { x1: "7", y1: "17", x2: "17", y2: "7" }), h("polyline", { points: "7,7 17,7 17,17" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'text' ? 'active' : ''}`, onClick: () => this.canvasDrawingTool = 'text', title: "Text" }, h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("polyline", { points: "4,7 4,4 20,4 20,7" }), h("line", { x1: "9", y1: "20", x2: "15", y2: "20" }), h("line", { x1: "12", y1: "4", x2: "12", y2: "20" }))), h("div", { class: "toolbar-divider" }), h("button", { class: "tool-btn undo-btn", onClick: this.undoLastAnnotation, disabled: this.annotations.length === 0, title: "Undo" }, h("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("polyline", { points: "1,4 1,10 7,10" }), h("path", { d: "M3.51,15a9,9,0,0,0,14.85-3.36,9,9,0,0,0-9.19-10.15L1.83,10" }))))), h("div", { class: "toolbar-section" }, h("div", { class: "color-palette" }, this.defaultColors.map((color, index) => (h("div", { class: "color-slot-wrapper" }, h("button", { class: `color-btn ${this.canvasDrawingColor === color ? 'active' : ''} ${this.editingColorIndex === index ? 'editing' : ''}`, style: { backgroundColor: color }, onClick: () => this.handleColorSlotClick(index), title: `Color ${index + 1} - Click to customize` }, this.editingColorIndex === index && (h("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "white", "stroke-width": "2" }, h("path", { d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" })))), this.editingColorIndex === index && this.showColorPicker && (h("div", { class: "color-picker-dropdown" }, h("input", { type: "color", value: color, onInput: (e) => this.handleColorPickerInput(e), onClick: (e) => this.handleColorPickerClick(e) })))))))), h("div", { class: "toolbar-section" }, h("div", { class: "size-control" }, h("input", { type: "range", min: "1", max: "10", value: this.canvasLineWidth, onInput: (e) => this.canvasLineWidth = parseInt(e.target.value), class: "size-slider" }), h("span", { class: "size-value" }, this.canvasLineWidth, "px"))), h("div", { class: "toolbar-section" }, h("button", { class: "action-btn secondary", onClick: this.closeCanvasEditor }, this.canvasEditorCancelText), h("button", { class: "action-btn primary", onClick: this.saveAnnotations }, this.canvasEditorSaveText))), h("div", { class: "canvas-editor-content" }, h("canvas", { ref: (el) => this.canvasRef = el, class: "annotation-canvas", onMouseDown: this.handleCanvasMouseDown, onMouseMove: this.handleCanvasMouseMove, onMouseUp: this.handleCanvasMouseUp, onMouseLeave: this.handleCanvasMouseUp }))))))));
269
+ this.encodedScreenshot ? this.screenshotAttachedText : this.screenshotButtonText)), h("button", { class: "feedback-modal-button feedback-modal-button--submit", type: "submit", disabled: this.sending }, this.sendButtonText)))) : this.formSuccess && !this.formError ? (h("div", { class: "feedback-modal-success" }, h("p", { class: "feedback-modal-message" }, this.successMessage))) : this.formError && this.formErrorStatus == 404 ? (h("p", { class: "feedback-modal-message" }, this.errorMessage404)) : this.formError && this.formErrorStatus == 403 ? (h("p", { class: "feedback-modal-message" }, this.errorMessage403)) : this.formError ? (h("p", { class: "feedback-modal-message" }, this.errorMessage)) : (h("span", null))), h("div", { class: "feedback-modal-footer" }, h("div", { class: "feedback-logo", style: { display: this.whitelabel ? 'none' : 'block' } }, "Powered by", ' ', h("a", { target: "_blank", href: "https://pushfeedback.com" }, "PushFeedback.com")), this.footerText && (h("div", { class: "feedback-footer-text" }, h("span", { innerHTML: this.footerText }))))))));
985
270
  }
986
271
  componentDidRender() {
987
272
  if (this.showModal) {
@@ -1693,6 +978,204 @@ export class FeedbackModal {
1693
978
  "attribute": "canvas-editor-save-text",
1694
979
  "reflect": false,
1695
980
  "defaultValue": "'Save'"
981
+ },
982
+ "editTextButtonText": {
983
+ "type": "string",
984
+ "mutable": false,
985
+ "complexType": {
986
+ "original": "string",
987
+ "resolved": "string",
988
+ "references": {}
989
+ },
990
+ "required": false,
991
+ "optional": false,
992
+ "docs": {
993
+ "tags": [],
994
+ "text": ""
995
+ },
996
+ "attribute": "edit-text-button-text",
997
+ "reflect": false,
998
+ "defaultValue": "'Edit Text'"
999
+ },
1000
+ "sizeLabelText": {
1001
+ "type": "string",
1002
+ "mutable": false,
1003
+ "complexType": {
1004
+ "original": "string",
1005
+ "resolved": "string",
1006
+ "references": {}
1007
+ },
1008
+ "required": false,
1009
+ "optional": false,
1010
+ "docs": {
1011
+ "tags": [],
1012
+ "text": ""
1013
+ },
1014
+ "attribute": "size-label-text",
1015
+ "reflect": false,
1016
+ "defaultValue": "'Size:'"
1017
+ },
1018
+ "borderLabelText": {
1019
+ "type": "string",
1020
+ "mutable": false,
1021
+ "complexType": {
1022
+ "original": "string",
1023
+ "resolved": "string",
1024
+ "references": {}
1025
+ },
1026
+ "required": false,
1027
+ "optional": false,
1028
+ "docs": {
1029
+ "tags": [],
1030
+ "text": ""
1031
+ },
1032
+ "attribute": "border-label-text",
1033
+ "reflect": false,
1034
+ "defaultValue": "'Border:'"
1035
+ },
1036
+ "editTextPromptText": {
1037
+ "type": "string",
1038
+ "mutable": false,
1039
+ "complexType": {
1040
+ "original": "string",
1041
+ "resolved": "string",
1042
+ "references": {}
1043
+ },
1044
+ "required": false,
1045
+ "optional": false,
1046
+ "docs": {
1047
+ "tags": [],
1048
+ "text": ""
1049
+ },
1050
+ "attribute": "edit-text-prompt-text",
1051
+ "reflect": false,
1052
+ "defaultValue": "'Edit text:'"
1053
+ },
1054
+ "screenshotErrorGeneral": {
1055
+ "type": "string",
1056
+ "mutable": false,
1057
+ "complexType": {
1058
+ "original": "string",
1059
+ "resolved": "string",
1060
+ "references": {}
1061
+ },
1062
+ "required": false,
1063
+ "optional": false,
1064
+ "docs": {
1065
+ "tags": [],
1066
+ "text": ""
1067
+ },
1068
+ "attribute": "screenshot-error-general",
1069
+ "reflect": false,
1070
+ "defaultValue": "'Failed to capture screenshot.'"
1071
+ },
1072
+ "screenshotErrorPermission": {
1073
+ "type": "string",
1074
+ "mutable": false,
1075
+ "complexType": {
1076
+ "original": "string",
1077
+ "resolved": "string",
1078
+ "references": {}
1079
+ },
1080
+ "required": false,
1081
+ "optional": false,
1082
+ "docs": {
1083
+ "tags": [],
1084
+ "text": ""
1085
+ },
1086
+ "attribute": "screenshot-error-permission",
1087
+ "reflect": false,
1088
+ "defaultValue": "'Permission denied. Please allow screen sharing to take screenshots.'"
1089
+ },
1090
+ "screenshotErrorNotSupported": {
1091
+ "type": "string",
1092
+ "mutable": false,
1093
+ "complexType": {
1094
+ "original": "string",
1095
+ "resolved": "string",
1096
+ "references": {}
1097
+ },
1098
+ "required": false,
1099
+ "optional": false,
1100
+ "docs": {
1101
+ "tags": [],
1102
+ "text": ""
1103
+ },
1104
+ "attribute": "screenshot-error-not-supported",
1105
+ "reflect": false,
1106
+ "defaultValue": "'Screen capture is not supported in this browser.'"
1107
+ },
1108
+ "screenshotErrorNotFound": {
1109
+ "type": "string",
1110
+ "mutable": false,
1111
+ "complexType": {
1112
+ "original": "string",
1113
+ "resolved": "string",
1114
+ "references": {}
1115
+ },
1116
+ "required": false,
1117
+ "optional": false,
1118
+ "docs": {
1119
+ "tags": [],
1120
+ "text": ""
1121
+ },
1122
+ "attribute": "screenshot-error-not-found",
1123
+ "reflect": false,
1124
+ "defaultValue": "'No screen sources available for capture.'"
1125
+ },
1126
+ "screenshotErrorCancelled": {
1127
+ "type": "string",
1128
+ "mutable": false,
1129
+ "complexType": {
1130
+ "original": "string",
1131
+ "resolved": "string",
1132
+ "references": {}
1133
+ },
1134
+ "required": false,
1135
+ "optional": false,
1136
+ "docs": {
1137
+ "tags": [],
1138
+ "text": ""
1139
+ },
1140
+ "attribute": "screenshot-error-cancelled",
1141
+ "reflect": false,
1142
+ "defaultValue": "'Screenshot capture was cancelled.'"
1143
+ },
1144
+ "screenshotErrorBrowserNotSupported": {
1145
+ "type": "string",
1146
+ "mutable": false,
1147
+ "complexType": {
1148
+ "original": "string",
1149
+ "resolved": "string",
1150
+ "references": {}
1151
+ },
1152
+ "required": false,
1153
+ "optional": false,
1154
+ "docs": {
1155
+ "tags": [],
1156
+ "text": ""
1157
+ },
1158
+ "attribute": "screenshot-error-browser-not-supported",
1159
+ "reflect": false,
1160
+ "defaultValue": "'Your browser does not support screen capture. Please use a browser like Chrome, Firefox, or Safari.'"
1161
+ },
1162
+ "screenshotErrorUnexpected": {
1163
+ "type": "string",
1164
+ "mutable": false,
1165
+ "complexType": {
1166
+ "original": "string",
1167
+ "resolved": "string",
1168
+ "references": {}
1169
+ },
1170
+ "required": false,
1171
+ "optional": false,
1172
+ "docs": {
1173
+ "tags": [],
1174
+ "text": ""
1175
+ },
1176
+ "attribute": "screenshot-error-unexpected",
1177
+ "reflect": false,
1178
+ "defaultValue": "'An unexpected error occurred. Please try again.'"
1696
1179
  }
1697
1180
  };
1698
1181
  }
@@ -1712,28 +1195,10 @@ export class FeedbackModal {
1712
1195
  "overlayVisible": {},
1713
1196
  "isAnimating": {},
1714
1197
  "takingScreenshot": {},
1715
- "showPreviewModal": {},
1716
- "screenshotError": {},
1717
1198
  "showScreenshotError": {},
1199
+ "screenshotError": {},
1718
1200
  "showCanvasEditor": {},
1719
- "canvasDrawingTool": {},
1720
- "canvasDrawingColor": {},
1721
- "canvasLineWidth": {},
1722
- "isDrawing": {},
1723
- "annotations": {},
1724
- "currentAnnotation": {},
1725
- "isDragging": {},
1726
- "draggedAnnotation": {},
1727
- "dragStartPos": {},
1728
- "showColorPicker": {},
1729
- "editingColorIndex": {},
1730
- "isResizing": {},
1731
- "resizingAnnotation": {},
1732
- "resizeStartSize": {},
1733
- "resizeStartDimensions": {},
1734
- "hoveredAnnotation": {},
1735
- "resizeHandle": {},
1736
- "defaultColors": {}
1201
+ "autoStartCapture": {}
1737
1202
  };
1738
1203
  }
1739
1204
  static get events() {