@mushi-mushi/react-native 0.20.0 → 0.21.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/CHANGELOG.md +34 -0
- package/CONTRIBUTING.md +10 -2
- package/README.md +16 -2
- package/SECURITY.md +24 -1
- package/dist/index.cjs +95 -49
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +107 -51
- package/package.json +18 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# @mushi-mushi/react-native
|
|
2
2
|
|
|
3
|
+
## 0.21.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 26e038f: SDK polish from the UI/UX audit pass:
|
|
8
|
+
|
|
9
|
+
- **web**: the widget now live-restyles when the OS `prefers-color-scheme`
|
|
10
|
+
changes under `theme: 'auto' | 'inherit'`, and mount is hardened so a host
|
|
11
|
+
page whose `matchMedia` lacks the modern `addEventListener` can never throw.
|
|
12
|
+
Richer banner layout (pill + message + flat actions), `hideOnSelector`
|
|
13
|
+
suppression, submit failure-kind surfacing, and expanded i18n copy
|
|
14
|
+
(en/es/ja/th).
|
|
15
|
+
- **react-native**: banner, bottom-sheet, and floating-button polish;
|
|
16
|
+
`submitReport()` now resolves to a typed failure result instead of
|
|
17
|
+
`undefined` when called before the provider finishes initializing.
|
|
18
|
+
- **core**: additional shared design tokens consumed by the surfaces above.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [26e038f]
|
|
23
|
+
- @mushi-mushi/web@1.26.0
|
|
24
|
+
- @mushi-mushi/core@1.26.0
|
|
25
|
+
|
|
26
|
+
## 0.20.1
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- 5feac27: # v0.20.1 — Post-release SDK reliability fixes
|
|
31
|
+
|
|
32
|
+
- **React Native types resolve after publish**: removed the `web-i18n.d.ts` ambient shim that re-exported `MushiLocale` from a monorepo-relative source path (`../../web/src/i18n/types`) that does not exist in a published install. `@mushi-mushi/web` already ships proper `./i18n` types via its `exports` map, which `moduleResolution: "bundler"`/`nodenext` resolves directly.
|
|
33
|
+
|
|
34
|
+
- Updated dependencies [5feac27]
|
|
35
|
+
- @mushi-mushi/web@1.21.1
|
|
36
|
+
|
|
3
37
|
## 0.20.0
|
|
4
38
|
|
|
5
39
|
### Minor Changes
|
package/CONTRIBUTING.md
CHANGED
|
@@ -76,6 +76,7 @@ packages/
|
|
|
76
76
|
apps/
|
|
77
77
|
admin/ Admin dashboard (React + Tailwind)
|
|
78
78
|
docs/ Documentation site (Nextra — apps/docs)
|
|
79
|
+
testers/ Tester marketplace portal (Next.js)
|
|
79
80
|
tooling/
|
|
80
81
|
eslint-config/ Shared ESLint flat config
|
|
81
82
|
tsconfig/ Shared TypeScript configs
|
|
@@ -118,15 +119,22 @@ If your PR changes **user-visible SDK or CLI behavior**, update docs in the **sa
|
|
|
118
119
|
Guardrails (run locally before pushing):
|
|
119
120
|
|
|
120
121
|
```bash
|
|
121
|
-
pnpm check:docs-stats # root README stat badges
|
|
122
|
+
pnpm check:docs-stats # root README stat badges + glance <sub> line
|
|
122
123
|
pnpm check:sdk-version-matrix # apps/docs/content/sdks/index.mdx versions
|
|
123
124
|
pnpm check:internal-doc-links # dead /foo links in MDX
|
|
124
|
-
pnpm check:onboarding-drift # phantom MCP env vars / stale tool names
|
|
125
|
+
pnpm check:onboarding-drift # phantom MCP env vars / stale tool names / native install coords
|
|
126
|
+
pnpm check:catalog-sync # MCP catalog ↔ admin counts ↔ prose "N tools"
|
|
127
|
+
pnpm check:mcp-tools-doc # mcp-tools.generated.mdx stale check
|
|
125
128
|
pnpm check:bundle-docs # @mushi-mushi/web size-limit vs README
|
|
129
|
+
pnpm check:docs-drift # versions + public docs links
|
|
126
130
|
pnpm gen:llms-txt # regenerate apps/docs/public/llms.txt after nav changes
|
|
127
131
|
pnpm gen:mcp-tools-doc # regenerate sdks/mcp-tools.generated.mdx from catalog.ts
|
|
128
132
|
```
|
|
129
133
|
|
|
134
|
+
**Same-PR rule:** if behavior, env vars, CLI commands, MCP tools, or install
|
|
135
|
+
coordinates change, update the matching docs in this PR and run
|
|
136
|
+
`pnpm check:docs-drift && pnpm check:onboarding-drift && pnpm check:catalog-sync`.
|
|
137
|
+
|
|
130
138
|
README changes on npm packages only appear on [npmjs.com](https://www.npmjs.com) after the next **Changesets publish**.
|
|
131
139
|
|
|
132
140
|
## Changesets
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Your AI wrote it. Mushi tells you why it broke.**
|
|
4
4
|
|
|
5
|
-
React Native SDK for [Mushi Mushi](https://www.npmjs.com/package/mushi-mushi) — shake-to-report widget, offline queue, and
|
|
5
|
+
React Native SDK for [Mushi Mushi](https://www.npmjs.com/package/mushi-mushi) — shake-to-report widget, offline queue, and a plain-English diagnosis in your editor, for iOS + Android.
|
|
6
6
|
|
|
7
7
|
> **One-command setup:** `npx mushi-mushi` auto-detects React Native / Expo and installs this package with the right env vars.
|
|
8
8
|
>
|
|
@@ -12,6 +12,20 @@ React Native SDK for [Mushi Mushi](https://www.npmjs.com/package/mushi-mushi)
|
|
|
12
12
|
|
|
13
13
|
Every bug your users feel — a button that won't tap, a screen that freezes, a layout that folds on their device — gets captured, classified by AI, and fed into a lesson library that prevents the same class of mistake in the next PR review and the next AI agent run. That's the closed loop the main [`mushi-mushi`](https://www.npmjs.com/package/mushi-mushi) README describes. This package is the mobile entry point.
|
|
14
14
|
|
|
15
|
+
## Error monitoring (optional)
|
|
16
|
+
|
|
17
|
+
Mushi captures user-reported bugs through its own ingest pipeline. If you also run **Sentry** in your React Native app, wire both for full coverage:
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import * as Sentry from '@sentry/react-native'
|
|
21
|
+
|
|
22
|
+
Sentry.init({ dsn: process.env.EXPO_PUBLIC_SENTRY_DSN })
|
|
23
|
+
|
|
24
|
+
// Mushi handles shake-to-report + screenshot context; Sentry handles thrown errors.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
See [`@mushi-mushi/web` Sentry bridge](https://github.com/kensaurus/mushi-mushi/tree/master/packages/web#sentry-companion) for the dual-capture pattern on web.
|
|
28
|
+
|
|
15
29
|
## Quick start
|
|
16
30
|
|
|
17
31
|
```tsx
|
|
@@ -168,4 +182,4 @@ MIT
|
|
|
168
182
|
<!-- mushi-readme-stats-footer -->
|
|
169
183
|
---
|
|
170
184
|
|
|
171
|
-
<sub>Monorepo scale (
|
|
185
|
+
<sub>Monorepo scale (July 2026): 51 edge functions · 323 SQL migrations · 13 outbound plugins · 11 inbound adapters · 19 pipeline agents. Canonical counts: <a href="https://github.com/kensaurus/mushi-mushi/blob/master/docs/stats.md">docs/stats.md</a> · <code>pnpm docs-stats</code></sub>
|
package/SECURITY.md
CHANGED
|
@@ -111,7 +111,7 @@ What we treat as in-scope attacker capabilities, and what we don't.
|
|
|
111
111
|
| User pasting a Stripe / OpenAI / GitHub PAT into a bug report | ✅ | PII scrubber redacts ~15 vendor token formats client-side before the report leaves the device. Mirrors `packages/core/src/pii-scrubber.ts` across iOS, Android, Flutter, React Native. |
|
|
112
112
|
| Stolen end-user device with the SDK installed | ⚠️ partial | Offline queue is AsyncStorage / Keychain / SharedPreferences — no app-level encryption beyond the OS default. Reports waiting to flush are vulnerable to a forensic attacker. |
|
|
113
113
|
| Compromised Supabase service-role key | ❌ | Treated as a tier-0 incident; would require key rotation and audit-log forensics. Not defendable in software. |
|
|
114
|
-
| Compromise of `kensaurus@gmail.com` | ❌ | Treated as a project-fork event; downstream consumers should pin to the last known-good version and follow the new release channel. |
|
|
114
|
+
| Compromise of `kensaurus@gmail.com` mail account | ❌ | Treated as a project-fork event; downstream consumers should pin to the last known-good version and follow the new release channel. |
|
|
115
115
|
| Physical / OS-level attacker on an end-user device | ❌ | Out of scope. |
|
|
116
116
|
| Malicious fork using the Mushi name to ship malware | ❌ (technical) ✅ (legal) | The MIT / AGPLv3 grant lets the fork exist; the trademark policy (`TRADEMARK.md`) makes shipping it under the Mushi name an infringement we will pursue. |
|
|
117
117
|
|
|
@@ -157,6 +157,29 @@ Structured fields you set explicitly (`metadata.userEmail`,
|
|
|
157
157
|
opt-in attribution data, and silently rewriting them would break
|
|
158
158
|
support workflows.
|
|
159
159
|
|
|
160
|
+
### CLI funnel telemetry
|
|
161
|
+
|
|
162
|
+
The Mushi CLI sends two fire-and-forget signals to `/v1/cli/funnel` on your
|
|
163
|
+
project's endpoint to help us detect where the onboarding wizard drops off:
|
|
164
|
+
|
|
165
|
+
| Signal | When | What is sent |
|
|
166
|
+
|--------|------|-------------|
|
|
167
|
+
| `wizard_env_written` | After `npx mushi-mushi` writes `.env.local` | `event`, `source: "cli"` |
|
|
168
|
+
| `mcp_setup_done` | After `mushi setup --ide` verifies the MCP key | `event`, `source: "cli"` |
|
|
169
|
+
|
|
170
|
+
No file contents, stack traces, or identifiers beyond your project ID and API
|
|
171
|
+
key are sent. The key is already on the endpoint — this adds no new disclosure.
|
|
172
|
+
|
|
173
|
+
**Opt out** by setting `MUSHI_NO_TELEMETRY=1` in your shell or CI environment:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
MUSHI_NO_TELEMETRY=1 npx mushi-mushi
|
|
177
|
+
MUSHI_NO_TELEMETRY=1 mushi setup --ide cursor
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
These signals never block or delay the wizard — they time out silently after
|
|
181
|
+
4 seconds and errors are discarded.
|
|
182
|
+
|
|
160
183
|
### Where data lives
|
|
161
184
|
|
|
162
185
|
- **Reports & telemetry** — Supabase Postgres in the `us-west-1` region.
|
package/dist/index.cjs
CHANGED
|
@@ -543,6 +543,7 @@ var MushiBottomSheet = ({
|
|
|
543
543
|
const [category, setCategory] = (0, import_react.useState)(null);
|
|
544
544
|
const [description, setDescription] = (0, import_react.useState)("");
|
|
545
545
|
const [phase, setPhase] = (0, import_react.useState)("form");
|
|
546
|
+
const [submitError, setSubmitError] = (0, import_react.useState)(null);
|
|
546
547
|
const [sheetTab, setSheetTab] = (0, import_react.useState)("report");
|
|
547
548
|
const [inboxReports, setInboxReports] = (0, import_react.useState)([]);
|
|
548
549
|
const [inboxLoading, setInboxLoading] = (0, import_react.useState)(false);
|
|
@@ -616,6 +617,8 @@ var MushiBottomSheet = ({
|
|
|
616
617
|
...prev,
|
|
617
618
|
{ role: "bot", text: botText, options: reply.options }
|
|
618
619
|
]);
|
|
620
|
+
} catch {
|
|
621
|
+
setAssistantError("Could not reach the assistant. Try again.");
|
|
619
622
|
} finally {
|
|
620
623
|
setAssistantSending(false);
|
|
621
624
|
}
|
|
@@ -689,24 +692,46 @@ var MushiBottomSheet = ({
|
|
|
689
692
|
).current;
|
|
690
693
|
const handleSubmit = async () => {
|
|
691
694
|
if (!category || !description.trim() || !mushi) return;
|
|
695
|
+
const trimmed = description.trim();
|
|
696
|
+
if (trimmed.length < 20) {
|
|
697
|
+
setSubmitError("Please write at least 20 characters so we can understand the issue.");
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
setSubmitError(null);
|
|
692
701
|
setPhase("sending");
|
|
693
702
|
try {
|
|
694
|
-
await mushi.submitReport({
|
|
703
|
+
const outcome = await mushi.submitReport({
|
|
695
704
|
category,
|
|
696
|
-
description:
|
|
705
|
+
description: trimmed,
|
|
697
706
|
screenshotDataUrl: screenshotDataUrl && screenshotAttached ? screenshotDataUrl : void 0
|
|
698
707
|
});
|
|
708
|
+
if (!outcome.ok) {
|
|
709
|
+
if (outcome.failureKind === "credentials" || outcome.failureKind === "quota") {
|
|
710
|
+
setSubmitError("Could not send \u2014 check the project API key or plan quota.");
|
|
711
|
+
setPhase("error");
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (outcome.failureKind === "rate_limited") {
|
|
715
|
+
setSubmitError("Sending too fast \u2014 we queued this and will retry shortly.");
|
|
716
|
+
setPhase("error");
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
setSubmitError("Queued for retry \u2014 we will send it when the connection is back.");
|
|
720
|
+
setPhase("error");
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
699
723
|
setPhase("sent");
|
|
700
724
|
setTimeout(handleClose, 1400);
|
|
701
725
|
} catch {
|
|
702
|
-
|
|
726
|
+
setSubmitError("Something went wrong. Please try again.");
|
|
727
|
+
setPhase("error");
|
|
703
728
|
}
|
|
704
729
|
};
|
|
705
730
|
const activeScreenshot = screenshotDataUrl && screenshotAttached ? screenshotDataUrl : null;
|
|
706
731
|
const pal = (0, import_core3.mushiPalette)(dark ? "dark" : "light");
|
|
707
|
-
const colors = dark ? { bg: pal.paper, text: pal.ink, sub: pal.inkMuted, card: pal.paperRaised, accent: import_core3.MUSHI_BANNER_NEON.bg, accentInk: import_core3.MUSHI_BANNER_NEON.fg, border: pal.ruleStrong, backdrop: "rgba(0,0,0,0.6)", disabled:
|
|
732
|
+
const colors = dark ? { bg: pal.paper, text: pal.ink, sub: pal.inkMuted, card: pal.paperRaised, accent: import_core3.MUSHI_BANNER_NEON.bg, accentInk: import_core3.MUSHI_BANNER_NEON.fg, border: pal.ruleStrong, backdrop: "rgba(0,0,0,0.6)", disabled: import_core3.MUSHI_CONTROL_DISABLED.dark, disabledText: pal.inkFaint } : { bg: pal.paperRaised, text: pal.ink, sub: pal.inkMuted, card: pal.paper, accent: import_core3.MUSHI_BANNER_NEON.bg, accentInk: import_core3.MUSHI_BANNER_NEON.fg, border: pal.ruleStrong, backdrop: "rgba(0,0,0,0.35)", disabled: import_core3.MUSHI_CONTROL_DISABLED.light, disabledText: pal.inkFaint };
|
|
708
733
|
const sheetTabs = ["report", "inbox", ...assistantEnabled ? ["assistant"] : []];
|
|
709
|
-
const canSubmit = !!category && description.trim().length
|
|
734
|
+
const canSubmit = !!category && description.trim().length >= 20 && (phase === "form" || phase === "error");
|
|
710
735
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Modal, { visible, transparent: true, animationType: "none", statusBarTranslucent: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
711
736
|
import_react_native2.KeyboardAvoidingView,
|
|
712
737
|
{
|
|
@@ -734,7 +759,7 @@ var MushiBottomSheet = ({
|
|
|
734
759
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: [s.brandEyebrow, { color: import_core3.MUSHI_BANNER_NEON.fg }], children: "MUSHI \xB7 BETA" }),
|
|
735
760
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: [s.brandTitle, { color: import_core3.MUSHI_BANNER_NEON.fg }], children: sheetTab === "assistant" ? assistantLabel : sheetTab === "inbox" ? t.flows.reports.title : t.widget.title })
|
|
736
761
|
] }),
|
|
737
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.View, { style: s.tabRow, children: sheetTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
762
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.View, { style: [s.tabRow, { borderBottomColor: colors.border }], children: sheetTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
738
763
|
import_react_native2.TouchableOpacity,
|
|
739
764
|
{
|
|
740
765
|
onPress: () => {
|
|
@@ -887,16 +912,22 @@ var MushiBottomSheet = ({
|
|
|
887
912
|
multiline: true,
|
|
888
913
|
textAlignVertical: "top",
|
|
889
914
|
value: description,
|
|
890
|
-
onChangeText:
|
|
891
|
-
|
|
915
|
+
onChangeText: (text) => {
|
|
916
|
+
setDescription(text.slice(0, 4e3));
|
|
917
|
+
if (submitError) setSubmitError(null);
|
|
918
|
+
if (phase === "error") setPhase("form");
|
|
919
|
+
},
|
|
920
|
+
editable: phase === "form" || phase === "error",
|
|
921
|
+
maxLength: 4e3
|
|
892
922
|
}
|
|
893
923
|
),
|
|
924
|
+
submitError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: { color: colors.accent, marginTop: 6, fontSize: 12 }, children: submitError }) : null,
|
|
894
925
|
activeScreenshot ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native2.View, { style: s.screenshotRow, children: [
|
|
895
926
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
896
927
|
import_react_native2.Image,
|
|
897
928
|
{
|
|
898
929
|
source: { uri: activeScreenshot },
|
|
899
|
-
style: s.screenshotThumb,
|
|
930
|
+
style: [s.screenshotThumb, { backgroundColor: colors.text }],
|
|
900
931
|
accessibilityLabel: t.step3.screenshotPreviewAlt
|
|
901
932
|
}
|
|
902
933
|
),
|
|
@@ -957,10 +988,10 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
957
988
|
left: 0,
|
|
958
989
|
right: 0,
|
|
959
990
|
minHeight: SHEET_HEIGHT,
|
|
960
|
-
borderTopLeftRadius:
|
|
961
|
-
borderTopRightRadius:
|
|
962
|
-
paddingBottom: import_react_native2.Platform.OS === "ios" ? 34 :
|
|
963
|
-
shadowColor:
|
|
991
|
+
borderTopLeftRadius: import_core3.MUSHI_RADIUS.sheet,
|
|
992
|
+
borderTopRightRadius: import_core3.MUSHI_RADIUS.sheet,
|
|
993
|
+
paddingBottom: import_react_native2.Platform.OS === "ios" ? 34 : import_core3.MUSHI_SPACING.roomy,
|
|
994
|
+
shadowColor: import_core3.MUSHI_SHADOW_INK,
|
|
964
995
|
shadowOffset: { width: 0, height: -3 },
|
|
965
996
|
shadowOpacity: 0.12,
|
|
966
997
|
shadowRadius: 8,
|
|
@@ -968,22 +999,22 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
968
999
|
},
|
|
969
1000
|
handleArea: {
|
|
970
1001
|
alignItems: "center",
|
|
971
|
-
paddingVertical:
|
|
1002
|
+
paddingVertical: import_core3.MUSHI_SPACING.comfy
|
|
972
1003
|
},
|
|
973
1004
|
handle: {
|
|
974
1005
|
width: 40,
|
|
975
1006
|
height: 5,
|
|
976
|
-
borderRadius:
|
|
1007
|
+
borderRadius: import_core3.MUSHI_RADIUS.control,
|
|
977
1008
|
opacity: 0.5
|
|
978
1009
|
},
|
|
979
1010
|
brandHeader: {
|
|
980
|
-
paddingHorizontal:
|
|
981
|
-
paddingTop:
|
|
982
|
-
paddingBottom:
|
|
1011
|
+
paddingHorizontal: import_core3.MUSHI_SPACING.lounge,
|
|
1012
|
+
paddingTop: import_core3.MUSHI_SPACING.comfy,
|
|
1013
|
+
paddingBottom: import_core3.MUSHI_SPACING.comfy,
|
|
983
1014
|
borderBottomWidth: 1.5
|
|
984
1015
|
},
|
|
985
1016
|
brandEyebrow: {
|
|
986
|
-
fontSize:
|
|
1017
|
+
fontSize: import_core3.MUSHI_TYPE.sizeLabel,
|
|
987
1018
|
fontWeight: "700",
|
|
988
1019
|
letterSpacing: 1.2,
|
|
989
1020
|
marginBottom: 2,
|
|
@@ -995,59 +1026,61 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
995
1026
|
letterSpacing: -0.2
|
|
996
1027
|
},
|
|
997
1028
|
body: {
|
|
998
|
-
paddingHorizontal:
|
|
999
|
-
paddingTop:
|
|
1029
|
+
paddingHorizontal: import_core3.MUSHI_SPACING.lounge,
|
|
1030
|
+
paddingTop: import_core3.MUSHI_SPACING.tight
|
|
1000
1031
|
},
|
|
1001
1032
|
title: {
|
|
1002
1033
|
fontSize: 20,
|
|
1003
1034
|
fontWeight: "700",
|
|
1004
|
-
marginBottom:
|
|
1035
|
+
marginBottom: import_core3.MUSHI_SPACING.roomy
|
|
1005
1036
|
},
|
|
1006
1037
|
stepLabel: {
|
|
1007
|
-
fontSize:
|
|
1038
|
+
fontSize: import_core3.MUSHI_TYPE.sizeLabel,
|
|
1008
1039
|
fontWeight: "600",
|
|
1009
1040
|
letterSpacing: 0.2,
|
|
1010
|
-
marginTop:
|
|
1011
|
-
marginBottom:
|
|
1041
|
+
marginTop: import_core3.MUSHI_SPACING.snug,
|
|
1042
|
+
marginBottom: import_core3.MUSHI_SPACING.comfy,
|
|
1012
1043
|
textTransform: "uppercase"
|
|
1013
1044
|
},
|
|
1014
1045
|
catRow: {
|
|
1015
1046
|
flexDirection: "row",
|
|
1016
1047
|
justifyContent: "space-between",
|
|
1017
|
-
marginBottom:
|
|
1048
|
+
marginBottom: import_core3.MUSHI_SPACING.roomy
|
|
1018
1049
|
},
|
|
1019
1050
|
catBtn: {
|
|
1020
1051
|
alignItems: "center",
|
|
1021
|
-
paddingVertical:
|
|
1052
|
+
paddingVertical: import_core3.MUSHI_SPACING.comfy,
|
|
1022
1053
|
paddingHorizontal: 6,
|
|
1023
|
-
borderRadius:
|
|
1054
|
+
borderRadius: import_core3.MUSHI_RADIUS.card,
|
|
1024
1055
|
borderWidth: 1,
|
|
1025
1056
|
flex: 1,
|
|
1026
|
-
marginHorizontal: 3
|
|
1057
|
+
marginHorizontal: 3,
|
|
1058
|
+
minHeight: 44
|
|
1027
1059
|
},
|
|
1028
1060
|
catEmoji: {
|
|
1029
1061
|
fontSize: 22,
|
|
1030
|
-
marginBottom:
|
|
1062
|
+
marginBottom: import_core3.MUSHI_SPACING.tight
|
|
1031
1063
|
},
|
|
1032
1064
|
catLabel: {
|
|
1033
|
-
fontSize:
|
|
1065
|
+
fontSize: import_core3.MUSHI_TYPE.sizeLabel,
|
|
1034
1066
|
fontWeight: "600"
|
|
1035
1067
|
},
|
|
1036
1068
|
input: {
|
|
1037
1069
|
borderWidth: 1,
|
|
1038
|
-
borderRadius:
|
|
1070
|
+
borderRadius: import_core3.MUSHI_RADIUS.card,
|
|
1039
1071
|
padding: 14,
|
|
1040
1072
|
fontSize: 15,
|
|
1041
1073
|
minHeight: 100,
|
|
1042
|
-
marginBottom:
|
|
1074
|
+
marginBottom: import_core3.MUSHI_SPACING.roomy
|
|
1043
1075
|
},
|
|
1044
1076
|
submitBtn: {
|
|
1045
|
-
borderRadius:
|
|
1077
|
+
borderRadius: import_core3.MUSHI_RADIUS.card,
|
|
1046
1078
|
paddingVertical: 14,
|
|
1047
|
-
alignItems: "center"
|
|
1079
|
+
alignItems: "center",
|
|
1080
|
+
minHeight: 44
|
|
1048
1081
|
},
|
|
1049
1082
|
submitText: {
|
|
1050
|
-
color:
|
|
1083
|
+
color: import_core3.MUSHI_INVERSE,
|
|
1051
1084
|
fontSize: 16,
|
|
1052
1085
|
fontWeight: "700"
|
|
1053
1086
|
},
|
|
@@ -1078,7 +1111,6 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
1078
1111
|
width: 48,
|
|
1079
1112
|
height: 36,
|
|
1080
1113
|
borderRadius: 4,
|
|
1081
|
-
backgroundColor: "#000",
|
|
1082
1114
|
flexShrink: 0
|
|
1083
1115
|
},
|
|
1084
1116
|
screenshotMeta: {
|
|
@@ -1100,7 +1132,6 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
1100
1132
|
tabRow: {
|
|
1101
1133
|
flexDirection: "row",
|
|
1102
1134
|
borderBottomWidth: import_react_native2.StyleSheet.hairlineWidth,
|
|
1103
|
-
borderBottomColor: "#ccc",
|
|
1104
1135
|
marginBottom: 8
|
|
1105
1136
|
},
|
|
1106
1137
|
tabBtn: {
|
|
@@ -1212,7 +1243,8 @@ var MushiFloatingButton = ({
|
|
|
1212
1243
|
const currentX = (0, import_react2.useRef)(baseX);
|
|
1213
1244
|
const currentY = (0, import_react2.useRef)(0);
|
|
1214
1245
|
const isDragging = (0, import_react2.useRef)(false);
|
|
1215
|
-
const
|
|
1246
|
+
const pal = (0, import_core4.mushiPalette)(dark ? "dark" : "light");
|
|
1247
|
+
const accentColor = accent ?? pal.accent;
|
|
1216
1248
|
(0, import_react2.useEffect)(() => {
|
|
1217
1249
|
if (!persist) return;
|
|
1218
1250
|
void loadStoredPos().then((pos) => {
|
|
@@ -1315,7 +1347,7 @@ var MushiFloatingButton = ({
|
|
|
1315
1347
|
left: posX,
|
|
1316
1348
|
bottom: import_react_native3.Animated.add(new import_react_native3.Animated.Value(baseBottom), import_react_native3.Animated.multiply(posY, -1)),
|
|
1317
1349
|
backgroundColor: accentColor,
|
|
1318
|
-
shadowColor: dark ? accentColor :
|
|
1350
|
+
shadowColor: dark ? accentColor : pal.ink,
|
|
1319
1351
|
transform: [{ scale }]
|
|
1320
1352
|
}
|
|
1321
1353
|
],
|
|
@@ -1404,7 +1436,7 @@ var s3 = import_react_native4.StyleSheet.create({
|
|
|
1404
1436
|
backgroundColor: import_core5.MUSHI_BANNER_NEON.bg,
|
|
1405
1437
|
borderTopWidth: 1,
|
|
1406
1438
|
borderTopColor: import_core5.MUSHI_BANNER_NEON.border,
|
|
1407
|
-
zIndex:
|
|
1439
|
+
zIndex: import_core5.MUSHI_Z.banner,
|
|
1408
1440
|
elevation: 8,
|
|
1409
1441
|
paddingBottom: import_react_native4.Platform.OS === "ios" ? 0 : 0
|
|
1410
1442
|
},
|
|
@@ -1412,12 +1444,13 @@ var s3 = import_react_native4.StyleSheet.create({
|
|
|
1412
1444
|
flex: 1,
|
|
1413
1445
|
flexDirection: "row",
|
|
1414
1446
|
alignItems: "center",
|
|
1415
|
-
gap:
|
|
1416
|
-
paddingHorizontal:
|
|
1417
|
-
paddingVertical:
|
|
1447
|
+
gap: import_core5.MUSHI_SPACING.snug,
|
|
1448
|
+
paddingHorizontal: import_core5.MUSHI_SPACING.comfy,
|
|
1449
|
+
paddingVertical: import_core5.MUSHI_SPACING.snug,
|
|
1450
|
+
minHeight: 44
|
|
1418
1451
|
},
|
|
1419
1452
|
eyebrow: {
|
|
1420
|
-
fontSize:
|
|
1453
|
+
fontSize: import_core5.MUSHI_TYPE.sizeLabel,
|
|
1421
1454
|
fontWeight: "700",
|
|
1422
1455
|
letterSpacing: 1.2,
|
|
1423
1456
|
color: import_core5.MUSHI_BANNER_NEON.fg
|
|
@@ -1448,7 +1481,7 @@ var s3 = import_react_native4.StyleSheet.create({
|
|
|
1448
1481
|
|
|
1449
1482
|
// src/version.ts
|
|
1450
1483
|
var MUSHI_SDK_PACKAGE = "@mushi-mushi/react-native";
|
|
1451
|
-
var MUSHI_SDK_VERSION = "0.
|
|
1484
|
+
var MUSHI_SDK_VERSION = "0.21.0" ? "0.21.0" : "0.17.0";
|
|
1452
1485
|
|
|
1453
1486
|
// src/provider.tsx
|
|
1454
1487
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
@@ -1738,21 +1771,34 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1738
1771
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1739
1772
|
};
|
|
1740
1773
|
const client = apiClientRef.current;
|
|
1741
|
-
if (!client)
|
|
1774
|
+
if (!client) {
|
|
1775
|
+
console.error(
|
|
1776
|
+
"[Mushi] submitReport called before the SDK finished initializing. Ensure <MushiProvider> is mounted with a valid projectId and apiKey."
|
|
1777
|
+
);
|
|
1778
|
+
return { ok: false, failureKind: "permanent" };
|
|
1779
|
+
}
|
|
1742
1780
|
const result = await client.submitReport(report);
|
|
1743
1781
|
if (!result.ok) {
|
|
1744
|
-
const
|
|
1782
|
+
const code = result.error?.code ?? "";
|
|
1783
|
+
const isCredentialError = code === "HTTP_401" || code === "HTTP_403" || code.includes("UNAUTHORIZED") || code.includes("FORBIDDEN") || code.includes("INVALID_API_KEY");
|
|
1745
1784
|
if (isCredentialError) {
|
|
1746
1785
|
const where = apiEndpoint.replace(/\/$/, "") === import_core6.DEFAULT_API_ENDPOINT ? "Get the correct values at: https://kensaur.us/mushi-mushi/admin/projects" : "Get the correct values from your Mushi console's Projects page.";
|
|
1747
1786
|
console.error(
|
|
1748
1787
|
'[Mushi] Credentials rejected. Check your projectId and apiKey (must have "report:write" scope). ' + where
|
|
1749
1788
|
);
|
|
1750
|
-
return;
|
|
1789
|
+
return { ok: false, failureKind: "credentials" };
|
|
1751
1790
|
}
|
|
1791
|
+
const isRateLimited = code === "HTTP_429" || code.includes("RATE_LIMIT");
|
|
1752
1792
|
await queueRef.current?.enqueue(report);
|
|
1753
1793
|
config.callbacks?.onReportQueued?.(report.id);
|
|
1794
|
+
return {
|
|
1795
|
+
ok: false,
|
|
1796
|
+
queued: true,
|
|
1797
|
+
failureKind: isRateLimited ? "rate_limited" : "retrying"
|
|
1798
|
+
};
|
|
1754
1799
|
} else {
|
|
1755
1800
|
config.callbacks?.onReportSubmitted?.(report.id);
|
|
1801
|
+
return { ok: true };
|
|
1756
1802
|
}
|
|
1757
1803
|
},
|
|
1758
1804
|
[config.projectId, config.callbacks, apiEndpoint]
|
package/dist/index.d.cts
CHANGED
|
@@ -128,7 +128,11 @@ interface MushiRNInstance {
|
|
|
128
128
|
description: string;
|
|
129
129
|
category: string;
|
|
130
130
|
screenshotDataUrl?: string;
|
|
131
|
-
}): Promise<
|
|
131
|
+
}): Promise<{
|
|
132
|
+
ok: boolean;
|
|
133
|
+
queued?: boolean;
|
|
134
|
+
failureKind?: 'offline' | 'retrying' | 'rate_limited' | 'quota' | 'permanent' | 'credentials';
|
|
135
|
+
}>;
|
|
132
136
|
getDeviceInfo(): ReturnType<typeof getDeviceInfo>;
|
|
133
137
|
getConsoleEntries(): ReturnType<ReturnType<typeof setupConsoleCapture>['getEntries']>;
|
|
134
138
|
getNetworkEntries(): ReturnType<ReturnType<typeof setupNetworkCapture>['getEntries']>;
|
package/dist/index.d.ts
CHANGED
|
@@ -128,7 +128,11 @@ interface MushiRNInstance {
|
|
|
128
128
|
description: string;
|
|
129
129
|
category: string;
|
|
130
130
|
screenshotDataUrl?: string;
|
|
131
|
-
}): Promise<
|
|
131
|
+
}): Promise<{
|
|
132
|
+
ok: boolean;
|
|
133
|
+
queued?: boolean;
|
|
134
|
+
failureKind?: 'offline' | 'retrying' | 'rate_limited' | 'quota' | 'permanent' | 'credentials';
|
|
135
|
+
}>;
|
|
132
136
|
getDeviceInfo(): ReturnType<typeof getDeviceInfo>;
|
|
133
137
|
getConsoleEntries(): ReturnType<ReturnType<typeof setupConsoleCapture>['getEntries']>;
|
|
134
138
|
getNetworkEntries(): ReturnType<ReturnType<typeof setupNetworkCapture>['getEntries']>;
|
package/dist/index.js
CHANGED
|
@@ -474,7 +474,17 @@ import {
|
|
|
474
474
|
ScrollView,
|
|
475
475
|
useColorScheme
|
|
476
476
|
} from "react-native";
|
|
477
|
-
import {
|
|
477
|
+
import {
|
|
478
|
+
mushiPalette,
|
|
479
|
+
MUSHI_BANNER_NEON,
|
|
480
|
+
MUSHI_COPY,
|
|
481
|
+
MUSHI_CONTROL_DISABLED,
|
|
482
|
+
MUSHI_INVERSE,
|
|
483
|
+
MUSHI_RADIUS,
|
|
484
|
+
MUSHI_SHADOW_INK,
|
|
485
|
+
MUSHI_SPACING,
|
|
486
|
+
MUSHI_TYPE
|
|
487
|
+
} from "@mushi-mushi/core";
|
|
478
488
|
import { getLocale } from "@mushi-mushi/web/i18n";
|
|
479
489
|
|
|
480
490
|
// src/reporter-status.ts
|
|
@@ -540,6 +550,7 @@ var MushiBottomSheet = ({
|
|
|
540
550
|
const [category, setCategory] = useState(null);
|
|
541
551
|
const [description, setDescription] = useState("");
|
|
542
552
|
const [phase, setPhase] = useState("form");
|
|
553
|
+
const [submitError, setSubmitError] = useState(null);
|
|
543
554
|
const [sheetTab, setSheetTab] = useState("report");
|
|
544
555
|
const [inboxReports, setInboxReports] = useState([]);
|
|
545
556
|
const [inboxLoading, setInboxLoading] = useState(false);
|
|
@@ -613,6 +624,8 @@ var MushiBottomSheet = ({
|
|
|
613
624
|
...prev,
|
|
614
625
|
{ role: "bot", text: botText, options: reply.options }
|
|
615
626
|
]);
|
|
627
|
+
} catch {
|
|
628
|
+
setAssistantError("Could not reach the assistant. Try again.");
|
|
616
629
|
} finally {
|
|
617
630
|
setAssistantSending(false);
|
|
618
631
|
}
|
|
@@ -686,24 +699,46 @@ var MushiBottomSheet = ({
|
|
|
686
699
|
).current;
|
|
687
700
|
const handleSubmit = async () => {
|
|
688
701
|
if (!category || !description.trim() || !mushi) return;
|
|
702
|
+
const trimmed = description.trim();
|
|
703
|
+
if (trimmed.length < 20) {
|
|
704
|
+
setSubmitError("Please write at least 20 characters so we can understand the issue.");
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
setSubmitError(null);
|
|
689
708
|
setPhase("sending");
|
|
690
709
|
try {
|
|
691
|
-
await mushi.submitReport({
|
|
710
|
+
const outcome = await mushi.submitReport({
|
|
692
711
|
category,
|
|
693
|
-
description:
|
|
712
|
+
description: trimmed,
|
|
694
713
|
screenshotDataUrl: screenshotDataUrl && screenshotAttached ? screenshotDataUrl : void 0
|
|
695
714
|
});
|
|
715
|
+
if (!outcome.ok) {
|
|
716
|
+
if (outcome.failureKind === "credentials" || outcome.failureKind === "quota") {
|
|
717
|
+
setSubmitError("Could not send \u2014 check the project API key or plan quota.");
|
|
718
|
+
setPhase("error");
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
if (outcome.failureKind === "rate_limited") {
|
|
722
|
+
setSubmitError("Sending too fast \u2014 we queued this and will retry shortly.");
|
|
723
|
+
setPhase("error");
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
setSubmitError("Queued for retry \u2014 we will send it when the connection is back.");
|
|
727
|
+
setPhase("error");
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
696
730
|
setPhase("sent");
|
|
697
731
|
setTimeout(handleClose, 1400);
|
|
698
732
|
} catch {
|
|
699
|
-
|
|
733
|
+
setSubmitError("Something went wrong. Please try again.");
|
|
734
|
+
setPhase("error");
|
|
700
735
|
}
|
|
701
736
|
};
|
|
702
737
|
const activeScreenshot = screenshotDataUrl && screenshotAttached ? screenshotDataUrl : null;
|
|
703
738
|
const pal = mushiPalette(dark ? "dark" : "light");
|
|
704
|
-
const colors = dark ? { bg: pal.paper, text: pal.ink, sub: pal.inkMuted, card: pal.paperRaised, accent: MUSHI_BANNER_NEON.bg, accentInk: MUSHI_BANNER_NEON.fg, border: pal.ruleStrong, backdrop: "rgba(0,0,0,0.6)", disabled:
|
|
739
|
+
const colors = dark ? { bg: pal.paper, text: pal.ink, sub: pal.inkMuted, card: pal.paperRaised, accent: MUSHI_BANNER_NEON.bg, accentInk: MUSHI_BANNER_NEON.fg, border: pal.ruleStrong, backdrop: "rgba(0,0,0,0.6)", disabled: MUSHI_CONTROL_DISABLED.dark, disabledText: pal.inkFaint } : { bg: pal.paperRaised, text: pal.ink, sub: pal.inkMuted, card: pal.paper, accent: MUSHI_BANNER_NEON.bg, accentInk: MUSHI_BANNER_NEON.fg, border: pal.ruleStrong, backdrop: "rgba(0,0,0,0.35)", disabled: MUSHI_CONTROL_DISABLED.light, disabledText: pal.inkFaint };
|
|
705
740
|
const sheetTabs = ["report", "inbox", ...assistantEnabled ? ["assistant"] : []];
|
|
706
|
-
const canSubmit = !!category && description.trim().length
|
|
741
|
+
const canSubmit = !!category && description.trim().length >= 20 && (phase === "form" || phase === "error");
|
|
707
742
|
return /* @__PURE__ */ jsx(Modal, { visible, transparent: true, animationType: "none", statusBarTranslucent: true, children: /* @__PURE__ */ jsxs(
|
|
708
743
|
KeyboardAvoidingView,
|
|
709
744
|
{
|
|
@@ -731,7 +766,7 @@ var MushiBottomSheet = ({
|
|
|
731
766
|
/* @__PURE__ */ jsx(Text, { style: [s.brandEyebrow, { color: MUSHI_BANNER_NEON.fg }], children: "MUSHI \xB7 BETA" }),
|
|
732
767
|
/* @__PURE__ */ jsx(Text, { style: [s.brandTitle, { color: MUSHI_BANNER_NEON.fg }], children: sheetTab === "assistant" ? assistantLabel : sheetTab === "inbox" ? t.flows.reports.title : t.widget.title })
|
|
733
768
|
] }),
|
|
734
|
-
/* @__PURE__ */ jsx(View, { style: s.tabRow, children: sheetTabs.map((tab) => /* @__PURE__ */ jsx(
|
|
769
|
+
/* @__PURE__ */ jsx(View, { style: [s.tabRow, { borderBottomColor: colors.border }], children: sheetTabs.map((tab) => /* @__PURE__ */ jsx(
|
|
735
770
|
TouchableOpacity,
|
|
736
771
|
{
|
|
737
772
|
onPress: () => {
|
|
@@ -884,16 +919,22 @@ var MushiBottomSheet = ({
|
|
|
884
919
|
multiline: true,
|
|
885
920
|
textAlignVertical: "top",
|
|
886
921
|
value: description,
|
|
887
|
-
onChangeText:
|
|
888
|
-
|
|
922
|
+
onChangeText: (text) => {
|
|
923
|
+
setDescription(text.slice(0, 4e3));
|
|
924
|
+
if (submitError) setSubmitError(null);
|
|
925
|
+
if (phase === "error") setPhase("form");
|
|
926
|
+
},
|
|
927
|
+
editable: phase === "form" || phase === "error",
|
|
928
|
+
maxLength: 4e3
|
|
889
929
|
}
|
|
890
930
|
),
|
|
931
|
+
submitError ? /* @__PURE__ */ jsx(Text, { style: { color: colors.accent, marginTop: 6, fontSize: 12 }, children: submitError }) : null,
|
|
891
932
|
activeScreenshot ? /* @__PURE__ */ jsxs(View, { style: s.screenshotRow, children: [
|
|
892
933
|
/* @__PURE__ */ jsx(
|
|
893
934
|
Image,
|
|
894
935
|
{
|
|
895
936
|
source: { uri: activeScreenshot },
|
|
896
|
-
style: s.screenshotThumb,
|
|
937
|
+
style: [s.screenshotThumb, { backgroundColor: colors.text }],
|
|
897
938
|
accessibilityLabel: t.step3.screenshotPreviewAlt
|
|
898
939
|
}
|
|
899
940
|
),
|
|
@@ -954,10 +995,10 @@ var s = StyleSheet.create({
|
|
|
954
995
|
left: 0,
|
|
955
996
|
right: 0,
|
|
956
997
|
minHeight: SHEET_HEIGHT,
|
|
957
|
-
borderTopLeftRadius:
|
|
958
|
-
borderTopRightRadius:
|
|
959
|
-
paddingBottom: Platform2.OS === "ios" ? 34 :
|
|
960
|
-
shadowColor:
|
|
998
|
+
borderTopLeftRadius: MUSHI_RADIUS.sheet,
|
|
999
|
+
borderTopRightRadius: MUSHI_RADIUS.sheet,
|
|
1000
|
+
paddingBottom: Platform2.OS === "ios" ? 34 : MUSHI_SPACING.roomy,
|
|
1001
|
+
shadowColor: MUSHI_SHADOW_INK,
|
|
961
1002
|
shadowOffset: { width: 0, height: -3 },
|
|
962
1003
|
shadowOpacity: 0.12,
|
|
963
1004
|
shadowRadius: 8,
|
|
@@ -965,22 +1006,22 @@ var s = StyleSheet.create({
|
|
|
965
1006
|
},
|
|
966
1007
|
handleArea: {
|
|
967
1008
|
alignItems: "center",
|
|
968
|
-
paddingVertical:
|
|
1009
|
+
paddingVertical: MUSHI_SPACING.comfy
|
|
969
1010
|
},
|
|
970
1011
|
handle: {
|
|
971
1012
|
width: 40,
|
|
972
1013
|
height: 5,
|
|
973
|
-
borderRadius:
|
|
1014
|
+
borderRadius: MUSHI_RADIUS.control,
|
|
974
1015
|
opacity: 0.5
|
|
975
1016
|
},
|
|
976
1017
|
brandHeader: {
|
|
977
|
-
paddingHorizontal:
|
|
978
|
-
paddingTop:
|
|
979
|
-
paddingBottom:
|
|
1018
|
+
paddingHorizontal: MUSHI_SPACING.lounge,
|
|
1019
|
+
paddingTop: MUSHI_SPACING.comfy,
|
|
1020
|
+
paddingBottom: MUSHI_SPACING.comfy,
|
|
980
1021
|
borderBottomWidth: 1.5
|
|
981
1022
|
},
|
|
982
1023
|
brandEyebrow: {
|
|
983
|
-
fontSize:
|
|
1024
|
+
fontSize: MUSHI_TYPE.sizeLabel,
|
|
984
1025
|
fontWeight: "700",
|
|
985
1026
|
letterSpacing: 1.2,
|
|
986
1027
|
marginBottom: 2,
|
|
@@ -992,59 +1033,61 @@ var s = StyleSheet.create({
|
|
|
992
1033
|
letterSpacing: -0.2
|
|
993
1034
|
},
|
|
994
1035
|
body: {
|
|
995
|
-
paddingHorizontal:
|
|
996
|
-
paddingTop:
|
|
1036
|
+
paddingHorizontal: MUSHI_SPACING.lounge,
|
|
1037
|
+
paddingTop: MUSHI_SPACING.tight
|
|
997
1038
|
},
|
|
998
1039
|
title: {
|
|
999
1040
|
fontSize: 20,
|
|
1000
1041
|
fontWeight: "700",
|
|
1001
|
-
marginBottom:
|
|
1042
|
+
marginBottom: MUSHI_SPACING.roomy
|
|
1002
1043
|
},
|
|
1003
1044
|
stepLabel: {
|
|
1004
|
-
fontSize:
|
|
1045
|
+
fontSize: MUSHI_TYPE.sizeLabel,
|
|
1005
1046
|
fontWeight: "600",
|
|
1006
1047
|
letterSpacing: 0.2,
|
|
1007
|
-
marginTop:
|
|
1008
|
-
marginBottom:
|
|
1048
|
+
marginTop: MUSHI_SPACING.snug,
|
|
1049
|
+
marginBottom: MUSHI_SPACING.comfy,
|
|
1009
1050
|
textTransform: "uppercase"
|
|
1010
1051
|
},
|
|
1011
1052
|
catRow: {
|
|
1012
1053
|
flexDirection: "row",
|
|
1013
1054
|
justifyContent: "space-between",
|
|
1014
|
-
marginBottom:
|
|
1055
|
+
marginBottom: MUSHI_SPACING.roomy
|
|
1015
1056
|
},
|
|
1016
1057
|
catBtn: {
|
|
1017
1058
|
alignItems: "center",
|
|
1018
|
-
paddingVertical:
|
|
1059
|
+
paddingVertical: MUSHI_SPACING.comfy,
|
|
1019
1060
|
paddingHorizontal: 6,
|
|
1020
|
-
borderRadius:
|
|
1061
|
+
borderRadius: MUSHI_RADIUS.card,
|
|
1021
1062
|
borderWidth: 1,
|
|
1022
1063
|
flex: 1,
|
|
1023
|
-
marginHorizontal: 3
|
|
1064
|
+
marginHorizontal: 3,
|
|
1065
|
+
minHeight: 44
|
|
1024
1066
|
},
|
|
1025
1067
|
catEmoji: {
|
|
1026
1068
|
fontSize: 22,
|
|
1027
|
-
marginBottom:
|
|
1069
|
+
marginBottom: MUSHI_SPACING.tight
|
|
1028
1070
|
},
|
|
1029
1071
|
catLabel: {
|
|
1030
|
-
fontSize:
|
|
1072
|
+
fontSize: MUSHI_TYPE.sizeLabel,
|
|
1031
1073
|
fontWeight: "600"
|
|
1032
1074
|
},
|
|
1033
1075
|
input: {
|
|
1034
1076
|
borderWidth: 1,
|
|
1035
|
-
borderRadius:
|
|
1077
|
+
borderRadius: MUSHI_RADIUS.card,
|
|
1036
1078
|
padding: 14,
|
|
1037
1079
|
fontSize: 15,
|
|
1038
1080
|
minHeight: 100,
|
|
1039
|
-
marginBottom:
|
|
1081
|
+
marginBottom: MUSHI_SPACING.roomy
|
|
1040
1082
|
},
|
|
1041
1083
|
submitBtn: {
|
|
1042
|
-
borderRadius:
|
|
1084
|
+
borderRadius: MUSHI_RADIUS.card,
|
|
1043
1085
|
paddingVertical: 14,
|
|
1044
|
-
alignItems: "center"
|
|
1086
|
+
alignItems: "center",
|
|
1087
|
+
minHeight: 44
|
|
1045
1088
|
},
|
|
1046
1089
|
submitText: {
|
|
1047
|
-
color:
|
|
1090
|
+
color: MUSHI_INVERSE,
|
|
1048
1091
|
fontSize: 16,
|
|
1049
1092
|
fontWeight: "700"
|
|
1050
1093
|
},
|
|
@@ -1075,7 +1118,6 @@ var s = StyleSheet.create({
|
|
|
1075
1118
|
width: 48,
|
|
1076
1119
|
height: 36,
|
|
1077
1120
|
borderRadius: 4,
|
|
1078
|
-
backgroundColor: "#000",
|
|
1079
1121
|
flexShrink: 0
|
|
1080
1122
|
},
|
|
1081
1123
|
screenshotMeta: {
|
|
@@ -1097,7 +1139,6 @@ var s = StyleSheet.create({
|
|
|
1097
1139
|
tabRow: {
|
|
1098
1140
|
flexDirection: "row",
|
|
1099
1141
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
1100
|
-
borderBottomColor: "#ccc",
|
|
1101
1142
|
marginBottom: 8
|
|
1102
1143
|
},
|
|
1103
1144
|
tabBtn: {
|
|
@@ -1216,7 +1257,8 @@ var MushiFloatingButton = ({
|
|
|
1216
1257
|
const currentX = useRef2(baseX);
|
|
1217
1258
|
const currentY = useRef2(0);
|
|
1218
1259
|
const isDragging = useRef2(false);
|
|
1219
|
-
const
|
|
1260
|
+
const pal = mushiPalette2(dark ? "dark" : "light");
|
|
1261
|
+
const accentColor = accent ?? pal.accent;
|
|
1220
1262
|
useEffect2(() => {
|
|
1221
1263
|
if (!persist) return;
|
|
1222
1264
|
void loadStoredPos().then((pos) => {
|
|
@@ -1319,7 +1361,7 @@ var MushiFloatingButton = ({
|
|
|
1319
1361
|
left: posX,
|
|
1320
1362
|
bottom: Animated2.add(new Animated2.Value(baseBottom), Animated2.multiply(posY, -1)),
|
|
1321
1363
|
backgroundColor: accentColor,
|
|
1322
|
-
shadowColor: dark ? accentColor :
|
|
1364
|
+
shadowColor: dark ? accentColor : pal.ink,
|
|
1323
1365
|
transform: [{ scale }]
|
|
1324
1366
|
}
|
|
1325
1367
|
],
|
|
@@ -1358,7 +1400,7 @@ import {
|
|
|
1358
1400
|
StyleSheet as StyleSheet3,
|
|
1359
1401
|
Platform as Platform4
|
|
1360
1402
|
} from "react-native";
|
|
1361
|
-
import { MUSHI_BANNER_NEON as MUSHI_BANNER_NEON2, MUSHI_COPY as MUSHI_COPY3, MUSHI_GEOMETRY as MUSHI_GEOMETRY2 } from "@mushi-mushi/core";
|
|
1403
|
+
import { MUSHI_BANNER_NEON as MUSHI_BANNER_NEON2, MUSHI_COPY as MUSHI_COPY3, MUSHI_GEOMETRY as MUSHI_GEOMETRY2, MUSHI_SPACING as MUSHI_SPACING2, MUSHI_TYPE as MUSHI_TYPE2, MUSHI_Z } from "@mushi-mushi/core";
|
|
1362
1404
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1363
1405
|
var MUSHI_BANNER_DEFAULT_HEIGHT = MUSHI_GEOMETRY2.bannerHeight;
|
|
1364
1406
|
var MushiBanner = ({
|
|
@@ -1414,7 +1456,7 @@ var s3 = StyleSheet3.create({
|
|
|
1414
1456
|
backgroundColor: MUSHI_BANNER_NEON2.bg,
|
|
1415
1457
|
borderTopWidth: 1,
|
|
1416
1458
|
borderTopColor: MUSHI_BANNER_NEON2.border,
|
|
1417
|
-
zIndex:
|
|
1459
|
+
zIndex: MUSHI_Z.banner,
|
|
1418
1460
|
elevation: 8,
|
|
1419
1461
|
paddingBottom: Platform4.OS === "ios" ? 0 : 0
|
|
1420
1462
|
},
|
|
@@ -1422,12 +1464,13 @@ var s3 = StyleSheet3.create({
|
|
|
1422
1464
|
flex: 1,
|
|
1423
1465
|
flexDirection: "row",
|
|
1424
1466
|
alignItems: "center",
|
|
1425
|
-
gap:
|
|
1426
|
-
paddingHorizontal:
|
|
1427
|
-
paddingVertical:
|
|
1467
|
+
gap: MUSHI_SPACING2.snug,
|
|
1468
|
+
paddingHorizontal: MUSHI_SPACING2.comfy,
|
|
1469
|
+
paddingVertical: MUSHI_SPACING2.snug,
|
|
1470
|
+
minHeight: 44
|
|
1428
1471
|
},
|
|
1429
1472
|
eyebrow: {
|
|
1430
|
-
fontSize:
|
|
1473
|
+
fontSize: MUSHI_TYPE2.sizeLabel,
|
|
1431
1474
|
fontWeight: "700",
|
|
1432
1475
|
letterSpacing: 1.2,
|
|
1433
1476
|
color: MUSHI_BANNER_NEON2.fg
|
|
@@ -1458,7 +1501,7 @@ var s3 = StyleSheet3.create({
|
|
|
1458
1501
|
|
|
1459
1502
|
// src/version.ts
|
|
1460
1503
|
var MUSHI_SDK_PACKAGE = "@mushi-mushi/react-native";
|
|
1461
|
-
var MUSHI_SDK_VERSION = "0.
|
|
1504
|
+
var MUSHI_SDK_VERSION = "0.21.0" ? "0.21.0" : "0.17.0";
|
|
1462
1505
|
|
|
1463
1506
|
// src/provider.tsx
|
|
1464
1507
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -1748,21 +1791,34 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1748
1791
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1749
1792
|
};
|
|
1750
1793
|
const client = apiClientRef.current;
|
|
1751
|
-
if (!client)
|
|
1794
|
+
if (!client) {
|
|
1795
|
+
console.error(
|
|
1796
|
+
"[Mushi] submitReport called before the SDK finished initializing. Ensure <MushiProvider> is mounted with a valid projectId and apiKey."
|
|
1797
|
+
);
|
|
1798
|
+
return { ok: false, failureKind: "permanent" };
|
|
1799
|
+
}
|
|
1752
1800
|
const result = await client.submitReport(report);
|
|
1753
1801
|
if (!result.ok) {
|
|
1754
|
-
const
|
|
1802
|
+
const code = result.error?.code ?? "";
|
|
1803
|
+
const isCredentialError = code === "HTTP_401" || code === "HTTP_403" || code.includes("UNAUTHORIZED") || code.includes("FORBIDDEN") || code.includes("INVALID_API_KEY");
|
|
1755
1804
|
if (isCredentialError) {
|
|
1756
1805
|
const where = apiEndpoint.replace(/\/$/, "") === DEFAULT_API_ENDPOINT ? "Get the correct values at: https://kensaur.us/mushi-mushi/admin/projects" : "Get the correct values from your Mushi console's Projects page.";
|
|
1757
1806
|
console.error(
|
|
1758
1807
|
'[Mushi] Credentials rejected. Check your projectId and apiKey (must have "report:write" scope). ' + where
|
|
1759
1808
|
);
|
|
1760
|
-
return;
|
|
1809
|
+
return { ok: false, failureKind: "credentials" };
|
|
1761
1810
|
}
|
|
1811
|
+
const isRateLimited = code === "HTTP_429" || code.includes("RATE_LIMIT");
|
|
1762
1812
|
await queueRef.current?.enqueue(report);
|
|
1763
1813
|
config.callbacks?.onReportQueued?.(report.id);
|
|
1814
|
+
return {
|
|
1815
|
+
ok: false,
|
|
1816
|
+
queued: true,
|
|
1817
|
+
failureKind: isRateLimited ? "rate_limited" : "retrying"
|
|
1818
|
+
};
|
|
1764
1819
|
} else {
|
|
1765
1820
|
config.callbacks?.onReportSubmitted?.(report.id);
|
|
1821
|
+
return { ok: true };
|
|
1766
1822
|
}
|
|
1767
1823
|
},
|
|
1768
1824
|
[config.projectId, config.callbacks, apiEndpoint]
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mushi-mushi/react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React Native SDK for Mushi Mushi — shake-to-report, bottom sheet widget, full context capture",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
|
+
"size-limit": [
|
|
10
|
+
{
|
|
11
|
+
"path": "dist/index.js",
|
|
12
|
+
"limit": "22 KB"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
9
15
|
"peerDependencies": {
|
|
10
16
|
"@react-native-async-storage/async-storage": ">=1.19",
|
|
11
17
|
"@react-native-community/netinfo": ">=11",
|
|
@@ -37,16 +43,21 @@
|
|
|
37
43
|
}
|
|
38
44
|
},
|
|
39
45
|
"dependencies": {
|
|
40
|
-
"@mushi-mushi/core": "^1.
|
|
41
|
-
"@mushi-mushi/web": "^1.
|
|
46
|
+
"@mushi-mushi/core": "^1.26.0",
|
|
47
|
+
"@mushi-mushi/web": "^1.26.0"
|
|
42
48
|
},
|
|
43
49
|
"devDependencies": {
|
|
50
|
+
"@size-limit/file": "^12.1.0",
|
|
44
51
|
"@types/react": "^19.2.17",
|
|
52
|
+
"eslint": "^10.6.0",
|
|
45
53
|
"react": "^19.2.7",
|
|
46
54
|
"react-native": "^0.86.0",
|
|
55
|
+
"size-limit": "^12.1.0",
|
|
47
56
|
"tsup": "^8.5.1",
|
|
48
57
|
"typescript": "^6.0.3",
|
|
49
|
-
"vitest": "^4.1.
|
|
58
|
+
"vitest": "^4.1.9",
|
|
59
|
+
"@mushi-mushi/eslint-config": "0.0.0",
|
|
60
|
+
"eslint-plugin-mushi-mushi": "0.3.1"
|
|
50
61
|
},
|
|
51
62
|
"author": "Kenji Sakuramoto (https://bsky.app/profile/mushimushi.dev)",
|
|
52
63
|
"repository": {
|
|
@@ -116,7 +127,9 @@
|
|
|
116
127
|
},
|
|
117
128
|
"scripts": {
|
|
118
129
|
"build": "tsup",
|
|
130
|
+
"lint": "eslint src/",
|
|
119
131
|
"test": "vitest run",
|
|
120
|
-
"typecheck": "tsc --noEmit"
|
|
132
|
+
"typecheck": "tsc --noEmit",
|
|
133
|
+
"size": "size-limit"
|
|
121
134
|
}
|
|
122
135
|
}
|