@mushi-mushi/web 1.7.8 → 1.8.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.js CHANGED
@@ -1510,6 +1510,106 @@ function getWidgetStyles(theme) {
1510
1510
  text-overflow: ellipsis;
1511
1511
  }
1512
1512
 
1513
+ /* Rich layout \u2014 pill + message + flat text actions (admin BetaBanner parity) */
1514
+ .mushi-banner--rich {
1515
+ justify-content: space-between;
1516
+ gap: 12px;
1517
+ min-height: 36px;
1518
+ height: auto;
1519
+ padding: 4px 12px 4px 16px;
1520
+ white-space: normal;
1521
+ }
1522
+ .mushi-banner-body {
1523
+ display: flex;
1524
+ align-items: center;
1525
+ gap: 8px;
1526
+ flex: 1;
1527
+ min-width: 0;
1528
+ overflow: hidden;
1529
+ }
1530
+ .mushi-banner-pill {
1531
+ display: inline-flex;
1532
+ flex-shrink: 0;
1533
+ align-items: center;
1534
+ padding: 1px 6px;
1535
+ border-radius: 3px;
1536
+ border: 1px solid currentColor;
1537
+ font-size: 10px;
1538
+ font-weight: 700;
1539
+ letter-spacing: 0.18em;
1540
+ text-transform: uppercase;
1541
+ opacity: 0.92;
1542
+ }
1543
+ .mushi-banner.neon .mushi-banner-pill {
1544
+ border-color: rgba(10,26,10,0.45);
1545
+ background: rgba(10,26,10,0.12);
1546
+ }
1547
+ .mushi-banner.brand .mushi-banner-pill {
1548
+ border-color: rgba(255,255,255,0.45);
1549
+ background: rgba(255,255,255,0.14);
1550
+ }
1551
+ .mushi-banner.subtle .mushi-banner-pill {
1552
+ border-color: ${ruleStrong};
1553
+ background: ${isDark ? "rgba(242,235,221,0.08)" : "rgba(14,13,11,0.06)"};
1554
+ }
1555
+ .mushi-banner-message {
1556
+ min-width: 0;
1557
+ overflow: hidden;
1558
+ text-overflow: ellipsis;
1559
+ white-space: nowrap;
1560
+ font-size: 11.5px;
1561
+ font-weight: 500;
1562
+ line-height: 1.3;
1563
+ opacity: 0.9;
1564
+ }
1565
+ .mushi-banner-actions {
1566
+ display: inline-flex;
1567
+ align-items: center;
1568
+ gap: 0;
1569
+ /* Shrinkable + swipe-scrollable so a long action row can never push
1570
+ past the viewport edge (dismiss sits outside this nav). */
1571
+ flex-shrink: 1;
1572
+ min-width: 0;
1573
+ overflow-x: auto;
1574
+ scrollbar-width: none;
1575
+ font-size: 11px;
1576
+ }
1577
+ .mushi-banner-actions::-webkit-scrollbar { display: none; }
1578
+ @media (max-width: 480px) {
1579
+ /* Phones: keep only the primary bug CTA (+ dismiss outside the nav). */
1580
+ .mushi-banner-actions .mushi-banner-extra { display: none; }
1581
+ }
1582
+ .mushi-banner-link {
1583
+ display: inline-flex;
1584
+ align-items: center;
1585
+ padding: 2px 8px;
1586
+ border: none;
1587
+ background: transparent;
1588
+ color: inherit;
1589
+ cursor: pointer;
1590
+ font: inherit;
1591
+ letter-spacing: inherit;
1592
+ text-decoration: none;
1593
+ opacity: 0.88;
1594
+ transition: opacity 0.15s ease;
1595
+ flex-shrink: 0;
1596
+ }
1597
+ .mushi-banner-link:hover { opacity: 1; }
1598
+ .mushi-banner-link:focus-visible {
1599
+ outline: 2px solid ${widgetAccent};
1600
+ outline-offset: 2px;
1601
+ border-radius: 2px;
1602
+ }
1603
+ .mushi-banner-divider {
1604
+ opacity: 0.28;
1605
+ padding: 0 1px;
1606
+ user-select: none;
1607
+ flex-shrink: 0;
1608
+ }
1609
+ .mushi-banner--rich .mushi-banner-dismiss {
1610
+ margin-left: 4px;
1611
+ }
1612
+
1513
1613
  .mushi-banner-btn {
1514
1614
  display: inline-flex;
1515
1615
  align-items: center;
@@ -1752,7 +1852,12 @@ var MushiWidget = class _MushiWidget {
1752
1852
  ...config.responseSlaLabel !== void 0 ? { responseSlaLabel: config.responseSlaLabel } : {},
1753
1853
  ...config.featureRequestCard !== void 0 ? { featureRequestCard: config.featureRequestCard } : {},
1754
1854
  ...config.featureRequestLabel !== void 0 ? { featureRequestLabel: config.featureRequestLabel } : {},
1755
- ...config.featureRequestDescription !== void 0 ? { featureRequestDescription: config.featureRequestDescription } : {}
1855
+ ...config.featureRequestDescription !== void 0 ? { featureRequestDescription: config.featureRequestDescription } : {},
1856
+ // Runtime/dashboard config delivers bannerMessage/bannerLabel via
1857
+ // mergeRuntimeConfig → bannerConfig. The widget is constructed before
1858
+ // that fetch resolves, so this pass-through is what makes server-driven
1859
+ // banner copy actually render.
1860
+ ...config.bannerConfig !== void 0 ? { bannerConfig: config.bannerConfig } : {}
1756
1861
  };
1757
1862
  this.locale = getLocale(this.config.locale === "auto" ? void 0 : this.config.locale);
1758
1863
  if (this.host.isConnected) this.syncHostChromeState();
@@ -2137,18 +2242,16 @@ var MushiWidget = class _MushiWidget {
2137
2242
  const bc = this.config.bannerConfig ?? {};
2138
2243
  const variant = bc.variant ?? "brand";
2139
2244
  const position = bc.position ?? "top";
2245
+ const message = bc.message?.trim() ?? "";
2246
+ const richLayout = message.length > 0;
2140
2247
  const bugLabel = bc.bugCta ?? "\u{1F41B} Report a bug";
2141
2248
  const showFeat = bc.featureCta !== false;
2142
2249
  const featLabel = bc.featureCtaLabel ?? "\u2728 Request feature";
2143
2250
  const zIdx = bc.zIndex ?? (this.config.zIndex ?? 99999) - 1;
2144
2251
  const banner = document.createElement("div");
2145
- banner.className = `mushi-banner ${variant} ${position}`;
2252
+ banner.className = `mushi-banner ${variant} ${position}${richLayout ? " mushi-banner--rich" : ""}`;
2146
2253
  banner.style.setProperty("--mushi-banner-z", String(zIdx));
2147
2254
  banner.setAttribute("role", "banner");
2148
- const bugBtn = document.createElement("button");
2149
- bugBtn.className = "mushi-banner-btn";
2150
- bugBtn.textContent = bugLabel;
2151
- bugBtn.addEventListener("click", () => this.open());
2152
2255
  const dismissBtn = document.createElement("button");
2153
2256
  dismissBtn.className = "mushi-banner-dismiss";
2154
2257
  dismissBtn.textContent = "\u2715";
@@ -2158,15 +2261,81 @@ var MushiWidget = class _MushiWidget {
2158
2261
  this.removeBodyNudge();
2159
2262
  this.render();
2160
2263
  });
2161
- banner.appendChild(bugBtn);
2162
- if (showFeat) {
2163
- const featBtn = document.createElement("button");
2164
- featBtn.className = "mushi-banner-btn";
2165
- featBtn.textContent = featLabel;
2166
- featBtn.addEventListener("click", () => this.open({ featureRequest: true }));
2167
- banner.appendChild(featBtn);
2168
- }
2169
- banner.appendChild(dismissBtn);
2264
+ if (richLayout) {
2265
+ const body = document.createElement("div");
2266
+ body.className = "mushi-banner-body";
2267
+ const labelText = bc.label === false ? null : bc.label ?? "Beta";
2268
+ if (labelText) {
2269
+ const pill = document.createElement("span");
2270
+ pill.className = "mushi-banner-pill";
2271
+ pill.textContent = labelText;
2272
+ body.appendChild(pill);
2273
+ }
2274
+ const msg = document.createElement("span");
2275
+ msg.className = "mushi-banner-message";
2276
+ msg.textContent = message;
2277
+ body.appendChild(msg);
2278
+ banner.appendChild(body);
2279
+ const nav = document.createElement("nav");
2280
+ nav.className = "mushi-banner-actions";
2281
+ nav.setAttribute("aria-label", "Feedback banner actions");
2282
+ const appendDivider = (extra = false) => {
2283
+ const sep = document.createElement("span");
2284
+ sep.className = `mushi-banner-divider${extra ? " mushi-banner-extra" : ""}`;
2285
+ sep.setAttribute("aria-hidden", "true");
2286
+ sep.textContent = "|";
2287
+ nav.appendChild(sep);
2288
+ };
2289
+ const appendAction = (label, onClick, extra = false) => {
2290
+ const btn = document.createElement("button");
2291
+ btn.type = "button";
2292
+ btn.className = `mushi-banner-link${extra ? " mushi-banner-extra" : ""}`;
2293
+ btn.textContent = label;
2294
+ btn.addEventListener("click", onClick);
2295
+ nav.appendChild(btn);
2296
+ };
2297
+ appendAction(bugLabel, () => this.open());
2298
+ if (showFeat) {
2299
+ appendDivider(true);
2300
+ appendAction(featLabel, () => this.open({ featureRequest: true }), true);
2301
+ }
2302
+ for (const link of bc.links ?? []) {
2303
+ const linkLabel = link.label?.trim();
2304
+ if (!linkLabel) continue;
2305
+ const href = link.href && (/^https?:\/\//i.test(link.href) || link.href.startsWith("/")) ? link.href : void 0;
2306
+ appendDivider(true);
2307
+ if (href) {
2308
+ const anchor = document.createElement("a");
2309
+ anchor.className = "mushi-banner-link mushi-banner-extra";
2310
+ anchor.href = href;
2311
+ anchor.textContent = linkLabel;
2312
+ anchor.target = "_blank";
2313
+ anchor.rel = "noopener noreferrer";
2314
+ nav.appendChild(anchor);
2315
+ } else {
2316
+ appendAction(linkLabel, () => {
2317
+ if (link.featureRequest) this.open({ featureRequest: true });
2318
+ else this.open();
2319
+ }, true);
2320
+ }
2321
+ }
2322
+ banner.appendChild(nav);
2323
+ banner.appendChild(dismissBtn);
2324
+ } else {
2325
+ const bugBtn = document.createElement("button");
2326
+ bugBtn.className = "mushi-banner-btn";
2327
+ bugBtn.textContent = bugLabel;
2328
+ bugBtn.addEventListener("click", () => this.open());
2329
+ banner.appendChild(bugBtn);
2330
+ if (showFeat) {
2331
+ const featBtn = document.createElement("button");
2332
+ featBtn.className = "mushi-banner-btn";
2333
+ featBtn.textContent = featLabel;
2334
+ featBtn.addEventListener("click", () => this.open({ featureRequest: true }));
2335
+ banner.appendChild(featBtn);
2336
+ }
2337
+ banner.appendChild(dismissBtn);
2338
+ }
2170
2339
  this.shadow.appendChild(banner);
2171
2340
  this.applyBodyNudge(position);
2172
2341
  }
@@ -4821,7 +4990,7 @@ function createProactiveManager(config = {}) {
4821
4990
 
4822
4991
  // src/version.ts
4823
4992
  var MUSHI_SDK_PACKAGE = "@mushi-mushi/web";
4824
- var MUSHI_SDK_VERSION = "1.7.7" ;
4993
+ var MUSHI_SDK_VERSION = "1.8.0" ;
4825
4994
 
4826
4995
  // src/mushi.ts
4827
4996
  var instance = null;
@@ -5002,8 +5171,10 @@ function createInstance(config) {
5002
5171
  });
5003
5172
  let detachAutoBreadcrumbs = null;
5004
5173
  detachAutoBreadcrumbs = installAutoBreadcrumbs(breadcrumbs);
5174
+ let screenshotCaptureInFlight = false;
5005
5175
  async function takeScreenshotWithoutChrome() {
5006
- if (!screenshotCap) return null;
5176
+ if (!screenshotCap || screenshotCaptureInFlight) return null;
5177
+ screenshotCaptureInFlight = true;
5007
5178
  const panelWasVisible = widget.getIsOpen();
5008
5179
  if (panelWasVisible) widget.hidePanel();
5009
5180
  const host = document.getElementById("mushi-mushi-widget");
@@ -5015,6 +5186,7 @@ function createInstance(config) {
5015
5186
  try {
5016
5187
  return await screenshotCap.take();
5017
5188
  } finally {
5189
+ screenshotCaptureInFlight = false;
5018
5190
  if (host) host.style.visibility = prevVisibility;
5019
5191
  if (panelWasVisible) widget.showPanel();
5020
5192
  }
@@ -5688,14 +5860,21 @@ function mergeRuntimeConfig(config, runtime) {
5688
5860
  const nativeTrigger = runtime.native?.triggerMode;
5689
5861
  const runtimeLauncher = runtime.widget?.launcher;
5690
5862
  const widgetTrigger = runtimeLauncher ?? runtime.widget?.trigger ?? (nativeTrigger === "none" || nativeTrigger === "shake" ? "manual" : void 0);
5691
- const runtimeBannerVariant = runtime.widget?.bannerVariant;
5692
- const runtimeBannerPosition = runtime.widget?.bannerPosition;
5693
- const runtimeBannerBugCta = runtime.widget?.bannerBugCta;
5694
- const runtimeBannerFeatureCta = runtime.widget?.bannerFeatureCta;
5695
- const derivedBannerConfig = runtimeBannerVariant || runtimeBannerPosition || runtimeBannerBugCta != null || runtimeBannerFeatureCta != null ? {
5863
+ const runtimeWidget = runtime.widget;
5864
+ const runtimeBannerVariant = runtimeWidget?.bannerVariant;
5865
+ const runtimeBannerPosition = runtimeWidget?.bannerPosition;
5866
+ const runtimeBannerMessage = runtimeWidget?.bannerMessage;
5867
+ const runtimeBannerLabel = runtimeWidget?.bannerLabel;
5868
+ const runtimeBannerBugCta = runtimeWidget?.bannerBugCta;
5869
+ const runtimeBannerFeatureCta = runtimeWidget?.bannerFeatureCta;
5870
+ const derivedBannerConfig = runtimeBannerVariant || runtimeBannerPosition || runtimeBannerMessage != null || runtimeBannerLabel != null || runtimeBannerBugCta != null || runtimeBannerFeatureCta != null ? {
5696
5871
  ...config.widget?.bannerConfig ?? {},
5697
5872
  ...runtimeBannerVariant ? { variant: runtimeBannerVariant } : {},
5698
5873
  ...runtimeBannerPosition ? { position: runtimeBannerPosition } : {},
5874
+ ...runtimeBannerMessage != null ? { message: runtimeBannerMessage } : {},
5875
+ // Dashboard sends an empty string to hide the pill (the runtime
5876
+ // payload has no way to express the local-config `label: false`).
5877
+ ...runtimeBannerLabel != null ? { label: runtimeBannerLabel === "" ? false : runtimeBannerLabel } : {},
5699
5878
  ...runtimeBannerBugCta != null ? { bugCta: runtimeBannerBugCta ?? void 0 } : {},
5700
5879
  ...runtimeBannerFeatureCta != null ? { featureCta: runtimeBannerFeatureCta } : {}
5701
5880
  } : void 0;