pushfeedback 0.1.66 → 0.1.68

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.
@@ -26,6 +26,9 @@ const FeedbackButton = class {
26
26
  this.project = '';
27
27
  this.rating = undefined;
28
28
  this.ratingMode = 'thumbs';
29
+ this.canvasEditorTitle = 'Edit screenshot';
30
+ this.canvasEditorCancelText = 'Cancel';
31
+ this.canvasEditorSaveText = 'Save';
29
32
  this.emailPlaceholder = 'Email address (optional)';
30
33
  this.errorMessage = 'Please try again later.';
31
34
  this.errorMessage403 = 'The request URL does not match the one defined in PushFeedback for this project.';
@@ -40,6 +43,7 @@ const FeedbackButton = class {
40
43
  this.ratingStarsPlaceholder = 'How would you rate this page?';
41
44
  this.screenshotAttachedText = 'Screenshot attached';
42
45
  this.screenshotButtonText = 'Add a screenshot';
46
+ this.screenshotTakingText = 'Taking screenshot...';
43
47
  this.screenshotTopbarText = 'Select an element on this page';
44
48
  this.sendButtonText = 'Send';
45
49
  this.successMessage = '';
@@ -85,6 +89,9 @@ const FeedbackButton = class {
85
89
  'project',
86
90
  'rating',
87
91
  'ratingMode',
92
+ 'canvasEditorTitle',
93
+ 'canvasEditorCancelText',
94
+ 'canvasEditorSaveText',
88
95
  'emailPlaceholder',
89
96
  'errorMessage',
90
97
  'errorMessage403',
@@ -100,6 +107,7 @@ const FeedbackButton = class {
100
107
  'ratingStarsPlaceholder',
101
108
  'screenshotAttachedText',
102
109
  'screenshotButtonText',
110
+ 'screenshotTakingText',
103
111
  'screenshotTopbarText',
104
112
  'sendButtonText',
105
113
  'successMessage',
@@ -197,14 +205,14 @@ function commonjsRequire () {
197
205
 
198
206
  var html2canvasPro = createCommonjsModule(function (module, exports) {
199
207
  /*!
200
- * html2canvas-pro 1.5.8 <https://yorickshan.github.io/html2canvas-pro/>
201
- * Copyright (c) 2024 yorickshan <https://github.com/yorickshan>
208
+ * html2canvas-pro 1.5.11 <https://yorickshan.github.io/html2canvas-pro/>
209
+ * Copyright (c) 2024-present yorickshan and html2canvas-pro contributors
202
210
  * Released under MIT License
203
211
  */
204
212
  (function (global, factory) {
205
213
  module.exports = factory() ;
206
214
  })(commonjsGlobal, (function () {
207
- /*! *****************************************************************************
215
+ /******************************************************************************
208
216
  Copyright (c) Microsoft Corporation.
209
217
 
210
218
  Permission to use, copy, modify, and/or distribute this software for any
@@ -218,7 +226,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
218
226
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
219
227
  PERFORMANCE OF THIS SOFTWARE.
220
228
  ***************************************************************************** */
221
- /* global Reflect, Promise */
229
+ /* global Reflect, Promise, SuppressedError, Symbol */
222
230
 
223
231
  var extendStatics = function(d, b) {
224
232
  extendStatics = Object.setPrototypeOf ||
@@ -262,7 +270,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
262
270
  function verb(n) { return function (v) { return step([n, v]); }; }
263
271
  function step(op) {
264
272
  if (f) throw new TypeError("Generator is already executing.");
265
- while (_) try {
273
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
266
274
  if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
267
275
  if (y = 0, t) op = [op[0] & 2, t.value];
268
276
  switch (op[0]) {
@@ -291,8 +299,13 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
291
299
  ar[i] = from[i];
292
300
  }
293
301
  }
294
- return to.concat(ar || from);
295
- }
302
+ return to.concat(ar || Array.prototype.slice.call(from));
303
+ }
304
+
305
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
306
+ var e = new Error(message);
307
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
308
+ };
296
309
 
297
310
  var Bounds = /** @class */ (function () {
298
311
  function Bounds(left, top, width, height) {
@@ -3927,7 +3940,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
3927
3940
  case 'gurmukhi':
3928
3941
  return 22 /* LIST_STYLE_TYPE.GURMUKHI */;
3929
3942
  case 'hebrew':
3930
- return 22 /* LIST_STYLE_TYPE.HEBREW */;
3943
+ return 52 /* LIST_STYLE_TYPE.HEBREW */;
3931
3944
  case 'hiragana':
3932
3945
  return 23 /* LIST_STYLE_TYPE.HIRAGANA */;
3933
3946
  case 'hiragana-iroha':
@@ -6203,7 +6216,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
6203
6216
  return createCounterStyleFromRange(value, 0xae6, 0xaef, true, defaultSuffix);
6204
6217
  case 22 /* LIST_STYLE_TYPE.GURMUKHI */:
6205
6218
  return createCounterStyleFromRange(value, 0xa66, 0xa6f, true, defaultSuffix);
6206
- case 22 /* LIST_STYLE_TYPE.HEBREW */:
6219
+ case 52 /* LIST_STYLE_TYPE.HEBREW */:
6207
6220
  return createAdditiveCounter(value, 1, 10999, HEBREW, 3 /* LIST_STYLE_TYPE.DECIMAL */, defaultSuffix);
6208
6221
  case 23 /* LIST_STYLE_TYPE.HIRAGANA */:
6209
6222
  return createCounterStyleFromSymbols(value, 'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわゐゑをん');
@@ -6662,8 +6675,8 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
6662
6675
  });
6663
6676
  };
6664
6677
  var ignoredStyleProperties = [
6665
- 'all',
6666
- 'd',
6678
+ 'all', // #2476
6679
+ 'd', // #2483
6667
6680
  'content' // Safari shows pseudoelements if content is set
6668
6681
  ];
6669
6682
  var copyCSSStyles = function (style, target) {
@@ -6780,12 +6793,21 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
6780
6793
  };
6781
6794
  Cache.prototype.loadImage = function (key) {
6782
6795
  return __awaiter(this, void 0, void 0, function () {
6783
- var isSameOrigin, useCORS, useProxy, src;
6796
+ var isSameOrigin, _a, useCORS, useProxy, src;
6784
6797
  var _this = this;
6785
- return __generator(this, function (_a) {
6786
- switch (_a.label) {
6798
+ return __generator(this, function (_b) {
6799
+ switch (_b.label) {
6787
6800
  case 0:
6788
- isSameOrigin = CacheStorage.isSameOrigin(key);
6801
+ if (!(typeof this._options.customIsSameOrigin === 'function')) return [3 /*break*/, 2];
6802
+ return [4 /*yield*/, this._options.customIsSameOrigin(key, CacheStorage.isSameOrigin)];
6803
+ case 1:
6804
+ _a = _b.sent();
6805
+ return [3 /*break*/, 3];
6806
+ case 2:
6807
+ _a = CacheStorage.isSameOrigin(key);
6808
+ _b.label = 3;
6809
+ case 3:
6810
+ isSameOrigin = _a;
6789
6811
  useCORS = !isInlineImage(key) && this._options.useCORS === true && FEATURES.SUPPORT_CORS_IMAGES && !isSameOrigin;
6790
6812
  useProxy = !isInlineImage(key) &&
6791
6813
  !isSameOrigin &&
@@ -6802,12 +6824,12 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
6802
6824
  return [2 /*return*/];
6803
6825
  }
6804
6826
  src = key;
6805
- if (!useProxy) return [3 /*break*/, 2];
6827
+ if (!useProxy) return [3 /*break*/, 5];
6806
6828
  return [4 /*yield*/, this.proxy(src)];
6807
- case 1:
6808
- src = _a.sent();
6809
- _a.label = 2;
6810
- case 2:
6829
+ case 4:
6830
+ src = _b.sent();
6831
+ _b.label = 5;
6832
+ case 5:
6811
6833
  this.context.logger.debug("Added image ".concat(key.substring(0, 256)));
6812
6834
  return [4 /*yield*/, new Promise(function (resolve, reject) {
6813
6835
  var img = new Image();
@@ -6826,7 +6848,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
6826
6848
  setTimeout(function () { return reject("Timed out (".concat(_this._options.imageTimeout, "ms) loading image")); }, _this._options.imageTimeout);
6827
6849
  }
6828
6850
  })];
6829
- case 3: return [2 /*return*/, _a.sent()];
6851
+ case 6: return [2 /*return*/, _b.sent()];
6830
6852
  }
6831
6853
  });
6832
6854
  });
@@ -7952,7 +7974,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
7952
7974
  };
7953
7975
  CanvasRenderer.prototype.renderNodeContent = function (paint) {
7954
7976
  return __awaiter(this, void 0, void 0, function () {
7955
- var container, curves, styles, _i, _a, child, image, image, iframeRenderer, canvas, size, _b, fontFamily, fontSize, baseline, bounds, x, textBounds, img, image, url, fontFamily, bounds;
7977
+ var container, curves, styles, _i, _a, child, image, image, iframeRenderer, canvas, size, _b, font, fontFamily, fontSize, baseline, bounds, x, textBounds, img, image, url, font, bounds;
7956
7978
  return __generator(this, function (_c) {
7957
7979
  switch (_c.label) {
7958
7980
  case 0:
@@ -8052,9 +8074,9 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
8052
8074
  }
8053
8075
  }
8054
8076
  if (isTextInputElement(container) && container.value.length) {
8055
- _b = this.createFontStyle(styles), fontFamily = _b[0], fontSize = _b[1];
8077
+ _b = this.createFontStyle(styles), font = _b[0], fontFamily = _b[1], fontSize = _b[2];
8056
8078
  baseline = this.fontMetrics.getMetrics(fontFamily, fontSize).baseline;
8057
- this.ctx.font = fontFamily;
8079
+ this.ctx.font = font;
8058
8080
  this.ctx.fillStyle = asString(styles.color);
8059
8081
  this.ctx.textBaseline = 'alphabetic';
8060
8082
  this.ctx.textAlign = canvasTextAlign(container.styles.textAlign);
@@ -8103,8 +8125,8 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
8103
8125
  case 18: return [3 /*break*/, 20];
8104
8126
  case 19:
8105
8127
  if (paint.listValue && container.styles.listStyleType !== -1 /* LIST_STYLE_TYPE.NONE */) {
8106
- fontFamily = this.createFontStyle(styles)[0];
8107
- this.ctx.font = fontFamily;
8128
+ font = this.createFontStyle(styles)[0];
8129
+ this.ctx.font = font;
8108
8130
  this.ctx.fillStyle = asString(styles.color);
8109
8131
  this.ctx.textBaseline = 'middle';
8110
8132
  this.ctx.textAlign = 'right';
@@ -8334,7 +8356,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
8334
8356
  canvas.height = height;
8335
8357
  ctx = canvas.getContext('2d');
8336
8358
  gradient_1 = ctx.createLinearGradient(x0, y0, x1, y1);
8337
- processColorStops(backgroundImage.stops, lineLength).forEach(function (colorStop) {
8359
+ processColorStops(backgroundImage.stops, lineLength || 1).forEach(function (colorStop) {
8338
8360
  return gradient_1.addColorStop(colorStop.stop, asString(colorStop.color));
8339
8361
  });
8340
8362
  ctx.fillStyle = gradient_1;
@@ -8869,7 +8891,8 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
8869
8891
  allowTaint: (_b = opts.allowTaint) !== null && _b !== void 0 ? _b : false,
8870
8892
  imageTimeout: (_c = opts.imageTimeout) !== null && _c !== void 0 ? _c : 15000,
8871
8893
  proxy: opts.proxy,
8872
- useCORS: (_d = opts.useCORS) !== null && _d !== void 0 ? _d : false
8894
+ useCORS: (_d = opts.useCORS) !== null && _d !== void 0 ? _d : false,
8895
+ customIsSameOrigin: opts.customIsSameOrigin
8873
8896
  };
8874
8897
  contextOptions = __assign({ logging: (_e = opts.logging) !== null && _e !== void 0 ? _e : true, cache: opts.cache }, resourceOptions);
8875
8898
  windowOptions = {
@@ -8970,7 +8993,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
8970
8993
  //# sourceMappingURL=html2canvas-pro.js.map
8971
8994
  });
8972
8995
 
8973
- const feedbackModalCss = ".text-center{flex-grow:1;text-align:center}.feedback-modal-wrapper *{font-family:var(--feedback-font-family)}.feedback-modal-wrapper--custom-font *{font-family:inherit}.feedback-modal-wrapper{position:absolute;z-index:var(--feedback-modal-modal-wrapper-z-index)}.feedback-overlay{background-color:var(--feedback-modal-screenshot-bg-color);height:100%;left:0;opacity:0;position:fixed;top:0;width:100%;z-index:var(--feedback-modal-screnshot-z-index);transition:opacity 0.2s ease-out}.feedback-overlay--visible{opacity:1}.feedback-modal{display:inline-block;position:relative}.feedback-modal-content{background-color:var(--feedback-modal-content-bg-color);border-color:1px solid var(--feedback-modal-header-text-color);border-radius:var(--feedback-modal-content-border-radius);box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);box-sizing:border-box;color:var(--feedback-modal-content-text-color);display:flex;flex-direction:column;left:50%;max-width:90%;padding:20px;position:fixed;top:50%;transform:translate(-50%, -50%) scale(0.95);opacity:0;width:100%;z-index:var(--feedback-modal-content-z-index);transition:transform 0.2s ease-out, opacity 0.2s ease-out}.feedback-modal-content--open{transform:translate(-50%, -50%) scale(1);opacity:1}.feedback-modal-header{align-items:center;color:var(--feedback-modal-header-text-color);display:flex;font-size:var(--feedback-header-font-size);font-weight:var(--feedback-modal-header-font-weight);justify-content:space-between;margin-bottom:20px}.feedback-modal-rating-buttons{width:100%;margin-bottom:20px}.feedback-modal-rating-button{padding:0;background-color:transparent;border:transparent;margin-right:5px;cursor:pointer}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button{border:1px solid var(--feedback-modal-button-border-color);border-radius:var(--feedback-modal-button-border-radius);color:var(--feedback-modal-button-text-color);font-size:var(--feedback-modal-button-font-size);font-weight:500;margin-right:10px;justify-content:center;padding:5px 10px}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button:hover,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--selected{background-color:var(--feedback-modal-button-bg-color-active);border:1px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-text-color-active)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button:hover svg,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--selected svg{stroke:var(--feedback-modal-rating-button-selected-color)}.feedback-modal-rating-buttons svg{stroke:var(--feedback-modal-rating-button-color);cursor:pointer}.feedback-modal-rating-buttons--stars .feedback-modal-rating-button--selected svg{fill:var(--feedback-modal-rating-button-stars-selected-color);stroke:var(--feedback-modal-rating-button-stars-selected-color)}.feedback-modal-text textarea{background-color:var(--feedback-modal-input-bg-color);border:1px solid var(--feedback-modal-input-border-color);border-radius:var(--feedback-modal-input-border-radius);box-sizing:border-box;color:var(--feedback-modal-input-text-color);font-size:var(--feedback-modal-input-font-size);margin-bottom:20px;height:100px;min-height:100px;padding:10px;resize:vertical;width:100%}.feedback-modal-email input{background-color:var(--feedback-modal-input-bg-color);border:1px solid var(--feedback-modal-input-border-color);border-radius:var(--feedback-modal-input-border-radius);box-sizing:border-box;color:var(--feedback-modal-input-text-color);font-size:var(--feedback-modal-input-font-size);margin-bottom:20px;height:40px;padding:10px;width:100%;margin-bottom:20px}.feedback-modal-privacy{font-size:var(--feedback-modal-input-font-size);margin-bottom:20px}.feedback-modal-text textarea:focus,.feedback-modal-email input:focus{border:1px solid var(--feedback-modal-input-border-color-focused);outline:none}.feedback-modal-buttons{display:flex;flex-direction:column}.feedback-modal-buttons .feedback-modal-button{margin-bottom:20px}.feedback-modal-button{align-items:center;background-color:transparent;border:1px solid var(--feedback-modal-button-border-color);border-radius:var(--feedback-modal-button-border-radius);color:var(--feedback-modal-button-text-color);cursor:pointer;display:flex;font-size:var(--feedback-modal-button-font-size);font-weight:500;justify-content:center;min-height:40px;padding:5px 10px}.feedback-modal-button svg{margin-right:6px}.feedback-modal-button path{fill:var(--feedback-modal-button-icon-color)}.feedback-modal-button:hover path,.feedback-modal-button--active path{fill:var(--feedback-modal-button-icon-color-active)}.feedback-modal-button--submit{background-color:var(--feedback-modal-button-submit-bg-color);border:1px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-submit-text-color)}.feedback-modal-button:hover,.feedback-modal-button--active{background-color:var(--feedback-modal-button-bg-color-active);border:1px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-text-color-active)}.feedback-modal-button--submit:hover{background-color:var(--feedback-modal-button-submit-bg-color-hover);border:1px solid var(--feedback-modal-button-submit-border-color-hover);color:var(--feedback-modal-button-submit-text-color-hover)}.feedback-modal-input-heading{display:block;font-size:14px;font-weight:300;padding-bottom:10px}.feedback-modal-footer{font-size:12px;text-align:center}.feedback-modal-footer a{color:var(--feedback-modal-footer-link);font-weight:500;text-decoration:none}.feedback-logo,.feedback-footer-text{display:block;text-align:center;margin-top:5px}.feedback-footer-text{margin-top:10px;line-height:1.5}.feedback-modal-close{background-color:var(--feedback-modal-close-bg-color);border:0;border-radius:50%;cursor:pointer;height:22px;margin-left:auto;padding:0;width:22px}.feedback-modal-close svg{stroke:var(--feedback-modal-close-color)}.feedback-modal-screenshot{background-color:var(--feedback-modal-screenshot-bg-color);height:100%;left:0;position:fixed;top:0;width:100%;z-index:var(--feedback-modal-screnshot-z-index)}.feedback-modal-screenshot-header{align-items:center;background-color:var(--feedback-modal-screenshot-header-bg-color);border-radius:var(--feedback-modal-content-border-radius);box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);box-sizing:border-box;color:var(--feedback-modal-screenshot-header-text-color);cursor:pointer;display:flex;left:50%;top:20px;transform:translateX(-50%);padding:10px;position:fixed;width:max-content;z-index:var(--feedback-modal-screenshot-header-z-index)}.feedback-modal-screenshot-close{height:24px;padding-left:10px;width:24px}.feedback-modal-screenshot-close svg{stroke:var(--feedback-modal-close-color)}.feedback-modal-message{font-size:var(--feedback-modal-message-font-size);margin-top:0}.feedback-modal-element-hover{background-color:transparent;cursor:pointer;border:1px solid var(--feedback-modal-element-hover-border-color)}.feedback-modal-element-selected{background-color:transparent;border:1px solid var(--feedback-modal-element-selected-border-color)}.screenshot-preview{display:inline-block;width:30px;height:30px;overflow:hidden;border-radius:4px;margin-right:10px;box-shadow:0 2px 4px rgba(0, 0, 0, 0.1);cursor:pointer}.screenshot-preview img{width:100%;height:100%;object-fit:cover}.preview-modal{position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);background-color:rgba(0, 0, 0, 0.8);padding:20px;border-radius:8px;z-index:1000}.preview-modal img{max-width:90vw;max-height:90vh}@media screen and (min-width: 768px){.feedback-modal-content{max-width:var(--feedback-modal-content-max-width)}.feedback-modal-content.feedback-modal-content--bottom-right{bottom:var(--feedback-modal-content-position-bottom);left:initial;right:var(--feedback-modal-content-position-right);top:initial;transform:initial}.feedback-modal-content.feedback-modal-content--bottom-left{bottom:var(--feedback-modal-content-position-bottom);left:var(--feedback-modal-content-position-left);top:initial;transform:initial}.feedback-modal-content.feedback-modal-content--top-right{right:var(--feedback-modal-content-position-right);top:var(--feedback-modal-content-position-top);transform:initial}.feedback-modal-content.feedback-modal-content--top-left{left:var(--feedback-modal-content-position-left);top:var(--feedback-modal-content-position-top);transform:initial}.feedback-modal-content.feedback-modal-content--center-left{left:5px;right:auto;top:50%;transform:translateY(-50%)}.feedback-modal-content.feedback-modal-content--center-right{left:auto;right:5px;top:50%;transform:translateY(-50%)}.feedback-modal-content.feedback-modal-content--sidebar-left.feedback-modal-content--open,.feedback-modal-content.feedback-modal-content--sidebar-right.feedback-modal-content--open{transform:translateX(0)}.feedback-modal-content.feedback-modal-content--sidebar-left{max-width:var(--feedback-modal-content-sidebar-max-width);left:0;right:auto;height:100vh;top:0;transform:translateX(-100%);transition:transform 0.5s ease-in-out;border-radius:0}.feedback-modal-content.feedback-modal-content--sidebar-right{max-width:var(--feedback-modal-content-sidebar-max-width);left:auto;right:0;height:100vh;top:0;transform:translateX(100%);transition:transform 0.5s ease-in-out;border-radius:0}.feedback-modal-text textarea{height:150px;min-height:150px}.feedback-modal-content.feedback-modal-content--bottom-right{transform:translateY(20px)}.feedback-modal-content.feedback-modal-content--bottom-right.feedback-modal-content--open{transform:translateY(0)}.feedback-modal-content.feedback-modal-content--bottom-left{transform:translateY(20px)}.feedback-modal-content.feedback-modal-content--bottom-left.feedback-modal-content--open{transform:translateY(0)}.feedback-modal-content.feedback-modal-content--top-right{transform:translateY(-20px)}.feedback-modal-content.feedback-modal-content--top-right.feedback-modal-content--open{transform:translateY(0)}.feedback-modal-content.feedback-modal-content--top-left{transform:translateY(-20px)}.feedback-modal-content.feedback-modal-content--top-left.feedback-modal-content--open{transform:translateY(0)}}";
8996
+ const feedbackModalCss = ".text-center{flex-grow:1;text-align:center}.feedback-modal-wrapper *{font-family:var(--feedback-font-family)}.feedback-modal-wrapper--custom-font *{font-family:inherit}.feedback-modal-wrapper{position:absolute;z-index:var(--feedback-modal-modal-wrapper-z-index)}.feedback-overlay{background-color:var(--feedback-modal-screenshot-bg-color);height:100%;left:0;opacity:0;position:fixed;top:0;width:100%;z-index:var(--feedback-modal-screnshot-z-index);transition:opacity 0.2s ease-out}.feedback-overlay--visible{opacity:1}.feedback-modal{display:inline-block;position:relative}.feedback-modal-content{background-color:var(--feedback-modal-content-bg-color);border-color:1px solid var(--feedback-modal-header-text-color);border-radius:var(--feedback-modal-content-border-radius);box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);box-sizing:border-box;color:var(--feedback-modal-content-text-color);display:flex;flex-direction:column;left:50%;max-width:90%;padding:20px;position:fixed;top:50%;transform:translate(-50%, -50%) scale(0.95);opacity:0;width:100%;z-index:var(--feedback-modal-content-z-index);transition:transform 0.2s ease-out, opacity 0.2s ease-out}.feedback-modal-content--open{transform:translate(-50%, -50%) scale(1);opacity:1}.feedback-modal-header{align-items:center;color:var(--feedback-modal-header-text-color);display:flex;font-size:var(--feedback-header-font-size);font-weight:var(--feedback-modal-header-font-weight);justify-content:space-between;margin-bottom:20px}.feedback-modal-rating-buttons{width:100%;margin-bottom:20px}.feedback-modal-rating-button{padding:0;background-color:transparent;border:transparent;margin-right:5px;cursor:pointer}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button{border:1px solid var(--feedback-modal-button-border-color);border-radius:var(--feedback-modal-button-border-radius);color:var(--feedback-modal-button-text-color);font-size:var(--feedback-modal-button-font-size);font-weight:500;margin-right:10px;justify-content:center;padding:5px 10px}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button:hover,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--selected{background-color:var(--feedback-modal-button-bg-color-active);border:1px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-text-color-active)}.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button:hover svg,.feedback-modal-rating-buttons--thumbs .feedback-modal-rating-button--selected svg{stroke:var(--feedback-modal-rating-button-selected-color)}.feedback-modal-rating-buttons svg{stroke:var(--feedback-modal-rating-button-color);cursor:pointer}.feedback-modal-rating-buttons--stars .feedback-modal-rating-button--selected svg{fill:var(--feedback-modal-rating-button-stars-selected-color);stroke:var(--feedback-modal-rating-button-stars-selected-color)}.feedback-modal-text textarea{background-color:var(--feedback-modal-input-bg-color);border:1px solid var(--feedback-modal-input-border-color);border-radius:var(--feedback-modal-input-border-radius);box-sizing:border-box;color:var(--feedback-modal-input-text-color);font-size:var(--feedback-modal-input-font-size);margin-bottom:20px;height:100px;min-height:100px;padding:10px;resize:vertical;width:100%}.feedback-modal-email input{background-color:var(--feedback-modal-input-bg-color);border:1px solid var(--feedback-modal-input-border-color);border-radius:var(--feedback-modal-input-border-radius);box-sizing:border-box;color:var(--feedback-modal-input-text-color);font-size:var(--feedback-modal-input-font-size);margin-bottom:20px;height:40px;padding:10px;width:100%;margin-bottom:20px}.feedback-modal-privacy{font-size:var(--feedback-modal-input-font-size);margin-bottom:20px}.feedback-modal-text textarea:focus,.feedback-modal-email input:focus{border:1px solid var(--feedback-modal-input-border-color-focused);outline:none}.feedback-modal-buttons{display:flex;flex-direction:column}.feedback-modal-buttons .feedback-modal-button{margin-bottom:20px}.feedback-modal-button{align-items:center;background-color:transparent;border:1px solid var(--feedback-modal-button-border-color);border-radius:var(--feedback-modal-button-border-radius);color:var(--feedback-modal-button-text-color);cursor:pointer;display:flex;font-size:var(--feedback-modal-button-font-size);font-weight:500;justify-content:center;min-height:40px;padding:5px 10px}.feedback-modal-button svg{margin-right:6px}.feedback-modal-button path{fill:var(--feedback-modal-button-icon-color)}.feedback-modal-button:hover path,.feedback-modal-button--active path{fill:var(--feedback-modal-button-icon-color-active)}.feedback-modal-button--submit{background-color:var(--feedback-modal-button-submit-bg-color);border:1px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-submit-text-color)}.feedback-modal-button:hover,.feedback-modal-button--active{background-color:var(--feedback-modal-button-bg-color-active);border:1px solid var(--feedback-modal-button-border-color-active);color:var(--feedback-modal-button-text-color-active)}.feedback-modal-button--submit:hover{background-color:var(--feedback-modal-button-submit-bg-color-hover);border:1px solid var(--feedback-modal-button-submit-border-color-hover);color:var(--feedback-modal-button-submit-text-color-hover)}.feedback-modal-input-heading{display:block;font-size:14px;font-weight:300;padding-bottom:10px}.feedback-modal-footer{font-size:12px;text-align:center}.feedback-modal-footer a{color:var(--feedback-modal-footer-link);font-weight:500;text-decoration:none}.feedback-logo,.feedback-footer-text{display:block;text-align:center;margin-top:5px}.feedback-footer-text{margin-top:10px;line-height:1.5}.feedback-modal-close{background-color:var(--feedback-modal-close-bg-color);border:0;border-radius:50%;cursor:pointer;height:22px;margin-left:auto;padding:0;width:22px}.feedback-modal-close svg{stroke:var(--feedback-modal-close-color)}.feedback-modal-screenshot{background-color:var(--feedback-modal-screenshot-bg-color);height:100%;left:0;position:fixed;top:0;width:100%;z-index:var(--feedback-modal-screnshot-z-index)}.feedback-modal-screenshot-header{align-items:center;background-color:var(--feedback-modal-screenshot-header-bg-color);border-radius:var(--feedback-modal-content-border-radius);box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);box-sizing:border-box;color:var(--feedback-modal-screenshot-header-text-color);cursor:pointer;display:flex;left:50%;top:20px;transform:translateX(-50%);padding:10px;position:fixed;width:max-content;z-index:var(--feedback-modal-screenshot-header-z-index)}.feedback-modal-screenshot-close{height:24px;padding-left:10px;width:24px}.feedback-modal-screenshot-close svg{stroke:var(--feedback-modal-close-color)}.feedback-modal-message{font-size:var(--feedback-modal-message-font-size);margin-top:0}.feedback-modal-element-hover{background-color:transparent;cursor:pointer;border:1px solid var(--feedback-modal-element-hover-border-color)}.feedback-modal-element-selected{background-color:transparent;border:3px solid var(--feedback-modal-element-selected-border-color) !important;box-shadow:0 0 0 2px rgba(0, 123, 255, 0.3) !important}.screenshot-preview{display:inline-block;width:30px;height:30px;overflow:hidden;border-radius:4px;margin-right:10px;box-shadow:0 2px 4px rgba(0, 0, 0, 0.1);cursor:pointer;transition:transform 0.2s ease}.screenshot-preview:hover{transform:scale(1.1)}.screenshot-preview img{width:100%;height:100%;object-fit:cover}.screenshot-loading{display:inline-flex;align-items:center;margin-right:8px}.canvas-editor-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background-color:var(--feedback-modal-screenshot-bg-color);z-index:10001;display:flex;align-items:center;justify-content:center}.canvas-editor-modal{width:95vw;height:98vh;background:var(--feedback-canvas-editor-bg-color);border-radius:var(--feedback-modal-content-border-radius);display:flex;flex-direction:column;overflow:hidden;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15)}.canvas-editor-header{background:var(--feedback-canvas-editor-header-bg-color);border-bottom:1px solid var(--feedback-canvas-editor-border-color);padding:12px 16px;display:flex;flex-direction:column;gap:12px;flex-shrink:0}.canvas-editor-title h3{margin:0;font-size:var(--feedback-modal-header-font-size);font-weight:var(--feedback-modal-header-font-weight);color:var(--feedback-modal-header-text-color);font-family:var(--feedback-modal-header-font-family)}.canvas-editor-toolbar{display:flex;align-items:center;gap:20px;flex-wrap:wrap}.toolbar-section{display:flex;align-items:center}.toolbar-section:last-child{margin-left:auto;gap:10px}.tool-group{display:flex;align-items:center;background:var(--feedback-canvas-editor-tool-bg-color);border:1px solid var(--feedback-canvas-editor-border-color);border-radius:var(--feedback-modal-button-border-radius);padding:4px;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .10)}.tool-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border:none;background:none;border-radius:var(--feedback-modal-button-border-radius);cursor:pointer;color:var(--feedback-canvas-editor-tool-text-color);transition:all 0.2s ease;position:relative}.tool-btn:hover{background:var(--feedback-canvas-editor-tool-bg-hover);color:var(--feedback-modal-button-text-color-active)}.tool-btn.active{background:var(--feedback-canvas-editor-tool-bg-active);color:var(--feedback-canvas-editor-tool-text-active)}.tool-btn:disabled{opacity:0.4;cursor:not-allowed}.tool-btn:disabled:hover{background:none;color:var(--feedback-canvas-editor-tool-text-color)}.toolbar-divider{width:1px;height:20px;background:var(--feedback-canvas-editor-divider-color);margin:0 6px}.undo-btn{background:var(--feedback-canvas-editor-tool-bg-color) !important;border:1px solid var(--feedback-canvas-editor-border-color) !important}.undo-btn:hover:not(:disabled){background:var(--feedback-canvas-editor-tool-bg-hover) !important}.color-palette{display:flex;align-items:center;gap:6px;background:var(--feedback-canvas-editor-tool-bg-color);border:1px solid var(--feedback-canvas-editor-border-color);border-radius:var(--feedback-modal-button-border-radius);padding:6px;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .10)}.color-slot-wrapper{position:relative}.color-btn{width:28px;height:28px;border-radius:var(--feedback-modal-button-border-radius);border:2px solid transparent;cursor:pointer;transition:all 0.2s ease;position:relative;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .15);display:flex;align-items:center;justify-content:center}.color-btn:hover{transform:scale(1.05);box-shadow:0px 2px 4px 0px rgba(60, 64, 67, .25)}.color-btn.active{border-color:var(--feedback-primary-color);transform:scale(1.1);box-shadow:0 0 0 2px rgba(0, 112, 244, 0.2)}.color-btn.editing{border-color:var(--feedback-highlight-color);box-shadow:0 0 0 2px rgba(255, 180, 34, 0.3)}.color-btn.editing:hover{border-color:var(--feedback-highlight-color);box-shadow:0 0 0 2px rgba(255, 180, 34, 0.4)}.color-picker-dropdown{position:absolute;top:100%;left:50%;transform:translateX(-50%);margin-top:6px;background:var(--feedback-canvas-editor-tool-bg-color);border:1px solid var(--feedback-canvas-editor-border-color);border-radius:var(--feedback-modal-button-border-radius);padding:6px;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);z-index:1000}.color-picker-dropdown input[type=\"color\"]{width:50px;height:32px;border:none;border-radius:var(--feedback-modal-button-border-radius);cursor:pointer}.size-control{display:flex;align-items:center;gap:10px;background:var(--feedback-canvas-editor-tool-bg-color);border:1px solid var(--feedback-canvas-editor-border-color);border-radius:var(--feedback-modal-button-border-radius);padding:6px 12px;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .10)}.size-slider{width:70px;height:4px;border-radius:2px;background:var(--feedback-canvas-editor-slider-track);outline:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;border:none}.size-slider::-webkit-slider-track{width:100%;height:4px;cursor:pointer;background:var(--feedback-canvas-editor-slider-track);border-radius:2px;border:none;box-shadow:none}.size-slider::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:var(--feedback-primary-color);cursor:pointer;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .20);border:none;margin-top:-5px;}.size-slider::-moz-range-track{width:100%;height:4px;cursor:pointer;background:var(--feedback-canvas-editor-slider-track);border-radius:2px;border:none;box-shadow:none}.size-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;background:var(--feedback-primary-color);cursor:pointer;border:none;box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .20)}.size-slider::-moz-range-progress{background:var(--feedback-canvas-editor-slider-track);height:4px;border-radius:2px}.size-slider::-ms-track{width:100%;height:4px;cursor:pointer;background:transparent;border-color:transparent;color:transparent}.size-slider::-ms-fill-lower{background:var(--feedback-canvas-editor-slider-track);border-radius:2px}.size-slider::-ms-fill-upper{background:var(--feedback-canvas-editor-slider-track);border-radius:2px}.size-slider::-ms-thumb{width:14px;height:14px;border-radius:50%;background:var(--feedback-primary-color);cursor:pointer;border:none}.size-value{font-weight:500;color:var(--feedback-canvas-editor-tool-text-color);font-size:var(--feedback-text-font-size);min-width:30px}.action-btn{display:flex;align-items:center;gap:6px;padding:5px 12px;border:1px solid var(--feedback-canvas-editor-action-secondary-border);border-radius:var(--feedback-modal-button-border-radius);cursor:pointer;font-size:var(--feedback-modal-button-font-size);font-weight:500;transition:all 0.2s ease;min-width:65px;justify-content:center;height:36px;font-family:var(--feedback-font-family)}.action-btn.secondary{background:var(--feedback-canvas-editor-action-secondary-bg);color:var(--feedback-canvas-editor-action-secondary-text);border-color:var(--feedback-canvas-editor-action-secondary-border)}.action-btn.secondary:hover{background:var(--feedback-canvas-editor-tool-bg-hover);color:var(--feedback-modal-button-text-color-active);border-color:var(--feedback-modal-button-border-color-active)}.action-btn.primary{background:var(--feedback-canvas-editor-action-primary-bg);color:var(--feedback-canvas-editor-action-primary-text);border-color:var(--feedback-modal-button-border-color-active)}.action-btn.primary:hover{background:var(--feedback-modal-button-submit-bg-color-hover);border-color:var(--feedback-modal-button-submit-border-color-hover)}.canvas-editor-content{flex:1;display:flex;align-items:center;justify-content:center;padding:16px;background:var(--feedback-canvas-editor-content-bg);overflow:hidden;min-height:0;min-width:0}.annotation-canvas{max-width:100%;max-height:100%;width:auto;height:auto;cursor:crosshair;border-radius:var(--feedback-modal-content-border-radius);box-shadow:0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);background:var(--feedback-white-color);transition:box-shadow 0.3s ease;object-fit:contain;display:block}.annotation-canvas:hover{box-shadow:0px 2px 4px 0px rgba(60, 64, 67, .35), 0px 4px 12px 4px rgba(60, 64, 67, .20)}@media screen and (min-width: 768px){.feedback-modal-content{max-width:var(--feedback-modal-content-max-width)}.feedback-modal-content.feedback-modal-content--bottom-right{bottom:var(--feedback-modal-content-position-bottom);left:initial;right:var(--feedback-modal-content-position-right);top:initial;transform:initial}.feedback-modal-content.feedback-modal-content--bottom-left{bottom:var(--feedback-modal-content-position-bottom);left:var(--feedback-modal-content-position-left);top:initial;transform:initial}.feedback-modal-content.feedback-modal-content--top-right{right:var(--feedback-modal-content-position-right);top:var(--feedback-modal-content-position-top);transform:initial}.feedback-modal-content.feedback-modal-content--top-left{left:var(--feedback-modal-content-position-left);top:var(--feedback-modal-content-position-top);transform:initial}.feedback-modal-content.feedback-modal-content--center-left{left:5px;right:auto;top:50%;transform:translateY(-50%)}.feedback-modal-content.feedback-modal-content--center-right{left:auto;right:5px;top:50%;transform:translateY(-50%)}.feedback-modal-content.feedback-modal-content--sidebar-left.feedback-modal-content--open,.feedback-modal-content.feedback-modal-content--sidebar-right.feedback-modal-content--open{transform:translateX(0)}.feedback-modal-content.feedback-modal-content--sidebar-left{max-width:var(--feedback-modal-content-sidebar-max-width);left:0;right:auto;height:100vh;top:0;transform:translateX(-100%);transition:transform 0.5s ease-in-out;border-radius:0}.feedback-modal-content.feedback-modal-content--sidebar-right{max-width:var(--feedback-modal-content-sidebar-max-width);left:auto;right:0;height:100vh;top:0;transform:translateX(100%);transition:transform 0.5s ease-in-out;border-radius:0}.feedback-modal-text textarea{height:150px;min-height:150px}.feedback-modal-content.feedback-modal-content--bottom-right{transform:translateY(20px)}.feedback-modal-content.feedback-modal-content--bottom-right.feedback-modal-content--open{transform:translateY(0)}.feedback-modal-content.feedback-modal-content--bottom-left{transform:translateY(20px)}.feedback-modal-content.feedback-modal-content--bottom-left.feedback-modal-content--open{transform:translateY(0)}.feedback-modal-content.feedback-modal-content--top-right{transform:translateY(-20px)}.feedback-modal-content.feedback-modal-content--top-right.feedback-modal-content--open{transform:translateY(0)}.feedback-modal-content.feedback-modal-content--top-left{transform:translateY(-20px)}.feedback-modal-content.feedback-modal-content--top-left.feedback-modal-content--open{transform:translateY(0)}}@media (max-width: 768px){.canvas-editor-modal{width:100vw;height:100vh;border-radius:0}.canvas-editor-header{padding:8px 12px;gap:8px}.canvas-editor-toolbar{flex-direction:column;align-items:stretch;gap:12px}.toolbar-section{justify-content:center}.toolbar-section:last-child{margin-left:0;justify-content:stretch;gap:8px}.action-btn{flex:1;min-width:auto}.canvas-editor-content{padding:8px}.tool-group{flex-wrap:wrap;justify-content:center}.color-palette{flex-wrap:wrap;justify-content:center}.size-control{flex-direction:column;gap:6px;text-align:center}.size-slider{width:100px}}";
8974
8997
 
8975
8998
  const FeedbackModal = class {
8976
8999
  constructor(hostRef) {
@@ -9057,8 +9080,27 @@ const FeedbackModal = class {
9057
9080
  this.showScreenshotTopBar = false;
9058
9081
  this.hasSelectedElement = false;
9059
9082
  this.encodedScreenshot = null;
9060
- this.originalElement = null;
9061
- this.selectedElementBounds = null;
9083
+ // Remove highlight from ALL selected elements
9084
+ document.querySelectorAll('.feedback-modal-element-selected').forEach(el => {
9085
+ el.classList.remove('feedback-modal-element-selected');
9086
+ });
9087
+ // Reset canvas editor states
9088
+ this.takingScreenshot = false;
9089
+ this.showPreviewModal = false;
9090
+ this.showCanvasEditor = false;
9091
+ this.annotations = [];
9092
+ this.currentAnnotation = null;
9093
+ this.isDrawing = false;
9094
+ this.canvasRef = null;
9095
+ this.canvasContext = null;
9096
+ this.originalImageData = null;
9097
+ // Reset resizing states
9098
+ this.isResizing = false;
9099
+ this.resizingAnnotation = null;
9100
+ this.resizeStartSize = 16;
9101
+ this.hoveredAnnotation = null;
9102
+ this.resizeHandle = false;
9103
+ // Reset form states
9062
9104
  this.formSuccess = false;
9063
9105
  this.formError = false;
9064
9106
  this.formErrorStatus = 500;
@@ -9067,109 +9109,609 @@ const FeedbackModal = class {
9067
9109
  this.resetOverflow();
9068
9110
  }, 200);
9069
9111
  };
9070
- this.openScreenShot = () => {
9071
- this.hasSelectedElement = false;
9072
- this.showModal = false;
9073
- this.showScreenshotMode = true;
9074
- this.showScreenshotTopBar = true;
9075
- this.encodedScreenshot = null;
9076
- this.originalElement = null;
9077
- this.selectedElementBounds = null;
9078
- if (window.innerWidth > document.documentElement.clientWidth) {
9079
- document.documentElement.classList.add('feedback-modal-screenshot-open--scroll');
9112
+ this.openScreenShot = async () => {
9113
+ // Show loading state immediately
9114
+ this.takingScreenshot = true;
9115
+ try {
9116
+ // Capture viewport screenshot immediately
9117
+ const dataUrl = await this.captureViewportScreenshot();
9118
+ this.encodedScreenshot = dataUrl;
9119
+ this.originalImageData = dataUrl;
9120
+ // Reset loading state
9121
+ this.takingScreenshot = false;
9122
+ // Skip preview modal and go directly to canvas editor
9123
+ this.showModal = false;
9124
+ this.showCanvasEditor = true;
9125
+ // Initialize canvas after a short delay to ensure DOM is ready
9126
+ setTimeout(() => {
9127
+ this.initializeCanvas();
9128
+ }, 100);
9129
+ }
9130
+ catch (error) {
9131
+ console.error('Failed to capture screenshot:', error);
9132
+ // Reset loading state on error
9133
+ this.takingScreenshot = false;
9134
+ // Show modal anyway
9135
+ this.showModal = true;
9080
9136
  }
9081
- const scrollY = window.scrollY;
9082
- document.documentElement.style.top = `-${scrollY}px`;
9083
- window.scrollTo(0, parseInt(document.documentElement.style.top || '0') * -1);
9084
- document.documentElement.classList.add('feedback-modal-screenshot-open');
9085
9137
  };
9086
- this.closeScreenShot = () => {
9138
+ this.openCanvasEditor = (event) => {
9139
+ if (event) {
9140
+ event.stopPropagation();
9141
+ }
9087
9142
  this.showModal = false;
9088
- this.showScreenshotMode = false;
9089
- this.showScreenshotTopBar = false;
9090
- this.hasSelectedElement = false;
9091
- this.encodedScreenshot = null;
9092
- this.originalElement = null;
9093
- this.selectedElementBounds = null;
9094
- this.resetOverflow();
9143
+ this.showCanvasEditor = true;
9144
+ // Initialize canvas after a short delay to ensure DOM is ready
9145
+ setTimeout(() => {
9146
+ this.initializeCanvas();
9147
+ }, 100);
9148
+ };
9149
+ this.closeCanvasEditor = () => {
9150
+ this.showCanvasEditor = false;
9151
+ this.showModal = true;
9152
+ };
9153
+ this.saveAnnotations = () => {
9154
+ if (this.canvasRef) {
9155
+ // Create final image with annotations
9156
+ const finalDataUrl = this.canvasRef.toDataURL('image/png');
9157
+ this.encodedScreenshot = finalDataUrl;
9158
+ }
9159
+ this.showCanvasEditor = false;
9160
+ this.showModal = true;
9095
9161
  };
9096
- this.handleMouseOverScreenShot = (event) => {
9097
- event.preventDefault();
9098
- if (this.hasSelectedElement)
9162
+ this.initializeCanvas = () => {
9163
+ if (!this.canvasRef || !this.originalImageData)
9099
9164
  return;
9100
- const borderOffset = 2;
9101
- this.screenshotModal.style.display = 'none';
9102
- const elementUnder = document.elementFromPoint(event.clientX, event.clientY);
9103
- const rect = elementUnder.getBoundingClientRect();
9104
- this.screenshotModal.style.display = '';
9105
- // Get the bounding box of the element selected
9106
- this.elementSelected.style.position = 'absolute';
9107
- this.elementSelected.style.left = `${rect.left}px`;
9108
- this.elementSelected.style.top = `${rect.top}px`;
9109
- this.elementSelected.style.width = `${rect.width}px`;
9110
- this.elementSelected.style.height = `${rect.height}px`;
9111
- this.elementSelected.classList.add('feedback-modal-element-hover');
9112
- // Set the background color of nonselected areas
9113
- // Top
9114
- this.topSide.style.position = 'absolute';
9115
- this.topSide.style.left = `${rect.left}px`;
9116
- this.topSide.style.top = '0px';
9117
- this.topSide.style.width = `${rect.width + borderOffset}px`;
9118
- this.topSide.style.height = `${rect.top}px`;
9119
- this.topSide.style.backgroundColor = 'rgba(0, 0, 0, 0.4)';
9120
- // Left
9121
- this.leftSide.style.position = 'absolute';
9122
- this.leftSide.style.left = '0px';
9123
- this.leftSide.style.top = '0px';
9124
- this.leftSide.style.width = `${rect.left}px`;
9125
- this.leftSide.style.height = '100vh';
9126
- this.leftSide.style.backgroundColor = 'rgba(0, 0, 0, 0.4)';
9127
- // Bottom
9128
- this.bottomSide.style.position = 'absolute';
9129
- this.bottomSide.style.left = `${rect.left}px`;
9130
- this.bottomSide.style.top = `${rect.bottom + borderOffset}px`;
9131
- this.bottomSide.style.width = `${rect.width + borderOffset}px`;
9132
- this.bottomSide.style.height = '100vh';
9133
- this.bottomSide.style.backgroundColor = 'rgba(0, 0, 0, 0.4)';
9134
- // Right
9135
- this.rightSide.style.position = 'absolute';
9136
- this.rightSide.style.left = `${rect.right + borderOffset}px`;
9137
- this.rightSide.style.top = '0px';
9138
- this.rightSide.style.width = '100%';
9139
- this.rightSide.style.height = '100vh';
9140
- this.rightSide.style.backgroundColor = 'rgba(0, 0, 0, 0.4)';
9141
- // Restore the visibility of the screenshot-modal
9142
- this.screenshotModal.style.backgroundColor = 'transparent';
9143
- };
9144
- this.handleMouseClickedSelectedElement = async (event) => {
9145
- event.preventDefault();
9146
- if (!this.elementSelected) {
9165
+ this.canvasContext = this.canvasRef.getContext('2d');
9166
+ const img = new Image();
9167
+ img.onload = () => {
9168
+ // Set canvas to original image dimensions
9169
+ this.canvasRef.width = img.width;
9170
+ this.canvasRef.height = img.height;
9171
+ // Get available container dimensions
9172
+ const containerWidth = this.canvasRef.parentElement.clientWidth - 32; // Account for reduced padding (16px * 2)
9173
+ const containerHeight = this.canvasRef.parentElement.clientHeight - 32;
9174
+ // Calculate scale factors for both dimensions
9175
+ const scaleX = containerWidth / img.width;
9176
+ const scaleY = containerHeight / img.height;
9177
+ // Use the smaller scale to ensure complete image fits
9178
+ const scale = Math.min(scaleX, scaleY, 1); // Never scale up, only down
9179
+ // Calculate final display dimensions
9180
+ const displayWidth = img.width * scale;
9181
+ const displayHeight = img.height * scale;
9182
+ // Set CSS size for display (this scales the canvas visually)
9183
+ this.canvasRef.style.width = `${displayWidth}px`;
9184
+ this.canvasRef.style.height = `${displayHeight}px`;
9185
+ console.log('Canvas initialized with complete image fit:', {
9186
+ originalWidth: img.width,
9187
+ originalHeight: img.height,
9188
+ displayWidth,
9189
+ displayHeight,
9190
+ scale,
9191
+ scaleX,
9192
+ scaleY,
9193
+ containerWidth,
9194
+ containerHeight,
9195
+ usingScale: scale === scaleX ? 'width-limited' : 'height-limited'
9196
+ });
9197
+ // Draw the original image at full resolution
9198
+ this.canvasContext.drawImage(img, 0, 0);
9199
+ // Redraw existing annotations
9200
+ this.redrawAnnotations();
9201
+ };
9202
+ img.src = this.originalImageData;
9203
+ };
9204
+ this.redrawAnnotations = () => {
9205
+ if (!this.canvasContext)
9206
+ return;
9207
+ // Clear and redraw background image
9208
+ const img = new Image();
9209
+ img.onload = () => {
9210
+ this.canvasContext.clearRect(0, 0, this.canvasRef.width, this.canvasRef.height);
9211
+ this.canvasContext.drawImage(img, 0, 0);
9212
+ // Draw all annotations
9213
+ this.annotations.forEach(annotation => {
9214
+ this.drawAnnotation(annotation);
9215
+ });
9216
+ };
9217
+ img.src = this.originalImageData;
9218
+ };
9219
+ this.drawAnnotation = (annotation) => {
9220
+ if (!this.canvasContext)
9147
9221
  return;
9222
+ this.canvasContext.strokeStyle = annotation.color;
9223
+ this.canvasContext.lineWidth = annotation.lineWidth;
9224
+ this.canvasContext.lineCap = 'round';
9225
+ this.canvasContext.lineJoin = 'round';
9226
+ switch (annotation.type) {
9227
+ case 'rectangle':
9228
+ this.canvasContext.strokeRect(annotation.startX, annotation.startY, annotation.width, annotation.height);
9229
+ // Rectangle resize handles disabled for now
9230
+ break;
9231
+ case 'line':
9232
+ this.canvasContext.beginPath();
9233
+ this.canvasContext.moveTo(annotation.startX, annotation.startY);
9234
+ this.canvasContext.lineTo(annotation.endX, annotation.endY);
9235
+ this.canvasContext.stroke();
9236
+ // Draw resize handles if this annotation is hovered
9237
+ if (this.hoveredAnnotation === annotation) {
9238
+ this.drawLineResizeHandles(annotation);
9239
+ }
9240
+ break;
9241
+ case 'arrow':
9242
+ this.drawArrow(annotation.startX, annotation.startY, annotation.endX, annotation.endY);
9243
+ // Draw resize handles if this annotation is hovered
9244
+ if (this.hoveredAnnotation === annotation) {
9245
+ this.drawLineResizeHandles(annotation); // Same as line
9246
+ }
9247
+ break;
9248
+ case 'text':
9249
+ const fontSize = annotation.fontSize || 16;
9250
+ this.canvasContext.fillStyle = annotation.color;
9251
+ this.canvasContext.font = `${fontSize}px Arial`;
9252
+ this.canvasContext.fillText(annotation.text, annotation.x, annotation.y);
9253
+ // Draw resize handle if this annotation is hovered
9254
+ if (this.hoveredAnnotation === annotation) {
9255
+ this.drawTextResizeHandle(annotation);
9256
+ }
9257
+ break;
9148
9258
  }
9149
- this.hasSelectedElement = true;
9150
- this.elementSelected.classList.add('feedback-modal-element-selected');
9151
- // Store the original element that was under the mouse
9152
- this.screenshotModal.style.display = 'none';
9153
- this.originalElement = document.elementFromPoint(event.clientX, event.clientY);
9154
- this.selectedElementBounds = this.originalElement.getBoundingClientRect();
9155
- this.screenshotModal.style.display = '';
9156
- // Hide the screenshot interface
9157
- this.showScreenshotTopBar = false;
9158
- this.showModal = false;
9159
- try {
9160
- const dataUrl = await this.captureScreenshot();
9161
- console.log('Screenshot captured');
9162
- this.encodedScreenshot = dataUrl;
9259
+ };
9260
+ // Draw resize handle for text annotation
9261
+ this.drawTextResizeHandle = (annotation) => {
9262
+ if (!this.canvasContext || annotation.type !== 'text')
9263
+ return;
9264
+ const fontSize = annotation.fontSize || 16;
9265
+ const textWidth = this.getTextWidth(annotation.text, fontSize);
9266
+ const handleSize = 8;
9267
+ const handleX = annotation.x + textWidth;
9268
+ const handleY = annotation.y;
9269
+ // Draw resize handle (small square) - using widget primary color
9270
+ this.canvasContext.fillStyle = '#0070F4'; // var(--feedback-primary-color)
9271
+ this.canvasContext.strokeStyle = '#ffffff';
9272
+ this.canvasContext.lineWidth = 2;
9273
+ this.canvasContext.fillRect(handleX - handleSize / 2, handleY - handleSize / 2, handleSize, handleSize);
9274
+ this.canvasContext.strokeRect(handleX - handleSize / 2, handleY - handleSize / 2, handleSize, handleSize);
9275
+ };
9276
+ this.drawArrow = (fromX, fromY, toX, toY) => {
9277
+ const headlen = 15; // Arrow head length
9278
+ const angle = Math.atan2(toY - fromY, toX - fromX);
9279
+ // Draw line
9280
+ this.canvasContext.beginPath();
9281
+ this.canvasContext.moveTo(fromX, fromY);
9282
+ this.canvasContext.lineTo(toX, toY);
9283
+ this.canvasContext.stroke();
9284
+ // Draw arrow head
9285
+ this.canvasContext.beginPath();
9286
+ this.canvasContext.moveTo(toX, toY);
9287
+ this.canvasContext.lineTo(toX - headlen * Math.cos(angle - Math.PI / 6), toY - headlen * Math.sin(angle - Math.PI / 6));
9288
+ this.canvasContext.moveTo(toX, toY);
9289
+ this.canvasContext.lineTo(toX - headlen * Math.cos(angle + Math.PI / 6), toY - headlen * Math.sin(angle + Math.PI / 6));
9290
+ this.canvasContext.stroke();
9291
+ };
9292
+ this.undoLastAnnotation = () => {
9293
+ this.annotations = this.annotations.slice(0, -1);
9294
+ this.redrawAnnotations();
9295
+ };
9296
+ // Handle color slot editing
9297
+ this.handleColorSlotClick = (colorIndex) => {
9298
+ if (this.editingColorIndex === colorIndex) {
9299
+ // If already editing this slot, just select the color
9300
+ this.canvasDrawingColor = this.defaultColors[colorIndex];
9301
+ this.showColorPicker = false;
9302
+ this.editingColorIndex = -1;
9163
9303
  }
9164
- catch (error) {
9165
- console.error('Failed to capture screenshot:', error);
9166
- this.hasSelectedElement = false;
9304
+ else {
9305
+ // Start editing this color slot
9306
+ this.editingColorIndex = colorIndex;
9307
+ this.showColorPicker = true;
9308
+ this.canvasDrawingColor = this.defaultColors[colorIndex];
9167
9309
  }
9168
- finally {
9169
- // Show the modal again
9170
- this.showModal = true;
9310
+ };
9311
+ // Update color in slot
9312
+ this.updateColorSlot = (newColor) => {
9313
+ if (this.editingColorIndex >= 0 && this.editingColorIndex < this.defaultColors.length) {
9314
+ this.defaultColors[this.editingColorIndex] = newColor;
9315
+ this.canvasDrawingColor = newColor;
9316
+ this.showColorPicker = false;
9317
+ this.editingColorIndex = -1;
9318
+ // Force reactivity
9319
+ this.defaultColors = [...this.defaultColors];
9171
9320
  }
9172
9321
  };
9322
+ // Handle color picker input without closing
9323
+ this.handleColorPickerInput = (event) => {
9324
+ event.stopPropagation();
9325
+ const newColor = event.target.value;
9326
+ if (this.editingColorIndex >= 0 && this.editingColorIndex < this.defaultColors.length) {
9327
+ this.defaultColors[this.editingColorIndex] = newColor;
9328
+ this.canvasDrawingColor = newColor;
9329
+ // Force reactivity
9330
+ this.defaultColors = [...this.defaultColors];
9331
+ }
9332
+ };
9333
+ // Handle color picker click to prevent closing
9334
+ this.handleColorPickerClick = (event) => {
9335
+ event.stopPropagation();
9336
+ };
9337
+ // Close color picker
9338
+ this.closeColorPicker = () => {
9339
+ this.showColorPicker = false;
9340
+ this.editingColorIndex = -1;
9341
+ };
9342
+ // Check if point is in resize handle for any annotation type
9343
+ this.isPointInResizeHandle = (x, y, annotation) => {
9344
+ const handleSize = 8;
9345
+ switch (annotation.type) {
9346
+ case 'text':
9347
+ const textWidth = this.getTextWidth(annotation.text, annotation.fontSize || 16);
9348
+ const handleX = annotation.x + textWidth;
9349
+ const handleY = annotation.y;
9350
+ return x >= handleX - handleSize / 2 && x <= handleX + handleSize / 2 &&
9351
+ y >= handleY - handleSize / 2 && y <= handleY + handleSize / 2;
9352
+ case 'rectangle':
9353
+ // Rectangle resizing disabled for now
9354
+ return false;
9355
+ case 'line':
9356
+ case 'arrow':
9357
+ // Check both endpoint handles
9358
+ const lineHandles = [
9359
+ { x: annotation.startX, y: annotation.startY, point: 'start' },
9360
+ { x: annotation.endX, y: annotation.endY, point: 'end' }
9361
+ ];
9362
+ for (const handle of lineHandles) {
9363
+ if (x >= handle.x - handleSize / 2 && x <= handle.x + handleSize / 2 &&
9364
+ y >= handle.y - handleSize / 2 && y <= handle.y + handleSize / 2) {
9365
+ return handle.point; // Return which endpoint was clicked
9366
+ }
9367
+ }
9368
+ return false;
9369
+ default:
9370
+ return false;
9371
+ }
9372
+ };
9373
+ // Get text width for resize handle positioning
9374
+ this.getTextWidth = (text, fontSize) => {
9375
+ // Approximate text width calculation
9376
+ return text.length * fontSize * 0.6;
9377
+ };
9378
+ // Start text resize
9379
+ this.startTextResize = (annotation, startPos) => {
9380
+ this.isResizing = true;
9381
+ this.resizingAnnotation = annotation;
9382
+ this.resizeStartSize = annotation.fontSize || 16;
9383
+ this.dragStartPos = startPos;
9384
+ };
9385
+ // Handle text resize
9386
+ this.handleTextResize = (currentPos) => {
9387
+ if (!this.resizingAnnotation || !this.dragStartPos)
9388
+ return;
9389
+ const deltaX = currentPos.x - this.dragStartPos.x;
9390
+ const deltaY = currentPos.y - this.dragStartPos.y;
9391
+ const avgDelta = (deltaX + deltaY) / 2;
9392
+ // Calculate new font size (minimum 8px, maximum 72px)
9393
+ const newSize = Math.max(8, Math.min(72, this.resizeStartSize + avgDelta * 0.5));
9394
+ // Update annotation font size
9395
+ const index = this.annotations.findIndex(a => a === this.resizingAnnotation);
9396
+ if (index !== -1) {
9397
+ this.annotations[index] = Object.assign(Object.assign({}, this.resizingAnnotation), { fontSize: Math.round(newSize) });
9398
+ this.resizingAnnotation = this.annotations[index];
9399
+ }
9400
+ this.redrawAnnotations();
9401
+ };
9402
+ // Start resize for any annotation type
9403
+ this.startResize = (annotation, handle, startPos) => {
9404
+ this.isResizing = true;
9405
+ this.resizingAnnotation = annotation;
9406
+ this.resizeHandle = handle;
9407
+ this.dragStartPos = startPos;
9408
+ // Store original values for different annotation types
9409
+ if (annotation.type === 'text') {
9410
+ this.resizeStartSize = annotation.fontSize || 16;
9411
+ }
9412
+ };
9413
+ // Enhanced mouse down handler with resize detection for all annotation types
9414
+ this.handleCanvasMouseDown = (event) => {
9415
+ if (!this.canvasRef)
9416
+ return;
9417
+ // Close color picker if open
9418
+ if (this.showColorPicker) {
9419
+ this.closeColorPicker();
9420
+ }
9421
+ const coords = this.getCanvasCoordinates(event);
9422
+ // Check if clicking on existing annotation first
9423
+ const found = this.findAnnotationAt(coords.x, coords.y);
9424
+ if (found) {
9425
+ // Check if clicking on resize handle for any annotation type
9426
+ const handle = this.isPointInResizeHandle(coords.x, coords.y, found.annotation);
9427
+ if (handle) {
9428
+ this.startResize(found.annotation, handle, coords);
9429
+ this.canvasRef.style.cursor = 'nw-resize';
9430
+ return;
9431
+ }
9432
+ // Start dragging existing annotation
9433
+ if (!this.isDrawing) {
9434
+ this.isDragging = true;
9435
+ this.draggedAnnotation = found.annotation;
9436
+ this.dragStartPos = coords;
9437
+ this.canvasRef.style.cursor = 'grabbing';
9438
+ return;
9439
+ }
9440
+ }
9441
+ // Original drawing logic
9442
+ this.isDrawing = true;
9443
+ if (this.canvasDrawingTool === 'text') {
9444
+ const text = prompt('Enter text:');
9445
+ if (text) {
9446
+ const annotation = {
9447
+ type: 'text',
9448
+ x: coords.x,
9449
+ y: coords.y,
9450
+ text,
9451
+ color: this.canvasDrawingColor,
9452
+ fontSize: 16
9453
+ };
9454
+ this.annotations = [...this.annotations, annotation];
9455
+ this.redrawAnnotations();
9456
+ }
9457
+ this.isDrawing = false;
9458
+ }
9459
+ else {
9460
+ this.currentAnnotation = {
9461
+ type: this.canvasDrawingTool,
9462
+ startX: coords.x,
9463
+ startY: coords.y,
9464
+ color: this.canvasDrawingColor,
9465
+ lineWidth: this.canvasLineWidth
9466
+ };
9467
+ }
9468
+ };
9469
+ this.handleCanvasMouseMove = (event) => {
9470
+ if (!this.canvasRef)
9471
+ return;
9472
+ const coords = this.getCanvasCoordinates(event);
9473
+ // Handle resizing for any annotation type
9474
+ if (this.isResizing && this.resizingAnnotation) {
9475
+ this.handleResize(coords);
9476
+ return;
9477
+ }
9478
+ // Handle dragging existing annotation
9479
+ if (this.isDragging && this.draggedAnnotation && this.dragStartPos) {
9480
+ const deltaX = coords.x - this.dragStartPos.x;
9481
+ const deltaY = coords.y - this.dragStartPos.y;
9482
+ // Update annotation position
9483
+ const updatedAnnotation = Object.assign({}, this.draggedAnnotation);
9484
+ switch (updatedAnnotation.type) {
9485
+ case 'rectangle':
9486
+ updatedAnnotation.startX += deltaX;
9487
+ updatedAnnotation.startY += deltaY;
9488
+ break;
9489
+ case 'line':
9490
+ case 'arrow':
9491
+ updatedAnnotation.startX += deltaX;
9492
+ updatedAnnotation.startY += deltaY;
9493
+ updatedAnnotation.endX += deltaX;
9494
+ updatedAnnotation.endY += deltaY;
9495
+ break;
9496
+ case 'text':
9497
+ updatedAnnotation.x += deltaX;
9498
+ updatedAnnotation.y += deltaY;
9499
+ break;
9500
+ }
9501
+ // Update annotation in array
9502
+ const index = this.annotations.findIndex(a => a === this.draggedAnnotation);
9503
+ if (index !== -1) {
9504
+ this.annotations[index] = updatedAnnotation;
9505
+ this.draggedAnnotation = updatedAnnotation;
9506
+ }
9507
+ this.dragStartPos = coords;
9508
+ this.redrawAnnotations();
9509
+ return;
9510
+ }
9511
+ // Handle drawing new annotation
9512
+ if (this.isDrawing && this.currentAnnotation) {
9513
+ if (this.canvasDrawingTool === 'rectangle') {
9514
+ this.currentAnnotation.width = coords.x - this.currentAnnotation.startX;
9515
+ this.currentAnnotation.height = coords.y - this.currentAnnotation.startY;
9516
+ }
9517
+ else {
9518
+ this.currentAnnotation.endX = coords.x;
9519
+ this.currentAnnotation.endY = coords.y;
9520
+ }
9521
+ this.redrawAnnotations();
9522
+ this.drawAnnotation(this.currentAnnotation);
9523
+ return;
9524
+ }
9525
+ // Handle hover states and cursor changes
9526
+ const found = this.findAnnotationAt(coords.x, coords.y);
9527
+ if (found) {
9528
+ // Check if hovering over resize handle for any annotation type
9529
+ const handle = this.isPointInResizeHandle(coords.x, coords.y, found.annotation);
9530
+ if (handle) {
9531
+ this.canvasRef.style.cursor = 'nw-resize';
9532
+ this.hoveredAnnotation = found.annotation;
9533
+ this.redrawAnnotations();
9534
+ return;
9535
+ }
9536
+ // Regular hover over annotation
9537
+ this.canvasRef.style.cursor = 'grab';
9538
+ if (this.hoveredAnnotation !== found.annotation) {
9539
+ this.hoveredAnnotation = found.annotation;
9540
+ this.redrawAnnotations();
9541
+ }
9542
+ }
9543
+ else {
9544
+ // No annotation under cursor
9545
+ this.canvasRef.style.cursor = 'crosshair';
9546
+ if (this.hoveredAnnotation) {
9547
+ this.hoveredAnnotation = null;
9548
+ this.redrawAnnotations();
9549
+ }
9550
+ }
9551
+ };
9552
+ this.handleCanvasMouseUp = () => {
9553
+ // Handle end of text resizing
9554
+ if (this.isResizing) {
9555
+ this.isResizing = false;
9556
+ this.resizingAnnotation = null;
9557
+ this.dragStartPos = null;
9558
+ this.resizeHandle = false;
9559
+ if (this.canvasRef) {
9560
+ this.canvasRef.style.cursor = 'crosshair';
9561
+ }
9562
+ return;
9563
+ }
9564
+ // Handle end of dragging
9565
+ if (this.isDragging) {
9566
+ this.isDragging = false;
9567
+ this.draggedAnnotation = null;
9568
+ this.dragStartPos = null;
9569
+ if (this.canvasRef) {
9570
+ this.canvasRef.style.cursor = 'crosshair';
9571
+ }
9572
+ return;
9573
+ }
9574
+ // Handle end of drawing
9575
+ if (!this.isDrawing || !this.currentAnnotation)
9576
+ return;
9577
+ this.isDrawing = false;
9578
+ this.annotations = [...this.annotations, this.currentAnnotation];
9579
+ this.currentAnnotation = null;
9580
+ this.redrawAnnotations();
9581
+ };
9582
+ // Draw resize handles for rectangle annotation
9583
+ this.drawRectangleResizeHandles = (annotation) => {
9584
+ if (!this.canvasContext || annotation.type !== 'rectangle')
9585
+ return;
9586
+ const handleSize = 8;
9587
+ const left = annotation.startX;
9588
+ const top = annotation.startY;
9589
+ const right = annotation.startX + annotation.width;
9590
+ const bottom = annotation.startY + annotation.height;
9591
+ // Define handle positions (4 corners)
9592
+ const handles = [
9593
+ { x: left, y: top },
9594
+ { x: right, y: top },
9595
+ { x: right, y: bottom },
9596
+ { x: left, y: bottom } // Bottom-left
9597
+ ];
9598
+ // Draw each handle
9599
+ this.canvasContext.fillStyle = '#0070F4'; // Primary color
9600
+ this.canvasContext.strokeStyle = '#ffffff';
9601
+ this.canvasContext.lineWidth = 2;
9602
+ handles.forEach(handle => {
9603
+ this.canvasContext.fillRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
9604
+ this.canvasContext.strokeRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
9605
+ });
9606
+ };
9607
+ // Draw resize handles for line/arrow annotation
9608
+ this.drawLineResizeHandles = (annotation) => {
9609
+ if (!this.canvasContext || (annotation.type !== 'line' && annotation.type !== 'arrow'))
9610
+ return;
9611
+ const handleSize = 8;
9612
+ // Define handle positions (2 endpoints)
9613
+ const handles = [
9614
+ { x: annotation.startX, y: annotation.startY },
9615
+ { x: annotation.endX, y: annotation.endY } // End point
9616
+ ];
9617
+ // Draw each handle
9618
+ this.canvasContext.fillStyle = '#0070F4'; // Primary color
9619
+ this.canvasContext.strokeStyle = '#ffffff';
9620
+ this.canvasContext.lineWidth = 2;
9621
+ handles.forEach(handle => {
9622
+ this.canvasContext.fillRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
9623
+ this.canvasContext.strokeRect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize);
9624
+ });
9625
+ };
9626
+ // Convert screen coordinates to canvas coordinates
9627
+ this.getCanvasCoordinates = (event) => {
9628
+ if (!this.canvasRef)
9629
+ return { x: 0, y: 0 };
9630
+ const rect = this.canvasRef.getBoundingClientRect();
9631
+ // Calculate the scale factor between display size and actual canvas size
9632
+ const scaleX = this.canvasRef.width / rect.width;
9633
+ const scaleY = this.canvasRef.height / rect.height;
9634
+ const x = (event.clientX - rect.left) * scaleX;
9635
+ const y = (event.clientY - rect.top) * scaleY;
9636
+ return { x, y };
9637
+ };
9638
+ // Find annotation under mouse cursor
9639
+ this.findAnnotationAt = (x, y) => {
9640
+ // Check in reverse order (top to bottom)
9641
+ for (let i = this.annotations.length - 1; i >= 0; i--) {
9642
+ const annotation = this.annotations[i];
9643
+ if (this.isPointInAnnotation(x, y, annotation)) {
9644
+ return { annotation, index: i };
9645
+ }
9646
+ }
9647
+ return null;
9648
+ };
9649
+ // Check if point is within annotation bounds
9650
+ this.isPointInAnnotation = (x, y, annotation) => {
9651
+ const tolerance = 10; // Click tolerance
9652
+ switch (annotation.type) {
9653
+ case 'rectangle':
9654
+ const left = Math.min(annotation.startX, annotation.startX + annotation.width);
9655
+ const right = Math.max(annotation.startX, annotation.startX + annotation.width);
9656
+ const top = Math.min(annotation.startY, annotation.startY + annotation.height);
9657
+ const bottom = Math.max(annotation.startY, annotation.startY + annotation.height);
9658
+ return x >= left - tolerance && x <= right + tolerance &&
9659
+ y >= top - tolerance && y <= bottom + tolerance;
9660
+ case 'line':
9661
+ case 'arrow':
9662
+ // Distance from point to line
9663
+ const A = annotation.endY - annotation.startY;
9664
+ const B = annotation.startX - annotation.endX;
9665
+ const C = annotation.endX * annotation.startY - annotation.startX * annotation.endY;
9666
+ const distance = Math.abs(A * x + B * y + C) / Math.sqrt(A * A + B * B);
9667
+ return distance <= tolerance;
9668
+ case 'text':
9669
+ // Simple bounding box for text
9670
+ return x >= annotation.x - tolerance && x <= annotation.x + 100 &&
9671
+ y >= annotation.y - 20 && y <= annotation.y + tolerance;
9672
+ default:
9673
+ return false;
9674
+ }
9675
+ };
9676
+ // Handle resize for different annotation types
9677
+ this.handleResize = (currentPos) => {
9678
+ if (!this.resizingAnnotation || !this.dragStartPos)
9679
+ return;
9680
+ const annotation = this.resizingAnnotation;
9681
+ const index = this.annotations.findIndex(a => a === annotation);
9682
+ if (index === -1)
9683
+ return;
9684
+ let updatedAnnotation = Object.assign({}, annotation);
9685
+ switch (annotation.type) {
9686
+ case 'text':
9687
+ // Text resize logic (existing)
9688
+ const deltaX = currentPos.x - this.dragStartPos.x;
9689
+ const deltaY = currentPos.y - this.dragStartPos.y;
9690
+ const avgDelta = (deltaX + deltaY) / 2;
9691
+ const newSize = Math.max(8, Math.min(72, this.resizeStartSize + avgDelta * 0.5));
9692
+ updatedAnnotation.fontSize = Math.round(newSize);
9693
+ break;
9694
+ case 'rectangle':
9695
+ // Rectangle resizing disabled for now
9696
+ return;
9697
+ case 'line':
9698
+ case 'arrow':
9699
+ // Line/arrow resize logic - move endpoints
9700
+ if (this.resizeHandle === 'start') {
9701
+ updatedAnnotation.startX = currentPos.x;
9702
+ updatedAnnotation.startY = currentPos.y;
9703
+ }
9704
+ else if (this.resizeHandle === 'end') {
9705
+ updatedAnnotation.endX = currentPos.x;
9706
+ updatedAnnotation.endY = currentPos.y;
9707
+ }
9708
+ break;
9709
+ }
9710
+ // Update annotation in array
9711
+ this.annotations[index] = updatedAnnotation;
9712
+ this.resizingAnnotation = updatedAnnotation;
9713
+ this.redrawAnnotations();
9714
+ };
9173
9715
  this.sending = false;
9174
9716
  this.formMessage = '';
9175
9717
  this.formEmail = '';
@@ -9183,6 +9725,26 @@ const FeedbackModal = class {
9183
9725
  this.selectedRating = -1;
9184
9726
  this.overlayVisible = false;
9185
9727
  this.isAnimating = false;
9728
+ this.takingScreenshot = false;
9729
+ this.showPreviewModal = false;
9730
+ this.showCanvasEditor = false;
9731
+ this.canvasDrawingTool = 'rectangle';
9732
+ this.canvasDrawingColor = '#ff0000';
9733
+ this.canvasLineWidth = 3;
9734
+ this.isDrawing = false;
9735
+ this.annotations = [];
9736
+ this.currentAnnotation = null;
9737
+ this.isDragging = false;
9738
+ this.draggedAnnotation = null;
9739
+ this.dragStartPos = null;
9740
+ this.showColorPicker = false;
9741
+ this.editingColorIndex = -1;
9742
+ this.isResizing = false;
9743
+ this.resizingAnnotation = null;
9744
+ this.resizeStartSize = 16;
9745
+ this.hoveredAnnotation = null;
9746
+ this.resizeHandle = false;
9747
+ this.defaultColors = ['#ff0000', '#00ff00', '#0000ff', '#000000'];
9186
9748
  this.customFont = false;
9187
9749
  this.emailAddress = '';
9188
9750
  this.hideEmail = false;
@@ -9213,10 +9775,14 @@ const FeedbackModal = class {
9213
9775
  this.ratingPlaceholder = 'Was this page helpful?';
9214
9776
  this.ratingStarsPlaceholder = 'How would you rate this page?';
9215
9777
  this.sendButtonText = 'Send';
9216
- this.screenshotButtonText = 'Add a screenshot';
9217
9778
  this.screenshotAttachedText = 'Screenshot attached';
9779
+ this.screenshotButtonText = 'Add a screenshot';
9780
+ this.screenshotTakingText = 'Taking screenshot...';
9218
9781
  this.screenshotTopbarText = 'Select an element on this page';
9219
9782
  this.successMessage = '';
9783
+ this.canvasEditorTitle = 'Edit screenshot';
9784
+ this.canvasEditorCancelText = 'Cancel';
9785
+ this.canvasEditorSaveText = 'Save';
9220
9786
  }
9221
9787
  componentWillLoad() {
9222
9788
  if (this.fetchData)
@@ -9240,15 +9806,10 @@ const FeedbackModal = class {
9240
9806
  }
9241
9807
  }
9242
9808
  resetOverflow() {
9809
+ // Just clean up any stray classes, don't add/remove during screenshot
9243
9810
  document.documentElement.classList.remove('feedback-modal-screenshot-open');
9244
9811
  document.documentElement.classList.remove('feedback-modal-screenshot-open--scroll');
9245
- document.documentElement.classList.add('feedback-modal-screenshot-closing');
9246
- // Only restore scroll position if we previously modified it
9247
- if (document.documentElement.style.top) {
9248
- window.scrollTo(0, parseInt(document.documentElement.style.top || '0') * -1);
9249
- document.documentElement.style.top = '';
9250
- }
9251
- window.addEventListener('scroll', this.onScrollDebounced);
9812
+ document.documentElement.classList.remove('feedback-modal-screenshot-closing');
9252
9813
  }
9253
9814
  handleMessageInput(event) {
9254
9815
  this.formMessage = event.target.value;
@@ -9256,31 +9817,48 @@ const FeedbackModal = class {
9256
9817
  handleEmailInput(event) {
9257
9818
  this.formEmail = event.target.value;
9258
9819
  }
9259
- captureScreenshot() {
9820
+ captureViewportScreenshot() {
9260
9821
  return new Promise((resolve, reject) => {
9261
9822
  requestAnimationFrame(() => {
9262
- html2canvasPro(document.body, {
9263
- x: window.scrollX,
9264
- y: window.scrollY,
9265
- width: window.innerWidth,
9266
- height: window.innerHeight,
9267
- allowTaint: true,
9823
+ // Get viewport dimensions and scroll position
9824
+ const viewportWidth = window.innerWidth;
9825
+ const viewportHeight = window.innerHeight;
9826
+ const scrollX = window.scrollX || window.pageXOffset || 0;
9827
+ const scrollY = window.scrollY || window.pageYOffset || 0;
9828
+ // Capture exactly what the user sees in their viewport
9829
+ html2canvasPro(document.documentElement, {
9830
+ x: scrollX,
9831
+ y: scrollY,
9832
+ width: viewportWidth,
9833
+ height: viewportHeight,
9834
+ scrollX: 0,
9835
+ scrollY: 0,
9836
+ allowTaint: false,
9268
9837
  useCORS: true,
9269
9838
  scale: 1,
9839
+ backgroundColor: '#ffffff',
9840
+ logging: false,
9841
+ foreignObjectRendering: false,
9842
+ imageTimeout: 15000,
9843
+ windowWidth: viewportWidth,
9844
+ windowHeight: viewportHeight,
9845
+ removeContainer: true,
9846
+ ignoreElements: (element) => {
9847
+ // Ignore all feedback modal elements
9848
+ return element.closest('feedback-modal') !== null ||
9849
+ element.classList.contains('feedback-overlay') ||
9850
+ element.classList.contains('feedback-modal-screenshot-header') ||
9851
+ element.tagName === 'FEEDBACK-MODAL' ||
9852
+ element.tagName === 'FEEDBACK-BUTTON';
9853
+ }
9270
9854
  })
9271
9855
  .then((canvas) => {
9272
- const context = canvas.getContext('2d');
9273
- if (context && this.selectedElementBounds) {
9274
- // Use the same color as HTML highlight
9275
- context.strokeStyle = 'rgba(0, 123, 255, 0.8)'; // Example color
9276
- context.lineWidth = 3;
9277
- context.strokeRect(this.selectedElementBounds.left + window.scrollX, this.selectedElementBounds.top + window.scrollY, this.selectedElementBounds.width, this.selectedElementBounds.height);
9278
- }
9279
- const dataUrl = canvas.toDataURL();
9856
+ const dataUrl = canvas.toDataURL('image/png');
9857
+ console.log('Screenshot captured successfully, size:', canvas.width, 'x', canvas.height);
9280
9858
  resolve(dataUrl);
9281
9859
  })
9282
9860
  .catch((error) => {
9283
- console.error('Failed to capture screenshot:', error);
9861
+ console.error('Failed to capture viewport screenshot:', error);
9284
9862
  reject(error);
9285
9863
  });
9286
9864
  });
@@ -9295,12 +9873,8 @@ const FeedbackModal = class {
9295
9873
  handleRatingChange(newRating) {
9296
9874
  this.selectedRating = newRating;
9297
9875
  }
9298
- // Remove the preview modal toggle function
9299
- // togglePreviewModal() {
9300
- // this.showPreviewModal = !this.showPreviewModal;
9301
- // }
9302
9876
  render() {
9303
- return (h("div", { class: `feedback-modal-wrapper ${this.customFont ? 'feedback-modal-wrapper--custom-font' : ''}` }, this.showScreenshotMode && (h("div", { class: "feedback-modal-screenshot", ref: (el) => (this.screenshotModal = el), onMouseMove: this.handleMouseOverScreenShot }, h("div", { class: "feedback-modal-screenshot-element-selected", ref: (el) => (this.elementSelected = el), onClick: this.handleMouseClickedSelectedElement }), h("div", { class: "top-side", ref: (el) => (this.topSide = el) }), h("div", { class: "left-side", ref: (el) => (this.leftSide = el) }), h("div", { class: "bottom-side", ref: (el) => (this.bottomSide = el) }), h("div", { class: "right-side", ref: (el) => (this.rightSide = el) }), this.showScreenshotTopBar && (h("div", { class: "feedback-modal-screenshot-header", onClick: this.closeScreenShot }, h("span", null, this.screenshotTopbarText), h("span", { class: "feedback-modal-screenshot-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" }))))))), 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
9877
+ return (h("div", { class: `feedback-modal-wrapper ${this.customFont ? 'feedback-modal-wrapper--custom-font' : ''}` }, 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
9304
9878
  ? 'feedback-modal-rating-button--selected'
9305
9879
  : ''}`, onClick: (event) => {
9306
9880
  event.preventDefault();
@@ -9315,7 +9889,8 @@ const FeedbackModal = class {
9315
9889
  : ''}`, onClick: (event) => {
9316
9890
  event.preventDefault();
9317
9891
  this.handleRatingChange(rating);
9318
- } }, 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.encodedScreenshot && (h("div", { class: "screenshot-preview" }, h("img", { src: this.encodedScreenshot, alt: "Screenshot Preview" }))), !this.encodedScreenshot && (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.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 }))))))));
9892
+ } }, 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 16 16" }, h("circle", { cx: "8", cy: "8", r: "6", fill: "none", stroke: "#666", "stroke-width": "2", "stroke-dasharray": "6 6", "transform-origin": "8 8" }, h("animateTransform", { attributeName: "transform", type: "rotate", values: "0 8 8;360 8 8", dur: "1s", repeatCount: "indefinite" }))))), this.takingScreenshot ? this.screenshotTakingText :
9893
+ 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 }))))))));
9319
9894
  }
9320
9895
  componentDidRender() {
9321
9896
  if (this.showModal) {