@mushi-mushi/web 1.7.2 → 1.7.6

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 CHANGED
@@ -265,9 +265,9 @@ function getWidgetStyles(theme) {
265
265
  const inkFaint = isDark ? "#5A5650" : "#9A9489";
266
266
  const rule = isDark ? "rgba(242,235,221,0.10)" : "rgba(14,13,11,0.10)";
267
267
  const ruleStrong = isDark ? "rgba(242,235,221,0.18)" : "rgba(14,13,11,0.16)";
268
- const vermillion = isDark ? "#FF5A47" : "#E03C2C";
269
- const vermillionWash = isDark ? "rgba(255,90,71,0.12)" : "rgba(224,60,44,0.08)";
270
- const vermillionInk = isDark ? "#FFE5E0" : "#7A1F15";
268
+ const widgetAccent = isDark ? "#FF5A47" : "#E03C2C";
269
+ const widgetAccentWash = isDark ? "rgba(255,90,71,0.12)" : "rgba(224,60,44,0.08)";
270
+ const widgetAccentInk = isDark ? "#FFE5E0" : "#7A1F15";
271
271
  const fontDisplay = `'Iowan Old Style', 'Palatino Linotype', 'Palatino', 'Book Antiqua', 'Cambria', Georgia, 'Times New Roman', serif`;
272
272
  const fontBody = `system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI Variable Display', 'Segoe UI', sans-serif`;
273
273
  const fontMono = `ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, 'Liberation Mono', monospace`;
@@ -283,6 +283,17 @@ function getWidgetStyles(theme) {
283
283
  -moz-osx-font-smoothing: grayscale;
284
284
  font-feature-settings: 'ss01', 'cv11'; /* nicer system-ui glyphs where supported */
285
285
  --mushi-ok: ${isDark ? "#4ade80" : "#16a34a"};
286
+ /* SDK contract: the host element is always pass-through. Only the
287
+ interactive surfaces (.mushi-trigger, .mushi-banner, .mushi-panel)
288
+ opt back into pointer events so the widget never creates an
289
+ invisible touch blocker over host-app UI. */
290
+ pointer-events: none;
291
+ }
292
+ /* Only actual widget controls receive touch/mouse events. */
293
+ .mushi-trigger,
294
+ .mushi-banner,
295
+ .mushi-panel {
296
+ pointer-events: auto;
286
297
  }
287
298
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
288
299
  button { font-family: inherit; }
@@ -305,7 +316,7 @@ function getWidgetStyles(theme) {
305
316
  box-shadow:
306
317
  0 1px 0 ${rule},
307
318
  0 6px 14px -8px rgba(14,13,11,0.35),
308
- inset 0 -3px 0 ${vermillion};
319
+ inset 0 -3px 0 ${widgetAccent};
309
320
  transition: transform 200ms ${easeStamp}, box-shadow 200ms ${easeStamp};
310
321
  overflow: visible;
311
322
  isolation: isolate;
@@ -318,8 +329,8 @@ function getWidgetStyles(theme) {
318
329
  width: 6px;
319
330
  height: 6px;
320
331
  border-radius: 50%;
321
- background: ${vermillion};
322
- box-shadow: 0 0 0 0 ${vermillion};
332
+ background: ${widgetAccent};
333
+ box-shadow: 0 0 0 0 ${widgetAccent};
323
334
  animation: mushi-pulse 2.4s ${easeStamp} infinite;
324
335
  }
325
336
  .mushi-trigger:hover {
@@ -327,17 +338,17 @@ function getWidgetStyles(theme) {
327
338
  box-shadow:
328
339
  0 1px 0 ${rule},
329
340
  0 14px 24px -10px rgba(14,13,11,0.45),
330
- inset 0 -3px 0 ${vermillion};
341
+ inset 0 -3px 0 ${widgetAccent};
331
342
  }
332
343
  .mushi-trigger:active {
333
344
  transform: translateY(0) rotate(0);
334
345
  box-shadow:
335
346
  0 1px 0 ${rule},
336
347
  0 2px 4px -2px rgba(14,13,11,0.35),
337
- inset 0 -2px 0 ${vermillion};
348
+ inset 0 -2px 0 ${widgetAccent};
338
349
  }
339
350
  .mushi-trigger:focus-visible {
340
- outline: 2px solid ${vermillion};
351
+ outline: 2px solid ${widgetAccent};
341
352
  outline-offset: 3px;
342
353
  }
343
354
  /* First-session welcome pulse. Three soft halos at 800ms each, then
@@ -381,7 +392,7 @@ function getWidgetStyles(theme) {
381
392
  box-shadow:
382
393
  0 1px 0 ${rule},
383
394
  0 10px 24px -14px rgba(14,13,11,0.45),
384
- inset -3px 0 0 ${vermillion};
395
+ inset -3px 0 0 ${widgetAccent};
385
396
  }
386
397
  .mushi-trigger.edge-tab.bottom-right,
387
398
  .mushi-trigger.edge-tab.top-right {
@@ -394,7 +405,7 @@ function getWidgetStyles(theme) {
394
405
  box-shadow:
395
406
  0 1px 0 ${rule},
396
407
  0 10px 24px -14px rgba(14,13,11,0.45),
397
- inset 3px 0 0 ${vermillion};
408
+ inset 3px 0 0 ${widgetAccent};
398
409
  }
399
410
  .mushi-trigger.shrunk {
400
411
  width: 36px;
@@ -404,7 +415,7 @@ function getWidgetStyles(theme) {
404
415
  }
405
416
 
406
417
  @keyframes mushi-pulse {
407
- 0% { box-shadow: 0 0 0 0 ${vermillion}; opacity: 1; }
418
+ 0% { box-shadow: 0 0 0 0 ${widgetAccent}; opacity: 1; }
408
419
  70% { box-shadow: 0 0 0 8px rgba(224,60,44,0); opacity: 0.5; }
409
420
  100% { box-shadow: 0 0 0 0 rgba(224,60,44,0); opacity: 1; }
410
421
  }
@@ -451,9 +462,9 @@ function getWidgetStyles(theme) {
451
462
  .mushi-outdated {
452
463
  margin: 12px 14px 0;
453
464
  padding: 10px 12px;
454
- border: 1px solid ${vermillionWash};
455
- background: ${vermillionWash};
456
- color: ${vermillionInk};
465
+ border: 1px solid ${widgetAccentWash};
466
+ background: ${widgetAccentWash};
467
+ color: ${widgetAccentInk};
457
468
  font-family: ${fontBody};
458
469
  font-size: 12px;
459
470
  line-height: 1.4;
@@ -493,7 +504,7 @@ function getWidgetStyles(theme) {
493
504
  width: 22px;
494
505
  height: 22px;
495
506
  border-radius: 3px;
496
- background: ${vermillion};
507
+ background: ${widgetAccent};
497
508
  color: #FAF7F0;
498
509
  font-family: ${fontDisplay};
499
510
  font-size: 14px;
@@ -554,9 +565,9 @@ function getWidgetStyles(theme) {
554
565
  border-radius: 3px;
555
566
  transition: color 150ms ${easeStamp};
556
567
  }
557
- .mushi-close:hover, .mushi-back:hover { color: ${vermillion}; }
568
+ .mushi-close:hover, .mushi-back:hover { color: ${widgetAccent}; }
558
569
  .mushi-close:focus-visible, .mushi-back:focus-visible {
559
- outline: 1.5px solid ${vermillion};
570
+ outline: 1.5px solid ${widgetAccent};
560
571
  outline-offset: 2px;
561
572
  }
562
573
 
@@ -591,12 +602,12 @@ function getWidgetStyles(theme) {
591
602
  position: relative;
592
603
  }
593
604
  .mushi-option-btn:last-child { border-bottom: none; }
594
- .mushi-option-btn:hover { padding-left: 6px; color: ${vermillion}; }
595
- .mushi-option-btn:hover .mushi-option-arrow { opacity: 1; transform: translateX(0); color: ${vermillion}; }
605
+ .mushi-option-btn:hover { padding-left: 6px; color: ${widgetAccent}; }
606
+ .mushi-option-btn:hover .mushi-option-arrow { opacity: 1; transform: translateX(0); color: ${widgetAccent}; }
596
607
  .mushi-option-btn:focus-visible {
597
608
  outline: none;
598
609
  padding-left: 6px;
599
- box-shadow: inset 2px 0 0 ${vermillion};
610
+ box-shadow: inset 2px 0 0 ${widgetAccent};
600
611
  }
601
612
  .mushi-option-icon {
602
613
  font-size: 18px;
@@ -638,7 +649,7 @@ function getWidgetStyles(theme) {
638
649
  }
639
650
  .mushi-feature-entry:hover,
640
651
  .mushi-reports-entry:hover {
641
- border-left-color: ${vermillion};
652
+ border-left-color: ${widgetAccent};
642
653
  padding-left: 14px;
643
654
  }
644
655
  .mushi-feature-entry .mushi-option-icon {
@@ -661,7 +672,7 @@ function getWidgetStyles(theme) {
661
672
  .mushi-report-status {
662
673
  font-family: ${fontMono};
663
674
  font-size: 10px;
664
- color: ${vermillion};
675
+ color: ${widgetAccent};
665
676
  text-transform: uppercase;
666
677
  }
667
678
  .mushi-report-title {
@@ -678,7 +689,7 @@ function getWidgetStyles(theme) {
678
689
  .mushi-thread-summary span {
679
690
  font-family: ${fontMono};
680
691
  font-size: 10px;
681
- color: ${vermillion};
692
+ color: ${widgetAccent};
682
693
  text-transform: uppercase;
683
694
  }
684
695
  .mushi-thread {
@@ -694,8 +705,8 @@ function getWidgetStyles(theme) {
694
705
  background: ${isDark ? "rgba(242,235,221,0.04)" : "rgba(14,13,11,0.03)"};
695
706
  }
696
707
  .mushi-thread-comment.reporter {
697
- border-color: ${vermillionWash};
698
- background: ${vermillionWash};
708
+ border-color: ${widgetAccentWash};
709
+ background: ${widgetAccentWash};
699
710
  }
700
711
  .mushi-thread-comment strong {
701
712
  display: block;
@@ -712,16 +723,16 @@ function getWidgetStyles(theme) {
712
723
  color: ${inkMuted};
713
724
  line-height: 1.45;
714
725
  }
715
- .mushi-error-inline { color: ${vermillion}; }
726
+ .mushi-error-inline { color: ${widgetAccent}; }
716
727
 
717
728
  .mushi-selected-category {
718
729
  display: inline-flex;
719
730
  align-items: center;
720
731
  gap: 8px;
721
732
  padding: 6px 10px 6px 12px;
722
- border-left: 2px solid ${vermillion};
723
- background: ${vermillionWash};
724
- color: ${vermillionInk};
733
+ border-left: 2px solid ${widgetAccent};
734
+ background: ${widgetAccentWash};
735
+ color: ${widgetAccentInk};
725
736
  font-family: ${fontMono};
726
737
  font-size: 11px;
727
738
  letter-spacing: 0.12em;
@@ -761,12 +772,12 @@ function getWidgetStyles(theme) {
761
772
  transition: opacity 220ms ${easeStamp}, transform 220ms ${easeStamp};
762
773
  }
763
774
  .mushi-intent-btn:last-child { border-bottom: none; }
764
- .mushi-intent-btn:hover { padding-left: 6px; color: ${vermillion}; }
765
- .mushi-intent-btn:hover::after { opacity: 1; transform: translateX(0); color: ${vermillion}; }
775
+ .mushi-intent-btn:hover { padding-left: 6px; color: ${widgetAccent}; }
776
+ .mushi-intent-btn:hover::after { opacity: 1; transform: translateX(0); color: ${widgetAccent}; }
766
777
  .mushi-intent-btn:focus-visible {
767
778
  outline: none;
768
779
  padding-left: 6px;
769
- box-shadow: inset 2px 0 0 ${vermillion};
780
+ box-shadow: inset 2px 0 0 ${widgetAccent};
770
781
  }
771
782
 
772
783
  /* Example starter chips \u2014 reduce first-report activation energy */
@@ -794,7 +805,7 @@ function getWidgetStyles(theme) {
794
805
  background: ${isDark ? "rgba(242,235,221,0.06)" : "rgba(14,13,11,0.04)"};
795
806
  }
796
807
  .mushi-example-chip:focus-visible {
797
- outline: 2px solid ${vermillion};
808
+ outline: 2px solid ${widgetAccent};
798
809
  outline-offset: 2px;
799
810
  }
800
811
 
@@ -833,7 +844,7 @@ function getWidgetStyles(theme) {
833
844
  color: ${inkFaint};
834
845
  font-style: italic;
835
846
  }
836
- .mushi-textarea:focus { border-bottom-color: ${vermillion}; }
847
+ .mushi-textarea:focus { border-bottom-color: ${widgetAccent}; }
837
848
 
838
849
  .mushi-attachments {
839
850
  display: flex;
@@ -862,30 +873,30 @@ function getWidgetStyles(theme) {
862
873
  border-color: ${ink};
863
874
  }
864
875
  .mushi-attach-btn.active {
865
- color: ${vermillion};
866
- border-color: ${vermillion};
867
- background: ${vermillionWash};
876
+ color: ${widgetAccent};
877
+ border-color: ${widgetAccent};
878
+ background: ${widgetAccentWash};
868
879
  }
869
880
  .mushi-attach-btn.danger {
870
- color: ${vermillionInk};
871
- border-color: ${vermillionWash};
881
+ color: ${widgetAccentInk};
882
+ border-color: ${widgetAccentWash};
872
883
  background: transparent;
873
884
  }
874
885
  .mushi-attach-btn.danger:hover {
875
- color: ${vermillion};
876
- border-color: ${vermillion};
877
- background: ${vermillionWash};
886
+ color: ${widgetAccent};
887
+ border-color: ${widgetAccent};
888
+ background: ${widgetAccentWash};
878
889
  }
879
890
  .mushi-attach-btn.loading {
880
891
  opacity: 0.7;
881
892
  cursor: wait;
882
893
  }
883
894
  .mushi-attach-btn.error {
884
- color: ${vermillion};
885
- border-color: ${vermillionWash};
895
+ color: ${widgetAccent};
896
+ border-color: ${widgetAccentWash};
886
897
  }
887
898
  .mushi-attach-btn:focus-visible {
888
- outline: 2px solid ${vermillion};
899
+ outline: 2px solid ${widgetAccent};
889
900
  outline-offset: 2px;
890
901
  }
891
902
  @keyframes mushi-spin {
@@ -926,9 +937,9 @@ function getWidgetStyles(theme) {
926
937
  align-items: center;
927
938
  gap: 8px;
928
939
  padding: 10px 18px;
929
- border: 1px solid ${vermillion};
940
+ border: 1px solid ${widgetAccent};
930
941
  border-radius: 3px;
931
- background: ${vermillion};
942
+ background: ${widgetAccent};
932
943
  color: #FAF7F0;
933
944
  font-family: ${fontMono};
934
945
  font-size: 11px;
@@ -960,7 +971,7 @@ function getWidgetStyles(theme) {
960
971
  opacity: 0.7;
961
972
  }
962
973
  .mushi-submit:focus-visible {
963
- outline: 2px solid ${vermillion};
974
+ outline: 2px solid ${widgetAccent};
964
975
  outline-offset: 3px;
965
976
  }
966
977
  .mushi-submit-arrow {
@@ -999,7 +1010,7 @@ function getWidgetStyles(theme) {
999
1010
  }
1000
1011
  .mushi-step-num.done { color: ${inkMuted}; text-decoration: line-through; text-decoration-color: ${inkFaint}; }
1001
1012
  .mushi-step-num.active {
1002
- color: ${vermillion};
1013
+ color: ${widgetAccent};
1003
1014
  font-family: ${fontDisplay};
1004
1015
  font-size: 14px;
1005
1016
  font-weight: 600;
@@ -1028,7 +1039,7 @@ function getWidgetStyles(theme) {
1028
1039
  }
1029
1040
  .mushi-success-stamp circle {
1030
1041
  fill: none;
1031
- stroke: ${vermillion};
1042
+ stroke: ${widgetAccent};
1032
1043
  stroke-width: 3;
1033
1044
  stroke-dasharray: 280;
1034
1045
  stroke-dashoffset: 280;
@@ -1040,7 +1051,7 @@ function getWidgetStyles(theme) {
1040
1051
  font-family: ${fontDisplay};
1041
1052
  font-size: 18px;
1042
1053
  font-weight: 600;
1043
- color: ${vermillion};
1054
+ color: ${widgetAccent};
1044
1055
  letter-spacing: 0.04em;
1045
1056
  transform: rotate(-6deg);
1046
1057
  opacity: 0;
@@ -1108,8 +1119,8 @@ function getWidgetStyles(theme) {
1108
1119
  .mushi-success-receipt-id:hover,
1109
1120
  .mushi-success-receipt-id:focus-visible {
1110
1121
  background: rgba(217, 65, 47, 0.06);
1111
- border-color: ${vermillion};
1112
- color: ${vermillion};
1122
+ border-color: ${widgetAccent};
1123
+ color: ${widgetAccent};
1113
1124
  outline: none;
1114
1125
  }
1115
1126
  .mushi-success-receipt-copy {
@@ -1123,7 +1134,7 @@ function getWidgetStyles(theme) {
1123
1134
  gap: 4px;
1124
1135
  padding: 6px 10px;
1125
1136
  border-radius: 4px;
1126
- background: ${vermillion};
1137
+ background: ${widgetAccent};
1127
1138
  color: #fff;
1128
1139
  font-family: ${fontMono};
1129
1140
  font-size: 11px;
@@ -1142,7 +1153,7 @@ function getWidgetStyles(theme) {
1142
1153
  height: 11px;
1143
1154
  border-radius: 50%;
1144
1155
  border: 1.5px solid ${rule};
1145
- border-top-color: ${vermillion};
1156
+ border-top-color: ${widgetAccent};
1146
1157
  animation: mushi-receipt-spin 0.8s linear infinite;
1147
1158
  }
1148
1159
  @keyframes mushi-receipt-spin {
@@ -1153,7 +1164,7 @@ function getWidgetStyles(theme) {
1153
1164
  font-style: italic;
1154
1165
  }
1155
1166
  .mushi-success-receipt-warn {
1156
- color: ${vermillion};
1167
+ color: ${widgetAccent};
1157
1168
  }
1158
1169
  .mushi-success-sla {
1159
1170
  margin-top: 2px;
@@ -1180,8 +1191,8 @@ function getWidgetStyles(theme) {
1180
1191
  .mushi-error {
1181
1192
  margin-top: 10px;
1182
1193
  padding: 8px 0 8px 10px;
1183
- border-left: 2px solid ${vermillion};
1184
- color: ${vermillion};
1194
+ border-left: 2px solid ${widgetAccent};
1195
+ color: ${widgetAccent};
1185
1196
  font-size: 12px;
1186
1197
  font-family: ${fontMono};
1187
1198
  letter-spacing: 0.02em;
@@ -1221,7 +1232,7 @@ function getWidgetStyles(theme) {
1221
1232
  .mushi-rewards-pts-earn {
1222
1233
  font-family: ${fontMono};
1223
1234
  font-size: 10px;
1224
- color: ${vermillion};
1235
+ color: ${widgetAccent};
1225
1236
  letter-spacing: 0.04em;
1226
1237
  white-space: nowrap;
1227
1238
  }
@@ -1234,7 +1245,7 @@ function getWidgetStyles(theme) {
1234
1245
  }
1235
1246
  .mushi-tier-bar-fill {
1236
1247
  height: 100%;
1237
- background: ${vermillion};
1248
+ background: ${widgetAccent};
1238
1249
  border-radius: 2px;
1239
1250
  transition: width 600ms ${easeStamp};
1240
1251
  }
@@ -1257,7 +1268,7 @@ function getWidgetStyles(theme) {
1257
1268
  font-family: ${fontMono};
1258
1269
  font-size: 22px;
1259
1270
  font-weight: 700;
1260
- color: ${vermillion};
1271
+ color: ${widgetAccent};
1261
1272
  text-align: center;
1262
1273
  letter-spacing: 0.06em;
1263
1274
  margin-bottom: 10px;
@@ -1272,13 +1283,17 @@ function getWidgetStyles(theme) {
1272
1283
  }
1273
1284
 
1274
1285
  /* \u2500\u2500\u2500 Beta mode strip (category step) \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 */
1286
+ /* Brand palette: widgetAccent (vermillion) + washi ink for the strip.
1287
+ The previous indigo (#6366f1) is the single most recognisable AI-template
1288
+ colour; replaced with the widget's own vermillion wash so the beta panel
1289
+ reads as a Mushi-native surface rather than a generic SaaS plug-in. */
1275
1290
 
1276
1291
  .mushi-beta-strip {
1277
1292
  margin: 0 16px 2px;
1278
1293
  padding: 9px 12px;
1279
- background: rgba(99, 102, 241, 0.07);
1280
- border: 1px solid rgba(99, 102, 241, 0.18);
1281
- border-radius: 8px;
1294
+ background: ${widgetAccentWash};
1295
+ border: 1px solid ${isDark ? "rgba(255,90,71,0.22)" : "rgba(224,60,44,0.16)"};
1296
+ border-radius: 4px;
1282
1297
  display: flex;
1283
1298
  flex-direction: column;
1284
1299
  gap: 4px;
@@ -1294,38 +1309,30 @@ function getWidgetStyles(theme) {
1294
1309
  display: inline-flex;
1295
1310
  align-items: center;
1296
1311
  padding: 1px 6px;
1297
- border-radius: 4px;
1298
- background: rgba(245, 158, 11, 0.15);
1299
- border: 1px solid rgba(245, 158, 11, 0.35);
1300
- color: #b45309;
1301
- font-family: var(--mushi-font-mono);
1312
+ border-radius: 3px;
1313
+ background: ${widgetAccent};
1314
+ color: #FAF7F0;
1315
+ font-family: ${fontMono};
1302
1316
  font-size: 9px;
1303
1317
  font-weight: 700;
1304
- letter-spacing: 0.08em;
1318
+ letter-spacing: 0.14em;
1305
1319
  line-height: 1.6;
1306
1320
  white-space: nowrap;
1307
1321
  flex-shrink: 0;
1308
- }
1309
-
1310
- @media (prefers-color-scheme: dark) {
1311
- .mushi-beta-tag {
1312
- background: rgba(245, 158, 11, 0.12);
1313
- border-color: rgba(245, 158, 11, 0.28);
1314
- color: #fbbf24;
1315
- }
1322
+ text-transform: uppercase;
1316
1323
  }
1317
1324
 
1318
1325
  .mushi-beta-msg {
1319
1326
  font-size: 11px;
1320
- color: var(--mushi-text-dim);
1327
+ color: ${inkMuted};
1321
1328
  line-height: 1.45;
1322
1329
  }
1323
1330
 
1324
1331
  .mushi-beta-contact-hint {
1325
1332
  font-size: 10px;
1326
- color: var(--mushi-text-dim);
1327
- opacity: 0.72;
1328
- font-family: var(--mushi-font-mono);
1333
+ color: ${inkFaint};
1334
+ font-family: ${fontMono};
1335
+ letter-spacing: 0.06em;
1329
1336
  }
1330
1337
 
1331
1338
  .mushi-beta-perks {
@@ -1339,16 +1346,10 @@ function getWidgetStyles(theme) {
1339
1346
 
1340
1347
  .mushi-beta-perks li {
1341
1348
  font-size: 10.5px;
1342
- color: #4f46e5;
1349
+ color: ${widgetAccentInk};
1343
1350
  font-weight: 500;
1344
1351
  }
1345
1352
 
1346
- @media (prefers-color-scheme: dark) {
1347
- .mushi-beta-perks li {
1348
- color: #818cf8;
1349
- }
1350
- }
1351
-
1352
1353
  /* \u2500\u2500\u2500 Beta changelog (collapsible What's new) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1353
1354
 
1354
1355
  .mushi-changelog {
@@ -1397,9 +1398,9 @@ function getWidgetStyles(theme) {
1397
1398
  .mushi-beta-success-footer {
1398
1399
  margin-top: 14px;
1399
1400
  padding: 10px 14px;
1400
- background: rgba(99, 102, 241, 0.06);
1401
- border: 1px solid rgba(99, 102, 241, 0.14);
1402
- border-radius: 8px;
1401
+ background: ${widgetAccentWash};
1402
+ border: 1px solid ${isDark ? "rgba(255,90,71,0.18)" : "rgba(224,60,44,0.14)"};
1403
+ border-radius: 4px;
1403
1404
  display: flex;
1404
1405
  flex-direction: column;
1405
1406
  gap: 3px;
@@ -1460,9 +1461,9 @@ function getWidgetStyles(theme) {
1460
1461
  background: rgba(0,0,0,0.22);
1461
1462
  }
1462
1463
 
1463
- /* --- brand variant (vermillion \u2014 editorial, app-quality) --- */
1464
+ /* --- brand variant (widgetAccent \u2014 editorial, app-quality) --- */
1464
1465
  .mushi-banner.brand {
1465
- background: ${vermillion};
1466
+ background: ${widgetAccent};
1466
1467
  color: #fff;
1467
1468
  border-bottom: 1.5px solid ${isDark ? "#C4321E" : "#B52F1F"};
1468
1469
  }
@@ -1479,23 +1480,29 @@ function getWidgetStyles(theme) {
1479
1480
  background: rgba(255,255,255,0.28);
1480
1481
  }
1481
1482
 
1482
- /* --- subtle variant (hairline, muted \u2014 least disruptive) --- */
1483
+ /* --- subtle variant (frosted-glass, muted \u2014 least disruptive) ---
1484
+ Uses the widget's own paper colour at high opacity + backdrop-blur so
1485
+ it blends with the host app while remaining legible. The previous 4-6%
1486
+ opacity values were effectively invisible \u2014 users could not distinguish
1487
+ the banner from the page content below it. */
1483
1488
  .mushi-banner.subtle {
1484
- background: ${isDark ? "rgba(242,235,221,0.06)" : "rgba(14,13,11,0.04)"};
1485
- color: ${inkMuted};
1486
- border-bottom: 1px solid ${rule};
1489
+ background: ${isDark ? "rgba(15,14,12,0.88)" : "rgba(248,244,237,0.92)"};
1490
+ backdrop-filter: blur(14px);
1491
+ -webkit-backdrop-filter: blur(14px);
1492
+ color: ${ink};
1493
+ border-bottom: 1px solid ${ruleStrong};
1487
1494
  }
1488
1495
  .mushi-banner.subtle.bottom {
1489
- border-top: 1px solid ${rule};
1496
+ border-top: 1px solid ${ruleStrong};
1490
1497
  border-bottom: none;
1491
1498
  }
1492
1499
  .mushi-banner.subtle .mushi-banner-btn {
1493
- background: ${isDark ? "rgba(242,235,221,0.10)" : "rgba(14,13,11,0.07)"};
1500
+ background: ${isDark ? "rgba(242,235,221,0.10)" : "rgba(14,13,11,0.08)"};
1494
1501
  color: ${ink};
1495
- border: 1px solid ${rule};
1502
+ border: 1px solid ${ruleStrong};
1496
1503
  }
1497
1504
  .mushi-banner.subtle .mushi-banner-btn:hover {
1498
- background: ${isDark ? "rgba(242,235,221,0.16)" : "rgba(14,13,11,0.12)"};
1505
+ background: ${isDark ? "rgba(242,235,221,0.18)" : "rgba(14,13,11,0.14)"};
1499
1506
  }
1500
1507
 
1501
1508
  .mushi-banner-label {
@@ -1520,7 +1527,7 @@ function getWidgetStyles(theme) {
1520
1527
  line-height: 1;
1521
1528
  }
1522
1529
  .mushi-banner-btn:focus-visible {
1523
- outline: 2px solid ${vermillion};
1530
+ outline: 2px solid ${widgetAccent};
1524
1531
  outline-offset: 2px;
1525
1532
  }
1526
1533
 
@@ -1631,7 +1638,8 @@ var MushiWidget = class _MushiWidget {
1631
1638
  responseSlaLabel: config.responseSlaLabel ?? "",
1632
1639
  featureRequestCard: config.featureRequestCard ?? true,
1633
1640
  featureRequestLabel: config.featureRequestLabel ?? "",
1634
- featureRequestDescription: config.featureRequestDescription ?? ""
1641
+ featureRequestDescription: config.featureRequestDescription ?? "",
1642
+ avoidSelectors: config.avoidSelectors ?? []
1635
1643
  };
1636
1644
  this.callbacks = callbacks;
1637
1645
  this.locale = getLocale(this.config.locale === "auto" ? void 0 : this.config.locale);
@@ -1708,6 +1716,7 @@ var MushiWidget = class _MushiWidget {
1708
1716
  bannerDismissed = false;
1709
1717
  mount() {
1710
1718
  if (this.host.isConnected) return;
1719
+ this.syncHostChromeState();
1711
1720
  document.body.appendChild(this.host);
1712
1721
  this.syncAttachedLaunchers();
1713
1722
  this.syncSmartHide();
@@ -1748,6 +1757,7 @@ var MushiWidget = class _MushiWidget {
1748
1757
  ...config.featureRequestDescription !== void 0 ? { featureRequestDescription: config.featureRequestDescription } : {}
1749
1758
  };
1750
1759
  this.locale = getLocale(this.config.locale === "auto" ? void 0 : this.config.locale);
1760
+ if (this.host.isConnected) this.syncHostChromeState();
1751
1761
  this.syncAttachedLaunchers();
1752
1762
  this.syncSmartHide();
1753
1763
  this.render();
@@ -1980,6 +1990,59 @@ var MushiWidget = class _MushiWidget {
1980
1990
  this.removeBodyNudge();
1981
1991
  this.host.remove();
1982
1992
  }
1993
+ /* ── Host chrome contract ────────────────────────────────────────────────
1994
+ The host element must never create an invisible full-screen touch blocker.
1995
+ We own these inline styles — consumer CSS can only win with `!important`,
1996
+ which is explicitly banned by the SDK contract. Calling this at mount()
1997
+ and after every zIndex update is the only safe invariant. */
1998
+ /**
1999
+ * Apply the SDK-owned pass-through layout to the host element so it is
2000
+ * always zero-sized and click/touch-transparent. Only the shadow-root
2001
+ * internals (`.mushi-trigger`, `.mushi-banner`, `.mushi-panel`) opt back
2002
+ * into pointer events. This is idempotent and safe to call repeatedly.
2003
+ */
2004
+ syncHostChromeState() {
2005
+ const s = this.host.style;
2006
+ s.setProperty("position", "fixed");
2007
+ s.setProperty("top", "0");
2008
+ s.setProperty("left", "0");
2009
+ s.setProperty("width", "0");
2010
+ s.setProperty("height", "0");
2011
+ s.setProperty("overflow", "visible");
2012
+ s.setProperty("pointer-events", "none");
2013
+ s.setProperty("z-index", String(this.config.zIndex));
2014
+ s.setProperty("margin", "0");
2015
+ s.setProperty("padding", "0");
2016
+ s.setProperty("border", "none");
2017
+ s.setProperty("background", "none");
2018
+ }
2019
+ /**
2020
+ * Returns true when a DOM element matching `hideOnSelector` is currently
2021
+ * present in the host document. Used by both the trigger and the banner
2022
+ * so a single selector consistently hides ALL SDK-injected launcher
2023
+ * surfaces. Invalid selectors are swallowed silently (non-fatal).
2024
+ */
2025
+ isSuppressedByHost() {
2026
+ if (!this.config.hideOnSelector || typeof document === "undefined") return false;
2027
+ try {
2028
+ return Boolean(document.querySelector(this.config.hideOnSelector));
2029
+ } catch {
2030
+ return false;
2031
+ }
2032
+ }
2033
+ /**
2034
+ * Returns a snapshot of the widget's host-layer health for use in
2035
+ * `Mushi.diagnose()`. Callers check this to know whether the widget
2036
+ * could ever block host-app UI without opening a browser devtools.
2037
+ */
2038
+ getWidgetDiagnostics() {
2039
+ const s = this.host.style;
2040
+ const widgetHostPointerSafe = s.pointerEvents === "none" && (s.width === "0" || s.width === "0px") && (s.height === "0" || s.height === "0px");
2041
+ const widgetHostBounds = this.host.isConnected ? { width: this.host.offsetWidth, height: this.host.offsetHeight } : null;
2042
+ const widgetSuppressed = this.isSuppressedByHost() || this.isRouteHidden() || !this.triggerVisible;
2043
+ const bannerRendered = this.config.trigger === "banner" && !this.bannerDismissed && !this.isSuppressedByHost() && !this.isRouteHidden() && this.triggerVisible;
2044
+ return { widgetHostPointerSafe, widgetHostBounds, widgetSuppressed, bannerRendered };
2045
+ }
1983
2046
  syncAttachedLaunchers() {
1984
2047
  this.attachedLaunchers.forEach((cleanup) => cleanup());
1985
2048
  this.attachedLaunchers = [];
@@ -2020,7 +2083,7 @@ var MushiWidget = class _MushiWidget {
2020
2083
  }
2021
2084
  if (this.isMobileSmartHidden()) return false;
2022
2085
  if (this.isRouteHidden()) return false;
2023
- if (this.config.hideOnSelector && document.querySelector(this.config.hideOnSelector)) return false;
2086
+ if (this.isSuppressedByHost()) return false;
2024
2087
  const action = this.config.environments[this.detectEnvironment()];
2025
2088
  return action !== "never" && action !== "manual";
2026
2089
  }
@@ -2069,6 +2132,10 @@ var MushiWidget = class _MushiWidget {
2069
2132
  this.removeBodyNudge();
2070
2133
  return;
2071
2134
  }
2135
+ if (this.isSuppressedByHost()) {
2136
+ this.removeBodyNudge();
2137
+ return;
2138
+ }
2072
2139
  const bc = this.config.bannerConfig ?? {};
2073
2140
  const variant = bc.variant ?? "brand";
2074
2141
  const position = bc.position ?? "top";
@@ -2187,6 +2254,32 @@ var MushiWidget = class _MushiWidget {
2187
2254
  this.trapFocus(panel);
2188
2255
  }
2189
2256
  }
2257
+ /**
2258
+ * Queries each `avoidSelectors` element in the host document and returns
2259
+ * the minimum top-offset in px so that a top-anchored element clears all
2260
+ * of them by `gap` pixels. Returns `null` when no selectors are provided
2261
+ * or no matching elements have a non-zero bounding rect.
2262
+ *
2263
+ * Runs in the host document (not shadow DOM) so it can reach fixed headers,
2264
+ * sticky nav bars, and sign-in CTAs.
2265
+ */
2266
+ computeAvoidTopPx(gap = 8) {
2267
+ const sels = this.config.avoidSelectors;
2268
+ if (!sels?.length) return null;
2269
+ let maxBottom = 0;
2270
+ for (const sel of sels) {
2271
+ try {
2272
+ const el = document.querySelector(sel);
2273
+ if (!el) continue;
2274
+ const r = el.getBoundingClientRect();
2275
+ if (r.bottom > maxBottom && r.width > 0 && r.height > 0) {
2276
+ maxBottom = r.bottom;
2277
+ }
2278
+ } catch {
2279
+ }
2280
+ }
2281
+ return maxBottom > 0 ? Math.ceil(maxBottom) + gap : null;
2282
+ }
2190
2283
  applyInsetVars(el) {
2191
2284
  const { anchor } = this.config;
2192
2285
  if (anchor && Object.keys(anchor).length > 0) {
@@ -2195,20 +2288,27 @@ var MushiWidget = class _MushiWidget {
2195
2288
  if (value !== void 0) el.style.setProperty(`--mushi-${edge}`, value);
2196
2289
  });
2197
2290
  el.style.setProperty("--mushi-safe-area", this.config.respectSafeArea ? "1" : "0");
2198
- return;
2199
- }
2200
- const { inset } = this.config;
2201
- if (!this.config.respectSafeArea) {
2291
+ } else {
2292
+ const { inset } = this.config;
2293
+ if (!this.config.respectSafeArea) {
2294
+ ["top", "right", "bottom", "left"].forEach((edge) => {
2295
+ if (inset[edge] === void 0) el.style.setProperty(`--mushi-${edge}`, "24px");
2296
+ });
2297
+ }
2202
2298
  ["top", "right", "bottom", "left"].forEach((edge) => {
2203
- if (inset[edge] === void 0) el.style.setProperty(`--mushi-${edge}`, "24px");
2299
+ const value = inset[edge];
2300
+ if (value === void 0) return;
2301
+ el.style.setProperty(`--mushi-${edge}`, value === "auto" ? "auto" : `${value}px`);
2204
2302
  });
2303
+ el.style.setProperty("--mushi-safe-area", this.config.respectSafeArea ? "1" : "0");
2304
+ }
2305
+ const isTopAnchored = anchor?.top !== void 0 || this.config.position?.startsWith("top") || !this.config.position && !anchor?.bottom;
2306
+ if (isTopAnchored) {
2307
+ const avoidPx = this.computeAvoidTopPx();
2308
+ if (avoidPx !== null) {
2309
+ el.style.setProperty("--mushi-top", `${avoidPx}px`);
2310
+ }
2205
2311
  }
2206
- ["top", "right", "bottom", "left"].forEach((edge) => {
2207
- const value = inset[edge];
2208
- if (value === void 0) return;
2209
- el.style.setProperty(`--mushi-${edge}`, value === "auto" ? "auto" : `${value}px`);
2210
- });
2211
- el.style.setProperty("--mushi-safe-area", this.config.respectSafeArea ? "1" : "0");
2212
2312
  }
2213
2313
  renderStep() {
2214
2314
  switch (this.step) {
@@ -3042,6 +3142,15 @@ var optedIn = false;
3042
3142
  var tierCache = null;
3043
3143
  var tierCacheTime = 0;
3044
3144
  var TIER_CACHE_TTL = 5 * 60 * 1e3;
3145
+ var rewardsApiBackoffUntil = 0;
3146
+ var REWARDS_4XX_BACKOFF_MS = 15 * 60 * 1e3;
3147
+ function isRewardsApiBackedOff() {
3148
+ return Date.now() < rewardsApiBackoffUntil;
3149
+ }
3150
+ function noteRewardsApiFailure(code) {
3151
+ if (!code?.startsWith("HTTP_4")) return;
3152
+ rewardsApiBackoffUntil = Date.now() + REWARDS_4XX_BACKOFF_MS;
3153
+ }
3045
3154
  var seenRoutes = /* @__PURE__ */ new Set();
3046
3155
  function getConsentKey(projectId) {
3047
3156
  return `mushi_rewards_consent_${projectId}`;
@@ -3146,14 +3255,22 @@ async function flush(ctx) {
3146
3255
  } catch {
3147
3256
  }
3148
3257
  }
3258
+ if (isRewardsApiBackedOff()) return;
3149
3259
  const batch = pendingEvents.splice(0, 100);
3150
3260
  try {
3151
- await ctx.client.submitActivity(currentUserId, batch, {
3261
+ const result = await ctx.client.submitActivity(currentUserId, batch, {
3152
3262
  userTraits: currentUserTraits ?? void 0,
3153
3263
  reporterTokenHash: reporterTokenHash ?? void 0,
3154
3264
  optedIn: true,
3155
3265
  hostJwt: hostJwt ?? void 0
3156
3266
  });
3267
+ if (!result.ok) {
3268
+ noteRewardsApiFailure(result.error?.code);
3269
+ const permanent = result.error?.code?.startsWith("HTTP_4");
3270
+ if (!permanent) {
3271
+ pendingEvents.unshift(...batch.slice(0, 50));
3272
+ }
3273
+ }
3157
3274
  } catch {
3158
3275
  pendingEvents.unshift(...batch.slice(0, 50));
3159
3276
  }
@@ -3164,13 +3281,14 @@ async function getTier(userId) {
3164
3281
  return fetchAndCacheTier(userId);
3165
3282
  }
3166
3283
  async function fetchAndCacheTier(userId) {
3167
- if (!apiClient) return null;
3284
+ if (!apiClient || isRewardsApiBackedOff()) return null;
3168
3285
  const res = await apiClient.getMyTier(userId);
3169
3286
  if (res.ok && res.data) {
3170
3287
  tierCache = res.data;
3171
3288
  tierCacheTime = Date.now();
3172
3289
  return tierCache;
3173
3290
  }
3291
+ noteRewardsApiFailure(res.error?.code);
3174
3292
  return null;
3175
3293
  }
3176
3294
  var routeObserver = null;
@@ -4598,6 +4716,7 @@ function recordApiFailure(failedRequests, callbacks) {
4598
4716
  // src/proactive-manager.ts
4599
4717
  var STORAGE_KEY_LAST_DISMISS = "mushi:lastDismiss";
4600
4718
  var STORAGE_KEY_CONSEC_DISMISS = "mushi:consecDismiss";
4719
+ var STORAGE_KEY_LAST_SHOWN = "mushi:lastShown";
4601
4720
  function readStorage(key) {
4602
4721
  try {
4603
4722
  return localStorage.getItem(key);
@@ -4615,20 +4734,30 @@ function createProactiveManager(config = {}) {
4615
4734
  const maxPerSession = config.maxProactivePerSession ?? 2;
4616
4735
  const cooldownHours = config.dismissCooldownHours ?? 24;
4617
4736
  const suppressThreshold = config.suppressAfterDismissals ?? 3;
4737
+ const reshowCooldownMs = (config.reshowCooldownMinutes ?? 30) * 60 * 1e3;
4618
4738
  let sessionPromptCount = 0;
4619
4739
  const sessionTriggerTypes = /* @__PURE__ */ new Set();
4620
4740
  function shouldShow(triggerType) {
4741
+ const now = Date.now();
4621
4742
  const consecDismissals = parseInt(readStorage(STORAGE_KEY_CONSEC_DISMISS) ?? "0", 10);
4622
4743
  if (consecDismissals >= suppressThreshold) return false;
4623
4744
  const lastDismiss = readStorage(STORAGE_KEY_LAST_DISMISS);
4624
4745
  if (lastDismiss) {
4625
- const elapsed = Date.now() - parseInt(lastDismiss, 10);
4746
+ const elapsed = now - parseInt(lastDismiss, 10);
4626
4747
  if (elapsed < cooldownHours * 60 * 60 * 1e3) return false;
4627
4748
  }
4749
+ if (reshowCooldownMs > 0 && sessionPromptCount === 0) {
4750
+ const lastShown = readStorage(STORAGE_KEY_LAST_SHOWN);
4751
+ if (lastShown) {
4752
+ const elapsed = now - parseInt(lastShown, 10);
4753
+ if (elapsed < reshowCooldownMs) return false;
4754
+ }
4755
+ }
4628
4756
  if (sessionPromptCount >= maxPerSession) return false;
4629
4757
  if (sessionTriggerTypes.has(triggerType)) return false;
4630
4758
  sessionTriggerTypes.add(triggerType);
4631
4759
  sessionPromptCount++;
4760
+ writeStorage(STORAGE_KEY_LAST_SHOWN, String(now));
4632
4761
  return true;
4633
4762
  }
4634
4763
  function recordDismissal() {
@@ -4642,13 +4771,17 @@ function createProactiveManager(config = {}) {
4642
4771
  function reset() {
4643
4772
  sessionPromptCount = 0;
4644
4773
  sessionTriggerTypes.clear();
4774
+ try {
4775
+ localStorage.removeItem(STORAGE_KEY_LAST_SHOWN);
4776
+ } catch {
4777
+ }
4645
4778
  }
4646
4779
  return { shouldShow, recordDismissal, recordSubmission, reset };
4647
4780
  }
4648
4781
 
4649
4782
  // src/version.ts
4650
4783
  var MUSHI_SDK_PACKAGE = "@mushi-mushi/web";
4651
- var MUSHI_SDK_VERSION = "1.7.2" ;
4784
+ var MUSHI_SDK_VERSION = "1.7.6" ;
4652
4785
 
4653
4786
  // src/mushi.ts
4654
4787
  var instance = null;
@@ -4829,15 +4962,26 @@ function createInstance(config) {
4829
4962
  });
4830
4963
  let detachAutoBreadcrumbs = null;
4831
4964
  detachAutoBreadcrumbs = installAutoBreadcrumbs(breadcrumbs);
4965
+ async function autoCaptureScreenshot(when) {
4966
+ const mode = activeConfig.capture?.screenshot;
4967
+ if (!screenshotCap || mode === "off" || pendingScreenshot) return;
4968
+ if (when === "open" && mode !== "auto") return;
4969
+ if (when === "submit" && mode !== "on-report" && mode !== "auto") return;
4970
+ log.debug("Auto-capturing screenshot", { when, mode });
4971
+ pendingScreenshot = await screenshotCap.take();
4972
+ widget.setScreenshotAttached(pendingScreenshot !== null);
4973
+ }
4832
4974
  widget = new MushiWidget(bootstrapConfig.widget, {
4833
4975
  onSubmit: async ({ category, description, intent }) => {
4834
4976
  log.info("Report submitted", { category, intent });
4835
4977
  proactiveManager?.recordSubmission();
4978
+ await autoCaptureScreenshot("submit");
4836
4979
  const outcome = await submitReport(category, description, intent);
4837
4980
  return outcome ?? { reportId: null, queuedOffline: true };
4838
4981
  },
4839
4982
  onOpen: () => {
4840
4983
  log.debug("Widget opened");
4984
+ void autoCaptureScreenshot("open");
4841
4985
  emit("widget:opened");
4842
4986
  },
4843
4987
  onClose: () => {
@@ -5066,7 +5210,7 @@ function createInstance(config) {
5066
5210
  ...sentryCtx.tags ? { tags: scrubTagsForWire(sentryCtx.tags) } : {}
5067
5211
  } : void 0;
5068
5212
  const report = {
5069
- id: crypto.randomUUID?.() ?? `mushi_${Date.now()}_${Math.random().toString(36).slice(2)}`,
5213
+ id: core.newUuid(),
5070
5214
  projectId: config.projectId,
5071
5215
  category,
5072
5216
  description: scrubbedDescription,
@@ -5244,7 +5388,8 @@ function createInstance(config) {
5244
5388
  widgetMounted: widget.getIsMounted(),
5245
5389
  runtimeConfigLoaded,
5246
5390
  captureScreenshotAvailable: screenshotCap !== null,
5247
- captureNetworkIntercepting: networkCap !== null
5391
+ captureNetworkIntercepting: networkCap !== null,
5392
+ widgetDiagnostics: widget.getWidgetDiagnostics()
5248
5393
  });
5249
5394
  },
5250
5395
  destroy() {
@@ -5287,7 +5432,7 @@ function createInstance(config) {
5287
5432
  ...sentryCtx.tags ? { tags: scrubTagsForWire(sentryCtx.tags) } : {}
5288
5433
  } : void 0;
5289
5434
  const report = {
5290
- id: crypto.randomUUID?.() ?? `mushi_${Date.now()}_${Math.random().toString(36).slice(2)}`,
5435
+ id: core.newUuid(),
5291
5436
  projectId: config.projectId,
5292
5437
  category,
5293
5438
  description,
@@ -5593,7 +5738,11 @@ async function runDiagnostics(options) {
5593
5738
  runtimeConfigLoaded: options.runtimeConfigLoaded,
5594
5739
  captureScreenshotAvailable: options.captureScreenshotAvailable,
5595
5740
  captureNetworkIntercepting: options.captureNetworkIntercepting,
5596
- sdkVersion: MUSHI_SDK_VERSION
5741
+ sdkVersion: MUSHI_SDK_VERSION,
5742
+ widgetHostPointerSafe: options.widgetDiagnostics?.widgetHostPointerSafe ?? false,
5743
+ widgetHostBounds: options.widgetDiagnostics?.widgetHostBounds ?? null,
5744
+ widgetSuppressed: options.widgetDiagnostics?.widgetSuppressed ?? false,
5745
+ bannerRendered: options.widgetDiagnostics?.bannerRendered ?? false
5597
5746
  };
5598
5747
  }
5599
5748
  async function diagnoseWithoutInstance() {
@@ -5606,7 +5755,11 @@ async function diagnoseWithoutInstance() {
5606
5755
  runtimeConfigLoaded: false,
5607
5756
  captureScreenshotAvailable: false,
5608
5757
  captureNetworkIntercepting: false,
5609
- sdkVersion: MUSHI_SDK_VERSION
5758
+ sdkVersion: MUSHI_SDK_VERSION,
5759
+ widgetHostPointerSafe: false,
5760
+ widgetHostBounds: null,
5761
+ widgetSuppressed: false,
5762
+ bannerRendered: false
5610
5763
  };
5611
5764
  }
5612
5765
  async function probeApiEndpoint(apiEndpoint) {