@triagly/sdk 0.1.1-beta.4 → 0.1.1-beta.5

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/index.js CHANGED
@@ -51,26 +51,23 @@
51
51
  // Button orientation
52
52
  const orientation = this.config.orientation || 'horizontal';
53
53
  button.classList.add(`triagly-orientation-${orientation}`);
54
+ // Speech bubble SVG icon
55
+ const speechBubbleIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" class="triagly-icon"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/></svg>`;
56
+ const largeIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="currentColor" class="triagly-icon"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/></svg>`;
54
57
  // Handle button text based on shape
55
- const fullText = this.config.buttonText || '🐛 Feedback';
58
+ const fullText = this.config.buttonText || 'Feedback';
56
59
  if (shape === 'circular') {
57
- button.innerHTML = '🐛';
60
+ button.innerHTML = largeIcon;
58
61
  button.setAttribute('aria-label', fullText);
59
62
  }
60
63
  else if (shape === 'expandable') {
61
- // Expandable starts with emoji, expands to full text on hover
62
- button.innerHTML = '<span class="triagly-btn-icon">🐛</span><span class="triagly-btn-text"> Feedback</span>';
64
+ // Expandable starts with icon, expands to full text on hover
65
+ button.innerHTML = `<span class="triagly-btn-icon">${largeIcon}</span><span class="triagly-btn-text"> ${this.config.buttonText || 'Feedback'}</span>`;
63
66
  button.setAttribute('aria-label', fullText);
64
- // Store custom text if provided
65
- if (this.config.buttonText) {
66
- const textSpan = button.querySelector('.triagly-btn-text');
67
- if (textSpan) {
68
- textSpan.textContent = ' ' + this.config.buttonText.replace('🐛', '').trim();
69
- }
70
- }
71
67
  }
72
68
  else {
73
- button.innerHTML = fullText;
69
+ // Default: icon + text
70
+ button.innerHTML = `${speechBubbleIcon}<span class="triagly-btn-label">${fullText}</span>`;
74
71
  }
75
72
  button.onclick = () => this.toggle();
76
73
  // Position button
@@ -136,9 +133,9 @@
136
133
  this.setupKeyboardEvents();
137
134
  // Set up focus trap
138
135
  this.setupFocusTrap();
139
- // Focus on title field
140
- const titleInput = this.container?.querySelector('input[type="text"]');
141
- titleInput?.focus();
136
+ // Focus on description field
137
+ const descInput = this.container?.querySelector('#triagly-description');
138
+ descInput?.focus();
142
139
  }, 0);
143
140
  }
144
141
  /**
@@ -183,7 +180,7 @@
183
180
  overlay.className = 'triagly-overlay';
184
181
  overlay.setAttribute('role', 'dialog');
185
182
  overlay.setAttribute('aria-modal', 'true');
186
- overlay.setAttribute('aria-labelledby', 'triagly-modal-title');
183
+ overlay.setAttribute('aria-label', 'Send feedback');
187
184
  overlay.onclick = (e) => {
188
185
  if (e.target === overlay)
189
186
  this.close('overlay');
@@ -194,7 +191,6 @@
194
191
  const header = document.createElement('div');
195
192
  header.className = 'triagly-header';
196
193
  header.innerHTML = `
197
- <h3 id="triagly-modal-title">Send Feedback</h3>
198
194
  <button type="button" class="triagly-close" aria-label="Close feedback form">×</button>
199
195
  `;
200
196
  const closeBtn = header.querySelector('.triagly-close');
@@ -203,16 +199,7 @@
203
199
  form.className = 'triagly-form';
204
200
  form.innerHTML = `
205
201
  <div class="triagly-field">
206
- <label for="triagly-title">Title (optional)</label>
207
- <input
208
- type="text"
209
- id="triagly-title"
210
- placeholder="Brief summary of your feedback"
211
- />
212
- </div>
213
-
214
- <div class="triagly-field">
215
- <label for="triagly-description">Description *</label>
202
+ <label for="triagly-description">What's on your mind?</label>
216
203
  <textarea
217
204
  id="triagly-description"
218
205
  required
@@ -221,6 +208,15 @@
221
208
  ></textarea>
