@checkflow/sdk 1.1.3 → 1.1.5
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.
- package/dist/chunk-NRA75GGU.mjs +1737 -0
- package/dist/highlighter-D0FpwCSU.d.mts +18 -0
- package/dist/highlighter-D0FpwCSU.d.ts +18 -0
- package/dist/{chunk-CD33QAA6.mjs → highlighter-EMSU6IYQ.mjs} +2 -3
- package/dist/index.d.mts +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +1578 -370
- package/dist/index.mjs +56 -504
- package/dist/react.d.mts +48 -2
- package/dist/react.d.ts +48 -2
- package/dist/react.js +1568 -217
- package/dist/react.mjs +33 -2
- package/dist/types-fCeePy5c.d.mts +101 -0
- package/dist/types-fCeePy5c.d.ts +101 -0
- package/dist/vue.d.mts +2 -1
- package/dist/vue.d.ts +2 -1
- package/dist/vue.js +1532 -216
- package/dist/vue.mjs +1 -1
- package/dist/widget-LD24NDDL.mjs +10 -0
- package/package.json +1 -1
- package/dist/highlighter-Dx2zURb6.d.mts +0 -73
- package/dist/highlighter-Dx2zURb6.d.ts +0 -73
- package/dist/highlighter-W4XDALRE.mjs +0 -8
package/dist/index.js
CHANGED
|
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
6
9
|
var __export = (target, all) => {
|
|
7
10
|
for (var name in all)
|
|
8
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -17,208 +20,1129 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
20
|
};
|
|
18
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
22
|
|
|
20
|
-
// src/
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
// src/annotation/types.ts
|
|
24
|
+
var DEFAULT_STYLE, HIGHLIGHT_STYLE, BLUR_STYLE, COLOR_PALETTE, STROKE_WIDTHS;
|
|
25
|
+
var init_types = __esm({
|
|
26
|
+
"src/annotation/types.ts"() {
|
|
27
|
+
"use strict";
|
|
28
|
+
DEFAULT_STYLE = {
|
|
29
|
+
strokeColor: "#FF3B30",
|
|
30
|
+
strokeWidth: 3,
|
|
31
|
+
fillColor: "transparent",
|
|
32
|
+
opacity: 1,
|
|
33
|
+
fontSize: 16,
|
|
34
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
35
|
+
};
|
|
36
|
+
HIGHLIGHT_STYLE = {
|
|
37
|
+
strokeColor: "transparent",
|
|
38
|
+
fillColor: "#FFEB3B",
|
|
39
|
+
opacity: 0.4
|
|
40
|
+
};
|
|
41
|
+
BLUR_STYLE = {
|
|
42
|
+
strokeColor: "#666666",
|
|
43
|
+
fillColor: "#666666",
|
|
44
|
+
opacity: 0.8
|
|
45
|
+
};
|
|
46
|
+
COLOR_PALETTE = [
|
|
47
|
+
"#FF3B30",
|
|
48
|
+
// Red
|
|
49
|
+
"#FF9500",
|
|
50
|
+
// Orange
|
|
51
|
+
"#FFCC00",
|
|
52
|
+
// Yellow
|
|
53
|
+
"#34C759",
|
|
54
|
+
// Green
|
|
55
|
+
"#007AFF",
|
|
56
|
+
// Blue
|
|
57
|
+
"#5856D6",
|
|
58
|
+
// Purple
|
|
59
|
+
"#AF52DE",
|
|
60
|
+
// Pink
|
|
61
|
+
"#000000",
|
|
62
|
+
// Black
|
|
63
|
+
"#FFFFFF"
|
|
64
|
+
// White
|
|
65
|
+
];
|
|
66
|
+
STROKE_WIDTHS = [1, 2, 3, 5, 8];
|
|
67
|
+
}
|
|
30
68
|
});
|
|
31
|
-
module.exports = __toCommonJS(index_exports);
|
|
32
69
|
|
|
33
|
-
// src/
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
// src/annotation/toolbar.ts
|
|
71
|
+
var TOOL_ICONS, TOOL_LABELS, AnnotationToolbar;
|
|
72
|
+
var init_toolbar = __esm({
|
|
73
|
+
"src/annotation/toolbar.ts"() {
|
|
74
|
+
"use strict";
|
|
75
|
+
init_types();
|
|
76
|
+
TOOL_ICONS = {
|
|
77
|
+
select: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"/><path d="M13 13l6 6"/></svg>`,
|
|
78
|
+
rectangle: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>`,
|
|
79
|
+
ellipse: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="12" rx="9" ry="6"/></svg>`,
|
|
80
|
+
arrow: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>`,
|
|
81
|
+
line: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="5" y1="19" x2="19" y2="5"/></svg>`,
|
|
82
|
+
highlight: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>`,
|
|
83
|
+
blur: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M6 13c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm-3 .5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm15 5.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM14 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-3.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm-11 10c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-17c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM10 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 5.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm8 .5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm3 8.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM14 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-4-12c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0 8.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm4-4.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-4c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z"/></svg>`,
|
|
84
|
+
text: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
|
|
85
|
+
freehand: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/><path d="M2 2l7.586 7.586"/><circle cx="11" cy="11" r="2"/></svg>`
|
|
86
|
+
};
|
|
87
|
+
TOOL_LABELS = {
|
|
88
|
+
select: "S\xE9lection",
|
|
89
|
+
rectangle: "Rectangle",
|
|
90
|
+
ellipse: "Ellipse",
|
|
91
|
+
arrow: "Fl\xE8che",
|
|
92
|
+
line: "Ligne",
|
|
93
|
+
highlight: "Surbrillance",
|
|
94
|
+
blur: "Floutage",
|
|
95
|
+
text: "Texte",
|
|
96
|
+
freehand: "Dessin libre"
|
|
97
|
+
};
|
|
98
|
+
AnnotationToolbar = class {
|
|
99
|
+
constructor(config2) {
|
|
100
|
+
this.colorPicker = null;
|
|
101
|
+
this.strokePicker = null;
|
|
102
|
+
this.config = config2;
|
|
103
|
+
this.element = this.createToolbar();
|
|
104
|
+
}
|
|
105
|
+
getElement() {
|
|
106
|
+
return this.element;
|
|
107
|
+
}
|
|
108
|
+
setActiveTool(tool) {
|
|
109
|
+
this.config.activeTool = tool;
|
|
110
|
+
this.updateActiveState();
|
|
111
|
+
}
|
|
112
|
+
setStyle(style) {
|
|
113
|
+
this.config.style = style;
|
|
114
|
+
this.updateStyleDisplay();
|
|
115
|
+
}
|
|
116
|
+
createToolbar() {
|
|
117
|
+
const toolbar = document.createElement("div");
|
|
118
|
+
toolbar.className = "cf-toolbar";
|
|
119
|
+
const toolsSection = document.createElement("div");
|
|
120
|
+
toolsSection.className = "cf-toolbar-section cf-toolbar-tools";
|
|
121
|
+
for (const tool of this.config.tools) {
|
|
122
|
+
const button = this.createToolButton(tool);
|
|
123
|
+
toolsSection.appendChild(button);
|
|
124
|
+
}
|
|
125
|
+
const separator1 = document.createElement("div");
|
|
126
|
+
separator1.className = "cf-toolbar-separator";
|
|
127
|
+
const styleSection = document.createElement("div");
|
|
128
|
+
styleSection.className = "cf-toolbar-section cf-toolbar-style";
|
|
129
|
+
const colorBtn = document.createElement("button");
|
|
130
|
+
colorBtn.className = "cf-toolbar-btn cf-color-btn";
|
|
131
|
+
colorBtn.title = "Couleur";
|
|
132
|
+
colorBtn.innerHTML = `<span class="cf-color-preview" style="background: ${this.config.style.strokeColor}"></span>`;
|
|
133
|
+
colorBtn.addEventListener("click", () => this.toggleColorPicker());
|
|
134
|
+
styleSection.appendChild(colorBtn);
|
|
135
|
+
const strokeBtn = document.createElement("button");
|
|
136
|
+
strokeBtn.className = "cf-toolbar-btn cf-stroke-btn";
|
|
137
|
+
strokeBtn.title = "\xC9paisseur";
|
|
138
|
+
strokeBtn.innerHTML = `<span class="cf-stroke-preview">${this.config.style.strokeWidth}px</span>`;
|
|
139
|
+
strokeBtn.addEventListener("click", () => this.toggleStrokePicker());
|
|
140
|
+
styleSection.appendChild(strokeBtn);
|
|
141
|
+
const separator2 = document.createElement("div");
|
|
142
|
+
separator2.className = "cf-toolbar-separator";
|
|
143
|
+
const actionsSection = document.createElement("div");
|
|
144
|
+
actionsSection.className = "cf-toolbar-section cf-toolbar-actions";
|
|
145
|
+
const undoBtn = document.createElement("button");
|
|
146
|
+
undoBtn.className = "cf-toolbar-btn";
|
|
147
|
+
undoBtn.title = "Annuler (Ctrl+Z)";
|
|
148
|
+
undoBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 00-9-9 9 9 0 00-6 2.3L3 13"/></svg>`;
|
|
149
|
+
undoBtn.addEventListener("click", () => this.config.onUndo());
|
|
150
|
+
actionsSection.appendChild(undoBtn);
|
|
151
|
+
const clearBtn = document.createElement("button");
|
|
152
|
+
clearBtn.className = "cf-toolbar-btn";
|
|
153
|
+
clearBtn.title = "Tout effacer";
|
|
154
|
+
clearBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/></svg>`;
|
|
155
|
+
clearBtn.addEventListener("click", () => this.config.onClear());
|
|
156
|
+
actionsSection.appendChild(clearBtn);
|
|
157
|
+
toolbar.appendChild(toolsSection);
|
|
158
|
+
toolbar.appendChild(separator1);
|
|
159
|
+
toolbar.appendChild(styleSection);
|
|
160
|
+
toolbar.appendChild(separator2);
|
|
161
|
+
toolbar.appendChild(actionsSection);
|
|
162
|
+
return toolbar;
|
|
163
|
+
}
|
|
164
|
+
createToolButton(tool) {
|
|
165
|
+
const button = document.createElement("button");
|
|
166
|
+
button.className = `cf-toolbar-btn cf-tool-btn ${tool === this.config.activeTool ? "active" : ""}`;
|
|
167
|
+
button.dataset.tool = tool;
|
|
168
|
+
button.title = TOOL_LABELS[tool];
|
|
169
|
+
button.innerHTML = TOOL_ICONS[tool];
|
|
170
|
+
button.addEventListener("click", () => {
|
|
171
|
+
this.config.onToolChange(tool);
|
|
172
|
+
this.setActiveTool(tool);
|
|
173
|
+
});
|
|
174
|
+
return button;
|
|
175
|
+
}
|
|
176
|
+
updateActiveState() {
|
|
177
|
+
const buttons = this.element.querySelectorAll(".cf-tool-btn");
|
|
178
|
+
buttons.forEach((btn) => {
|
|
179
|
+
const button = btn;
|
|
180
|
+
button.classList.toggle("active", button.dataset.tool === this.config.activeTool);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
updateStyleDisplay() {
|
|
184
|
+
const colorPreview = this.element.querySelector(".cf-color-preview");
|
|
185
|
+
if (colorPreview) {
|
|
186
|
+
colorPreview.style.background = this.config.style.strokeColor;
|
|
187
|
+
}
|
|
188
|
+
const strokePreview = this.element.querySelector(".cf-stroke-preview");
|
|
189
|
+
if (strokePreview) {
|
|
190
|
+
strokePreview.textContent = `${this.config.style.strokeWidth}px`;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
toggleColorPicker() {
|
|
194
|
+
if (this.colorPicker) {
|
|
195
|
+
this.colorPicker.remove();
|
|
196
|
+
this.colorPicker = null;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (this.strokePicker) {
|
|
200
|
+
this.strokePicker.remove();
|
|
201
|
+
this.strokePicker = null;
|
|
202
|
+
}
|
|
203
|
+
this.colorPicker = document.createElement("div");
|
|
204
|
+
this.colorPicker.className = "cf-picker cf-color-picker";
|
|
205
|
+
for (const color of COLOR_PALETTE) {
|
|
206
|
+
const swatch = document.createElement("button");
|
|
207
|
+
swatch.className = `cf-color-swatch ${color === this.config.style.strokeColor ? "active" : ""}`;
|
|
208
|
+
swatch.style.background = color;
|
|
209
|
+
swatch.addEventListener("click", () => {
|
|
210
|
+
this.config.onStyleChange({ strokeColor: color });
|
|
211
|
+
this.setStyle({ ...this.config.style, strokeColor: color });
|
|
212
|
+
this.colorPicker?.remove();
|
|
213
|
+
this.colorPicker = null;
|
|
214
|
+
});
|
|
215
|
+
this.colorPicker.appendChild(swatch);
|
|
216
|
+
}
|
|
217
|
+
const colorBtn = this.element.querySelector(".cf-color-btn");
|
|
218
|
+
colorBtn?.appendChild(this.colorPicker);
|
|
219
|
+
}
|
|
220
|
+
toggleStrokePicker() {
|
|
221
|
+
if (this.strokePicker) {
|
|
222
|
+
this.strokePicker.remove();
|
|
223
|
+
this.strokePicker = null;
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (this.colorPicker) {
|
|
227
|
+
this.colorPicker.remove();
|
|
228
|
+
this.colorPicker = null;
|
|
229
|
+
}
|
|
230
|
+
this.strokePicker = document.createElement("div");
|
|
231
|
+
this.strokePicker.className = "cf-picker cf-stroke-picker";
|
|
232
|
+
for (const width of STROKE_WIDTHS) {
|
|
233
|
+
const option = document.createElement("button");
|
|
234
|
+
option.className = `cf-stroke-option ${width === this.config.style.strokeWidth ? "active" : ""}`;
|
|
235
|
+
option.innerHTML = `<span style="height: ${width}px"></span> ${width}px`;
|
|
236
|
+
option.addEventListener("click", () => {
|
|
237
|
+
this.config.onStyleChange({ strokeWidth: width });
|
|
238
|
+
this.setStyle({ ...this.config.style, strokeWidth: width });
|
|
239
|
+
this.strokePicker?.remove();
|
|
240
|
+
this.strokePicker = null;
|
|
241
|
+
});
|
|
242
|
+
this.strokePicker.appendChild(option);
|
|
243
|
+
}
|
|
244
|
+
const strokeBtn = this.element.querySelector(".cf-stroke-btn");
|
|
245
|
+
strokeBtn?.appendChild(this.strokePicker);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
65
248
|
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// src/annotation/styles.ts
|
|
252
|
+
function injectAnnotationStyles() {
|
|
253
|
+
if (stylesInjected) return;
|
|
254
|
+
const styleElement = document.createElement("style");
|
|
255
|
+
styleElement.id = "cf-annotation-styles";
|
|
256
|
+
styleElement.textContent = STYLES;
|
|
257
|
+
document.head.appendChild(styleElement);
|
|
258
|
+
stylesInjected = true;
|
|
66
259
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
260
|
+
var STYLES, stylesInjected;
|
|
261
|
+
var init_styles = __esm({
|
|
262
|
+
"src/annotation/styles.ts"() {
|
|
263
|
+
"use strict";
|
|
264
|
+
STYLES = `
|
|
265
|
+
/* Annotation Overlay */
|
|
266
|
+
.cf-annotation-overlay {
|
|
267
|
+
position: fixed;
|
|
268
|
+
top: 0;
|
|
269
|
+
left: 0;
|
|
270
|
+
right: 0;
|
|
271
|
+
bottom: 0;
|
|
272
|
+
z-index: 999999;
|
|
273
|
+
background: rgba(0, 0, 0, 0.85);
|
|
274
|
+
display: flex;
|
|
275
|
+
align-items: center;
|
|
276
|
+
justify-content: center;
|
|
277
|
+
animation: cf-fade-in 0.2s ease-out;
|
|
73
278
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (ua.includes("Android")) return "Android";
|
|
79
|
-
if (ua.includes("iPhone") || ua.includes("iPad")) return "iOS";
|
|
80
|
-
return "Unknown";
|
|
279
|
+
|
|
280
|
+
@keyframes cf-fade-in {
|
|
281
|
+
from { opacity: 0; }
|
|
282
|
+
to { opacity: 1; }
|
|
81
283
|
}
|
|
82
284
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
warn: console.warn,
|
|
94
|
-
error: console.error
|
|
95
|
-
};
|
|
96
|
-
["log", "warn", "error"].forEach((level) => {
|
|
97
|
-
console[level] = (...args) => {
|
|
98
|
-
logs.push({
|
|
99
|
-
level,
|
|
100
|
-
message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" "),
|
|
101
|
-
timestamp: Date.now()
|
|
102
|
-
});
|
|
103
|
-
if (logs.length > MAX_LOGS) logs.shift();
|
|
104
|
-
origConsole[level](...args);
|
|
105
|
-
};
|
|
106
|
-
});
|
|
107
|
-
window.addEventListener("error", (event) => {
|
|
108
|
-
errorsCapture.push({
|
|
109
|
-
message: event.message,
|
|
110
|
-
filename: event.filename,
|
|
111
|
-
lineno: event.lineno,
|
|
112
|
-
colno: event.colno,
|
|
113
|
-
timestamp: Date.now()
|
|
114
|
-
});
|
|
115
|
-
if (errorsCapture.length > MAX_LOGS) errorsCapture.shift();
|
|
116
|
-
});
|
|
117
|
-
window.addEventListener("unhandledrejection", (event) => {
|
|
118
|
-
errorsCapture.push({
|
|
119
|
-
message: String(event.reason),
|
|
120
|
-
type: "unhandledrejection",
|
|
121
|
-
timestamp: Date.now()
|
|
122
|
-
});
|
|
123
|
-
if (errorsCapture.length > MAX_LOGS) errorsCapture.shift();
|
|
124
|
-
});
|
|
285
|
+
/* Editor Wrapper */
|
|
286
|
+
.cf-annotation-wrapper {
|
|
287
|
+
display: flex;
|
|
288
|
+
flex-direction: column;
|
|
289
|
+
max-width: 95vw;
|
|
290
|
+
max-height: 95vh;
|
|
291
|
+
background: #1a1a1a;
|
|
292
|
+
border-radius: 12px;
|
|
293
|
+
overflow: hidden;
|
|
294
|
+
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
|
|
125
295
|
}
|
|
126
|
-
|
|
127
|
-
|
|
296
|
+
|
|
297
|
+
/* Canvas Container */
|
|
298
|
+
.cf-annotation-canvas-container {
|
|
299
|
+
flex: 1;
|
|
300
|
+
display: flex;
|
|
301
|
+
align-items: center;
|
|
302
|
+
justify-content: center;
|
|
303
|
+
overflow: auto;
|
|
304
|
+
padding: 16px;
|
|
305
|
+
background: #0d0d0d;
|
|
128
306
|
}
|
|
129
|
-
|
|
130
|
-
|
|
307
|
+
|
|
308
|
+
.cf-annotation-canvas {
|
|
309
|
+
max-width: 100%;
|
|
310
|
+
max-height: calc(95vh - 140px);
|
|
311
|
+
border-radius: 4px;
|
|
312
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
313
|
+
cursor: crosshair;
|
|
131
314
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
315
|
+
|
|
316
|
+
/* Toolbar */
|
|
317
|
+
.cf-toolbar {
|
|
318
|
+
display: flex;
|
|
319
|
+
align-items: center;
|
|
320
|
+
gap: 8px;
|
|
321
|
+
padding: 12px 16px;
|
|
322
|
+
background: #2a2a2a;
|
|
323
|
+
border-bottom: 1px solid #3a3a3a;
|
|
135
324
|
}
|
|
136
325
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
function installNetworkInterceptor() {
|
|
142
|
-
if (installed2 || typeof window === "undefined") return;
|
|
143
|
-
installed2 = true;
|
|
144
|
-
const origFetch = window.fetch;
|
|
145
|
-
window.fetch = async function(...args) {
|
|
146
|
-
const start = Date.now();
|
|
147
|
-
const req = new Request(...args);
|
|
148
|
-
const entry = {
|
|
149
|
-
method: req.method,
|
|
150
|
-
url: req.url,
|
|
151
|
-
type: "fetch",
|
|
152
|
-
timestamp: start
|
|
153
|
-
};
|
|
154
|
-
try {
|
|
155
|
-
const res = await origFetch.apply(this, args);
|
|
156
|
-
entry.status = res.status;
|
|
157
|
-
entry.duration = Date.now() - start;
|
|
158
|
-
entry.response_type = res.headers.get("content-type") || void 0;
|
|
159
|
-
pushLog(entry);
|
|
160
|
-
return res;
|
|
161
|
-
} catch (err) {
|
|
162
|
-
entry.duration = Date.now() - start;
|
|
163
|
-
entry.error = err.message || "Network error";
|
|
164
|
-
pushLog(entry);
|
|
165
|
-
throw err;
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
const origOpen = XMLHttpRequest.prototype.open;
|
|
169
|
-
const origSend = XMLHttpRequest.prototype.send;
|
|
170
|
-
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
|
|
171
|
-
this.__cf_method = method;
|
|
172
|
-
this.__cf_url = String(url);
|
|
173
|
-
return origOpen.apply(this, [method, url, ...rest]);
|
|
174
|
-
};
|
|
175
|
-
XMLHttpRequest.prototype.send = function(...args) {
|
|
176
|
-
const start = Date.now();
|
|
177
|
-
const xhr = this;
|
|
178
|
-
xhr.addEventListener("loadend", () => {
|
|
179
|
-
const entry = {
|
|
180
|
-
method: xhr.__cf_method || "GET",
|
|
181
|
-
url: xhr.__cf_url || "",
|
|
182
|
-
status: xhr.status,
|
|
183
|
-
duration: Date.now() - start,
|
|
184
|
-
type: "xhr",
|
|
185
|
-
timestamp: start,
|
|
186
|
-
response_type: xhr.getResponseHeader("content-type") || void 0
|
|
187
|
-
};
|
|
188
|
-
if (xhr.status === 0) entry.error = "Request failed";
|
|
189
|
-
pushLog(entry);
|
|
190
|
-
});
|
|
191
|
-
return origSend.apply(this, args);
|
|
192
|
-
};
|
|
326
|
+
.cf-toolbar-section {
|
|
327
|
+
display: flex;
|
|
328
|
+
align-items: center;
|
|
329
|
+
gap: 4px;
|
|
193
330
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
331
|
+
|
|
332
|
+
.cf-toolbar-separator {
|
|
333
|
+
width: 1px;
|
|
334
|
+
height: 24px;
|
|
335
|
+
background: #4a4a4a;
|
|
336
|
+
margin: 0 8px;
|
|
198
337
|
}
|
|
199
|
-
|
|
200
|
-
|
|
338
|
+
|
|
339
|
+
.cf-toolbar-btn {
|
|
340
|
+
display: flex;
|
|
341
|
+
align-items: center;
|
|
342
|
+
justify-content: center;
|
|
343
|
+
width: 36px;
|
|
344
|
+
height: 36px;
|
|
345
|
+
padding: 0;
|
|
346
|
+
border: none;
|
|
347
|
+
border-radius: 8px;
|
|
348
|
+
background: transparent;
|
|
349
|
+
color: #999;
|
|
350
|
+
cursor: pointer;
|
|
351
|
+
transition: all 0.15s ease;
|
|
201
352
|
}
|
|
202
|
-
|
|
203
|
-
|
|
353
|
+
|
|
354
|
+
.cf-toolbar-btn:hover {
|
|
355
|
+
background: #3a3a3a;
|
|
356
|
+
color: #fff;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.cf-toolbar-btn.active {
|
|
360
|
+
background: #007AFF;
|
|
361
|
+
color: #fff;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.cf-toolbar-btn svg {
|
|
365
|
+
width: 20px;
|
|
366
|
+
height: 20px;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/* Color Button */
|
|
370
|
+
.cf-color-btn {
|
|
371
|
+
position: relative;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.cf-color-preview {
|
|
375
|
+
width: 20px;
|
|
376
|
+
height: 20px;
|
|
377
|
+
border-radius: 50%;
|
|
378
|
+
border: 2px solid #fff;
|
|
379
|
+
box-shadow: 0 0 0 1px rgba(0,0,0,0.2);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/* Stroke Button */
|
|
383
|
+
.cf-stroke-btn {
|
|
384
|
+
width: auto;
|
|
385
|
+
padding: 0 12px;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.cf-stroke-preview {
|
|
389
|
+
font-size: 12px;
|
|
390
|
+
font-weight: 500;
|
|
391
|
+
color: inherit;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/* Pickers */
|
|
395
|
+
.cf-picker {
|
|
396
|
+
position: absolute;
|
|
397
|
+
top: 100%;
|
|
398
|
+
left: 50%;
|
|
399
|
+
transform: translateX(-50%);
|
|
400
|
+
margin-top: 8px;
|
|
401
|
+
padding: 8px;
|
|
402
|
+
background: #2a2a2a;
|
|
403
|
+
border-radius: 8px;
|
|
404
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
|
405
|
+
z-index: 10;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.cf-color-picker {
|
|
409
|
+
display: grid;
|
|
410
|
+
grid-template-columns: repeat(5, 1fr);
|
|
411
|
+
gap: 4px;
|
|
412
|
+
width: 140px;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.cf-color-swatch {
|
|
416
|
+
width: 24px;
|
|
417
|
+
height: 24px;
|
|
418
|
+
border-radius: 50%;
|
|
419
|
+
border: 2px solid transparent;
|
|
420
|
+
cursor: pointer;
|
|
421
|
+
transition: transform 0.15s ease;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.cf-color-swatch:hover {
|
|
425
|
+
transform: scale(1.15);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.cf-color-swatch.active {
|
|
429
|
+
border-color: #fff;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.cf-stroke-picker {
|
|
433
|
+
display: flex;
|
|
434
|
+
flex-direction: column;
|
|
435
|
+
gap: 4px;
|
|
436
|
+
min-width: 80px;
|
|
204
437
|
}
|
|
205
438
|
|
|
439
|
+
.cf-stroke-option {
|
|
440
|
+
display: flex;
|
|
441
|
+
align-items: center;
|
|
442
|
+
gap: 8px;
|
|
443
|
+
padding: 6px 10px;
|
|
444
|
+
border: none;
|
|
445
|
+
border-radius: 4px;
|
|
446
|
+
background: transparent;
|
|
447
|
+
color: #999;
|
|
448
|
+
font-size: 12px;
|
|
449
|
+
cursor: pointer;
|
|
450
|
+
transition: all 0.15s ease;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.cf-stroke-option:hover {
|
|
454
|
+
background: #3a3a3a;
|
|
455
|
+
color: #fff;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.cf-stroke-option.active {
|
|
459
|
+
background: #007AFF;
|
|
460
|
+
color: #fff;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.cf-stroke-option span {
|
|
464
|
+
width: 24px;
|
|
465
|
+
background: currentColor;
|
|
466
|
+
border-radius: 2px;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/* Action Buttons */
|
|
470
|
+
.cf-annotation-actions {
|
|
471
|
+
display: flex;
|
|
472
|
+
justify-content: flex-end;
|
|
473
|
+
gap: 12px;
|
|
474
|
+
padding: 12px 16px;
|
|
475
|
+
background: #2a2a2a;
|
|
476
|
+
border-top: 1px solid #3a3a3a;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.cf-btn {
|
|
480
|
+
display: flex;
|
|
481
|
+
align-items: center;
|
|
482
|
+
justify-content: center;
|
|
483
|
+
padding: 10px 20px;
|
|
484
|
+
border: none;
|
|
485
|
+
border-radius: 8px;
|
|
486
|
+
font-size: 14px;
|
|
487
|
+
font-weight: 500;
|
|
488
|
+
cursor: pointer;
|
|
489
|
+
transition: all 0.15s ease;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.cf-btn-primary {
|
|
493
|
+
background: #007AFF;
|
|
494
|
+
color: #fff;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.cf-btn-primary:hover {
|
|
498
|
+
background: #0066DD;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.cf-btn-secondary {
|
|
502
|
+
background: #3a3a3a;
|
|
503
|
+
color: #fff;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.cf-btn-secondary:hover {
|
|
507
|
+
background: #4a4a4a;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* Text Input */
|
|
511
|
+
.cf-annotation-text-input {
|
|
512
|
+
position: fixed;
|
|
513
|
+
z-index: 1000000;
|
|
514
|
+
padding: 4px 8px;
|
|
515
|
+
border: 2px solid #007AFF;
|
|
516
|
+
border-radius: 4px;
|
|
517
|
+
background: rgba(255, 255, 255, 0.95);
|
|
518
|
+
font-size: 16px;
|
|
519
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
520
|
+
outline: none;
|
|
521
|
+
min-width: 150px;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/* Tool-specific cursors */
|
|
525
|
+
.cf-annotation-canvas[data-tool="select"] { cursor: default; }
|
|
526
|
+
.cf-annotation-canvas[data-tool="rectangle"] { cursor: crosshair; }
|
|
527
|
+
.cf-annotation-canvas[data-tool="ellipse"] { cursor: crosshair; }
|
|
528
|
+
.cf-annotation-canvas[data-tool="arrow"] { cursor: crosshair; }
|
|
529
|
+
.cf-annotation-canvas[data-tool="line"] { cursor: crosshair; }
|
|
530
|
+
.cf-annotation-canvas[data-tool="highlight"] { cursor: crosshair; }
|
|
531
|
+
.cf-annotation-canvas[data-tool="blur"] { cursor: crosshair; }
|
|
532
|
+
.cf-annotation-canvas[data-tool="text"] { cursor: text; }
|
|
533
|
+
.cf-annotation-canvas[data-tool="freehand"] { cursor: crosshair; }
|
|
534
|
+
|
|
535
|
+
/* Responsive */
|
|
536
|
+
@media (max-width: 768px) {
|
|
537
|
+
.cf-toolbar {
|
|
538
|
+
flex-wrap: wrap;
|
|
539
|
+
justify-content: center;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.cf-toolbar-separator {
|
|
543
|
+
display: none;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.cf-annotation-actions {
|
|
547
|
+
justify-content: stretch;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.cf-annotation-actions .cf-btn {
|
|
551
|
+
flex: 1;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
`;
|
|
555
|
+
stylesInjected = false;
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
// src/annotation/editor.ts
|
|
560
|
+
var AnnotationEditor;
|
|
561
|
+
var init_editor = __esm({
|
|
562
|
+
"src/annotation/editor.ts"() {
|
|
563
|
+
"use strict";
|
|
564
|
+
init_types();
|
|
565
|
+
init_toolbar();
|
|
566
|
+
init_styles();
|
|
567
|
+
AnnotationEditor = class {
|
|
568
|
+
constructor(config2) {
|
|
569
|
+
this.container = null;
|
|
570
|
+
this.canvas = null;
|
|
571
|
+
this.ctx = null;
|
|
572
|
+
this.toolbar = null;
|
|
573
|
+
this.annotations = [];
|
|
574
|
+
this.backgroundImage = null;
|
|
575
|
+
this.state = {
|
|
576
|
+
activeTool: "rectangle",
|
|
577
|
+
style: { ...DEFAULT_STYLE },
|
|
578
|
+
isDrawing: false,
|
|
579
|
+
currentAnnotation: null
|
|
580
|
+
};
|
|
581
|
+
this.startPoint = null;
|
|
582
|
+
this.freehandPoints = [];
|
|
583
|
+
this.textInput = null;
|
|
584
|
+
this.config = {
|
|
585
|
+
...config2,
|
|
586
|
+
tools: config2.tools || ["select", "rectangle", "arrow", "highlight", "blur", "text", "freehand"],
|
|
587
|
+
defaultStyle: config2.defaultStyle || DEFAULT_STYLE
|
|
588
|
+
};
|
|
589
|
+
this.state.style = { ...this.config.defaultStyle };
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Open the annotation editor with a screenshot
|
|
593
|
+
*/
|
|
594
|
+
async open(screenshotDataUrl2) {
|
|
595
|
+
injectAnnotationStyles();
|
|
596
|
+
this.backgroundImage = await this.loadImage(screenshotDataUrl2);
|
|
597
|
+
this.createEditorUI();
|
|
598
|
+
this.setupEventListeners();
|
|
599
|
+
this.render();
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Close the editor
|
|
603
|
+
*/
|
|
604
|
+
close() {
|
|
605
|
+
if (this.container) {
|
|
606
|
+
this.container.remove();
|
|
607
|
+
this.container = null;
|
|
608
|
+
}
|
|
609
|
+
this.canvas = null;
|
|
610
|
+
this.ctx = null;
|
|
611
|
+
this.toolbar = null;
|
|
612
|
+
this.annotations = [];
|
|
613
|
+
this.backgroundImage = null;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Get the annotated image as data URL
|
|
617
|
+
*/
|
|
618
|
+
getAnnotatedImage() {
|
|
619
|
+
if (!this.canvas || !this.ctx) return "";
|
|
620
|
+
const tempCanvas = document.createElement("canvas");
|
|
621
|
+
tempCanvas.width = this.canvas.width;
|
|
622
|
+
tempCanvas.height = this.canvas.height;
|
|
623
|
+
const tempCtx = tempCanvas.getContext("2d");
|
|
624
|
+
if (this.backgroundImage) {
|
|
625
|
+
tempCtx.drawImage(this.backgroundImage, 0, 0);
|
|
626
|
+
}
|
|
627
|
+
this.drawAnnotations(tempCtx);
|
|
628
|
+
return tempCanvas.toDataURL("image/png");
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Get annotations data
|
|
632
|
+
*/
|
|
633
|
+
getAnnotations() {
|
|
634
|
+
return [...this.annotations];
|
|
635
|
+
}
|
|
636
|
+
// Private methods
|
|
637
|
+
loadImage(src) {
|
|
638
|
+
return new Promise((resolve, reject) => {
|
|
639
|
+
const img = new Image();
|
|
640
|
+
img.onload = () => resolve(img);
|
|
641
|
+
img.onerror = reject;
|
|
642
|
+
img.src = src;
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
createEditorUI() {
|
|
646
|
+
this.container = document.createElement("div");
|
|
647
|
+
this.container.className = "cf-annotation-overlay";
|
|
648
|
+
const wrapper = document.createElement("div");
|
|
649
|
+
wrapper.className = "cf-annotation-wrapper";
|
|
650
|
+
const canvasContainer = document.createElement("div");
|
|
651
|
+
canvasContainer.className = "cf-annotation-canvas-container";
|
|
652
|
+
this.canvas = document.createElement("canvas");
|
|
653
|
+
this.canvas.className = "cf-annotation-canvas";
|
|
654
|
+
this.canvas.width = this.backgroundImage?.width || 1920;
|
|
655
|
+
this.canvas.height = this.backgroundImage?.height || 1080;
|
|
656
|
+
this.ctx = this.canvas.getContext("2d");
|
|
657
|
+
canvasContainer.appendChild(this.canvas);
|
|
658
|
+
this.toolbar = new AnnotationToolbar({
|
|
659
|
+
tools: this.config.tools,
|
|
660
|
+
activeTool: this.state.activeTool,
|
|
661
|
+
style: this.state.style,
|
|
662
|
+
onToolChange: (tool) => this.setActiveTool(tool),
|
|
663
|
+
onStyleChange: (style) => this.setStyle(style),
|
|
664
|
+
onUndo: () => this.undo(),
|
|
665
|
+
onClear: () => this.clearAll(),
|
|
666
|
+
onSave: () => this.save(),
|
|
667
|
+
onCancel: () => this.cancel()
|
|
668
|
+
});
|
|
669
|
+
const actions = document.createElement("div");
|
|
670
|
+
actions.className = "cf-annotation-actions";
|
|
671
|
+
actions.innerHTML = `
|
|
672
|
+
<button class="cf-btn cf-btn-secondary" data-action="cancel">Annuler</button>
|
|
673
|
+
<button class="cf-btn cf-btn-primary" data-action="save">Enregistrer</button>
|
|
674
|
+
`;
|
|
675
|
+
wrapper.appendChild(this.toolbar.getElement());
|
|
676
|
+
wrapper.appendChild(canvasContainer);
|
|
677
|
+
wrapper.appendChild(actions);
|
|
678
|
+
this.container.appendChild(wrapper);
|
|
679
|
+
document.body.appendChild(this.container);
|
|
680
|
+
actions.querySelector('[data-action="cancel"]')?.addEventListener("click", () => this.cancel());
|
|
681
|
+
actions.querySelector('[data-action="save"]')?.addEventListener("click", () => this.save());
|
|
682
|
+
}
|
|
683
|
+
setupEventListeners() {
|
|
684
|
+
if (!this.canvas) return;
|
|
685
|
+
this.canvas.addEventListener("mousedown", this.handleMouseDown.bind(this));
|
|
686
|
+
this.canvas.addEventListener("mousemove", this.handleMouseMove.bind(this));
|
|
687
|
+
this.canvas.addEventListener("mouseup", this.handleMouseUp.bind(this));
|
|
688
|
+
this.canvas.addEventListener("mouseleave", this.handleMouseUp.bind(this));
|
|
689
|
+
this.canvas.addEventListener("touchstart", this.handleTouchStart.bind(this));
|
|
690
|
+
this.canvas.addEventListener("touchmove", this.handleTouchMove.bind(this));
|
|
691
|
+
this.canvas.addEventListener("touchend", this.handleTouchEnd.bind(this));
|
|
692
|
+
document.addEventListener("keydown", this.handleKeyDown.bind(this));
|
|
693
|
+
}
|
|
694
|
+
handleMouseDown(e) {
|
|
695
|
+
const point = this.getCanvasPoint(e);
|
|
696
|
+
this.startDrawing(point);
|
|
697
|
+
}
|
|
698
|
+
handleMouseMove(e) {
|
|
699
|
+
const point = this.getCanvasPoint(e);
|
|
700
|
+
this.continueDrawing(point);
|
|
701
|
+
}
|
|
702
|
+
handleMouseUp(_e) {
|
|
703
|
+
this.finishDrawing();
|
|
704
|
+
}
|
|
705
|
+
handleTouchStart(e) {
|
|
706
|
+
e.preventDefault();
|
|
707
|
+
const touch = e.touches[0];
|
|
708
|
+
const point = this.getCanvasPointFromTouch(touch);
|
|
709
|
+
this.startDrawing(point);
|
|
710
|
+
}
|
|
711
|
+
handleTouchMove(e) {
|
|
712
|
+
e.preventDefault();
|
|
713
|
+
const touch = e.touches[0];
|
|
714
|
+
const point = this.getCanvasPointFromTouch(touch);
|
|
715
|
+
this.continueDrawing(point);
|
|
716
|
+
}
|
|
717
|
+
handleTouchEnd(e) {
|
|
718
|
+
e.preventDefault();
|
|
719
|
+
this.finishDrawing();
|
|
720
|
+
}
|
|
721
|
+
handleKeyDown(e) {
|
|
722
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "z") {
|
|
723
|
+
e.preventDefault();
|
|
724
|
+
this.undo();
|
|
725
|
+
}
|
|
726
|
+
if (e.key === "Escape") {
|
|
727
|
+
if (this.state.isDrawing) {
|
|
728
|
+
this.state.isDrawing = false;
|
|
729
|
+
this.state.currentAnnotation = null;
|
|
730
|
+
this.render();
|
|
731
|
+
} else {
|
|
732
|
+
this.cancel();
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
if (e.key === "Enter" && !this.state.isDrawing) {
|
|
736
|
+
this.save();
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
getCanvasPoint(e) {
|
|
740
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
741
|
+
const scaleX = this.canvas.width / rect.width;
|
|
742
|
+
const scaleY = this.canvas.height / rect.height;
|
|
743
|
+
return {
|
|
744
|
+
x: (e.clientX - rect.left) * scaleX,
|
|
745
|
+
y: (e.clientY - rect.top) * scaleY
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
getCanvasPointFromTouch(touch) {
|
|
749
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
750
|
+
const scaleX = this.canvas.width / rect.width;
|
|
751
|
+
const scaleY = this.canvas.height / rect.height;
|
|
752
|
+
return {
|
|
753
|
+
x: (touch.clientX - rect.left) * scaleX,
|
|
754
|
+
y: (touch.clientY - rect.top) * scaleY
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
startDrawing(point) {
|
|
758
|
+
if (this.state.activeTool === "select") return;
|
|
759
|
+
this.state.isDrawing = true;
|
|
760
|
+
this.startPoint = point;
|
|
761
|
+
if (this.state.activeTool === "text") {
|
|
762
|
+
this.showTextInput(point);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
if (this.state.activeTool === "freehand") {
|
|
766
|
+
this.freehandPoints = [point];
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
continueDrawing(point) {
|
|
770
|
+
if (!this.state.isDrawing || !this.startPoint) return;
|
|
771
|
+
if (this.state.activeTool === "freehand") {
|
|
772
|
+
this.freehandPoints.push(point);
|
|
773
|
+
}
|
|
774
|
+
this.state.currentAnnotation = this.createAnnotation(this.startPoint, point);
|
|
775
|
+
this.render();
|
|
776
|
+
}
|
|
777
|
+
finishDrawing() {
|
|
778
|
+
if (!this.state.isDrawing || !this.state.currentAnnotation) {
|
|
779
|
+
this.state.isDrawing = false;
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
if (this.isValidAnnotation(this.state.currentAnnotation)) {
|
|
783
|
+
this.annotations.push(this.state.currentAnnotation);
|
|
784
|
+
}
|
|
785
|
+
this.state.isDrawing = false;
|
|
786
|
+
this.state.currentAnnotation = null;
|
|
787
|
+
this.startPoint = null;
|
|
788
|
+
this.freehandPoints = [];
|
|
789
|
+
this.render();
|
|
790
|
+
}
|
|
791
|
+
isValidAnnotation(annotation) {
|
|
792
|
+
switch (annotation.type) {
|
|
793
|
+
case "rectangle":
|
|
794
|
+
case "ellipse":
|
|
795
|
+
case "highlight":
|
|
796
|
+
case "blur":
|
|
797
|
+
const bounds = annotation.bounds;
|
|
798
|
+
return Math.abs(bounds.width) > 5 && Math.abs(bounds.height) > 5;
|
|
799
|
+
case "arrow":
|
|
800
|
+
case "line":
|
|
801
|
+
const start = annotation.start;
|
|
802
|
+
const end = annotation.end;
|
|
803
|
+
const dist = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2));
|
|
804
|
+
return dist > 10;
|
|
805
|
+
case "freehand":
|
|
806
|
+
return annotation.points.length > 2;
|
|
807
|
+
case "text":
|
|
808
|
+
return !!annotation.text?.trim();
|
|
809
|
+
default:
|
|
810
|
+
return true;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
createAnnotation(start, end) {
|
|
814
|
+
const id = this.generateId();
|
|
815
|
+
const baseStyle = { ...this.state.style };
|
|
816
|
+
switch (this.state.activeTool) {
|
|
817
|
+
case "rectangle":
|
|
818
|
+
return {
|
|
819
|
+
id,
|
|
820
|
+
type: "rectangle",
|
|
821
|
+
bounds: this.createBounds(start, end),
|
|
822
|
+
style: baseStyle,
|
|
823
|
+
timestamp: Date.now()
|
|
824
|
+
};
|
|
825
|
+
case "ellipse":
|
|
826
|
+
return {
|
|
827
|
+
id,
|
|
828
|
+
type: "ellipse",
|
|
829
|
+
bounds: this.createBounds(start, end),
|
|
830
|
+
style: baseStyle,
|
|
831
|
+
timestamp: Date.now()
|
|
832
|
+
};
|
|
833
|
+
case "arrow":
|
|
834
|
+
return {
|
|
835
|
+
id,
|
|
836
|
+
type: "arrow",
|
|
837
|
+
start: { ...start },
|
|
838
|
+
end: { ...end },
|
|
839
|
+
style: baseStyle,
|
|
840
|
+
timestamp: Date.now()
|
|
841
|
+
};
|
|
842
|
+
case "line":
|
|
843
|
+
return {
|
|
844
|
+
id,
|
|
845
|
+
type: "line",
|
|
846
|
+
start: { ...start },
|
|
847
|
+
end: { ...end },
|
|
848
|
+
style: baseStyle,
|
|
849
|
+
timestamp: Date.now()
|
|
850
|
+
};
|
|
851
|
+
case "highlight":
|
|
852
|
+
return {
|
|
853
|
+
id,
|
|
854
|
+
type: "highlight",
|
|
855
|
+
bounds: this.createBounds(start, end),
|
|
856
|
+
style: { ...baseStyle, ...HIGHLIGHT_STYLE },
|
|
857
|
+
timestamp: Date.now()
|
|
858
|
+
};
|
|
859
|
+
case "blur":
|
|
860
|
+
return {
|
|
861
|
+
id,
|
|
862
|
+
type: "blur",
|
|
863
|
+
bounds: this.createBounds(start, end),
|
|
864
|
+
style: { ...baseStyle, ...BLUR_STYLE },
|
|
865
|
+
blurAmount: 10,
|
|
866
|
+
timestamp: Date.now()
|
|
867
|
+
};
|
|
868
|
+
case "freehand":
|
|
869
|
+
return {
|
|
870
|
+
id,
|
|
871
|
+
type: "freehand",
|
|
872
|
+
points: [...this.freehandPoints],
|
|
873
|
+
style: baseStyle,
|
|
874
|
+
timestamp: Date.now()
|
|
875
|
+
};
|
|
876
|
+
default:
|
|
877
|
+
return {
|
|
878
|
+
id,
|
|
879
|
+
type: "rectangle",
|
|
880
|
+
bounds: this.createBounds(start, end),
|
|
881
|
+
style: baseStyle,
|
|
882
|
+
timestamp: Date.now()
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
createBounds(start, end) {
|
|
887
|
+
return {
|
|
888
|
+
x: Math.min(start.x, end.x),
|
|
889
|
+
y: Math.min(start.y, end.y),
|
|
890
|
+
width: Math.abs(end.x - start.x),
|
|
891
|
+
height: Math.abs(end.y - start.y)
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
showTextInput(point) {
|
|
895
|
+
if (this.textInput) {
|
|
896
|
+
this.textInput.remove();
|
|
897
|
+
}
|
|
898
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
899
|
+
const scaleX = rect.width / this.canvas.width;
|
|
900
|
+
const scaleY = rect.height / this.canvas.height;
|
|
901
|
+
this.textInput = document.createElement("input");
|
|
902
|
+
this.textInput.type = "text";
|
|
903
|
+
this.textInput.className = "cf-annotation-text-input";
|
|
904
|
+
this.textInput.style.left = `${rect.left + point.x * scaleX}px`;
|
|
905
|
+
this.textInput.style.top = `${rect.top + point.y * scaleY}px`;
|
|
906
|
+
this.textInput.style.color = this.state.style.strokeColor;
|
|
907
|
+
this.textInput.style.fontSize = `${(this.state.style.fontSize || 16) * scaleY}px`;
|
|
908
|
+
this.textInput.placeholder = "Tapez votre texte...";
|
|
909
|
+
document.body.appendChild(this.textInput);
|
|
910
|
+
this.textInput.focus();
|
|
911
|
+
const handleTextSubmit = () => {
|
|
912
|
+
if (this.textInput && this.textInput.value.trim()) {
|
|
913
|
+
const textAnnotation = {
|
|
914
|
+
id: this.generateId(),
|
|
915
|
+
type: "text",
|
|
916
|
+
position: point,
|
|
917
|
+
text: this.textInput.value.trim(),
|
|
918
|
+
style: { ...this.state.style },
|
|
919
|
+
timestamp: Date.now()
|
|
920
|
+
};
|
|
921
|
+
this.annotations.push(textAnnotation);
|
|
922
|
+
this.render();
|
|
923
|
+
}
|
|
924
|
+
this.textInput?.remove();
|
|
925
|
+
this.textInput = null;
|
|
926
|
+
this.state.isDrawing = false;
|
|
927
|
+
};
|
|
928
|
+
this.textInput.addEventListener("blur", handleTextSubmit);
|
|
929
|
+
this.textInput.addEventListener("keydown", (e) => {
|
|
930
|
+
if (e.key === "Enter") {
|
|
931
|
+
handleTextSubmit();
|
|
932
|
+
} else if (e.key === "Escape") {
|
|
933
|
+
this.textInput?.remove();
|
|
934
|
+
this.textInput = null;
|
|
935
|
+
this.state.isDrawing = false;
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
generateId() {
|
|
940
|
+
return `ann_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
941
|
+
}
|
|
942
|
+
setActiveTool(tool) {
|
|
943
|
+
this.state.activeTool = tool;
|
|
944
|
+
this.toolbar?.setActiveTool(tool);
|
|
945
|
+
if (this.canvas) {
|
|
946
|
+
this.canvas.style.cursor = tool === "select" ? "default" : "crosshair";
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
setStyle(style) {
|
|
950
|
+
this.state.style = { ...this.state.style, ...style };
|
|
951
|
+
this.toolbar?.setStyle(this.state.style);
|
|
952
|
+
}
|
|
953
|
+
undo() {
|
|
954
|
+
if (this.annotations.length > 0) {
|
|
955
|
+
this.annotations.pop();
|
|
956
|
+
this.render();
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
clearAll() {
|
|
960
|
+
this.annotations = [];
|
|
961
|
+
this.render();
|
|
962
|
+
}
|
|
963
|
+
save() {
|
|
964
|
+
const imageData = this.getAnnotatedImage();
|
|
965
|
+
this.config.onSave?.(this.annotations, imageData);
|
|
966
|
+
this.close();
|
|
967
|
+
}
|
|
968
|
+
cancel() {
|
|
969
|
+
this.config.onCancel?.();
|
|
970
|
+
this.close();
|
|
971
|
+
}
|
|
972
|
+
render() {
|
|
973
|
+
if (!this.ctx || !this.canvas) return;
|
|
974
|
+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
975
|
+
if (this.backgroundImage) {
|
|
976
|
+
this.ctx.drawImage(this.backgroundImage, 0, 0);
|
|
977
|
+
}
|
|
978
|
+
this.drawAnnotations(this.ctx);
|
|
979
|
+
if (this.state.currentAnnotation) {
|
|
980
|
+
this.drawAnnotation(this.ctx, this.state.currentAnnotation);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
drawAnnotations(ctx) {
|
|
984
|
+
for (const annotation of this.annotations) {
|
|
985
|
+
this.drawAnnotation(ctx, annotation);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
drawAnnotation(ctx, annotation) {
|
|
989
|
+
ctx.save();
|
|
990
|
+
ctx.globalAlpha = annotation.style.opacity;
|
|
991
|
+
ctx.strokeStyle = annotation.style.strokeColor;
|
|
992
|
+
ctx.fillStyle = annotation.style.fillColor;
|
|
993
|
+
ctx.lineWidth = annotation.style.strokeWidth;
|
|
994
|
+
ctx.lineCap = "round";
|
|
995
|
+
ctx.lineJoin = "round";
|
|
996
|
+
switch (annotation.type) {
|
|
997
|
+
case "rectangle":
|
|
998
|
+
this.drawRectangle(ctx, annotation.bounds, annotation.style);
|
|
999
|
+
break;
|
|
1000
|
+
case "ellipse":
|
|
1001
|
+
this.drawEllipse(ctx, annotation.bounds, annotation.style);
|
|
1002
|
+
break;
|
|
1003
|
+
case "arrow":
|
|
1004
|
+
this.drawArrow(ctx, annotation.start, annotation.end, annotation.style);
|
|
1005
|
+
break;
|
|
1006
|
+
case "line":
|
|
1007
|
+
this.drawLine(ctx, annotation.start, annotation.end);
|
|
1008
|
+
break;
|
|
1009
|
+
case "highlight":
|
|
1010
|
+
this.drawHighlight(ctx, annotation.bounds, annotation.style);
|
|
1011
|
+
break;
|
|
1012
|
+
case "blur":
|
|
1013
|
+
this.drawBlur(ctx, annotation.bounds, annotation.blurAmount);
|
|
1014
|
+
break;
|
|
1015
|
+
case "text":
|
|
1016
|
+
this.drawText(ctx, annotation.position, annotation.text, annotation.style);
|
|
1017
|
+
break;
|
|
1018
|
+
case "freehand":
|
|
1019
|
+
this.drawFreehand(ctx, annotation.points);
|
|
1020
|
+
break;
|
|
1021
|
+
}
|
|
1022
|
+
ctx.restore();
|
|
1023
|
+
}
|
|
1024
|
+
drawRectangle(ctx, bounds, style) {
|
|
1025
|
+
if (style.fillColor !== "transparent") {
|
|
1026
|
+
ctx.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
1027
|
+
}
|
|
1028
|
+
ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
1029
|
+
}
|
|
1030
|
+
drawEllipse(ctx, bounds, style) {
|
|
1031
|
+
const centerX = bounds.x + bounds.width / 2;
|
|
1032
|
+
const centerY = bounds.y + bounds.height / 2;
|
|
1033
|
+
const radiusX = bounds.width / 2;
|
|
1034
|
+
const radiusY = bounds.height / 2;
|
|
1035
|
+
ctx.beginPath();
|
|
1036
|
+
ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2);
|
|
1037
|
+
if (style.fillColor !== "transparent") {
|
|
1038
|
+
ctx.fill();
|
|
1039
|
+
}
|
|
1040
|
+
ctx.stroke();
|
|
1041
|
+
}
|
|
1042
|
+
drawArrow(ctx, start, end, style) {
|
|
1043
|
+
const headLength = 15 + style.strokeWidth * 2;
|
|
1044
|
+
const angle = Math.atan2(end.y - start.y, end.x - start.x);
|
|
1045
|
+
ctx.beginPath();
|
|
1046
|
+
ctx.moveTo(start.x, start.y);
|
|
1047
|
+
ctx.lineTo(end.x, end.y);
|
|
1048
|
+
ctx.stroke();
|
|
1049
|
+
ctx.beginPath();
|
|
1050
|
+
ctx.moveTo(end.x, end.y);
|
|
1051
|
+
ctx.lineTo(
|
|
1052
|
+
end.x - headLength * Math.cos(angle - Math.PI / 6),
|
|
1053
|
+
end.y - headLength * Math.sin(angle - Math.PI / 6)
|
|
1054
|
+
);
|
|
1055
|
+
ctx.lineTo(
|
|
1056
|
+
end.x - headLength * Math.cos(angle + Math.PI / 6),
|
|
1057
|
+
end.y - headLength * Math.sin(angle + Math.PI / 6)
|
|
1058
|
+
);
|
|
1059
|
+
ctx.closePath();
|
|
1060
|
+
ctx.fillStyle = style.strokeColor;
|
|
1061
|
+
ctx.fill();
|
|
1062
|
+
}
|
|
1063
|
+
drawLine(ctx, start, end) {
|
|
1064
|
+
ctx.beginPath();
|
|
1065
|
+
ctx.moveTo(start.x, start.y);
|
|
1066
|
+
ctx.lineTo(end.x, end.y);
|
|
1067
|
+
ctx.stroke();
|
|
1068
|
+
}
|
|
1069
|
+
drawHighlight(ctx, bounds, style) {
|
|
1070
|
+
ctx.fillStyle = style.fillColor;
|
|
1071
|
+
ctx.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
1072
|
+
}
|
|
1073
|
+
drawBlur(ctx, bounds, blurAmount) {
|
|
1074
|
+
const pixelSize = Math.max(blurAmount, 5);
|
|
1075
|
+
if (this.backgroundImage) {
|
|
1076
|
+
const tempCanvas = document.createElement("canvas");
|
|
1077
|
+
tempCanvas.width = bounds.width;
|
|
1078
|
+
tempCanvas.height = bounds.height;
|
|
1079
|
+
const tempCtx = tempCanvas.getContext("2d");
|
|
1080
|
+
tempCtx.drawImage(
|
|
1081
|
+
this.backgroundImage,
|
|
1082
|
+
bounds.x,
|
|
1083
|
+
bounds.y,
|
|
1084
|
+
bounds.width,
|
|
1085
|
+
bounds.height,
|
|
1086
|
+
0,
|
|
1087
|
+
0,
|
|
1088
|
+
bounds.width,
|
|
1089
|
+
bounds.height
|
|
1090
|
+
);
|
|
1091
|
+
const w = tempCanvas.width;
|
|
1092
|
+
const h = tempCanvas.height;
|
|
1093
|
+
tempCtx.imageSmoothingEnabled = false;
|
|
1094
|
+
tempCtx.drawImage(tempCanvas, 0, 0, w, h, 0, 0, w / pixelSize, h / pixelSize);
|
|
1095
|
+
tempCtx.drawImage(tempCanvas, 0, 0, w / pixelSize, h / pixelSize, 0, 0, w, h);
|
|
1096
|
+
ctx.drawImage(tempCanvas, bounds.x, bounds.y);
|
|
1097
|
+
}
|
|
1098
|
+
ctx.strokeStyle = "#999";
|
|
1099
|
+
ctx.lineWidth = 1;
|
|
1100
|
+
ctx.setLineDash([5, 5]);
|
|
1101
|
+
ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
1102
|
+
ctx.setLineDash([]);
|
|
1103
|
+
}
|
|
1104
|
+
drawText(ctx, position, text, style) {
|
|
1105
|
+
ctx.font = `${style.fontSize || 16}px ${style.fontFamily || "system-ui"}`;
|
|
1106
|
+
ctx.fillStyle = style.strokeColor;
|
|
1107
|
+
ctx.textBaseline = "top";
|
|
1108
|
+
const metrics = ctx.measureText(text);
|
|
1109
|
+
const padding = 4;
|
|
1110
|
+
ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
|
|
1111
|
+
ctx.fillRect(
|
|
1112
|
+
position.x - padding,
|
|
1113
|
+
position.y - padding,
|
|
1114
|
+
metrics.width + padding * 2,
|
|
1115
|
+
(style.fontSize || 16) + padding * 2
|
|
1116
|
+
);
|
|
1117
|
+
ctx.fillStyle = style.strokeColor;
|
|
1118
|
+
ctx.fillText(text, position.x, position.y);
|
|
1119
|
+
}
|
|
1120
|
+
drawFreehand(ctx, points) {
|
|
1121
|
+
if (points.length < 2) return;
|
|
1122
|
+
ctx.beginPath();
|
|
1123
|
+
ctx.moveTo(points[0].x, points[0].y);
|
|
1124
|
+
for (let i = 1; i < points.length; i++) {
|
|
1125
|
+
ctx.lineTo(points[i].x, points[i].y);
|
|
1126
|
+
}
|
|
1127
|
+
ctx.stroke();
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1132
|
+
|
|
206
1133
|
// src/widget.ts
|
|
207
|
-
var
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
};
|
|
213
|
-
var container = null;
|
|
214
|
-
var onSubmitCallback = null;
|
|
215
|
-
var screenshotDataUrl = null;
|
|
216
|
-
var annotationsData = [];
|
|
217
|
-
var isExpanded = false;
|
|
1134
|
+
var widget_exports = {};
|
|
1135
|
+
__export(widget_exports, {
|
|
1136
|
+
mountWidget: () => mountWidget,
|
|
1137
|
+
openFeedbackModal: () => openFeedbackModal,
|
|
1138
|
+
unmountWidget: () => unmountWidget
|
|
1139
|
+
});
|
|
218
1140
|
function mountWidget(config2 = {}, onSubmit, opts) {
|
|
219
1141
|
if (container) return;
|
|
220
1142
|
onSubmitCallback = onSubmit;
|
|
1143
|
+
currentUser = opts?.user;
|
|
221
1144
|
const cfg = { ...DEFAULT_CONFIG, ...config2 };
|
|
1145
|
+
currentCfg = cfg;
|
|
222
1146
|
container = document.createElement("div");
|
|
223
1147
|
container.id = "checkflow-widget";
|
|
224
1148
|
injectStyles(cfg);
|
|
@@ -227,15 +1151,29 @@ function mountWidget(config2 = {}, onSubmit, opts) {
|
|
|
227
1151
|
const btn = container.querySelector("#cf-trigger");
|
|
228
1152
|
btn?.addEventListener("click", () => openModal(cfg, opts));
|
|
229
1153
|
}
|
|
1154
|
+
function openFeedbackModal(config2 = {}, onSubmit, opts) {
|
|
1155
|
+
onSubmitCallback = onSubmit;
|
|
1156
|
+
currentUser = opts?.user;
|
|
1157
|
+
const cfg = { ...DEFAULT_CONFIG, ...config2 };
|
|
1158
|
+
currentCfg = cfg;
|
|
1159
|
+
injectStyles(cfg);
|
|
1160
|
+
openModal(cfg, opts);
|
|
1161
|
+
}
|
|
1162
|
+
function getBackdropClass(cfg, expanded) {
|
|
1163
|
+
if (expanded) return "cf-backdrop--center";
|
|
1164
|
+
if (cfg.position === "center") return "cf-backdrop--center";
|
|
1165
|
+
return "cf-backdrop--corner cf-pos-" + (cfg.position || "bottom-right");
|
|
1166
|
+
}
|
|
230
1167
|
function openModal(cfg, opts) {
|
|
231
|
-
if (!container) return;
|
|
232
1168
|
const existing = document.getElementById("cf-modal-backdrop");
|
|
233
1169
|
if (existing) existing.remove();
|
|
234
1170
|
isExpanded = false;
|
|
235
1171
|
screenshotDataUrl = null;
|
|
1172
|
+
annotatedImageUrl = null;
|
|
236
1173
|
annotationsData = [];
|
|
237
1174
|
const backdrop = document.createElement("div");
|
|
238
1175
|
backdrop.id = "cf-modal-backdrop";
|
|
1176
|
+
backdrop.className = getBackdropClass(cfg, false);
|
|
239
1177
|
backdrop.innerHTML = getModalHTML(cfg, false);
|
|
240
1178
|
document.body.appendChild(backdrop);
|
|
241
1179
|
backdrop.addEventListener("click", (e) => {
|
|
@@ -248,6 +1186,7 @@ function closeModal() {
|
|
|
248
1186
|
if (backdrop) backdrop.remove();
|
|
249
1187
|
isExpanded = false;
|
|
250
1188
|
screenshotDataUrl = null;
|
|
1189
|
+
annotatedImageUrl = null;
|
|
251
1190
|
annotationsData = [];
|
|
252
1191
|
}
|
|
253
1192
|
function expandModal(cfg, opts) {
|
|
@@ -257,6 +1196,7 @@ function expandModal(cfg, opts) {
|
|
|
257
1196
|
const email = backdrop.querySelector("#cf-email")?.value || "";
|
|
258
1197
|
const desc = backdrop.querySelector("#cf-desc")?.value || "";
|
|
259
1198
|
isExpanded = true;
|
|
1199
|
+
backdrop.className = getBackdropClass(cfg, true);
|
|
260
1200
|
backdrop.innerHTML = getModalHTML(cfg, true);
|
|
261
1201
|
bindModalEvents(cfg, opts);
|
|
262
1202
|
const nameEl = backdrop.querySelector("#cf-name");
|
|
@@ -274,7 +1214,9 @@ function collapseModal(cfg, opts) {
|
|
|
274
1214
|
const desc = backdrop.querySelector("#cf-desc")?.value || "";
|
|
275
1215
|
isExpanded = false;
|
|
276
1216
|
screenshotDataUrl = null;
|
|
1217
|
+
annotatedImageUrl = null;
|
|
277
1218
|
annotationsData = [];
|
|
1219
|
+
backdrop.className = getBackdropClass(cfg, false);
|
|
278
1220
|
backdrop.innerHTML = getModalHTML(cfg, false);
|
|
279
1221
|
bindModalEvents(cfg, opts);
|
|
280
1222
|
const nameEl = backdrop.querySelector("#cf-name");
|
|
@@ -284,6 +1226,30 @@ function collapseModal(cfg, opts) {
|
|
|
284
1226
|
if (emailEl) emailEl.value = email;
|
|
285
1227
|
if (descEl) descEl.value = desc;
|
|
286
1228
|
}
|
|
1229
|
+
function openAnnotationEditor(cfg, opts) {
|
|
1230
|
+
if (!screenshotDataUrl) return;
|
|
1231
|
+
const backdrop = document.getElementById("cf-modal-backdrop");
|
|
1232
|
+
if (backdrop) backdrop.style.display = "none";
|
|
1233
|
+
const editor = new AnnotationEditor({
|
|
1234
|
+
tools: ["rectangle", "ellipse", "arrow", "highlight", "blur", "text", "freehand"],
|
|
1235
|
+
defaultStyle: { strokeColor: "#FF3B30", strokeWidth: 3, fillColor: "transparent", opacity: 1, fontSize: 16, fontFamily: "system-ui, -apple-system, sans-serif" },
|
|
1236
|
+
onSave: (annotations, imageData) => {
|
|
1237
|
+
annotationsData = annotations;
|
|
1238
|
+
annotatedImageUrl = imageData;
|
|
1239
|
+
if (backdrop) {
|
|
1240
|
+
backdrop.style.display = "flex";
|
|
1241
|
+
const img = backdrop.querySelector("#cf-screenshot-img");
|
|
1242
|
+
if (img) img.src = annotatedImageUrl;
|
|
1243
|
+
const annotBtn = backdrop.querySelector("#cf-annotate-btn");
|
|
1244
|
+
if (annotBtn) annotBtn.textContent = `Annoter (${annotationsData.length})`;
|
|
1245
|
+
}
|
|
1246
|
+
},
|
|
1247
|
+
onCancel: () => {
|
|
1248
|
+
if (backdrop) backdrop.style.display = "flex";
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
editor.open(screenshotDataUrl);
|
|
1252
|
+
}
|
|
287
1253
|
function bindModalEvents(cfg, opts) {
|
|
288
1254
|
const backdrop = document.getElementById("cf-modal-backdrop");
|
|
289
1255
|
if (!backdrop) return;
|
|
@@ -306,38 +1272,15 @@ function bindModalEvents(cfg, opts) {
|
|
|
306
1272
|
backdrop.querySelector("#cf-remove-screenshot")?.addEventListener("click", () => {
|
|
307
1273
|
collapseModal(cfg, opts);
|
|
308
1274
|
});
|
|
309
|
-
backdrop.querySelector("#cf-
|
|
310
|
-
|
|
311
|
-
backdrop.style.display = "none";
|
|
312
|
-
try {
|
|
313
|
-
const annotations = await opts.onHighlight();
|
|
314
|
-
if (annotations && annotations.length > 0) {
|
|
315
|
-
annotationsData = annotations;
|
|
316
|
-
}
|
|
317
|
-
} catch {
|
|
318
|
-
}
|
|
319
|
-
backdrop.style.display = "flex";
|
|
320
|
-
const hlBtn = backdrop.querySelector("#cf-highlight-btn");
|
|
321
|
-
if (hlBtn && annotationsData.length > 0) {
|
|
322
|
-
hlBtn.textContent = `Surligner (${annotationsData.length})`;
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
backdrop.querySelector("#cf-mask-btn")?.addEventListener("click", async () => {
|
|
326
|
-
if (!opts?.onHighlight) return;
|
|
327
|
-
backdrop.style.display = "none";
|
|
328
|
-
try {
|
|
329
|
-
const annotations = await opts.onHighlight();
|
|
330
|
-
if (annotations && annotations.length > 0) {
|
|
331
|
-
annotationsData = [...annotationsData, ...annotations];
|
|
332
|
-
}
|
|
333
|
-
} catch {
|
|
334
|
-
}
|
|
335
|
-
backdrop.style.display = "flex";
|
|
1275
|
+
backdrop.querySelector("#cf-annotate-btn")?.addEventListener("click", () => {
|
|
1276
|
+
openAnnotationEditor(cfg, opts);
|
|
336
1277
|
});
|
|
337
1278
|
backdrop.querySelector("#cf-submit")?.addEventListener("click", () => {
|
|
338
1279
|
const desc = backdrop.querySelector("#cf-desc")?.value;
|
|
339
|
-
const
|
|
340
|
-
const
|
|
1280
|
+
const nameEl = backdrop.querySelector("#cf-name");
|
|
1281
|
+
const emailEl = backdrop.querySelector("#cf-email");
|
|
1282
|
+
const name = nameEl?.value || currentUser?.name;
|
|
1283
|
+
const email = emailEl?.value || currentUser?.email;
|
|
341
1284
|
if (!desc?.trim()) {
|
|
342
1285
|
const descEl = backdrop.querySelector("#cf-desc");
|
|
343
1286
|
if (descEl) {
|
|
@@ -351,7 +1294,7 @@ function bindModalEvents(cfg, opts) {
|
|
|
351
1294
|
description: desc.trim(),
|
|
352
1295
|
type: "BUG",
|
|
353
1296
|
priority: "MEDIUM",
|
|
354
|
-
screenshot: screenshotDataUrl || void 0,
|
|
1297
|
+
screenshot: annotatedImageUrl || screenshotDataUrl || void 0,
|
|
355
1298
|
annotations: annotationsData.length > 0 ? annotationsData : void 0,
|
|
356
1299
|
reporter_name: name?.trim() || void 0,
|
|
357
1300
|
reporter_email: email?.trim() || void 0
|
|
@@ -379,33 +1322,93 @@ function showToast(msg) {
|
|
|
379
1322
|
setTimeout(() => toast.remove(), 3e3);
|
|
380
1323
|
}
|
|
381
1324
|
function injectStyles(cfg) {
|
|
1325
|
+
const existingStyle = document.getElementById("cf-widget-styles");
|
|
1326
|
+
if (existingStyle) existingStyle.remove();
|
|
1327
|
+
const btn = cfg.button || {};
|
|
1328
|
+
const sizePreset = SIZE_PRESETS[btn.size || "default"] || SIZE_PRESETS.default;
|
|
1329
|
+
const variantPreset = VARIANT_PRESETS[btn.variant || "default"] || VARIANT_PRESETS.default;
|
|
1330
|
+
const bgColor = btn.backgroundColor || cfg.color || variantPreset.bg;
|
|
1331
|
+
const textColor = btn.textColor || variantPreset.color;
|
|
1332
|
+
const borderRadius = btn.borderRadius || "8px";
|
|
1333
|
+
const padding = btn.padding || sizePreset.padding;
|
|
1334
|
+
const fontSize = btn.fontSize || sizePreset.fontSize;
|
|
1335
|
+
const fontWeight = btn.fontWeight || "500";
|
|
1336
|
+
const border = btn.border || variantPreset.border;
|
|
1337
|
+
const boxShadow = btn.boxShadow || "0 2px 8px rgba(0,0,0,0.15)";
|
|
1338
|
+
const primaryColor = cfg.color || "#1e3a5f";
|
|
382
1339
|
const style = document.createElement("style");
|
|
383
1340
|
style.id = "cf-widget-styles";
|
|
384
1341
|
style.textContent = `
|
|
385
1342
|
@keyframes cf-toast-in { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:translateY(0); } }
|
|
386
1343
|
@keyframes cf-modal-in { from { opacity:0; transform:scale(.96) translateY(8px); } to { opacity:1; transform:scale(1) translateY(0); } }
|
|
387
1344
|
|
|
1345
|
+
/* User card styles */
|
|
1346
|
+
.cf-user-card {
|
|
1347
|
+
display:flex;
|
|
1348
|
+
align-items:center;
|
|
1349
|
+
gap:12px;
|
|
1350
|
+
padding:12px;
|
|
1351
|
+
background:#f8f9fa;
|
|
1352
|
+
border-radius:10px;
|
|
1353
|
+
margin-bottom:4px;
|
|
1354
|
+
}
|
|
1355
|
+
.cf-user-avatar {
|
|
1356
|
+
width:40px;
|
|
1357
|
+
height:40px;
|
|
1358
|
+
border-radius:50%;
|
|
1359
|
+
object-fit:cover;
|
|
1360
|
+
}
|
|
1361
|
+
.cf-user-avatar-placeholder {
|
|
1362
|
+
width:40px;
|
|
1363
|
+
height:40px;
|
|
1364
|
+
border-radius:50%;
|
|
1365
|
+
background:${primaryColor};
|
|
1366
|
+
color:#fff;
|
|
1367
|
+
display:flex;
|
|
1368
|
+
align-items:center;
|
|
1369
|
+
justify-content:center;
|
|
1370
|
+
font-size:16px;
|
|
1371
|
+
font-weight:600;
|
|
1372
|
+
}
|
|
1373
|
+
.cf-user-info {
|
|
1374
|
+
flex:1;
|
|
1375
|
+
min-width:0;
|
|
1376
|
+
}
|
|
1377
|
+
.cf-user-name {
|
|
1378
|
+
font-size:14px;
|
|
1379
|
+
font-weight:600;
|
|
1380
|
+
color:#1a1a2e;
|
|
1381
|
+
}
|
|
1382
|
+
.cf-user-email {
|
|
1383
|
+
font-size:12px;
|
|
1384
|
+
color:#666;
|
|
1385
|
+
overflow:hidden;
|
|
1386
|
+
text-overflow:ellipsis;
|
|
1387
|
+
white-space:nowrap;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
/* Trigger button - now inline, not fixed position */
|
|
388
1391
|
#cf-trigger {
|
|
389
1392
|
position:fixed;
|
|
390
|
-
${getPositionCSS(cfg.position)}
|
|
1393
|
+
${getPositionCSS(cfg.position || "bottom-right")}
|
|
391
1394
|
z-index:100000;
|
|
392
|
-
background:${
|
|
393
|
-
color
|
|
394
|
-
border
|
|
395
|
-
border-radius
|
|
396
|
-
padding
|
|
397
|
-
font-size
|
|
398
|
-
font-weight
|
|
1395
|
+
background:${bgColor};
|
|
1396
|
+
color:${textColor};
|
|
1397
|
+
border:${border};
|
|
1398
|
+
border-radius:${borderRadius};
|
|
1399
|
+
padding:${padding};
|
|
1400
|
+
font-size:${fontSize};
|
|
1401
|
+
font-weight:${fontWeight};
|
|
399
1402
|
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
|
400
1403
|
cursor:pointer;
|
|
401
|
-
box-shadow
|
|
402
|
-
display:flex;
|
|
1404
|
+
box-shadow:${boxShadow};
|
|
1405
|
+
display:inline-flex;
|
|
403
1406
|
align-items:center;
|
|
404
1407
|
gap:8px;
|
|
405
|
-
transition:transform .15s,box-shadow .15s;
|
|
1408
|
+
transition:transform .15s,box-shadow .15s,opacity .15s;
|
|
406
1409
|
}
|
|
407
|
-
#cf-trigger:hover {
|
|
408
|
-
#cf-trigger svg { width:
|
|
1410
|
+
#cf-trigger:hover { opacity:0.9; }
|
|
1411
|
+
#cf-trigger svg { width:16px; height:16px; flex-shrink:0; }
|
|
409
1412
|
|
|
410
1413
|
#cf-modal-backdrop {
|
|
411
1414
|
position:fixed;
|
|
@@ -413,9 +1416,30 @@ function injectStyles(cfg) {
|
|
|
413
1416
|
background:rgba(0,0,0,.45);
|
|
414
1417
|
z-index:100001;
|
|
415
1418
|
display:flex;
|
|
1419
|
+
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
|
1420
|
+
}
|
|
1421
|
+
#cf-modal-backdrop.cf-backdrop--center {
|
|
416
1422
|
align-items:center;
|
|
417
1423
|
justify-content:center;
|
|
418
|
-
|
|
1424
|
+
}
|
|
1425
|
+
#cf-modal-backdrop.cf-backdrop--corner {
|
|
1426
|
+
padding:20px;
|
|
1427
|
+
}
|
|
1428
|
+
#cf-modal-backdrop.cf-pos-bottom-right {
|
|
1429
|
+
align-items:flex-end;
|
|
1430
|
+
justify-content:flex-end;
|
|
1431
|
+
}
|
|
1432
|
+
#cf-modal-backdrop.cf-pos-bottom-left {
|
|
1433
|
+
align-items:flex-end;
|
|
1434
|
+
justify-content:flex-start;
|
|
1435
|
+
}
|
|
1436
|
+
#cf-modal-backdrop.cf-pos-top-right {
|
|
1437
|
+
align-items:flex-start;
|
|
1438
|
+
justify-content:flex-end;
|
|
1439
|
+
}
|
|
1440
|
+
#cf-modal-backdrop.cf-pos-top-left {
|
|
1441
|
+
align-items:flex-start;
|
|
1442
|
+
justify-content:flex-start;
|
|
419
1443
|
}
|
|
420
1444
|
|
|
421
1445
|
.cf-modal {
|
|
@@ -485,7 +1509,7 @@ function injectStyles(cfg) {
|
|
|
485
1509
|
transition:all .15s;
|
|
486
1510
|
}
|
|
487
1511
|
.cf-tool-highlight {
|
|
488
|
-
background:${
|
|
1512
|
+
background:${primaryColor};
|
|
489
1513
|
color:#fff;
|
|
490
1514
|
}
|
|
491
1515
|
.cf-tool-highlight:hover { opacity:.9; }
|
|
@@ -533,8 +1557,8 @@ function injectStyles(cfg) {
|
|
|
533
1557
|
}
|
|
534
1558
|
.cf-field input::placeholder, .cf-field textarea::placeholder { color:#bbb; }
|
|
535
1559
|
.cf-field input:focus, .cf-field textarea:focus {
|
|
536
|
-
border-color:${
|
|
537
|
-
box-shadow:0 0 0 3px ${
|
|
1560
|
+
border-color:${primaryColor};
|
|
1561
|
+
box-shadow:0 0 0 3px ${primaryColor}18;
|
|
538
1562
|
}
|
|
539
1563
|
.cf-field textarea { resize:vertical; min-height:100px; }
|
|
540
1564
|
|
|
@@ -573,7 +1597,7 @@ function injectStyles(cfg) {
|
|
|
573
1597
|
padding:13px;
|
|
574
1598
|
border:none;
|
|
575
1599
|
border-radius:10px;
|
|
576
|
-
background:${
|
|
1600
|
+
background:${primaryColor};
|
|
577
1601
|
color:#fff;
|
|
578
1602
|
font-size:15px;
|
|
579
1603
|
font-weight:600;
|
|
@@ -581,7 +1605,7 @@ function injectStyles(cfg) {
|
|
|
581
1605
|
font-family:inherit;
|
|
582
1606
|
transition:all .15s;
|
|
583
1607
|
}
|
|
584
|
-
.cf-submit-btn:hover { opacity:.92; transform:translateY(-1px); box-shadow:0 4px 12px ${
|
|
1608
|
+
.cf-submit-btn:hover { opacity:.92; transform:translateY(-1px); box-shadow:0 4px 12px ${primaryColor}40; }
|
|
585
1609
|
.cf-submit-btn:disabled { opacity:.5; cursor:not-allowed; transform:none; }
|
|
586
1610
|
|
|
587
1611
|
.cf-cancel-btn {
|
|
@@ -612,7 +1636,7 @@ function injectStyles(cfg) {
|
|
|
612
1636
|
text-decoration:none;
|
|
613
1637
|
font-weight:500;
|
|
614
1638
|
}
|
|
615
|
-
.cf-footer a:hover { color:${
|
|
1639
|
+
.cf-footer a:hover { color:${primaryColor}; }
|
|
616
1640
|
`;
|
|
617
1641
|
document.head.appendChild(style);
|
|
618
1642
|
}
|
|
@@ -624,15 +1648,22 @@ function getPositionCSS(pos) {
|
|
|
624
1648
|
return "top:20px;right:20px;";
|
|
625
1649
|
case "top-left":
|
|
626
1650
|
return "top:20px;left:20px;";
|
|
1651
|
+
case "center":
|
|
1652
|
+
return "bottom:20px;left:50%;transform:translateX(-50%);";
|
|
627
1653
|
default:
|
|
628
1654
|
return "bottom:20px;right:20px;";
|
|
629
1655
|
}
|
|
630
1656
|
}
|
|
631
1657
|
function getTriggerHTML(cfg) {
|
|
1658
|
+
const btn = cfg.button || {};
|
|
1659
|
+
const text = btn.text || cfg.text || "Report Bug";
|
|
1660
|
+
const showIcon = btn.showIcon !== false;
|
|
1661
|
+
const customClass = btn.className || "";
|
|
1662
|
+
const icon = showIcon ? `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>` : "";
|
|
632
1663
|
return `
|
|
633
|
-
<button id="cf-trigger">
|
|
634
|
-
|
|
635
|
-
${
|
|
1664
|
+
<button id="cf-trigger" class="${customClass}">
|
|
1665
|
+
${icon}
|
|
1666
|
+
${text}
|
|
636
1667
|
</button>
|
|
637
1668
|
`;
|
|
638
1669
|
}
|
|
@@ -641,21 +1672,37 @@ function getCheckflowLogo() {
|
|
|
641
1672
|
}
|
|
642
1673
|
function getModalHTML(cfg, expanded) {
|
|
643
1674
|
const modalClass = expanded ? "cf-modal cf-modal--expanded" : "cf-modal cf-modal--compact";
|
|
644
|
-
const
|
|
1675
|
+
const hasUser = currentUser && (currentUser.name || currentUser.email);
|
|
1676
|
+
const userInfoCard = hasUser ? `
|
|
1677
|
+
<div class="cf-user-card">
|
|
1678
|
+
${currentUser?.avatar ? `<img class="cf-user-avatar" src="${currentUser.avatar}" alt="${currentUser.name || "User"}" />` : `<div class="cf-user-avatar-placeholder">${(currentUser?.name || currentUser?.email || "U").charAt(0).toUpperCase()}</div>`}
|
|
1679
|
+
<div class="cf-user-info">
|
|
1680
|
+
${currentUser?.name ? `<div class="cf-user-name">${currentUser.name}</div>` : ""}
|
|
1681
|
+
${currentUser?.email ? `<div class="cf-user-email">${currentUser.email}</div>` : ""}
|
|
1682
|
+
</div>
|
|
1683
|
+
</div>
|
|
1684
|
+
` : "";
|
|
1685
|
+
const nameEmailFields = hasUser ? "" : `
|
|
645
1686
|
<div class="cf-field">
|
|
646
|
-
<label>Nom <span>(
|
|
1687
|
+
<label>Nom <span>(optionnel)</span></label>
|
|
647
1688
|
<input id="cf-name" type="text" placeholder="Votre nom" />
|
|
648
1689
|
</div>
|
|
649
1690
|
<div class="cf-field">
|
|
650
|
-
<label>Email <span>(
|
|
1691
|
+
<label>Email <span>(optionnel)</span></label>
|
|
651
1692
|
<input id="cf-email" type="email" placeholder="votre.email@exemple.com" />
|
|
652
1693
|
</div>
|
|
1694
|
+
`;
|
|
1695
|
+
const formFields = `
|
|
1696
|
+
${userInfoCard}
|
|
1697
|
+
${nameEmailFields}
|
|
653
1698
|
<div class="cf-field">
|
|
654
1699
|
<label>Description <span>(obligatoire)</span></label>
|
|
655
1700
|
<textarea id="cf-desc" placeholder="Quel est le probl\xE8me ? Que vous attendiez-vous \xE0 voir ?"></textarea>
|
|
656
1701
|
</div>
|
|
657
1702
|
`;
|
|
658
1703
|
if (expanded && screenshotDataUrl) {
|
|
1704
|
+
const displayImg = annotatedImageUrl || screenshotDataUrl;
|
|
1705
|
+
const annotLabel = annotationsData.length > 0 ? `Annoter (${annotationsData.length})` : "Annoter";
|
|
659
1706
|
return `
|
|
660
1707
|
<div class="${modalClass}">
|
|
661
1708
|
<div class="cf-modal-header">
|
|
@@ -664,10 +1711,9 @@ function getModalHTML(cfg, expanded) {
|
|
|
664
1711
|
</div>
|
|
665
1712
|
<div class="cf-modal-body">
|
|
666
1713
|
<div class="cf-screenshot-panel">
|
|
667
|
-
<img src="${
|
|
1714
|
+
<img id="cf-screenshot-img" src="${displayImg}" alt="Capture d'\xE9cran" />
|
|
668
1715
|
<div class="cf-screenshot-tools">
|
|
669
|
-
<button class="cf-tool-highlight" id="cf-
|
|
670
|
-
<button class="cf-tool-mask" id="cf-mask-btn">Masquer</button>
|
|
1716
|
+
<button class="cf-tool-highlight" id="cf-annotate-btn">${annotLabel}</button>
|
|
671
1717
|
</div>
|
|
672
1718
|
</div>
|
|
673
1719
|
<div class="cf-form-panel">
|
|
@@ -697,6 +1743,246 @@ function getModalHTML(cfg, expanded) {
|
|
|
697
1743
|
</div>
|
|
698
1744
|
`;
|
|
699
1745
|
}
|
|
1746
|
+
var DEFAULT_BUTTON_CONFIG, SIZE_PRESETS, VARIANT_PRESETS, DEFAULT_CONFIG, container, onSubmitCallback, screenshotDataUrl, annotatedImageUrl, annotationsData, isExpanded, currentCfg, currentUser;
|
|
1747
|
+
var init_widget = __esm({
|
|
1748
|
+
"src/widget.ts"() {
|
|
1749
|
+
"use strict";
|
|
1750
|
+
init_editor();
|
|
1751
|
+
DEFAULT_BUTTON_CONFIG = {
|
|
1752
|
+
text: "Report Bug",
|
|
1753
|
+
backgroundColor: "#1e3a5f",
|
|
1754
|
+
textColor: "#ffffff",
|
|
1755
|
+
borderRadius: "8px",
|
|
1756
|
+
padding: "10px 18px",
|
|
1757
|
+
fontSize: "14px",
|
|
1758
|
+
fontWeight: "500",
|
|
1759
|
+
border: "none",
|
|
1760
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
|
|
1761
|
+
className: "",
|
|
1762
|
+
showIcon: true,
|
|
1763
|
+
size: "default",
|
|
1764
|
+
variant: "default"
|
|
1765
|
+
};
|
|
1766
|
+
SIZE_PRESETS = {
|
|
1767
|
+
xs: { padding: "6px 10px", fontSize: "12px" },
|
|
1768
|
+
sm: { padding: "8px 14px", fontSize: "13px" },
|
|
1769
|
+
default: { padding: "10px 18px", fontSize: "14px" },
|
|
1770
|
+
lg: { padding: "12px 24px", fontSize: "15px" }
|
|
1771
|
+
};
|
|
1772
|
+
VARIANT_PRESETS = {
|
|
1773
|
+
default: { bg: "#1e3a5f", color: "#ffffff", border: "none" },
|
|
1774
|
+
outline: { bg: "transparent", color: "#1e3a5f", border: "1px solid #1e3a5f" },
|
|
1775
|
+
ghost: { bg: "transparent", color: "#1e3a5f", border: "none" },
|
|
1776
|
+
link: { bg: "transparent", color: "#1e3a5f", border: "none" }
|
|
1777
|
+
};
|
|
1778
|
+
DEFAULT_CONFIG = {
|
|
1779
|
+
position: "bottom-right",
|
|
1780
|
+
color: "#1e3a5f",
|
|
1781
|
+
text: "Report Bug",
|
|
1782
|
+
showOnInit: true,
|
|
1783
|
+
button: DEFAULT_BUTTON_CONFIG
|
|
1784
|
+
};
|
|
1785
|
+
container = null;
|
|
1786
|
+
onSubmitCallback = null;
|
|
1787
|
+
screenshotDataUrl = null;
|
|
1788
|
+
annotatedImageUrl = null;
|
|
1789
|
+
annotationsData = [];
|
|
1790
|
+
isExpanded = false;
|
|
1791
|
+
currentCfg = { ...DEFAULT_CONFIG };
|
|
1792
|
+
currentUser = void 0;
|
|
1793
|
+
}
|
|
1794
|
+
});
|
|
1795
|
+
|
|
1796
|
+
// src/index.ts
|
|
1797
|
+
var index_exports = {};
|
|
1798
|
+
__export(index_exports, {
|
|
1799
|
+
captureScreenshot: () => captureScreenshot,
|
|
1800
|
+
clearUser: () => clearUser,
|
|
1801
|
+
destroy: () => destroy,
|
|
1802
|
+
hideWidget: () => hideWidget,
|
|
1803
|
+
init: () => init,
|
|
1804
|
+
openFeedbackModal: () => openFeedbackModal2,
|
|
1805
|
+
sendFeedback: () => sendFeedback,
|
|
1806
|
+
setUser: () => setUser,
|
|
1807
|
+
showWidget: () => showWidget
|
|
1808
|
+
});
|
|
1809
|
+
module.exports = __toCommonJS(index_exports);
|
|
1810
|
+
|
|
1811
|
+
// src/collector.ts
|
|
1812
|
+
function collectContext() {
|
|
1813
|
+
const ua = navigator.userAgent;
|
|
1814
|
+
return {
|
|
1815
|
+
url: window.location.href,
|
|
1816
|
+
viewport: {
|
|
1817
|
+
width: window.innerWidth,
|
|
1818
|
+
height: window.innerHeight,
|
|
1819
|
+
device: window.innerWidth < 768 ? "mobile" : window.innerWidth < 1024 ? "tablet" : "desktop"
|
|
1820
|
+
},
|
|
1821
|
+
user_agent: ua,
|
|
1822
|
+
browser: detectBrowser(ua),
|
|
1823
|
+
os: detectOS(ua),
|
|
1824
|
+
locale: navigator.language,
|
|
1825
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
function collectPerformance() {
|
|
1829
|
+
try {
|
|
1830
|
+
const entries = performance.getEntriesByType("paint");
|
|
1831
|
+
const metrics = {};
|
|
1832
|
+
entries.forEach((e) => {
|
|
1833
|
+
if (e.name === "first-contentful-paint") metrics.fcp = Math.round(e.startTime);
|
|
1834
|
+
});
|
|
1835
|
+
const nav = performance.getEntriesByType("navigation")[0];
|
|
1836
|
+
if (nav) {
|
|
1837
|
+
metrics.dom_load = Math.round(nav.domContentLoadedEventEnd - nav.startTime);
|
|
1838
|
+
metrics.load = Math.round(nav.loadEventEnd - nav.startTime);
|
|
1839
|
+
}
|
|
1840
|
+
return Object.keys(metrics).length > 0 ? metrics : void 0;
|
|
1841
|
+
} catch {
|
|
1842
|
+
return void 0;
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
function detectBrowser(ua) {
|
|
1846
|
+
if (ua.includes("Firefox/")) return "Firefox";
|
|
1847
|
+
if (ua.includes("Edg/")) return "Edge";
|
|
1848
|
+
if (ua.includes("Chrome/")) return "Chrome";
|
|
1849
|
+
if (ua.includes("Safari/")) return "Safari";
|
|
1850
|
+
return "Unknown";
|
|
1851
|
+
}
|
|
1852
|
+
function detectOS(ua) {
|
|
1853
|
+
if (ua.includes("Windows")) return "Windows";
|
|
1854
|
+
if (ua.includes("Mac OS")) return "macOS";
|
|
1855
|
+
if (ua.includes("Linux")) return "Linux";
|
|
1856
|
+
if (ua.includes("Android")) return "Android";
|
|
1857
|
+
if (ua.includes("iPhone") || ua.includes("iPad")) return "iOS";
|
|
1858
|
+
return "Unknown";
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
// src/console-interceptor.ts
|
|
1862
|
+
var MAX_LOGS = 50;
|
|
1863
|
+
var logs = [];
|
|
1864
|
+
var errorsCapture = [];
|
|
1865
|
+
var installed = false;
|
|
1866
|
+
function installInterceptors() {
|
|
1867
|
+
if (installed) return;
|
|
1868
|
+
installed = true;
|
|
1869
|
+
const origConsole = {
|
|
1870
|
+
log: console.log,
|
|
1871
|
+
warn: console.warn,
|
|
1872
|
+
error: console.error
|
|
1873
|
+
};
|
|
1874
|
+
["log", "warn", "error"].forEach((level) => {
|
|
1875
|
+
console[level] = (...args) => {
|
|
1876
|
+
logs.push({
|
|
1877
|
+
level,
|
|
1878
|
+
message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" "),
|
|
1879
|
+
timestamp: Date.now()
|
|
1880
|
+
});
|
|
1881
|
+
if (logs.length > MAX_LOGS) logs.shift();
|
|
1882
|
+
origConsole[level](...args);
|
|
1883
|
+
};
|
|
1884
|
+
});
|
|
1885
|
+
window.addEventListener("error", (event) => {
|
|
1886
|
+
errorsCapture.push({
|
|
1887
|
+
message: event.message,
|
|
1888
|
+
filename: event.filename,
|
|
1889
|
+
lineno: event.lineno,
|
|
1890
|
+
colno: event.colno,
|
|
1891
|
+
timestamp: Date.now()
|
|
1892
|
+
});
|
|
1893
|
+
if (errorsCapture.length > MAX_LOGS) errorsCapture.shift();
|
|
1894
|
+
});
|
|
1895
|
+
window.addEventListener("unhandledrejection", (event) => {
|
|
1896
|
+
errorsCapture.push({
|
|
1897
|
+
message: String(event.reason),
|
|
1898
|
+
type: "unhandledrejection",
|
|
1899
|
+
timestamp: Date.now()
|
|
1900
|
+
});
|
|
1901
|
+
if (errorsCapture.length > MAX_LOGS) errorsCapture.shift();
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1904
|
+
function getConsoleLogs() {
|
|
1905
|
+
return [...logs];
|
|
1906
|
+
}
|
|
1907
|
+
function getJavascriptErrors() {
|
|
1908
|
+
return [...errorsCapture];
|
|
1909
|
+
}
|
|
1910
|
+
function clearLogs() {
|
|
1911
|
+
logs = [];
|
|
1912
|
+
errorsCapture = [];
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
// src/network-interceptor.ts
|
|
1916
|
+
var MAX_LOGS2 = 50;
|
|
1917
|
+
var logs2 = [];
|
|
1918
|
+
var installed2 = false;
|
|
1919
|
+
function installNetworkInterceptor() {
|
|
1920
|
+
if (installed2 || typeof window === "undefined") return;
|
|
1921
|
+
installed2 = true;
|
|
1922
|
+
const origFetch = window.fetch;
|
|
1923
|
+
window.fetch = async function(...args) {
|
|
1924
|
+
const start = Date.now();
|
|
1925
|
+
const req = new Request(...args);
|
|
1926
|
+
const entry = {
|
|
1927
|
+
method: req.method,
|
|
1928
|
+
url: req.url,
|
|
1929
|
+
type: "fetch",
|
|
1930
|
+
timestamp: start
|
|
1931
|
+
};
|
|
1932
|
+
try {
|
|
1933
|
+
const res = await origFetch.apply(this, args);
|
|
1934
|
+
entry.status = res.status;
|
|
1935
|
+
entry.duration = Date.now() - start;
|
|
1936
|
+
entry.response_type = res.headers.get("content-type") || void 0;
|
|
1937
|
+
pushLog(entry);
|
|
1938
|
+
return res;
|
|
1939
|
+
} catch (err) {
|
|
1940
|
+
entry.duration = Date.now() - start;
|
|
1941
|
+
entry.error = err.message || "Network error";
|
|
1942
|
+
pushLog(entry);
|
|
1943
|
+
throw err;
|
|
1944
|
+
}
|
|
1945
|
+
};
|
|
1946
|
+
const origOpen = XMLHttpRequest.prototype.open;
|
|
1947
|
+
const origSend = XMLHttpRequest.prototype.send;
|
|
1948
|
+
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
|
|
1949
|
+
this.__cf_method = method;
|
|
1950
|
+
this.__cf_url = String(url);
|
|
1951
|
+
return origOpen.apply(this, [method, url, ...rest]);
|
|
1952
|
+
};
|
|
1953
|
+
XMLHttpRequest.prototype.send = function(...args) {
|
|
1954
|
+
const start = Date.now();
|
|
1955
|
+
const xhr = this;
|
|
1956
|
+
xhr.addEventListener("loadend", () => {
|
|
1957
|
+
const entry = {
|
|
1958
|
+
method: xhr.__cf_method || "GET",
|
|
1959
|
+
url: xhr.__cf_url || "",
|
|
1960
|
+
status: xhr.status,
|
|
1961
|
+
duration: Date.now() - start,
|
|
1962
|
+
type: "xhr",
|
|
1963
|
+
timestamp: start,
|
|
1964
|
+
response_type: xhr.getResponseHeader("content-type") || void 0
|
|
1965
|
+
};
|
|
1966
|
+
if (xhr.status === 0) entry.error = "Request failed";
|
|
1967
|
+
pushLog(entry);
|
|
1968
|
+
});
|
|
1969
|
+
return origSend.apply(this, args);
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
function pushLog(entry) {
|
|
1973
|
+
if (entry.url.includes("/sdk/feedback")) return;
|
|
1974
|
+
logs2.push(entry);
|
|
1975
|
+
if (logs2.length > MAX_LOGS2) logs2.shift();
|
|
1976
|
+
}
|
|
1977
|
+
function getNetworkLogs() {
|
|
1978
|
+
return [...logs2];
|
|
1979
|
+
}
|
|
1980
|
+
function clearNetworkLogs() {
|
|
1981
|
+
logs2 = [];
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
// src/index.ts
|
|
1985
|
+
init_widget();
|
|
700
1986
|
|
|
701
1987
|
// src/screenshot.ts
|
|
702
1988
|
var screenshotData = null;
|
|
@@ -749,130 +2035,6 @@ function clearScreenshot() {
|
|
|
749
2035
|
screenshotData = null;
|
|
750
2036
|
}
|
|
751
2037
|
|
|
752
|
-
// src/highlighter.ts
|
|
753
|
-
var overlay = null;
|
|
754
|
-
var highlights = [];
|
|
755
|
-
var isActive = false;
|
|
756
|
-
var onDoneCallback = null;
|
|
757
|
-
function getSelector(el) {
|
|
758
|
-
if (el.id) return `#${el.id}`;
|
|
759
|
-
const parts = [];
|
|
760
|
-
let current = el;
|
|
761
|
-
while (current && current !== document.body) {
|
|
762
|
-
let sel = current.tagName.toLowerCase();
|
|
763
|
-
if (current.id) {
|
|
764
|
-
parts.unshift(`#${current.id}`);
|
|
765
|
-
break;
|
|
766
|
-
}
|
|
767
|
-
if (current.className && typeof current.className === "string") {
|
|
768
|
-
const cls = current.className.trim().split(/\s+/).slice(0, 2).join(".");
|
|
769
|
-
if (cls) sel += `.${cls}`;
|
|
770
|
-
}
|
|
771
|
-
const parent = current.parentElement;
|
|
772
|
-
if (parent) {
|
|
773
|
-
const siblings = Array.from(parent.children).filter((c) => c.tagName === current.tagName);
|
|
774
|
-
if (siblings.length > 1) sel += `:nth-child(${Array.from(parent.children).indexOf(current) + 1})`;
|
|
775
|
-
}
|
|
776
|
-
parts.unshift(sel);
|
|
777
|
-
current = current.parentElement;
|
|
778
|
-
}
|
|
779
|
-
return parts.join(" > ");
|
|
780
|
-
}
|
|
781
|
-
function createOverlay() {
|
|
782
|
-
overlay = document.createElement("div");
|
|
783
|
-
overlay.id = "cf-highlight-overlay";
|
|
784
|
-
overlay.style.cssText = "position:fixed;top:0;left:0;width:100%;height:100%;z-index:99999;cursor:crosshair;";
|
|
785
|
-
const toolbar = document.createElement("div");
|
|
786
|
-
toolbar.style.cssText = "position:fixed;top:12px;left:50%;transform:translateX(-50%);z-index:100002;background:#1e3a5f;color:#fff;padding:8px 16px;border-radius:10px;font-family:system-ui;font-size:13px;display:flex;align-items:center;gap:12px;box-shadow:0 4px 20px rgba(0,0,0,.25);";
|
|
787
|
-
toolbar.innerHTML = `
|
|
788
|
-
<span>Click elements to highlight them</span>
|
|
789
|
-
<button id="cf-hl-done" style="background:#10b981;color:#fff;border:none;border-radius:6px;padding:6px 14px;font-size:12px;cursor:pointer;font-weight:500;">Done (${highlights.length})</button>
|
|
790
|
-
<button id="cf-hl-cancel" style="background:transparent;color:#fff;border:1px solid rgba(255,255,255,.3);border-radius:6px;padding:6px 14px;font-size:12px;cursor:pointer;">Cancel</button>
|
|
791
|
-
`;
|
|
792
|
-
overlay.appendChild(toolbar);
|
|
793
|
-
document.body.appendChild(overlay);
|
|
794
|
-
overlay.addEventListener("click", handleClick);
|
|
795
|
-
overlay.addEventListener("mousemove", handleHover);
|
|
796
|
-
document.getElementById("cf-hl-done")?.addEventListener("click", finishHighlighting);
|
|
797
|
-
document.getElementById("cf-hl-cancel")?.addEventListener("click", cancelHighlighting);
|
|
798
|
-
}
|
|
799
|
-
var hoverBox = null;
|
|
800
|
-
function handleHover(e) {
|
|
801
|
-
if (!hoverBox) {
|
|
802
|
-
hoverBox = document.createElement("div");
|
|
803
|
-
hoverBox.style.cssText = "position:fixed;border:2px solid #0c66e4;background:rgba(12,102,228,0.08);pointer-events:none;z-index:100001;border-radius:3px;transition:all 0.05s;";
|
|
804
|
-
document.body.appendChild(hoverBox);
|
|
805
|
-
}
|
|
806
|
-
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
807
|
-
if (target && target !== overlay && !overlay?.contains(target)) {
|
|
808
|
-
const rect = target.getBoundingClientRect();
|
|
809
|
-
hoverBox.style.left = rect.left + "px";
|
|
810
|
-
hoverBox.style.top = rect.top + "px";
|
|
811
|
-
hoverBox.style.width = rect.width + "px";
|
|
812
|
-
hoverBox.style.height = rect.height + "px";
|
|
813
|
-
hoverBox.style.display = "block";
|
|
814
|
-
} else {
|
|
815
|
-
hoverBox.style.display = "none";
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
function handleClick(e) {
|
|
819
|
-
e.preventDefault();
|
|
820
|
-
e.stopPropagation();
|
|
821
|
-
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
822
|
-
if (!target || target === overlay || overlay?.contains(target)) return;
|
|
823
|
-
const rect = target.getBoundingClientRect();
|
|
824
|
-
highlights.push({ el: target, rect });
|
|
825
|
-
const marker = document.createElement("div");
|
|
826
|
-
marker.className = "cf-hl-marker";
|
|
827
|
-
marker.style.cssText = `position:fixed;left:${rect.left}px;top:${rect.top}px;width:${rect.width}px;height:${rect.height}px;border:2px solid #ae2a19;background:rgba(174,42,25,0.12);z-index:100001;pointer-events:none;border-radius:3px;`;
|
|
828
|
-
const badge = document.createElement("div");
|
|
829
|
-
badge.style.cssText = "position:absolute;top:-10px;right:-10px;background:#ae2a19;color:#fff;width:20px;height:20px;border-radius:50%;font-size:11px;display:flex;align-items:center;justify-content:center;font-family:system-ui;font-weight:600;";
|
|
830
|
-
badge.textContent = String(highlights.length);
|
|
831
|
-
marker.appendChild(badge);
|
|
832
|
-
overlay?.appendChild(marker);
|
|
833
|
-
const doneBtn = document.getElementById("cf-hl-done");
|
|
834
|
-
if (doneBtn) doneBtn.textContent = `Done (${highlights.length})`;
|
|
835
|
-
}
|
|
836
|
-
function finishHighlighting() {
|
|
837
|
-
const annotations = highlights.map((h) => ({
|
|
838
|
-
selector: getSelector(h.el),
|
|
839
|
-
tagName: h.el.tagName.toLowerCase(),
|
|
840
|
-
text: h.el.textContent?.slice(0, 100) || void 0,
|
|
841
|
-
rect: { x: Math.round(h.rect.x), y: Math.round(h.rect.y), width: Math.round(h.rect.width), height: Math.round(h.rect.height) },
|
|
842
|
-
note: h.note
|
|
843
|
-
}));
|
|
844
|
-
cleanup();
|
|
845
|
-
onDoneCallback?.(annotations);
|
|
846
|
-
}
|
|
847
|
-
function cancelHighlighting() {
|
|
848
|
-
cleanup();
|
|
849
|
-
onDoneCallback?.([]);
|
|
850
|
-
}
|
|
851
|
-
function cleanup() {
|
|
852
|
-
if (hoverBox) {
|
|
853
|
-
hoverBox.remove();
|
|
854
|
-
hoverBox = null;
|
|
855
|
-
}
|
|
856
|
-
if (overlay) {
|
|
857
|
-
overlay.remove();
|
|
858
|
-
overlay = null;
|
|
859
|
-
}
|
|
860
|
-
highlights = [];
|
|
861
|
-
isActive = false;
|
|
862
|
-
}
|
|
863
|
-
function startHighlighting() {
|
|
864
|
-
return new Promise((resolve) => {
|
|
865
|
-
if (isActive) {
|
|
866
|
-
resolve([]);
|
|
867
|
-
return;
|
|
868
|
-
}
|
|
869
|
-
isActive = true;
|
|
870
|
-
highlights = [];
|
|
871
|
-
onDoneCallback = resolve;
|
|
872
|
-
createOverlay();
|
|
873
|
-
});
|
|
874
|
-
}
|
|
875
|
-
|
|
876
2038
|
// src/index.ts
|
|
877
2039
|
var SDK_VERSION = "1.1.0";
|
|
878
2040
|
var DEFAULT_ENDPOINT = "https://api.checkflow.space/api/v1";
|
|
@@ -904,7 +2066,7 @@ function init(cfg) {
|
|
|
904
2066
|
},
|
|
905
2067
|
{
|
|
906
2068
|
onScreenshot: () => captureScreenshot(),
|
|
907
|
-
|
|
2069
|
+
user: config.user
|
|
908
2070
|
}
|
|
909
2071
|
);
|
|
910
2072
|
}
|
|
@@ -934,12 +2096,22 @@ async function sendFeedback(data) {
|
|
|
934
2096
|
if (data.annotations && data.annotations.length > 0) {
|
|
935
2097
|
payload.annotations = data.annotations;
|
|
936
2098
|
}
|
|
937
|
-
if (
|
|
2099
|
+
if (config.user?.name) {
|
|
2100
|
+
payload.reporter_name = config.user.name;
|
|
2101
|
+
} else if (data.reporter_name) {
|
|
938
2102
|
payload.reporter_name = data.reporter_name;
|
|
939
2103
|
}
|
|
940
|
-
if (
|
|
2104
|
+
if (config.user?.email) {
|
|
2105
|
+
payload.reporter_email = config.user.email;
|
|
2106
|
+
} else if (data.reporter_email) {
|
|
941
2107
|
payload.reporter_email = data.reporter_email;
|
|
942
2108
|
}
|
|
2109
|
+
if (config.user?.id) {
|
|
2110
|
+
payload.user_id = config.user.id;
|
|
2111
|
+
}
|
|
2112
|
+
if (config.user?.avatar) {
|
|
2113
|
+
payload.user_avatar = config.user.avatar;
|
|
2114
|
+
}
|
|
943
2115
|
try {
|
|
944
2116
|
const res = await fetch(`${config.endpoint}/sdk/feedback`, {
|
|
945
2117
|
method: "POST",
|
|
@@ -976,10 +2148,44 @@ function showWidget() {
|
|
|
976
2148
|
},
|
|
977
2149
|
{
|
|
978
2150
|
onScreenshot: () => captureScreenshot(),
|
|
979
|
-
|
|
2151
|
+
user: config.user
|
|
980
2152
|
}
|
|
981
2153
|
);
|
|
982
2154
|
}
|
|
2155
|
+
function setUser(user) {
|
|
2156
|
+
if (config) {
|
|
2157
|
+
config.user = user;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
function clearUser() {
|
|
2161
|
+
if (config) {
|
|
2162
|
+
config.user = void 0;
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
function openFeedbackModal2() {
|
|
2166
|
+
if (!config) return;
|
|
2167
|
+
Promise.resolve().then(() => (init_widget(), widget_exports)).then(({ openFeedbackModal: openModal2 }) => {
|
|
2168
|
+
openModal2(
|
|
2169
|
+
config.widget,
|
|
2170
|
+
(data) => {
|
|
2171
|
+
sendFeedback({
|
|
2172
|
+
title: data.title,
|
|
2173
|
+
description: data.description,
|
|
2174
|
+
type: data.type,
|
|
2175
|
+
priority: data.priority,
|
|
2176
|
+
screenshot_data: data.screenshot,
|
|
2177
|
+
annotations: data.annotations,
|
|
2178
|
+
reporter_name: data.reporter_name,
|
|
2179
|
+
reporter_email: data.reporter_email
|
|
2180
|
+
});
|
|
2181
|
+
},
|
|
2182
|
+
{
|
|
2183
|
+
onScreenshot: () => captureScreenshot(),
|
|
2184
|
+
user: config.user
|
|
2185
|
+
}
|
|
2186
|
+
);
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
983
2189
|
function hideWidget() {
|
|
984
2190
|
unmountWidget();
|
|
985
2191
|
}
|
|
@@ -993,10 +2199,12 @@ function destroy() {
|
|
|
993
2199
|
// Annotate the CommonJS export names for ESM import in node:
|
|
994
2200
|
0 && (module.exports = {
|
|
995
2201
|
captureScreenshot,
|
|
2202
|
+
clearUser,
|
|
996
2203
|
destroy,
|
|
997
2204
|
hideWidget,
|
|
998
2205
|
init,
|
|
2206
|
+
openFeedbackModal,
|
|
999
2207
|
sendFeedback,
|
|
1000
|
-
|
|
1001
|
-
|
|
2208
|
+
setUser,
|
|
2209
|
+
showWidget
|
|
1002
2210
|
});
|