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