@mushi-mushi/web 1.9.0 → 1.11.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 +401 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +401 -33
- 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
|
+
transition: color 150ms ${easeStamp};
|
|
567
|
+
}
|
|
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;
|
|
566
583
|
transition: color 150ms ${easeStamp};
|
|
567
584
|
}
|
|
568
|
-
.mushi-close:hover
|
|
569
|
-
.mushi-
|
|
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,103 @@ 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 \u2014 confirm?";
|
|
1838
|
+
case "verified":
|
|
1839
|
+
return "Verified";
|
|
1840
|
+
case "reopened":
|
|
1841
|
+
return "Reopened";
|
|
1842
|
+
case "dismissed":
|
|
1843
|
+
return "Closed";
|
|
1844
|
+
default:
|
|
1845
|
+
return status.replace(/_/g, " ");
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
function reporterStatusTone(status) {
|
|
1849
|
+
switch (status) {
|
|
1850
|
+
case "new":
|
|
1851
|
+
case "queued":
|
|
1852
|
+
case "pending":
|
|
1853
|
+
case "submitted":
|
|
1854
|
+
return "sent";
|
|
1855
|
+
case "classified":
|
|
1856
|
+
case "triaged":
|
|
1857
|
+
case "grouped":
|
|
1858
|
+
case "dispatched":
|
|
1859
|
+
return "review";
|
|
1860
|
+
case "fixing":
|
|
1861
|
+
return "fixing";
|
|
1862
|
+
case "fixed":
|
|
1863
|
+
case "resolved":
|
|
1864
|
+
case "completed":
|
|
1865
|
+
return "fixed";
|
|
1866
|
+
case "verified":
|
|
1867
|
+
return "fixed";
|
|
1868
|
+
case "reopened":
|
|
1869
|
+
return "fixing";
|
|
1870
|
+
case "dismissed":
|
|
1871
|
+
return "closed";
|
|
1872
|
+
default:
|
|
1873
|
+
return "unknown";
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
function formatRelativeTime(iso) {
|
|
1877
|
+
const then = Date.parse(iso);
|
|
1878
|
+
if (Number.isNaN(then)) return "";
|
|
1879
|
+
const diffMs = Date.now() - then;
|
|
1880
|
+
if (diffMs < 0) return "just now";
|
|
1881
|
+
const sec = Math.floor(diffMs / 1e3);
|
|
1882
|
+
if (sec < 60) return "just now";
|
|
1883
|
+
const min = Math.floor(sec / 60);
|
|
1884
|
+
if (min < 60) return `${min}m ago`;
|
|
1885
|
+
const hr = Math.floor(min / 60);
|
|
1886
|
+
if (hr < 24) return `${hr}h ago`;
|
|
1887
|
+
const day = Math.floor(hr / 24);
|
|
1888
|
+
if (day < 7) return `${day}d ago`;
|
|
1889
|
+
const week = Math.floor(day / 7);
|
|
1890
|
+
if (week < 5) return `${week}w ago`;
|
|
1891
|
+
return new Date(then).toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
1892
|
+
}
|
|
1690
1893
|
function pad2(n) {
|
|
1691
1894
|
return n < 10 ? `0${n}` : String(n);
|
|
1692
1895
|
}
|
|
@@ -1801,6 +2004,8 @@ var MushiWidget = class _MushiWidget {
|
|
|
1801
2004
|
successTimer = null;
|
|
1802
2005
|
autoCloseTimer = null;
|
|
1803
2006
|
rewardsState = null;
|
|
2007
|
+
leaderboardEntries = null;
|
|
2008
|
+
leaderboardLoading = false;
|
|
1804
2009
|
/** Server-confirmed id for the just-submitted report. Surfaces in
|
|
1805
2010
|
* the success step as a copyable receipt + optional deep link to
|
|
1806
2011
|
* the Mushi console (when `dashboardUrl` is configured). Cleared
|
|
@@ -2073,6 +2278,11 @@ var MushiWidget = class _MushiWidget {
|
|
|
2073
2278
|
this.rewardsState = state;
|
|
2074
2279
|
if (this.isOpen) this.render();
|
|
2075
2280
|
}
|
|
2281
|
+
setLeaderboard(entries, loading = false) {
|
|
2282
|
+
this.leaderboardEntries = entries;
|
|
2283
|
+
this.leaderboardLoading = loading;
|
|
2284
|
+
if (this.isOpen && this.step === "leaderboard") this.render();
|
|
2285
|
+
}
|
|
2076
2286
|
destroy() {
|
|
2077
2287
|
if (this.successTimer !== null) {
|
|
2078
2288
|
clearTimeout(this.successTimer);
|
|
@@ -2493,6 +2703,8 @@ var MushiWidget = class _MushiWidget {
|
|
|
2493
2703
|
return this.renderReportsStep();
|
|
2494
2704
|
case "report-detail":
|
|
2495
2705
|
return this.renderReportDetailStep();
|
|
2706
|
+
case "leaderboard":
|
|
2707
|
+
return this.renderLeaderboardStep();
|
|
2496
2708
|
}
|
|
2497
2709
|
}
|
|
2498
2710
|
renderOutdatedBanner() {
|
|
@@ -2525,7 +2737,7 @@ var MushiWidget = class _MushiWidget {
|
|
|
2525
2737
|
renderHeader(opts) {
|
|
2526
2738
|
const t = this.locale;
|
|
2527
2739
|
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
|
|
2740
|
+
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
2741
|
const counterHtml = step ? `<span class="mushi-step-counter" aria-label="Step ${step} of ${TOTAL_STEPS}"><b>${pad2(step)}</b> / ${pad2(TOTAL_STEPS)}</span>` : "";
|
|
2530
2742
|
return `
|
|
2531
2743
|
<div class="mushi-header">
|
|
@@ -2661,24 +2873,61 @@ var MushiWidget = class _MushiWidget {
|
|
|
2661
2873
|
`;
|
|
2662
2874
|
}
|
|
2663
2875
|
renderReportsStep() {
|
|
2664
|
-
const reports = this.reporterReports.map((report) =>
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2876
|
+
const reports = this.reporterReports.map((report) => {
|
|
2877
|
+
const title = report.summary ?? report.description ?? `Report ${report.id.slice(0, 8)}`;
|
|
2878
|
+
const tone = reporterStatusTone(report.status);
|
|
2879
|
+
const when = formatRelativeTime(report.created_at);
|
|
2880
|
+
const unread = report.unread_count && report.unread_count > 0 ? `<span class="mushi-unread-badge" aria-label="${report.unread_count} unread">${report.unread_count}</span>` : "";
|
|
2881
|
+
return `
|
|
2882
|
+
<button type="button" class="mushi-report-row" data-report-id="${escapeHtml(report.id)}" aria-label="View report: ${escapeHtml(title)}">
|
|
2883
|
+
<div class="mushi-report-main">
|
|
2884
|
+
<span class="mushi-report-title">${escapeHtml(title)}</span>
|
|
2885
|
+
<span class="mushi-report-meta">
|
|
2886
|
+
<span class="mushi-report-status mushi-status-${tone}">${escapeHtml(reporterStatusShort(report.status))}</span>
|
|
2887
|
+
${when ? `<span class="mushi-report-when">${escapeHtml(when)}</span>` : ""}
|
|
2888
|
+
${unread}
|
|
2889
|
+
</span>
|
|
2890
|
+
</div>
|
|
2891
|
+
<span class="mushi-report-chevron" aria-hidden="true">\u203A</span>
|
|
2892
|
+
</button>`;
|
|
2893
|
+
}).join("");
|
|
2894
|
+
const leaderboardBtn = this.rewardsState ? `<button type="button" class="mushi-leaderboard-link" data-action="open-leaderboard">\u{1F3C6} Leaderboard</button>` : "";
|
|
2671
2895
|
return `
|
|
2672
2896
|
${this.renderHeader({ title: "Your reports", showBack: true, eyebrow: "Mushi \xB7 Inbox" })}
|
|
2673
2897
|
<div class="mushi-body">
|
|
2674
2898
|
${this.reporterLoading ? '<p class="mushi-muted">Loading reports\u2026</p>' : ""}
|
|
2675
2899
|
${this.reporterError ? `<p class="mushi-error-inline">${escapeHtml(this.reporterError)}</p>` : ""}
|
|
2676
2900
|
${reports || (!this.reporterLoading ? '<p class="mushi-muted">No reports from this browser yet.</p>' : "")}
|
|
2901
|
+
${leaderboardBtn}
|
|
2902
|
+
</div>
|
|
2903
|
+
`;
|
|
2904
|
+
}
|
|
2905
|
+
renderLeaderboardStep() {
|
|
2906
|
+
const myRank = this.rewardsState && this.leaderboardEntries ? this.leaderboardEntries.findIndex((e) => e.display_name === "You") + 1 : 0;
|
|
2907
|
+
const rows = (this.leaderboardEntries ?? []).map((e, i) => `
|
|
2908
|
+
<div class="mushi-lb-row ${i === 0 ? "mushi-lb-top" : ""}">
|
|
2909
|
+
<span class="mushi-lb-rank">#${i + 1}</span>
|
|
2910
|
+
<span class="mushi-lb-name">${escapeHtml(e.display_name)}</span>
|
|
2911
|
+
${e.tier_name ? `<span class="mushi-lb-tier">${escapeHtml(e.tier_name)}</span>` : ""}
|
|
2912
|
+
<span class="mushi-lb-pts">${e.total_points.toLocaleString()} pts</span>
|
|
2913
|
+
</div>
|
|
2914
|
+
`).join("");
|
|
2915
|
+
return `
|
|
2916
|
+
${this.renderHeader({ title: "\u{1F3C6} Leaderboard", showBack: true, eyebrow: "Mushi \xB7 Contributors" })}
|
|
2917
|
+
<div class="mushi-body">
|
|
2918
|
+
${this.leaderboardLoading ? '<p class="mushi-muted">Loading leaderboard\u2026</p>' : ""}
|
|
2919
|
+
${!this.leaderboardLoading && !this.leaderboardEntries?.length ? '<p class="mushi-muted">No contributors yet \u2014 be the first!</p>' : ""}
|
|
2920
|
+
<div class="mushi-lb-list">${rows}</div>
|
|
2921
|
+
${myRank > 0 ? `<p class="mushi-lb-myrank">You are ranked #${myRank}</p>` : ""}
|
|
2922
|
+
<p class="mushi-lb-note">Top contributors this month \xB7 Points refresh monthly</p>
|
|
2677
2923
|
</div>
|
|
2678
2924
|
`;
|
|
2679
2925
|
}
|
|
2680
2926
|
renderReportDetailStep() {
|
|
2681
2927
|
const report = this.reporterReports.find((r) => r.id === this.selectedReportId);
|
|
2928
|
+
const status = report?.status ?? "unknown";
|
|
2929
|
+
const tone = reporterStatusTone(status);
|
|
2930
|
+
const when = report?.created_at ? formatRelativeTime(report.created_at) : "";
|
|
2682
2931
|
const comments = this.reporterComments.map((comment) => `
|
|
2683
2932
|
<div class="mushi-thread-comment ${comment.author_kind}">
|
|
2684
2933
|
<strong>${escapeHtml(comment.author_kind === "reporter" ? "You" : comment.author_name ?? "Developer")}</strong>
|
|
@@ -2689,12 +2938,21 @@ var MushiWidget = class _MushiWidget {
|
|
|
2689
2938
|
${this.renderHeader({ title: "Report thread", showBack: true, eyebrow: "Mushi \xB7 Inbox" })}
|
|
2690
2939
|
<div class="mushi-body">
|
|
2691
2940
|
<div class="mushi-thread-summary">
|
|
2692
|
-
<
|
|
2941
|
+
<div class="mushi-thread-summary-meta">
|
|
2942
|
+
<span class="mushi-report-status mushi-status-${tone}">${escapeHtml(reporterStatusLabel(status))}</span>
|
|
2943
|
+
${when ? `<span class="mushi-report-when">Reported ${escapeHtml(when)}</span>` : ""}
|
|
2944
|
+
</div>
|
|
2693
2945
|
<p>${escapeHtml(report?.summary ?? report?.description ?? "Report details")}</p>
|
|
2694
2946
|
</div>
|
|
2695
2947
|
<div class="mushi-thread">
|
|
2696
2948
|
${this.reporterLoading ? '<p class="mushi-muted">Loading thread\u2026</p>' : comments || '<p class="mushi-muted">No developer replies yet.</p>'}
|
|
2697
2949
|
</div>
|
|
2950
|
+
${["fixed", "resolved", "verified"].includes(status) ? `
|
|
2951
|
+
<div class="mushi-verify-actions" role="group" aria-label="Fix verification">
|
|
2952
|
+
<button type="button" class="mushi-intent-btn" data-action="reporter-confirms">Yes, fixed for me</button>
|
|
2953
|
+
<button type="button" class="mushi-intent-btn" data-action="reporter-not-fixed">Not fixed yet</button>
|
|
2954
|
+
</div>
|
|
2955
|
+
` : ""}
|
|
2698
2956
|
<textarea class="mushi-textarea" data-role="reporter-reply" rows="3" placeholder="Reply to the developer\u2026"></textarea>
|
|
2699
2957
|
<button type="button" class="mushi-submit" data-action="reporter-reply">
|
|
2700
2958
|
<span>Reply</span><span class="mushi-submit-arrow" aria-hidden="true">\u2192</span>
|
|
@@ -2997,6 +3255,8 @@ var MushiWidget = class _MushiWidget {
|
|
|
2997
3255
|
} else if (this.step === "report-detail") {
|
|
2998
3256
|
this.step = "reports";
|
|
2999
3257
|
this.selectedReportId = null;
|
|
3258
|
+
} else if (this.step === "leaderboard") {
|
|
3259
|
+
this.step = "reports";
|
|
3000
3260
|
}
|
|
3001
3261
|
this.render();
|
|
3002
3262
|
});
|
|
@@ -3016,9 +3276,20 @@ var MushiWidget = class _MushiWidget {
|
|
|
3016
3276
|
if (reportId) void this.loadReporterComments(reportId);
|
|
3017
3277
|
});
|
|
3018
3278
|
});
|
|
3279
|
+
panel.querySelector('[data-action="open-leaderboard"]')?.addEventListener("click", () => {
|
|
3280
|
+
this.step = "leaderboard";
|
|
3281
|
+
this.callbacks.onLeaderboardOpen?.();
|
|
3282
|
+
this.render();
|
|
3283
|
+
});
|
|
3019
3284
|
panel.querySelector('[data-action="reporter-reply"]')?.addEventListener("click", () => {
|
|
3020
3285
|
void this.submitReporterReply(panel);
|
|
3021
3286
|
});
|
|
3287
|
+
panel.querySelector('[data-action="reporter-confirms"]')?.addEventListener("click", () => {
|
|
3288
|
+
void this.submitReporterFeedback("confirms");
|
|
3289
|
+
});
|
|
3290
|
+
panel.querySelector('[data-action="reporter-not-fixed"]')?.addEventListener("click", () => {
|
|
3291
|
+
void this.submitReporterFeedback("not_fixed");
|
|
3292
|
+
});
|
|
3022
3293
|
panel.querySelector('[data-action="copy-report-id"]')?.addEventListener("click", (e) => {
|
|
3023
3294
|
const btn = e.currentTarget;
|
|
3024
3295
|
const id = btn.dataset.copyId;
|
|
@@ -3200,6 +3471,21 @@ var MushiWidget = class _MushiWidget {
|
|
|
3200
3471
|
this.render();
|
|
3201
3472
|
}
|
|
3202
3473
|
}
|
|
3474
|
+
async submitReporterFeedback(signal) {
|
|
3475
|
+
const reportId = this.selectedReportId;
|
|
3476
|
+
if (!reportId || this.reporterLoading) return;
|
|
3477
|
+
this.reporterLoading = true;
|
|
3478
|
+
this.render();
|
|
3479
|
+
try {
|
|
3480
|
+
await this.callbacks.onReporterFeedback?.(reportId, signal);
|
|
3481
|
+
await this.loadReporterReports();
|
|
3482
|
+
if (reportId) await this.loadReporterComments(reportId);
|
|
3483
|
+
} catch (err) {
|
|
3484
|
+
this.reporterError = err instanceof Error ? err.message : "Could not send feedback.";
|
|
3485
|
+
this.reporterLoading = false;
|
|
3486
|
+
this.render();
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3203
3489
|
async submitReporterReply(panel) {
|
|
3204
3490
|
const reportId = this.selectedReportId;
|
|
3205
3491
|
const textarea = panel.querySelector('[data-role="reporter-reply"]');
|
|
@@ -3267,6 +3553,10 @@ var MushiWidget = class _MushiWidget {
|
|
|
3267
3553
|
const submit = this.shadow.querySelector('[data-action="submit"]');
|
|
3268
3554
|
submit?.click();
|
|
3269
3555
|
}
|
|
3556
|
+
recorderOpenMyReports() {
|
|
3557
|
+
if (!this.isOpen) this.open();
|
|
3558
|
+
void this.loadReporterReports();
|
|
3559
|
+
}
|
|
3270
3560
|
};
|
|
3271
3561
|
|
|
3272
3562
|
// src/marketing-recorder.ts
|
|
@@ -3289,7 +3579,8 @@ function exposeMarketingRecorder(widget) {
|
|
|
3289
3579
|
selectCategory: (category) => widget.recorderSelectCategory(category),
|
|
3290
3580
|
selectIntent: (label) => widget.recorderSelectIntent(label),
|
|
3291
3581
|
focusDescription: () => widget.recorderFocusDescription(),
|
|
3292
|
-
submit: () => widget.recorderSubmit()
|
|
3582
|
+
submit: () => widget.recorderSubmit(),
|
|
3583
|
+
openMyReports: () => widget.recorderOpenMyReports()
|
|
3293
3584
|
};
|
|
3294
3585
|
globalThis.__mushiRecorder = api;
|
|
3295
3586
|
}
|
|
@@ -3460,6 +3751,24 @@ async function fetchAndCacheTier(userId) {
|
|
|
3460
3751
|
noteRewardsApiFailure(res.error?.code);
|
|
3461
3752
|
return null;
|
|
3462
3753
|
}
|
|
3754
|
+
async function fetchLeaderboard(limit = 10) {
|
|
3755
|
+
if (!apiClient || isRewardsApiBackedOff()) return null;
|
|
3756
|
+
try {
|
|
3757
|
+
const res = await apiClient.getHallOfFame(limit);
|
|
3758
|
+
if (res.ok && res.data) {
|
|
3759
|
+
return (res.data.data ?? []).map((e) => ({
|
|
3760
|
+
display_name: e.display_name,
|
|
3761
|
+
tier_name: e.tier_name,
|
|
3762
|
+
total_points: e.total_points,
|
|
3763
|
+
points_30d: e.points_30d
|
|
3764
|
+
}));
|
|
3765
|
+
}
|
|
3766
|
+
noteRewardsApiFailure(res.error?.code);
|
|
3767
|
+
return null;
|
|
3768
|
+
} catch {
|
|
3769
|
+
return null;
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3463
3772
|
var routeObserver = null;
|
|
3464
3773
|
var clickHandler = null;
|
|
3465
3774
|
var origPushState = null;
|
|
@@ -5030,7 +5339,7 @@ function createProactiveManager(config = {}) {
|
|
|
5030
5339
|
|
|
5031
5340
|
// src/version.ts
|
|
5032
5341
|
var MUSHI_SDK_PACKAGE = "@mushi-mushi/web";
|
|
5033
|
-
var MUSHI_SDK_VERSION = "1.
|
|
5342
|
+
var MUSHI_SDK_VERSION = "1.11.0" ;
|
|
5034
5343
|
|
|
5035
5344
|
// src/mushi.ts
|
|
5036
5345
|
var instance = null;
|
|
@@ -5308,6 +5617,22 @@ function createInstance(config) {
|
|
|
5308
5617
|
async onReporterReply(reportId, body) {
|
|
5309
5618
|
const result = await apiClient2.replyToReporterReport(reportId, core.getReporterToken(), body);
|
|
5310
5619
|
if (!result.ok) throw new Error(result.error?.message ?? "Could not send reply");
|
|
5620
|
+
},
|
|
5621
|
+
async onReporterFeedback(reportId, signal, note) {
|
|
5622
|
+
const result = await apiClient2.replyToReporterReport(reportId, core.getReporterToken(), note ?? "", signal);
|
|
5623
|
+
if (!result.ok) throw new Error(result.error?.message ?? "Could not send feedback");
|
|
5624
|
+
return result.data?.feedback ?? null;
|
|
5625
|
+
},
|
|
5626
|
+
async onReporterReopen(reportId, note) {
|
|
5627
|
+
const result = await apiClient2.reopenReporterReport(reportId, core.getReporterToken(), note);
|
|
5628
|
+
if (!result.ok) throw new Error(result.error?.message ?? "Could not reopen report");
|
|
5629
|
+
return result.data?.outcome ?? null;
|
|
5630
|
+
},
|
|
5631
|
+
onLeaderboardOpen() {
|
|
5632
|
+
widget.setLeaderboard(null, true);
|
|
5633
|
+
void fetchLeaderboard(10).then((entries) => {
|
|
5634
|
+
widget.setLeaderboard(entries, false);
|
|
5635
|
+
});
|
|
5311
5636
|
}
|
|
5312
5637
|
}, MUSHI_SDK_VERSION);
|
|
5313
5638
|
syncCaptureModules();
|
|
@@ -5564,7 +5889,7 @@ function createInstance(config) {
|
|
|
5564
5889
|
await offlineQueue.enqueue(finalReport);
|
|
5565
5890
|
log.info("Offline \u2014 report queued", { reportId: finalReport.id });
|
|
5566
5891
|
emit("report:queued", { reportId: finalReport.id });
|
|
5567
|
-
return;
|
|
5892
|
+
return { reportId: null, queuedOffline: true };
|
|
5568
5893
|
}
|
|
5569
5894
|
const result = await apiClient2.submitReport(finalReport);
|
|
5570
5895
|
if (result.ok) {
|
|
@@ -5862,6 +6187,41 @@ function createInstance(config) {
|
|
|
5862
6187
|
},
|
|
5863
6188
|
pulseTrigger() {
|
|
5864
6189
|
widget.pulseTrigger?.();
|
|
6190
|
+
},
|
|
6191
|
+
// ─── Reporter API (cross-platform) ────────────────────────────────
|
|
6192
|
+
async listMyReports() {
|
|
6193
|
+
const result = await apiClient2.listReporterReports(core.getReporterToken());
|
|
6194
|
+
if (!result.ok) return [];
|
|
6195
|
+
return result.data?.reports ?? [];
|
|
6196
|
+
},
|
|
6197
|
+
async listMyComments(reportId) {
|
|
6198
|
+
const result = await apiClient2.listReporterComments(reportId, core.getReporterToken());
|
|
6199
|
+
if (!result.ok) return [];
|
|
6200
|
+
return result.data?.comments ?? [];
|
|
6201
|
+
},
|
|
6202
|
+
async replyToReport(reportId, body) {
|
|
6203
|
+
const result = await apiClient2.replyToReporterReport(reportId, core.getReporterToken(), body);
|
|
6204
|
+
if (!result.ok) return null;
|
|
6205
|
+
return result.data?.comment ?? null;
|
|
6206
|
+
},
|
|
6207
|
+
async submitFeedbackSignal(reportId, signal, note) {
|
|
6208
|
+
const result = await apiClient2.replyToReporterReport(reportId, core.getReporterToken(), note ?? "", signal);
|
|
6209
|
+
if (!result.ok) return null;
|
|
6210
|
+
return result.data?.feedback ?? null;
|
|
6211
|
+
},
|
|
6212
|
+
async reopenReport(reportId, note) {
|
|
6213
|
+
const result = await apiClient2.reopenReporterReport(reportId, core.getReporterToken(), note);
|
|
6214
|
+
if (!result.ok) return null;
|
|
6215
|
+
return result.data?.outcome ?? null;
|
|
6216
|
+
},
|
|
6217
|
+
openMyReports() {
|
|
6218
|
+
widget.recorderOpenMyReports();
|
|
6219
|
+
},
|
|
6220
|
+
async getHallOfFame(limit = 20) {
|
|
6221
|
+
const result = await apiClient2.getHallOfFame(limit);
|
|
6222
|
+
if (!result.ok) return [];
|
|
6223
|
+
const raw = result.data;
|
|
6224
|
+
return raw?.data ?? [];
|
|
5865
6225
|
}
|
|
5866
6226
|
};
|
|
5867
6227
|
if (typeof globalThis !== "undefined" && (bootstrapConfig.debug ?? false)) {
|
|
@@ -6179,7 +6539,15 @@ function createNoopInstance() {
|
|
|
6179
6539
|
recordActivity: () => {
|
|
6180
6540
|
},
|
|
6181
6541
|
pulseTrigger: () => {
|
|
6182
|
-
}
|
|
6542
|
+
},
|
|
6543
|
+
listMyReports: async () => [],
|
|
6544
|
+
listMyComments: async () => [],
|
|
6545
|
+
replyToReport: async () => null,
|
|
6546
|
+
submitFeedbackSignal: async () => null,
|
|
6547
|
+
reopenReport: async () => null,
|
|
6548
|
+
openMyReports: () => {
|
|
6549
|
+
},
|
|
6550
|
+
getHallOfFame: async () => []
|
|
6183
6551
|
};
|
|
6184
6552
|
}
|
|
6185
6553
|
function installAutoBreadcrumbs(buffer) {
|