pushfeedback 0.1.83 → 0.1.85

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.
@@ -0,0 +1,90 @@
1
+ // Tags allowed in the rich-text props (privacy policy, reCAPTCHA notice,
2
+ // footer). These only ever need links and light inline emphasis.
3
+ const ALLOWED_TAGS = new Set([
4
+ 'A',
5
+ 'B',
6
+ 'BR',
7
+ 'CODE',
8
+ 'EM',
9
+ 'I',
10
+ 'P',
11
+ 'SMALL',
12
+ 'SPAN',
13
+ 'STRONG',
14
+ 'U',
15
+ ]);
16
+ // No `class`: an injected class name cannot reach the host page's CSS (this
17
+ // renders inside a shadow root), but it could match the widget's own internal
18
+ // styles. The default prop values do not use classes, so dropping it is free.
19
+ const ALLOWED_ATTRS = new Set(['href', 'target', 'rel', 'title']);
20
+ // Schemes permitted in href. Anything else, notably javascript:, is dropped.
21
+ const SAFE_URL = /^(https?:|mailto:|tel:|#|\/|\.\/|\.\.\/)/i;
22
+ /**
23
+ * Removes anything that can execute from a fragment: script and style
24
+ * elements, event-handler attributes, javascript: URLs, and any tag not on the
25
+ * allowlist. Text inside a disallowed tag is kept, so stripping a wrapper does
26
+ * not silently delete the words inside it.
27
+ */
28
+ function scrub(root) {
29
+ const elements = Array.from(root.querySelectorAll('*'));
30
+ for (const el of elements) {
31
+ // The node may already have been removed along with an ancestor
32
+ if (!root.contains(el)) {
33
+ continue;
34
+ }
35
+ const tag = el.tagName.toUpperCase();
36
+ if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'IFRAME' || tag === 'OBJECT') {
37
+ el.remove();
38
+ continue;
39
+ }
40
+ if (!ALLOWED_TAGS.has(tag)) {
41
+ // Unwrap: keep the text, drop the element
42
+ el.replaceWith(...Array.from(el.childNodes));
43
+ continue;
44
+ }
45
+ for (const attr of Array.from(el.attributes)) {
46
+ const name = attr.name.toLowerCase();
47
+ if (name.startsWith('on') || !ALLOWED_ATTRS.has(name)) {
48
+ el.removeAttribute(attr.name);
49
+ continue;
50
+ }
51
+ if (name === 'href' && !SAFE_URL.test(attr.value.trim())) {
52
+ el.removeAttribute(attr.name);
53
+ }
54
+ }
55
+ // Links opening a new tab must not hand the opener over
56
+ if (tag === 'A' && el.getAttribute('target') === '_blank') {
57
+ el.setAttribute('rel', 'noopener noreferrer');
58
+ }
59
+ }
60
+ }
61
+ /**
62
+ * Sanitizes a fragment of HTML for use with innerHTML.
63
+ *
64
+ * Prefers the browser's native Element.setHTML() where available, which parses
65
+ * without executing. Otherwise parses in an inert document (no scripts run, no
66
+ * images or other subresources are fetched) and scrubs the result.
67
+ */
68
+ export function sanitizeHtml(html) {
69
+ if (!html) {
70
+ return '';
71
+ }
72
+ const template = document.createElement('template');
73
+ // Native sanitizer, when the browser has it
74
+ const withSetHtml = template;
75
+ if (typeof withSetHtml.setHTML === 'function') {
76
+ try {
77
+ withSetHtml.setHTML(html);
78
+ scrub(template.content);
79
+ return template.innerHTML;
80
+ }
81
+ catch (_a) {
82
+ // Fall through to the manual path
83
+ }
84
+ }
85
+ // Inert parsing: content inside <template> is not rendered, so scripts do not
86
+ // run and no subresource requests are made while we clean it up.
87
+ template.innerHTML = html;
88
+ scrub(template.content);
89
+ return template.innerHTML;
90
+ }
@@ -2,6 +2,9 @@ import { proxyCustomElement, HTMLElement, createEvent, h } from '@stencil/core/i
2
2
 
