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