@mushi-mushi/web 1.8.0 → 1.10.0
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.cjs +383 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +383 -37
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -551,23 +551,47 @@ function getWidgetStyles(theme) {
|
|
|
551
551
|
font-weight: 600;
|
|
552
552
|
color: ${ink};
|
|
553
553
|
}
|
|
554
|
-
.mushi-close
|
|
554
|
+
.mushi-close {
|
|
555
555
|
background: none;
|
|
556
556
|
border: none;
|
|
557
557
|
cursor: pointer;
|
|
558
|
-
padding: 4px;
|
|
558
|
+
padding: 2px 4px;
|
|
559
559
|
color: ${inkMuted};
|
|
560
560
|
font-family: ${fontBody};
|
|
561
|
-
font-size:
|
|
561
|
+
font-size: 16px;
|
|
562
562
|
line-height: 1;
|
|
563
|
-
border-radius:
|
|
563
|
+
border-radius: 0;
|
|
564
564
|
transition: color 150ms ${easeStamp};
|
|
565
565
|
}
|
|
566
|
-
.mushi-
|
|
567
|
-
|
|
566
|
+
.mushi-back {
|
|
567
|
+
align-self: flex-start;
|
|
568
|
+
background: none;
|
|
569
|
+
border: none;
|
|
570
|
+
cursor: pointer;
|
|
571
|
+
padding: 0;
|
|
572
|
+
margin: 0 0 2px;
|
|
573
|
+
color: ${inkMuted};
|
|
574
|
+
font-family: ${fontMono};
|
|
575
|
+
font-size: 10px;
|
|
576
|
+
font-weight: 500;
|
|
577
|
+
letter-spacing: 0.12em;
|
|
578
|
+
text-transform: uppercase;
|
|
579
|
+
line-height: 1.2;
|
|
580
|
+
border-radius: 0;
|
|
581
|
+
transition: color 150ms ${easeStamp};
|
|
582
|
+
}
|
|
583
|
+
.mushi-close:hover { color: ${widgetAccent}; }
|
|
584
|
+
.mushi-back:hover { color: ${ink}; }
|
|
585
|
+
.mushi-close:focus-visible {
|
|
568
586
|
outline: 1.5px solid ${widgetAccent};
|
|
569
587
|
outline-offset: 2px;
|
|
570
588
|
}
|
|
589
|
+
.mushi-back:focus-visible {
|
|
590
|
+
outline: none;
|
|
591
|
+
color: ${widgetAccent};
|
|
592
|
+
text-decoration: underline;
|
|
593
|
+
text-underline-offset: 3px;
|
|
594
|
+
}
|
|
571
595
|
|
|
572
596
|
/* \u2500\u2500 Body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
573
597
|
Generous left/right padding (22px) so type breathes. Vertical
|
|
@@ -656,39 +680,121 @@ function getWidgetStyles(theme) {
|
|
|
656
680
|
.mushi-report-row {
|
|
657
681
|
width: 100%;
|
|
658
682
|
display: grid;
|
|
659
|
-
grid-template-columns:
|
|
660
|
-
gap:
|
|
683
|
+
grid-template-columns: 1fr auto;
|
|
684
|
+
gap: 10px;
|
|
661
685
|
align-items: center;
|
|
662
|
-
padding:
|
|
686
|
+
padding: 12px 4px 12px 0;
|
|
663
687
|
border: 0;
|
|
664
688
|
border-bottom: 1px solid ${rule};
|
|
665
689
|
background: transparent;
|
|
666
690
|
color: ${ink};
|
|
667
691
|
cursor: pointer;
|
|
668
692
|
text-align: left;
|
|
693
|
+
transition: background 180ms ${easeStamp}, padding-left 180ms ${easeStamp};
|
|
694
|
+
}
|
|
695
|
+
.mushi-report-row:hover,
|
|
696
|
+
.mushi-report-row:focus-visible {
|
|
697
|
+
background: ${isDark ? "rgba(242,235,221,0.04)" : "rgba(14,13,11,0.03)"};
|
|
698
|
+
padding-left: 4px;
|
|
699
|
+
}
|
|
700
|
+
.mushi-report-main {
|
|
701
|
+
min-width: 0;
|
|
702
|
+
display: grid;
|
|
703
|
+
gap: 6px;
|
|
704
|
+
}
|
|
705
|
+
.mushi-report-title {
|
|
706
|
+
font-size: 13px;
|
|
707
|
+
line-height: 1.35;
|
|
708
|
+
display: -webkit-box;
|
|
709
|
+
-webkit-line-clamp: 2;
|
|
710
|
+
-webkit-box-orient: vertical;
|
|
711
|
+
overflow: hidden;
|
|
712
|
+
}
|
|
713
|
+
.mushi-report-meta {
|
|
714
|
+
display: flex;
|
|
715
|
+
flex-wrap: wrap;
|
|
716
|
+
align-items: center;
|
|
717
|
+
gap: 8px;
|
|
669
718
|
}
|
|
670
719
|
.mushi-report-status {
|
|
720
|
+
display: inline-flex;
|
|
721
|
+
align-items: center;
|
|
671
722
|
font-family: ${fontMono};
|
|
672
723
|
font-size: 10px;
|
|
673
|
-
|
|
724
|
+
font-weight: 600;
|
|
725
|
+
letter-spacing: 0.04em;
|
|
674
726
|
text-transform: uppercase;
|
|
727
|
+
padding: 2px 7px;
|
|
728
|
+
border-radius: 999px;
|
|
729
|
+
border: 1px solid transparent;
|
|
730
|
+
}
|
|
731
|
+
.mushi-status-sent {
|
|
732
|
+
color: ${isDark ? "#A8C4FF" : "#1E4A8C"};
|
|
733
|
+
background: ${isDark ? "rgba(120,160,255,0.12)" : "rgba(30,74,140,0.08)"};
|
|
734
|
+
border-color: ${isDark ? "rgba(120,160,255,0.22)" : "rgba(30,74,140,0.16)"};
|
|
735
|
+
}
|
|
736
|
+
.mushi-status-review {
|
|
737
|
+
color: ${isDark ? "#FFD27A" : "#8A5A00"};
|
|
738
|
+
background: ${isDark ? "rgba(255,190,90,0.12)" : "rgba(180,120,0,0.10)"};
|
|
739
|
+
border-color: ${isDark ? "rgba(255,190,90,0.22)" : "rgba(180,120,0,0.18)"};
|
|
740
|
+
}
|
|
741
|
+
.mushi-status-fixing {
|
|
742
|
+
color: ${isDark ? "#FFB899" : "#9A3D12"};
|
|
743
|
+
background: ${isDark ? "rgba(255,120,60,0.12)" : "rgba(224,60,44,0.10)"};
|
|
744
|
+
border-color: ${isDark ? "rgba(255,120,60,0.24)" : "rgba(224,60,44,0.18)"};
|
|
745
|
+
}
|
|
746
|
+
.mushi-status-fixed {
|
|
747
|
+
color: ${isDark ? "#8FE3B0" : "#1F6B3A"};
|
|
748
|
+
background: ${isDark ? "rgba(80,200,130,0.12)" : "rgba(31,107,58,0.10)"};
|
|
749
|
+
border-color: ${isDark ? "rgba(80,200,130,0.22)" : "rgba(31,107,58,0.18)"};
|
|
750
|
+
}
|
|
751
|
+
.mushi-status-closed,
|
|
752
|
+
.mushi-status-unknown {
|
|
753
|
+
color: ${inkMuted};
|
|
754
|
+
background: ${isDark ? "rgba(242,235,221,0.06)" : "rgba(14,13,11,0.05)"};
|
|
755
|
+
border-color: ${ruleStrong};
|
|
675
756
|
}
|
|
676
|
-
.mushi-report-
|
|
677
|
-
font-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
757
|
+
.mushi-report-when {
|
|
758
|
+
font-family: ${fontMono};
|
|
759
|
+
font-size: 10px;
|
|
760
|
+
color: ${inkFaint};
|
|
761
|
+
}
|
|
762
|
+
.mushi-unread-badge {
|
|
763
|
+
display: inline-flex;
|
|
764
|
+
align-items: center;
|
|
765
|
+
justify-content: center;
|
|
766
|
+
min-width: 18px;
|
|
767
|
+
height: 18px;
|
|
768
|
+
padding: 0 5px;
|
|
769
|
+
border-radius: 999px;
|
|
770
|
+
font-family: ${fontMono};
|
|
771
|
+
font-size: 10px;
|
|
772
|
+
font-weight: 700;
|
|
773
|
+
color: ${widgetAccentInk};
|
|
774
|
+
background: ${widgetAccent};
|
|
775
|
+
}
|
|
776
|
+
.mushi-report-chevron {
|
|
777
|
+
font-size: 18px;
|
|
778
|
+
line-height: 1;
|
|
779
|
+
color: ${inkFaint};
|
|
780
|
+
transition: color 180ms ${easeStamp}, transform 180ms ${easeStamp};
|
|
781
|
+
}
|
|
782
|
+
.mushi-report-row:hover .mushi-report-chevron,
|
|
783
|
+
.mushi-report-row:focus-visible .mushi-report-chevron {
|
|
784
|
+
color: ${widgetAccent};
|
|
785
|
+
transform: translateX(2px);
|
|
681
786
|
}
|
|
682
787
|
.mushi-thread-summary {
|
|
683
788
|
border-bottom: 1px solid ${rule};
|
|
684
789
|
padding-bottom: 10px;
|
|
685
790
|
margin-bottom: 10px;
|
|
686
791
|
}
|
|
687
|
-
.mushi-thread-summary
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
792
|
+
.mushi-thread-summary-meta {
|
|
793
|
+
display: flex;
|
|
794
|
+
flex-wrap: wrap;
|
|
795
|
+
align-items: center;
|
|
796
|
+
gap: 10px;
|
|
797
|
+
margin-bottom: 8px;
|
|
692
798
|
}
|
|
693
799
|
.mushi-thread {
|
|
694
800
|
display: grid;
|
|
@@ -1685,6 +1791,95 @@ var CATEGORY_ICONS = {
|
|
|
1685
1791
|
other: "\u{1F4DD}"
|
|
1686
1792
|
};
|
|
1687
1793
|
var FEATURE_REQUEST_INTENT = "Feature request";
|
|
1794
|
+
function reporterStatusShort(status) {
|
|
1795
|
+
switch (status) {
|
|
1796
|
+
case "new":
|
|
1797
|
+
case "queued":
|
|
1798
|
+
case "pending":
|
|
1799
|
+
case "submitted":
|
|
1800
|
+
return "Sent";
|
|
1801
|
+
case "classified":
|
|
1802
|
+
case "triaged":
|
|
1803
|
+
case "grouped":
|
|
1804
|
+
case "dispatched":
|
|
1805
|
+
return "Review";
|
|
1806
|
+
case "fixing":
|
|
1807
|
+
return "Fixing";
|
|
1808
|
+
case "fixed":
|
|
1809
|
+
case "resolved":
|
|
1810
|
+
case "completed":
|
|
1811
|
+
return "Fixed";
|
|
1812
|
+
case "dismissed":
|
|
1813
|
+
return "Closed";
|
|
1814
|
+
default:
|
|
1815
|
+
return status.replace(/_/g, " ").slice(0, 12);
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
function reporterStatusLabel(status) {
|
|
1819
|
+
switch (status) {
|
|
1820
|
+
case "new":
|
|
1821
|
+
case "queued":
|
|
1822
|
+
case "pending":
|
|
1823
|
+
case "submitted":
|
|
1824
|
+
return "Submitted";
|
|
1825
|
+
case "classified":
|
|
1826
|
+
case "triaged":
|
|
1827
|
+
case "grouped":
|
|
1828
|
+
case "dispatched":
|
|
1829
|
+
return "In review";
|
|
1830
|
+
case "fixing":
|
|
1831
|
+
return "Fix in progress";
|
|
1832
|
+
case "fixed":
|
|
1833
|
+
case "resolved":
|
|
1834
|
+
case "completed":
|
|
1835
|
+
return "Fixed";
|
|
1836
|
+
case "dismissed":
|
|
1837
|
+
return "Closed";
|
|
1838
|
+
default:
|
|
1839
|
+
return status.replace(/_/g, " ");
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
function reporterStatusTone(status) {
|
|
1843
|
+
switch (status) {
|
|
1844
|
+
case "new":
|
|
1845
|
+
case "queued":
|
|
1846
|
+
case "pending":
|
|
1847
|
+
case "submitted":
|
|
1848
|
+
return "sent";
|
|
1849
|
+
case "classified":
|
|
1850
|
+
case "triaged":
|
|
1851
|
+
case "grouped":
|
|
1852
|
+
case "dispatched":
|
|
1853
|
+
return "review";
|
|
1854
|
+
case "fixing":
|
|
1855
|
+
return "fixing";
|
|
1856
|
+
case "fixed":
|
|
1857
|
+
case "resolved":
|
|
1858
|
+
case "completed":
|
|
1859
|
+
return "fixed";
|
|
1860
|
+
case "dismissed":
|
|
1861
|
+
return "closed";
|
|
1862
|
+
default:
|
|
1863
|
+
return "unknown";
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
function formatRelativeTime(iso) {
|
|
1867
|
+
const then = Date.parse(iso);
|
|
1868
|
+
if (Number.isNaN(then)) return "";
|
|
1869
|
+
const diffMs = Date.now() - then;
|
|
1870
|
+
if (diffMs < 0) return "just now";
|
|
1871
|
+
const sec = Math.floor(diffMs / 1e3);
|
|
1872
|
+
if (sec < 60) return "just now";
|
|
1873
|
+
const min = Math.floor(sec / 60);
|
|
1874
|
+
if (min < 60) return `${min}m ago`;
|
|
1875
|
+
const hr = Math.floor(min / 60);
|
|
1876
|
+
if (hr < 24) return `${hr}h ago`;
|
|
1877
|
+
const day = Math.floor(hr / 24);
|
|
1878
|
+
if (day < 7) return `${day}d ago`;
|
|
1879
|
+
const week = Math.floor(day / 7);
|
|
1880
|
+
if (week < 5) return `${week}w ago`;
|
|
1881
|
+
return new Date(then).toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
1882
|
+
}
|
|
1688
1883
|
function pad2(n) {
|
|
1689
1884
|
return n < 10 ? `0${n}` : String(n);
|
|
1690
1885
|
}
|
|
@@ -1799,6 +1994,8 @@ var MushiWidget = class _MushiWidget {
|
|
|
1799
1994
|
successTimer = null;
|
|
1800
1995
|
autoCloseTimer = null;
|
|
1801
1996
|
rewardsState = null;
|
|
1997
|
+
leaderboardEntries = null;
|
|
1998
|
+
leaderboardLoading = false;
|
|
1802
1999
|
/** Server-confirmed id for the just-submitted report. Surfaces in
|
|
1803
2000
|
* the success step as a copyable receipt + optional deep link to
|
|
1804
2001
|
* the Mushi console (when `dashboardUrl` is configured). Cleared
|
|
@@ -2071,6 +2268,11 @@ var MushiWidget = class _MushiWidget {
|
|
|
2071
2268
|
this.rewardsState = state;
|
|
2072
2269
|
if (this.isOpen) this.render();
|
|
2073
2270
|
}
|
|
2271
|
+
setLeaderboard(entries, loading = false) {
|
|
2272
|
+
this.leaderboardEntries = entries;
|
|
2273
|
+
this.leaderboardLoading = loading;
|
|
2274
|
+
if (this.isOpen && this.step === "leaderboard") this.render();
|
|
2275
|
+
}
|
|
2074
2276
|
destroy() {
|
|
2075
2277
|
if (this.successTimer !== null) {
|
|
2076
2278
|
clearTimeout(this.successTimer);
|
|
@@ -2491,6 +2693,8 @@ var MushiWidget = class _MushiWidget {
|
|
|
2491
2693
|
return this.renderReportsStep();
|
|
2492
2694
|
case "report-detail":
|
|
2493
2695
|
return this.renderReportDetailStep();
|
|
2696
|
+
case "leaderboard":
|
|
2697
|
+
return this.renderLeaderboardStep();
|
|
2494
2698
|
}
|
|
2495
2699
|
}
|
|
2496
2700
|
renderOutdatedBanner() {
|
|
@@ -2523,7 +2727,7 @@ var MushiWidget = class _MushiWidget {
|
|
|
2523
2727
|
renderHeader(opts) {
|
|
2524
2728
|
const t = this.locale;
|
|
2525
2729
|
const { title, showBack = false, step, eyebrow } = opts;
|
|
2526
|
-
const eyebrowHtml = showBack ? `<button type="button" class="mushi-back" data-action="back" aria-label="${t.widget.back}">\u2190
|
|
2730
|
+
const eyebrowHtml = showBack ? `<button type="button" class="mushi-back" data-action="back" aria-label="${t.widget.back}">\u2190</button>` : `<span class="mushi-header-eyebrow">${eyebrow ?? "Mushi \xB7 Report"}</span>`;
|
|
2527
2731
|
const counterHtml = step ? `<span class="mushi-step-counter" aria-label="Step ${step} of ${TOTAL_STEPS}"><b>${pad2(step)}</b> / ${pad2(TOTAL_STEPS)}</span>` : "";
|
|
2528
2732
|
return `
|
|
2529
2733
|
<div class="mushi-header">
|
|
@@ -2659,24 +2863,61 @@ var MushiWidget = class _MushiWidget {
|
|
|
2659
2863
|
`;
|
|
2660
2864
|
}
|
|
2661
2865
|
renderReportsStep() {
|
|
2662
|
-
const reports = this.reporterReports.map((report) =>
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2866
|
+
const reports = this.reporterReports.map((report) => {
|
|
2867
|
+
const title = report.summary ?? report.description ?? `Report ${report.id.slice(0, 8)}`;
|
|
2868
|
+
const tone = reporterStatusTone(report.status);
|
|
2869
|
+
const when = formatRelativeTime(report.created_at);
|
|
2870
|
+
const unread = report.unread_count && report.unread_count > 0 ? `<span class="mushi-unread-badge" aria-label="${report.unread_count} unread">${report.unread_count}</span>` : "";
|
|
2871
|
+
return `
|
|
2872
|
+
<button type="button" class="mushi-report-row" data-report-id="${escapeHtml(report.id)}" aria-label="View report: ${escapeHtml(title)}">
|
|
2873
|
+
<div class="mushi-report-main">
|
|
2874
|
+
<span class="mushi-report-title">${escapeHtml(title)}</span>
|
|
2875
|
+
<span class="mushi-report-meta">
|
|
2876
|
+
<span class="mushi-report-status mushi-status-${tone}">${escapeHtml(reporterStatusShort(report.status))}</span>
|
|
2877
|
+
${when ? `<span class="mushi-report-when">${escapeHtml(when)}</span>` : ""}
|
|
2878
|
+
${unread}
|
|
2879
|
+
</span>
|
|
2880
|
+
</div>
|
|
2881
|
+
<span class="mushi-report-chevron" aria-hidden="true">\u203A</span>
|
|
2882
|
+
</button>`;
|
|
2883
|
+
}).join("");
|
|
2884
|
+
const leaderboardBtn = this.rewardsState ? `<button type="button" class="mushi-leaderboard-link" data-action="open-leaderboard">\u{1F3C6} Leaderboard</button>` : "";
|
|
2669
2885
|
return `
|
|
2670
2886
|
${this.renderHeader({ title: "Your reports", showBack: true, eyebrow: "Mushi \xB7 Inbox" })}
|
|
2671
2887
|
<div class="mushi-body">
|
|
2672
2888
|
${this.reporterLoading ? '<p class="mushi-muted">Loading reports\u2026</p>' : ""}
|
|
2673
2889
|
${this.reporterError ? `<p class="mushi-error-inline">${escapeHtml(this.reporterError)}</p>` : ""}
|
|
2674
2890
|
${reports || (!this.reporterLoading ? '<p class="mushi-muted">No reports from this browser yet.</p>' : "")}
|
|
2891
|
+
${leaderboardBtn}
|
|
2892
|
+
</div>
|
|
2893
|
+
`;
|
|
2894
|
+
}
|
|
2895
|
+
renderLeaderboardStep() {
|
|
2896
|
+
const myRank = this.rewardsState && this.leaderboardEntries ? this.leaderboardEntries.findIndex((e) => e.display_name === "You") + 1 : 0;
|
|
2897
|
+
const rows = (this.leaderboardEntries ?? []).map((e, i) => `
|
|
2898
|
+
<div class="mushi-lb-row ${i === 0 ? "mushi-lb-top" : ""}">
|
|
2899
|
+
<span class="mushi-lb-rank">#${i + 1}</span>
|
|
2900
|
+
<span class="mushi-lb-name">${escapeHtml(e.display_name)}</span>
|
|
2901
|
+
${e.tier_name ? `<span class="mushi-lb-tier">${escapeHtml(e.tier_name)}</span>` : ""}
|
|
2902
|
+
<span class="mushi-lb-pts">${e.total_points.toLocaleString()} pts</span>
|
|
2903
|
+
</div>
|
|
2904
|
+
`).join("");
|
|
2905
|
+
return `
|
|
2906
|
+
${this.renderHeader({ title: "\u{1F3C6} Leaderboard", showBack: true, eyebrow: "Mushi \xB7 Contributors" })}
|
|
2907
|
+
<div class="mushi-body">
|
|
2908
|
+
${this.leaderboardLoading ? '<p class="mushi-muted">Loading leaderboard\u2026</p>' : ""}
|
|
2909
|
+
${!this.leaderboardLoading && !this.leaderboardEntries?.length ? '<p class="mushi-muted">No contributors yet \u2014 be the first!</p>' : ""}
|
|
2910
|
+
<div class="mushi-lb-list">${rows}</div>
|
|
2911
|
+
${myRank > 0 ? `<p class="mushi-lb-myrank">You are ranked #${myRank}</p>` : ""}
|
|
2912
|
+
<p class="mushi-lb-note">Top contributors this month \xB7 Points refresh monthly</p>
|
|
2675
2913
|
</div>
|
|
2676
2914
|
`;
|
|
2677
2915
|
}
|
|
2678
2916
|
renderReportDetailStep() {
|
|
2679
2917
|
const report = this.reporterReports.find((r) => r.id === this.selectedReportId);
|
|
2918
|
+
const status = report?.status ?? "unknown";
|
|
2919
|
+
const tone = reporterStatusTone(status);
|
|
2920
|
+
const when = report?.created_at ? formatRelativeTime(report.created_at) : "";
|
|
2680
2921
|
const comments = this.reporterComments.map((comment) => `
|
|
2681
2922
|
<div class="mushi-thread-comment ${comment.author_kind}">
|
|
2682
2923
|
<strong>${escapeHtml(comment.author_kind === "reporter" ? "You" : comment.author_name ?? "Developer")}</strong>
|
|
@@ -2687,7 +2928,10 @@ var MushiWidget = class _MushiWidget {
|
|
|
2687
2928
|
${this.renderHeader({ title: "Report thread", showBack: true, eyebrow: "Mushi \xB7 Inbox" })}
|
|
2688
2929
|
<div class="mushi-body">
|
|
2689
2930
|
<div class="mushi-thread-summary">
|
|
2690
|
-
<
|
|
2931
|
+
<div class="mushi-thread-summary-meta">
|
|
2932
|
+
<span class="mushi-report-status mushi-status-${tone}">${escapeHtml(reporterStatusLabel(status))}</span>
|
|
2933
|
+
${when ? `<span class="mushi-report-when">Reported ${escapeHtml(when)}</span>` : ""}
|
|
2934
|
+
</div>
|
|
2691
2935
|
<p>${escapeHtml(report?.summary ?? report?.description ?? "Report details")}</p>
|
|
2692
2936
|
</div>
|
|
2693
2937
|
<div class="mushi-thread">
|
|
@@ -2995,6 +3239,8 @@ var MushiWidget = class _MushiWidget {
|
|
|
2995
3239
|
} else if (this.step === "report-detail") {
|
|
2996
3240
|
this.step = "reports";
|
|
2997
3241
|
this.selectedReportId = null;
|
|
3242
|
+
} else if (this.step === "leaderboard") {
|
|
3243
|
+
this.step = "reports";
|
|
2998
3244
|
}
|
|
2999
3245
|
this.render();
|
|
3000
3246
|
});
|
|
@@ -3014,6 +3260,11 @@ var MushiWidget = class _MushiWidget {
|
|
|
3014
3260
|
if (reportId) void this.loadReporterComments(reportId);
|
|
3015
3261
|
});
|
|
3016
3262
|
});
|
|
3263
|
+
panel.querySelector('[data-action="open-leaderboard"]')?.addEventListener("click", () => {
|
|
3264
|
+
this.step = "leaderboard";
|
|
3265
|
+
this.callbacks.onLeaderboardOpen?.();
|
|
3266
|
+
this.render();
|
|
3267
|
+
});
|
|
3017
3268
|
panel.querySelector('[data-action="reporter-reply"]')?.addEventListener("click", () => {
|
|
3018
3269
|
void this.submitReporterReply(panel);
|
|
3019
3270
|
});
|
|
@@ -3265,6 +3516,10 @@ var MushiWidget = class _MushiWidget {
|
|
|
3265
3516
|
const submit = this.shadow.querySelector('[data-action="submit"]');
|
|
3266
3517
|
submit?.click();
|
|
3267
3518
|
}
|
|
3519
|
+
recorderOpenMyReports() {
|
|
3520
|
+
if (!this.isOpen) this.open();
|
|
3521
|
+
void this.loadReporterReports();
|
|
3522
|
+
}
|
|
3268
3523
|
};
|
|
3269
3524
|
|
|
3270
3525
|
// src/marketing-recorder.ts
|
|
@@ -3287,7 +3542,8 @@ function exposeMarketingRecorder(widget) {
|
|
|
3287
3542
|
selectCategory: (category) => widget.recorderSelectCategory(category),
|
|
3288
3543
|
selectIntent: (label) => widget.recorderSelectIntent(label),
|
|
3289
3544
|
focusDescription: () => widget.recorderFocusDescription(),
|
|
3290
|
-
submit: () => widget.recorderSubmit()
|
|
3545
|
+
submit: () => widget.recorderSubmit(),
|
|
3546
|
+
openMyReports: () => widget.recorderOpenMyReports()
|
|
3291
3547
|
};
|
|
3292
3548
|
globalThis.__mushiRecorder = api;
|
|
3293
3549
|
}
|
|
@@ -3458,6 +3714,24 @@ async function fetchAndCacheTier(userId) {
|
|
|
3458
3714
|
noteRewardsApiFailure(res.error?.code);
|
|
3459
3715
|
return null;
|
|
3460
3716
|
}
|
|
3717
|
+
async function fetchLeaderboard(limit = 10) {
|
|
3718
|
+
if (!apiClient || isRewardsApiBackedOff()) return null;
|
|
3719
|
+
try {
|
|
3720
|
+
const res = await apiClient.getHallOfFame(limit);
|
|
3721
|
+
if (res.ok && res.data) {
|
|
3722
|
+
return (res.data.data ?? []).map((e) => ({
|
|
3723
|
+
display_name: e.display_name,
|
|
3724
|
+
tier_name: e.tier_name,
|
|
3725
|
+
total_points: e.total_points,
|
|
3726
|
+
points_30d: e.points_30d
|
|
3727
|
+
}));
|
|
3728
|
+
}
|
|
3729
|
+
noteRewardsApiFailure(res.error?.code);
|
|
3730
|
+
return null;
|
|
3731
|
+
} catch {
|
|
3732
|
+
return null;
|
|
3733
|
+
}
|
|
3734
|
+
}
|
|
3461
3735
|
var routeObserver = null;
|
|
3462
3736
|
var clickHandler = null;
|
|
3463
3737
|
var origPushState = null;
|
|
@@ -3727,6 +4001,29 @@ function readHeader(headers, name) {
|
|
|
3727
4001
|
|
|
3728
4002
|
// src/capture/network.ts
|
|
3729
4003
|
var MAX_ENTRIES2 = 30;
|
|
4004
|
+
var TRACEPARENT_VERSION = "00";
|
|
4005
|
+
function generateTraceparent() {
|
|
4006
|
+
const traceBytes = crypto.getRandomValues(new Uint8Array(16));
|
|
4007
|
+
const spanBytes = crypto.getRandomValues(new Uint8Array(8));
|
|
4008
|
+
const toHex = (bytes) => Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
4009
|
+
const traceId = toHex(traceBytes);
|
|
4010
|
+
const spanId = toHex(spanBytes);
|
|
4011
|
+
return {
|
|
4012
|
+
traceparent: `${TRACEPARENT_VERSION}-${traceId}-${spanId}-01`,
|
|
4013
|
+
traceId,
|
|
4014
|
+
spanId
|
|
4015
|
+
};
|
|
4016
|
+
}
|
|
4017
|
+
function matchesCorsUrls(url, corsUrls) {
|
|
4018
|
+
for (const pattern of corsUrls) {
|
|
4019
|
+
if (typeof pattern === "string") {
|
|
4020
|
+
if (url.includes(pattern)) return true;
|
|
4021
|
+
} else {
|
|
4022
|
+
if (pattern.test(url)) return true;
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
return false;
|
|
4026
|
+
}
|
|
3730
4027
|
function createNetworkCapture(options = {}) {
|
|
3731
4028
|
const entries = [];
|
|
3732
4029
|
const originalFetch = globalThis.fetch;
|
|
@@ -3737,15 +4034,29 @@ function createNetworkCapture(options = {}) {
|
|
|
3737
4034
|
const url = getRequestUrl(input);
|
|
3738
4035
|
const internalKind = getInternalRequestKind(input, init);
|
|
3739
4036
|
const shouldRecord = !internalKind && !shouldIgnoreMushiUrl(url, activeOptions);
|
|
4037
|
+
let traceId;
|
|
4038
|
+
let patchedInit = init;
|
|
4039
|
+
const tp = activeOptions.tracePropagation;
|
|
4040
|
+
if (shouldRecord && tp?.enabled && tp.corsUrls?.length && matchesCorsUrls(url, tp.corsUrls)) {
|
|
4041
|
+
const { traceparent, traceId: tid } = generateTraceparent();
|
|
4042
|
+
traceId = tid;
|
|
4043
|
+
const existingHeaders = init?.headers ? new Headers(init.headers) : new Headers();
|
|
4044
|
+
existingHeaders.set("traceparent", traceparent);
|
|
4045
|
+
if (activeOptions.sessionId) {
|
|
4046
|
+
existingHeaders.set("x-mushi-session", activeOptions.sessionId);
|
|
4047
|
+
}
|
|
4048
|
+
patchedInit = { ...init, headers: existingHeaders };
|
|
4049
|
+
}
|
|
3740
4050
|
try {
|
|
3741
|
-
const response = await originalFetch.call(globalThis, input,
|
|
4051
|
+
const response = await originalFetch.call(globalThis, input, patchedInit);
|
|
3742
4052
|
if (shouldRecord) {
|
|
3743
4053
|
addEntry({
|
|
3744
4054
|
method,
|
|
3745
4055
|
url: truncateUrl(url),
|
|
3746
4056
|
status: response.status,
|
|
3747
4057
|
duration: Date.now() - startTime,
|
|
3748
|
-
timestamp: startTime
|
|
4058
|
+
timestamp: startTime,
|
|
4059
|
+
...traceId ? { traceId } : {}
|
|
3749
4060
|
});
|
|
3750
4061
|
}
|
|
3751
4062
|
return response;
|
|
@@ -3757,7 +4068,8 @@ function createNetworkCapture(options = {}) {
|
|
|
3757
4068
|
status: 0,
|
|
3758
4069
|
duration: Date.now() - startTime,
|
|
3759
4070
|
timestamp: startTime,
|
|
3760
|
-
error: error instanceof Error ? error.message : "Network error"
|
|
4071
|
+
error: error instanceof Error ? error.message : "Network error",
|
|
4072
|
+
...traceId ? { traceId } : {}
|
|
3761
4073
|
});
|
|
3762
4074
|
}
|
|
3763
4075
|
throw error;
|
|
@@ -4990,7 +5302,7 @@ function createProactiveManager(config = {}) {
|
|
|
4990
5302
|
|
|
4991
5303
|
// src/version.ts
|
|
4992
5304
|
var MUSHI_SDK_PACKAGE = "@mushi-mushi/web";
|
|
4993
|
-
var MUSHI_SDK_VERSION = "1.
|
|
5305
|
+
var MUSHI_SDK_VERSION = "1.10.0" ;
|
|
4994
5306
|
|
|
4995
5307
|
// src/mushi.ts
|
|
4996
5308
|
var instance = null;
|
|
@@ -5080,7 +5392,9 @@ function createInstance(config) {
|
|
|
5080
5392
|
if (activeConfig.capture?.network !== false) {
|
|
5081
5393
|
const networkOptions = {
|
|
5082
5394
|
apiEndpoint: resolveApiEndpoint(activeConfig),
|
|
5083
|
-
ignoreUrls: activeConfig.capture?.ignoreUrls
|
|
5395
|
+
ignoreUrls: activeConfig.capture?.ignoreUrls,
|
|
5396
|
+
tracePropagation: activeConfig.capture?.tracePropagation,
|
|
5397
|
+
sessionId: getSessionId()
|
|
5084
5398
|
};
|
|
5085
5399
|
if (networkCap) {
|
|
5086
5400
|
networkCap.updateOptions(networkOptions);
|
|
@@ -5266,6 +5580,12 @@ function createInstance(config) {
|
|
|
5266
5580
|
async onReporterReply(reportId, body) {
|
|
5267
5581
|
const result = await apiClient2.replyToReporterReport(reportId, getReporterToken(), body);
|
|
5268
5582
|
if (!result.ok) throw new Error(result.error?.message ?? "Could not send reply");
|
|
5583
|
+
},
|
|
5584
|
+
onLeaderboardOpen() {
|
|
5585
|
+
widget.setLeaderboard(null, true);
|
|
5586
|
+
void fetchLeaderboard(10).then((entries) => {
|
|
5587
|
+
widget.setLeaderboard(entries, false);
|
|
5588
|
+
});
|
|
5269
5589
|
}
|
|
5270
5590
|
}, MUSHI_SDK_VERSION);
|
|
5271
5591
|
syncCaptureModules();
|
|
@@ -5522,7 +5842,7 @@ function createInstance(config) {
|
|
|
5522
5842
|
await offlineQueue.enqueue(finalReport);
|
|
5523
5843
|
log.info("Offline \u2014 report queued", { reportId: finalReport.id });
|
|
5524
5844
|
emit("report:queued", { reportId: finalReport.id });
|
|
5525
|
-
return;
|
|
5845
|
+
return { reportId: null, queuedOffline: true };
|
|
5526
5846
|
}
|
|
5527
5847
|
const result = await apiClient2.submitReport(finalReport);
|
|
5528
5848
|
if (result.ok) {
|
|
@@ -5820,6 +6140,28 @@ function createInstance(config) {
|
|
|
5820
6140
|
},
|
|
5821
6141
|
pulseTrigger() {
|
|
5822
6142
|
widget.pulseTrigger?.();
|
|
6143
|
+
},
|
|
6144
|
+
// ─── Reporter API (cross-platform) ────────────────────────────────
|
|
6145
|
+
async listMyReports() {
|
|
6146
|
+
const result = await apiClient2.listReporterReports(getReporterToken());
|
|
6147
|
+
if (!result.ok) return [];
|
|
6148
|
+
return result.data?.reports ?? [];
|
|
6149
|
+
},
|
|
6150
|
+
async listMyComments(reportId) {
|
|
6151
|
+
const result = await apiClient2.listReporterComments(reportId, getReporterToken());
|
|
6152
|
+
if (!result.ok) return [];
|
|
6153
|
+
return result.data?.comments ?? [];
|
|
6154
|
+
},
|
|
6155
|
+
async replyToReport(reportId, body) {
|
|
6156
|
+
const result = await apiClient2.replyToReporterReport(reportId, getReporterToken(), body);
|
|
6157
|
+
if (!result.ok) return null;
|
|
6158
|
+
return result.data?.comment ?? null;
|
|
6159
|
+
},
|
|
6160
|
+
async getHallOfFame(limit = 20) {
|
|
6161
|
+
const result = await apiClient2.getHallOfFame(limit);
|
|
6162
|
+
if (!result.ok) return [];
|
|
6163
|
+
const raw = result.data;
|
|
6164
|
+
return raw?.data ?? [];
|
|
5823
6165
|
}
|
|
5824
6166
|
};
|
|
5825
6167
|
if (typeof globalThis !== "undefined" && (bootstrapConfig.debug ?? false)) {
|
|
@@ -6137,7 +6479,11 @@ function createNoopInstance() {
|
|
|
6137
6479
|
recordActivity: () => {
|
|
6138
6480
|
},
|
|
6139
6481
|
pulseTrigger: () => {
|
|
6140
|
-
}
|
|
6482
|
+
},
|
|
6483
|
+
listMyReports: async () => [],
|
|
6484
|
+
listMyComments: async () => [],
|
|
6485
|
+
replyToReport: async () => null,
|
|
6486
|
+
getHallOfFame: async () => []
|
|
6141
6487
|
};
|
|
6142
6488
|
}
|
|
6143
6489
|
function installAutoBreadcrumbs(buffer) {
|