@mushi-mushi/web 1.5.0 → 1.6.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.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,34 @@ 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;
102
135
  constructor(config: MushiWidgetConfig | undefined, callbacks: WidgetCallbacks, sdkVersion?: string);
103
136
  mount(): void;
104
137
  getIsMounted(): boolean;
105
138
  updateConfig(config?: MushiWidgetConfig): void;
106
139
  open(options?: {
107
140
  category?: MushiReportCategory;
141
+ featureRequest?: boolean;
108
142
  }): void;
109
143
  close(): void;
144
+ /**
145
+ * Briefly highlight the trigger button (a soft pulse + tooltip) without
146
+ * opening the full reporter panel. Use for first-session welcome nudges
147
+ * and other "by the way, this exists" prompts where forcing the panel
148
+ * open would feel aggressive. Honours `position: 'none'` (no-op when
149
+ * the trigger button is hidden).
150
+ */
151
+ pulseTrigger(): void;
110
152
  getIsOpen(): boolean;
111
153
  showTrigger(): void;
112
154
  hideTrigger(): void;
@@ -166,6 +208,19 @@ declare class MushiWidget {
166
208
  */
167
209
  private renderStepIndicator;
168
210
  private renderCategoryStep;
211
+ /**
212
+ * First-class "Feature request" entry rendered at the top of the
213
+ * category step. Beta apps consistently get more useful signal when
214
+ * the user has a no-friction path to say "I wish this did X" — burying
215
+ * it as an intent under the "Other" category drops feature submissions
216
+ * by ~40% in industry studies (Userpilot, Usersnap 2025).
217
+ *
218
+ * Wire format: still routes through the standard `other` category with
219
+ * a `user_category = 'Feature request'` stamp, so we don't need a DB
220
+ * migration. The admin console filters on that string to surface the
221
+ * Feature-request swimlane.
222
+ */
223
+ private renderFeatureRequestEntry;
169
224
  /** Collapsible "What's new" changelog row. Closes the reporter feedback loop. */
170
225
  private renderBetaChangelog;
171
226
  /**
@@ -188,6 +243,19 @@ declare class MushiWidget {
188
243
  * frame instantly.
189
244
  */
190
245
  private renderSuccessStep;
246
+ /**
247
+ * Two-way receipt block. Until the host's `onSubmit` resolves with a
248
+ * server-confirmed report id, we show a discreet "delivering..." pill so
249
+ * the user knows their submission is still in flight. Once we have the
250
+ * id, we surface a short monospaced id + a copy button + an optional
251
+ * "Track on Mushi" deep link to `dashboardUrl/reports/<id>` so the user
252
+ * can watch the status walk through queued -> classified -> fixed in
253
+ * real time (Peak-End rule: the last impression sticks). If we never
254
+ * get an id (offline retry queue), we say so explicitly rather than
255
+ * pretending everything is fine.
256
+ */
257
+ private renderSuccessReceipt;
258
+ private renderSlaLine;
191
259
  /**
192
260
  * Reciprocity footer on the success step: closes the feedback loop by
193
261
  * attributing where the report goes, sets a response expectation, and
@@ -238,20 +306,12 @@ interface NetworkCaptureOptions {
238
306
  declare function createNetworkCapture(options?: NetworkCaptureOptions): NetworkCapture;
239
307
 
240
308
  interface ScreenshotCapture {
241
- take(): Promise<ScreenshotResult>;
309
+ take(): Promise<string | null>;
242
310
  updateOptions(options: ScreenshotCaptureOptions): void;
243
311
  }
244
312
  interface ScreenshotCaptureOptions {
245
313
  privacy?: MushiPrivacyConfig;
246
314
  }
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
315
  declare function createScreenshotCapture(options?: ScreenshotCaptureOptions): ScreenshotCapture;
256
316
 
257
317
  interface PerformanceCapture {
@@ -343,6 +403,35 @@ interface ProactiveTriggerConfig {
343
403
  apiCascade?: boolean | MushiApiCascadeConfig;
344
404
  apiEndpoint?: string;
345
405
  errorBoundary?: boolean;
406
+ /**
407
+ * Beta-mode nudges. Fire when the user has been on the same route for
408
+ * `pageDwellMs` continuous milliseconds without filing any report. Default
409
+ * disabled because production apps usually don't want unsolicited prompts;
410
+ * recommended only when `betaMode.enabled` is true on the widget.
411
+ *
412
+ * Pass `true` to use the default 5-minute threshold, or a config object
413
+ * to override. Set to `false` (default) to disable entirely.
414
+ */
415
+ pageDwell?: boolean | {
416
+ thresholdMs?: number;
417
+ excludeRoutes?: string[];
418
+ };
419
+ /**
420
+ * First-session welcome. Fires exactly once per user (tracked via
421
+ * `localStorage`) `delayMs` milliseconds after `Mushi.init`. Use to
422
+ * gently surface the bug button to new beta users so they know feedback
423
+ * is welcome. Default disabled.
424
+ */
425
+ firstSession?: boolean | {
426
+ delayMs?: number;
427
+ storageKey?: string;
428
+ };
429
+ /**
430
+ * The project ID, used to namespace the `firstSession` localStorage key so
431
+ * multi-tenant single-page apps don't share first-session state across
432
+ * projects. Sourced from `MushiConfig.projectId` by the SDK.
433
+ */
434
+ projectId?: string;
346
435
  }
