@mushi-mushi/web 1.5.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/CONTRIBUTING.md +0 -11
- package/dist/index.cjs +785 -151
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -10
- package/dist/index.d.ts +108 -10
- package/dist/index.js +785 -151
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
package/dist/index.d.cts
CHANGED
|
@@ -36,12 +36,27 @@ interface WidgetRewardsState {
|
|
|
36
36
|
/** Expected base points for a `report_submit` action (default 50). */
|
|
37
37
|
pointsForReport: number;
|
|
38
38
|
}
|
|
39
|
+
interface WidgetSubmitOutcome {
|
|
40
|
+
/** Server-confirmed report id. When `null` the report was queued
|
|
41
|
+
* offline / failed-and-queued for retry; the success step degrades
|
|
42
|
+
* gracefully (no "track on console" link, just the receipt stamp). */
|
|
43
|
+
reportId: string | null;
|
|
44
|
+
/** Convenience flag for the widget to decide whether to render the
|
|
45
|
+
* optimistic copy ("queued offline, we'll send it when you're back")
|
|
46
|
+
* versus the confirmed copy ("received — track at #abc12345"). */
|
|
47
|
+
queuedOffline?: boolean;
|
|
48
|
+
}
|
|
39
49
|
interface WidgetCallbacks {
|
|
50
|
+
/**
|
|
51
|
+
* Returns the outcome of the submission so the widget can render a
|
|
52
|
+
* real receipt (report id, deep link). Older callers that return
|
|
53
|
+
* `void` still work — the widget falls back to the legacy stamp.
|
|
54
|
+
*/
|
|
40
55
|
onSubmit(data: {
|
|
41
56
|
category: MushiReportCategory;
|
|
42
57
|
description: string;
|
|
43
58
|
intent?: string;
|
|
44
|
-
}): void
|
|
59
|
+
}): void | Promise<WidgetSubmitOutcome | void>;
|
|
45
60
|
onOpen(): void;
|
|
46
61
|
onClose(): void;
|
|
47
62
|
onScreenshotRequest(): void;
|
|
@@ -62,6 +77,13 @@ declare class MushiWidget {
|
|
|
62
77
|
private step;
|
|
63
78
|
private selectedCategory;
|
|
64
79
|
private selectedIntent;
|
|
80
|
+
/**
|
|
81
|
+
* True when the user took the "Feature request" shortcut. We track this
|
|
82
|
+
* separately from `selectedCategory='other'` so the Back button on the
|
|
83
|
+
* details step jumps straight back to the category picker instead of
|
|
84
|
+
* landing on the intent picker the user explicitly skipped.
|
|
85
|
+
*/
|
|
86
|
+
private viaFeatureRequest;
|
|
65
87
|
private screenshotAttached;
|
|
66
88
|
private screenshotCapturing;
|
|
67
89
|
private screenshotError;
|
|
@@ -99,14 +121,36 @@ declare class MushiWidget {
|
|
|
99
121
|
private successTimer;
|
|
100
122
|
private autoCloseTimer;
|
|
101
123
|
private rewardsState;
|
|
124
|
+
/** Server-confirmed id for the just-submitted report. Surfaces in
|
|
125
|
+
* the success step as a copyable receipt + optional deep link to
|
|
126
|
+
* the Mushi console (when `dashboardUrl` is configured). Cleared
|
|
127
|
+
* on every new `open()` so a re-opened widget never reuses a
|
|
128
|
+
* stale id from the previous session. */
|
|
129
|
+
private lastReportId;
|
|
130
|
+
/** True when the just-submitted report was queued offline (no
|
|
131
|
+
* network, or the API errored and went into the retry queue).
|
|
132
|
+
* Drives a different success copy so the user knows the report
|
|
133
|
+
* hasn't actually reached the console yet. */
|
|
134
|
+
private lastSubmitQueuedOffline;
|
|
135
|
+
/** Whether the user has clicked ✕ on the header banner this session. */
|
|
136
|
+
private bannerDismissed;
|
|
102
137
|
constructor(config: MushiWidgetConfig | undefined, callbacks: WidgetCallbacks, sdkVersion?: string);
|
|
103
138
|
mount(): void;
|
|
104
139
|
getIsMounted(): boolean;
|
|
105
140
|
updateConfig(config?: MushiWidgetConfig): void;
|
|
106
141
|
open(options?: {
|
|
107
142
|
category?: MushiReportCategory;
|
|
143
|
+
featureRequest?: boolean;
|
|
108
144
|
}): void;
|
|
109
145
|
close(): void;
|
|
146
|
+
/**
|
|
147
|
+
* Briefly highlight the trigger button (a soft pulse + tooltip) without
|
|
148
|
+
* opening the full reporter panel. Use for first-session welcome nudges
|
|
149
|
+
* and other "by the way, this exists" prompts where forcing the panel
|
|
150
|
+
* open would feel aggressive. Honours `position: 'none'` (no-op when
|
|
151
|
+
* the trigger button is hidden).
|
|
152
|
+
*/
|
|
153
|
+
pulseTrigger(): void;
|
|
110
154
|
getIsOpen(): boolean;
|
|
111
155
|
showTrigger(): void;
|
|
112
156
|
hideTrigger(): void;
|
|
@@ -138,6 +182,13 @@ declare class MushiWidget {
|
|
|
138
182
|
private syncAttachedLaunchers;
|
|
139
183
|
private syncSmartHide;
|
|
140
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;
|
|
141
192
|
private effectiveTrigger;
|
|
142
193
|
private isMobileSmartHidden;
|
|
143
194
|
private detectEnvironment;
|
|
@@ -166,6 +217,19 @@ declare class MushiWidget {
|
|
|
166
217
|
*/
|
|
167
218
|
private renderStepIndicator;
|
|
168
219
|
private renderCategoryStep;
|
|
220
|
+
/**
|
|
221
|
+
* First-class "Feature request" entry rendered at the top of the
|
|
222
|
+
* category step. Beta apps consistently get more useful signal when
|
|
223
|
+
* the user has a no-friction path to say "I wish this did X" — burying
|
|
224
|
+
* it as an intent under the "Other" category drops feature submissions
|
|
225
|
+
* by ~40% in industry studies (Userpilot, Usersnap 2025).
|
|
226
|
+
*
|
|
227
|
+
* Wire format: still routes through the standard `other` category with
|
|
228
|
+
* a `user_category = 'Feature request'` stamp, so we don't need a DB
|
|
229
|
+
* migration. The admin console filters on that string to surface the
|
|
230
|
+
* Feature-request swimlane.
|
|
231
|
+
*/
|
|
232
|
+
private renderFeatureRequestEntry;
|
|
169
233
|
/** Collapsible "What's new" changelog row. Closes the reporter feedback loop. */
|
|
170
234
|
private renderBetaChangelog;
|
|
171
235
|
/**
|
|
@@ -188,6 +252,19 @@ declare class MushiWidget {
|
|
|
188
252
|
* frame instantly.
|
|
189
253
|
*/
|
|
190
254
|
private renderSuccessStep;
|
|
255
|
+
/**
|
|
256
|
+
* Two-way receipt block. Until the host's `onSubmit` resolves with a
|
|
257
|
+
* server-confirmed report id, we show a discreet "delivering..." pill so
|
|
258
|
+
* the user knows their submission is still in flight. Once we have the
|
|
259
|
+
* id, we surface a short monospaced id + a copy button + an optional
|
|
260
|
+
* "Track on Mushi" deep link to `dashboardUrl/reports/<id>` so the user
|
|
261
|
+
* can watch the status walk through queued -> classified -> fixed in
|
|
262
|
+
* real time (Peak-End rule: the last impression sticks). If we never
|
|
263
|
+
* get an id (offline retry queue), we say so explicitly rather than
|
|
264
|
+
* pretending everything is fine.
|
|
265
|
+
*/
|
|
266
|
+
private renderSuccessReceipt;
|
|
267
|
+
private renderSlaLine;
|
|
191
268
|
/**
|
|
192
269
|
* Reciprocity footer on the success step: closes the feedback loop by
|
|
193
270
|
* attributing where the report goes, sets a response expectation, and
|
|
@@ -238,20 +315,12 @@ interface NetworkCaptureOptions {
|
|
|
238
315
|
declare function createNetworkCapture(options?: NetworkCaptureOptions): NetworkCapture;
|
|
239
316
|
|
|
240
317
|
interface ScreenshotCapture {
|
|
241
|
-
take(): Promise<
|
|
318
|
+
take(): Promise<string | null>;
|
|
242
319
|
updateOptions(options: ScreenshotCaptureOptions): void;
|
|
243
320
|
}
|
|
244
321
|
interface ScreenshotCaptureOptions {
|
|
245
322
|
privacy?: MushiPrivacyConfig;
|
|
246
323
|
}
|
|
247
|
-
type ScreenshotResult = {
|
|
248
|
-
ok: true;
|
|
249
|
-
dataUrl: string;
|
|
250
|
-
} | {
|
|
251
|
-
ok: false;
|
|
252
|
-
reason: 'tainted' | 'load-error' | 'unsupported' | 'cancelled' | 'error';
|
|
253
|
-
message?: string;
|
|
254
|
-
};
|
|
255
324
|
declare function createScreenshotCapture(options?: ScreenshotCaptureOptions): ScreenshotCapture;
|
|
256
325
|
|
|
257
326
|
interface PerformanceCapture {
|
|
@@ -343,6 +412,35 @@ interface ProactiveTriggerConfig {
|
|
|
343
412
|
apiCascade?: boolean | MushiApiCascadeConfig;
|
|
344
413
|
apiEndpoint?: string;
|
|
345
414
|
errorBoundary?: boolean;
|
|
415
|
+
/**
|
|
416
|
+
* Beta-mode nudges. Fire when the user has been on the same route for
|
|
417
|
+
* `pageDwellMs` continuous milliseconds without filing any report. Default
|
|
418
|
+
* disabled because production apps usually don't want unsolicited prompts;
|
|
419
|
+
* recommended only when `betaMode.enabled` is true on the widget.
|
|
420
|
+
*
|
|
421
|
+
* Pass `true` to use the default 5-minute threshold, or a config object
|
|
422
|
+
* to override. Set to `false` (default) to disable entirely.
|
|
423
|
+
*/
|
|
424
|
+
pageDwell?: boolean | {
|
|
425
|
+
thresholdMs?: number;
|
|
426
|
+
excludeRoutes?: string[];
|
|
427
|
+
};
|
|
428
|
+
/**
|
|
429
|
+
* First-session welcome. Fires exactly once per user (tracked via
|
|
430
|
+
* `localStorage`) `delayMs` milliseconds after `Mushi.init`. Use to
|
|
431
|
+
* gently surface the bug button to new beta users so they know feedback
|
|
432
|
+
* is welcome. Default disabled.
|
|
433
|
+
*/
|
|
434
|
+
firstSession?: boolean | {
|
|
435
|
+
delayMs?: number;
|
|
436
|
+
storageKey?: string;
|
|
437
|
+
};
|
|
438
|
+
/**
|
|
439
|
+
* The project ID, used to namespace the `firstSession` localStorage key so
|
|
440
|
+
* multi-tenant single-page apps don't share first-session state across
|
|
441
|
+
* projects. Sourced from `MushiConfig.projectId` by the SDK.
|
|
442
|
+
*/
|
|
443
|
+
projectId?: string;
|
|
346
444
|
}
|
|
347
445
|
interface ProactiveTriggerCallbacks {
|
|
348
446
|
onTrigger: (type: string, context: Record<string, unknown>) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -36,12 +36,27 @@ interface WidgetRewardsState {
|
|
|
36
36
|
/** Expected base points for a `report_submit` action (default 50). */
|
|
37
37
|
pointsForReport: number;
|
|
38
38
|
}
|
|
39
|
+
interface WidgetSubmitOutcome {
|
|
40
|
+
/** Server-confirmed report id. When `null` the report was queued
|
|
41
|
+
* offline / failed-and-queued for retry; the success step degrades
|
|
42
|
+
* gracefully (no "track on console" link, just the receipt stamp). */
|
|
43
|
+
reportId: string | null;
|
|
44
|
+
/** Convenience flag for the widget to decide whether to render the
|
|
45
|
+
* optimistic copy ("queued offline, we'll send it when you're back")
|
|
46
|
+
* versus the confirmed copy ("received — track at #abc12345"). */
|
|
47
|
+
queuedOffline?: boolean;
|
|
48
|
+
}
|
|
39
49
|
interface WidgetCallbacks {
|
|
50
|
+
/**
|
|
51
|
+
* Returns the outcome of the submission so the widget can render a
|
|
52
|
+
* real receipt (report id, deep link). Older callers that return
|
|
53
|
+
* `void` still work — the widget falls back to the legacy stamp.
|
|
54
|
+
*/
|
|
40
55
|
onSubmit(data: {
|
|
41
56
|
category: MushiReportCategory;
|
|
42
57
|
description: string;
|
|
43
58
|
intent?: string;
|
|
44
|
-
}): void
|
|
59
|
+
}): void | Promise<WidgetSubmitOutcome | void>;
|
|
45
60
|
onOpen(): void;
|
|
46
61
|
onClose(): void;
|
|
47
62
|
onScreenshotRequest(): void;
|
|
@@ -62,6 +77,13 @@ declare class MushiWidget {
|
|
|
62
77
|
private step;
|
|
63
78
|
private selectedCategory;
|
|
64
79
|
private selectedIntent;
|
|
80
|
+
/**
|
|
81
|
+
* True when the user took the "Feature request" shortcut. We track this
|
|
82
|
+
* separately from `selectedCategory='other'` so the Back button on the
|
|
83
|
+
* details step jumps straight back to the category picker instead of
|
|
84
|
+
* landing on the intent picker the user explicitly skipped.
|
|
85
|
+
*/
|
|
86
|
+
private viaFeatureRequest;
|
|
65
87
|
private screenshotAttached;
|
|
66
88
|
private screenshotCapturing;
|
|
67
89
|
private screenshotError;
|
|
@@ -99,14 +121,36 @@ declare class MushiWidget {
|
|
|
99
121
|
private successTimer;
|
|
100
122
|
private autoCloseTimer;
|
|
101
123
|
private rewardsState;
|
|
124
|
+
/** Server-confirmed id for the just-submitted report. Surfaces in
|
|
125
|
+
* the success step as a copyable receipt + optional deep link to
|
|
126
|
+
* the Mushi console (when `dashboardUrl` is configured). Cleared
|
|
127
|
+
* on every new `open()` so a re-opened widget never reuses a
|
|
128
|
+
* stale id from the previous session. */
|
|
129
|
+
private lastReportId;
|
|
130
|
+
/** True when the just-submitted report was queued offline (no
|
|
131
|
+
* network, or the API errored and went into the retry queue).
|
|
132
|
+
* Drives a different success copy so the user knows the report
|
|
133
|
+
* hasn't actually reached the console yet. */
|
|
134
|
+
private lastSubmitQueuedOffline;
|
|
135
|
+
/** Whether the user has clicked ✕ on the header banner this session. */
|
|
136
|
+
private bannerDismissed;
|
|
102
137
|
constructor(config: MushiWidgetConfig | undefined, callbacks: WidgetCallbacks, sdkVersion?: string);
|
|
103
138
|
mount(): void;
|
|
104
139
|
getIsMounted(): boolean;
|
|
105
140
|
updateConfig(config?: MushiWidgetConfig): void;
|
|
106
141
|
open(options?: {
|
|
107
142
|
category?: MushiReportCategory;
|
|
143
|
+
featureRequest?: boolean;
|
|
108
144
|
}): void;
|
|
109
145
|
close(): void;
|
|
146
|
+
/**
|
|
147
|
+
* Briefly highlight the trigger button (a soft pulse + tooltip) without
|
|
148
|
+
* opening the full reporter panel. Use for first-session welcome nudges
|
|
149
|
+
* and other "by the way, this exists" prompts where forcing the panel
|
|
150
|
+
* open would feel aggressive. Honours `position: 'none'` (no-op when
|
|
151
|
+
* the trigger button is hidden).
|
|
152
|
+
*/
|
|
153
|
+
pulseTrigger(): void;
|
|
110
154
|
getIsOpen(): boolean;
|
|
111
155
|
showTrigger(): void;
|
|
112
156
|
hideTrigger(): void;
|
|
@@ -138,6 +182,13 @@ declare class MushiWidget {
|
|
|
138
182
|
private syncAttachedLaunchers;
|
|
139
183
|
private syncSmartHide;
|
|
140
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;
|
|
141
192
|
private effectiveTrigger;
|
|
142
193
|
private isMobileSmartHidden;
|
|
143
194
|
private detectEnvironment;
|
|
@@ -166,6 +217,19 @@ declare class MushiWidget {
|
|
|
166
217
|
*/
|
|
167
218
|
private renderStepIndicator;
|
|
168
219
|
private renderCategoryStep;
|
|
220
|
+
/**
|
|
221
|
+
* First-class "Feature request" entry rendered at the top of the
|
|
222
|
+
* category step. Beta apps consistently get more useful signal when
|
|
223
|
+
* the user has a no-friction path to say "I wish this did X" — burying
|
|
224
|
+
* it as an intent under the "Other" category drops feature submissions
|
|
225
|
+
* by ~40% in industry studies (Userpilot, Usersnap 2025).
|
|
226
|
+
*
|
|
227
|
+
* Wire format: still routes through the standard `other` category with
|
|
228
|
+
* a `user_category = 'Feature request'` stamp, so we don't need a DB
|
|
229
|
+
* migration. The admin console filters on that string to surface the
|
|
230
|
+
* Feature-request swimlane.
|
|
231
|
+
*/
|
|
232
|
+
private renderFeatureRequestEntry;
|
|
169
233
|
/** Collapsible "What's new" changelog row. Closes the reporter feedback loop. */
|
|
170
234
|
private renderBetaChangelog;
|
|
171
235
|
/**
|
|
@@ -188,6 +252,19 @@ declare class MushiWidget {
|
|
|
188
252
|
* frame instantly.
|
|
189
253
|
*/
|
|
190
254
|
private renderSuccessStep;
|
|
255
|
+
/**
|
|
256
|
+
* Two-way receipt block. Until the host's `onSubmit` resolves with a
|
|
257
|
+
* server-confirmed report id, we show a discreet "delivering..." pill so
|
|
258
|
+
* the user knows their submission is still in flight. Once we have the
|
|
259
|
+
* id, we surface a short monospaced id + a copy button + an optional
|
|
260
|
+
* "Track on Mushi" deep link to `dashboardUrl/reports/<id>` so the user
|
|
261
|
+
* can watch the status walk through queued -> classified -> fixed in
|
|
262
|
+
* real time (Peak-End rule: the last impression sticks). If we never
|
|
263
|
+
* get an id (offline retry queue), we say so explicitly rather than
|
|
264
|
+
* pretending everything is fine.
|
|
265
|
+
*/
|
|
266
|
+
private renderSuccessReceipt;
|
|
267
|
+
private renderSlaLine;
|
|
191
268
|
/**
|
|
192
269
|
* Reciprocity footer on the success step: closes the feedback loop by
|
|
193
270
|
* attributing where the report goes, sets a response expectation, and
|
|
@@ -238,20 +315,12 @@ interface NetworkCaptureOptions {
|
|
|
238
315
|
declare function createNetworkCapture(options?: NetworkCaptureOptions): NetworkCapture;
|
|
239
316
|
|
|
240
317
|
interface ScreenshotCapture {
|
|
241
|
-
take(): Promise<
|
|
318
|
+
take(): Promise<string | null>;
|
|
242
319
|
updateOptions(options: ScreenshotCaptureOptions): void;
|
|
243
320
|
}
|
|
244
321
|
interface ScreenshotCaptureOptions {
|
|
245
322
|
privacy?: MushiPrivacyConfig;
|
|
246
323
|
}
|
|
247
|
-
type ScreenshotResult = {
|
|
248
|
-
ok: true;
|
|
249
|
-
dataUrl: string;
|
|
250
|
-
} | {
|
|
251
|
-
ok: false;
|
|
252
|
-
reason: 'tainted' | 'load-error' | 'unsupported' | 'cancelled' | 'error';
|
|
253
|
-
message?: string;
|
|
254
|
-
};
|
|
255
324
|
declare function createScreenshotCapture(options?: ScreenshotCaptureOptions): ScreenshotCapture;
|
|
256
325
|
|
|
257
326
|
interface PerformanceCapture {
|
|
@@ -343,6 +412,35 @@ interface ProactiveTriggerConfig {
|
|
|
343
412
|
apiCascade?: boolean | MushiApiCascadeConfig;
|
|
344
413
|
apiEndpoint?: string;
|
|
345
414
|
errorBoundary?: boolean;
|
|
415
|
+
/**
|
|
416
|
+
* Beta-mode nudges. Fire when the user has been on the same route for
|
|
417
|
+
* `pageDwellMs` continuous milliseconds without filing any report. Default
|
|
418
|
+
* disabled because production apps usually don't want unsolicited prompts;
|
|
419
|
+
* recommended only when `betaMode.enabled` is true on the widget.
|
|
420
|
+
*
|
|
421
|
+
* Pass `true` to use the default 5-minute threshold, or a config object
|
|
422
|
+
* to override. Set to `false` (default) to disable entirely.
|
|
423
|
+
*/
|
|
424
|
+
pageDwell?: boolean | {
|
|
425
|
+
thresholdMs?: number;
|
|
426
|
+
excludeRoutes?: string[];
|
|
427
|
+
};
|
|
428
|
+
/**
|
|
429
|
+
* First-session welcome. Fires exactly once per user (tracked via
|
|
430
|
+
* `localStorage`) `delayMs` milliseconds after `Mushi.init`. Use to
|
|
431
|
+
* gently surface the bug button to new beta users so they know feedback
|
|
432
|
+
* is welcome. Default disabled.
|
|
433
|
+
*/
|
|
434
|
+
firstSession?: boolean | {
|
|
435
|
+
delayMs?: number;
|
|
436
|
+
storageKey?: string;
|
|
437
|
+
};
|
|
438
|
+
/**
|
|
439
|
+
* The project ID, used to namespace the `firstSession` localStorage key so
|
|
440
|
+
* multi-tenant single-page apps don't share first-session state across
|
|
441
|
+
* projects. Sourced from `MushiConfig.projectId` by the SDK.
|
|
442
|
+
*/
|
|
443
|
+
projectId?: string;
|
|
346
444
|
}
|
|
347
445
|
interface ProactiveTriggerCallbacks {
|
|
348
446
|
onTrigger: (type: string, context: Record<string, unknown>) => void;
|