@uxbertlabs/reportly 1.0.5 → 1.0.6

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,43 @@ 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
+ allowTaint: true,
9460
+ useCORS: true,
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
+ });
9469
+ }
9470
+ else {
9471
+ // Scroll to top to capture full page
9472
+ window.scrollTo(0, 0);
9473
+ // Get full page dimensions
9474
+ const fullPageHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
9475
+ const fullPageWidth = Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);
9476
+ // Capture the full page
9477
+ canvas = await html2canvas(document.body, {
9478
+ allowTaint: true,
9479
+ useCORS: true,
9480
+ logging: false,
9481
+ width: fullPageWidth,
9482
+ height: fullPageHeight,
9483
+ });
9484
+ // Restore scroll position
9485
+ window.scrollTo(0, originalScrollY);
9486
+ }
9423
9487
  // Show UXbert UI elements again
9424
9488
  this.showUXbertElements();
9425
9489
  // Convert to base64
@@ -9495,29 +9559,46 @@ class AnnotationManager {
9495
9559
  document.body.appendChild(this.canvas);
9496
9560
  return this.canvas;
9497
9561
  }
9498
- updateCanvasSize() {
9562
+ updateCanvasSize(mode = 'fullpage') {
9499
9563
  if (!this.canvas)
9500
9564
  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;
9565
+ if (mode === 'viewport') {
9566
+ // Set canvas to viewport dimensions
9567
+ this.canvas.width = window.innerWidth;
9568
+ this.canvas.height = window.innerHeight;
9569
+ }
9570
+ else {
9571
+ // Get full page dimensions
9572
+ const fullPageHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
9573
+ const fullPageWidth = Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);
9574
+ this.canvas.width = fullPageWidth;
9575
+ this.canvas.height = fullPageHeight;
9576
+ }
9506
9577
  }