347
436
  interface ProactiveTriggerCallbacks {
348
437
  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,34 @@ 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;
102
135
  constructor(config: MushiWidgetConfig | undefined, callbacks: WidgetCallbacks, sdkVersion?: string);
103
136
  mount(): void;
104
137
  getIsMounted(): boolean;
105
138
  updateConfig(config?: MushiWidgetConfig): void;
106
139
  open(options?: {
107
140
  category?: MushiReportCategory;
141
+ featureRequest?: boolean;
108
142
  }): void;
109
143
  close(): void;
144
+ /**
145
+ * Briefly highlight the trigger button (a soft pulse + tooltip) without
146
+ * opening the full reporter panel. Use for first-session welcome nudges
147
+ * and other "by the way, this exists" prompts where forcing the panel
148
+ * open would feel aggressive. Honours `position: 'none'` (no-op when
149
+ * the trigger button is hidden).
150
+ */
151
+ pulseTrigger(): void;
110
152
  getIsOpen(): boolean;
111
153
  showTrigger(): void;
112
154
  hideTrigger(): void;
@@ -166,6 +208,19 @@ declare class MushiWidget {
166
208
  */
167
209
  private renderStepIndicator;
168
210
  private renderCategoryStep;
211
+ /**
212
+ * First-class "Feature request" entry rendered at the top of the
213
+ * category step. Beta apps consistently get more useful signal when
214
+ * the user has a no-friction path to say "I wish this did X" — burying
215
+ * it as an intent under the "Other" category drops feature submissions
216
+ * by ~40% in industry studies (Userpilot, Usersnap 2025).
217
+ *
218
+ * Wire format: still routes through the standard `other` category with
219
+ * a `user_category = 'Feature request'` stamp, so we don't need a DB
220
+ * migration. The admin console filters on that string to surface the
221
+ * Feature-request swimlane.
222
+ */
223
+ private renderFeatureRequestEntry;
169
224
  /** Collapsible "What's new" changelog row. Closes the reporter feedback loop. */
170
225
  private renderBetaChangelog;
171
226
  /**
@@ -188,6 +243,19 @@ declare class MushiWidget {
188
243
  * frame instantly.
189
244
  */
190
245
  private renderSuccessStep;
246
+ /**
247
+ * Two-way receipt block. Until the host's `onSubmit` resolves with a
248
+ * server-confirmed report id, we show a discreet "delivering..." pill so
249
+ * the user knows their submission is still in flight. Once we have the
250
+ * id, we surface a short monospaced id + a copy button + an optional
251
+ * "Track on Mushi" deep link to `dashboardUrl/reports/<id>` so the user
252
+ * can watch the status walk through queued -> classified -> fixed in
253
+ * real time (Peak-End rule: the last impression sticks). If we never
254
+ * get an id (offline retry queue), we say so explicitly rather than
255
+ * pretending everything is fine.
256
+ */
257
+ private renderSuccessReceipt;
258
+ private renderSlaLine;
191
259
  /**
192
260
  * Reciprocity footer on the success step: closes the feedback loop by
193
261
  * attributing where the report goes, sets a response expectation, and
@@ -238,20 +306,12 @@ interface NetworkCaptureOptions {
238
306
  declare function createNetworkCapture(options?: NetworkCaptureOptions): NetworkCapture;
239
307
 
240
308
  interface ScreenshotCapture {
241
- take(): Promise<ScreenshotResult>;
309
+ take(): Promise<string | null>;
242
310
  updateOptions(options: ScreenshotCaptureOptions): void;
243
311
  }
244
312
  interface ScreenshotCaptureOptions {
245
313
  privacy?: MushiPrivacyConfig;
246
314
  }
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
315
  declare function createScreenshotCapture(options?: ScreenshotCaptureOptions): ScreenshotCapture;
256
316
 
257
317
  interface PerformanceCapture {
@@ -343,6 +403,35 @@ interface ProactiveTriggerConfig {
343
403
  apiCascade?: boolean | MushiApiCascadeConfig;
344
404
  apiEndpoint?: string;
345
405
  errorBoundary?: boolean;
406
+ /**
407
+ * Beta-mode nudges. Fire when the user has been on the same route for
408
+ * `pageDwellMs` continuous milliseconds without filing any report. Default
409
+ * disabled because production apps usually don't want unsolicited prompts;
410
+ * recommended only when `betaMode.enabled` is true on the widget.
411
+ *
412
+ * Pass `true` to use the default 5-minute threshold, or a config object
413
+ * to override. Set to `false` (default) to disable entirely.
414
+ */
415
+ pageDwell?: boolean | {
416
+ thresholdMs?: number;
417
+ excludeRoutes?: string[];
418
+ };
419
+ /**
420
+ * First-session welcome. Fires exactly once per user (tracked via
421
+ * `localStorage`) `delayMs` milliseconds after `Mushi.init`. Use to
422
+ * gently surface the bug button to new beta users so they know feedback
423
+ * is welcome. Default disabled.
424
+ */
425
+ firstSession?: boolean | {
426
+ delayMs?: number;
427
+ storageKey?: string;
428
+ };
429
+ /**
430
+ * The project ID, used to namespace the `firstSession` localStorage key so
431
+ * multi-tenant single-page apps don't share first-session state across
432
+ * projects. Sourced from `MushiConfig.projectId` by the SDK.
433
+ */
434
+ projectId?: string;
346
435
  }
347
436
  interface ProactiveTriggerCallbacks {
348
437
  onTrigger: (type: string, context: Record<string, unknown>) => void;