@mushi-mushi/web 1.2.0 → 1.3.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/CONTRIBUTING.md +11 -0
- package/dist/index.cjs +566 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.js +566 -84
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -47,10 +47,20 @@ var en = {
|
|
|
47
47
|
heading: "Tell us more",
|
|
48
48
|
descriptionPlaceholder: "Describe what happened\u2026",
|
|
49
49
|
screenshotButton: "Attach Screenshot",
|
|
50
|
-
screenshotAttached: "Screenshot attached",
|
|
50
|
+
screenshotAttached: "Screenshot attached \u2713",
|
|
51
|
+
screenshotCapturing: "Taking screenshot\u2026",
|
|
52
|
+
screenshotFailed: "Couldn't capture \u2014 describe it instead",
|
|
51
53
|
elementButton: "Select Element",
|
|
52
|
-
elementSelected: "Element selected",
|
|
53
|
-
|
|
54
|
+
elementSelected: "Element selected \u2713",
|
|
55
|
+
elementCapturing: "Click anything on the page\u2026",
|
|
56
|
+
elementSelectorHint: "Click any element \xB7 Esc to cancel",
|
|
57
|
+
optional: "(optional)",
|
|
58
|
+
tooShort: "A bit more detail helps us fix it faster",
|
|
59
|
+
examplePrompts: [
|
|
60
|
+
"The save button does nothing",
|
|
61
|
+
"Page froze for 10 seconds",
|
|
62
|
+
"Layout looks broken here"
|
|
63
|
+
]
|
|
54
64
|
}
|
|
55
65
|
};
|
|
56
66
|
|
|
@@ -97,10 +107,20 @@ var ja = {
|
|
|
97
107
|
heading: "\u8A73\u7D30\u3092\u6559\u3048\u3066\u304F\u3060\u3055\u3044",
|
|
98
108
|
descriptionPlaceholder: "\u4F55\u304C\u8D77\u304D\u305F\u304B\u8AAC\u660E\u3057\u3066\u304F\u3060\u3055\u3044\u2026",
|
|
99
109
|
screenshotButton: "\u30B9\u30AF\u30EA\u30FC\u30F3\u30B7\u30E7\u30C3\u30C8\u6DFB\u4ED8",
|
|
100
|
-
screenshotAttached: "\u30B9\u30AF\u30EA\u30FC\u30F3\u30B7\u30E7\u30C3\u30C8\u6DFB\u4ED8\u6E08\u307F",
|
|
110
|
+
screenshotAttached: "\u30B9\u30AF\u30EA\u30FC\u30F3\u30B7\u30E7\u30C3\u30C8\u6DFB\u4ED8\u6E08\u307F \u2713",
|
|
111
|
+
screenshotCapturing: "\u30B9\u30AF\u30EA\u30FC\u30F3\u30B7\u30E7\u30C3\u30C8\u64AE\u5F71\u4E2D\u2026",
|
|
112
|
+
screenshotFailed: "\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F \u2014 \u6587\u5B57\u3067\u6559\u3048\u3066\u304F\u3060\u3055\u3044",
|
|
101
113
|
elementButton: "\u8981\u7D20\u3092\u9078\u629E",
|
|
102
|
-
elementSelected: "\u8981\u7D20\u9078\u629E\u6E08\u307F",
|
|
103
|
-
|
|
114
|
+
elementSelected: "\u8981\u7D20\u9078\u629E\u6E08\u307F \u2713",
|
|
115
|
+
elementCapturing: "\u30DA\u30FC\u30B8\u4E0A\u306E\u8981\u7D20\u3092\u30AF\u30EA\u30C3\u30AF\u2026",
|
|
116
|
+
elementSelectorHint: "\u8981\u7D20\u3092\u30AF\u30EA\u30C3\u30AF \xB7 Esc \u3067\u30AD\u30E3\u30F3\u30BB\u30EB",
|
|
117
|
+
optional: "\uFF08\u4EFB\u610F\uFF09",
|
|
118
|
+
tooShort: "\u3082\u3046\u5C11\u3057\u8A73\u3057\u304F\u6559\u3048\u3066\u304F\u3060\u3055\u3044",
|
|
119
|
+
examplePrompts: [
|
|
120
|
+
"\u4FDD\u5B58\u30DC\u30BF\u30F3\u304C\u53CD\u5FDC\u3057\u306A\u3044",
|
|
121
|
+
"\u30DA\u30FC\u30B8\u304C10\u79D2\u56FA\u307E\u3063\u305F",
|
|
122
|
+
"\u30EC\u30A4\u30A2\u30A6\u30C8\u304C\u5D29\u308C\u3066\u3044\u308B"
|
|
123
|
+
]
|
|
104
124
|
}
|
|
105
125
|
};
|
|
106
126
|
|
|
@@ -147,10 +167,20 @@ var th = {
|
|
|
147
167
|
heading: "\u0E1A\u0E2D\u0E01\u0E40\u0E23\u0E32\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E40\u0E15\u0E34\u0E21",
|
|
148
168
|
descriptionPlaceholder: "\u0E2D\u0E18\u0E34\u0E1A\u0E32\u0E22\u0E2A\u0E34\u0E48\u0E07\u0E17\u0E35\u0E48\u0E40\u0E01\u0E34\u0E14\u0E02\u0E36\u0E49\u0E19\u2026",
|
|
149
169
|
screenshotButton: "\u0E41\u0E19\u0E1A\u0E2A\u0E01\u0E23\u0E35\u0E19\u0E0A\u0E47\u0E2D\u0E15",
|
|
150
|
-
screenshotAttached: "\u0E41\u0E19\u0E1A\u0E2A\u0E01\u0E23\u0E35\u0E19\u0E0A\u0E47\u0E2D\u0E15\u0E41\u0E25\u0E49\u0E27",
|
|
170
|
+
screenshotAttached: "\u0E41\u0E19\u0E1A\u0E2A\u0E01\u0E23\u0E35\u0E19\u0E0A\u0E47\u0E2D\u0E15\u0E41\u0E25\u0E49\u0E27 \u2713",
|
|
171
|
+
screenshotCapturing: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E16\u0E48\u0E32\u0E22\u0E2A\u0E01\u0E23\u0E35\u0E19\u0E0A\u0E47\u0E2D\u0E15\u2026",
|
|
172
|
+
screenshotFailed: "\u0E44\u0E21\u0E48\u0E2A\u0E32\u0E21\u0E32\u0E23\u0E16\u0E16\u0E48\u0E32\u0E22\u0E20\u0E32\u0E1E\u0E44\u0E14\u0E49 \u2014 \u0E42\u0E1B\u0E23\u0E14\u0E2D\u0E18\u0E34\u0E1A\u0E32\u0E22\u0E41\u0E17\u0E19",
|
|
151
173
|
elementButton: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E2D\u0E07\u0E04\u0E4C\u0E1B\u0E23\u0E30\u0E01\u0E2D\u0E1A",
|
|
152
|
-
elementSelected: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E2D\u0E07\u0E04\u0E4C\u0E1B\u0E23\u0E30\u0E01\u0E2D\u0E1A\u0E41\u0E25\u0E49\u0E27",
|
|
153
|
-
|
|
174
|
+
elementSelected: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E2D\u0E07\u0E04\u0E4C\u0E1B\u0E23\u0E30\u0E01\u0E2D\u0E1A\u0E41\u0E25\u0E49\u0E27 \u2713",
|
|
175
|
+
elementCapturing: "\u0E04\u0E25\u0E34\u0E01\u0E2D\u0E07\u0E04\u0E4C\u0E1B\u0E23\u0E30\u0E01\u0E2D\u0E1A\u0E1A\u0E19\u0E2B\u0E19\u0E49\u0E32\u2026",
|
|
176
|
+
elementSelectorHint: "\u0E04\u0E25\u0E34\u0E01\u0E2D\u0E07\u0E04\u0E4C\u0E1B\u0E23\u0E30\u0E01\u0E2D\u0E1A\u0E43\u0E14\u0E01\u0E47\u0E44\u0E14\u0E49 \xB7 Esc \u0E40\u0E1E\u0E37\u0E48\u0E2D\u0E22\u0E01\u0E40\u0E25\u0E34\u0E01",
|
|
177
|
+
optional: "(\u0E44\u0E21\u0E48\u0E1A\u0E31\u0E07\u0E04\u0E31\u0E1A)",
|
|
178
|
+
tooShort: "\u0E01\u0E23\u0E38\u0E13\u0E32\u0E43\u0E2B\u0E49\u0E23\u0E32\u0E22\u0E25\u0E30\u0E40\u0E2D\u0E35\u0E22\u0E14\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E40\u0E15\u0E34\u0E21",
|
|
179
|
+
examplePrompts: [
|
|
180
|
+
"\u0E1B\u0E38\u0E48\u0E21\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E44\u0E21\u0E48\u0E17\u0E33\u0E07\u0E32\u0E19",
|
|
181
|
+
"\u0E2B\u0E19\u0E49\u0E32\u0E04\u0E49\u0E32\u0E07\u0E19\u0E32\u0E19 10 \u0E27\u0E34\u0E19\u0E32\u0E17\u0E35",
|
|
182
|
+
"\u0E40\u0E25\u0E22\u0E4C\u0E40\u0E2D\u0E32\u0E15\u0E4C\u0E1E\u0E31\u0E07"
|
|
183
|
+
]
|
|
154
184
|
}
|
|
155
185
|
};
|
|
156
186
|
|
|
@@ -197,18 +227,29 @@ var es = {
|
|
|
197
227
|
heading: "Cu\xE9ntanos m\xE1s",
|
|
198
228
|
descriptionPlaceholder: "Describe lo que pas\xF3\u2026",
|
|
199
229
|
screenshotButton: "Adjuntar captura",
|
|
200
|
-
screenshotAttached: "Captura adjunta",
|
|
230
|
+
screenshotAttached: "Captura adjunta \u2713",
|
|
231
|
+
screenshotCapturing: "Tomando captura\u2026",
|
|
232
|
+
screenshotFailed: "No se pudo capturar \u2014 descr\xEDbelo en su lugar",
|
|
201
233
|
elementButton: "Seleccionar elemento",
|
|
202
|
-
elementSelected: "Elemento seleccionado",
|
|
203
|
-
|
|
234
|
+
elementSelected: "Elemento seleccionado \u2713",
|
|
235
|
+
elementCapturing: "Haz clic en cualquier elemento\u2026",
|
|
236
|
+
elementSelectorHint: "Clic en cualquier elemento \xB7 Esc para cancelar",
|
|
237
|
+
optional: "(opcional)",
|
|
238
|
+
tooShort: "Un poco m\xE1s de detalle nos ayuda a resolverlo",
|
|
239
|
+
examplePrompts: [
|
|
240
|
+
"El bot\xF3n guardar no responde",
|
|
241
|
+
"La p\xE1gina se congel\xF3 10 segundos",
|
|
242
|
+
"El dise\xF1o se ve roto aqu\xED"
|
|
243
|
+
]
|
|
204
244
|
}
|
|
205
245
|
};
|
|
206
246
|
|
|
207
247
|
// src/i18n/index.ts
|
|
208
248
|
var locales = { en, ja, th, es };
|
|
209
249
|
function getLocale(code) {
|
|
210
|
-
|
|
211
|
-
|
|
250
|
+
const resolved = code && code !== "auto" ? code : typeof navigator !== "undefined" ? navigator.language ?? navigator.languages?.[0] : void 0;
|
|
251
|
+
if (!resolved) return en;
|
|
252
|
+
const base = resolved.split("-")[0].toLowerCase();
|
|
212
253
|
return locales[base] ?? en;
|
|
213
254
|
}
|
|
214
255
|
function getAvailableLocales() {
|
|
@@ -241,6 +282,7 @@ function getWidgetStyles(theme) {
|
|
|
241
282
|
-webkit-font-smoothing: antialiased;
|
|
242
283
|
-moz-osx-font-smoothing: grayscale;
|
|
243
284
|
font-feature-settings: 'ss01', 'cv11'; /* nicer system-ui glyphs where supported */
|
|
285
|
+
--mushi-ok: ${isDark ? "#4ade80" : "#16a34a"};
|
|
244
286
|
}
|
|
245
287
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
246
288
|
button { font-family: inherit; }
|
|
@@ -693,6 +735,51 @@ function getWidgetStyles(theme) {
|
|
|
693
735
|
box-shadow: inset 2px 0 0 ${vermillion};
|
|
694
736
|
}
|
|
695
737
|
|
|
738
|
+
/* Example starter chips \u2014 reduce first-report activation energy */
|
|
739
|
+
.mushi-example-chips {
|
|
740
|
+
display: flex;
|
|
741
|
+
flex-wrap: wrap;
|
|
742
|
+
gap: 6px;
|
|
743
|
+
margin-bottom: 10px;
|
|
744
|
+
}
|
|
745
|
+
.mushi-example-chip {
|
|
746
|
+
padding: 4px 10px;
|
|
747
|
+
border: 1px solid ${rule};
|
|
748
|
+
border-radius: 12px;
|
|
749
|
+
background: transparent;
|
|
750
|
+
color: ${inkMuted};
|
|
751
|
+
font-family: ${fontBody};
|
|
752
|
+
font-size: 11px;
|
|
753
|
+
cursor: pointer;
|
|
754
|
+
transition: color 150ms ${easeStamp}, border-color 150ms ${easeStamp}, background 150ms ${easeStamp};
|
|
755
|
+
white-space: nowrap;
|
|
756
|
+
}
|
|
757
|
+
.mushi-example-chip:hover {
|
|
758
|
+
color: ${ink};
|
|
759
|
+
border-color: ${inkMuted};
|
|
760
|
+
background: ${isDark ? "rgba(242,235,221,0.06)" : "rgba(14,13,11,0.04)"};
|
|
761
|
+
}
|
|
762
|
+
.mushi-example-chip:focus-visible {
|
|
763
|
+
outline: 2px solid ${vermillion};
|
|
764
|
+
outline-offset: 2px;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/* Textarea wrapper to position char counter */
|
|
768
|
+
.mushi-textarea-wrap {
|
|
769
|
+
position: relative;
|
|
770
|
+
}
|
|
771
|
+
.mushi-char-counter {
|
|
772
|
+
position: absolute;
|
|
773
|
+
bottom: 4px;
|
|
774
|
+
right: 0;
|
|
775
|
+
font-family: ${fontMono};
|
|
776
|
+
font-size: 10px;
|
|
777
|
+
letter-spacing: 0.04em;
|
|
778
|
+
color: ${inkFaint};
|
|
779
|
+
pointer-events: none;
|
|
780
|
+
transition: color 200ms ${easeStamp};
|
|
781
|
+
}
|
|
782
|
+
|
|
696
783
|
.mushi-textarea {
|
|
697
784
|
width: 100%;
|
|
698
785
|
min-height: 96px;
|
|
@@ -755,10 +842,34 @@ function getWidgetStyles(theme) {
|
|
|
755
842
|
border-color: ${vermillion};
|
|
756
843
|
background: ${vermillionWash};
|
|
757
844
|
}
|
|
845
|
+
.mushi-attach-btn.loading {
|
|
846
|
+
opacity: 0.7;
|
|
847
|
+
cursor: wait;
|
|
848
|
+
}
|
|
849
|
+
.mushi-attach-btn.error {
|
|
850
|
+
color: ${vermillion};
|
|
851
|
+
border-color: ${vermillionWash};
|
|
852
|
+
}
|
|
758
853
|
.mushi-attach-btn:focus-visible {
|
|
759
854
|
outline: 2px solid ${vermillion};
|
|
760
855
|
outline-offset: 2px;
|
|
761
856
|
}
|
|
857
|
+
@keyframes mushi-spin {
|
|
858
|
+
to { transform: rotate(360deg); }
|
|
859
|
+
}
|
|
860
|
+
@keyframes mushi-fade-in {
|
|
861
|
+
from { opacity: 0; transform: translateY(4px); }
|
|
862
|
+
to { opacity: 1; transform: translateY(0); }
|
|
863
|
+
}
|
|
864
|
+
.mushi-spinner {
|
|
865
|
+
display: inline-block;
|
|
866
|
+
width: 10px;
|
|
867
|
+
height: 10px;
|
|
868
|
+
border: 1.5px solid currentColor;
|
|
869
|
+
border-top-color: transparent;
|
|
870
|
+
border-radius: 50%;
|
|
871
|
+
animation: mushi-spin 0.7s linear infinite;
|
|
872
|
+
}
|
|
762
873
|
|
|
763
874
|
.mushi-footer {
|
|
764
875
|
padding: 14px 22px 16px;
|
|
@@ -1232,7 +1343,8 @@ var MushiWidget = class {
|
|
|
1232
1343
|
draggable: config.draggable ?? false,
|
|
1233
1344
|
brandFooter: config.brandFooter ?? true,
|
|
1234
1345
|
outdatedBanner: config.outdatedBanner ?? "auto",
|
|
1235
|
-
betaMode: config.betaMode ?? {}
|
|
1346
|
+
betaMode: config.betaMode ?? {},
|
|
1347
|
+
minDescriptionLength: config.minDescriptionLength ?? 20
|
|
1236
1348
|
};
|
|
1237
1349
|
this.callbacks = callbacks;
|
|
1238
1350
|
this.locale = getLocale(this.config.locale === "auto" ? void 0 : this.config.locale);
|
|
@@ -1251,12 +1363,22 @@ var MushiWidget = class {
|
|
|
1251
1363
|
selectedCategory = null;
|
|
1252
1364
|
selectedIntent = null;
|
|
1253
1365
|
screenshotAttached = false;
|
|
1366
|
+
screenshotCapturing = false;
|
|
1367
|
+
screenshotError = false;
|
|
1254
1368
|
allowScreenshotRemove = true;
|
|
1255
1369
|
elementSelected = false;
|
|
1370
|
+
elementCapturing = false;
|
|
1256
1371
|
submitting = false;
|
|
1372
|
+
/** Hint element injected outside the shadow DOM during element selection. */
|
|
1373
|
+
selectorHint = null;
|
|
1257
1374
|
triggerVisible = true;
|
|
1258
1375
|
triggerShrunk = false;
|
|
1259
1376
|
triggerHiddenByScroll = false;
|
|
1377
|
+
/** Milliseconds since mount — used for the 30s first-time nudge gate. */
|
|
1378
|
+
mountedAt = null;
|
|
1379
|
+
nudgeShown = false;
|
|
1380
|
+
nudgeEl = null;
|
|
1381
|
+
nudgeTimer = null;
|
|
1260
1382
|
sdkFreshness = null;
|
|
1261
1383
|
reporterReports = [];
|
|
1262
1384
|
reporterComments = [];
|
|
@@ -1283,6 +1405,7 @@ var MushiWidget = class {
|
|
|
1283
1405
|
this.syncAttachedLaunchers();
|
|
1284
1406
|
this.syncSmartHide();
|
|
1285
1407
|
this.render();
|
|
1408
|
+
this.mountedAt = Date.now();
|
|
1286
1409
|
}
|
|
1287
1410
|
getIsMounted() {
|
|
1288
1411
|
return this.host.isConnected;
|
|
@@ -1309,7 +1432,8 @@ var MushiWidget = class {
|
|
|
1309
1432
|
...config.draggable !== void 0 ? { draggable: config.draggable } : {},
|
|
1310
1433
|
...config.brandFooter !== void 0 ? { brandFooter: config.brandFooter } : {},
|
|
1311
1434
|
...config.outdatedBanner !== void 0 ? { outdatedBanner: config.outdatedBanner } : {},
|
|
1312
|
-
...config.betaMode !== void 0 ? { betaMode: config.betaMode } : {}
|
|
1435
|
+
...config.betaMode !== void 0 ? { betaMode: config.betaMode } : {},
|
|
1436
|
+
...config.minDescriptionLength !== void 0 ? { minDescriptionLength: config.minDescriptionLength } : {}
|
|
1313
1437
|
};
|
|
1314
1438
|
this.locale = getLocale(this.config.locale === "auto" ? void 0 : this.config.locale);
|
|
1315
1439
|
this.syncAttachedLaunchers();
|
|
@@ -1320,9 +1444,13 @@ var MushiWidget = class {
|
|
|
1320
1444
|
if (this.isOpen) return;
|
|
1321
1445
|
this.isOpen = true;
|
|
1322
1446
|
this.screenshotAttached = false;
|
|
1447
|
+
this.screenshotCapturing = false;
|
|
1448
|
+
this.screenshotError = false;
|
|
1323
1449
|
this.elementSelected = false;
|
|
1450
|
+
this.elementCapturing = false;
|
|
1324
1451
|
this.submitting = false;
|
|
1325
1452
|
this.submittedAt = null;
|
|
1453
|
+
this.removeSelectorHint();
|
|
1326
1454
|
if (options?.category) {
|
|
1327
1455
|
this.selectedCategory = options.category;
|
|
1328
1456
|
this.selectedIntent = null;
|
|
@@ -1378,8 +1506,114 @@ var MushiWidget = class {
|
|
|
1378
1506
|
}
|
|
1379
1507
|
setElementSelected(selected) {
|
|
1380
1508
|
this.elementSelected = selected;
|
|
1509
|
+
this.elementCapturing = false;
|
|
1510
|
+
this.removeSelectorHint();
|
|
1511
|
+
if (this.isOpen) this.render();
|
|
1512
|
+
}
|
|
1513
|
+
setScreenshotCapturing(capturing) {
|
|
1514
|
+
this.screenshotCapturing = capturing;
|
|
1515
|
+
this.screenshotError = false;
|
|
1516
|
+
if (this.isOpen) this.render();
|
|
1517
|
+
}
|
|
1518
|
+
setScreenshotError(failed) {
|
|
1519
|
+
this.screenshotError = failed;
|
|
1520
|
+
this.screenshotCapturing = false;
|
|
1521
|
+
if (this.isOpen) this.render();
|
|
1522
|
+
}
|
|
1523
|
+
setElementCapturing(capturing) {
|
|
1524
|
+
this.elementCapturing = capturing;
|
|
1525
|
+
if (capturing) {
|
|
1526
|
+
this.showSelectorHint();
|
|
1527
|
+
} else {
|
|
1528
|
+
this.removeSelectorHint();
|
|
1529
|
+
}
|
|
1381
1530
|
if (this.isOpen) this.render();
|
|
1382
1531
|
}
|
|
1532
|
+
/** Hide the widget panel (but keep the host element) during element selection
|
|
1533
|
+
* so the user can click any element on the page without the panel
|
|
1534
|
+
* intercepting the event. */
|
|
1535
|
+
hidePanel() {
|
|
1536
|
+
const panel = this.shadow.querySelector(".mushi-panel");
|
|
1537
|
+
if (panel) panel.style.display = "none";
|
|
1538
|
+
}
|
|
1539
|
+
showPanel() {
|
|
1540
|
+
const panel = this.shadow.querySelector(".mushi-panel");
|
|
1541
|
+
if (panel) panel.style.display = "";
|
|
1542
|
+
}
|
|
1543
|
+
showSelectorHint() {
|
|
1544
|
+
this.removeSelectorHint();
|
|
1545
|
+
const hint = document.createElement("div");
|
|
1546
|
+
hint.id = "mushi-selector-hint";
|
|
1547
|
+
hint.setAttribute("role", "status");
|
|
1548
|
+
hint.setAttribute("aria-live", "polite");
|
|
1549
|
+
hint.style.cssText = `
|
|
1550
|
+
position: fixed;
|
|
1551
|
+
bottom: 24px;
|
|
1552
|
+
left: 50%;
|
|
1553
|
+
transform: translateX(-50%);
|
|
1554
|
+
z-index: 2147483646;
|
|
1555
|
+
background: rgba(17,17,17,0.92);
|
|
1556
|
+
color: #fff;
|
|
1557
|
+
font-family: ui-monospace, SFMono-Regular, monospace;
|
|
1558
|
+
font-size: 12px;
|
|
1559
|
+
letter-spacing: 0.04em;
|
|
1560
|
+
padding: 8px 16px;
|
|
1561
|
+
border-radius: 20px;
|
|
1562
|
+
pointer-events: none;
|
|
1563
|
+
white-space: nowrap;
|
|
1564
|
+
backdrop-filter: blur(4px);
|
|
1565
|
+
box-shadow: 0 2px 12px rgba(0,0,0,0.35);
|
|
1566
|
+
`;
|
|
1567
|
+
hint.textContent = this.locale.step3.elementSelectorHint;
|
|
1568
|
+
document.body.appendChild(hint);
|
|
1569
|
+
this.selectorHint = hint;
|
|
1570
|
+
}
|
|
1571
|
+
removeSelectorHint() {
|
|
1572
|
+
this.selectorHint?.remove();
|
|
1573
|
+
this.selectorHint = null;
|
|
1574
|
+
document.getElementById("mushi-selector-hint")?.remove();
|
|
1575
|
+
}
|
|
1576
|
+
showNudge() {
|
|
1577
|
+
if (this.nudgeShown || this.nudgeEl) return;
|
|
1578
|
+
this.nudgeShown = true;
|
|
1579
|
+
const trigger = this.shadow.querySelector(".mushi-trigger");
|
|
1580
|
+
const rect = trigger?.getBoundingClientRect();
|
|
1581
|
+
const nudge = document.createElement("div");
|
|
1582
|
+
nudge.id = "mushi-nudge-bubble";
|
|
1583
|
+
nudge.setAttribute("role", "tooltip");
|
|
1584
|
+
const isRight = this.config.position.includes("right");
|
|
1585
|
+
nudge.style.cssText = `
|
|
1586
|
+
position: fixed;
|
|
1587
|
+
z-index: 2147483645;
|
|
1588
|
+
${rect ? `bottom: ${window.innerHeight - rect.top + 8}px; ${isRight ? `right: ${window.innerWidth - rect.right}px;` : `left: ${rect.left}px;`}` : "bottom: 80px; right: 24px;"}
|
|
1589
|
+
background: rgba(17,17,17,0.92);
|
|
1590
|
+
color: #fff;
|
|
1591
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
1592
|
+
font-size: 12px;
|
|
1593
|
+
line-height: 1.4;
|
|
1594
|
+
padding: 8px 12px;
|
|
1595
|
+
border-radius: 8px;
|
|
1596
|
+
max-width: 200px;
|
|
1597
|
+
pointer-events: none;
|
|
1598
|
+
backdrop-filter: blur(4px);
|
|
1599
|
+
box-shadow: 0 2px 12px rgba(0,0,0,0.35);
|
|
1600
|
+
animation: mushi-fade-in 0.15s ease forwards;
|
|
1601
|
+
`;
|
|
1602
|
+
nudge.textContent = this.locale.step3.tooShort.startsWith("A bit") ? "Found a bug? One sentence is enough \u{1F41B}" : "\u30D0\u30B0\u3092\u898B\u3064\u3051\u305F\uFF1F\u4E00\u884C\u3067\u5927\u4E08\u592B\u3067\u3059 \u{1F41B}";
|
|
1603
|
+
document.body.appendChild(nudge);
|
|
1604
|
+
this.nudgeEl = nudge;
|
|
1605
|
+
if (this.nudgeTimer !== null) clearTimeout(this.nudgeTimer);
|
|
1606
|
+
this.nudgeTimer = setTimeout(() => this.removeNudge(), 5e3);
|
|
1607
|
+
}
|
|
1608
|
+
removeNudge() {
|
|
1609
|
+
if (this.nudgeTimer !== null) {
|
|
1610
|
+
clearTimeout(this.nudgeTimer);
|
|
1611
|
+
this.nudgeTimer = null;
|
|
1612
|
+
}
|
|
1613
|
+
this.nudgeEl?.remove();
|
|
1614
|
+
this.nudgeEl = null;
|
|
1615
|
+
document.getElementById("mushi-nudge-bubble")?.remove();
|
|
1616
|
+
}
|
|
1383
1617
|
setSdkFreshness(info) {
|
|
1384
1618
|
this.sdkFreshness = info;
|
|
1385
1619
|
if (this.isOpen) this.render();
|
|
@@ -1405,6 +1639,8 @@ var MushiWidget = class {
|
|
|
1405
1639
|
this.smartHideCleanup = null;
|
|
1406
1640
|
this.attachedLaunchers.forEach((cleanup) => cleanup());
|
|
1407
1641
|
this.attachedLaunchers = [];
|
|
1642
|
+
this.removeSelectorHint();
|
|
1643
|
+
this.removeNudge();
|
|
1408
1644
|
this.host.remove();
|
|
1409
1645
|
}
|
|
1410
1646
|
syncAttachedLaunchers() {
|
|
@@ -1500,9 +1736,22 @@ var MushiWidget = class {
|
|
|
1500
1736
|
trigger.style.zIndex = String(this.config.zIndex);
|
|
1501
1737
|
this.applyInsetVars(trigger);
|
|
1502
1738
|
trigger.addEventListener("click", () => {
|
|
1739
|
+
this.removeNudge();
|
|
1503
1740
|
if (this.isOpen) this.close();
|
|
1504
1741
|
else this.open();
|
|
1505
1742
|
});
|
|
1743
|
+
trigger.addEventListener("mouseenter", () => {
|
|
1744
|
+
const onPageMs = this.mountedAt ? Date.now() - this.mountedAt : 0;
|
|
1745
|
+
if (!this.nudgeShown && !this.isOpen && onPageMs >= 3e4) {
|
|
1746
|
+
this.showNudge();
|
|
1747
|
+
}
|
|
1748
|
+
});
|
|
1749
|
+
trigger.addEventListener("mouseleave", () => {
|
|
1750
|
+
if (this.nudgeEl) {
|
|
1751
|
+
if (this.nudgeTimer !== null) clearTimeout(this.nudgeTimer);
|
|
1752
|
+
this.nudgeTimer = setTimeout(() => this.removeNudge(), 2e3);
|
|
1753
|
+
}
|
|
1754
|
+
});
|
|
1506
1755
|
this.shadow.appendChild(trigger);
|
|
1507
1756
|
}
|
|
1508
1757
|
const panel = document.createElement("div");
|
|
@@ -1754,25 +2003,62 @@ var MushiWidget = class {
|
|
|
1754
2003
|
${this.renderStepIndicator(STEP_NUMBER.intent)}
|
|
1755
2004
|
`;
|
|
1756
2005
|
}
|
|
2006
|
+
effectiveMinLength() {
|
|
2007
|
+
const base = this.config.minDescriptionLength ?? 20;
|
|
2008
|
+
const lang = this.config.locale === "auto" ? typeof navigator !== "undefined" ? navigator.language ?? "" : "" : this.config.locale ?? "";
|
|
2009
|
+
const isCjk = /^(ja|zh|ko)/i.test(lang);
|
|
2010
|
+
return isCjk ? Math.max(4, Math.floor(base / 2)) : base;
|
|
2011
|
+
}
|
|
1757
2012
|
renderDetailsStep() {
|
|
1758
2013
|
const t = this.locale;
|
|
2014
|
+
const minLen = this.effectiveMinLength();
|
|
2015
|
+
const screenshotLabel = this.screenshotCapturing ? t.step3.screenshotCapturing : this.screenshotError ? t.step3.screenshotFailed : this.screenshotAttached ? t.step3.screenshotAttached : t.step3.screenshotButton;
|
|
2016
|
+
const screenshotClass = [
|
|
2017
|
+
"mushi-attach-btn",
|
|
2018
|
+
this.screenshotAttached ? "active" : "",
|
|
2019
|
+
this.screenshotError ? "error" : "",
|
|
2020
|
+
this.screenshotCapturing ? "loading" : ""
|
|
2021
|
+
].filter(Boolean).join(" ");
|
|
2022
|
+
const elementLabel = this.elementCapturing ? t.step3.elementCapturing : this.elementSelected ? t.step3.elementSelected : t.step3.elementButton;
|
|
2023
|
+
const elementClass = [
|
|
2024
|
+
"mushi-attach-btn",
|
|
2025
|
+
this.elementSelected ? "active" : "",
|
|
2026
|
+
this.elementCapturing ? "loading" : ""
|
|
2027
|
+
].filter(Boolean).join(" ");
|
|
2028
|
+
const exampleChips = t.step3.examplePrompts.map((p) => `<button type="button" class="mushi-example-chip" data-example="${escapeHtml(p)}">${escapeHtml(p)}</button>`).join("");
|
|
1759
2029
|
return `
|
|
1760
2030
|
${this.renderHeader({ title: t.step3.heading, showBack: true, step: STEP_NUMBER.details })}
|
|
1761
2031
|
<div class="mushi-body">
|
|
1762
|
-
<
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
2032
|
+
<div class="mushi-example-chips" aria-label="Example prompts">${exampleChips}</div>
|
|
2033
|
+
<div class="mushi-textarea-wrap">
|
|
2034
|
+
<textarea
|
|
2035
|
+
class="mushi-textarea"
|
|
2036
|
+
placeholder="${t.step3.descriptionPlaceholder}"
|
|
2037
|
+
rows="4"
|
|
2038
|
+
aria-label="${t.step3.heading}"
|
|
2039
|
+
autofocus
|
|
2040
|
+
></textarea>
|
|
2041
|
+
<div class="mushi-char-counter" data-role="char-counter" aria-hidden="true">
|
|
2042
|
+
<span data-role="char-current">0</span>/<span data-role="char-min">${minLen}</span>
|
|
2043
|
+
</div>
|
|
2044
|
+
</div>
|
|
1769
2045
|
<div class="mushi-attachments">
|
|
1770
|
-
<button type="button" class="
|
|
1771
|
-
|
|
2046
|
+
<button type="button" class="${screenshotClass}"
|
|
2047
|
+
data-action="screenshot"
|
|
2048
|
+
${this.screenshotCapturing ? "disabled" : ""}
|
|
2049
|
+
aria-label="${escapeHtml(screenshotLabel)}"
|
|
2050
|
+
>
|
|
2051
|
+
${this.screenshotCapturing ? '<span class="mushi-spinner" aria-hidden="true"></span>' : "\u{1F4F8}"}
|
|
2052
|
+
${escapeHtml(screenshotLabel)}
|
|
1772
2053
|
</button>
|
|
1773
|
-
${this.screenshotAttached && this.allowScreenshotRemove ? '<button type="button" class="mushi-attach-btn danger" data-action="remove-screenshot">\u2715 Remove
|
|
1774
|
-
<button type="button" class="
|
|
1775
|
-
|
|
2054
|
+
${this.screenshotAttached && this.allowScreenshotRemove ? '<button type="button" class="mushi-attach-btn danger" data-action="remove-screenshot" aria-label="Remove screenshot">\u2715 Remove</button>' : ""}
|
|
2055
|
+
<button type="button" class="${elementClass}"
|
|
2056
|
+
data-action="element"
|
|
2057
|
+
${this.elementCapturing ? "disabled" : ""}
|
|
2058
|
+
aria-label="${escapeHtml(elementLabel)}"
|
|
2059
|
+
>
|
|
2060
|
+
${this.elementCapturing ? '<span class="mushi-spinner" aria-hidden="true"></span>' : "\u{1F3AF}"}
|
|
2061
|
+
${escapeHtml(elementLabel)}
|
|
1776
2062
|
</button>
|
|
1777
2063
|
</div>
|
|
1778
2064
|
<div class="mushi-error" style="display:none" role="alert"></div>
|
|
@@ -1942,6 +2228,30 @@ var MushiWidget = class {
|
|
|
1942
2228
|
this.render();
|
|
1943
2229
|
});
|
|
1944
2230
|
});
|
|
2231
|
+
const textarea = panel.querySelector(".mushi-textarea");
|
|
2232
|
+
const charCurrentEl = panel.querySelector('[data-role="char-current"]');
|
|
2233
|
+
if (textarea && charCurrentEl) {
|
|
2234
|
+
const minLen = this.effectiveMinLength();
|
|
2235
|
+
const updateCounter = () => {
|
|
2236
|
+
const len = textarea.value.trim().length;
|
|
2237
|
+
charCurrentEl.textContent = String(len);
|
|
2238
|
+
const counterEl = panel.querySelector('[data-role="char-counter"]');
|
|
2239
|
+
if (counterEl) {
|
|
2240
|
+
counterEl.style.color = len >= minLen ? "var(--mushi-ok, #22c55e)" : "";
|
|
2241
|
+
}
|
|
2242
|
+
};
|
|
2243
|
+
textarea.addEventListener("input", updateCounter);
|
|
2244
|
+
}
|
|
2245
|
+
panel.querySelectorAll("[data-example]").forEach((chip) => {
|
|
2246
|
+
chip.addEventListener("click", () => {
|
|
2247
|
+
const example = chip.dataset.example ?? "";
|
|
2248
|
+
if (textarea) {
|
|
2249
|
+
textarea.value = example;
|
|
2250
|
+
textarea.focus();
|
|
2251
|
+
textarea.dispatchEvent(new Event("input"));
|
|
2252
|
+
}
|
|
2253
|
+
});
|
|
2254
|
+
});
|
|
1945
2255
|
panel.querySelector('[data-action="screenshot"]')?.addEventListener("click", () => {
|
|
1946
2256
|
this.callbacks.onScreenshotRequest();
|
|
1947
2257
|
});
|
|
@@ -1952,14 +2262,16 @@ var MushiWidget = class {
|
|
|
1952
2262
|
this.callbacks.onElementSelectorRequest?.();
|
|
1953
2263
|
});
|
|
1954
2264
|
const submitReport = () => {
|
|
1955
|
-
const
|
|
1956
|
-
const description =
|
|
2265
|
+
const textarea2 = panel.querySelector(".mushi-textarea");
|
|
2266
|
+
const description = textarea2?.value?.trim() ?? "";
|
|
1957
2267
|
const errorEl = panel.querySelector(".mushi-error");
|
|
1958
|
-
const
|
|
1959
|
-
if (description.length <
|
|
2268
|
+
const minLen = this.effectiveMinLength();
|
|
2269
|
+
if (description.length < minLen) {
|
|
1960
2270
|
if (errorEl) {
|
|
1961
|
-
|
|
2271
|
+
const msg = `${t.step3.tooShort} (${description.length}/${minLen})`;
|
|
2272
|
+
errorEl.textContent = msg;
|
|
1962
2273
|
errorEl.style.display = "block";
|
|
2274
|
+
textarea2?.focus();
|
|
1963
2275
|
}
|
|
1964
2276
|
return;
|
|
1965
2277
|
}
|
|
@@ -2056,8 +2368,81 @@ var MushiWidget = class {
|
|
|
2056
2368
|
this.render();
|
|
2057
2369
|
}
|
|
2058
2370
|
}
|
|
2371
|
+
/* ── Marketing / Playwright recorder (debug GIF capture) ─────────── */
|
|
2372
|
+
getRecorderStep() {
|
|
2373
|
+
return this.step;
|
|
2374
|
+
}
|
|
2375
|
+
getRecorderTrigger() {
|
|
2376
|
+
return this.shadow.querySelector(".mushi-trigger");
|
|
2377
|
+
}
|
|
2378
|
+
getRecorderCategoryButton(category) {
|
|
2379
|
+
return this.shadow.querySelector(`[data-category="${category}"]`);
|
|
2380
|
+
}
|
|
2381
|
+
getRecorderIntentButton(label) {
|
|
2382
|
+
return Array.from(this.shadow.querySelectorAll("[data-intent]")).find(
|
|
2383
|
+
(el) => el.dataset.intent === label
|
|
2384
|
+
) ?? null;
|
|
2385
|
+
}
|
|
2386
|
+
getRecorderSubmitButton() {
|
|
2387
|
+
return this.shadow.querySelector('[data-action="submit"]');
|
|
2388
|
+
}
|
|
2389
|
+
recorderClickTrigger() {
|
|
2390
|
+
if (this.isOpen) this.close();
|
|
2391
|
+
this.open();
|
|
2392
|
+
}
|
|
2393
|
+
recorderSelectCategory(category) {
|
|
2394
|
+
if (!this.isOpen) this.open();
|
|
2395
|
+
if (this.step !== "category") {
|
|
2396
|
+
this.selectedCategory = null;
|
|
2397
|
+
this.selectedIntent = null;
|
|
2398
|
+
this.step = "category";
|
|
2399
|
+
this.render();
|
|
2400
|
+
}
|
|
2401
|
+
this.selectedCategory = category;
|
|
2402
|
+
this.step = "intent";
|
|
2403
|
+
this.render();
|
|
2404
|
+
}
|
|
2405
|
+
recorderSelectIntent(label) {
|
|
2406
|
+
if (!this.isOpen || this.step !== "intent") return;
|
|
2407
|
+
this.selectedIntent = label;
|
|
2408
|
+
this.step = "details";
|
|
2409
|
+
this.render();
|
|
2410
|
+
}
|
|
2411
|
+
recorderFocusDescription() {
|
|
2412
|
+
const textarea = this.shadow.querySelector(".mushi-textarea");
|
|
2413
|
+
textarea?.focus();
|
|
2414
|
+
}
|
|
2415
|
+
recorderSubmit() {
|
|
2416
|
+
const submit = this.shadow.querySelector('[data-action="submit"]');
|
|
2417
|
+
submit?.click();
|
|
2418
|
+
}
|
|
2059
2419
|
};
|
|
2060
2420
|
|
|
2421
|
+
// src/marketing-recorder.ts
|
|
2422
|
+
function centerOf(el) {
|
|
2423
|
+
if (!el) return null;
|
|
2424
|
+
const r = el.getBoundingClientRect();
|
|
2425
|
+
if (r.width === 0 && r.height === 0) return null;
|
|
2426
|
+
return { x: r.left + r.width / 2, y: r.top + r.height / 2 };
|
|
2427
|
+
}
|
|
2428
|
+
function exposeMarketingRecorder(widget) {
|
|
2429
|
+
if (typeof globalThis === "undefined") return;
|
|
2430
|
+
const api = {
|
|
2431
|
+
ready: () => widget.getIsMounted(),
|
|
2432
|
+
getStep: () => widget.getRecorderStep(),
|
|
2433
|
+
getTriggerCenter: () => centerOf(widget.getRecorderTrigger()),
|
|
2434
|
+
getCategoryCenter: (category) => centerOf(widget.getRecorderCategoryButton(category)),
|
|
2435
|
+
getIntentCenter: (label) => centerOf(widget.getRecorderIntentButton(label)),
|
|
2436
|
+
getSubmitCenter: () => centerOf(widget.getRecorderSubmitButton()),
|
|
2437
|
+
clickTrigger: () => widget.recorderClickTrigger(),
|
|
2438
|
+
selectCategory: (category) => widget.recorderSelectCategory(category),
|
|
2439
|
+
selectIntent: (label) => widget.recorderSelectIntent(label),
|
|
2440
|
+
focusDescription: () => widget.recorderFocusDescription(),
|
|
2441
|
+
submit: () => widget.recorderSubmit()
|
|
2442
|
+
};
|
|
2443
|
+
globalThis.__mushiRecorder = api;
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2061
2446
|
// src/rewards.ts
|
|
2062
2447
|
var MIN_FLUSH_INTERVAL = 3e4;
|
|
2063
2448
|
var DEFAULT_FLUSH_INTERVAL = 3e5;
|
|
@@ -2549,50 +2934,16 @@ function truncateUrl(url) {
|
|
|
2549
2934
|
function createScreenshotCapture(options = {}) {
|
|
2550
2935
|
let activeOptions = options;
|
|
2551
2936
|
async function take() {
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
const
|
|
2559
|
-
|
|
2560
|
-
canvas.width = width * dpr;
|
|
2561
|
-
canvas.height = height * dpr;
|
|
2562
|
-
ctx.scale(dpr, dpr);
|
|
2563
|
-
const safeDocument = buildPrivacySafeDocument(activeOptions.privacy);
|
|
2564
|
-
const svgData = `
|
|
2565
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
|
|
2566
|
-
<foreignObject width="100%" height="100%">
|
|
2567
|
-
<div xmlns="http://www.w3.org/1999/xhtml">
|
|
2568
|
-
${new XMLSerializer().serializeToString(safeDocument)}
|
|
2569
|
-
</div>
|
|
2570
|
-
</foreignObject>
|
|
2571
|
-
</svg>
|
|
2572
|
-
`;
|
|
2573
|
-
const img = new Image();
|
|
2574
|
-
const blob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" });
|
|
2575
|
-
const url = URL.createObjectURL(blob);
|
|
2576
|
-
return new Promise((resolve) => {
|
|
2577
|
-
img.onload = () => {
|
|
2578
|
-
ctx.drawImage(img, 0, 0, width, height);
|
|
2579
|
-
URL.revokeObjectURL(url);
|
|
2580
|
-
try {
|
|
2581
|
-
const dataUrl = canvas.toDataURL("image/jpeg", 0.7);
|
|
2582
|
-
resolve(dataUrl);
|
|
2583
|
-
} catch {
|
|
2584
|
-
resolve(null);
|
|
2585
|
-
}
|
|
2586
|
-
};
|
|
2587
|
-
img.onerror = () => {
|
|
2588
|
-
URL.revokeObjectURL(url);
|
|
2589
|
-
resolve(null);
|
|
2590
|
-
};
|
|
2591
|
-
img.src = url;
|
|
2592
|
-
});
|
|
2593
|
-
} catch {
|
|
2594
|
-
return null;
|
|
2937
|
+
if (typeof document === "undefined") {
|
|
2938
|
+
return { ok: false, reason: "unsupported", message: "Not in a browser context" };
|
|
2939
|
+
}
|
|
2940
|
+
const svgResult = await trySvgCapture(activeOptions.privacy);
|
|
2941
|
+
if (svgResult.ok) return svgResult;
|
|
2942
|
+
if (svgResult.reason !== "unsupported") {
|
|
2943
|
+
const mediaResult = await tryDisplayMediaCapture();
|
|
2944
|
+
if (mediaResult.ok) return mediaResult;
|
|
2595
2945
|
}
|
|
2946
|
+
return svgResult;
|
|
2596
2947
|
}
|
|
2597
2948
|
return {
|
|
2598
2949
|
take,
|
|
@@ -2601,8 +2952,110 @@ function createScreenshotCapture(options = {}) {
|
|
|
2601
2952
|
}
|
|
2602
2953
|
};
|
|
2603
2954
|
}
|
|
2955
|
+
async function trySvgCapture(privacy) {
|
|
2956
|
+
try {
|
|
2957
|
+
const canvas = document.createElement("canvas");
|
|
2958
|
+
const ctx = canvas.getContext("2d");
|
|
2959
|
+
if (!ctx) return { ok: false, reason: "unsupported", message: "Canvas 2d context unavailable" };
|
|
2960
|
+
const width = window.innerWidth;
|
|
2961
|
+
const height = window.innerHeight;
|
|
2962
|
+
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
|
2963
|
+
canvas.width = width * dpr;
|
|
2964
|
+
canvas.height = height * dpr;
|
|
2965
|
+
ctx.scale(dpr, dpr);
|
|
2966
|
+
const safeDocument = buildPrivacySafeDocument(privacy);
|
|
2967
|
+
const svgData = `
|
|
2968
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
|
|
2969
|
+
<foreignObject width="100%" height="100%">
|
|
2970
|
+
<div xmlns="http://www.w3.org/1999/xhtml">
|
|
2971
|
+
${new XMLSerializer().serializeToString(safeDocument)}
|
|
2972
|
+
</div>
|
|
2973
|
+
</foreignObject>
|
|
2974
|
+
</svg>
|
|
2975
|
+
`;
|
|
2976
|
+
const img = new Image();
|
|
2977
|
+
const blob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" });
|
|
2978
|
+
const url = URL.createObjectURL(blob);
|
|
2979
|
+
const loadResult = await new Promise((resolve) => {
|
|
2980
|
+
img.onload = () => resolve("loaded");
|
|
2981
|
+
img.onerror = () => resolve("error");
|
|
2982
|
+
const timeout = setTimeout(() => resolve("error"), 5e3);
|
|
2983
|
+
img.onload = () => {
|
|
2984
|
+
clearTimeout(timeout);
|
|
2985
|
+
resolve("loaded");
|
|
2986
|
+
};
|
|
2987
|
+
});
|
|
2988
|
+
URL.revokeObjectURL(url);
|
|
2989
|
+
if (loadResult === "error") {
|
|
2990
|
+
return { ok: false, reason: "load-error", message: "SVG image load failed" };
|
|
2991
|
+
}
|
|
2992
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
2993
|
+
try {
|
|
2994
|
+
const dataUrl = canvas.toDataURL("image/jpeg", 0.75);
|
|
2995
|
+
return { ok: true, dataUrl };
|
|
2996
|
+
} catch (err) {
|
|
2997
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2998
|
+
return { ok: false, reason: "tainted", message };
|
|
2999
|
+
}
|
|
3000
|
+
} catch (err) {
|
|
3001
|
+
return { ok: false, reason: "error", message: err instanceof Error ? err.message : String(err) };
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
async function tryDisplayMediaCapture() {
|
|
3005
|
+
if (typeof navigator === "undefined" || !("mediaDevices" in navigator)) {
|
|
3006
|
+
return { ok: false, reason: "unsupported", message: "mediaDevices not available" };
|
|
3007
|
+
}
|
|
3008
|
+
const mediaDevices = navigator.mediaDevices;
|
|
3009
|
+
if (typeof mediaDevices.getDisplayMedia !== "function") {
|
|
3010
|
+
return { ok: false, reason: "unsupported", message: "getDisplayMedia not available" };
|
|
3011
|
+
}
|
|
3012
|
+
let stream = null;
|
|
3013
|
+
try {
|
|
3014
|
+
stream = await mediaDevices.getDisplayMedia({
|
|
3015
|
+
video: { displaySurface: "browser" },
|
|
3016
|
+
audio: false
|
|
3017
|
+
});
|
|
3018
|
+
const track = stream.getVideoTracks()[0];
|
|
3019
|
+
if (!track) return { ok: false, reason: "error", message: "No video track" };
|
|
3020
|
+
const imageCapture = new window.ImageCapture(track);
|
|
3021
|
+
const bitmap = await imageCapture.grabFrame();
|
|
3022
|
+
const canvas = document.createElement("canvas");
|
|
3023
|
+
canvas.width = bitmap.width;
|
|
3024
|
+
canvas.height = bitmap.height;
|
|
3025
|
+
const ctx = canvas.getContext("2d");
|
|
3026
|
+
ctx.drawImage(bitmap, 0, 0);
|
|
3027
|
+
bitmap.close();
|
|
3028
|
+
const dataUrl = canvas.toDataURL("image/jpeg", 0.85);
|
|
3029
|
+
return { ok: true, dataUrl };
|
|
3030
|
+
} catch (err) {
|
|
3031
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3032
|
+
if (err instanceof Error && err.name === "NotAllowedError") {
|
|
3033
|
+
return { ok: false, reason: "cancelled", message };
|
|
3034
|
+
}
|
|
3035
|
+
return { ok: false, reason: "error", message };
|
|
3036
|
+
} finally {
|
|
3037
|
+
stream?.getTracks().forEach((t) => t.stop());
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
2604
3040
|
function buildPrivacySafeDocument(privacy) {
|
|
2605
3041
|
const clone = document.documentElement.cloneNode(true);
|
|
3042
|
+
for (const img of Array.from(clone.querySelectorAll("img[src]"))) {
|
|
3043
|
+
const src = img.getAttribute("src") ?? "";
|
|
3044
|
+
try {
|
|
3045
|
+
const url = new URL(src, window.location.href);
|
|
3046
|
+
if (url.origin !== window.location.origin) {
|
|
3047
|
+
img.removeAttribute("src");
|
|
3048
|
+
img.removeAttribute("srcset");
|
|
3049
|
+
}
|
|
3050
|
+
} catch {
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
for (const el of Array.from(clone.querySelectorAll("[style]"))) {
|
|
3054
|
+
const style = el.getAttribute("style") ?? "";
|
|
3055
|
+
if (/url\(["']?https?:\/\/(?!localhost)/.test(style)) {
|
|
3056
|
+
el.setAttribute("style", style.replace(/url\([^)]*\)/g, "none"));
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
2606
3059
|
for (const selector of privacy?.blockSelectors ?? []) {
|
|
2607
3060
|
for (const el of safeQueryAll(clone, selector)) {
|
|
2608
3061
|
el.remove();
|
|
@@ -2799,6 +3252,11 @@ function createElementSelector() {
|
|
|
2799
3252
|
e.stopPropagation();
|
|
2800
3253
|
const target = e.target;
|
|
2801
3254
|
if (target === overlay) return;
|
|
3255
|
+
const path = e.composedPath ? e.composedPath() : [];
|
|
3256
|
+
const hitsMushiHost = path.some(
|
|
3257
|
+
(node) => node instanceof Element && node.id === "mushi-mushi-widget"
|
|
3258
|
+
);
|
|
3259
|
+
if (hitsMushiHost) return;
|
|
2802
3260
|
const captured = captureElement(target);
|
|
2803
3261
|
finish(captured);
|
|
2804
3262
|
}
|
|
@@ -3513,7 +3971,7 @@ function createProactiveManager(config = {}) {
|
|
|
3513
3971
|
|
|
3514
3972
|
// src/version.ts
|
|
3515
3973
|
var MUSHI_SDK_PACKAGE = "@mushi-mushi/web";
|
|
3516
|
-
var MUSHI_SDK_VERSION = "1.
|
|
3974
|
+
var MUSHI_SDK_VERSION = "1.3.0" ;
|
|
3517
3975
|
|
|
3518
3976
|
// src/mushi.ts
|
|
3519
3977
|
var instance = null;
|
|
@@ -3718,8 +4176,21 @@ function createInstance(config) {
|
|
|
3718
4176
|
onScreenshotRequest: async () => {
|
|
3719
4177
|
if (!screenshotCap || activeConfig.capture?.screenshot === "off") return;
|
|
3720
4178
|
log.debug("Taking screenshot");
|
|
3721
|
-
|
|
3722
|
-
|
|
4179
|
+
widget.setScreenshotCapturing(true);
|
|
4180
|
+
const result = await screenshotCap.take();
|
|
4181
|
+
if (result.ok) {
|
|
4182
|
+
pendingScreenshot = result.dataUrl;
|
|
4183
|
+
widget.setScreenshotAttached(true);
|
|
4184
|
+
log.debug("Screenshot captured");
|
|
4185
|
+
} else {
|
|
4186
|
+
pendingScreenshot = null;
|
|
4187
|
+
if (result.reason !== "cancelled") {
|
|
4188
|
+
widget.setScreenshotError(true);
|
|
4189
|
+
log.debug("Screenshot failed", { reason: result.reason, message: result.message });
|
|
4190
|
+
} else {
|
|
4191
|
+
widget.setScreenshotCapturing(false);
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
3723
4194
|
},
|
|
3724
4195
|
onScreenshotRemove: () => {
|
|
3725
4196
|
log.debug("Screenshot attachment removed");
|
|
@@ -3729,11 +4200,19 @@ function createInstance(config) {
|
|
|
3729
4200
|
onElementSelectorRequest: async () => {
|
|
3730
4201
|
if (!elementSelector || activeConfig.capture?.elementSelector === false) return;
|
|
3731
4202
|
log.debug("Element selector activated");
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
4203
|
+
widget.setElementCapturing(true);
|
|
4204
|
+
widget.hidePanel();
|
|
4205
|
+
try {
|
|
4206
|
+
const el = await elementSelector.activate();
|
|
4207
|
+
if (el) {
|
|
4208
|
+
pendingElement = el;
|
|
4209
|
+
widget.setElementSelected(true);
|
|
4210
|
+
log.debug("Element selected", { tagName: el.tagName, xpath: el.xpath });
|
|
4211
|
+
} else {
|
|
4212
|
+
widget.setElementCapturing(false);
|
|
4213
|
+
}
|
|
4214
|
+
} finally {
|
|
4215
|
+
widget.showPanel();
|
|
3737
4216
|
}
|
|
3738
4217
|
},
|
|
3739
4218
|
async onReporterReportsRequest() {
|
|
@@ -4261,6 +4740,9 @@ function createInstance(config) {
|
|
|
4261
4740
|
enqueue({ action, metadata });
|
|
4262
4741
|
}
|
|
4263
4742
|
};
|
|
4743
|
+
if (typeof globalThis !== "undefined" && (bootstrapConfig.debug ?? false)) {
|
|
4744
|
+
exposeMarketingRecorder(widget);
|
|
4745
|
+
}
|
|
4264
4746
|
return sdk;
|
|
4265
4747
|
}
|
|
4266
4748
|
function mergeRuntimeConfig(config, runtime) {
|