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