3
3
  const canvasEditorCss = ":host{display:block}.canvas-editor-wrapper{position:relative}.canvas-editor-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0, 0, 0, 0.8);display:flex;align-items:center;justify-content:center;z-index:9999}.canvas-editor-modal{width:95vw;height:95vh;max-width:none;max-height:none;background:var(--feedback-canvas-editor-bg-color, #ffffff);border-radius:12px;border:1px solid rgba(0, 0, 0, 0.08);display:flex;flex-direction:column;overflow:hidden;box-shadow:0 0 0 1px rgba(0, 0, 0, 0.02),\n 0 8px 16px rgba(0, 0, 0, 0.08),\n 0 16px 32px rgba(0, 0, 0, 0.08),\n 0 32px 64px rgba(0, 0, 0, 0.06)}.canvas-editor-header{background:var(--feedback-canvas-editor-header-bg-color, #fafafa);border-bottom:1px solid rgba(0, 0, 0, 0.06);padding:16px 20px;display:flex;flex-direction:column;gap:16px}.canvas-editor-title h3{margin:0;font-size:18px;font-weight:600;color:var(--feedback-canvas-editor-tool-text-color, #1a1a1a);letter-spacing:-0.02em}.canvas-editor-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:16px}.toolbar-section{display:flex;align-items:center;gap:8px}.tool-group{display:flex;align-items:center;gap:4px}.tool-btn{width:36px;height:36px;display:flex;align-items:center;justify-content:center;background:var(--feedback-canvas-editor-tool-bg-color, #ffffff);border:1.5px solid rgba(0, 0, 0, 0.08);border-radius:8px;cursor:pointer;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);padding:0;box-shadow:0 1px 2px rgba(0, 0, 0, 0.04)}.tool-btn svg{width:18px;height:18px;color:var(--feedback-canvas-editor-tool-text-color, #333)}.tool-btn:hover:not(:disabled){background:var(--feedback-canvas-editor-tool-bg-hover, #f5f5f5);border-color:rgba(0, 0, 0, 0.12);transform:translateY(-1px);box-shadow:0 2px 4px rgba(0, 0, 0, 0.08)}.tool-btn.active,.tool-btn.active:hover{background:var(--feedback-canvas-editor-tool-bg-active, #0070f4);color:var(--feedback-canvas-editor-tool-text-active, #ffffff);border-color:var(--feedback-canvas-editor-tool-bg-active, #0070f4);box-shadow:0 2px 8px rgba(0, 112, 244, 0.3)}.tool-btn.active svg{color:var(--feedback-canvas-editor-tool-text-active, #ffffff)}.tool-btn:disabled{opacity:0.4;cursor:not-allowed;color:var(--feedback-canvas-editor-tool-text-color, #333)}.toolbar-divider{width:1px;height:24px;background:var(--feedback-canvas-editor-divider-color, #e0e0e0);margin:0 4px}.undo-btn,.delete-btn{background:var(--feedback-canvas-editor-tool-bg-color, #ffffff) !important;border:1px solid var(--feedback-canvas-editor-border-color, #e0e0e0) !important}.undo-btn:hover:not(:disabled),.delete-btn:hover:not(:disabled){background:var(--feedback-canvas-editor-tool-bg-hover, #f0f0f0) !important}.undo-btn:disabled,.delete-btn:disabled{opacity:0.3;cursor:not-allowed}.delete-btn:hover:not(:disabled){background:#fee !important;border-color:#fcc !important}.delete-btn:hover:not(:disabled) svg{color:#c53030}.color-palette{display:flex;align-items:center;gap:6px}.color-slot-wrapper{position:relative;display:flex;align-items:center}.color-btn{width:32px;height:32px;border-radius:8px;border:2px solid transparent;cursor:pointer;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);display:flex;align-items:center;justify-content:center;background:var(--feedback-canvas-editor-tool-bg-color, #ffffff);border:2px solid rgba(0, 0, 0, 0.08);box-shadow:0 1px 2px rgba(0, 0, 0, 0.04)}.color-btn:hover{transform:translateY(-2px) scale(1.05);box-shadow:0 4px 8px rgba(0, 0, 0, 0.12)}.color-btn.active{border-color:var(--feedback-primary-color, #0070f4);box-shadow:0 0 0 2px rgba(0, 112, 244, 0.2)}.color-btn.editing{border-color:var(--feedback-primary-color, #0070f4)}.color-picker-dropdown{position:absolute;top:100%;left:0;z-index:1000;margin-top:4px;background:var(--feedback-canvas-editor-tool-bg-color, #ffffff);border:1px solid var(--feedback-canvas-editor-border-color, #e0e0e0);border-radius:6px;padding:8px;box-shadow:0 4px 12px rgba(0, 0, 0, 0.1)}.color-picker-dropdown input[type=\"color\"]{width:40px;height:40px;border:none;border-radius:4px;cursor:pointer}.size-control{display:flex;align-items:center;gap:8px;background:var(--feedback-canvas-editor-tool-bg-color, #ffffff);border:1px solid var(--feedback-canvas-editor-border-color, #e0e0e0);border-radius:6px;padding:6px 12px}.size-slider{width:80px;height:14px;-webkit-appearance:none;appearance:none;background:transparent;border-radius:2px;outline:none;cursor:pointer;border:none;position:relative}.size-slider::before{content:'';position:absolute;top:50%;left:0;right:0;height:4px;background:rgba(0, 0, 0, 0.15);border-radius:2px;transform:translateY(-50%);pointer-events:none}.size-slider::-webkit-slider-track{width:100%;height:4px;background:transparent;border-radius:2px;border:none}.size-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:14px;height:14px;background:var(--feedback-primary-color, #0070f4);border-radius:50%;cursor:pointer;position:relative;z-index:1}.size-slider::-moz-range-track{width:100%;height:4px;background:transparent;border-radius:2px;border:none}.size-slider::-moz-range-thumb{width:14px;height:14px;background:var(--feedback-primary-color, #0070f4);border-radius:50%;border:none;cursor:pointer}.size-slider::-ms-track{width:100%;height:4px;background:transparent;border-radius:2px;border:none;color:transparent}.size-slider::-ms-thumb{width:14px;height:14px;background:var(--feedback-primary-color, #0070f4);border-radius:50%;border:none;cursor:pointer}.size-value{font-weight:500;color:var(--feedback-canvas-editor-tool-text-color, #333);font-size:12px;min-width:30px}.selected-annotation-controls{border-left:2px solid var(--feedback-canvas-editor-divider-color, #e0e0e0);padding-left:12px;margin-left:8px;min-width:200px}.text-controls{display:flex;flex-direction:row;align-items:center;gap:12px}.font-size-control,.border-width-control{display:flex;align-items:center;gap:6px}.font-size-control label,.border-width-control label{font-size:12px;color:var(--feedback-canvas-editor-tool-text-color, #333);font-weight:500;min-width:35px}.action-btn{display:flex;align-items:center;gap:8px;padding:0 20px;border:1.5px solid rgba(0, 0, 0, 0.08);border-radius:8px;cursor:pointer;font-size:14px;font-weight:500;transition:all 0.2s cubic-bezier(0.4, 0, 0.2, 1);min-width:80px;justify-content:center;height:40px;box-shadow:0 1px 2px rgba(0, 0, 0, 0.04)}.action-btn.secondary{background:var(--feedback-canvas-editor-tool-bg-color, #ffffff);color:var(--feedback-canvas-editor-tool-text-color, #333);border-color:rgba(0, 0, 0, 0.08)}.action-btn.secondary:hover{background:var(--feedback-canvas-editor-tool-bg-hover, #f5f5f5);border-color:rgba(0, 0, 0, 0.12);transform:translateY(-1px);box-shadow:0 4px 8px rgba(0, 0, 0, 0.08)}.action-btn.primary{background:var(--feedback-primary-color, #0070f4);color:#ffffff;border-color:var(--feedback-primary-color, #0070f4);box-shadow:0 2px 4px rgba(0, 112, 244, 0.2)}.action-btn.primary:hover{background:#0056cc;border-color:#0056cc;transform:translateY(-1px);box-shadow:0 6px 12px rgba(0, 112, 244, 0.3)}.action-btn:active{transform:translateY(0)}.action-btn.small{height:28px;padding:4px 8px;font-size:12px;min-width:65px}.shape-controls{display:flex;flex-direction:column;gap:8px}.canvas-editor-content{flex:1;display:flex;align-items:center;justify-content:center;padding:24px;background:var(--feedback-canvas-editor-content-bg, #f5f5f5);overflow:hidden;min-height:0;min-width:0;position:relative}.canvas-editor-content::-webkit-scrollbar{width:8px;height:8px}.canvas-editor-content::-webkit-scrollbar-track{background:transparent}.canvas-editor-content::-webkit-scrollbar-thumb{background:rgba(0, 0, 0, 0.2);border-radius:4px}.canvas-editor-content::-webkit-scrollbar-thumb:hover{background:rgba(0, 0, 0, 0.3)}.annotation-canvas{max-width:calc(100% - 48px);max-height:calc(100% - 48px);width:auto !important;height:auto !important;cursor:crosshair;border-radius:8px;box-shadow:0 0 0 1px rgba(0, 0, 0, 0.04),\n 0 4px 6px rgba(0, 0, 0, 0.06),\n 0 8px 16px rgba(0, 0, 0, 0.08);background:#ffffff;transition:box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);object-fit:contain;display:block;margin:auto}.annotation-canvas:hover{box-shadow:0 0 0 1px rgba(0, 0, 0, 0.06),\n 0 6px 10px rgba(0, 0, 0, 0.08),\n 0 12px 24px rgba(0, 0, 0, 0.1)}@media screen and (min-width: 1920px){.canvas-editor-modal{width:90vw;height:90vh}.canvas-editor-content{padding:32px}}@media screen and (min-width: 1200px) and (max-width: 1919px){.canvas-editor-modal{width:92vw;height:92vh}.canvas-editor-content{padding:28px}}@media screen and (min-width: 769px) and (max-width: 1199px){.canvas-editor-modal{width:95vw;height:95vh}}@media screen and (max-width: 768px){.canvas-editor-modal{width:100vw;height:100vh;border-radius:0}.canvas-editor-toolbar{flex-direction:column;align-items:stretch;gap:8px}.toolbar-section{justify-content:center}.selected-annotation-controls{border-left:none;border-top:2px solid var(--feedback-canvas-editor-divider-color, #e0e0e0);padding-left:0;padding-top:8px;margin-left:0;margin-top:8px;min-width:auto}.canvas-editor-content{padding:16px}}";