222
209
  </div>
223
210
 
211
+ <div class="triagly-field">
212
+ <label for="triagly-name">Name (optional)</label>
213
+ <input
214
+ type="text"
215
+ id="triagly-name"
216
+ placeholder="Your name"
217
+ />
218
+ </div>
219
+
224
220
  <div class="triagly-field">
225
221
  <label for="triagly-email">Email (optional)</label>
226
222
  <input
@@ -230,13 +226,6 @@
230
226
  />
231
227
  </div>
232
228
 
233
- <div class="triagly-field triagly-checkbox">
234
- <label>
235
- <input type="checkbox" id="triagly-screenshot" checked />
236
- <span>Include screenshot</span>
237
- </label>
238
- </div>
239
-
240
229
  ${this.config.turnstileSiteKey ? `
241
230
  <div class="triagly-field triagly-turnstile">
242
231
  <div class="cf-turnstile" data-sitekey="${this.config.turnstileSiteKey}" data-theme="light"></div>
@@ -260,8 +249,16 @@
260
249
  e.preventDefault();
261
250
  this.handleSubmit(form);
262
251
  };
252
+ const footer = document.createElement('div');
253
+ footer.className = 'triagly-footer';
254
+ footer.innerHTML = `
255
+ <a href="https://triagly.com" target="_blank" rel="noopener noreferrer" class="triagly-branding">
256
+ Powered by <strong>Triagly</strong>
257
+ </a>
258
+ `;
263
259
  modal.appendChild(header);
264
260
  modal.appendChild(form);
261
+ modal.appendChild(footer);
265
262
  overlay.appendChild(modal);
266
263
  // Render Turnstile widget if available
267
264
  if (this.config.turnstileSiteKey) {
@@ -310,10 +307,9 @@
310
307
  * Handle form submission
311
308
  */
312
309
  async handleSubmit(form) {
313
- const titleInput = form.querySelector('#triagly-title');
314
310
  const descInput = form.querySelector('#triagly-description');
311
+ const nameInput = form.querySelector('#triagly-name');
315
312
  const emailInput = form.querySelector('#triagly-email');
316
- const screenshotCheckbox = form.querySelector('#triagly-screenshot');
317
313
  const statusDiv = form.querySelector('#triagly-status');
318
314
  const submitBtn = form.querySelector('button[type="submit"]');
319
315
  const turnstileContainer = form.querySelector('.cf-turnstile');
@@ -327,10 +323,9 @@
327
323
  turnstileToken = turnstileContainer.getAttribute('data-turnstile-response') || undefined;
328
324
  }
329
325
  const data = {
330
- title: titleInput.value.trim() || undefined,
331
326
  description: descInput.value.trim(),
327
+ reporterName: nameInput.value.trim() || undefined,
332
328
  reporterEmail: emailInput.value.trim() || undefined,
333
- includeScreenshot: screenshotCheckbox.checked,
334
329
  turnstileToken,
335
330
  };
336
331
  // Create a promise that waits for actual submission result
@@ -394,21 +389,32 @@
394
389
  position: fixed;
395
390
  z-index: 999999;
396
391
  padding: 12px 20px;
397
- background: var(--triagly-button-bg, #6366f1);
392
+ background: var(--triagly-button-bg, #18181b);
398
393
  color: var(--triagly-button-text, #ffffff);
399
394
  border: none;
400
395
  border-radius: var(--triagly-button-radius, 8px);
401
396
  font-size: 14px;
402
397
  font-weight: 500;
403
398
  cursor: pointer;
404
- box-shadow: var(--triagly-button-shadow, 0 4px 12px rgba(99, 102, 241, 0.3));
399
+ box-shadow: var(--triagly-button-shadow, 0 4px 12px rgba(0, 0, 0, 0.15));
405
400
  transition: all 0.2s;
401
+ display: inline-flex;
402
+ align-items: center;
403
+ gap: 8px;
406
404
  }
407
405
 
408
406
  .triagly-button:hover {
409
- background: var(--triagly-button-bg-hover, #4f46e5);
407
+ background: var(--triagly-button-bg-hover, #27272a);
410
408
  transform: translateY(-2px);
411
- box-shadow: var(--triagly-button-shadow-hover, 0 6px 16px rgba(99, 102, 241, 0.4));
409
+ box-shadow: var(--triagly-button-shadow-hover, 0 6px 16px rgba(0, 0, 0, 0.2));
410
+ }
411
+
412
+ .triagly-button .triagly-icon {
413
+ flex-shrink: 0;
414
+ }
415
+
416
+ .triagly-button .triagly-btn-label {
417
+ line-height: 1;
412
418
  }
413
419
 
414
420
  /* Prevent expandable buttons from shifting on hover */
@@ -502,8 +508,8 @@
502
508
  min-width: auto;
503
509
  padding: 12px 20px;
504
510
  border-radius: 30px;
505
- background: var(--triagly-button-bg-hover, #4f46e5);
506
- box-shadow: var(--triagly-button-shadow-hover, 0 6px 16px rgba(99, 102, 241, 0.4));
511
+ background: var(--triagly-button-bg-hover, #27272a);
512
+ box-shadow: var(--triagly-button-shadow-hover, 0 6px 16px rgba(0, 0, 0, 0.2));
507
513
  }
508
514
  .triagly-shape-expandable:hover .triagly-btn-text {
509
515
  width: auto;
@@ -562,18 +568,10 @@
562
568
 
563
569
  .triagly-header {
564
570
  display: flex;
565
- justify-content: space-between;
571
+ justify-content: flex-end;
566
572
  align-items: center;
567
- padding: 20px 24px;
573
+ padding: 8px 12px 0;
568
574
  background: var(--triagly-header-bg, #ffffff);
569
- border-bottom: 1px solid var(--triagly-header-border, #e5e7eb);
570
- }
571
-
572
- .triagly-header h3 {
573
- margin: 0;
574
- font-size: 18px;
575
- font-weight: 600;
576
- color: var(--triagly-header-text, #111827);
577
575
  }
578
576
 
579
577
  .triagly-close {
@@ -631,46 +629,21 @@
631
629
  .triagly-field input:focus,
632
630
  .triagly-field textarea:focus {
633
631
  outline: none;
634
- border-color: var(--triagly-input-border-focus, #6366f1);
635
- box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
636
- }
637
-
638
- .triagly-checkbox label {
639
- display: flex;
640
- align-items: center;
641
- gap: 8px;
642
- cursor: pointer;
643
- font-weight: 400;
644
- }
645
-
646
- .triagly-checkbox label span {
647
- user-select: none;
648
- }
649
-
650
- .triagly-checkbox input {
651
- width: 16px;
652
- height: 16px;
653
- margin: 0;
654
- cursor: pointer;
632
+ border-color: var(--triagly-input-border-focus, #a1a1aa);
633
+ box-shadow: 0 0 0 2px rgba(161, 161, 170, 0.15);
655
634
  }
656
635
 
657
636
  /* Focus visible styles for accessibility */
658
637
  .triagly-button:focus-visible,
659
638
  .triagly-field input:focus-visible,
660
639
  .triagly-field textarea:focus-visible,
661
- .triagly-checkbox input:focus-visible,
662
640
  .triagly-btn-primary:focus-visible,
663
641
  .triagly-btn-secondary:focus-visible,
664
642
  .triagly-close:focus-visible {
665
- outline: 2px solid #6366f1;
643
+ outline: 2px solid #a1a1aa;
666
644
  outline-offset: 2px;
667
645
  }
668
646
 
669
- /* Checkbox label gets visual indicator when checkbox is focused */
670
- .triagly-checkbox input:focus-visible + span {
671
- text-decoration: underline;
672
- }
673
-
674
647
  .triagly-turnstile {
675
648
  display: flex;
676
649
  justify-content: center;
@@ -696,12 +669,12 @@
696
669
  }
697
670
 
698
671
  .triagly-btn-primary {
699
- background: var(--triagly-btn-primary-bg, #6366f1);
672
+ background: var(--triagly-btn-primary-bg, #18181b);
700
673
  color: var(--triagly-btn-primary-text, #ffffff);
701
674
  }
702
675
 
703
676
  .triagly-btn-primary:hover:not(:disabled) {
704
- background: var(--triagly-btn-primary-bg-hover, #4f46e5);
677
+ background: var(--triagly-btn-primary-bg-hover, #27272a);
705
678
  }
706
679
 
707
680
  .triagly-btn-primary:disabled {
@@ -737,6 +710,29 @@
737
710
  background: var(--triagly-error-bg, #fee2e2);
738
711
  color: var(--triagly-error-text, #991b1b);
739
712
  }
713
+
714
+ .triagly-footer {
715
+ padding: 12px 24px 16px;
716
+ text-align: right;
717
+ border-top: 1px solid var(--triagly-footer-border, #e5e7eb);
718
+ background: var(--triagly-footer-bg, #f9fafb);
719
+ border-radius: 0 0 var(--triagly-modal-radius, 12px) var(--triagly-modal-radius, 12px);
720
+ }
721
+
722
+ .triagly-branding {
723
+ font-size: 12px;
724
+ color: var(--triagly-footer-text, #6b7280);
725
+ text-decoration: none;
726
+ transition: color 0.2s;
727
+ }
728
+
729
+ .triagly-branding:hover {
730
+ color: var(--triagly-footer-text-hover, #18181b);
731
+ }
732
+
733
+ .triagly-branding strong {
734
+ font-weight: 600;
735
+ }
740
736
  `;
741
737
  document.head.appendChild(style);
742
738
  }
