@ateam-ai-mobile/ui 0.1.2 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai-mobile/ui",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Prebuilt ADAS chat experience for React Native \u2014 full-screen chat, chips, widget views. Built on @ateam-ai-mobile/core + @ateam-ai-mobile/plugin-runtime.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,8 +9,8 @@
9
9
  "build": "tsup"
10
10
  },
11
11
  "dependencies": {
12
- "@ateam-ai-mobile/core": "0.1.2",
13
- "@ateam-ai-mobile/plugin-runtime": "0.1.2",
12
+ "@ateam-ai-mobile/core": "0.1.3",
13
+ "@ateam-ai-mobile/plugin-runtime": "0.1.3",
14
14
  "zustand": "^5.0.0"
15
15
  },
16
16
  "peerDependencies": {
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
2
  import { View, Text, Animated, StyleSheet } from 'react-native';
3
3
  import type { JobProgress } from '@ateam-ai-mobile/core';
4
- import { ada, fontSize, borderRadius } from '@ateam-ai-mobile/core';
4
+ import { ada, fontSize } from '@ateam-ai-mobile/core';
5
5
 
6
6
  // Semantic warm-palette colors for terminal states (kept local — Ada palette
7
7
  // is mostly orange/ink, but progress states need a small warm green/red/amber
@@ -15,9 +15,12 @@ const WARM_AMBER_SOFT = 'rgba(212, 154, 78, 0.16)';
15
15
 
16
16
  interface Props {
17
17
  progress: JobProgress;
18
+ /** Standalone placeholder (transparent bubble) — drop the top divider so it
19
+ * floats cleanly over the background. */
20
+ standalone?: boolean;
18
21
  }
19
22
 
20
- export default function JobProgressBar({ progress }: Props) {
23
+ export default function JobProgressBar({ progress, standalone }: Props) {
21
24
  const pulseAnim = useRef(new Animated.Value(0.5)).current;
22
25
  const [elapsed, setElapsed] = useState(0);
23
26
 
@@ -34,8 +37,10 @@ export default function JobProgressBar({ progress }: Props) {
34
37
  }
35
38
  const anim = Animated.loop(
36
39
  Animated.sequence([
37
- Animated.timing(pulseAnim, { toValue: 1, duration: 800, useNativeDriver: true }),
38
- Animated.timing(pulseAnim, { toValue: 0.5, duration: 800, useNativeDriver: true }),
40
+ // Gentle breath — a soft fade rather than a hard blink, so the running
41
+ // indicator recedes into the bubble instead of demanding attention.
42
+ Animated.timing(pulseAnim, { toValue: 0.55, duration: 1100, useNativeDriver: true }),
43
+ Animated.timing(pulseAnim, { toValue: 0.22, duration: 1100, useNativeDriver: true }),
39
44
  ]),
40
45
  );
41
46
  anim.start();
@@ -75,7 +80,7 @@ export default function JobProgressBar({ progress }: Props) {
75
80
  // Terminal states — simple one-liner
76
81
  if (isDone || isError) {
77
82
  return (
78
- <View style={styles.container}>
83
+ <View style={[styles.container, standalone && styles.containerStandalone]}>
79
84
  <View style={styles.row}>
80
85
  <View style={[styles.dot, { backgroundColor: statusColor }]} />
81
86
  <Text style={[styles.label, { color: statusColor }]}>
@@ -88,7 +93,7 @@ export default function JobProgressBar({ progress }: Props) {
88
93
 
89
94
  if (isPaused) {
90
95
  return (
91
- <View style={styles.container}>
96
+ <View style={[styles.container, standalone && styles.containerStandalone]}>
92
97
  <View style={styles.row}>
93
98
  <View style={[styles.dot, { backgroundColor: statusColor }]} />
94
99
  <Text style={[styles.label, { color: statusColor }]}>
@@ -99,10 +104,13 @@ export default function JobProgressBar({ progress }: Props) {
99
104
  );
100
105
  }
101
106
 
102
- // Running — voice-agent-style compact line:
103
- // ● messaging-agent → whatsapp.send · iter 3 · 0:42
107
+ // Running. Prefer the backend's warm, localized humanStatus; fall back to
108
+ // the raw skill/tool tokens (translated via humanLabels when available).
109
+ // Elapsed shows in human units: "3s", "1m 20s". (Docs/STATUS_I18N_MOBILE_API)
104
110
  const elapsedStr = progress.chainStartedAt
105
- ? `${Math.floor(elapsed / 60)}:${String(elapsed % 60).padStart(2, '0')}`
111
+ ? elapsed < 60
112
+ ? `${elapsed}s`
113
+ : `${Math.floor(elapsed / 60)}m ${elapsed % 60}s`
106
114
  : null;
107
115
 
108
116
  const metaParts = [
@@ -110,20 +118,26 @@ export default function JobProgressBar({ progress }: Props) {
110
118
  elapsedStr,
111
119
  ].filter(Boolean) as string[];
112
120
 
121
+ const labels = progress.humanLabels;
122
+ const label = (t?: string) => (t && labels?.[t]) || t;
123
+ const headline = progress.humanStatus || label(progress.skillSlug) || 'Processing...';
124
+ // When we already have a warm headline, don't also show the raw "→ tool".
125
+ const showRawTool = !progress.humanStatus && !!progress.currentTool;
126
+
113
127
  const lp = progress.liveProgress;
114
128
 
115
129
  return (
116
- <View style={styles.container}>
130
+ <View style={[styles.container, standalone && styles.containerStandalone]}>
117
131
  <View style={styles.row}>
118
132
  <Animated.View
119
- style={[styles.dot, { backgroundColor: ada.orange, opacity: pulseAnim }]}
133
+ style={[styles.dot, styles.dotRunning, { opacity: pulseAnim }]}
120
134
  />
121
135
  <Text style={styles.skill} numberOfLines={1}>
122
- {progress.skillSlug || 'Processing...'}
136
+ {headline}
123
137
  </Text>
124
- {!!progress.currentTool && (
138
+ {showRawTool && (
125
139
  <Text style={styles.tool} numberOfLines={1}>
126
- {` → ${progress.currentTool}`}
140
+ {` → ${label(progress.currentTool)}`}
127
141
  </Text>
128
142
  )}
129
143
  {metaParts.length > 0 && (
@@ -141,9 +155,9 @@ export default function JobProgressBar({ progress }: Props) {
141
155
  <Text style={styles.liveProgressMsg} numberOfLines={1}>{lp.message}</Text>
142
156
  </View>
143
157
  )}
144
- <View style={[styles.progressTrack, { backgroundColor: statusBg }]}>
158
+ <View style={styles.progressTrack}>
145
159
  <Animated.View
146
- style={[styles.progressFill, { backgroundColor: ada.orange, opacity: pulseAnim }]}
160
+ style={[styles.progressFill, { opacity: pulseAnim }]}
147
161
  />
148
162
  </View>
149
163
  </View>
@@ -157,6 +171,12 @@ const styles = StyleSheet.create({
157
171
  borderTopWidth: 1,
158
172
  borderTopColor: ada.border,
159
173
  },
174
+ // Standalone (transparent-bubble) placeholder — no divider/top spacing.
175
+ containerStandalone: {
176
+ marginTop: 0,
177
+ paddingTop: 0,
178
+ borderTopWidth: 0,
179
+ },
160
180
  row: {
161
181
  flexDirection: 'row',
162
182
  alignItems: 'center',
@@ -164,21 +184,26 @@ const styles = StyleSheet.create({
164
184
  overflow: 'hidden',
165
185
  },
166
186
  dot: {
167
- width: 7,
168
- height: 7,
169
- borderRadius: 4,
187
+ width: 6,
188
+ height: 6,
189
+ borderRadius: 3,
170
190
  marginRight: 6,
171
191
  flexShrink: 0,
172
192
  },
193
+ // Running dot: soft orange, not the full-saturation accent.
194
+ dotRunning: {
195
+ backgroundColor: 'rgba(255, 122, 40, 0.6)',
196
+ },
173
197
  label: {
174
198
  fontSize: fontSize.xs,
175
199
  fontWeight: '600',
176
200
  },
177
- // Running state text segments
201
+ // Running state text segments — muted so the indicator blends into the
202
+ // bubble rather than shouting in orange.
178
203
  skill: {
179
204
  fontSize: fontSize.xs,
180
- fontWeight: '600',
181
- color: ada.orange,
205
+ fontWeight: '500',
206
+ color: ada.inkSoft,
182
207
  flexShrink: 1,
183
208
  },
184
209
  tool: {
@@ -204,18 +229,22 @@ const styles = StyleSheet.create({
204
229
  },
205
230
  liveProgressMsg: {
206
231
  fontSize: fontSize.xs,
207
- color: ada.orange,
232
+ color: ada.inkSoft,
208
233
  flex: 1,
209
234
  },
210
235
  progressTrack: {
211
- height: 3,
212
- borderRadius: 2,
213
- marginTop: 6,
236
+ height: 2,
237
+ borderRadius: 1,
238
+ marginTop: 8,
214
239
  overflow: 'hidden',
240
+ // Barely-there rail so the bar reads as a hint, not a status meter.
241
+ backgroundColor: 'rgba(26, 20, 16, 0.05)',
215
242
  },
216
243
  progressFill: {
217
244
  height: '100%',
218
- width: '60%',
219
- borderRadius: 2,
245
+ width: '55%',
246
+ borderRadius: 1,
247
+ // Muted orange — present but recessive.
248
+ backgroundColor: 'rgba(255, 122, 40, 0.4)',
220
249
  },
221
250
  });
@@ -73,6 +73,14 @@ export default function MessageBubble({ message }: Props) {
73
73
  const isUser = message.role === 'user';
74
74
  const isSystem = message.role === 'system';
75
75
  const isNotification = !isUser && message.kind === 'notification';
76
+ // A "working on it" placeholder — just the live chain indicator, no real
77
+ // answer yet. Render it near-transparent so it floats over the background
78
+ // instead of looking like a solid message bubble.
79
+ const st = message.jobProgress?.status;
80
+ const isProgressOnly =
81
+ !isUser &&
82
+ (st === 'running' || st === 'paused') &&
83
+ (!message.content?.trim() || message.content === 'Working on it...');
76
84
  const [copied, setCopied] = useState('');
77
85
 
78
86
  const showCopied = useCallback((label: string) => {
@@ -113,6 +121,7 @@ export default function MessageBubble({ message }: Props) {
113
121
  isUser && styles.bubbleUser,
114
122
  isSystem && styles.bubbleSystem,
115
123
  isNotification && styles.bubbleNotification,
124
+ isProgressOnly && styles.bubbleProgress,
116
125
  ]}
117
126
  >
118
127
  {isNotification && (
@@ -157,6 +166,9 @@ export default function MessageBubble({ message }: Props) {
157
166
  message.content && !(message.content === 'See attachment' && message.attachments?.length) ? (
158
167
  <Text selectable style={styles.userText}>{message.content}</Text>
159
168
  ) : null
169
+ ) : isProgressOnly ? (
170
+ // No "Working on it…" text — the indicator below is the whole message.
171
+ null
160
172
  ) : (
161
173
  renderAssistantContent(message.content, copyText)
162
174
  )}
@@ -169,14 +181,16 @@ export default function MessageBubble({ message }: Props) {
169
181
  <ToolCallsBadge calls={message.toolCalls} />
170
182
  )}
171
183
  {message.jobProgress && (
172
- <JobProgressBar progress={message.jobProgress} />
184
+ <JobProgressBar progress={message.jobProgress} standalone={isProgressOnly} />
173
185
  )}
174
186
  {!isUser && message.actions && message.actions.length > 0 && (
175
187
  <UIActionsRow actions={message.actions} />
176
188
  )}
177
- <Text style={[styles.timeText, isUser && styles.timeTextUser]}>
178
- {formatTime(message.timestamp)}
179
- </Text>
189
+ {!isProgressOnly && (
190
+ <Text style={[styles.timeText, isUser && styles.timeTextUser]}>
191
+ {formatTime(message.timestamp)}
192
+ </Text>
193
+ )}
180
194
  </Pressable>
181
195
  {isUser && <View style={styles.avatarSpacer} />}
182
196
  </View>
@@ -323,6 +337,14 @@ const styles = StyleSheet.create({
323
337
  backgroundColor: ada.userBubble,
324
338
  borderColor: 'transparent',
325
339
  },
340
+ // Live-chain placeholder: no fill, no border, tighter padding — the
341
+ // indicator floats over the background instead of sitting in a bubble.
342
+ bubbleProgress: {
343
+ backgroundColor: 'transparent',
344
+ borderColor: 'transparent',
345
+ paddingHorizontal: 4,
346
+ paddingVertical: 2,
347
+ },
326
348
  bubbleSystem: {
327
349
  backgroundColor: ada.orangeSoft,
328
350
  borderColor: ada.orange,