9507
- show() {
9578
+ show(mode = 'fullpage') {
9508
9579
  if (this.canvas && this.ctx) {
9509
9580
  // Update canvas size to match current page dimensions
9510
- this.updateCanvasSize();
9581
+ this.updateCanvasSize(mode);
9511
9582
  // Reset context properties after resize
9512
9583
  this.ctx.lineCap = 'round';
9513
9584
  this.ctx.lineJoin = 'round';
9514
9585
  this.canvas.classList.add('active');
9586
+ // If viewport mode, add viewport-mode class
9587
+ if (mode === 'viewport') {
9588
+ this.canvas.classList.add('viewport-mode');
9589
+ // Note: We don't disable scrolling in viewport mode to allow users to scroll
9590
+ // and annotate different parts of the viewport
9591
+ }
9592
+ else {
9593
+ this.canvas.classList.remove('viewport-mode');
9594
+ }
9515
9595
  this.redraw();
9516
9596
  }
9517
9597
  }
9518
9598
  hide() {
9519
9599
  if (this.canvas) {
9520
9600
  this.canvas.classList.remove('active');
9601
+ this.canvas.classList.remove('viewport-mode');
9521
9602
  }
9522
9603
  // Clean up any active text input
9523
9604
  this.removeTextInput();
@@ -9530,10 +9611,13 @@ class AnnotationManager {
9530
9611
  this.currentColor = color;
9531
9612
  }
9532
9613
  handleMouseDown(e) {
9614
+ const isViewportMode = this.canvas?.classList.contains('viewport-mode');
9615
+ const scrollOffsetX = isViewportMode ? 0 : window.scrollX;
9616
+ const scrollOffsetY = isViewportMode ? 0 : window.scrollY;
9533
9617
  // Handle text tool separately
9534
9618
  if (this.currentTool === 'text') {
9535
- const x = e.clientX + window.scrollX;
9536
- const y = e.clientY + window.scrollY;
9619
+ const x = e.clientX + scrollOffsetX;
9620
+ const y = e.clientY + scrollOffsetY;
9537
9621
  // Check if clicking on existing text to edit
9538
9622
  const clickedText = this.getTextAnnotationAt(x, y);
9539
9623
  if (clickedText) {
@@ -9545,15 +9629,18 @@ class AnnotationManager {
9545
9629
  return;
9546
9630
  }
9547
9631
  this.isDrawing = true;
9548
- this.startX = e.clientX + window.scrollX;
9549
- this.startY = e.clientY + window.scrollY;
9632
+ this.startX = e.clientX + scrollOffsetX;
9633
+ this.startY = e.clientY + scrollOffsetY;
9550
9634
  this.currentPath = [{ x: this.startX, y: this.startY }];
9551
9635
  }
9552
9636
  handleMouseMove(e) {
9553
9637
  if (!this.isDrawing || !this.ctx)
9554
9638
  return;
9555
- const x = e.clientX + window.scrollX;
9556
- const y = e.clientY + window.scrollY;
9639
+ const isViewportMode = this.canvas?.classList.contains('viewport-mode');
9640
+ const scrollOffsetX = isViewportMode ? 0 : window.scrollX;
9641
+ const scrollOffsetY = isViewportMode ? 0 : window.scrollY;
9642
+ const x = e.clientX + scrollOffsetX;
9643
+ const y = e.clientY + scrollOffsetY;
9557
9644
  if (this.currentTool === 'pen') {
9558
9645
  this.currentPath.push({ x, y });
9559
9646
  this.redraw();
@@ -9573,8 +9660,11 @@ class AnnotationManager {
9573
9660
  if (!this.isDrawing)
9574
9661
  return;
9575
9662
  this.isDrawing = false;
9576
- const x = e.clientX + window.scrollX;
9577
- const y = e.clientY + window.scrollY;
9663
+ const isViewportMode = this.canvas?.classList.contains('viewport-mode');
9664
+ const scrollOffsetX = isViewportMode ? 0 : window.scrollX;
9665
+ const scrollOffsetY = isViewportMode ? 0 : window.scrollY;
9666
+ const x = e.clientX + scrollOffsetX;
9667
+ const y = e.clientY + scrollOffsetY;
9578
9668
  // Save the annotation
9579
9669
  if (this.currentTool === 'pen') {
9580
9670
  this.annotations.push({
@@ -10047,7 +10137,7 @@ function styleInject(css, ref) {
10047
10137
  }
10048
10138
  }
10049
10139
 
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}}";
10140
+ 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
10141
  styleInject(css_248z);
10052
10142
 
10053
10143
  // Main initialization file
@@ -10089,6 +10179,7 @@ class Reportly {
10089
10179
  onClose: () => this.handleModalClose(),
10090
10180
  onAnnotate: () => this.startAnnotation(),
10091
10181
  onRetake: () => this.retakeScreenshot(),
10182
+ onCapture: () => this.captureWithMode(),
10092
10183
  });
10093
10184
  this.toolbar = new Toolbar({
10094
10185
  onToolChange: (tool) => this.annotation?.setTool(tool),
@@ -10119,14 +10210,27 @@ class Reportly {
10119
10210
  });
10120
10211
  }
10121
10212
  async handleButtonClick() {
10213
+ try {
10214
+ // Open modal first to let user choose capture mode
10215
+ this.modal?.open();
10216
+ }
10217
+ catch (error) {
10218
+ console.error("Failed to open modal:", error);
10219
+ alert("Failed to open modal. Please try again.");
10220
+ }
10221
+ }
10222
+ async captureWithMode() {
10122
10223
  try {
10123
10224
  this.state?.setState(STATE.CAPTURING);
10225
+ // Get capture mode from modal
10226
+ const captureMode = this.modal?.getCaptureMode() || 'viewport';
10227
+ // Save capture mode in state
10228
+ this.state?.setCaptureMode(captureMode);
10124
10229
  // Capture screenshot
10125
- const screenshot = await this.screenshot.capture();
10230
+ const screenshot = await this.screenshot.capture(captureMode);
10126
10231
  this.state?.setScreenshot(screenshot);
10127
10232
  // Show modal with screenshot
10128
10233
  this.modal?.setScreenshot(screenshot);
10129
- this.modal?.open();
10130
10234
  this.state?.setState(STATE.IDLE);
10131
10235
  }
10132
10236
  catch (error) {
@@ -10139,7 +10243,8 @@ class Reportly {
10139
10243
  this.state?.setState(STATE.ANNOTATING);
10140
10244
  this.modal?.close();
10141
10245
  this.button?.hide();
10142
- this.annotation?.show();
10246
+ const captureMode = this.state?.getCaptureMode() || 'viewport';
10247
+ this.annotation?.show(captureMode);
10143
10248
  this.toolbar?.show();
10144
10249
  }
10145
10250
  exitAnnotation() {
@@ -10175,13 +10280,16 @@ class Reportly {
10175
10280
  }
10176
10281
  async retakeScreenshot() {
10177
10282
  try {
10283
+ // Get the current capture mode from modal before closing
10284
+ const captureMode = this.modal?.getCaptureMode() || 'viewport';
10178
10285
  this.modal?.close();
10179
10286
  this.state?.setState(STATE.CAPTURING);
10180
10287
  // Clear previous annotations
10181
10288
  this.annotation?.clear();
10182
- // Capture new screenshot
10183
- const screenshot = await this.screenshot.capture();
10289
+ // Capture new screenshot with the same mode
10290
+ const screenshot = await this.screenshot.capture(captureMode);
10184
10291
  this.state?.setScreenshot(screenshot);
10292
+ this.state?.setCaptureMode(captureMode);
10185
10293
  // Show modal with new screenshot
10186
10294
  this.modal?.setScreenshot(screenshot);
10187
10295
  this.modal?.open();