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