4
4
 
5
+ // Upper bound on waiting for the capture stream to produce video metadata,
6
+ // after which the stream is stopped so it cannot leak
7
+ const CAPTURE_TIMEOUT_MS = 10000;
5
8
  const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
6
9
  constructor() {
7
10
  super();
@@ -10,6 +13,50 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
10
13
  this.screenshotReady = createEvent(this, "screenshotReady", 7);
11
14
  this.screenshotCancelled = createEvent(this, "screenshotCancelled", 7);
12
15
  this.screenshotFailed = createEvent(this, "screenshotFailed", 7);
16
+ this.handleKeyDown = (event) => {
17
+ if (!this.showCanvasEditor || !this.escapeClose || event.key !== 'Escape') {
18
+ return;
19
+ }
20
+ // While the browser is capturing the screen, the picker owns the keyboard
21
+ if (this.takingScreenshot) {
22
+ return;
23
+ }
24
+ event.preventDefault();
25
+ // The colour picker is a popup on top of the editor, so Escape dismisses
26
+ // that first and leaves the editor (and any annotations) in place
27
+ if (this.showColorPicker) {
28
+ this.showColorPicker = false;
29
+ this.editingColorIndex = -1;
30
+ return;
31
+ }
32
+ // Deselect before closing, the way drawing tools do. Closing throws away
33
+ // every annotation, so someone who has just selected one gets a cheap
34
+ // Escape that only drops the selection rather than their work
35
+ if (this.selectedAnnotation) {
36
+ this.selectedAnnotation = null;
37
+ this.redrawAnnotations();
38
+ return;
39
+ }
40
+ // Same as the Cancel button: discards annotations and returns to the form
41
+ this.closeCanvasEditor();
42
+ };
43
+ // Tracks whether the press that started this click landed on the backdrop.
44
+ // Dragging an annotation and releasing past the edge of the editor fires a
45
+ // click on the backdrop too, and that must not be read as "clicked outside".
46
+ this.pressStartedOnOverlay = false;
47
+ this.handleOverlayMouseDown = (event) => {
48
+ this.pressStartedOnOverlay = event.target === event.currentTarget;
49
+ };
50
+ this.handleOverlayClick = (event) => {
51
+ const startedOutside = this.pressStartedOnOverlay;
52
+ this.pressStartedOnOverlay = false;
53
+ if (!this.clickOutsideClose || !startedOutside || event.target !== event.currentTarget) {
54
+ return;
55
+ }
56
+ // Closes the editor only. The feedback form reopens behind it, so a stray
57
+ // click never dismisses the whole widget
58
+ this.closeCanvasEditor();
59
+ };
13
60
  this.handleWindowResize = () => {
14
61
  // Debounce resize events
15
62
  if (this.resizeTimeout) {
@@ -689,8 +736,16 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
689
736
  if (!this.isDrawing || !this.currentAnnotation)
690
737
  return;
691
738
  this.isDrawing = false;
692
- this.annotations = [...this.annotations, this.currentAnnotation];
739
+ const annotation = this.currentAnnotation;
693
740
  this.currentAnnotation = null;
741
+ // A click with no drag never reaches mousemove, so the shape has no end
742
+ // point or size. Committing it added an invisible annotation that could
743
+ // not be selected, dragged or deleted, and it still consumed an undo step.
744
+ if (this.isDegenerateAnnotation(annotation)) {
745
+ this.redrawAnnotations();
746
+ return;
747
+ }
748
+ this.annotations = [...this.annotations, annotation];
694
749
  this.redrawAnnotations();
695
750
  };
696
751
  // Convert screen coordinates to canvas coordinates
@@ -732,11 +787,28 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
732
787
  }
733
788
  case 'line':
734
789
  case 'arrow': {
735
- // Distance from point to line
736
- const A = annotation.endY - annotation.startY;
737
- const B = annotation.startX - annotation.endX;
738
- const C = annotation.endX * annotation.startY - annotation.startX * annotation.endY;
739
- const distance = Math.abs(A * x + B * y + C) / Math.sqrt(A * A + B * B);
790
+ // Distance from the point to the line *segment*, not to the infinite
791
+ // line through it. The previous formula measured the infinite line, so
792
+ // a click far past either end still counted as a hit, and a zero-length
793
+ // segment divided by zero and gave NaN, leaving that annotation
794
+ // impossible to select, drag or delete.
795
+ // A click with no drag never reaches mousemove, so endX/endY are
796
+ // undefined rather than equal to the start. Treat that as a segment
797
+ // collapsed to its start point instead of letting NaN propagate.
798
+ const endX = Number.isFinite(annotation.endX) ? annotation.endX : annotation.startX;
799
+ const endY = Number.isFinite(annotation.endY) ? annotation.endY : annotation.startY;
800
+ const dx = endX - annotation.startX;
801
+ const dy = endY - annotation.startY;
802
+ const lengthSquared = dx * dx + dy * dy;
803
+ // Zero-length segment: t stays 0 and we measure to the single point
804
+ let t = 0;
805
+ if (lengthSquared > 0) {
806
+ t = ((x - annotation.startX) * dx + (y - annotation.startY) * dy) / lengthSquared;
807
+ t = Math.max(0, Math.min(1, t));
808
+ }
809
+ const closestX = annotation.startX + t * dx;
810
+ const closestY = annotation.startY + t * dy;
811
+ const distance = Math.hypot(x - closestX, y - closestY);
740
812
  return distance <= tolerance;
741
813
  }
742
814
  case 'text': {
@@ -761,6 +833,8 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
761
833
  this.screenshotTakingText = 'Taking screenshot...';
762
834
  this.screenshotAttachedText = 'Screenshot attached';
763
835
  this.screenshotButtonText = 'Add a screenshot';
836
+ this.escapeClose = true;
837
+ this.clickOutsideClose = true;
764
838
  this.autoStartScreenshot = false;
765
839
  this.existingScreenshot = '';
766
840
  this.editTextButtonText = 'Edit Text';
@@ -816,10 +890,12 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
816
890
  // Add window resize listener for canvas adaptation
817
891
  this.handleWindowResize = this.handleWindowResize.bind(this);
818
892
  window.addEventListener('resize', this.handleWindowResize);
893
+ document.addEventListener('keydown', this.handleKeyDown);
819
894
  }
820
895
  disconnectedCallback() {
821
896
  // Clean up resize listener
822
897
  window.removeEventListener('resize', this.handleWindowResize);
898
+ document.removeEventListener('keydown', this.handleKeyDown);
823
899
  }
824
900
  async captureViewportScreenshot() {
825
901
  try {
@@ -843,6 +919,30 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
843
919
  video.autoplay = true;
844
920
  video.muted = true;
845
921
  return new Promise((resolve, reject) => {
922
+ // If neither onloadedmetadata nor onerror ever fires, the promise would
923
+ // never settle: the capture stream would stay live with the browser's
924
+ // recording indicator on, and the widget would sit in "Taking
925
+ // screenshot..." forever with no way back short of a page reload.
926
+ let settled = false;
927
+ const stopStream = () => {
928
+ stream.getTracks().forEach((track) => track.stop());
929
+ };
930
+ const finish = (fn) => {
931
+ if (settled) {
932
+ return;
933
+ }
934
+ settled = true;
935
+ clearTimeout(watchdog);
936
+ video.onloadedmetadata = null;
937
+ video.onerror = null;
938
+ fn();
939
+ };
940
+ const watchdog = setTimeout(() => {
941
+ finish(() => {
942
+ stopStream();
943
+ reject(new Error('Screenshot capture timed out'));
944
+ });
945
+ }, CAPTURE_TIMEOUT_MS);
846
946
  video.onloadedmetadata = () => {
847
947
  video.play();
848
948
  // Wait a moment for video to stabilize
@@ -854,22 +954,26 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
854
954
  canvas.height = video.videoHeight;
855
955
  const ctx = canvas.getContext('2d');
856
956
  ctx.drawImage(video, 0, 0);
857
- // Stop the stream
858
- stream.getTracks().forEach((track) => track.stop());
859
957
  // Convert to data URL
860
958
  const dataUrl = canvas.toDataURL('image/png');
861
- console.log('Screenshot captured successfully using Screen Capture API');
862
- resolve(dataUrl);
959
+ finish(() => {
960
+ stopStream();
961
+ resolve(dataUrl);
962
+ });
863
963
  }
864
964
  catch (error) {
865
- stream.getTracks().forEach((track) => track.stop());
866
- reject(error);
965
+ finish(() => {
966
+ stopStream();
967
+ reject(error);
968
+ });
867
969
  }
868
970
  }, 100);
869
971
  };
870
972
  video.onerror = () => {
871
- stream.getTracks().forEach((track) => track.stop());
872
- reject(new Error('Failed to load video for screenshot capture'));
973
+ finish(() => {
974
+ stopStream();
975
+ reject(new Error('Failed to load video for screenshot capture'));
976
+ });
873
977
  };
874
978
  });
