@mobileai/react-native 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -7
- package/lib/module/components/AIAgent.js +40 -4
- package/lib/module/components/AIAgent.js.map +1 -1
- package/lib/module/components/AgentChatBar.js +177 -29
- package/lib/module/components/AgentChatBar.js.map +1 -1
- package/lib/module/core/AgentRuntime.js +268 -126
- package/lib/module/core/AgentRuntime.js.map +1 -1
- package/lib/module/core/FiberTreeWalker.js +74 -20
- package/lib/module/core/FiberTreeWalker.js.map +1 -1
- package/lib/module/core/systemPrompt.js +164 -0
- package/lib/module/core/systemPrompt.js.map +1 -0
- package/lib/module/providers/GeminiProvider.js +189 -73
- package/lib/module/providers/GeminiProvider.js.map +1 -1
- package/lib/typescript/src/components/AIAgent.d.ts +9 -1
- package/lib/typescript/src/components/AIAgent.d.ts.map +1 -1
- package/lib/typescript/src/components/AgentChatBar.d.ts +4 -3
- package/lib/typescript/src/components/AgentChatBar.d.ts.map +1 -1
- package/lib/typescript/src/core/AgentRuntime.d.ts +16 -0
- package/lib/typescript/src/core/AgentRuntime.d.ts.map +1 -1
- package/lib/typescript/src/core/FiberTreeWalker.d.ts +5 -0
- package/lib/typescript/src/core/FiberTreeWalker.d.ts.map +1 -1
- package/lib/typescript/src/core/systemPrompt.d.ts +9 -0
- package/lib/typescript/src/core/systemPrompt.d.ts.map +1 -0
- package/lib/typescript/src/core/types.d.ts +51 -13
- package/lib/typescript/src/core/types.d.ts.map +1 -1
- package/lib/typescript/src/providers/GeminiProvider.d.ts +33 -13
- package/lib/typescript/src/providers/GeminiProvider.d.ts.map +1 -1
- package/package.json +16 -14
- package/src/components/AIAgent.tsx +41 -1
- package/src/components/AgentChatBar.tsx +150 -28
- package/src/core/AgentRuntime.ts +287 -131
- package/src/core/FiberTreeWalker.ts +74 -19
- package/src/core/systemPrompt.ts +162 -0
- package/src/core/types.ts +58 -10
- package/src/providers/GeminiProvider.ts +174 -101
|
@@ -1,23 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* GeminiProvider —
|
|
3
|
-
*
|
|
2
|
+
* GeminiProvider — Gemini API integration with structured action pattern.
|
|
3
|
+
*
|
|
4
|
+
* Uses a single forced function call (`agent_step`) that bundles
|
|
5
|
+
* structured reasoning (evaluation, memory, plan) alongside the action.
|
|
6
|
+
* This replaces free-form text + separate tool calls for stability.
|
|
4
7
|
*/
|
|
5
|
-
import type { AIProvider, ToolDefinition, AgentStep } from '../core/types';
|
|
8
|
+
import type { AIProvider, ToolDefinition, AgentStep, ProviderResult } from '../core/types';
|
|
6
9
|
export declare class GeminiProvider implements AIProvider {
|
|
7
10
|
private apiKey;
|
|
8
11
|
private model;
|
|
9
12
|
constructor(apiKey: string, model?: string);
|
|
10
|
-
generateContent(systemPrompt: string, userMessage: string, tools: ToolDefinition[], history: AgentStep[]): Promise<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
generateContent(systemPrompt: string, userMessage: string, tools: ToolDefinition[], history: AgentStep[]): Promise<ProviderResult>;
|
|
14
|
+
/**
|
|
15
|
+
* Builds a single `agent_step` function declaration that combines:
|
|
16
|
+
* - Structured reasoning fields (previous_goal_eval, memory, plan)
|
|
17
|
+
* - action_name (enum of all available tool names)
|
|
18
|
+
* - All tool parameter fields as flat top-level properties
|
|
19
|
+
*
|
|
20
|
+
* Flat schema avoids Gemini's "deeply nested schema" rejection in ANY mode.
|
|
21
|
+
*/
|
|
22
|
+
private buildAgentStepDeclaration;
|
|
18
23
|
private mapParamType;
|
|
24
|
+
/**
|
|
25
|
+
* Builds Gemini conversation contents.
|
|
26
|
+
*
|
|
27
|
+
* Each step is a STATELESS single-turn request (matching page-agent's approach):
|
|
28
|
+
* - System prompt has general instructions
|
|
29
|
+
* - User message contains full context: task, history, screen state
|
|
30
|
+
* - Model responds with agent_step function call
|
|
31
|
+
*
|
|
32
|
+
* History is embedded as text in assembleUserPrompt (via <agent_history>),
|
|
33
|
+
* NOT as functionCall/functionResponse pairs. This avoids Gemini's
|
|
34
|
+
* conversation format requirements and thought_signature complexity.
|
|
35
|
+
*/
|
|
19
36
|
private buildContents;
|
|
20
|
-
|
|
21
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Parses the Gemini response expecting a single agent_step function call.
|
|
39
|
+
* Extracts structured reasoning + action, and determines which tool to execute.
|
|
40
|
+
*/
|
|
41
|
+
private parseAgentStepResponse;
|
|
22
42
|
}
|
|
23
43
|
//# sourceMappingURL=GeminiProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GeminiProvider.d.ts","sourceRoot":"","sources":["../../../../src/providers/GeminiProvider.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"GeminiProvider.d.ts","sourceRoot":"","sources":["../../../../src/providers/GeminiProvider.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,cAAc,EAAkB,MAAM,eAAe,CAAC;AAsB3G,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAA2B;IAKxD,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,EAAE,SAAS,EAAE,GACnB,OAAO,CAAC,cAAc,CAAC;IA2D1B;;;;;;;OAOG;IACH,OAAO,CAAC,yBAAyB;IA0DjC,OAAO,CAAC,YAAY;IAYpB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IASrB;;;OAGG;IACH,OAAO,CAAC,sBAAsB;CA2E/B"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mobileai/react-native",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Build autonomous AI agents for React Native and Expo apps. Provides AI-native UI traversal, tool calling, and structured reasoning.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"source": "./src/index.ts",
|
|
7
7
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -40,32 +40,34 @@
|
|
|
40
40
|
"prepare": "bob build",
|
|
41
41
|
"typecheck": "tsc",
|
|
42
42
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
43
|
-
"test": "jest"
|
|
43
|
+
"test": "jest",
|
|
44
|
+
"publish:dual": "bash scripts/publish-dual.sh",
|
|
45
|
+
"publish:dual:dry": "bash scripts/publish-dual.sh --dry-run"
|
|
44
46
|
},
|
|
45
47
|
"keywords": [
|
|
46
48
|
"react-native",
|
|
49
|
+
"expo",
|
|
47
50
|
"ai",
|
|
51
|
+
"llm",
|
|
48
52
|
"agent",
|
|
49
|
-
"
|
|
53
|
+
"autonomous-agent",
|
|
50
54
|
"gemini",
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"ios",
|
|
57
|
-
"android"
|
|
55
|
+
"openai",
|
|
56
|
+
"artificial-intelligence",
|
|
57
|
+
"mobile-ai",
|
|
58
|
+
"ui-automation",
|
|
59
|
+
"react native ai agent"
|
|
58
60
|
],
|
|
59
61
|
"repository": {
|
|
60
62
|
"type": "git",
|
|
61
|
-
"url": "git+https://github.com/mohamed2m2018/react-native
|
|
63
|
+
"url": "git+https://github.com/mohamed2m2018/mobileai-react-native.git"
|
|
62
64
|
},
|
|
63
65
|
"author": "Mohamed Salah <mohamedsalah@example.com> (https://example.com)",
|
|
64
66
|
"license": "MIT",
|
|
65
67
|
"bugs": {
|
|
66
|
-
"url": "https://github.com/mohamed2m2018/react-native
|
|
68
|
+
"url": "https://github.com/mohamed2m2018/mobileai-react-native/issues"
|
|
67
69
|
},
|
|
68
|
-
"homepage": "https://github.com/mohamed2m2018/react-native
|
|
70
|
+
"homepage": "https://github.com/mohamed2m2018/mobileai-react-native#readme",
|
|
69
71
|
"publishConfig": {
|
|
70
72
|
"registry": "https://registry.npmjs.org/"
|
|
71
73
|
},
|
|
@@ -75,6 +75,14 @@ interface AIAgentProps {
|
|
|
75
75
|
stepDelay?: number;
|
|
76
76
|
/** WebSocket URL to companion MCP server bridge (e.g., ws://localhost:3101) */
|
|
77
77
|
mcpServerUrl?: string;
|
|
78
|
+
/** Expo Router instance (from useRouter()) */
|
|
79
|
+
router?: {
|
|
80
|
+
push: (href: string) => void;
|
|
81
|
+
replace: (href: string) => void;
|
|
82
|
+
back: () => void;
|
|
83
|
+
};
|
|
84
|
+
/** Expo Router pathname (from usePathname()) */
|
|
85
|
+
pathname?: string;
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
// ─── Component ─────────────────────────────────────────────────
|
|
@@ -100,12 +108,17 @@ export function AIAgent({
|
|
|
100
108
|
instructions,
|
|
101
109
|
stepDelay,
|
|
102
110
|
mcpServerUrl,
|
|
111
|
+
router,
|
|
112
|
+
pathname,
|
|
103
113
|
}: AIAgentProps) {
|
|
104
114
|
const rootViewRef = useRef<any>(null);
|
|
105
115
|
const [isThinking, setIsThinking] = useState(false);
|
|
106
116
|
const [statusText, setStatusText] = useState('');
|
|
107
117
|
const [lastResult, setLastResult] = useState<ExecutionResult | null>(null);
|
|
108
118
|
|
|
119
|
+
// Ref-based resolver for ask_user — stays alive across renders
|
|
120
|
+
const askUserResolverRef = useRef<((answer: string) => void) | null>(null);
|
|
121
|
+
|
|
109
122
|
// ─── Create Runtime ──────────────────────────────────────────
|
|
110
123
|
|
|
111
124
|
const config: AgentConfig = useMemo(() => ({
|
|
@@ -124,12 +137,25 @@ export function AIAgent({
|
|
|
124
137
|
instructions,
|
|
125
138
|
stepDelay,
|
|
126
139
|
mcpServerUrl,
|
|
140
|
+
router,
|
|
141
|
+
pathname,
|
|
142
|
+
onStatusUpdate: setStatusText,
|
|
143
|
+
// Page-agent pattern: block the agent loop until user responds
|
|
144
|
+
onAskUser: (question: string) => {
|
|
145
|
+
return new Promise<string>((resolve) => {
|
|
146
|
+
askUserResolverRef.current = resolve;
|
|
147
|
+
// Show question in chat bar, allow user input
|
|
148
|
+
setLastResult({ success: true, message: `❓ ${question}`, steps: [] });
|
|
149
|
+
setIsThinking(false);
|
|
150
|
+
setStatusText('');
|
|
151
|
+
});
|
|
152
|
+
},
|
|
127
153
|
}), [
|
|
128
154
|
apiKey, model, language, maxSteps,
|
|
129
155
|
interactiveBlacklist, interactiveWhitelist,
|
|
130
156
|
onBeforeStep, onAfterStep, onBeforeTask, onAfterTask,
|
|
131
157
|
transformScreenContent, customTools, instructions, stepDelay,
|
|
132
|
-
mcpServerUrl,
|
|
158
|
+
mcpServerUrl, router, pathname,
|
|
133
159
|
]);
|
|
134
160
|
|
|
135
161
|
const provider = useMemo(() => new GeminiProvider(apiKey, model), [apiKey, model]);
|
|
@@ -164,6 +190,19 @@ export function AIAgent({
|
|
|
164
190
|
if (!message.trim()) return;
|
|
165
191
|
|
|
166
192
|
logger.info('AIAgent', `User message: "${message}"`);
|
|
193
|
+
|
|
194
|
+
// If there's a pending ask_user, resolve it instead of starting a new execution
|
|
195
|
+
if (askUserResolverRef.current) {
|
|
196
|
+
const resolver = askUserResolverRef.current;
|
|
197
|
+
askUserResolverRef.current = null;
|
|
198
|
+
setIsThinking(true);
|
|
199
|
+
setStatusText('Processing your answer...');
|
|
200
|
+
setLastResult(null);
|
|
201
|
+
resolver(message);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Normal execution — new task
|
|
167
206
|
setIsThinking(true);
|
|
168
207
|
setStatusText('Thinking...');
|
|
169
208
|
setLastResult(null);
|
|
@@ -209,6 +248,7 @@ export function AIAgent({
|
|
|
209
248
|
isThinking={isThinking}
|
|
210
249
|
lastResult={lastResult}
|
|
211
250
|
language={language}
|
|
251
|
+
onDismiss={() => setLastResult(null)}
|
|
212
252
|
/>
|
|
213
253
|
)}
|
|
214
254
|
</AgentContext.Provider>
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AgentChatBar — Floating
|
|
3
|
-
*
|
|
2
|
+
* AgentChatBar — Floating, draggable, compressible chat widget.
|
|
3
|
+
* Does not block underlying UI natively.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { useState } from 'react';
|
|
6
|
+
import { useState, useRef } from 'react';
|
|
7
7
|
import {
|
|
8
8
|
View,
|
|
9
9
|
TextInput,
|
|
10
10
|
Pressable,
|
|
11
11
|
Text,
|
|
12
12
|
StyleSheet,
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
Animated,
|
|
14
|
+
PanResponder,
|
|
15
|
+
useWindowDimensions,
|
|
15
16
|
} from 'react-native';
|
|
16
17
|
import type { ExecutionResult } from '../core/types';
|
|
17
18
|
|
|
@@ -20,12 +21,43 @@ interface AgentChatBarProps {
|
|
|
20
21
|
isThinking: boolean;
|
|
21
22
|
lastResult: ExecutionResult | null;
|
|
22
23
|
language: 'en' | 'ar';
|
|
24
|
+
onDismiss?: () => void;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
export function AgentChatBar({ onSend, isThinking, lastResult, language }: AgentChatBarProps) {
|
|
27
|
+
export function AgentChatBar({ onSend, isThinking, lastResult, language, onDismiss }: AgentChatBarProps) {
|
|
26
28
|
const [text, setText] = useState('');
|
|
29
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
30
|
+
const { height } = useWindowDimensions();
|
|
27
31
|
const isArabic = language === 'ar';
|
|
28
32
|
|
|
33
|
+
// Initial position: Bottom right for FAB, Bottom center for Expanded
|
|
34
|
+
// For simplicity, we just initialize to a safe generic spot on screen.
|
|
35
|
+
const pan = useRef(new Animated.ValueXY({ x: 10, y: height - 200 })).current;
|
|
36
|
+
|
|
37
|
+
// PanResponder for dragging the widget
|
|
38
|
+
const panResponder = useRef(
|
|
39
|
+
PanResponder.create({
|
|
40
|
+
onMoveShouldSetPanResponder: (_, gestureState) => {
|
|
41
|
+
// Only trigger drag if moving more than 5px (allows taps to register inside)
|
|
42
|
+
return Math.abs(gestureState.dx) > 5 || Math.abs(gestureState.dy) > 5;
|
|
43
|
+
},
|
|
44
|
+
onPanResponderGrant: () => {
|
|
45
|
+
pan.setOffset({
|
|
46
|
+
x: (pan.x as any)._value,
|
|
47
|
+
y: (pan.y as any)._value,
|
|
48
|
+
});
|
|
49
|
+
pan.setValue({ x: 0, y: 0 });
|
|
50
|
+
},
|
|
51
|
+
onPanResponderMove: Animated.event(
|
|
52
|
+
[null, { dx: pan.x, dy: pan.y }],
|
|
53
|
+
{ useNativeDriver: false }
|
|
54
|
+
),
|
|
55
|
+
onPanResponderRelease: () => {
|
|
56
|
+
pan.flattenOffset();
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
).current;
|
|
60
|
+
|
|
29
61
|
const handleSend = () => {
|
|
30
62
|
if (text.trim() && !isThinking) {
|
|
31
63
|
onSend(text.trim());
|
|
@@ -33,15 +65,41 @@ export function AgentChatBar({ onSend, isThinking, lastResult, language }: Agent
|
|
|
33
65
|
}
|
|
34
66
|
};
|
|
35
67
|
|
|
68
|
+
// ─── Compressed State (FAB) ───
|
|
69
|
+
if (!isExpanded) {
|
|
70
|
+
return (
|
|
71
|
+
<Animated.View style={[styles.fabContainer, pan.getLayout()]} {...panResponder.panHandlers}>
|
|
72
|
+
<Pressable
|
|
73
|
+
style={styles.fab}
|
|
74
|
+
onPress={() => setIsExpanded(true)}
|
|
75
|
+
accessibilityLabel="Open AI Agent Chat"
|
|
76
|
+
>
|
|
77
|
+
<Text style={styles.fabIcon}>{isThinking ? '⏳' : '🤖'}</Text>
|
|
78
|
+
</Pressable>
|
|
79
|
+
</Animated.View>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ─── Expanded State (Widget) ───
|
|
36
84
|
return (
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
style={styles.
|
|
40
|
-
|
|
85
|
+
<Animated.View style={[styles.expandedContainer, pan.getLayout()]}>
|
|
86
|
+
{/* Drag Handle Area */}
|
|
87
|
+
<View {...panResponder.panHandlers} style={styles.dragHandleArea} accessibilityLabel="Drag AI Agent">
|
|
88
|
+
<View style={styles.dragGrip} />
|
|
89
|
+
<Pressable onPress={() => setIsExpanded(false)} style={styles.minimizeBtn} accessibilityLabel="Minimize AI Agent">
|
|
90
|
+
<Text style={styles.minimizeText}>—</Text>
|
|
91
|
+
</Pressable>
|
|
92
|
+
</View>
|
|
93
|
+
|
|
41
94
|
{/* Result message */}
|
|
42
95
|
{lastResult && (
|
|
43
96
|
<View style={[styles.resultBubble, lastResult.success ? styles.resultSuccess : styles.resultError]}>
|
|
44
97
|
<Text style={styles.resultText}>{lastResult.message}</Text>
|
|
98
|
+
{onDismiss && (
|
|
99
|
+
<Pressable style={styles.dismissButton} onPress={onDismiss} hitSlop={12}>
|
|
100
|
+
<Text style={styles.dismissText}>✕</Text>
|
|
101
|
+
</Pressable>
|
|
102
|
+
)}
|
|
45
103
|
</View>
|
|
46
104
|
)}
|
|
47
105
|
|
|
@@ -49,7 +107,7 @@ export function AgentChatBar({ onSend, isThinking, lastResult, language }: Agent
|
|
|
49
107
|
<View style={styles.inputRow}>
|
|
50
108
|
<TextInput
|
|
51
109
|
style={[styles.input, isArabic && styles.inputRTL]}
|
|
52
|
-
placeholder={isArabic ? 'اكتب طلبك...' : 'Ask
|
|
110
|
+
placeholder={isArabic ? 'اكتب طلبك...' : 'Ask AI...'}
|
|
53
111
|
placeholderTextColor="#999"
|
|
54
112
|
value={text}
|
|
55
113
|
onChangeText={setText}
|
|
@@ -62,33 +120,87 @@ export function AgentChatBar({ onSend, isThinking, lastResult, language }: Agent
|
|
|
62
120
|
style={[styles.sendButton, isThinking && styles.sendButtonDisabled]}
|
|
63
121
|
onPress={handleSend}
|
|
64
122
|
disabled={isThinking || !text.trim()}
|
|
123
|
+
accessibilityLabel="Send request to AI Agent"
|
|
65
124
|
>
|
|
66
125
|
<Text style={styles.sendButtonText}>
|
|
67
126
|
{isThinking ? '⏳' : '🚀'}
|
|
68
127
|
</Text>
|
|
69
128
|
</Pressable>
|
|
70
129
|
</View>
|
|
71
|
-
</
|
|
130
|
+
</Animated.View>
|
|
72
131
|
);
|
|
73
132
|
}
|
|
74
133
|
|
|
75
134
|
const styles = StyleSheet.create({
|
|
76
|
-
|
|
135
|
+
// FAB Styles
|
|
136
|
+
fabContainer: {
|
|
77
137
|
position: 'absolute',
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
138
|
+
zIndex: 9999,
|
|
139
|
+
},
|
|
140
|
+
fab: {
|
|
141
|
+
width: 60,
|
|
142
|
+
height: 60,
|
|
143
|
+
borderRadius: 30,
|
|
144
|
+
backgroundColor: '#1a1a2e',
|
|
145
|
+
justifyContent: 'center',
|
|
146
|
+
alignItems: 'center',
|
|
147
|
+
elevation: 5,
|
|
148
|
+
shadowColor: '#000',
|
|
149
|
+
shadowOffset: { width: 0, height: 4 },
|
|
150
|
+
shadowOpacity: 0.3,
|
|
151
|
+
shadowRadius: 6,
|
|
152
|
+
},
|
|
153
|
+
fabIcon: {
|
|
154
|
+
fontSize: 28,
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
// Expanded Styles
|
|
158
|
+
expandedContainer: {
|
|
159
|
+
position: 'absolute',
|
|
160
|
+
zIndex: 9999,
|
|
161
|
+
width: 340,
|
|
84
162
|
backgroundColor: 'rgba(26, 26, 46, 0.95)',
|
|
85
|
-
|
|
86
|
-
|
|
163
|
+
borderRadius: 24,
|
|
164
|
+
padding: 16,
|
|
165
|
+
paddingTop: 8,
|
|
166
|
+
elevation: 8,
|
|
167
|
+
shadowColor: '#000',
|
|
168
|
+
shadowOffset: { width: 0, height: 8 },
|
|
169
|
+
shadowOpacity: 0.4,
|
|
170
|
+
shadowRadius: 10,
|
|
171
|
+
},
|
|
172
|
+
dragHandleArea: {
|
|
173
|
+
width: '100%',
|
|
174
|
+
height: 30,
|
|
175
|
+
justifyContent: 'center',
|
|
176
|
+
alignItems: 'center',
|
|
177
|
+
marginBottom: 8,
|
|
178
|
+
},
|
|
179
|
+
dragGrip: {
|
|
180
|
+
width: 40,
|
|
181
|
+
height: 5,
|
|
182
|
+
backgroundColor: 'rgba(255, 255, 255, 0.3)',
|
|
183
|
+
borderRadius: 4,
|
|
184
|
+
},
|
|
185
|
+
minimizeBtn: {
|
|
186
|
+
position: 'absolute',
|
|
187
|
+
right: 0,
|
|
188
|
+
top: 0,
|
|
189
|
+
padding: 8,
|
|
87
190
|
},
|
|
191
|
+
minimizeText: {
|
|
192
|
+
color: '#fff',
|
|
193
|
+
fontSize: 18,
|
|
194
|
+
fontWeight: 'bold',
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
// Results & Input
|
|
88
198
|
resultBubble: {
|
|
89
199
|
borderRadius: 12,
|
|
90
200
|
padding: 12,
|
|
91
|
-
marginBottom:
|
|
201
|
+
marginBottom: 12,
|
|
202
|
+
flexDirection: 'row',
|
|
203
|
+
alignItems: 'flex-start',
|
|
92
204
|
},
|
|
93
205
|
resultSuccess: {
|
|
94
206
|
backgroundColor: 'rgba(40, 167, 69, 0.2)',
|
|
@@ -100,6 +212,16 @@ const styles = StyleSheet.create({
|
|
|
100
212
|
color: '#fff',
|
|
101
213
|
fontSize: 14,
|
|
102
214
|
lineHeight: 20,
|
|
215
|
+
flex: 1,
|
|
216
|
+
},
|
|
217
|
+
dismissButton: {
|
|
218
|
+
marginLeft: 8,
|
|
219
|
+
padding: 2,
|
|
220
|
+
},
|
|
221
|
+
dismissText: {
|
|
222
|
+
color: 'rgba(255, 255, 255, 0.6)',
|
|
223
|
+
fontSize: 14,
|
|
224
|
+
fontWeight: 'bold',
|
|
103
225
|
},
|
|
104
226
|
inputRow: {
|
|
105
227
|
flexDirection: 'row',
|
|
@@ -109,9 +231,9 @@ const styles = StyleSheet.create({
|
|
|
109
231
|
input: {
|
|
110
232
|
flex: 1,
|
|
111
233
|
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
|
112
|
-
borderRadius:
|
|
234
|
+
borderRadius: 20,
|
|
113
235
|
paddingHorizontal: 16,
|
|
114
|
-
paddingVertical:
|
|
236
|
+
paddingVertical: 10,
|
|
115
237
|
color: '#fff',
|
|
116
238
|
fontSize: 16,
|
|
117
239
|
},
|
|
@@ -120,9 +242,9 @@ const styles = StyleSheet.create({
|
|
|
120
242
|
writingDirection: 'rtl',
|
|
121
243
|
},
|
|
122
244
|
sendButton: {
|
|
123
|
-
width:
|
|
124
|
-
height:
|
|
125
|
-
borderRadius:
|
|
245
|
+
width: 40,
|
|
246
|
+
height: 40,
|
|
247
|
+
borderRadius: 20,
|
|
126
248
|
backgroundColor: 'rgba(255, 255, 255, 0.15)',
|
|
127
249
|
justifyContent: 'center',
|
|
128
250
|
alignItems: 'center',
|
|
@@ -131,6 +253,6 @@ const styles = StyleSheet.create({
|
|
|
131
253
|
opacity: 0.5,
|
|
132
254
|
},
|
|
133
255
|
sendButtonText: {
|
|
134
|
-
fontSize:
|
|
256
|
+
fontSize: 18,
|
|
135
257
|
},
|
|
136
258
|
});
|