@graineai/inapp-react-native 0.1.0 → 0.2.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/README.md +46 -10
- package/dist/client.js +9 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/react-native/ui.d.ts +7 -4
- package/dist/react-native/ui.js +55 -104
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -9,30 +9,57 @@ Android and iOS from one package.
|
|
|
9
9
|
npm install @graineai/inapp-react-native
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
Full documentation: **https://
|
|
12
|
+
Full documentation: **https://www.graine.ai/docs/in-app**
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Before you write any code
|
|
17
|
+
|
|
18
|
+
**Add `app://` to the agent's Allowed domains** — dashboard → your agent →
|
|
19
|
+
Embed & Widgets → Allowed domains.
|
|
20
|
+
|
|
21
|
+
A mobile app sends no `Origin` header; there is no page, so there is nothing for
|
|
22
|
+
a browser to report. Without that entry every call is refused:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
403 This endpoint must be called from a browser page.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Nothing in your code fixes that, which is why it is the first thing on this
|
|
29
|
+
page. The SDK detects this specific failure and says so, rather than passing the
|
|
30
|
+
server's browser-shaped message through.
|
|
13
31
|
|
|
14
32
|
---
|
|
15
33
|
|
|
16
34
|
## Quick start
|
|
17
35
|
|
|
18
36
|
```tsx
|
|
19
|
-
import { GraineProvider,
|
|
37
|
+
import { GraineProvider, GraineAgentBar } from "@graineai/inapp-react-native";
|
|
20
38
|
|
|
21
39
|
export default function App() {
|
|
22
40
|
return (
|
|
23
41
|
<GraineProvider
|
|
24
|
-
baseUrl="https://
|
|
42
|
+
baseUrl="https://www.graine.ai"
|
|
25
43
|
publishableKey="pk_live_…"
|
|
26
44
|
variables={{ customer_name: user.firstName }}
|
|
27
45
|
>
|
|
28
46
|
<RootNavigator />
|
|
29
|
-
<
|
|
47
|
+
<GraineAgentBar accent="#318CE7" name="Assistant" />
|
|
30
48
|
</GraineProvider>
|
|
31
49
|
);
|
|
32
50
|
}
|
|
33
51
|
```
|
|
34
52
|
|
|
35
|
-
Your publishable key is in the
|
|
53
|
+
Your publishable key is in the dashboard under **Embed & Widgets**.
|
|
54
|
+
|
|
55
|
+
**Mount the bar at your root, never inside a screen.** The agent's job is to
|
|
56
|
+
move the customer to the right screen — and a bar rendered inside one unmounts
|
|
57
|
+
the instant it does, killing the conversation on the very action that proves the
|
|
58
|
+
feature works.
|
|
59
|
+
|
|
60
|
+
It is a bar rather than a sheet for the same reason: a sheet covers the screen
|
|
61
|
+
the agent is helping with, so the moment it says "tap Continue", Continue is
|
|
62
|
+
behind it.
|
|
36
63
|
|
|
37
64
|
## Tell the agent what the customer can see
|
|
38
65
|
|
|
@@ -55,8 +82,17 @@ discuss a screen the customer has left.
|
|
|
55
82
|
|
|
56
83
|
## Let the agent act
|
|
57
84
|
|
|
58
|
-
Declare the
|
|
59
|
-
|
|
85
|
+
Declare it in the dashboard first — **Embed & Widgets → What this agent may do
|
|
86
|
+
in your app**. Presets cover navigate, fill a field, highlight and retry. Saving
|
|
87
|
+
there also pushes the tools to the agent; a declaration that is saved but not
|
|
88
|
+
synced leaves the agent with the tools it had before, and looks like nothing
|
|
89
|
+
happened.
|
|
90
|
+
|
|
91
|
+
Declared server-side and nowhere else: a client that could name its own tools
|
|
92
|
+
could invent one and talk the model into calling it. The SDK may only narrow the
|
|
93
|
+
list, never extend it.
|
|
94
|
+
|
|
95
|
+
Then implement a handler with the same name, where it belongs:
|
|
60
96
|
|
|
61
97
|
```tsx
|
|
62
98
|
useGraineAction("fill_pan_from_aadhaar", async () => {
|
|
@@ -117,7 +153,7 @@ requests a permission on your behalf.
|
|
|
117
153
|
> from an APK or IPA, and the web domain allowlist does not apply to mobile
|
|
118
154
|
> apps. Rate limits, single-use connection tickets and your account's credit
|
|
119
155
|
> limits are what bound it. See the
|
|
120
|
-
> [security notes](https://
|
|
156
|
+
> [security notes](https://www.graine.ai/docs/in-app/privacy) before launch.
|
|
121
157
|
|
|
122
158
|
## Requirements
|
|
123
159
|
|
|
@@ -133,7 +169,7 @@ requests a permission on your behalf.
|
|
|
133
169
|
| `useGraineScreen(context)` | Report what this screen shows. |
|
|
134
170
|
| `useGraineAction(name, handler)` | Implement one action the agent may call. |
|
|
135
171
|
| `useGraineVoice(adapter, opts)` | Microphone, playback and barge-in. |
|
|
136
|
-
| `
|
|
172
|
+
| `GraineAgentBar` | Optional default UI. Mount at the root. Replace it with your own. |
|
|
137
173
|
| `addMaskRule(pattern, token)` | Mask an app-specific identifier format. |
|
|
138
174
|
|
|
139
175
|
`GraineInAppClient` is exported for apps that want the transport without the
|
|
@@ -141,7 +177,7 @@ React layer.
|
|
|
141
177
|
|
|
142
178
|
## Support
|
|
143
179
|
|
|
144
|
-
https://
|
|
180
|
+
https://www.graine.ai/docs/in-app
|
|
145
181
|
|
|
146
182
|
## License
|
|
147
183
|
|
package/dist/client.js
CHANGED
|
@@ -51,8 +51,16 @@ export class GraineInAppClient {
|
|
|
51
51
|
body: JSON.stringify({ publishableKey: this.opts.publishableKey }),
|
|
52
52
|
});
|
|
53
53
|
const data = await res.json().catch(() => ({}));
|
|
54
|
-
if (!res.ok)
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
if (res.status === 403) {
|
|
56
|
+
throw new Error(`This agent does not accept connections from a mobile app yet. Add "app://" to its ` +
|
|
57
|
+
`Allowed domains in the dashboard, under Embed & Widgets. (server said: ${data?.error ?? "forbidden"})`);
|
|
58
|
+
}
|
|
59
|
+
if (res.status === 401) {
|
|
60
|
+
throw new Error(`Publishable key was rejected. Check it against Embed & Widgets in the dashboard.`);
|
|
61
|
+
}
|
|
55
62
|
throw new Error(data?.error || `Embed rejected (${res.status})`);
|
|
63
|
+
}
|
|
56
64
|
this.config = data;
|
|
57
65
|
return this.config;
|
|
58
66
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export { GraineInAppClient, type GraineInAppOptions, type SessionConfig } from "
|
|
|
3
3
|
export { VoiceSession, liveAudioStreamAdapter, base64ToPcm16, CAPTURE_SAMPLE_RATE, type AudioAdapter, type VoiceSessionOptions, } from "./voice";
|
|
4
4
|
export { addMaskRule, maskDeep, maskString } from "./mask";
|
|
5
5
|
export { GraineProvider, useGraineAgent, useGraineScreen, useGraineAction, useGraineVoice, type GraineProviderProps, type Turn, } from "./react-native/index";
|
|
6
|
-
export { GraineLauncher, type GraineLauncherProps } from "./react-native/ui";
|
|
6
|
+
export { GraineAgentBar, GraineLauncher, type GraineAgentBarProps, type GraineLauncherProps } from "./react-native/ui";
|
package/dist/index.js
CHANGED
|
@@ -2,4 +2,4 @@ export { GraineInAppClient } from "./client";
|
|
|
2
2
|
export { VoiceSession, liveAudioStreamAdapter, base64ToPcm16, CAPTURE_SAMPLE_RATE, } from "./voice";
|
|
3
3
|
export { addMaskRule, maskDeep, maskString } from "./mask";
|
|
4
4
|
export { GraineProvider, useGraineAgent, useGraineScreen, useGraineAction, useGraineVoice, } from "./react-native/index";
|
|
5
|
-
export { GraineLauncher } from "./react-native/ui";
|
|
5
|
+
export { GraineAgentBar, GraineLauncher } from "./react-native/ui";
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
export interface
|
|
2
|
+
export interface GraineAgentBarProps {
|
|
3
3
|
accent?: string;
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
name?: string;
|
|
5
|
+
bottomInset?: number;
|
|
6
|
+
hidden?: boolean;
|
|
6
7
|
}
|
|
7
|
-
export declare function
|
|
8
|
+
export declare function GraineAgentBar({ accent, name, bottomInset, hidden, }: GraineAgentBarProps): React.JSX.Element | null;
|
|
9
|
+
export declare const GraineLauncher: typeof GraineAgentBar;
|
|
10
|
+
export type GraineLauncherProps = GraineAgentBarProps;
|
package/dist/react-native/ui.js
CHANGED
|
@@ -1,125 +1,76 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
-
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { ActivityIndicator,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { ActivityIndicator, KeyboardAvoidingView, Platform, Pressable, ScrollView, StyleSheet, Text, TextInput, View, } from "react-native";
|
|
4
4
|
import { useGraineAgent } from "./index";
|
|
5
|
-
export function
|
|
6
|
-
const { connected, connecting, error,
|
|
5
|
+
export function GraineAgentBar({ accent = "#318CE7", name = "Assistant", bottomInset = 96, hidden = false, }) {
|
|
6
|
+
const { connected, connecting, error, messages, send } = useGraineAgent();
|
|
7
|
+
const [expanded, setExpanded] = useState(false);
|
|
7
8
|
const [draft, setDraft] = useState("");
|
|
8
9
|
const scrollRef = useRef(null);
|
|
9
|
-
const [
|
|
10
|
+
const last = messages[messages.length - 1];
|
|
11
|
+
const status = error
|
|
12
|
+
? "unavailable"
|
|
13
|
+
: connecting ? "connecting…"
|
|
14
|
+
: last?.role === "agent" && last.live ? "speaking"
|
|
15
|
+
: connected ? "listening"
|
|
16
|
+
: "starting…";
|
|
10
17
|
useEffect(() => {
|
|
11
|
-
if (
|
|
12
|
-
setUnread((n) => n + 1);
|
|
13
|
-
if (open)
|
|
14
|
-
setUnread(0);
|
|
15
|
-
}, [messages, open]);
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
if (open)
|
|
18
|
+
if (expanded)
|
|
18
19
|
requestAnimationFrame(() => scrollRef.current?.scrollToEnd({ animated: true }));
|
|
19
|
-
}, [messages,
|
|
20
|
-
const slide = useRef(new Animated.Value(0)).current;
|
|
20
|
+
}, [messages, expanded]);
|
|
21
21
|
useEffect(() => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
useNativeDriver: true,
|
|
27
|
-
}).start();
|
|
28
|
-
}, [open, slide]);
|
|
29
|
-
if (error)
|
|
30
|
-
return null;
|
|
31
|
-
const submit = () => {
|
|
22
|
+
if (!expanded && last?.role === "agent")
|
|
23
|
+
setExpanded(true);
|
|
24
|
+
}, [messages.length]);
|
|
25
|
+
const submit = useCallback(() => {
|
|
32
26
|
const text = draft.trim();
|
|
33
27
|
if (!text)
|
|
34
28
|
return;
|
|
35
29
|
setDraft("");
|
|
36
30
|
send(text);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
bottom: bottom + 68,
|
|
42
|
-
opacity: slide,
|
|
43
|
-
transform: [{ translateY: slide.interpolate({ inputRange: [0, 1], outputRange: [12, 0] }) }],
|
|
44
|
-
},
|
|
45
|
-
], children: [_jsxs(View, { style: [styles.header, { backgroundColor: accent }], children: [_jsx(Text, { style: styles.headerTitle, children: title }), _jsxs(View, { style: styles.headerRight, children: [connected ? _jsx(View, { style: styles.liveDot }) : connecting ? _jsx(ActivityIndicator, { size: "small", color: "#fff" }) : null, _jsx(Pressable, { onPress: () => setOpen(false), hitSlop: 12, accessibilityLabel: "Close assistant", children: _jsx(Text, { style: styles.close, children: "\u2715" }) })] })] }), _jsxs(ScrollView, { ref: scrollRef, style: styles.transcript, contentContainerStyle: styles.transcriptInner, children: [messages.length === 0 && (_jsx(Text, { style: styles.empty, children: "I can see what you're working on \u2014 ask me anything, or I'll step in if you get stuck." })), messages.map((m, i) => (_jsx(View, { style: [
|
|
31
|
+
}, [draft, send]);
|
|
32
|
+
if (hidden || error)
|
|
33
|
+
return null;
|
|
34
|
+
return (_jsxs(View, { pointerEvents: "box-none", style: [styles.root, { bottom: bottomInset }], children: [expanded && (_jsxs(View, { style: styles.panel, children: [_jsxs(ScrollView, { ref: scrollRef, style: styles.transcript, contentContainerStyle: styles.transcriptInner, showsVerticalScrollIndicator: false, children: [messages.length === 0 && (_jsx(Text, { style: styles.empty, children: "I can see what you're working on \u2014 ask me anything, or I'll step in if you get stuck." })), messages.map((m, i) => (_jsx(View, { style: [
|
|
46
35
|
styles.bubble,
|
|
47
|
-
m.role === "agent"
|
|
48
|
-
|
|
49
|
-
: [styles.customerBubble, { backgroundColor: accent }],
|
|
50
|
-
], children: _jsx(Text, { style: m.role === "agent" ? styles.agentText : styles.customerText, children: m.text }) }, i)))] }), _jsx(KeyboardAvoidingView, { behavior: Platform.OS === "ios" ? "padding" : undefined, children: _jsxs(View, { style: styles.composer, children: [_jsx(TextInput, { style: styles.input, value: draft, onChangeText: setDraft, placeholder: "Type a message", placeholderTextColor: "#9aa0a6", onSubmitEditing: submit, returnKeyType: "send", blurOnSubmit: false }), _jsx(Pressable, { onPress: submit, disabled: !draft.trim(), style: [styles.sendButton, { backgroundColor: accent, opacity: draft.trim() ? 1 : 0.4 }], accessibilityLabel: "Send", children: _jsx(Text, { style: styles.sendLabel, children: "\u2191" }) })] }) })] })), _jsxs(Pressable, { onPress: () => setOpen(!open), style: [styles.launcher, { backgroundColor: accent, bottom }], accessibilityLabel: open ? "Close assistant" : "Open assistant", accessibilityRole: "button", children: [_jsx(Text, { style: styles.launcherIcon, children: open ? "✕" : "✦" }), !open && unread > 0 && _jsx(View, { style: styles.badge })] })] }));
|
|
36
|
+
m.role === "agent" ? styles.agentBubble : [styles.customerBubble, { backgroundColor: accent }],
|
|
37
|
+
], children: _jsx(Text, { style: m.role === "agent" ? styles.agentText : styles.customerText, children: m.text }) }, i)))] }), _jsx(KeyboardAvoidingView, { behavior: Platform.OS === "ios" ? "padding" : undefined, children: _jsxs(View, { style: styles.composer, children: [_jsx(TextInput, { style: styles.input, value: draft, onChangeText: setDraft, placeholder: "Type a message", placeholderTextColor: "#8a8f98", onSubmitEditing: submit, returnKeyType: "send", blurOnSubmit: false }), _jsx(Pressable, { onPress: submit, disabled: !draft.trim(), accessibilityLabel: "Send", style: [styles.send, { backgroundColor: accent, opacity: draft.trim() ? 1 : 0.4 }], children: _jsx(Text, { style: styles.sendLabel, children: "\u2191" }) })] }) })] })), _jsxs(View, { style: styles.bar, children: [_jsx(View, { style: [styles.avatar, { backgroundColor: accent }], children: _jsx(Text, { style: styles.avatarGlyph, children: "\u2726" }) }), _jsxs(Pressable, { onPress: () => setExpanded((v) => !v), style: { flex: 1 }, accessibilityLabel: expanded ? "Collapse" : "Expand", children: [_jsx(Text, { style: styles.name, children: name }), _jsx(Text, { style: styles.status, children: status })] }), connecting ? _jsx(ActivityIndicator, { size: "small", color: accent }) : null, _jsx(Pressable, { onPress: () => setExpanded((v) => !v), hitSlop: 8, accessibilityLabel: expanded ? "Collapse" : "Expand", style: styles.round, children: _jsx(Text, { style: styles.chevron, children: expanded ? "⌄" : "⌃" }) })] })] }));
|
|
51
38
|
}
|
|
39
|
+
export const GraineLauncher = GraineAgentBar;
|
|
52
40
|
const styles = StyleSheet.create({
|
|
53
|
-
|
|
54
|
-
position: "absolute",
|
|
55
|
-
right: 20,
|
|
56
|
-
width: 56,
|
|
57
|
-
height: 56,
|
|
58
|
-
borderRadius: 28,
|
|
59
|
-
alignItems: "center",
|
|
60
|
-
justifyContent: "center",
|
|
61
|
-
elevation: 8,
|
|
62
|
-
shadowColor: "#000",
|
|
63
|
-
shadowOpacity: 0.22,
|
|
64
|
-
shadowRadius: 10,
|
|
65
|
-
shadowOffset: { width: 0, height: 4 },
|
|
66
|
-
},
|
|
67
|
-
launcherIcon: { color: "#fff", fontSize: 22, lineHeight: 26 },
|
|
68
|
-
badge: {
|
|
69
|
-
position: "absolute",
|
|
70
|
-
top: 12,
|
|
71
|
-
right: 12,
|
|
72
|
-
width: 10,
|
|
73
|
-
height: 10,
|
|
74
|
-
borderRadius: 5,
|
|
75
|
-
backgroundColor: "#ff3b30",
|
|
76
|
-
borderWidth: 2,
|
|
77
|
-
borderColor: "#fff",
|
|
78
|
-
},
|
|
41
|
+
root: { position: "absolute", left: 12, right: 12, zIndex: 900 },
|
|
79
42
|
panel: {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
left: 20,
|
|
83
|
-
maxHeight: 460,
|
|
84
|
-
backgroundColor: "#fff",
|
|
85
|
-
borderRadius: 18,
|
|
86
|
-
overflow: "hidden",
|
|
87
|
-
elevation: 12,
|
|
88
|
-
shadowColor: "#000",
|
|
89
|
-
shadowOpacity: 0.18,
|
|
90
|
-
shadowRadius: 24,
|
|
91
|
-
shadowOffset: { width: 0, height: 10 },
|
|
43
|
+
backgroundColor: "#17161A", borderRadius: 22, borderWidth: 1, borderColor: "rgba(255,255,255,0.09)",
|
|
44
|
+
marginBottom: 8, overflow: "hidden",
|
|
92
45
|
},
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
transcript: { maxHeight: 320 },
|
|
99
|
-
transcriptInner: { padding: 12, gap: 8 },
|
|
100
|
-
empty: { color: "#9aa0a6", fontSize: 13, lineHeight: 19, padding: 8 },
|
|
101
|
-
bubble: { maxWidth: "86%", paddingHorizontal: 12, paddingVertical: 9, borderRadius: 16 },
|
|
102
|
-
agentBubble: { alignSelf: "flex-start", backgroundColor: "#f1f3f4", borderBottomLeftRadius: 5 },
|
|
46
|
+
transcript: { maxHeight: 240 },
|
|
47
|
+
transcriptInner: { padding: 14, gap: 8 },
|
|
48
|
+
empty: { color: "#9aa0a6", fontSize: 14, lineHeight: 21 },
|
|
49
|
+
bubble: { maxWidth: "88%", paddingHorizontal: 12, paddingVertical: 9, borderRadius: 16 },
|
|
50
|
+
agentBubble: { alignSelf: "flex-start", backgroundColor: "rgba(255,255,255,0.08)", borderBottomLeftRadius: 5 },
|
|
103
51
|
customerBubble: { alignSelf: "flex-end", borderBottomRightRadius: 5 },
|
|
104
|
-
agentText: { color: "#
|
|
105
|
-
customerText: { color: "#fff", fontSize: 14, lineHeight:
|
|
106
|
-
composer: {
|
|
107
|
-
flexDirection: "row",
|
|
108
|
-
alignItems: "center",
|
|
109
|
-
gap: 8,
|
|
110
|
-
padding: 10,
|
|
111
|
-
borderTopWidth: StyleSheet.hairlineWidth,
|
|
112
|
-
borderTopColor: "#e8eaed",
|
|
113
|
-
},
|
|
52
|
+
agentText: { color: "#F2F3F5", fontSize: 14.5, lineHeight: 21 },
|
|
53
|
+
customerText: { color: "#fff", fontSize: 14.5, lineHeight: 21 },
|
|
54
|
+
composer: { flexDirection: "row", alignItems: "center", gap: 8, paddingHorizontal: 12, paddingBottom: 12 },
|
|
114
55
|
input: {
|
|
115
|
-
flex: 1,
|
|
116
|
-
paddingHorizontal: 12,
|
|
117
|
-
paddingVertical: Platform.OS === "ios" ? 10 : 6,
|
|
118
|
-
backgroundColor: "#f1f3f4",
|
|
119
|
-
borderRadius: 20,
|
|
120
|
-
fontSize: 14,
|
|
121
|
-
color: "#202124",
|
|
56
|
+
flex: 1, borderWidth: 1, borderColor: "rgba(255,255,255,0.12)", borderRadius: 16,
|
|
57
|
+
paddingHorizontal: 12, paddingVertical: Platform.OS === "ios" ? 10 : 7, color: "#F2F3F5", fontSize: 14.5,
|
|
122
58
|
},
|
|
123
|
-
|
|
59
|
+
send: { width: 38, height: 38, borderRadius: 19, alignItems: "center", justifyContent: "center" },
|
|
124
60
|
sendLabel: { color: "#fff", fontSize: 17, lineHeight: 20 },
|
|
61
|
+
bar: {
|
|
62
|
+
flexDirection: "row", alignItems: "center", gap: 11,
|
|
63
|
+
backgroundColor: "#17161A", borderRadius: 26, borderWidth: 1, borderColor: "rgba(255,255,255,0.09)",
|
|
64
|
+
paddingHorizontal: 8, paddingVertical: 8,
|
|
65
|
+
shadowColor: "#000", shadowOpacity: 0.3, shadowRadius: 16, shadowOffset: { width: 0, height: 6 }, elevation: 12,
|
|
66
|
+
},
|
|
67
|
+
avatar: { width: 38, height: 38, borderRadius: 19, alignItems: "center", justifyContent: "center" },
|
|
68
|
+
avatarGlyph: { color: "#fff", fontSize: 17, lineHeight: 20 },
|
|
69
|
+
name: { color: "#F2F3F5", fontSize: 15, fontWeight: "800" },
|
|
70
|
+
status: { color: "#9aa0a6", fontSize: 12.5, marginTop: 1 },
|
|
71
|
+
round: {
|
|
72
|
+
width: 38, height: 38, borderRadius: 19, alignItems: "center", justifyContent: "center",
|
|
73
|
+
backgroundColor: "rgba(255,255,255,0.08)",
|
|
74
|
+
},
|
|
75
|
+
chevron: { color: "#F2F3F5", fontSize: 16, lineHeight: 18 },
|
|
125
76
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graineai/inapp-react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Graine in-app agent for React Native \u2014 an agent that sees the screen your customer is on and can act on it.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"engines": {
|
|
57
57
|
"node": ">=18"
|
|
58
58
|
},
|
|
59
|
-
"homepage": "https://
|
|
59
|
+
"homepage": "https://www.graine.ai/docs/in-app",
|
|
60
60
|
"bugs": {
|
|
61
|
-
"url": "https://
|
|
61
|
+
"url": "https://www.graine.ai/docs/in-app"
|
|
62
62
|
}
|
|
63
63
|
}
|