@@ -902,7 +898,6 @@
902
898
  consoleLogs: data.consoleLogs,
903
899
  },
904
900
  tags: data.tags,
905
- screenshot: data.screenshot,
906
901
  reporterEmail: data.reporterEmail,
907
902
  reporterName: data.reporterName,
908
903
  turnstileToken,
@@ -989,34 +984,6 @@
989
984
  }
990
985
  return browser;
991
986
  }
992
- /**
993
- * Capture screenshot of current page
994
- */
995
- async function captureScreenshot() {
996
- try {
997
- // Use html2canvas library if available
998
- if (typeof window.html2canvas !== 'undefined') {
999
- const canvas = await window.html2canvas(document.body, {
1000
- logging: false,
1001
- useCORS: true,
1002
- allowTaint: true,
1003
- });
1004
- return canvas.toDataURL('image/png');
1005
- }
1006
- // Fallback to native screenshot API if supported (limited browser support)
1007
- if ('mediaDevices' in navigator && 'getDisplayMedia' in navigator.mediaDevices) {
1008
- // This requires user interaction and shows a permission dialog
1009
- // Not ideal for automatic screenshots
1010
- console.warn('Screenshot capture requires html2canvas library');
1011
- return null;
1012
- }
1013
- return null;
1014
- }
1015
- catch (error) {
1016
- console.error('Screenshot capture failed:', error);
1017
- return null;
1018
- }
1019
- }
1020
987
  /**
1021
988
  * Simple rate limiter using localStorage
1022
989
  */
@@ -1237,7 +1204,7 @@
1237
1204
  theme: 'auto',
1238
1205
  position: 'bottom-right',
1239
1206
  buttonShape: 'rounded',
1240
- buttonText: '🐛 Feedback',
1207
+ buttonText: 'Feedback',
1241
1208
  placeholderText: 'Describe what happened...',
1242
1209
  successMessage: 'Feedback sent successfully!',
1243
1210
  errorMessage: 'Failed to send feedback. Please try again.',
@@ -1292,17 +1259,11 @@
1292
1259
  }
1293
1260
  // Collect metadata
1294
1261
  const metadata = collectMetadata(this.config.metadata);
1295
- // Capture screenshot if requested
1296
- let screenshot = null;
1297
- if (data.includeScreenshot) {
1298
- screenshot = await captureScreenshot();
1299
- }
1300
1262
  // Prepare feedback data
1301
1263
  const feedbackData = {
1302
1264
  title: data.title,
1303
1265
  description: data.description,
1304
1266
  reporterEmail: data.reporterEmail,
1305
- screenshot: screenshot || undefined,
1306
1267
  consoleLogs: this.consoleLogger?.getLogs(),
1307
1268
  };
1308
1269
  // Submit to API with Turnstile token if provided