@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.
@@ -182,11 +182,11 @@ class Modal {
182
182
  }
183
183
  create() {
184
184
  // Create overlay
185
- this.overlay = document.createElement('div');
186
- this.overlay.className = 'uxbert-overlay';
185
+ this.overlay = document.createElement("div");
186
+ this.overlay.className = "uxbert-overlay";
187
187
  // Create modal
188
- this.modal = document.createElement('div');
189
- this.modal.className = 'uxbert-modal';
188
+ this.modal = document.createElement("div");
189
+ this.modal.className = "uxbert-modal";
190
190
  // Modal HTML
191
191
  this.modal.innerHTML = `
192
192
  <div class="uxbert-modal-header">
@@ -231,12 +231,13 @@ class Modal {
231
231
 
232
232
  <div class="uxbert-form-group">
233
233
  <label class="uxbert-form-label" for="uxbert-priority">Priority</label>
234
- <select id="uxbert-priority" class="uxbert-form-select">
235
- <option value="Low">Low</option>
236
- <option value="Medium" selected>Medium</option>
237
- <option value="High">High</option>
238
- <option value="Critical">Critical</option>
239
- </select>
234
+ <select id="uxbert-priority" class="uxbert-form-select">
235
+ <option value="5" selected>Lowest</option>
236
+ <option value="4">Low</option>
237
+ <option value="3" selected>Medium</option>
238
+ <option value="2">High</option>
239
+ <option value="1">Highest</option>
240
+ </select>
240
241
  </div>
241
242
 
242
243
  <div class="uxbert-capture-action" id="uxbert-capture-action">
@@ -280,67 +281,67 @@ class Modal {
280
281
  if (!this.modal)
281
282
  return;
282
283
  // Close button
283
- const closeBtn = this.modal.querySelector('.uxbert-modal-close');
284
- closeBtn.addEventListener('click', () => this.close());
284
+ const closeBtn = this.modal.querySelector(".uxbert-modal-close");
285
+ closeBtn.addEventListener("click", () => this.close());
285
286
  // Cancel button
286
- const cancelBtn = this.modal.querySelector('#uxbert-cancel-btn');
287
- cancelBtn.addEventListener('click', () => this.close());
287
+ const cancelBtn = this.modal.querySelector("#uxbert-cancel-btn");
288
+ cancelBtn.addEventListener("click", () => this.close());
288
289
  // Overlay click to close
289
- this.overlay?.addEventListener('click', (e) => {
290
+ this.overlay?.addEventListener("click", (e) => {
290
291
  if (e.target === this.overlay) {
291
292
  this.close();
292
293
  }
293
294
  });
294
295
  // Form submit
295
- const form = this.modal.querySelector('#uxbert-issue-form');
296
- form.addEventListener('submit', (e) => {
296
+ const form = this.modal.querySelector("#uxbert-issue-form");
297
+ form.addEventListener("submit", (e) => {
297
298
  e.preventDefault();
298
299
  this.handleSubmit();
299
300
  });
300
301
  // Capture button
301
- const captureBtn = this.modal.querySelector('#uxbert-capture-btn');
302
- captureBtn.addEventListener('click', () => {
302
+ const captureBtn = this.modal.querySelector("#uxbert-capture-btn");
303
+ captureBtn.addEventListener("click", () => {
303
304
  if (this.callbacks.onCapture) {
304
305
  this.callbacks.onCapture();
305
306
  }
306
307
  });
307
308
  // Annotate button
308
- const annotateBtn = this.modal.querySelector('#uxbert-annotate-btn');
309
- annotateBtn.addEventListener('click', () => {
309
+ const annotateBtn = this.modal.querySelector("#uxbert-annotate-btn");
310
+ annotateBtn.addEventListener("click", () => {
310
311
  if (this.callbacks.onAnnotate) {
311
312
  this.callbacks.onAnnotate();
312
313
  }
313
314
  });
314
315
  // Retake button
315
- const retakeBtn = this.modal.querySelector('#uxbert-retake-btn');
316
- retakeBtn.addEventListener('click', () => {
316
+ const retakeBtn = this.modal.querySelector("#uxbert-retake-btn");
317
+ retakeBtn.addEventListener("click", () => {
317
318
  if (this.callbacks.onRetake) {
318
319
  this.callbacks.onRetake();
319
320
  }
320
321
  });
321
322
  // n8n button
322
- const n8nBtn = this.modal.querySelector('#uxbert-n8n-btn');
323
- n8nBtn.addEventListener('click', () => {
323
+ const n8nBtn = this.modal.querySelector("#uxbert-n8n-btn");
324
+ n8nBtn.addEventListener("click", () => {
324
325
  this.handleN8nSubmit();
325
326
  });
326
327
  }
327
328
  handleN8nSubmit() {
328
329
  if (!this.modal)
329
330
  return;
330
- const titleInput = this.modal.querySelector('#uxbert-title');
331
- const descriptionInput = this.modal.querySelector('#uxbert-description');
332
- const prioritySelect = this.modal.querySelector('#uxbert-priority');
331
+ const titleInput = this.modal.querySelector("#uxbert-title");
332
+ const descriptionInput = this.modal.querySelector("#uxbert-description");
333
+ const prioritySelect = this.modal.querySelector("#uxbert-priority");
333
334
  const title = titleInput.value;
334
335
  const description = descriptionInput.value;
335
336
  const priority = prioritySelect.value;
336
337
  if (!title.trim()) {
337
- alert('Please enter an issue title');
338
+ alert("Please enter an issue title");
338
339
  return;
339
340
  }
340
341
  const issueData = {
341
342
  title: title.trim(),
342
343
  description: description.trim(),
343
- priority
344
+ priority,
344
345
  };
345
346
  if (this.callbacks.onSendToN8n) {
346
347
  this.callbacks.onSendToN8n(issueData);
@@ -349,30 +350,34 @@ class Modal {
349
350
  handleSubmit() {
350
351
  if (!this.modal)
351
352
  return;
352
- const titleInput = this.modal.querySelector('#uxbert-title');
353
- const descriptionInput = this.modal.querySelector('#uxbert-description');
354
- const prioritySelect = this.modal.querySelector('#uxbert-priority');
353
+ const titleInput = this.modal.querySelector("#uxbert-title");
354
+ const descriptionInput = this.modal.querySelector("#uxbert-description");
355
+ const prioritySelect = this.modal.querySelector("#uxbert-priority");
355
356
  const title = titleInput.value;
356
357
  const description = descriptionInput.value;
357
358
  const priority = prioritySelect.value;
358
359
  if (!title.trim()) {
359
- alert('Please enter an issue title');
360
+ alert("Please enter an issue title");
360
361
  return;
361
362
  }
362
363
  const issueData = {
363
364
  title: title.trim(),
364
365
  description: description.trim(),
365
- priority
366
+ priority,
366
367
  };
367
368
  if (this.callbacks.onSubmit) {
368
369
  this.callbacks.onSubmit(issueData);
369
370
  }
370
371
  }
371
372
  open() {
372
- this.overlay?.classList.add('active');
373
+ this.overlay?.classList.add("active");
374
+ // Prevent body scroll when modal is open
375
+ document.body.style.overflow = "hidden";
373
376
  }
374
377
  close() {
375
- this.overlay?.classList.remove('active');
378
+ this.overlay?.classList.remove("active");
379
+ // Restore body scroll when modal is closed
380
+ document.body.style.overflow = "";
376
381
  this.reset();
377
382
  if (this.callbacks.onClose) {
378
383
  this.callbacks.onClose();
@@ -381,47 +386,49 @@ class Modal {
381
386
  setScreenshot(screenshot) {
382
387
  if (!this.modal)
383
388
  return;
384
- const container = this.modal.querySelector('#uxbert-screenshot-container');
385
- const img = this.modal.querySelector('#uxbert-screenshot-img');
386
- const captureAction = this.modal.querySelector('#uxbert-capture-action');
387
- const submitBtn = this.modal.querySelector('#uxbert-submit-btn');
389
+ const container = this.modal.querySelector("#uxbert-screenshot-container");
390
+ const img = this.modal.querySelector("#uxbert-screenshot-img");
391
+ const captureAction = this.modal.querySelector("#uxbert-capture-action");
392
+ const submitBtn = this.modal.querySelector("#uxbert-submit-btn");
388
393
  if (screenshot) {
389
394
  img.src = screenshot;
390
- container.style.display = 'block';
391
- captureAction.style.display = 'none';
395
+ container.style.display = "block";
396
+ captureAction.style.display = "none";
392
397
  submitBtn.disabled = false;
393
398
  }
394
399
  else {
395
- container.style.display = 'none';
396
- captureAction.style.display = 'block';
400
+ container.style.display = "none";
401
+ captureAction.style.display = "block";
397
402
  submitBtn.disabled = true;
398
403
  }
399
404
  }
400
405
  getCaptureMode() {
401
406
  if (!this.modal)
402
- return 'viewport';
403
- const viewportRadio = this.modal.querySelector('#uxbert-capture-viewport');
404
- return viewportRadio.checked ? 'viewport' : 'fullpage';
407
+ return "viewport";
408
+ const viewportRadio = this.modal.querySelector("#uxbert-capture-viewport");
409
+ return viewportRadio.checked ? "viewport" : "fullpage";
405
410
  }
406
411
  setN8nButtonVisible(visible) {
407
412
  if (!this.modal)
408
413
  return;
409
- const n8nBtn = this.modal.querySelector('#uxbert-n8n-btn');
414
+ const n8nBtn = this.modal.querySelector("#uxbert-n8n-btn");
410
415
  if (n8nBtn) {
411
- n8nBtn.style.display = visible ? 'inline-block' : 'none';
416
+ n8nBtn.style.display = visible ? "inline-block" : "none";
412
417
  }
413
418
  }
414
419
  reset() {
415
420
  if (!this.modal)
416
421
  return;
417
- this.modal.querySelector('#uxbert-title').value = '';
418
- this.modal.querySelector('#uxbert-description').value = '';
419
- this.modal.querySelector('#uxbert-priority').value = 'Medium';
422
+ this.modal.querySelector("#uxbert-title").value = "";
423
+ this.modal.querySelector("#uxbert-description").value = "";
424
+ this.modal.querySelector("#uxbert-priority").value = "Medium";
420
425
  this.setScreenshot(null);
421
426
  }
422
427
  destroy() {
423
428
  if (this.overlay && this.overlay.parentNode) {
424
429
  this.overlay.parentNode.removeChild(this.overlay);
430
+ // Restore body scroll when destroying modal
431
+ document.body.style.overflow = "";
425
432
  this.overlay = null;
426
433
  this.modal = null;
427
434
  }
@@ -10220,24 +10227,23 @@ class N8nIntegration {
10220
10227
  if (!this.config || !this.config.webhookUrl) {
10221
10228
  return {
10222
10229
  success: false,
10223
- message: 'n8n webhook URL is not configured',
10224
- error: 'MISSING_CONFIG'
10230
+ message: "n8n webhook URL is not configured",
10231
+ error: "MISSING_CONFIG",
10225
10232
  };
10226
10233
  }
10227
10234
  if (!this.config.enabled) {
10228
10235
  return {
10229
10236
  success: false,
10230
- message: 'n8n integration is disabled',
10231
- error: 'DISABLED'
10237
+ message: "n8n integration is disabled",
10238
+ error: "DISABLED",
10232
10239
  };
10233
10240
  }
10234
10241
  try {
10235
- // Prepare payload
10236
- const payload = this.preparePayload(issueData);
10237
- // Setup request headers
10242
+ // Prepare FormData with binary screenshot
10243
+ const formData = await this.prepareFormData(issueData);
10244
+ // Setup request headers (don't set Content-Type - browser will set it with boundary for FormData)
10238
10245
  const headers = {
10239
- 'Content-Type': 'application/json',
10240
- ...this.config.headers
10246
+ ...this.config.headers,
10241
10247
  };
10242
10248
  // Setup timeout
10243
10249
  const timeout = this.config.timeout || 30000; // 30 seconds default
@@ -10245,19 +10251,19 @@ class N8nIntegration {
10245
10251
  const timeoutId = setTimeout(() => controller.abort(), timeout);
10246
10252
  // Send request
10247
10253
  const response = await fetch(this.config.webhookUrl, {
10248
- method: 'POST',
10254
+ method: "POST",
10249
10255
  headers,
10250
- body: JSON.stringify(payload),
10251
- signal: controller.signal
10256
+ body: formData,
10257
+ signal: controller.signal,
10252
10258
  });
10253
10259
  clearTimeout(timeoutId);
10254
10260
  // Check response
10255
10261
  if (!response.ok) {
10256
- const errorText = await response.text().catch(() => 'Unknown error');
10262
+ const errorText = await response.text().catch(() => "Unknown error");
10257
10263
  return {
10258
10264
  success: false,
10259
10265
  message: `n8n webhook request failed: ${response.status} ${response.statusText}`,
10260
- error: errorText
10266
+ error: errorText,
10261
10267
  };
10262
10268
  }
10263
10269
  // Parse response
@@ -10267,76 +10273,87 @@ class N8nIntegration {
10267
10273
  }
10268
10274
  catch (e) {
10269
10275
  // Some webhooks don't return JSON
10270
- responseData = { message: 'Success' };
10276
+ responseData = { message: "Success" };
10271
10277
  }
10272
10278
  return {
10273
10279
  success: true,
10274
- message: 'Issue sent to n8n successfully',
10275
- workflowId: responseData.workflowId || responseData.executionId
10280
+ message: "Issue sent to n8n successfully",
10281
+ workflowId: responseData.workflowId || responseData.executionId,
10276
10282
  };
10277
10283
  }
10278
10284
  catch (error) {
10279
10285
  if (error instanceof Error) {
10280
- if (error.name === 'AbortError') {
10286
+ if (error.name === "AbortError") {
10281
10287
  return {
10282
10288
  success: false,
10283
- message: 'Request to n8n webhook timed out',
10284
- error: 'TIMEOUT'
10289
+ message: "Request to n8n webhook timed out",
10290
+ error: "TIMEOUT",
10285
10291
  };
10286
10292
  }
10287
10293
  return {
10288
10294
  success: false,
10289
10295
  message: `Failed to send issue to n8n: ${error.message}`,
10290
- error: error.message
10296
+ error: error.message,
10291
10297
  };
10292
10298
  }
10293
10299
  return {
10294
10300
  success: false,
10295
- message: 'Unknown error occurred while sending to n8n',
10296
- error: 'UNKNOWN'
10301
+ message: "Unknown error occurred while sending to n8n",
10302
+ error: "UNKNOWN",
10297
10303
  };
10298
10304
  }
10299
10305
  }
10300
10306
  /**
10301
- * Prepare payload for n8n webhook
10302
- * This formats the data in a structure that's easy to work with in n8n
10307
+ * Convert base64 data URL to Blob
10303
10308
  */
10304
- preparePayload(issueData) {
10305
- return {
10306
- // Main issue data
10307
- issue: {
10308
- title: issueData.title,
10309
- description: issueData.description,
10310
- priority: issueData.priority,
10311
- createdAt: issueData.createdAt
10312
- },
10313
- // Screenshot data (base64)
10314
- screenshot: {
10315
- data: issueData.screenshot,
10316
- format: 'base64',
10317
- mimeType: 'image/png'
10318
- },
10319
- // Device and browser information
10320
- context: {
10321
- browser: issueData.deviceInfo.browser,
10322
- os: issueData.deviceInfo.os,
10323
- screenResolution: issueData.deviceInfo.screenResolution,
10324
- viewport: issueData.deviceInfo.viewport,
10325
- url: issueData.deviceInfo.url,
10326
- userAgent: issueData.deviceInfo.userAgent,
10327
- language: issueData.deviceInfo.language,
10328
- platform: issueData.deviceInfo.platform,
10329
- cookiesEnabled: issueData.deviceInfo.cookiesEnabled,
10330
- onLine: issueData.deviceInfo.onLine,
10331
- timestamp: issueData.deviceInfo.timestamp
10332
- },
10333
- // Metadata for tracking
10334
- metadata: {
10335
- source: 'uxbert-reportly',
10336
- version: '1.0.0',
10337
- timestamp: new Date().toISOString()
10338
- }
10339
- };
10309
+ base64ToBlob(base64Data) {
10310
+ // Extract the base64 string and mime type
10311
+ const matches = base64Data.match(/^data:([^;]+);base64,(.+)$/);
10312
+ if (!matches) {
10313
+ throw new Error("Invalid base64 data URL");
10314
+ }
10315
+ const mimeType = matches[1];
10316
+ const base64String = matches[2];
10317
+ // Decode base64
10318
+ const byteCharacters = atob(base64String);
10319
+ const byteNumbers = new Array(byteCharacters.length);
10320
+ for (let i = 0; i < byteCharacters.length; i++) {
10321
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
10322
+ }
10323
+ const byteArray = new Uint8Array(byteNumbers);
10324
+ return new Blob([byteArray], { type: mimeType });
10325
+ }
10326
+ /**
10327
+ * Prepare FormData payload for n8n webhook
10328
+ * This sends the screenshot as a binary file instead of base64 JSON
10329
+ */
10330
+ async prepareFormData(issueData) {
10331
+ const formData = new FormData();
10332
+ // Add issue data as JSON string
10333
+ formData.append("title", issueData.title);
10334
+ formData.append("description", issueData.description);
10335
+ formData.append("priority", issueData.priority);
10336
+ formData.append("createdAt", issueData.createdAt);
10337
+ // Convert base64 screenshot to binary Blob and add as file
10338
+ const screenshotBlob = this.base64ToBlob(issueData.screenshot);
10339
+ const filename = `screenshot-${Date.now()}.png`;
10340
+ formData.append("screenshot", screenshotBlob, filename);
10341
+ // Add device info as individual fields
10342
+ formData.append("browser", issueData.deviceInfo.browser);
10343
+ formData.append("os", issueData.deviceInfo.os);
10344
+ formData.append("screenResolution", issueData.deviceInfo.screenResolution);
10345
+ formData.append("viewport", issueData.deviceInfo.viewport);
10346
+ formData.append("url", issueData.deviceInfo.url);
10347
+ formData.append("userAgent", issueData.deviceInfo.userAgent);
10348
+ formData.append("language", issueData.deviceInfo.language || "");
10349
+ formData.append("platform", issueData.deviceInfo.platform);
10350
+ formData.append("cookiesEnabled", String(issueData.deviceInfo.cookiesEnabled));
10351
+ formData.append("onLine", String(issueData.deviceInfo.onLine));
10352
+ formData.append("timestamp", issueData.deviceInfo.timestamp);
10353
+ // Add metadata
10354
+ formData.append("source", "uxbert-reportly");
10355
+ formData.append("version", "1.0.0");
10356
+ return formData;
10340
10357
  }
10341
10358
  /**
10342
10359
  * Test connection to n8n webhook
@@ -10345,28 +10362,28 @@ class N8nIntegration {
10345
10362
  if (!this.config || !this.config.webhookUrl) {
10346
10363
  return {
10347
10364
  success: false,
10348
- message: 'n8n webhook URL is not configured',
10349
- error: 'MISSING_CONFIG'
10365
+ message: "n8n webhook URL is not configured",
10366
+ error: "MISSING_CONFIG",
10350
10367
  };
10351
10368
  }
10352
10369
  try {
10353
10370
  const controller = new AbortController();
10354
10371
  const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout for test
10355
10372
  await fetch(this.config.webhookUrl, {
10356
- method: 'HEAD',
10357
- signal: controller.signal
10373
+ method: "HEAD",
10374
+ signal: controller.signal,
10358
10375
  });
10359
10376
  clearTimeout(timeoutId);
10360
10377
  return {
10361
10378
  success: true,
10362
- message: 'n8n webhook is reachable'
10379
+ message: "n8n webhook is reachable",
10363
10380
  };
10364
10381
  }
10365
10382
  catch (error) {
10366
10383
  return {
10367
10384
  success: false,
10368
- message: 'Failed to reach n8n webhook',
10369
- error: error instanceof Error ? error.message : 'UNKNOWN'
10385
+ message: "Failed to reach n8n webhook",
10386
+ error: error instanceof Error ? error.message : "UNKNOWN",
10370
10387
  };
10371
10388
  }
10372
10389
  }
@@ -10383,7 +10400,7 @@ class N8nIntegration {
10383
10400
  if (!this.config) {
10384
10401
  this.config = {
10385
10402
  webhookUrl: url,
10386
- enabled: true
10403
+ enabled: true,
10387
10404
  };
10388
10405
  }
10389
10406
  else {
@@ -10549,7 +10566,7 @@ function styleInject(css, ref) {
10549
10566
  }
10550
10567
  }
10551
10568
 
10552
- 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}}";
10569
+ 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}}";
10553
10570
  styleInject(css_248z);
10554
10571
 
10555
10572
  // Main initialization file