@uxbertlabs/reportly 1.0.18 → 1.0.19

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.
@@ -1,4 +1,4 @@
1
- import type { CompleteIssue } from '../types';
1
+ import type { CompleteIssue } from "../types";
2
2
  export interface N8nConfig {
3
3
  webhookUrl: string;
4
4
  enabled: boolean;
@@ -27,10 +27,14 @@ declare class N8nIntegration {
27
27
  */
28
28
  sendToN8n(issueData: CompleteIssue): Promise<N8nResponse>;
29
29
  /**
30
- * Prepare payload for n8n webhook
31
- * This formats the data in a structure that's easy to work with in n8n
30
+ * Convert base64 data URL to Blob
32
31
  */
33
- private preparePayload;
32
+ private base64ToBlob;
33
+ /**
34
+ * Prepare FormData payload for n8n webhook
35
+ * This sends the screenshot as a binary file instead of base64 JSON
36
+ */
37
+ private prepareFormData;
34
38
  /**
35
39
  * Test connection to n8n webhook
36
40
  */
@@ -1 +1 @@
1
- {"version":3,"file":"n8n-integration.d.ts","sourceRoot":"","sources":["../../src/features/n8n-integration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,cAAM,cAAc;IAClB,OAAO,CAAC,MAAM,CAAmB;gBAErB,MAAM,CAAC,EAAE,SAAS;IAI9B;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAIlC;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACG,SAAS,CAAC,SAAS,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC;IA4F/D;;;OAGG;IACH,OAAO,CAAC,cAAc;IAyCtB;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAiC5C;;OAEG;IACH,SAAS,IAAI,SAAS,GAAG,IAAI;IAI7B;;OAEG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAWhC;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAKnC;AAED,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"n8n-integration.d.ts","sourceRoot":"","sources":["../../src/features/n8n-integration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,cAAM,cAAc;IAClB,OAAO,CAAC,MAAM,CAAmB;gBAErB,MAAM,CAAC,EAAE,SAAS;IAI9B;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAIlC;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACG,SAAS,CAAC,SAAS,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC;IA0F/D;;OAEG;IACH,OAAO,CAAC,YAAY;IAsBpB;;;OAGG;YACW,eAAe;IAkC7B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAiC5C;;OAEG;IACH,SAAS,IAAI,SAAS,GAAG,IAAI;IAI7B;;OAEG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAWhC;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAKnC;AAED,eAAe,cAAc,CAAC"}
@@ -184,11 +184,11 @@ class Modal {
184
184
  }
185
185
  create() {
186
186
  // Create overlay
187
- this.overlay = document.createElement('div');
188
- this.overlay.className = 'uxbert-overlay';
187
+ this.overlay = document.createElement("div");
188
+ this.overlay.className = "uxbert-overlay";
189
189
  // Create modal
190
- this.modal = document.createElement('div');
191
- this.modal.className = 'uxbert-modal';
190
+ this.modal = document.createElement("div");
191
+ this.modal.className = "uxbert-modal";
192
192
  // Modal HTML
193
193
  this.modal.innerHTML = `
194
194
  <div class="uxbert-modal-header">
@@ -233,12 +233,13 @@ class Modal {
233
233
 
234
234
  <div class="uxbert-form-group">
235
235
  <label class="uxbert-form-label" for="uxbert-priority">Priority</label>
236
- <select id="uxbert-priority" class="uxbert-form-select">
237
- <option value="Low">Low</option>
238
- <option value="Medium" selected>Medium</option>
239
- <option value="High">High</option>
240
- <option value="Critical">Critical</option>
241
- </select>
236
+ <select id="uxbert-priority" class="uxbert-form-select">
237
+ <option value="5" selected>Lowest</option>
238
+ <option value="4">Low</option>
239
+ <option value="3" selected>Medium</option>
240
+ <option value="2">High</option>
241
+ <option value="1">Highest</option>
242
+ </select>
242
243
  </div>
243
244
 
244
245
  <div class="uxbert-capture-action" id="uxbert-capture-action">
@@ -282,67 +283,67 @@ class Modal {
282
283
  if (!this.modal)
283
284
  return;
284
285
  // Close button
285
- const closeBtn = this.modal.querySelector('.uxbert-modal-close');
286
- closeBtn.addEventListener('click', () => this.close());
286
+ const closeBtn = this.modal.querySelector(".uxbert-modal-close");
287
+ closeBtn.addEventListener("click", () => this.close());
287
288
  // Cancel button
288
- const cancelBtn = this.modal.querySelector('#uxbert-cancel-btn');
289
- cancelBtn.addEventListener('click', () => this.close());
289
+ const cancelBtn = this.modal.querySelector("#uxbert-cancel-btn");
290
+ cancelBtn.addEventListener("click", () => this.close());
290
291
  // Overlay click to close
291
- this.overlay?.addEventListener('click', (e) => {
292
+ this.overlay?.addEventListener("click", (e) => {
292
293
  if (e.target === this.overlay) {
293
294
  this.close();
294
295
  }
295
296
  });
296
297
  // Form submit
297
- const form = this.modal.querySelector('#uxbert-issue-form');
298
- form.addEventListener('submit', (e) => {
298
+ const form = this.modal.querySelector("#uxbert-issue-form");
299
+ form.addEventListener("submit", (e) => {
299
300
  e.preventDefault();
300
301
  this.handleSubmit();
301
302
  });
302
303
  // Capture button
303
- const captureBtn = this.modal.querySelector('#uxbert-capture-btn');
304
- captureBtn.addEventListener('click', () => {
304
+ const captureBtn = this.modal.querySelector("#uxbert-capture-btn");
305
+ captureBtn.addEventListener("click", () => {
305
306
  if (this.callbacks.onCapture) {
306
307
  this.callbacks.onCapture();
307
308
  }
308
309
  });
309
310
  // Annotate button
310
- const annotateBtn = this.modal.querySelector('#uxbert-annotate-btn');
311
- annotateBtn.addEventListener('click', () => {
311
+ const annotateBtn = this.modal.querySelector("#uxbert-annotate-btn");
312
+ annotateBtn.addEventListener("click", () => {
312
313
  if (this.callbacks.onAnnotate) {
313
314
  this.callbacks.onAnnotate();
314
315
  }
315
316
  });
316
317
  // Retake button
317
- const retakeBtn = this.modal.querySelector('#uxbert-retake-btn');
318
- retakeBtn.addEventListener('click', () => {
318
+ const retakeBtn = this.modal.querySelector("#uxbert-retake-btn");
319
+ retakeBtn.addEventListener("click", () => {
319
320
  if (this.callbacks.onRetake) {
320
321
  this.callbacks.onRetake();
321
322
  }
322
323
  });
323
324
  // n8n button
324
- const n8nBtn = this.modal.querySelector('#uxbert-n8n-btn');
325
- n8nBtn.addEventListener('click', () => {
325
+ const n8nBtn = this.modal.querySelector("#uxbert-n8n-btn");
326
+ n8nBtn.addEventListener("click", () => {
326
327
  this.handleN8nSubmit();
327
328
  });
328
329
  }
329
330
  handleN8nSubmit() {
330
331
  if (!this.modal)
331
332
  return;
332
- const titleInput = this.modal.querySelector('#uxbert-title');
333
- const descriptionInput = this.modal.querySelector('#uxbert-description');
334
- const prioritySelect = this.modal.querySelector('#uxbert-priority');
333
+ const titleInput = this.modal.querySelector("#uxbert-title");
334
+ const descriptionInput = this.modal.querySelector("#uxbert-description");
335
+ const prioritySelect = this.modal.querySelector("#uxbert-priority");
335
336
  const title = titleInput.value;
336
337
  const description = descriptionInput.value;
337
338
  const priority = prioritySelect.value;
338
339
  if (!title.trim()) {
339
- alert('Please enter an issue title');
340
+ alert("Please enter an issue title");
340
341
  return;
341
342
  }
342
343
  const issueData = {
343
344
  title: title.trim(),
344
345
  description: description.trim(),
345
- priority
346
+ priority,
346
347
  };
347
348
  if (this.callbacks.onSendToN8n) {
348
349
  this.callbacks.onSendToN8n(issueData);
@@ -351,30 +352,34 @@ class Modal {
351
352
  handleSubmit() {
352
353
  if (!this.modal)
353
354
  return;
354
- const titleInput = this.modal.querySelector('#uxbert-title');
355
- const descriptionInput = this.modal.querySelector('#uxbert-description');
356
- const prioritySelect = this.modal.querySelector('#uxbert-priority');
355
+ const titleInput = this.modal.querySelector("#uxbert-title");
356
+ const descriptionInput = this.modal.querySelector("#uxbert-description");
357
+ const prioritySelect = this.modal.querySelector("#uxbert-priority");
357
358
  const title = titleInput.value;
358
359
  const description = descriptionInput.value;
359
360
  const priority = prioritySelect.value;
360
361
  if (!title.trim()) {
361
- alert('Please enter an issue title');
362
+ alert("Please enter an issue title");
362
363
  return;
363
364
  }
364
365
  const issueData = {
365
366
  title: title.trim(),
366
367
  description: description.trim(),
367
- priority
368
+ priority,
368
369
  };
369
370
  if (this.callbacks.onSubmit) {
370
371
  this.callbacks.onSubmit(issueData);
371
372
  }
372
373
  }
373
374
  open() {
374
- this.overlay?.classList.add('active');
375
+ this.overlay?.classList.add("active");
376
+ // Prevent body scroll when modal is open
377
+ document.body.style.overflow = "hidden";
375
378
  }
376
379
  close() {
377
- this.overlay?.classList.remove('active');
380
+ this.overlay?.classList.remove("active");
381
+ // Restore body scroll when modal is closed
382
+ document.body.style.overflow = "";
378
383
  this.reset();
379
384
  if (this.callbacks.onClose) {
380
385
  this.callbacks.onClose();
@@ -383,47 +388,49 @@ class Modal {
383
388
  setScreenshot(screenshot) {
384
389
  if (!this.modal)
385
390
  return;
386
- const container = this.modal.querySelector('#uxbert-screenshot-container');
387
- const img = this.modal.querySelector('#uxbert-screenshot-img');
388
- const captureAction = this.modal.querySelector('#uxbert-capture-action');
389
- const submitBtn = this.modal.querySelector('#uxbert-submit-btn');
391
+ const container = this.modal.querySelector("#uxbert-screenshot-container");
392
+ const img = this.modal.querySelector("#uxbert-screenshot-img");
393
+ const captureAction = this.modal.querySelector("#uxbert-capture-action");
394
+ const submitBtn = this.modal.querySelector("#uxbert-submit-btn");
390
395
  if (screenshot) {
391
396
  img.src = screenshot;
392
- container.style.display = 'block';
393
- captureAction.style.display = 'none';
397
+ container.style.display = "block";
398
+ captureAction.style.display = "none";
394
399
  submitBtn.disabled = false;
395
400
  }
396
401
  else {
397
- container.style.display = 'none';
398
- captureAction.style.display = 'block';
402
+ container.style.display = "none";
403
+ captureAction.style.display = "block";
399
404
  submitBtn.disabled = true;
400
405
  }
401
406
  }
402
407
  getCaptureMode() {
403
408
  if (!this.modal)
404
- return 'viewport';
405
- const viewportRadio = this.modal.querySelector('#uxbert-capture-viewport');
406
- return viewportRadio.checked ? 'viewport' : 'fullpage';
409
+ return "viewport";
410
+ const viewportRadio = this.modal.querySelector("#uxbert-capture-viewport");
411
+ return viewportRadio.checked ? "viewport" : "fullpage";
407
412
  }
408
413
  setN8nButtonVisible(visible) {
409
414
  if (!this.modal)
410
415
  return;
411
- const n8nBtn = this.modal.querySelector('#uxbert-n8n-btn');
416
+ const n8nBtn = this.modal.querySelector("#uxbert-n8n-btn");
412
417
  if (n8nBtn) {
413
- n8nBtn.style.display = visible ? 'inline-block' : 'none';
418
+ n8nBtn.style.display = visible ? "inline-block" : "none";
414
419
  }
415
420
  }
416
421
  reset() {
417
422
  if (!this.modal)
418
423
  return;
419
- this.modal.querySelector('#uxbert-title').value = '';
420
- this.modal.querySelector('#uxbert-description').value = '';
421
- this.modal.querySelector('#uxbert-priority').value = 'Medium';
424
+ this.modal.querySelector("#uxbert-title").value = "";
425
+ this.modal.querySelector("#uxbert-description").value = "";
426
+ this.modal.querySelector("#uxbert-priority").value = "Medium";
422
427
  this.setScreenshot(null);
423
428
  }
424
429
  destroy() {
425
430
  if (this.overlay && this.overlay.parentNode) {
426
431
  this.overlay.parentNode.removeChild(this.overlay);
432
+ // Restore body scroll when destroying modal
433
+ document.body.style.overflow = "";
427
434
  this.overlay = null;
428
435
  this.modal = null;
429
436
  }
@@ -10222,24 +10229,23 @@ class N8nIntegration {
10222
10229
  if (!this.config || !this.config.webhookUrl) {
10223
10230
  return {
10224
10231
  success: false,
10225
- message: 'n8n webhook URL is not configured',
10226
- error: 'MISSING_CONFIG'
10232
+ message: "n8n webhook URL is not configured",
10233
+ error: "MISSING_CONFIG",
10227
10234
  };
10228
10235
  }
10229
10236
  if (!this.config.enabled) {
10230
10237
  return {
10231
10238
  success: false,
10232
- message: 'n8n integration is disabled',
10233
- error: 'DISABLED'
10239
+ message: "n8n integration is disabled",
10240
+ error: "DISABLED",
10234
10241
  };
10235
10242
  }
10236
10243
  try {
10237
- // Prepare payload
10238
- const payload = this.preparePayload(issueData);
10239
- // Setup request headers
10244
+ // Prepare FormData with binary screenshot
10245
+ const formData = await this.prepareFormData(issueData);
10246
+ // Setup request headers (don't set Content-Type - browser will set it with boundary for FormData)
10240
10247
  const headers = {
10241
- 'Content-Type': 'application/json',
10242
- ...this.config.headers
10248
+ ...this.config.headers,
10243
10249
  };
10244
10250
  // Setup timeout
10245
10251
  const timeout = this.config.timeout || 30000; // 30 seconds default
@@ -10247,19 +10253,19 @@ class N8nIntegration {
10247
10253
  const timeoutId = setTimeout(() => controller.abort(), timeout);
10248
10254
  // Send request
10249
10255
  const response = await fetch(this.config.webhookUrl, {
10250
- method: 'POST',
10256
+ method: "POST",
10251
10257
  headers,
10252
- body: JSON.stringify(payload),
10253
- signal: controller.signal
10258
+ body: formData,
10259
+ signal: controller.signal,
10254
10260
  });
10255
10261
  clearTimeout(timeoutId);
10256
10262
  // Check response
10257
10263
  if (!response.ok) {
10258
- const errorText = await response.text().catch(() => 'Unknown error');
10264
+ const errorText = await response.text().catch(() => "Unknown error");
10259
10265
  return {
10260
10266
  success: false,
10261
10267
  message: `n8n webhook request failed: ${response.status} ${response.statusText}`,
10262
- error: errorText
10268
+ error: errorText,
10263
10269
  };
10264
10270
  }
10265
10271
  // Parse response
@@ -10269,76 +10275,87 @@ class N8nIntegration {
10269
10275
  }
10270
10276
  catch (e) {
10271
10277
  // Some webhooks don't return JSON
10272
- responseData = { message: 'Success' };
10278
+ responseData = { message: "Success" };
10273
10279
  }
10274
10280
  return {
10275
10281
  success: true,
10276
- message: 'Issue sent to n8n successfully',
10277
- workflowId: responseData.workflowId || responseData.executionId
10282
+ message: "Issue sent to n8n successfully",
10283
+ workflowId: responseData.workflowId || responseData.executionId,
10278
10284
  };
10279
10285
  }
10280
10286
  catch (error) {
10281
10287
  if (error instanceof Error) {
10282
- if (error.name === 'AbortError') {
10288
+ if (error.name === "AbortError") {
10283
10289
  return {
10284
10290
  success: false,
10285
- message: 'Request to n8n webhook timed out',
10286
- error: 'TIMEOUT'
10291
+ message: "Request to n8n webhook timed out",
10292
+ error: "TIMEOUT",
10287
10293
  };
10288
10294
  }
10289
10295
  return {
10290
10296
  success: false,
10291
10297
  message: `Failed to send issue to n8n: ${error.message}`,
10292
- error: error.message
10298
+ error: error.message,
10293
10299
  };
10294
10300
  }
10295
10301
  return {
10296
10302
  success: false,
10297
- message: 'Unknown error occurred while sending to n8n',
10298
- error: 'UNKNOWN'
10303
+ message: "Unknown error occurred while sending to n8n",
10304
+ error: "UNKNOWN",
10299
10305
  };
10300
10306
  }
10301
10307
  }
10302
10308
  /**
10303
- * Prepare payload for n8n webhook
10304
- * This formats the data in a structure that's easy to work with in n8n
10309
+ * Convert base64 data URL to Blob
10305
10310
  */
10306
- preparePayload(issueData) {
10307
- return {
10308
- // Main issue data
10309
- issue: {
10310
- title: issueData.title,
10311
- description: issueData.description,
10312
- priority: issueData.priority,
10313
- createdAt: issueData.createdAt
10314
- },
10315
- // Screenshot data (base64)
10316
- screenshot: {
10317
- data: issueData.screenshot,
10318
- format: 'base64',
10319
- mimeType: 'image/png'
10320
- },
10321
- // Device and browser information
10322
- context: {
10323
- browser: issueData.deviceInfo.browser,
10324
- os: issueData.deviceInfo.os,
10325
- screenResolution: issueData.deviceInfo.screenResolution,
10326
- viewport: issueData.deviceInfo.viewport,
10327
- url: issueData.deviceInfo.url,
10328
- userAgent: issueData.deviceInfo.userAgent,
10329
- language: issueData.deviceInfo.language,
10330
- platform: issueData.deviceInfo.platform,
10331
- cookiesEnabled: issueData.deviceInfo.cookiesEnabled,
10332
- onLine: issueData.deviceInfo.onLine,
10333
- timestamp: issueData.deviceInfo.timestamp
10334
- },
10335
- // Metadata for tracking
10336
- metadata: {
10337
- source: 'uxbert-reportly',
10338
- version: '1.0.0',
10339
- timestamp: new Date().toISOString()
10340
- }
10341
- };
10311
+ base64ToBlob(base64Data) {
10312
+ // Extract the base64 string and mime type
10313
+ const matches = base64Data.match(/^data:([^;]+);base64,(.+)$/);
10314
+ if (!matches) {
10315
+ throw new Error("Invalid base64 data URL");
10316
+ }
10317
+ const mimeType = matches[1];
10318
+ const base64String = matches[2];
10319
+ // Decode base64
10320
+ const byteCharacters = atob(base64String);
10321
+ const byteNumbers = new Array(byteCharacters.length);
10322
+ for (let i = 0; i < byteCharacters.length; i++) {
10323
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
10324
+ }
10325
+ const byteArray = new Uint8Array(byteNumbers);
10326
+ return new Blob([byteArray], { type: mimeType });
10327
+ }
10328
+ /**
10329
+ * Prepare FormData payload for n8n webhook
10330
+ * This sends the screenshot as a binary file instead of base64 JSON
10331
+ */
10332
+ async prepareFormData(issueData) {
10333
+ const formData = new FormData();
10334
+ // Add issue data as JSON string
10335
+ formData.append("title", issueData.title);
10336
+ formData.append("description", issueData.description);
10337
+ formData.append("priority", issueData.priority);
10338
+ formData.append("createdAt", issueData.createdAt);
10339
+ // Convert base64 screenshot to binary Blob and add as file
10340
+ const screenshotBlob = this.base64ToBlob(issueData.screenshot);
10341
+ const filename = `screenshot-${Date.now()}.png`;
10342
+ formData.append("screenshot", screenshotBlob, filename);
10343
+ // Add device info as individual fields
10344
+ formData.append("browser", issueData.deviceInfo.browser);
10345
+ formData.append("os", issueData.deviceInfo.os);
10346
+ formData.append("screenResolution", issueData.deviceInfo.screenResolution);
10347
+ formData.append("viewport", issueData.deviceInfo.viewport);
10348
+ formData.append("url", issueData.deviceInfo.url);
10349
+ formData.append("userAgent", issueData.deviceInfo.userAgent);
10350
+ formData.append("language", issueData.deviceInfo.language || "");
10351
+ formData.append("platform", issueData.deviceInfo.platform);
10352
+ formData.append("cookiesEnabled", String(issueData.deviceInfo.cookiesEnabled));
10353
+ formData.append("onLine", String(issueData.deviceInfo.onLine));
10354
+ formData.append("timestamp", issueData.deviceInfo.timestamp);
10355
+ // Add metadata
10356
+ formData.append("source", "uxbert-reportly");
10357
+ formData.append("version", "1.0.0");
10358
+ return formData;
10342
10359
  }
10343
10360
  /**
10344
10361
  * Test connection to n8n webhook
@@ -10347,28 +10364,28 @@ class N8nIntegration {
10347
10364
  if (!this.config || !this.config.webhookUrl) {
10348
10365
  return {
10349
10366
  success: false,
10350
- message: 'n8n webhook URL is not configured',
10351
- error: 'MISSING_CONFIG'
10367
+ message: "n8n webhook URL is not configured",
10368
+ error: "MISSING_CONFIG",
10352
10369
  };
10353
10370
  }
10354
10371
  try {
10355
10372
  const controller = new AbortController();
10356
10373
  const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout for test
10357
10374
  await fetch(this.config.webhookUrl, {
10358
- method: 'HEAD',
10359
- signal: controller.signal
10375
+ method: "HEAD",
10376
+ signal: controller.signal,
10360
10377
  });
10361
10378
  clearTimeout(timeoutId);
10362
10379
  return {
10363
10380
  success: true,
10364
- message: 'n8n webhook is reachable'
10381
+ message: "n8n webhook is reachable",
10365
10382
  };
10366
10383
  }
10367
10384
  catch (error) {
10368
10385
  return {
10369
10386
  success: false,
10370
- message: 'Failed to reach n8n webhook',
10371
- error: error instanceof Error ? error.message : 'UNKNOWN'
10387
+ message: "Failed to reach n8n webhook",
10388
+ error: error instanceof Error ? error.message : "UNKNOWN",
10372
10389
  };
10373
10390
  }
10374
10391
  }
@@ -10385,7 +10402,7 @@ class N8nIntegration {
10385
10402
  if (!this.config) {
10386
10403
  this.config = {
10387
10404
  webhookUrl: url,
10388
- enabled: true
10405
+ enabled: true,
10389
10406
  };
10390
10407
  }
10391
10408
  else {
@@ -10551,7 +10568,7 @@ function styleInject(css, ref) {
10551
10568
  }
10552
10569
  }
10553
10570
 
10554
- var css_248z = ":root{--uxbert-primary:#4f46e5;--uxbert-primary-hover:#4338ca;--uxbert-danger:#ef4444;--uxbert-success:#10b981;--uxbert-bg:#fff;--uxbert-text:#1f2937;--uxbert-border:#e5e7eb;--uxbert-shadow:0 10px 25px rgba(0,0,0,.1);--uxbert-z-button:999999;--uxbert-z-modal:1000000;--uxbert-z-canvas:1000001;--uxbert-z-toolbar:1000002}[data-theme=dark]{--uxbert-bg:#1f2937;--uxbert-text:#f9fafb;--uxbert-border:#374151}.uxbert-fab{align-items:center;background:var(--uxbert-primary);border:none;border-radius:50%;box-shadow:var(--uxbert-shadow);color:#fff;cursor:pointer;display:flex;font-size:24px;height:56px;justify-content:center;position:fixed;transition:all .3s ease;width:56px;z-index:var(--uxbert-z-button)}.uxbert-fab:hover{background:var(--uxbert-primary-hover);transform:scale(1.1)}.uxbert-fab.bottom-right{bottom:24px;right:24px}.uxbert-fab.bottom-left{bottom:24px;left:24px}.uxbert-fab.top-right{right:24px;top:24px}.uxbert-fab.top-left{left:24px;top:24px}.uxbert-overlay{align-items:center;animation:fadeIn .3s ease;background:rgba(0,0,0,.5);display:none;height:100%;justify-content:center;left:0;position:fixed;top:0;width:100%;z-index:var(--uxbert-z-modal)}.uxbert-overlay.active{display:flex}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.uxbert-modal{animation:slideUp .3s ease;background:var(--uxbert-bg);border-radius:12px;box-shadow:var(--uxbert-shadow);color:var(--uxbert-text);max-height:90vh;max-width:600px;overflow-y:auto;padding:24px;width:90%}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.uxbert-modal-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:20px}.uxbert-modal-title{font-size:20px;font-weight:600;margin:0}.uxbert-modal-close{align-items:center;background:none;border:none;border-radius:4px;color:var(--uxbert-text);cursor:pointer;display:flex;font-size:24px;height:32px;justify-content:center;padding:0;width:32px}.uxbert-modal-close:hover{background:var(--uxbert-border)}.uxbert-form-group{margin-bottom:16px}.uxbert-form-label{display:block;font-size:14px;font-weight:500;margin-bottom:8px}.uxbert-capture-mode{display:flex;flex-wrap:wrap;gap:12px}.uxbert-radio-label{align-items:center;border:2px solid var(--uxbert-border);border-radius:8px;cursor:pointer;display:flex;flex:1;gap:8px;min-width:140px;padding:10px 16px;transition:all .2s ease}.uxbert-radio-label:hover{background:rgba(79,70,229,.05);border-color:var(--uxbert-primary)}.uxbert-radio-label input[type=radio]{cursor:pointer;height:18px;margin:0;width:18px}.uxbert-radio-label input[type=radio]:checked{accent-color:var(--uxbert-primary)}.uxbert-radio-label:has(input[type=radio]:checked){background:rgba(79,70,229,.1);border-color:var(--uxbert-primary)}.uxbert-radio-label span{flex:1;font-size:14px;font-weight:500}.uxbert-form-input,.uxbert-form-select,.uxbert-form-textarea{background:var(--uxbert-bg);border:1px solid var(--uxbert-border);border-radius:6px;box-sizing:border-box;color:var(--uxbert-text);font-family:inherit;font-size:14px;padding:10px;width:100%}.uxbert-form-textarea{min-height:100px;resize:vertical}.uxbert-form-input:focus,.uxbert-form-select:focus,.uxbert-form-textarea:focus{border-color:var(--uxbert-primary);outline:none}.uxbert-capture-action{margin:16px 0;text-align:center}.uxbert-capture-action .uxbert-btn{min-width:200px}.uxbert-screenshot-preview{border:1px solid var(--uxbert-border);border-radius:8px;margin:16px 0;overflow:hidden}.uxbert-screenshot-preview img{display:block;width:100%}.uxbert-screenshot-actions{background:var(--uxbert-border);display:flex;gap:8px;padding:12px}.uxbert-btn{align-items:center;border:none;border-radius:6px;cursor:pointer;display:inline-flex;font-size:14px;font-weight:500;gap:8px;padding:10px 20px;transition:all .2s ease}.uxbert-btn-primary{background:var(--uxbert-primary);color:#fff}.uxbert-btn-primary:hover{background:var(--uxbert-primary-hover)}.uxbert-btn-secondary{background:var(--uxbert-border);color:var(--uxbert-text)}.uxbert-btn-secondary:hover{background:#d1d5db}.uxbert-btn-success{background:var(--uxbert-success);color:#fff}.uxbert-btn-success:hover{background:#059669}.uxbert-btn-danger{background:var(--uxbert-danger);color:#fff}.uxbert-btn-danger:hover{background:#dc2626}.uxbert-modal-actions{display:flex;gap:12px;justify-content:flex-end;margin-top:20px}.uxbert-toolbar-toggle{align-items:center;background:var(--uxbert-primary);border:none;border-radius:50%;bottom:24px;box-shadow:var(--uxbert-shadow);color:#fff;cursor:pointer;display:none;font-size:24px;height:56px;justify-content:center;position:fixed;right:24px;transition:all .3s ease;width:56px;z-index:var(--uxbert-z-toolbar)}.uxbert-toolbar-toggle.active{display:flex}.uxbert-toolbar-toggle.hidden{opacity:0;pointer-events:none;transform:scale(.8)}.uxbert-toolbar-toggle:hover{background:var(--uxbert-primary-hover);transform:scale(1.1)}.uxbert-toolbar{background:var(--uxbert-bg);border-radius:12px;bottom:24px;box-shadow:var(--uxbert-shadow);display:none;flex-direction:column;gap:8px;max-height:80vh;opacity:0;overflow-y:auto;padding:16px;pointer-events:none;position:fixed;right:24px;transform:translateY(20px);transition:all .3s ease;z-index:var(--uxbert-z-toolbar)}.uxbert-toolbar.active{display:flex}.uxbert-toolbar.expanded{opacity:1;pointer-events:all;transform:translateY(0)}.uxbert-toolbar-header{align-items:center;display:flex;gap:8px;justify-content:space-between;margin-bottom:8px}.uxbert-toolbar-title{flex:1;font-size:14px;font-weight:600}.uxbert-toolbar-exit,.uxbert-toolbar-minimize{align-items:center;background:var(--uxbert-border);border:none;border-radius:6px;color:var(--uxbert-text);cursor:pointer;display:flex;font-size:20px;font-weight:700;height:32px;justify-content:center;transition:all .2s ease;width:32px}.uxbert-toolbar-exit{background:var(--uxbert-danger);color:#fff}.uxbert-toolbar-minimize:hover{background:#d1d5db;transform:scale(1.1)}.uxbert-toolbar-exit:hover{background:#dc2626;transform:scale(1.1)}.uxbert-toolbar-tools{display:flex;flex-wrap:wrap;gap:8px}.uxbert-tool-btn{align-items:center;background:var(--uxbert-bg);border:2px solid var(--uxbert-border);border-radius:6px;cursor:pointer;display:flex;font-family:Arial,sans-serif;font-size:18px;font-weight:700;height:40px;justify-content:center;transition:all .2s ease;width:40px}.uxbert-tool-btn.active,.uxbert-tool-btn:hover{background:var(--uxbert-primary);border-color:var(--uxbert-primary);color:#fff}.uxbert-color-picker{display:flex;gap:6px;margin:8px 0}.uxbert-color-option{border:2px solid var(--uxbert-border);border-radius:50%;cursor:pointer;height:32px;transition:transform .2s ease;width:32px}.uxbert-color-option:hover{transform:scale(1.1)}.uxbert-color-option.active{border:3px solid var(--uxbert-text)}.uxbert-canvas-overlay{cursor:crosshair;display:none;left:0;position:absolute;top:0;z-index:var(--uxbert-z-canvas)}.uxbert-canvas-overlay.active{display:block}.uxbert-canvas-overlay.viewport-mode{left:0;position:fixed;top:0}.uxbert-text-input{background:hsla(0,0%,100%,.95);border:2px solid var(--uxbert-primary);border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.15);color:var(--uxbert-text);font-family:Arial,sans-serif;font-size:16px;font-weight:700;min-width:200px;outline:none;padding:8px 12px;z-index:var(--uxbert-z-toolbar)}.uxbert-text-input:focus{border-color:var(--uxbert-primary-hover);box-shadow:0 4px 16px rgba(79,70,229,.3)}.uxbert-text-input::placeholder{color:#9ca3af;font-weight:400}@media (max-width:768px){.uxbert-modal{max-height:95vh;padding:16px;width:95%}.uxbert-toolbar{bottom:90px;left:16px;max-height:60vh;right:16px;width:auto}.uxbert-toolbar-toggle{bottom:16px;height:56px;right:16px;width:56px}.uxbert-fab{font-size:20px;height:48px;width:48px}.uxbert-color-picker,.uxbert-toolbar-tools{justify-content:center}}";
10571
+ var css_248z = ":root{--uxbert-primary:#4f46e5;--uxbert-primary-hover:#4338ca;--uxbert-danger:#ef4444;--uxbert-success:#10b981;--uxbert-bg:#fff;--uxbert-text:#1f2937;--uxbert-border:#e5e7eb;--uxbert-shadow:0 10px 25px rgba(0,0,0,.1);--uxbert-z-button:999999;--uxbert-z-modal:1000000;--uxbert-z-canvas:1000001;--uxbert-z-toolbar:1000002}[data-theme=dark]{--uxbert-bg:#1f2937;--uxbert-text:#f9fafb;--uxbert-border:#374151}.uxbert-fab{align-items:center;background:var(--uxbert-primary);border:none;border-radius:50%;box-shadow:var(--uxbert-shadow);color:#fff;cursor:pointer;display:flex;font-size:24px;height:56px;justify-content:center;position:fixed;transition:all .3s ease;width:56px;z-index:var(--uxbert-z-button)}.uxbert-fab:hover{background:var(--uxbert-primary-hover);transform:scale(1.1)}.uxbert-fab.bottom-right{bottom:24px;right:24px}.uxbert-fab.bottom-left{bottom:24px;left:24px}.uxbert-fab.top-right{right:24px;top:24px}.uxbert-fab.top-left{left:24px;top:24px}.uxbert-overlay{align-items:center;animation:fadeIn .3s ease;background:rgba(0,0,0,.5);display:none;height:100%;justify-content:center;left:0;overflow:hidden;position:fixed;top:0;width:100%;z-index:var(--uxbert-z-modal)}.uxbert-overlay.active{display:flex}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.uxbert-modal{animation:slideUp .3s ease;background:var(--uxbert-bg);border-radius:12px;box-shadow:var(--uxbert-shadow);color:var(--uxbert-text);max-height:90vh;max-width:600px;overflow-y:auto;padding:24px;width:90%}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.uxbert-modal-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:20px}.uxbert-modal-title{font-size:20px;font-weight:600;margin:0}.uxbert-modal-close{align-items:center;background:none;border:none;border-radius:4px;color:var(--uxbert-text);cursor:pointer;display:flex;font-size:24px;height:32px;justify-content:center;padding:0;width:32px}.uxbert-modal-close:hover{background:var(--uxbert-border)}.uxbert-form-group{margin-bottom:16px}.uxbert-form-label{display:block;font-size:14px;font-weight:500;margin-bottom:8px}.uxbert-capture-mode{display:flex;flex-wrap:wrap;gap:12px}.uxbert-radio-label{align-items:center;border:2px solid var(--uxbert-border);border-radius:8px;cursor:pointer;display:flex;flex:1;gap:8px;min-width:140px;padding:10px 16px;transition:all .2s ease}.uxbert-radio-label:hover{background:rgba(79,70,229,.05);border-color:var(--uxbert-primary)}.uxbert-radio-label input[type=radio]{cursor:pointer;height:18px;margin:0;width:18px}.uxbert-radio-label input[type=radio]:checked{accent-color:var(--uxbert-primary)}.uxbert-radio-label:has(input[type=radio]:checked){background:rgba(79,70,229,.1);border-color:var(--uxbert-primary)}.uxbert-radio-label span{flex:1;font-size:14px;font-weight:500}.uxbert-form-input,.uxbert-form-select,.uxbert-form-textarea{background:var(--uxbert-bg);border:1px solid var(--uxbert-border);border-radius:6px;box-sizing:border-box;color:var(--uxbert-text);font-family:inherit;font-size:14px;padding:10px;width:100%}.uxbert-form-textarea{min-height:100px;resize:vertical}.uxbert-form-input:focus,.uxbert-form-select:focus,.uxbert-form-textarea:focus{border-color:var(--uxbert-primary);outline:none}.uxbert-capture-action{margin:16px 0;text-align:center}.uxbert-capture-action .uxbert-btn{min-width:200px}.uxbert-screenshot-preview{border:1px solid var(--uxbert-border);border-radius:8px;margin:16px 0;overflow:hidden}.uxbert-screenshot-preview img{display:block;width:100%}.uxbert-screenshot-actions{background:var(--uxbert-border);display:flex;gap:8px;padding:12px}.uxbert-btn{align-items:center;border:none;border-radius:6px;cursor:pointer;display:inline-flex;font-size:14px;font-weight:500;gap:8px;padding:10px 20px;transition:all .2s ease}.uxbert-btn-primary{background:var(--uxbert-primary);color:#fff}.uxbert-btn-primary:hover{background:var(--uxbert-primary-hover)}.uxbert-btn-secondary{background:var(--uxbert-border);color:var(--uxbert-text)}.uxbert-btn-secondary:hover{background:#d1d5db}.uxbert-btn-success{background:var(--uxbert-success);color:#fff}.uxbert-btn-success:hover{background:#059669}.uxbert-btn-danger{background:var(--uxbert-danger);color:#fff}.uxbert-btn-danger:hover{background:#dc2626}.uxbert-modal-actions{display:flex;gap:12px;justify-content:flex-end;margin-top:20px}.uxbert-toolbar-toggle{align-items:center;background:var(--uxbert-primary);border:none;border-radius:50%;bottom:24px;box-shadow:var(--uxbert-shadow);color:#fff;cursor:pointer;display:none;font-size:24px;height:56px;justify-content:center;position:fixed;right:24px;transition:all .3s ease;width:56px;z-index:var(--uxbert-z-toolbar)}.uxbert-toolbar-toggle.active{display:flex}.uxbert-toolbar-toggle.hidden{opacity:0;pointer-events:none;transform:scale(.8)}.uxbert-toolbar-toggle:hover{background:var(--uxbert-primary-hover);transform:scale(1.1)}.uxbert-toolbar{background:var(--uxbert-bg);border-radius:12px;bottom:24px;box-shadow:var(--uxbert-shadow);display:none;flex-direction:column;gap:8px;max-height:80vh;opacity:0;overflow-y:auto;padding:16px;pointer-events:none;position:fixed;right:24px;transform:translateY(20px);transition:all .3s ease;z-index:var(--uxbert-z-toolbar)}.uxbert-toolbar.active{display:flex}.uxbert-toolbar.expanded{opacity:1;pointer-events:all;transform:translateY(0)}.uxbert-toolbar-header{align-items:center;display:flex;gap:8px;justify-content:space-between;margin-bottom:8px}.uxbert-toolbar-title{flex:1;font-size:14px;font-weight:600}.uxbert-toolbar-exit,.uxbert-toolbar-minimize{align-items:center;background:var(--uxbert-border);border:none;border-radius:6px;color:var(--uxbert-text);cursor:pointer;display:flex;font-size:20px;font-weight:700;height:32px;justify-content:center;transition:all .2s ease;width:32px}.uxbert-toolbar-exit{background:var(--uxbert-danger);color:#fff}.uxbert-toolbar-minimize:hover{background:#d1d5db;transform:scale(1.1)}.uxbert-toolbar-exit:hover{background:#dc2626;transform:scale(1.1)}.uxbert-toolbar-tools{display:flex;flex-wrap:wrap;gap:8px}.uxbert-tool-btn{align-items:center;background:var(--uxbert-bg);border:2px solid var(--uxbert-border);border-radius:6px;cursor:pointer;display:flex;font-family:Arial,sans-serif;font-size:18px;font-weight:700;height:40px;justify-content:center;transition:all .2s ease;width:40px}.uxbert-tool-btn.active,.uxbert-tool-btn:hover{background:var(--uxbert-primary);border-color:var(--uxbert-primary);color:#fff}.uxbert-color-picker{display:flex;gap:6px;margin:8px 0}.uxbert-color-option{border:2px solid var(--uxbert-border);border-radius:50%;cursor:pointer;height:32px;transition:transform .2s ease;width:32px}.uxbert-color-option:hover{transform:scale(1.1)}.uxbert-color-option.active{border:3px solid var(--uxbert-text)}.uxbert-canvas-overlay{cursor:crosshair;display:none;left:0;position:absolute;top:0;z-index:var(--uxbert-z-canvas)}.uxbert-canvas-overlay.active{display:block}.uxbert-canvas-overlay.viewport-mode{left:0;position:fixed;top:0}.uxbert-text-input{background:hsla(0,0%,100%,.95);border:2px solid var(--uxbert-primary);border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.15);color:var(--uxbert-text);font-family:Arial,sans-serif;font-size:16px;font-weight:700;min-width:200px;outline:none;padding:8px 12px;z-index:var(--uxbert-z-toolbar)}.uxbert-text-input:focus{border-color:var(--uxbert-primary-hover);box-shadow:0 4px 16px rgba(79,70,229,.3)}.uxbert-text-input::placeholder{color:#9ca3af;font-weight:400}@media (max-width:768px){.uxbert-modal{max-height:95vh;padding:16px;width:95%}.uxbert-toolbar{bottom:90px;left:16px;max-height:60vh;right:16px;width:auto}.uxbert-toolbar-toggle{bottom:16px;height:56px;right:16px;width:56px}.uxbert-fab{font-size:20px;height:48px;width:48px}.uxbert-color-picker,.uxbert-toolbar-tools{justify-content:center}}";
10555
10572
  styleInject(css_248z);
10556
10573
 
10557
10574
  // Main initialization file