@mushi-mushi/web 1.6.0 → 1.7.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 +235 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +235 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -132,6 +132,8 @@ declare class MushiWidget {
|
|
|
132
132
|
* Drives a different success copy so the user knows the report
|
|
133
133
|
* hasn't actually reached the console yet. */
|
|
134
134
|
private lastSubmitQueuedOffline;
|
|
135
|
+
/** Whether the user has clicked ✕ on the header banner this session. */
|
|
136
|
+
private bannerDismissed;
|
|
135
137
|
constructor(config: MushiWidgetConfig | undefined, callbacks: WidgetCallbacks, sdkVersion?: string);
|
|
136
138
|
mount(): void;
|
|
137
139
|
getIsMounted(): boolean;
|
|
@@ -180,6 +182,13 @@ declare class MushiWidget {
|
|
|
180
182
|
private syncAttachedLaunchers;
|
|
181
183
|
private syncSmartHide;
|
|
182
184
|
private shouldRenderTrigger;
|
|
185
|
+
/** Height of the banner in px — kept in sync with the CSS `.mushi-banner` height (36px). */
|
|
186
|
+
private static readonly BANNER_HEIGHT;
|
|
187
|
+
/** CSS property applied to document.body so host-app content doesn't slide under the banner. */
|
|
188
|
+
private static readonly BODY_NUDGE_PROP;
|
|
189
|
+
private applyBodyNudge;
|
|
190
|
+
private removeBodyNudge;
|
|
191
|
+
private renderBanner;
|
|
183
192
|
private effectiveTrigger;
|
|
184
193
|
private isMobileSmartHidden;
|
|
185
194
|
private detectEnvironment;
|
package/dist/index.d.ts
CHANGED
|
@@ -132,6 +132,8 @@ declare class MushiWidget {
|
|
|
132
132
|
* Drives a different success copy so the user knows the report
|
|
133
133
|
* hasn't actually reached the console yet. */
|
|
134
134
|
private lastSubmitQueuedOffline;
|
|
135
|
+
/** Whether the user has clicked ✕ on the header banner this session. */
|
|
136
|
+
private bannerDismissed;
|
|
135
137
|
constructor(config: MushiWidgetConfig | undefined, callbacks: WidgetCallbacks, sdkVersion?: string);
|
|
136
138
|
mount(): void;
|
|
137
139
|
getIsMounted(): boolean;
|
|
@@ -180,6 +182,13 @@ declare class MushiWidget {
|
|
|
180
182
|
private syncAttachedLaunchers;
|
|
181
183
|
private syncSmartHide;
|
|
182
184
|
private shouldRenderTrigger;
|
|
185
|
+
/** Height of the banner in px — kept in sync with the CSS `.mushi-banner` height (36px). */
|
|
186
|
+
private static readonly BANNER_HEIGHT;
|
|
187
|
+
/** CSS property applied to document.body so host-app content doesn't slide under the banner. */
|
|
188
|
+
private static readonly BODY_NUDGE_PROP;
|
|
189
|
+
private applyBodyNudge;
|
|
190
|
+
private removeBodyNudge;
|
|
191
|
+
private renderBanner;
|
|
183
192
|
private effectiveTrigger;
|
|
184
193
|
private isMobileSmartHidden;
|
|
185
194
|
private detectEnvironment;
|
package/dist/index.js
CHANGED
|
@@ -1415,6 +1415,145 @@ function getWidgetStyles(theme) {
|
|
|
1415
1415
|
font-size: 10.5px;
|
|
1416
1416
|
}
|
|
1417
1417
|
|
|
1418
|
+
/* \u2500\u2500\u2500 Banner launcher (trigger: 'banner') \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 */
|
|
1419
|
+
|
|
1420
|
+
.mushi-banner {
|
|
1421
|
+
position: fixed;
|
|
1422
|
+
left: 0;
|
|
1423
|
+
right: 0;
|
|
1424
|
+
height: 36px;
|
|
1425
|
+
display: flex;
|
|
1426
|
+
align-items: center;
|
|
1427
|
+
justify-content: center;
|
|
1428
|
+
gap: 10px;
|
|
1429
|
+
padding: 0 16px;
|
|
1430
|
+
font-family: ${fontMono};
|
|
1431
|
+
font-size: 11.5px;
|
|
1432
|
+
letter-spacing: 0.04em;
|
|
1433
|
+
white-space: nowrap;
|
|
1434
|
+
overflow: hidden;
|
|
1435
|
+
z-index: var(--mushi-banner-z, 99998);
|
|
1436
|
+
animation: mushi-banner-slide-in 0.3s ${easeStamp} both;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
.mushi-banner.top { top: 0; }
|
|
1440
|
+
.mushi-banner.bottom { bottom: 0; }
|
|
1441
|
+
|
|
1442
|
+
/* --- neon variant (electric lime \u2014 dev / beta tool aesthetic) --- */
|
|
1443
|
+
.mushi-banner.neon {
|
|
1444
|
+
background: #0FFF50;
|
|
1445
|
+
color: #0a1a0a;
|
|
1446
|
+
border-bottom: 1.5px solid #00C43A;
|
|
1447
|
+
}
|
|
1448
|
+
.mushi-banner.neon.bottom {
|
|
1449
|
+
border-top: 1.5px solid #00C43A;
|
|
1450
|
+
border-bottom: none;
|
|
1451
|
+
}
|
|
1452
|
+
.mushi-banner.neon .mushi-banner-btn {
|
|
1453
|
+
background: rgba(0,0,0,0.14);
|
|
1454
|
+
color: #0a1a0a;
|
|
1455
|
+
border: 1px solid rgba(0,0,0,0.22);
|
|
1456
|
+
}
|
|
1457
|
+
.mushi-banner.neon .mushi-banner-btn:hover {
|
|
1458
|
+
background: rgba(0,0,0,0.22);
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
/* --- brand variant (vermillion \u2014 editorial, app-quality) --- */
|
|
1462
|
+
.mushi-banner.brand {
|
|
1463
|
+
background: ${vermillion};
|
|
1464
|
+
color: #fff;
|
|
1465
|
+
border-bottom: 1.5px solid ${isDark ? "#C4321E" : "#B52F1F"};
|
|
1466
|
+
}
|
|
1467
|
+
.mushi-banner.brand.bottom {
|
|
1468
|
+
border-top: 1.5px solid ${isDark ? "#C4321E" : "#B52F1F"};
|
|
1469
|
+
border-bottom: none;
|
|
1470
|
+
}
|
|
1471
|
+
.mushi-banner.brand .mushi-banner-btn {
|
|
1472
|
+
background: rgba(255,255,255,0.18);
|
|
1473
|
+
color: #fff;
|
|
1474
|
+
border: 1px solid rgba(255,255,255,0.32);
|
|
1475
|
+
}
|
|
1476
|
+
.mushi-banner.brand .mushi-banner-btn:hover {
|
|
1477
|
+
background: rgba(255,255,255,0.28);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
/* --- subtle variant (hairline, muted \u2014 least disruptive) --- */
|
|
1481
|
+
.mushi-banner.subtle {
|
|
1482
|
+
background: ${isDark ? "rgba(242,235,221,0.06)" : "rgba(14,13,11,0.04)"};
|
|
1483
|
+
color: ${inkMuted};
|
|
1484
|
+
border-bottom: 1px solid ${rule};
|
|
1485
|
+
}
|
|
1486
|
+
.mushi-banner.subtle.bottom {
|
|
1487
|
+
border-top: 1px solid ${rule};
|
|
1488
|
+
border-bottom: none;
|
|
1489
|
+
}
|
|
1490
|
+
.mushi-banner.subtle .mushi-banner-btn {
|
|
1491
|
+
background: ${isDark ? "rgba(242,235,221,0.10)" : "rgba(14,13,11,0.07)"};
|
|
1492
|
+
color: ${ink};
|
|
1493
|
+
border: 1px solid ${rule};
|
|
1494
|
+
}
|
|
1495
|
+
.mushi-banner.subtle .mushi-banner-btn:hover {
|
|
1496
|
+
background: ${isDark ? "rgba(242,235,221,0.16)" : "rgba(14,13,11,0.12)"};
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
.mushi-banner-label {
|
|
1500
|
+
flex: 1;
|
|
1501
|
+
text-align: center;
|
|
1502
|
+
overflow: hidden;
|
|
1503
|
+
text-overflow: ellipsis;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
.mushi-banner-btn {
|
|
1507
|
+
display: inline-flex;
|
|
1508
|
+
align-items: center;
|
|
1509
|
+
gap: 4px;
|
|
1510
|
+
padding: 3px 10px;
|
|
1511
|
+
border-radius: 3px;
|
|
1512
|
+
cursor: pointer;
|
|
1513
|
+
font: inherit;
|
|
1514
|
+
letter-spacing: inherit;
|
|
1515
|
+
transition: background 0.15s ease, opacity 0.15s ease;
|
|
1516
|
+
flex-shrink: 0;
|
|
1517
|
+
height: 24px;
|
|
1518
|
+
line-height: 1;
|
|
1519
|
+
}
|
|
1520
|
+
.mushi-banner-btn:focus-visible {
|
|
1521
|
+
outline: 2px solid ${vermillion};
|
|
1522
|
+
outline-offset: 2px;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
.mushi-banner-dismiss {
|
|
1526
|
+
background: transparent !important;
|
|
1527
|
+
border: none !important;
|
|
1528
|
+
opacity: 0.65;
|
|
1529
|
+
cursor: pointer;
|
|
1530
|
+
font-size: 14px;
|
|
1531
|
+
line-height: 1;
|
|
1532
|
+
padding: 4px 8px;
|
|
1533
|
+
margin-left: auto;
|
|
1534
|
+
flex-shrink: 0;
|
|
1535
|
+
color: inherit;
|
|
1536
|
+
border-radius: 3px;
|
|
1537
|
+
transition: opacity 0.15s, background 0.15s;
|
|
1538
|
+
}
|
|
1539
|
+
.mushi-banner-dismiss:hover {
|
|
1540
|
+
opacity: 1;
|
|
1541
|
+
background: rgba(0,0,0,0.12) !important;
|
|
1542
|
+
}
|
|
1543
|
+
.mushi-banner.neon .mushi-banner-dismiss:hover { background: rgba(0,0,0,0.18) !important; }
|
|
1544
|
+
|
|
1545
|
+
@keyframes mushi-banner-slide-in {
|
|
1546
|
+
from { transform: translateY(calc(-1 * 100%)); opacity: 0.5; }
|
|
1547
|
+
to { transform: translateY(0); opacity: 1; }
|
|
1548
|
+
}
|
|
1549
|
+
.mushi-banner.bottom {
|
|
1550
|
+
animation-name: mushi-banner-slide-in-bottom;
|
|
1551
|
+
}
|
|
1552
|
+
@keyframes mushi-banner-slide-in-bottom {
|
|
1553
|
+
from { transform: translateY(100%); opacity: 0.5; }
|
|
1554
|
+
to { transform: translateY(0); opacity: 1; }
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1418
1557
|
@media (prefers-reduced-motion: reduce) {
|
|
1419
1558
|
*,
|
|
1420
1559
|
*::before,
|
|
@@ -1453,7 +1592,7 @@ function isSubmitShortcut(e) {
|
|
|
1453
1592
|
function escapeHtml(value) {
|
|
1454
1593
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1455
1594
|
}
|
|
1456
|
-
var MushiWidget = class {
|
|
1595
|
+
var MushiWidget = class _MushiWidget {
|
|
1457
1596
|
constructor(config = {}, callbacks, sdkVersion = "0.7.0") {
|
|
1458
1597
|
this.sdkVersion = sdkVersion;
|
|
1459
1598
|
this.config = {
|
|
@@ -1473,6 +1612,7 @@ var MushiWidget = class {
|
|
|
1473
1612
|
locale: config.locale ?? "auto",
|
|
1474
1613
|
zIndex: config.zIndex ?? 99999,
|
|
1475
1614
|
trigger: config.trigger ?? "auto",
|
|
1615
|
+
bannerConfig: config.bannerConfig ?? {},
|
|
1476
1616
|
attachToSelector: config.attachToSelector ?? "",
|
|
1477
1617
|
inset: config.inset ?? {},
|
|
1478
1618
|
respectSafeArea: config.respectSafeArea ?? true,
|
|
@@ -1562,6 +1702,8 @@ var MushiWidget = class {
|
|
|
1562
1702
|
* Drives a different success copy so the user knows the report
|
|
1563
1703
|
* hasn't actually reached the console yet. */
|
|
1564
1704
|
lastSubmitQueuedOffline = false;
|
|
1705
|
+
/** Whether the user has clicked ✕ on the header banner this session. */
|
|
1706
|
+
bannerDismissed = false;
|
|
1565
1707
|
mount() {
|
|
1566
1708
|
if (this.host.isConnected) return;
|
|
1567
1709
|
document.body.appendChild(this.host);
|
|
@@ -1870,7 +2012,7 @@ var MushiWidget = class {
|
|
|
1870
2012
|
shouldRenderTrigger() {
|
|
1871
2013
|
if (!this.triggerVisible) return false;
|
|
1872
2014
|
if (this.triggerHiddenByScroll) return false;
|
|
1873
|
-
if (this.config.trigger === "manual" || this.config.trigger === "hidden" || this.config.trigger === "attach") {
|
|
2015
|
+
if (this.config.trigger === "manual" || this.config.trigger === "hidden" || this.config.trigger === "attach" || this.config.trigger === "banner") {
|
|
1874
2016
|
return false;
|
|
1875
2017
|
}
|
|
1876
2018
|
if (this.isMobileSmartHidden()) return false;
|
|
@@ -1879,6 +2021,81 @@ var MushiWidget = class {
|
|
|
1879
2021
|
const action = this.config.environments[this.detectEnvironment()];
|
|
1880
2022
|
return action !== "never" && action !== "manual";
|
|
1881
2023
|
}
|
|
2024
|
+
/** Height of the banner in px — kept in sync with the CSS `.mushi-banner` height (36px). */
|
|
2025
|
+
static BANNER_HEIGHT = 36;
|
|
2026
|
+
/** CSS property applied to document.body so host-app content doesn't slide under the banner. */
|
|
2027
|
+
static BODY_NUDGE_PROP = "--mushi-banner-offset";
|
|
2028
|
+
applyBodyNudge(position) {
|
|
2029
|
+
const h = `${_MushiWidget.BANNER_HEIGHT}px`;
|
|
2030
|
+
if (position === "top") {
|
|
2031
|
+
document.documentElement.style.setProperty(_MushiWidget.BODY_NUDGE_PROP, h);
|
|
2032
|
+
if (!document.body.style.paddingTop) {
|
|
2033
|
+
document.body.style.paddingTop = h;
|
|
2034
|
+
document.body.dataset.mushiBannerNudged = "top";
|
|
2035
|
+
}
|
|
2036
|
+
} else {
|
|
2037
|
+
document.documentElement.style.setProperty(_MushiWidget.BODY_NUDGE_PROP, h);
|
|
2038
|
+
if (!document.body.style.paddingBottom) {
|
|
2039
|
+
document.body.style.paddingBottom = h;
|
|
2040
|
+
document.body.dataset.mushiBannerNudged = "bottom";
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
removeBodyNudge() {
|
|
2045
|
+
document.documentElement.style.removeProperty(_MushiWidget.BODY_NUDGE_PROP);
|
|
2046
|
+
const nudged = document.body.dataset.mushiBannerNudged;
|
|
2047
|
+
if (nudged === "top") {
|
|
2048
|
+
document.body.style.paddingTop = "";
|
|
2049
|
+
delete document.body.dataset.mushiBannerNudged;
|
|
2050
|
+
} else if (nudged === "bottom") {
|
|
2051
|
+
document.body.style.paddingBottom = "";
|
|
2052
|
+
delete document.body.dataset.mushiBannerNudged;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
renderBanner() {
|
|
2056
|
+
if (this.config.trigger !== "banner") return;
|
|
2057
|
+
if (this.bannerDismissed) {
|
|
2058
|
+
this.removeBodyNudge();
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
if (!this.triggerVisible) return;
|
|
2062
|
+
if (this.isRouteHidden()) return;
|
|
2063
|
+
const bc = this.config.bannerConfig ?? {};
|
|
2064
|
+
const variant = bc.variant ?? "brand";
|
|
2065
|
+
const position = bc.position ?? "top";
|
|
2066
|
+
const bugLabel = bc.bugCta ?? "\u{1F41B} Report a bug";
|
|
2067
|
+
const showFeat = bc.featureCta !== false;
|
|
2068
|
+
const featLabel = bc.featureCtaLabel ?? "\u2728 Request feature";
|
|
2069
|
+
const zIdx = bc.zIndex ?? (this.config.zIndex ?? 99999) - 1;
|
|
2070
|
+
const banner = document.createElement("div");
|
|
2071
|
+
banner.className = `mushi-banner ${variant} ${position}`;
|
|
2072
|
+
banner.style.setProperty("--mushi-banner-z", String(zIdx));
|
|
2073
|
+
banner.setAttribute("role", "banner");
|
|
2074
|
+
const bugBtn = document.createElement("button");
|
|
2075
|
+
bugBtn.className = "mushi-banner-btn";
|
|
2076
|
+
bugBtn.textContent = bugLabel;
|
|
2077
|
+
bugBtn.addEventListener("click", () => this.open());
|
|
2078
|
+
const dismissBtn = document.createElement("button");
|
|
2079
|
+
dismissBtn.className = "mushi-banner-dismiss";
|
|
2080
|
+
dismissBtn.textContent = "\u2715";
|
|
2081
|
+
dismissBtn.setAttribute("aria-label", "Dismiss feedback banner");
|
|
2082
|
+
dismissBtn.addEventListener("click", () => {
|
|
2083
|
+
this.bannerDismissed = true;
|
|
2084
|
+
this.removeBodyNudge();
|
|
2085
|
+
this.render();
|
|
2086
|
+
});
|
|
2087
|
+
banner.appendChild(bugBtn);
|
|
2088
|
+
if (showFeat) {
|
|
2089
|
+
const featBtn = document.createElement("button");
|
|
2090
|
+
featBtn.className = "mushi-banner-btn";
|
|
2091
|
+
featBtn.textContent = featLabel;
|
|
2092
|
+
featBtn.addEventListener("click", () => this.open({ featureRequest: true }));
|
|
2093
|
+
banner.appendChild(featBtn);
|
|
2094
|
+
}
|
|
2095
|
+
banner.appendChild(dismissBtn);
|
|
2096
|
+
this.shadow.appendChild(banner);
|
|
2097
|
+
this.applyBodyNudge(position);
|
|
2098
|
+
}
|
|
1882
2099
|
effectiveTrigger() {
|
|
1883
2100
|
if (!this.config.smartHide || typeof window === "undefined") return this.config.trigger;
|
|
1884
2101
|
const smart = this.config.smartHide === true ? { onMobile: "edge-tab" } : this.config.smartHide;
|
|
@@ -1917,6 +2134,7 @@ var MushiWidget = class {
|
|
|
1917
2134
|
const style = document.createElement("style");
|
|
1918
2135
|
style.textContent = getWidgetStyles(theme);
|
|
1919
2136
|
this.shadow.appendChild(style);
|
|
2137
|
+
this.renderBanner();
|
|
1920
2138
|
if (this.shouldRenderTrigger()) {
|
|
1921
2139
|
const effectiveTrigger = this.effectiveTrigger();
|
|
1922
2140
|
const trigger = document.createElement("button");
|
|
@@ -4421,7 +4639,7 @@ function createProactiveManager(config = {}) {
|
|
|
4421
4639
|
|
|
4422
4640
|
// src/version.ts
|
|
4423
4641
|
var MUSHI_SDK_PACKAGE = "@mushi-mushi/web";
|
|
4424
|
-
var MUSHI_SDK_VERSION = "1.
|
|
4642
|
+
var MUSHI_SDK_VERSION = "1.7.0" ;
|
|
4425
4643
|
|
|
4426
4644
|
// src/mushi.ts
|
|
4427
4645
|
var instance = null;
|
|
@@ -5257,13 +5475,26 @@ function createInstance(config) {
|
|
|
5257
5475
|
}
|
|
5258
5476
|
function mergeRuntimeConfig(config, runtime) {
|
|
5259
5477
|
const nativeTrigger = runtime.native?.triggerMode;
|
|
5260
|
-
const
|
|
5478
|
+
const runtimeLauncher = runtime.widget?.launcher;
|
|
5479
|
+
const widgetTrigger = runtimeLauncher ?? runtime.widget?.trigger ?? (nativeTrigger === "none" || nativeTrigger === "shake" ? "manual" : void 0);
|
|
5480
|
+
const runtimeBannerVariant = runtime.widget?.bannerVariant;
|
|
5481
|
+
const runtimeBannerPosition = runtime.widget?.bannerPosition;
|
|
5482
|
+
const runtimeBannerBugCta = runtime.widget?.bannerBugCta;
|
|
5483
|
+
const runtimeBannerFeatureCta = runtime.widget?.bannerFeatureCta;
|
|
5484
|
+
const derivedBannerConfig = runtimeBannerVariant || runtimeBannerPosition || runtimeBannerBugCta != null || runtimeBannerFeatureCta != null ? {
|
|
5485
|
+
...config.widget?.bannerConfig ?? {},
|
|
5486
|
+
...runtimeBannerVariant ? { variant: runtimeBannerVariant } : {},
|
|
5487
|
+
...runtimeBannerPosition ? { position: runtimeBannerPosition } : {},
|
|
5488
|
+
...runtimeBannerBugCta != null ? { bugCta: runtimeBannerBugCta ?? void 0 } : {},
|
|
5489
|
+
...runtimeBannerFeatureCta != null ? { featureCta: runtimeBannerFeatureCta } : {}
|
|
5490
|
+
} : void 0;
|
|
5261
5491
|
return {
|
|
5262
5492
|
...config,
|
|
5263
5493
|
widget: {
|
|
5264
5494
|
...config.widget,
|
|
5265
5495
|
...runtime.widget,
|
|
5266
5496
|
...widgetTrigger ? { trigger: widgetTrigger } : {},
|
|
5497
|
+
...derivedBannerConfig ? { bannerConfig: derivedBannerConfig } : {},
|
|
5267
5498
|
// betaMode is local-only: set by the host app, not the dashboard.
|
|
5268
5499
|
// Restore it after the runtime spread so it is never silently cleared.
|
|
5269
5500
|
...config.widget?.betaMode ? { betaMode: config.widget.betaMode } : {}
|