875
979
  }
@@ -878,9 +982,27 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
878
982
  throw error;
879
983
  }
880
984
  }
985
+ // Whether a freshly drawn shape is too small to be worth keeping
986
+ isDegenerateAnnotation(annotation) {
987
+ const MIN_SIZE = 2; // canvas pixels
988
+ if (annotation.type === 'line' || annotation.type === 'arrow') {
989
+ if (!Number.isFinite(annotation.endX) || !Number.isFinite(annotation.endY)) {
990
+ return true;
991
+ }
992
+ return (Math.hypot(annotation.endX - annotation.startX, annotation.endY - annotation.startY) <
993
+ MIN_SIZE);
994
+ }
995
+ if (annotation.type === 'rectangle') {
996
+ if (!Number.isFinite(annotation.width) || !Number.isFinite(annotation.height)) {
997
+ return true;
998
+ }
999
+ return Math.abs(annotation.width) < MIN_SIZE && Math.abs(annotation.height) < MIN_SIZE;
1000
+ }
1001
+ return false;
1002
+ }
881
1003
  render() {
882
1004
  var _a, _b, _c, _d, _e, _f;
883
- return (h("div", { class: "canvas-editor-wrapper" }, this.showCanvasEditor && (h("div", { class: "canvas-editor-overlay", part: "overlay" }, h("div", { class: "canvas-editor-modal", part: "modal" }, h("div", { class: "canvas-editor-header", part: "header" }, h("div", { class: "canvas-editor-title", part: "title" }, h("h3", null, this.canvasEditorTitle)), h("div", { class: "canvas-editor-toolbar", part: "toolbar" }, h("div", { class: "toolbar-section" }, h("div", { class: "tool-group" }, h("button", { class: `tool-btn ${this.canvasDrawingTool === 'rectangle' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'rectangle'), title: "Rectangle" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-square" }, h("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'line' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'line'), title: "Line" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-minus" }, h("path", { d: "M5 12h14" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'arrow' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'arrow'), title: "Arrow" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-move-up-right" }, h("path", { d: "M13 5H19V11" }), h("path", { d: "M19 5L5 19" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'text' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'text'), title: "Text" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-type" }, h("path", { d: "M12 4v16" }), h("path", { d: "M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2" }), h("path", { d: "M9 20h6" }))), h("div", { class: "toolbar-divider" }), h("button", { class: "tool-btn undo-btn", part: "tool-button", onClick: this.undoLastAnnotation, disabled: this.annotations.length === 0, title: "Undo" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-undo" }, h("path", { d: "M3 7v6h6" }), h("path", { d: "M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13" }))), this.selectedAnnotation && (h("button", { class: "tool-btn delete-btn", part: "tool-button", onClick: this.deleteSelectedAnnotation, title: "Delete" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-trash-2" }, h("path", { d: "M3 6h18" }), h("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }), h("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }), h("line", { x1: "10", x2: "10", y1: "11", y2: "17" }), h("line", { x1: "14", x2: "14", y1: "11", y2: "17" })))))), 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) })))))))), (this.selectedAnnotation || this.canvasDrawingTool) && (h("div", { class: "toolbar-section selected-annotation-controls" }, (((_a = this.selectedAnnotation) === null || _a === void 0 ? void 0 : _a.type) === 'text' ||
1005
+ return (h("div", { class: "canvas-editor-wrapper" }, this.showCanvasEditor && (h("div", { class: "canvas-editor-overlay", part: "overlay", onMouseDown: this.handleOverlayMouseDown, onClick: this.handleOverlayClick }, h("div", { class: "canvas-editor-modal", part: "modal", role: "dialog", "aria-modal": "true", "aria-labelledby": "canvas-editor-title" }, h("div", { class: "canvas-editor-header", part: "header" }, h("div", { class: "canvas-editor-title", part: "title" }, h("h3", { id: "canvas-editor-title" }, this.canvasEditorTitle)), h("div", { class: "canvas-editor-toolbar", part: "toolbar" }, h("div", { class: "toolbar-section" }, h("div", { class: "tool-group" }, h("button", { class: `tool-btn ${this.canvasDrawingTool === 'rectangle' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'rectangle'), title: "Rectangle" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-square" }, h("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'line' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'line'), title: "Line" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-minus" }, h("path", { d: "M5 12h14" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'arrow' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'arrow'), title: "Arrow" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-move-up-right" }, h("path", { d: "M13 5H19V11" }), h("path", { d: "M19 5L5 19" }))), h("button", { class: `tool-btn ${this.canvasDrawingTool === 'text' ? 'active' : ''}`, part: "tool-button", onClick: () => (this.canvasDrawingTool = 'text'), title: "Text" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-type" }, h("path", { d: "M12 4v16" }), h("path", { d: "M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2" }), h("path", { d: "M9 20h6" }))), h("div", { class: "toolbar-divider" }), h("button", { class: "tool-btn undo-btn", part: "tool-button", onClick: this.undoLastAnnotation, disabled: this.annotations.length === 0, title: "Undo" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-undo" }, h("path", { d: "M3 7v6h6" }), h("path", { d: "M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13" }))), this.selectedAnnotation && (h("button", { class: "tool-btn delete-btn", part: "tool-button", onClick: this.deleteSelectedAnnotation, title: "Delete" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-trash-2" }, h("path", { d: "M3 6h18" }), h("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }), h("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }), h("line", { x1: "10", x2: "10", y1: "11", y2: "17" }), h("line", { x1: "14", x2: "14", y1: "11", y2: "17" })))))), 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) })))))))), (this.selectedAnnotation || this.canvasDrawingTool) && (h("div", { class: "toolbar-section selected-annotation-controls" }, (((_a = this.selectedAnnotation) === null || _a === void 0 ? void 0 : _a.type) === 'text' ||
884
1006
  (!this.selectedAnnotation && this.canvasDrawingTool === 'text')) && (h("div", { class: "text-controls" }, h("div", { class: "font-size-control" }, h("label", null, this.sizeLabelText), h("input", { type: "range", min: "8", max: "72", value: ((_b = this.selectedAnnotation) === null || _b === void 0 ? void 0 : _b.fontSize) || this.canvasTextSize, onInput: (e) => {
885
1007
  const newSize = parseInt(e.target.value);
886
1008
  if (this.selectedAnnotation) {
@@ -909,6 +1031,8 @@ const CanvasEditor = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement
909
1031
  "screenshotTakingText": [1, "screenshot-taking-text"],
910
1032
  "screenshotAttachedText": [1, "screenshot-attached-text"],
911
1033
  "screenshotButtonText": [1, "screenshot-button-text"],
1034
+ "escapeClose": [4, "escape-close"],
1035
+ "clickOutsideClose": [4, "click-outside-close"],
912
1036
  "autoStartScreenshot": [4, "auto-start-screenshot"],
913
1037
  "existingScreenshot": [1, "existing-screenshot"],
914
1038
  "editTextButtonText": [1, "edit-text-button-text"],
@@ -2,7 +2,7 @@ import { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/
2
2
  import { d as defineCustomElement$3 } from './canvas-editor2.js';
3
3
  import { d as defineCustomElement$2 } from './feedback-modal2.js';
4
4
 
5
- const feedbackButtonCss = ".feedback-button-content{cursor:pointer;max-width:fit-content;letter-spacing:var(--feedback-button-letter-spacing);z-index:var(--feedback-button-z-index);font-family:var(--feedback-font-family)}.feedback-button-content--custom-font{font-family:inherit}.feedback-button-content--light{align-items:center;background-color:var(--feedback-button-light-bg-color);border-radius:var(--feedback-button-border-radius);box-shadow:rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;box-sizing:border-box;color:var(--feedback-button-light-text-color);display:flex;font-size:var(--feedback-button-text-font-size);font-weight:var(--feedback-button-text-font-weight);padding:8px 15px}.feedback-button-content--dark{align-items:center;background-color:var(--feedback-button-dark-bg-color);border-radius:var(--feedback-button-border-radius);box-shadow:rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;box-sizing:border-box;color:var(--feedback-button-dark-text-color);display:flex;font-weight:var(--feedback-button-text-font-weight);font-size:var(--feedback-button-text-font-size);padding:8px 15px}.icon-edit{stroke:var(--feedback-button-light-icon-color)}.feedback-button-content--dark .icon-edit{stroke:var(--feedback-button-dark-icon-color)}.feedback-button-content--bottom-right{bottom:10px;position:fixed;right:10px}.feedback-button-content--center-right{position:fixed;transform:rotate(-90deg) translateY(-50%);top:50%}.feedback-button-content--center-right.feedback-button-content--dark,.feedback-button-content--center-right.feedback-button-content--light{border-radius:4px;border-bottom-left-radius:0px;border-bottom-right-radius:0px}.feedback-button-content-icon{height:16px;margin-right:5px;width:16px}.feedback-button-content--center-right .feedback-button-content-icon{rotate:90deg}@media screen and (max-width: 767px){.feedback-button-content--hide-mobile{display:none}}";
5
+ const feedbackButtonCss = ".feedback-button-content{cursor:pointer;max-width:fit-content;letter-spacing:var(--feedback-button-letter-spacing);z-index:var(--feedback-button-z-index);font-family:var(--feedback-font-family);background:none;border:none;margin:0;padding:0;color:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;text-align:inherit}.feedback-button-content--custom-font{font-family:inherit}.feedback-button-content--light{align-items:center;background-color:var(--feedback-button-light-bg-color);border-radius:var(--feedback-button-border-radius);box-shadow:rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;box-sizing:border-box;color:var(--feedback-button-light-text-color);display:flex;font-size:var(--feedback-button-text-font-size);font-weight:var(--feedback-button-text-font-weight);padding:8px 15px}.feedback-button-content--dark{align-items:center;background-color:var(--feedback-button-dark-bg-color);border-radius:var(--feedback-button-border-radius);box-shadow:rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;box-sizing:border-box;color:var(--feedback-button-dark-text-color);display:flex;font-weight:var(--feedback-button-text-font-weight);font-size:var(--feedback-button-text-font-size);padding:8px 15px}.icon-edit{stroke:var(--feedback-button-light-icon-color)}.feedback-button-content--dark .icon-edit{stroke:var(--feedback-button-dark-icon-color)}.feedback-button-content--bottom-right{bottom:10px;position:fixed;right:10px}.feedback-button-content--bottom-left{bottom:10px;left:10px;position:fixed}.feedback-button-content--top-right{position:fixed;right:10px;top:10px}.feedback-button-content--top-left{left:10px;position:fixed;top:10px}.feedback-button-content--center-right{position:fixed;transform:rotate(-90deg) translateY(-50%);top:50%}.feedback-button-content--center-left{position:fixed;transform:rotate(90deg) translateY(-50%);top:50%}.feedback-button-content--center-right.feedback-button-content--dark,.feedback-button-content--center-right.feedback-button-content--light,.feedback-button-content--center-left.feedback-button-content--dark,.feedback-button-content--center-left.feedback-button-content--light{border-radius:4px;border-bottom-left-radius:0px;border-bottom-right-radius:0px}.feedback-button-content-icon{height:16px;margin-right:5px;width:16px}.feedback-button-content--center-right .feedback-button-content-icon{rotate:90deg}.feedback-button-content--center-left .feedback-button-content-icon{rotate:-90deg}@media screen and (max-width: 767px){.feedback-button-content--hide-mobile{display:none}}";
6
6
 
7
7
  const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
8
8
  constructor() {
@@ -18,8 +18,11 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
18
18
  this.sessionId = '';
19
19
  this.metadata = '';
20
20
  this.submit = false;
21
+ this.apiKey = '';
22
+ this.clickOutsideClose = true;
21
23
  this.customFont = false;
22
24
  this.emailAddress = '';
25
+ this.historyClose = true;
23
26
  this.isEmailRequired = false;
24
27
  this.fetchData = true;
25
28
  this.hideEmail = false;
@@ -32,6 +35,7 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
32
35
  this.ratingMode = 'thumbs';
33
36
  this.emailPlaceholder = 'Email address (optional)';
34
37
  this.errorMessage = 'Please try again later.';
38
+ this.errorMessage401 = 'This project requires a valid API key to submit feedback. Check the api-key attribute.';
35
39
  this.errorMessage403 = 'The request URL does not match the one defined in PushFeedback for this project.';
36
40
  this.errorMessage404 = 'We could not find the provided project id in PushFeedback.';
37
41
  this.footerText = '';
@@ -77,13 +81,19 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
77
81
  }
78
82
  }
79
83
  componentDidLoad() {
80
- if (this.buttonPosition === 'center-right') {
84
+ if (this.buttonPosition === 'center-right' || this.buttonPosition === 'center-left') {
81
85
  const buttonContent = this.el.shadowRoot.querySelector('.feedback-button-content');
82
86
  let adjustement = 0;
83
87
  if (this.isSafariBrowser()) {
84
88
  adjustement = 5;
85
89
  }
86
- buttonContent.style.right = `${((buttonContent.offsetWidth + adjustement) / 2) * -1}px`;
90
+ const offset = `${((buttonContent.offsetWidth + adjustement) / 2) * -1}px`;
91
+ if (this.buttonPosition === 'center-right') {
92
+ buttonContent.style.right = offset;
93
+ }
94
+ else {
95
+ buttonContent.style.left = offset;
96
+ }
87
97
  }
88
98
  if (!this.customFont) {
89
99
  this.loadInterFont();
@@ -92,9 +102,12 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
92
102
  connectedCallback() {
93
103
  this.feedbackModal = document.createElement('feedback-modal');
94
104
  const props = [
105
+ 'apiKey',
106
+ 'clickOutsideClose',
95
107
  'customFont',
96
108
  'emailAddress',
97
109
  'fetchData',
110
+ 'historyClose',
98
111
  'hideEmail',
99
112
  'hidePrivacyPolicy',
100
113
  'hideRating',
@@ -120,6 +133,7 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
120
133
  'screenshotErrorUnexpected',
121
134
  'emailPlaceholder',
122
135
  'errorMessage',
136
+ 'errorMessage401',
123
137
  'errorMessage403',
124
138
  'errorMessage404',
125
139
  'footerText',
@@ -184,12 +198,16 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
184
198
  }
185
199
  }
186
200
  const body = Object.assign({ url: window.location.href, project: this.project, rating: this.rating || -1, ratingMode: this.ratingMode, message: '', metadata: this.metadata, session: localStorage.getItem('pushfeedback_sessionid') || '' }, (recaptchaToken && { recaptchaToken }));
187
- const res = await fetch('https://app.pushfeedback.com/api/feedback/', {
201
+ const headers = {
202
+ 'Content-Type': 'application/json',
203
+ };
204
+ if (this.apiKey) {
205
+ headers['Authorization'] = `Api-Key ${this.apiKey}`;
206
+ }
207
+ const res = await fetch('https://app.pushfeedback.com/api/v1/feedback/', {
188
208
  method: 'POST',
189
209
  body: JSON.stringify(body),
190
- headers: {
191
- 'Content-Type': 'application/json',
192
- },
210
+ headers,
193
211
  });
194
212
  if (res.status === 201) {
195
213
  const feedback_with_id = Object.assign(Object.assign({}, body), { id: await res.json() });
@@ -220,7 +238,7 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
220
238
  }
221
239
  }
222
240
  render() {
223
- return (h(Host, null, h("a", { class: `feedback-button-content feedback-button-content--${this.buttonStyle} feedback-button-content--${this.buttonPosition} ${this.customFont ? 'feedback-button-content--custom-font' : ''} ${this.hideMobile ? 'feedback-button-content--hide-mobile' : ''}`, part: "button", onClick: () => this.showModal() }, !this.hideIcon && this.buttonStyle != 'default' && (h("span", { class: "feedback-button-content-icon", part: "icon" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#fff", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "icon-edit" }, h("path", { d: "M12 20h9" }), h("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" })))), h("slot", null))));
241
+ return (h(Host, null, h("button", { type: "button", class: `feedback-button-content feedback-button-content--${this.buttonStyle} feedback-button-content--${this.buttonPosition} ${this.customFont ? 'feedback-button-content--custom-font' : ''} ${this.hideMobile ? 'feedback-button-content--hide-mobile' : ''}`, part: "button", onClick: () => this.showModal() }, !this.hideIcon && this.buttonStyle != 'default' && (h("span", { class: "feedback-button-content-icon", part: "icon" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#fff", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "icon-edit" }, h("path", { d: "M12 20h9" }), h("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" })))), h("slot", null))));
224
242
  }
225
243
  get el() { return this; }
226
244
  static get style() { return feedbackButtonCss; }
@@ -232,8 +250,11 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
232
250
  "sessionId": [1537, "session-id"],
233
251
  "metadata": [1],
234
252
  "submit": [4],
253
+ "apiKey": [1, "api-key"],
254
+ "clickOutsideClose": [4, "click-outside-close"],
235
255
  "customFont": [4, "custom-font"],
236
256
  "emailAddress": [1, "email-address"],
257
+ "historyClose": [4, "history-close"],
237
258
  "isEmailRequired": [4, "is-email-required"],
238
259
  "fetchData": [4, "fetch-data"],
239
260
  "hideEmail": [4, "hide-email"],
@@ -246,6 +267,7 @@ const FeedbackButton$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElem
246
267
  "ratingMode": [1, "rating-mode"],
247
268
  "emailPlaceholder": [1, "email-placeholder"],
248
269
  "errorMessage": [1, "error-message"],
270
+ "errorMessage401": [1, "error-message-4-0-1"],
249
271
  "errorMessage403": [1, "error-message-4-0-3"],
250
272
  "errorMessage404": [1, "error-message-4-0-4"],
251
273
  "footerText": [1, "footer-text"],