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