pushfeedback 0.1.67 → 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.';
@@ -90,6 +93,9 @@ const FeedbackButton = class {
90
93
  'project',
91
94
  'rating',
92
95
  'ratingMode',
96
+ 'canvasEditorTitle',
97
+ 'canvasEditorCancelText',
98
+ 'canvasEditorSaveText',
93
99
  'emailPlaceholder',
94
100
  'errorMessage',
95
101
  'errorMessage403',
@@ -203,14 +209,14 @@ function commonjsRequire () {
203
209
 
204
210
  var html2canvasPro = createCommonjsModule(function (module, exports) {
205
211
  /*!
206
- * html2canvas-pro 1.5.8 <https://yorickshan.github.io/html2canvas-pro/>
207
- * 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
208
214
  * Released under MIT License
209
215
  */
210
216
  (function (global, factory) {
211
217
  module.exports = factory() ;
212
218
  })(commonjsGlobal, (function () {
213
- /*! *****************************************************************************
219
+ /******************************************************************************
214
220
  Copyright (c) Microsoft Corporation.
215
221
 
216
222
  Permission to use, copy, modify, and/or distribute this software for any
@@ -224,7 +230,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
224
230
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
225
231
  PERFORMANCE OF THIS SOFTWARE.
226
232
  ***************************************************************************** */
227
- /* global Reflect, Promise */
233
+ /* global Reflect, Promise, SuppressedError, Symbol */
228
234
 
229
235
  var extendStatics = function(d, b) {
230
236
  extendStatics = Object.setPrototypeOf ||
@@ -268,7 +274,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
268
274
  function verb(n) { return function (v) { return step([n, v]); }; }
269
275
  function step(op) {
270
276
  if (f) throw new TypeError("Generator is already executing.");
271
- while (_) try {
277
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
272
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;
273
279
  if (y = 0, t) op = [op[0] & 2, t.value];
274
280
  switch (op[0]) {
@@ -297,8 +303,13 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
297
303
  ar[i] = from[i];
298
304
  }
299
305
  }
300
- return to.concat(ar || from);
301
- }
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
+ };
302
313
 
303
314
  var Bounds = /** @class */ (function () {
304
315
  function Bounds(left, top, width, height) {
@@ -3933,7 +3944,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
3933
3944
  case 'gurmukhi':
3934
3945
  return 22 /* LIST_STYLE_TYPE.GURMUKHI */;
3935
3946
  case 'hebrew':
3936
- return 22 /* LIST_STYLE_TYPE.HEBREW */;
3947
+ return 52 /* LIST_STYLE_TYPE.HEBREW */;
3937
3948
  case 'hiragana':
3938
3949
  return 23 /* LIST_STYLE_TYPE.HIRAGANA */;
3939
3950
  case 'hiragana-iroha':
@@ -6209,7 +6220,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
6209
6220
  return createCounterStyleFromRange(value, 0xae6, 0xaef, true, defaultSuffix);
6210
6221
  case 22 /* LIST_STYLE_TYPE.GURMUKHI */:
6211
6222
  return createCounterStyleFromRange(value, 0xa66, 0xa6f, true, defaultSuffix);
6212
- case 22 /* LIST_STYLE_TYPE.HEBREW */:
6223
+ case 52 /* LIST_STYLE_TYPE.HEBREW */:
6213
6224
  return createAdditiveCounter(value, 1, 10999, HEBREW, 3 /* LIST_STYLE_TYPE.DECIMAL */, defaultSuffix);
6214
6225
  case 23 /* LIST_STYLE_TYPE.HIRAGANA */:
6215
6226
  return createCounterStyleFromSymbols(value, 'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわゐゑをん');
@@ -6668,8 +6679,8 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
6668
6679
  });
6669
6680
  };
6670
6681
  var ignoredStyleProperties = [
6671
- 'all',
6672
- 'd',
6682
+ 'all', // #2476
6683
+ 'd', // #2483
6673
6684
  'content' // Safari shows pseudoelements if content is set
6674
6685
  ];
6675
6686
  var copyCSSStyles = function (style, target) {
@@ -6786,12 +6797,21 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
6786
6797
  };
6787
6798
  Cache.prototype.loadImage = function (key) {
6788
6799
  return __awaiter(this, void 0, void 0, function () {
6789
- var isSameOrigin, useCORS, useProxy, src;
6800
+ var isSameOrigin, _a, useCORS, useProxy, src;
6790
6801
  var _this = this;
6791
- return __generator(this, function (_a) {
6792
- switch (_a.label) {
6802
+ return __generator(this, function (_b) {
6803
+ switch (_b.label) {
6793
6804
  case 0:
6794
- 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;
6795
6815
  useCORS = !isInlineImage(key) && this._options.useCORS === true && FEATURES.SUPPORT_CORS_IMAGES && !isSameOrigin;
6796
6816
  useProxy = !isInlineImage(key) &&
6797
6817
  !isSameOrigin &&
@@ -6808,12 +6828,12 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
6808
6828
  return [2 /*return*/];
6809
6829
  }
6810
6830
  src = key;
6811
- if (!useProxy) return [3 /*break*/, 2];
6831
+ if (!useProxy) return [3 /*break*/, 5];
6812
6832
  return [4 /*yield*/, this.proxy(src)];
6813
- case 1:
6814
- src = _a.sent();
6815
- _a.label = 2;
6816
- case 2:
6833
+ case 4:
6834
+ src = _b.sent();
6835
+ _b.label = 5;
6836
+ case 5:
6817
6837
  this.context.logger.debug("Added image ".concat(key.substring(0, 256)));
6818
6838
  return [4 /*yield*/, new Promise(function (resolve, reject) {
6819
6839
  var img = new Image();
@@ -6832,7 +6852,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
6832
6852
  setTimeout(function () { return reject("Timed out (".concat(_this._options.imageTimeout, "ms) loading image")); }, _this._options.imageTimeout);
6833
6853
  }
6834
6854
  })];
6835
- case 3: return [2 /*return*/, _a.sent()];
6855
+ case 6: return [2 /*return*/, _b.sent()];
6836
6856
  }
6837
6857
  });
6838
6858
  });
@@ -7958,7 +7978,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
7958
7978
  };
7959
7979
  CanvasRenderer.prototype.renderNodeContent = function (paint) {
7960
7980
  return __awaiter(this, void 0, void 0, function () {
7961
- 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;
7962
7982
  return __generator(this, function (_c) {
7963
7983
  switch (_c.label) {
7964
7984
  case 0:
@@ -8058,9 +8078,9 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
8058
8078
  }
8059
8079
  }
8060
8080
  if (isTextInputElement(container) && container.value.length) {
8061
- _b = this.createFontStyle(styles), fontFamily = _b[0], fontSize = _b[1];
8081
+ _b = this.createFontStyle(styles), font = _b[0], fontFamily = _b[1], fontSize = _b[2];
8062
8082
  baseline = this.fontMetrics.getMetrics(fontFamily, fontSize).baseline;
8063
- this.ctx.font = fontFamily;
8083
+ this.ctx.font = font;
8064
8084
  this.ctx.fillStyle = asString(styles.color);
8065
8085
  this.ctx.textBaseline = 'alphabetic';
8066
8086
  this.ctx.textAlign = canvasTextAlign(container.styles.textAlign);
@@ -8109,8 +8129,8 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
8109
8129
  case 18: return [3 /*break*/, 20];
8110
8130
  case 19:
8111
8131
  if (paint.listValue && container.styles.listStyleType !== -1 /* LIST_STYLE_TYPE.NONE */) {
8112
- fontFamily = this.createFontStyle(styles)[0];
8113
- this.ctx.font = fontFamily;
8132
+ font = this.createFontStyle(styles)[0];
8133
+ this.ctx.font = font;
8114
8134
  this.ctx.fillStyle = asString(styles.color);
8115
8135
  this.ctx.textBaseline = 'middle';
8116
8136
  this.ctx.textAlign = 'right';
@@ -8340,7 +8360,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
8340
8360
  canvas.height = height;
8341
8361
  ctx = canvas.getContext('2d');
8342
8362
  gradient_1 = ctx.createLinearGradient(x0, y0, x1, y1);
8343
- processColorStops(backgroundImage.stops, lineLength).forEach(function (colorStop) {
8363
+ processColorStops(backgroundImage.stops, lineLength || 1).forEach(function (colorStop) {
8344
8364
  return gradient_1.addColorStop(colorStop.stop, asString(colorStop.color));
8345
8365
  });
8346
8366
  ctx.fillStyle = gradient_1;
@@ -8875,7 +8895,8 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
8875
8895
  allowTaint: (_b = opts.allowTaint) !== null && _b !== void 0 ? _b : false,
8876
8896
  imageTimeout: (_c = opts.imageTimeout) !== null && _c !== void 0 ? _c : 15000,
8877
8897
  proxy: opts.proxy,
8878
- 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
8879
8900
  };
8880
8901
  contextOptions = __assign({ logging: (_e = opts.logging) !== null && _e !== void 0 ? _e : true, cache: opts.cache }, resourceOptions);
8881
8902
  windowOptions = {
@@ -8976,7 +8997,7 @@ var html2canvasPro = createCommonjsModule(function (module, exports) {
8976
8997
  //# sourceMappingURL=html2canvas-pro.js.map
8977
8998
  });
8978
8999
 
8979
- 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}.preview-modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background-color:rgba(0, 0, 0, 0.8);z-index:10000;display:flex;align-items:center;justify-content:center;cursor:pointer}.preview-modal{position:relative;max-width:90vw;max-height:90vh;border-radius:8px;overflow:hidden;cursor:default}.preview-modal img{max-width:100%;max-height:100%;display:block}@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}}";
8980
9001
 
8981
9002
  const FeedbackModal = class {
8982
9003
  constructor(hostRef) {
@@ -9067,144 +9088,634 @@ const FeedbackModal = class {
9067
9088
  document.querySelectorAll('.feedback-modal-element-selected').forEach(el => {
9068
9089
  el.classList.remove('feedback-modal-element-selected');
9069
9090
  });
9091
+ // Reset canvas editor states
9070
9092
  this.takingScreenshot = false;
9071
- this.originalElement = null;
9072
- this.selectedElementBounds = null;
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
9073
9108
  this.formSuccess = false;
9074
9109
  this.formError = false;
9075
9110
  this.formErrorStatus = 500;
9076
9111
  this.formMessage = '';
9077
9112
  this.formEmail = '';
9078
- this.showPreviewModal = false;
9079
9113
  this.resetOverflow();
9080
9114
  }, 200);
9081
9115
  };
9082
- this.openScreenShot = () => {
9083
- this.hasSelectedElement = false;
9084
- this.showModal = false;
9085
- this.showScreenshotMode = true;
9086
- this.showScreenshotTopBar = true;
9087
- // Clear previous screenshot and selection data
9088
- this.encodedScreenshot = null;
9089
- this.originalElement = null;
9090
- this.selectedElementBounds = null;
9091
- this.hoveredElement = null;
9092
- this.hoveredElementBounds = null;
9093
- // NO CSS CLASSES - they cause scroll jumping
9094
- };
9095
- this.closeScreenShot = () => {
9096
- // Remove highlight from ALL selected elements
9097
- document.querySelectorAll('.feedback-modal-element-selected').forEach(el => {
9098
- el.classList.remove('feedback-modal-element-selected');
9099
- });
9100
- // Reset loading state
9101
- this.takingScreenshot = false;
9102
- this.showModal = false;
9103
- this.showScreenshotMode = false;
9104
- this.showScreenshotTopBar = false;
9105
- };
9106
- this.openPreviewModal = (event) => {
9107
- event.stopPropagation(); // Prevent button click from firing
9108
- this.showPreviewModal = true;
9109
- };
9110
- this.closePreviewModal = () => {
9111
- this.showPreviewModal = false;
9112
- };
9113
- this.handleMouseOverScreenShot = (event) => {
9114
- event.preventDefault();
9115
- if (this.hasSelectedElement)
9116
- return;
9117
- const borderOffset = 2;
9118
- this.screenshotModal.style.display = 'none';
9119
- const elementUnder = document.elementFromPoint(event.clientX, event.clientY);
9120
- const rect = elementUnder.getBoundingClientRect();
9121
- this.screenshotModal.style.display = '';
9122
- // Store the hovered element and its bounds for later use
9123
- this.hoveredElement = elementUnder;
9124
- this.hoveredElementBounds = rect;
9125
- // Get the bounding box of the element selected
9126
- this.elementSelected.style.position = 'absolute';
9127
- this.elementSelected.style.left = `${rect.left}px`;
9128
- this.elementSelected.style.top = `${rect.top}px`;
9129
- this.elementSelected.style.width = `${rect.width}px`;
9130
- this.elementSelected.style.height = `${rect.height}px`;
9131
- this.elementSelected.classList.add('feedback-modal-element-hover');
9132
- // Set the background color of nonselected areas
9133
- // Top
9134
- this.topSide.style.position = 'absolute';
9135
- this.topSide.style.left = `${rect.left}px`;
9136
- this.topSide.style.top = '0px';
9137
- this.topSide.style.width = `${rect.width + borderOffset}px`;
9138
- this.topSide.style.height = `${rect.top}px`;
9139
- this.topSide.style.backgroundColor = 'rgba(0, 0, 0, 0.4)';
9140
- // Left
9141
- this.leftSide.style.position = 'absolute';
9142
- this.leftSide.style.left = '0px';
9143
- this.leftSide.style.top = '0px';
9144
- this.leftSide.style.width = `${rect.left}px`;
9145
- this.leftSide.style.height = '100vh';
9146
- this.leftSide.style.backgroundColor = 'rgba(0, 0, 0, 0.4)';
9147
- // Bottom
9148
- this.bottomSide.style.position = 'absolute';
9149
- this.bottomSide.style.left = `${rect.left}px`;
9150
- this.bottomSide.style.top = `${rect.bottom + borderOffset}px`;
9151
- this.bottomSide.style.width = `${rect.width + borderOffset}px`;
9152
- this.bottomSide.style.height = '100vh';
9153
- this.bottomSide.style.backgroundColor = 'rgba(0, 0, 0, 0.4)';
9154
- // Right
9155
- this.rightSide.style.position = 'absolute';
9156
- this.rightSide.style.left = `${rect.right + borderOffset}px`;
9157
- this.rightSide.style.top = '0px';
9158
- this.rightSide.style.width = '100%';
9159
- this.rightSide.style.height = '100vh';
9160
- this.rightSide.style.backgroundColor = 'rgba(0, 0, 0, 0.4)';
9161
- // Restore the visibility of the screenshot-modal
9162
- this.screenshotModal.style.backgroundColor = 'transparent';
9163
- };
9164
- this.handleMouseClickedSelectedElement = async (event) => {
9165
- event.preventDefault();
9166
- if (!this.elementSelected || !this.hoveredElement) {
9167
- return;
9168
- }
9169
- this.hasSelectedElement = true;
9170
- // Remove highlight from ALL previously selected elements
9171
- document.querySelectorAll('.feedback-modal-element-selected').forEach(el => {
9172
- el.classList.remove('feedback-modal-element-selected');
9173
- });
9174
- // Add highlight to newly selected element
9175
- this.hoveredElement.classList.add('feedback-modal-element-selected');
9176
- // Store element bounds in viewport coordinates
9177
- this.selectedElementBounds = this.hoveredElementBounds;
9178
- this.originalElement = this.hoveredElement;
9179
- // Show loading state in top bar
9116
+ this.openScreenShot = async () => {
9117
+ // Show loading state immediately
9180
9118
  this.takingScreenshot = true;
9181
- // Take screenshot FIRST while highlight is still visible
9182
9119
  try {
9183
- const dataUrl = await this.captureScreenshot();
9184
- console.log('Screenshot captured');
9120
+ // Capture viewport screenshot immediately
9121
+ const dataUrl = await this.captureViewportScreenshot();
9185
9122
  this.encodedScreenshot = dataUrl;
9123
+ this.originalImageData = dataUrl;
9186
9124
  // Reset loading state
9187
9125
  this.takingScreenshot = false;
9188
- // NOW hide screenshot interface after capturing
9189
- this.showScreenshotTopBar = false;
9190
- this.showScreenshotMode = false;
9191
- // Restore normal page state
9192
- this.resetOverflow();
9193
- // Show modal with the captured screenshot
9194
- this.showModal = true;
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);
9195
9133
  }
9196
9134
  catch (error) {
9197
9135
  console.error('Failed to capture screenshot:', error);
9198
- this.hasSelectedElement = false;
9199
9136
  // Reset loading state on error
9200
9137
  this.takingScreenshot = false;
9201
- // Still need to cleanup on error
9202
- this.showScreenshotTopBar = false;
9203
- this.showScreenshotMode = false;
9204
- this.resetOverflow();
9138
+ // Show modal anyway
9205
9139
  this.showModal = true;
9206
9140
  }
9207
9141
  };
9142
+ this.openCanvasEditor = (event) => {
9143
+ if (event) {
9144
+ event.stopPropagation();
9145
+ }
9146
+ this.showModal = false;
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;
9165
+ };
9166
+ this.initializeCanvas = () => {
9167
+ if (!this.canvasRef || !this.originalImageData)
9168
+ return;
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)
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;
9262
+ }
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;
9307
+ }
9308
+ else {
9309
+ // Start editing this color slot
9310
+ this.editingColorIndex = colorIndex;
9311
+ this.showColorPicker = true;
9312
+ this.canvasDrawingColor = this.defaultColors[colorIndex];
9313
+ }
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];
9324
+ }
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
+ };
9208
9719
  this.sending = false;
9209
9720
  this.formMessage = '';
9210
9721
  this.formEmail = '';
@@ -9218,6 +9729,26 @@ const FeedbackModal = class {
9218
9729
  this.selectedRating = -1;
9219
9730
  this.overlayVisible = false;
9220
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'];
9221
9752
  this.customFont = false;
9222
9753
  this.emailAddress = '';
9223
9754
  this.hideEmail = false;
@@ -9253,8 +9784,9 @@ const FeedbackModal = class {
9253
9784
  this.screenshotTakingText = 'Taking screenshot...';
9254
9785
  this.screenshotTopbarText = 'Select an element on this page';
9255
9786
  this.successMessage = '';
9256
- this.takingScreenshot = false;
9257
- this.showPreviewModal = false;
9787
+ this.canvasEditorTitle = 'Edit screenshot';
9788
+ this.canvasEditorCancelText = 'Cancel';
9789
+ this.canvasEditorSaveText = 'Save';
9258
9790
  }
9259
9791
  componentWillLoad() {
9260
9792
  if (this.fetchData)
@@ -9289,46 +9821,51 @@ const FeedbackModal = class {
9289
9821
  handleEmailInput(event) {
9290
9822
  this.formEmail = event.target.value;
9291
9823
  }
9292
- captureScreenshot() {
9824
+ captureViewportScreenshot() {
9293
9825
  return new Promise((resolve, reject) => {
9294
- // Add a small delay to ensure CSS highlight is applied
9295
- setTimeout(() => {
9296
- requestAnimationFrame(() => {
9297
- if (!this.selectedElementBounds) {
9298
- reject(new Error('No element selected'));
9299
- return;
9826
+ requestAnimationFrame(() => {
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,
9841
+ useCORS: true,
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';
9300
9857
  }
9301
- // Capture what's currently visible in the viewport
9302
- html2canvasPro(document.body, {
9303
- x: window.scrollX,
9304
- y: window.scrollY,
9305
- width: window.innerWidth,
9306
- height: window.innerHeight,
9307
- scrollX: window.scrollX,
9308
- scrollY: window.scrollY,
9309
- allowTaint: false,
9310
- useCORS: true,
9311
- scale: 1,
9312
- backgroundColor: '#ffffff',
9313
- logging: true,
9314
- foreignObjectRendering: false,
9315
- imageTimeout: 10000,
9316
- ignoreElements: (element) => {
9317
- // Only ignore screenshot UI, keep everything else including highlights
9318
- return element.classList.contains('feedback-modal-screenshot-header') ||
9319
- element.classList.contains('feedback-overlay');
9320
- }
9321
- })
9322
- .then((canvas) => {
9323
- const dataUrl = canvas.toDataURL();
9324
- resolve(dataUrl);
9325
- })
9326
- .catch((error) => {
9327
- console.error('Failed to capture screenshot:', error);
9328
- reject(error);
9329
- });
9858
+ })
9859
+ .then((canvas) => {
9860
+ const dataUrl = canvas.toDataURL('image/png');
9861
+ console.log('Screenshot captured successfully, size:', canvas.width, 'x', canvas.height);
9862
+ resolve(dataUrl);
9863
+ })
9864
+ .catch((error) => {
9865
+ console.error('Failed to capture viewport screenshot:', error);
9866
+ reject(error);
9330
9867
  });
9331
- }, 100); // Small delay to ensure CSS is applied
9868
+ });
9332
9869
  });
9333
9870
  }
9334
9871
  handleCheckboxChange(event) {
@@ -9341,7 +9878,7 @@ const FeedbackModal = class {
9341
9878
  this.selectedRating = newRating;
9342
9879
  }
9343
9880
  render() {
9344
- 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.takingScreenshot ? this.screenshotTakingText : 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
9345
9882
  ? 'feedback-modal-rating-button--selected'
9346
9883
  : ''}`, onClick: (event) => {
9347
9884
  event.preventDefault();
@@ -9356,7 +9893,8 @@ const FeedbackModal = class {
9356
9893
  : ''}`, onClick: (event) => {
9357
9894
  event.preventDefault();
9358
9895
  this.handleRatingChange(rating);
9359
- } }, 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", onClick: this.openPreviewModal }, 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 })))))), this.showPreviewModal && (index.h("div", { class: "preview-modal-overlay", onClick: this.closePreviewModal }, index.h("div", { class: "preview-modal" }, index.h("img", { src: this.encodedScreenshot, alt: "Screenshot Preview", onClick: (e) => e.stopPropagation() }))))));
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 }))))))));
9360
9898
  }
9361
9899
  componentDidRender() {
9362
9900
  if (this.showModal) {