@uxbertlabs/reportly 1.0.5 → 1.0.7

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.
@@ -80,6 +80,7 @@ class State {
80
80
  this.screenshot = null;
81
81
  this.annotations = [];
82
82
  this.listeners = {};
83
+ this.captureMode = 'viewport';
83
84
  }
84
85
  setState(newState) {
85
86
  this.currentState = newState;
@@ -109,11 +110,18 @@ class State {
109
110
  clearAnnotations() {
110
111
  this.annotations = [];
111
112
  }
113
+ setCaptureMode(mode) {
114
+ this.captureMode = mode;
115
+ }
116
+ getCaptureMode() {
117
+ return this.captureMode;
118
+ }
112
119
  reset() {
113
120
  this.currentState = STATE.IDLE;
114
121
  this.currentIssue = null;
115
122
  this.screenshot = null;
116
123
  this.annotations = [];
124
+ this.captureMode = 'viewport';
117
125
  }
118
126
  // Event system
119
127
  on(event, callback) {
@@ -187,6 +195,20 @@ class Modal {
187
195
  </div>
188
196
 
189
197
  <form class="uxbert-form" id="uxbert-issue-form">
198
+ <div class="uxbert-form-group">
199
+ <label class="uxbert-form-label">Capture Mode</label>
200
+ <div class="uxbert-capture-mode">
201
+ <label class="uxbert-radio-label">
202
+ <input type="radio" name="capture-mode" value="viewport" id="uxbert-capture-viewport" checked />
203
+ <span>Current View</span>
204
+ </label>
205
+ <label class="uxbert-radio-label">
206
+ <input type="radio" name="capture-mode" value="fullpage" id="uxbert-capture-fullpage" />
207
+ <span>Full Page</span>
208
+ </label>
209
+ </div>
210
+ </div>
211
+
190
212
  <div class="uxbert-form-group">
191
213
  <label class="uxbert-form-label" for="uxbert-title">Issue Title *</label>
192
214
  <input
@@ -217,6 +239,12 @@ class Modal {
217
239
  </select>
218
240
  </div>
219
241
 
242
+ <div class="uxbert-capture-action" id="uxbert-capture-action">
243
+ <button type="button" class="uxbert-btn uxbert-btn-primary" id="uxbert-capture-btn">
244
+ 📸 Take Screenshot
245
+ </button>
246
+ </div>
247
+
220
248
  <div class="uxbert-screenshot-preview" id="uxbert-screenshot-container" style="display: none;">
221
249
  <img id="uxbert-screenshot-img" src="" alt="Screenshot" />
222
250
  <div class="uxbert-screenshot-actions">
@@ -233,7 +261,7 @@ class Modal {
233
261
  <button type="button" class="uxbert-btn uxbert-btn-secondary" id="uxbert-cancel-btn">
234
262
  Cancel
235
263
  </button>
236
- <button type="submit" class="uxbert-btn uxbert-btn-primary">
264
+ <button type="submit" class="uxbert-btn uxbert-btn-primary" id="uxbert-submit-btn">
237
265
  📥 Download JSON
238
266
  </button>
239
267
  </div>
@@ -266,6 +294,13 @@ class Modal {
266
294
  e.preventDefault();
267
295
  this.handleSubmit();
268
296
  });
297
+ // Capture button
298
+ const captureBtn = this.modal.querySelector('#uxbert-capture-btn');
299
+ captureBtn.addEventListener('click', () => {
300
+ if (this.callbacks.onCapture) {
301
+ this.callbacks.onCapture();
302
+ }
303
+ });
269
304
  // Annotate button
270
305
  const annotateBtn = this.modal.querySelector('#uxbert-annotate-btn');
271
306
  annotateBtn.addEventListener('click', () => {
@@ -318,14 +353,26 @@ class Modal {
318
353
  return;
319
354
  const container = this.modal.querySelector('#uxbert-screenshot-container');
320
355
  const img = this.modal.querySelector('#uxbert-screenshot-img');
356
+ const captureAction = this.modal.querySelector('#uxbert-capture-action');
357
+ const submitBtn = this.modal.querySelector('#uxbert-submit-btn');
321
358
  if (screenshot) {
322
359
  img.src = screenshot;
323
360
  container.style.display = 'block';
361
+ captureAction.style.display = 'none';
362
+ submitBtn.disabled = false;
324
363
  }
325
364
  else {
326
365
  container.style.display = 'none';
366
+ captureAction.style.display = 'block';
367
+ submitBtn.disabled = true;
327
368
  }
328
369
  }
370
+ getCaptureMode() {
371
+ if (!this.modal)
372
+ return 'viewport';
373
+ const viewportRadio = this.modal.querySelector('#uxbert-capture-viewport');
374
+ return viewportRadio.checked ? 'viewport' : 'fullpage';
375
+ }
329
376
  reset() {
330
377
  if (!this.modal)
331
378
  return;
@@ -9400,26 +9447,87 @@ class Screenshot {
9400
9447
  constructor() {
9401
9448
  this.currentScreenshot = null;
9402
9449
  }
9403
- async capture() {
9450
+ async capture(mode = 'fullpage') {
9404
9451
  try {
9405
9452
  // Hide UXbert UI elements before capturing
9406
9453
  this.hideUXbertElements();
9407
- // Scroll to top to capture full page
9408
9454
  const originalScrollY = window.scrollY;
9409
- window.scrollTo(0, 0);
9410
- // Get full page dimensions
9411
- const fullPageHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
9412
- const fullPageWidth = Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);
9413
- // Capture the full page
9414
- const canvas = await html2canvas(document.body, {
9415
- allowTaint: true,
9416
- useCORS: true,
9417
- logging: false,
9418
- width: fullPageWidth,
9419
- height: fullPageHeight,
9420
- });
9421
- // Restore scroll position
9422
- window.scrollTo(0, originalScrollY);
9455
+ let canvas;
9456
+ if (mode === 'viewport') {
9457
+ // Capture only the current viewport
9458
+ canvas = await html2canvas(document.body, {
9459
+ useCORS: true,
9460
+ allowTaint: false,
9461
+ logging: false,
9462
+ width: window.innerWidth,
9463
+ height: window.innerHeight,
9464
+ windowWidth: window.innerWidth,
9465
+ windowHeight: window.innerHeight,
9466
+ x: window.scrollX,
9467
+ y: window.scrollY,
9468
+ ignoreElements: (element) => {
9469
+ // Skip cross-origin images that might cause issues
9470
+ if (element.tagName === 'IMG') {
9471
+ const img = element;
9472
+ try {
9473
+ // Test if image is accessible
9474
+ const canvas = document.createElement('canvas');
9475
+ const ctx = canvas.getContext('2d');
9476
+ canvas.width = 1;
9477
+ canvas.height = 1;
9478
+ ctx?.drawImage(img, 0, 0, 1, 1);
9479
+ canvas.toDataURL(); // This will throw if tainted
9480
+ return false; // Include the image
9481
+ }
9482
+ catch (e) {
9483
+ // Image is tainted, skip it
9484
+ console.warn('Skipping cross-origin image:', img.src);
9485
+ return true;
9486
+ }
9487
+ }
9488
+ return false;
9489
+ },
9490
+ });
9491
+ }
9492
+ else {
9493
+ // Scroll to top to capture full page
9494
+ window.scrollTo(0, 0);
9495
+ // Get full page dimensions
9496
+ const fullPageHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
9497
+ const fullPageWidth = Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);
9498
+ // Capture the full page
9499
+ canvas = await html2canvas(document.body, {
9500
+ useCORS: true,
9501
+ allowTaint: false,
9502
+ logging: false,
9503
+ width: fullPageWidth,
9504
+ height: fullPageHeight,
9505
+ ignoreElements: (element) => {
9506
+ // Skip cross-origin images that might cause issues
9507
+ if (element.tagName === 'IMG') {
9508
+ const img = element;
9509
+ try {
9510
+ // Test if image is accessible
9511
+ const canvas = document.createElement('canvas');
9512
+ const ctx = canvas.getContext('2d');
9513
+ canvas.width = 1;
9514
+ canvas.height = 1;
9515
+ ctx?.drawImage(img, 0, 0, 1, 1);
9516
+ canvas.toDataURL(); // This will throw if tainted
9517
+ return false; // Include the image
9518
+ }
9519
+ catch (e) {
9520
+ // Image is tainted, skip it
9521
+ console.warn('Skipping cross-origin image:', img.src);
9522
+ return true;
9523
+ }
9524
+ }
9525
+ return false;
9526
+ },
9527
+ });
9528
+ // Restore scroll position
9529
+ window.scrollTo(0, originalScrollY);
9530
+ }
9423
9531
  // Show UXbert UI elements again
9424
9532
  this.showUXbertElements();
9425
9533
  // Convert to base64
@@ -9495,29 +9603,46 @@ class AnnotationManager {
9495
9603
  document.body.appendChild(this.canvas);
9496
9604
  return this.canvas;
9497
9605
  }
9498
- updateCanvasSize() {
9606
+ updateCanvasSize(mode = 'fullpage') {
9499
9607
  if (!this.canvas)
9500
9608
  return;
9501
- // Get full page dimensions
9502
- const fullPageHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
9503
- const fullPageWidth = Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);
9504
- this.canvas.width = fullPageWidth;
9505
- this.canvas.height = fullPageHeight;
9609
+ if (mode === 'viewport') {
9610
+ // Set canvas to viewport dimensions
9611
+ this.canvas.width = window.innerWidth;
9612
+ this.canvas.height = window.innerHeight;
9613
+ }
9614
+ else {
9615
+ // Get full page dimensions
9616
+ const fullPageHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
9617
+ const fullPageWidth = Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);
9618
+ this.canvas.width = fullPageWidth;
9619
+ this.canvas.height = fullPageHeight;
9620
+ }
9506
9621
  }
9507
- show() {
9622
+ show(mode = 'fullpage') {
9508
9623
  if (this.canvas && this.ctx) {
9509
9624
  // Update canvas size to match current page dimensions
9510
- this.updateCanvasSize();
9625
+ this.updateCanvasSize(mode);
9511
9626
  // Reset context properties after resize
9512
9627
  this.ctx.lineCap = 'round';
9513
9628
  this.ctx.lineJoin = 'round';
9514
9629
  this.canvas.classList.add('active');
9630
+ // If viewport mode, add viewport-mode class
9631
+ if (mode === 'viewport') {
9632
+ this.canvas.classList.add('viewport-mode');
9633
+ // Note: We don't disable scrolling in viewport mode to allow users to scroll
9634
+ // and annotate different parts of the viewport
9635
+ }
9636
+ else {
9637
+ this.canvas.classList.remove('viewport-mode');
9638
+ }
9515
9639
  this.redraw();
9516
9640
  }
9517
9641
  }
9518
9642
  hide() {
9519
9643
  if (this.canvas) {
9520
9644
  this.canvas.classList.remove('active');
9645
+ this.canvas.classList.remove('viewport-mode');
9521
9646
  }
9522
9647
  // Clean up any active text input
9523
9648
  this.removeTextInput();
@@ -9530,10 +9655,13 @@ class AnnotationManager {
9530
9655
  this.currentColor = color;
9531
9656
  }
9532
9657
  handleMouseDown(e) {
9658
+ const isViewportMode = this.canvas?.classList.contains('viewport-mode');
9659
+ const scrollOffsetX = isViewportMode ? 0 : window.scrollX;
9660
+ const scrollOffsetY = isViewportMode ? 0 : window.scrollY;
9533
9661
  // Handle text tool separately
9534
9662
  if (this.currentTool === 'text') {
9535
- const x = e.clientX + window.scrollX;
9536
- const y = e.clientY + window.scrollY;
9663
+ const x = e.clientX + scrollOffsetX;
9664
+ const y = e.clientY + scrollOffsetY;
9537
9665
  // Check if clicking on existing text to edit
9538
9666
  const clickedText = this.getTextAnnotationAt(x, y);
9539
9667
  if (clickedText) {
@@ -9545,15 +9673,18 @@ class AnnotationManager {
9545
9673
  return;
9546
9674
  }
9547
9675
  this.isDrawing = true;
9548
- this.startX = e.clientX + window.scrollX;
9549
- this.startY = e.clientY + window.scrollY;
9676
+ this.startX = e.clientX + scrollOffsetX;
9677
+ this.startY = e.clientY + scrollOffsetY;
9550
9678
  this.currentPath = [{ x: this.startX, y: this.startY }];
9551
9679
  }
9552
9680
  handleMouseMove(e) {
9553
9681
  if (!this.isDrawing || !this.ctx)
9554
9682
  return;
9555
- const x = e.clientX + window.scrollX;
9556
- const y = e.clientY + window.scrollY;
9683
+ const isViewportMode = this.canvas?.classList.contains('viewport-mode');
9684
+ const scrollOffsetX = isViewportMode ? 0 : window.scrollX;
9685
+ const scrollOffsetY = isViewportMode ? 0 : window.scrollY;
9686
+ const x = e.clientX + scrollOffsetX;
9687
+ const y = e.clientY + scrollOffsetY;
9557
9688
  if (this.currentTool === 'pen') {
9558
9689
  this.currentPath.push({ x, y });
9559
9690
  this.redraw();
@@ -9573,8 +9704,11 @@ class AnnotationManager {
9573
9704
  if (!this.isDrawing)
9574
9705
  return;
9575
9706
  this.isDrawing = false;
9576
- const x = e.clientX + window.scrollX;
9577
- const y = e.clientY + window.scrollY;
9707
+ const isViewportMode = this.canvas?.classList.contains('viewport-mode');
9708
+ const scrollOffsetX = isViewportMode ? 0 : window.scrollX;
9709
+ const scrollOffsetY = isViewportMode ? 0 : window.scrollY;
9710
+ const x = e.clientX + scrollOffsetX;
9711
+ const y = e.clientY + scrollOffsetY;
9578
9712
  // Save the annotation
9579
9713
  if (this.currentTool === 'pen') {
9580
9714
  this.annotations.push({
@@ -10047,7 +10181,7 @@ function styleInject(css, ref) {
10047
10181
  }
10048
10182
  }
10049
10183
 
10050
- 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-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-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-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-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}}";
10184
+ 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-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}}";
10051
10185
  styleInject(css_248z);
10052
10186
 
10053
10187
  // Main initialization file
@@ -10089,6 +10223,7 @@ class Reportly {
10089
10223
  onClose: () => this.handleModalClose(),
10090
10224
  onAnnotate: () => this.startAnnotation(),
10091
10225
  onRetake: () => this.retakeScreenshot(),
10226
+ onCapture: () => this.captureWithMode(),
10092
10227
  });
10093
10228
  this.toolbar = new Toolbar({
10094
10229
  onToolChange: (tool) => this.annotation?.setTool(tool),
@@ -10119,14 +10254,27 @@ class Reportly {
10119
10254
  });
10120
10255
  }
10121
10256
  async handleButtonClick() {
10257
+ try {
10258
+ // Open modal first to let user choose capture mode
10259
+ this.modal?.open();
10260
+ }
10261
+ catch (error) {
10262
+ console.error("Failed to open modal:", error);
10263
+ alert("Failed to open modal. Please try again.");
10264
+ }
10265
+ }
10266
+ async captureWithMode() {
10122
10267
  try {
10123
10268
  this.state?.setState(STATE.CAPTURING);
10269
+ // Get capture mode from modal
10270
+ const captureMode = this.modal?.getCaptureMode() || 'viewport';
10271
+ // Save capture mode in state
10272
+ this.state?.setCaptureMode(captureMode);
10124
10273
  // Capture screenshot
10125
- const screenshot = await this.screenshot.capture();
10274
+ const screenshot = await this.screenshot.capture(captureMode);
10126
10275
  this.state?.setScreenshot(screenshot);
10127
10276
  // Show modal with screenshot
10128
10277
  this.modal?.setScreenshot(screenshot);
10129
- this.modal?.open();
10130
10278
  this.state?.setState(STATE.IDLE);
10131
10279
  }
10132
10280
  catch (error) {
@@ -10139,7 +10287,8 @@ class Reportly {
10139
10287
  this.state?.setState(STATE.ANNOTATING);
10140
10288
  this.modal?.close();
10141
10289
  this.button?.hide();
10142
- this.annotation?.show();
10290
+ const captureMode = this.state?.getCaptureMode() || 'viewport';
10291
+ this.annotation?.show(captureMode);
10143
10292
  this.toolbar?.show();
10144
10293
  }
10145
10294
  exitAnnotation() {
@@ -10175,13 +10324,16 @@ class Reportly {
10175
10324
  }
10176
10325
  async retakeScreenshot() {
10177
10326
  try {
10327
+ // Get the current capture mode from modal before closing
10328
+ const captureMode = this.modal?.getCaptureMode() || 'viewport';
10178
10329
  this.modal?.close();
10179
10330
  this.state?.setState(STATE.CAPTURING);
10180
10331
  // Clear previous annotations
10181
10332
  this.annotation?.clear();
10182
- // Capture new screenshot
10183
- const screenshot = await this.screenshot.capture();
10333
+ // Capture new screenshot with the same mode
10334
+ const screenshot = await this.screenshot.capture(captureMode);
10184
10335
  this.state?.setScreenshot(screenshot);
10336
+ this.state?.setCaptureMode(captureMode);
10185
10337
  // Show modal with new screenshot
10186
10338
  this.modal?.setScreenshot(screenshot);
10187
10339
  this.modal?.open();