@ateam-ai-mobile/ui 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -14,7 +14,7 @@ import { ada as ada7, useChatStore, pluginCommandEmitter as pluginCommandEmitter
14
14
  import { useState as useState4, useCallback as useCallback3 } from "react";
15
15
  import { View as View7, Text as Text6, Pressable as Pressable3, StyleSheet as StyleSheet6, Image, Platform as Platform4 } from "react-native";
16
16
  import Markdown from "react-native-markdown-display";
17
- import { TABLE_BLOCK_OPEN, TABLE_BLOCK_CLOSE, getConfig as getConfig2, ada as ada6, borderRadius as borderRadius3, fontSize as fontSize4 } from "@ateam-ai-mobile/core";
17
+ import { TABLE_BLOCK_OPEN, TABLE_BLOCK_CLOSE, getConfig as getConfig2, ada as ada6, borderRadius as borderRadius2, fontSize as fontSize4 } from "@ateam-ai-mobile/core";
18
18
  import { AdaOrb, getHaptics as getHaptics2 } from "@ateam-ai-mobile/plugin-runtime";
19
19
 
20
20
  // src/chat/JobProgressBar.tsx
@@ -28,7 +28,7 @@ var WARM_RED = "#C15545";
28
28
  var WARM_RED_SOFT = "rgba(193, 85, 69, 0.14)";
29
29
  var WARM_AMBER = "#D49A4E";
30
30
  var WARM_AMBER_SOFT = "rgba(212, 154, 78, 0.16)";
31
- function JobProgressBar({ progress }) {
31
+ function JobProgressBar({ progress, standalone }) {
32
32
  const pulseAnim = useRef(new Animated.Value(0.5)).current;
33
33
  const [elapsed, setElapsed] = useState(0);
34
34
  const isRunning = progress.status === "running";
@@ -42,8 +42,10 @@ function JobProgressBar({ progress }) {
42
42
  }
43
43
  const anim = Animated.loop(
44
44
  Animated.sequence([
45
- Animated.timing(pulseAnim, { toValue: 1, duration: 800, useNativeDriver: true }),
46
- Animated.timing(pulseAnim, { toValue: 0.5, duration: 800, useNativeDriver: true })
45
+ // Gentle breath — a soft fade rather than a hard blink, so the running
46
+ // indicator recedes into the bubble instead of demanding attention.
47
+ Animated.timing(pulseAnim, { toValue: 0.55, duration: 1100, useNativeDriver: true }),
48
+ Animated.timing(pulseAnim, { toValue: 0.22, duration: 1100, useNativeDriver: true })
47
49
  ])
48
50
  );
49
51
  anim.start();
@@ -64,33 +66,37 @@ function JobProgressBar({ progress }) {
64
66
  const statusColor = isDone ? WARM_GREEN : isError ? WARM_RED : isPaused ? WARM_AMBER : ada.orange;
65
67
  const statusBg = isDone ? WARM_GREEN_SOFT : isError ? WARM_RED_SOFT : isPaused ? WARM_AMBER_SOFT : ada.orangeSoft;
66
68
  if (isDone || isError) {
67
- return /* @__PURE__ */ jsx(View, { style: styles.container, children: /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
69
+ return /* @__PURE__ */ jsx(View, { style: [styles.container, standalone && styles.containerStandalone], children: /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
68
70
  /* @__PURE__ */ jsx(View, { style: [styles.dot, { backgroundColor: statusColor }] }),
69
71
  /* @__PURE__ */ jsx(Text, { style: [styles.label, { color: statusColor }], children: isDone ? "Completed" : "Failed" })
70
72
  ] }) });
71
73
  }
72
74
  if (isPaused) {
73
- return /* @__PURE__ */ jsx(View, { style: styles.container, children: /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
75
+ return /* @__PURE__ */ jsx(View, { style: [styles.container, standalone && styles.containerStandalone], children: /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
74
76
  /* @__PURE__ */ jsx(View, { style: [styles.dot, { backgroundColor: statusColor }] }),
75
77
  /* @__PURE__ */ jsx(Text, { style: [styles.label, { color: statusColor }], children: "Waiting for your reply" })
76
78
  ] }) });
77
79
  }
78
- const elapsedStr = progress.chainStartedAt ? `${Math.floor(elapsed / 60)}:${String(elapsed % 60).padStart(2, "0")}` : null;
80
+ const elapsedStr = progress.chainStartedAt ? elapsed < 60 ? `${elapsed}s` : `${Math.floor(elapsed / 60)}m ${elapsed % 60}s` : null;
79
81
  const metaParts = [
80
82
  progress.iteration != null && progress.iteration > 0 ? `iter ${progress.iteration}` : null,
81
83
  elapsedStr
82
84
  ].filter(Boolean);
85
+ const labels = progress.humanLabels;
86
+ const label = (t) => t && labels?.[t] || t;
87
+ const headline = progress.humanStatus || label(progress.skillSlug) || "Processing...";
88
+ const showRawTool = !progress.humanStatus && !!progress.currentTool;
83
89
  const lp = progress.liveProgress;
84
- return /* @__PURE__ */ jsxs(View, { style: styles.container, children: [
90
+ return /* @__PURE__ */ jsxs(View, { style: [styles.container, standalone && styles.containerStandalone], children: [
85
91
  /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
86
92
  /* @__PURE__ */ jsx(
87
93
  Animated.View,
88
94
  {
89
- style: [styles.dot, { backgroundColor: ada.orange, opacity: pulseAnim }]
95
+ style: [styles.dot, styles.dotRunning, { opacity: pulseAnim }]
90
96
  }
91
97
  ),
92
- /* @__PURE__ */ jsx(Text, { style: styles.skill, numberOfLines: 1, children: progress.skillSlug || "Processing..." }),
93
- !!progress.currentTool && /* @__PURE__ */ jsx(Text, { style: styles.tool, numberOfLines: 1, children: ` \u2192 ${progress.currentTool}` }),
98
+ /* @__PURE__ */ jsx(Text, { style: styles.skill, numberOfLines: 1, children: headline }),
99
+ showRawTool && /* @__PURE__ */ jsx(Text, { style: styles.tool, numberOfLines: 1, children: ` \u2192 ${label(progress.currentTool)}` }),
94
100
  metaParts.length > 0 && /* @__PURE__ */ jsx(Text, { style: styles.meta, numberOfLines: 1, children: ` \xB7 ${metaParts.join(" \xB7 ")}` })
95
101
  ] }),
96
102
  !!lp?.message && /* @__PURE__ */ jsxs(View, { style: styles.liveProgressRow, children: [
@@ -103,10 +109,10 @@ function JobProgressBar({ progress }) {
103
109
  ] }),
104
110
  /* @__PURE__ */ jsx(Text, { style: styles.liveProgressMsg, numberOfLines: 1, children: lp.message })
105
111
  ] }),
106
- /* @__PURE__ */ jsx(View, { style: [styles.progressTrack, { backgroundColor: statusBg }], children: /* @__PURE__ */ jsx(
112
+ /* @__PURE__ */ jsx(View, { style: styles.progressTrack, children: /* @__PURE__ */ jsx(
107
113
  Animated.View,
108
114
  {
109
- style: [styles.progressFill, { backgroundColor: ada.orange, opacity: pulseAnim }]
115
+ style: [styles.progressFill, { opacity: pulseAnim }]
110
116
  }
111
117
  ) })
112
118
  ] });
@@ -118,6 +124,12 @@ var styles = StyleSheet.create({
118
124
  borderTopWidth: 1,
119
125
  borderTopColor: ada.border
120
126
  },
127
+ // Standalone (transparent-bubble) placeholder — no divider/top spacing.
128
+ containerStandalone: {
129
+ marginTop: 0,
130
+ paddingTop: 0,
131
+ borderTopWidth: 0
132
+ },
121
133
  row: {
122
134
  flexDirection: "row",
123
135
  alignItems: "center",
@@ -125,21 +137,26 @@ var styles = StyleSheet.create({
125
137
  overflow: "hidden"
126
138
  },
127
139
  dot: {
128
- width: 7,
129
- height: 7,
130
- borderRadius: 4,
140
+ width: 6,
141
+ height: 6,
142
+ borderRadius: 3,
131
143
  marginRight: 6,
132
144
  flexShrink: 0
133
145
  },
146
+ // Running dot: soft orange, not the full-saturation accent.
147
+ dotRunning: {
148
+ backgroundColor: "rgba(255, 122, 40, 0.6)"
149
+ },
134
150
  label: {
135
151
  fontSize: fontSize.xs,
136
152
  fontWeight: "600"
137
153
  },
138
- // Running state text segments
154
+ // Running state text segments — muted so the indicator blends into the
155
+ // bubble rather than shouting in orange.
139
156
  skill: {
140
157
  fontSize: fontSize.xs,
141
- fontWeight: "600",
142
- color: ada.orange,
158
+ fontWeight: "500",
159
+ color: ada.inkSoft,
143
160
  flexShrink: 1
144
161
  },
145
162
  tool: {
@@ -165,19 +182,23 @@ var styles = StyleSheet.create({
165
182
  },
166
183
  liveProgressMsg: {
167
184
  fontSize: fontSize.xs,
168
- color: ada.orange,
185
+ color: ada.inkSoft,
169
186
  flex: 1
170
187
  },
171
188
  progressTrack: {
172
- height: 3,
173
- borderRadius: 2,
174
- marginTop: 6,
175
- overflow: "hidden"
189
+ height: 2,
190
+ borderRadius: 1,
191
+ marginTop: 8,
192
+ overflow: "hidden",
193
+ // Barely-there rail so the bar reads as a hint, not a status meter.
194
+ backgroundColor: "rgba(26, 20, 16, 0.05)"
176
195
  },
177
196
  progressFill: {
178
197
  height: "100%",
179
- width: "60%",
180
- borderRadius: 2
198
+ width: "55%",
199
+ borderRadius: 1,
200
+ // Muted orange — present but recessive.
201
+ backgroundColor: "rgba(255, 122, 40, 0.4)"
181
202
  }
182
203
  });
183
204
 
@@ -979,7 +1000,7 @@ var styles4 = StyleSheet4.create({
979
1000
 
980
1001
  // src/chat/TableCardBlock.tsx
981
1002
  import { View as View6, Text as Text5, StyleSheet as StyleSheet5, Platform as Platform3 } from "react-native";
982
- import { ada as ada5, borderRadius as borderRadius2, fontSize as fontSize3, spacing } from "@ateam-ai-mobile/core";
1003
+ import { ada as ada5, borderRadius, fontSize as fontSize3, spacing } from "@ateam-ai-mobile/core";
983
1004
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
984
1005
  var fontFam2 = Platform3.OS === "ios" ? "SF Pro Rounded" : void 0;
985
1006
  function TableCardBlock({ columns, rows }) {
@@ -1008,7 +1029,7 @@ var styles5 = StyleSheet5.create({
1008
1029
  },
1009
1030
  card: {
1010
1031
  backgroundColor: ada5.paper,
1011
- borderRadius: borderRadius2.md,
1032
+ borderRadius: borderRadius.md,
1012
1033
  borderWidth: 1,
1013
1034
  borderColor: ada5.border,
1014
1035
  padding: spacing.md
@@ -1104,6 +1125,8 @@ function MessageBubble({ message }) {
1104
1125
  const isUser = message.role === "user";
1105
1126
  const isSystem = message.role === "system";
1106
1127
  const isNotification = !isUser && message.kind === "notification";
1128
+ const st = message.jobProgress?.status;
1129
+ const isProgressOnly = !isUser && (st === "running" || st === "paused") && (!message.content?.trim() || message.content === "Working on it...");
1107
1130
  const [copied, setCopied] = useState4("");
1108
1131
  const showCopied = useCallback3((label) => {
1109
1132
  setCopied(label);
@@ -1139,7 +1162,8 @@ function MessageBubble({ message }) {
1139
1162
  styles6.bubble,
1140
1163
  isUser && styles6.bubbleUser,
1141
1164
  isSystem && styles6.bubbleSystem,
1142
- isNotification && styles6.bubbleNotification
1165
+ isNotification && styles6.bubbleNotification,
1166
+ isProgressOnly && styles6.bubbleProgress
1143
1167
  ],
1144
1168
  children: [
1145
1169
  isNotification && /* @__PURE__ */ jsxs6(View7, { style: styles6.notificationHeader, children: [
@@ -1172,12 +1196,15 @@ function MessageBubble({ message }) {
1172
1196
  isUser ? (
1173
1197
  // Hide the "See attachment" placeholder when the bubble already shows the photo.
1174
1198
  message.content && !(message.content === "See attachment" && message.attachments?.length) ? /* @__PURE__ */ jsx8(Text6, { selectable: true, style: styles6.userText, children: message.content }) : null
1199
+ ) : isProgressOnly ? (
1200
+ // No "Working on it…" text — the indicator below is the whole message.
1201
+ null
1175
1202
  ) : renderAssistantContent(message.content, copyText),
1176
1203
  !!copied && /* @__PURE__ */ jsx8(View7, { style: styles6.copiedBadge, children: /* @__PURE__ */ jsx8(Text6, { style: styles6.copiedText, children: copied }) }),
1177
1204
  message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsx8(ToolCallsBadge, { calls: message.toolCalls }),
1178
- message.jobProgress && /* @__PURE__ */ jsx8(JobProgressBar, { progress: message.jobProgress }),
1205
+ message.jobProgress && /* @__PURE__ */ jsx8(JobProgressBar, { progress: message.jobProgress, standalone: isProgressOnly }),
1179
1206
  !isUser && message.actions && message.actions.length > 0 && /* @__PURE__ */ jsx8(UIActionsRow, { actions: message.actions }),
1180
- /* @__PURE__ */ jsx8(Text6, { style: [styles6.timeText, isUser && styles6.timeTextUser], children: formatTime(message.timestamp) })
1207
+ !isProgressOnly && /* @__PURE__ */ jsx8(Text6, { style: [styles6.timeText, isUser && styles6.timeTextUser], children: formatTime(message.timestamp) })
1181
1208
  ]
1182
1209
  }
1183
1210
  ),
@@ -1292,7 +1319,7 @@ var styles6 = StyleSheet6.create({
1292
1319
  backgroundColor: ada6.botBubble,
1293
1320
  borderWidth: 1,
1294
1321
  borderColor: ada6.botBubbleBorder,
1295
- borderRadius: borderRadius3.xl,
1322
+ borderRadius: borderRadius2.xl,
1296
1323
  paddingHorizontal: 16,
1297
1324
  paddingVertical: 12
1298
1325
  },
@@ -1300,6 +1327,14 @@ var styles6 = StyleSheet6.create({
1300
1327
  backgroundColor: ada6.userBubble,
1301
1328
  borderColor: "transparent"
1302
1329
  },
1330
+ // Live-chain placeholder: no fill, no border, tighter padding — the
1331
+ // indicator floats over the background instead of sitting in a bubble.
1332
+ bubbleProgress: {
1333
+ backgroundColor: "transparent",
1334
+ borderColor: "transparent",
1335
+ paddingHorizontal: 4,
1336
+ paddingVertical: 2
1337
+ },
1303
1338
  bubbleSystem: {
1304
1339
  backgroundColor: ada6.orangeSoft,
1305
1340
  borderColor: ada6.orange,
@@ -1308,7 +1343,7 @@ var styles6 = StyleSheet6.create({
1308
1343
  bubbleNotification: {
1309
1344
  backgroundColor: ada6.orangeSoft,
1310
1345
  borderColor: "rgba(255, 122, 40, 0.35)",
1311
- borderRadius: borderRadius3.lg,
1346
+ borderRadius: borderRadius2.lg,
1312
1347
  maxWidth: "90%"
1313
1348
  },
1314
1349
  notificationLeftStripe: {
@@ -1380,7 +1415,7 @@ var styles6 = StyleSheet6.create({
1380
1415
  backgroundColor: "#5A8A5C",
1381
1416
  paddingHorizontal: 8,
1382
1417
  paddingVertical: 2,
1383
- borderRadius: borderRadius3.sm
1418
+ borderRadius: borderRadius2.sm
1384
1419
  },
1385
1420
  copiedText: {
1386
1421
  color: "#fff",
@@ -1496,7 +1531,7 @@ import {
1496
1531
  Platform as Platform5
1497
1532
  } from "react-native";
1498
1533
  import Svg, { Path, Line, Polygon } from "react-native-svg";
1499
- import { colors as colors2, borderRadius as borderRadius4 } from "@ateam-ai-mobile/core";
1534
+ import { colors as colors2, borderRadius as borderRadius3 } from "@ateam-ai-mobile/core";
1500
1535
  import { getHaptics as getHaptics3 } from "@ateam-ai-mobile/plugin-runtime";
1501
1536
  import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1502
1537
  function ChatInput({ onSend, onCallStart, loading, prefill, placeholder }) {
@@ -1564,7 +1599,7 @@ var styles8 = StyleSheet8.create({
1564
1599
  flexDirection: "row",
1565
1600
  alignItems: "flex-end",
1566
1601
  backgroundColor: colors2.light.bgInput,
1567
- borderRadius: borderRadius4.xl,
1602
+ borderRadius: borderRadius3.xl,
1568
1603
  borderWidth: 1,
1569
1604
  borderColor: colors2.light.border,
1570
1605
  paddingHorizontal: 4,
@@ -1893,7 +1928,7 @@ var styles11 = StyleSheet11.create({
1893
1928
  // src/TenantPicker.tsx
1894
1929
  import { useEffect as useEffect7, useState as useState8 } from "react";
1895
1930
  import { View as View13, Text as Text9, Pressable as Pressable7, ActivityIndicator as ActivityIndicator5, StyleSheet as StyleSheet12, Platform as Platform8 } from "react-native";
1896
- import { ada as ada9, fontSize as fontSize5, borderRadius as borderRadius5, listMyTenants, switchTenant, getTenant as getTenant4 } from "@ateam-ai-mobile/core";
1931
+ import { ada as ada9, fontSize as fontSize5, borderRadius as borderRadius4, listMyTenants, switchTenant, getTenant as getTenant4 } from "@ateam-ai-mobile/core";
1897
1932
  import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1898
1933
  var fontFam5 = Platform8.OS === "ios" ? "SF Pro Rounded" : void 0;
1899
1934
  function TenantPicker({ onSwitched, onSelect, currentTenantId }) {
@@ -1967,7 +2002,7 @@ var styles12 = StyleSheet12.create({
1967
2002
  alignItems: "center",
1968
2003
  gap: 12,
1969
2004
  backgroundColor: ada9.paperSoft,
1970
- borderRadius: borderRadius5.lg,
2005
+ borderRadius: borderRadius4.lg,
1971
2006
  borderWidth: 1,
1972
2007
  borderColor: "transparent",
1973
2008
  paddingHorizontal: 16,
@@ -2037,7 +2072,21 @@ import { AdaOrb as AdaOrb3, SurfaceFullscreen as SurfaceFullscreen2 } from "@ate
2037
2072
 
2038
2073
  // src/voice/useVoiceCall.ts
2039
2074
  import { useState as useState9, useRef as useRef5, useCallback as useCallback6 } from "react";
2040
- import { getVoiceApiUrl as getVoiceApiUrl2, getToken as getToken2, getTenant as getTenant5, getUser as getUser2 } from "@ateam-ai-mobile/core";
2075
+ import { getVoiceApiUrl as getVoiceApiUrl2, getToken as getToken2, getTenant as getTenant5, getUser as getUser2, refreshToken } from "@ateam-ai-mobile/core";
2076
+ var TOKEN_REFRESH_SKEW_MS = 9e4;
2077
+ function tokenNeedsRefresh(token) {
2078
+ if (!token) return true;
2079
+ try {
2080
+ const payloadB64 = token.split(".")[1];
2081
+ const padded = payloadB64.replace(/-/g, "+").replace(/_/g, "/");
2082
+ const json = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8");
2083
+ const exp = JSON.parse(json)?.exp;
2084
+ if (typeof exp !== "number") return false;
2085
+ return Date.now() >= exp * 1e3 - TOKEN_REFRESH_SKEW_MS;
2086
+ } catch {
2087
+ return false;
2088
+ }
2089
+ }
2041
2090
  var INJECT_TRACK_HOOK = `
2042
2091
  (function() {
2043
2092
  if (window._adaTrackHookInstalled) return;
@@ -2128,7 +2177,7 @@ var buildMuteScript = (muted) => `
2128
2177
  console.log('[ada-mute]', muted ? 'MUTED' : 'UNMUTED', 'tracks=', n);
2129
2178
  })(${muted}); true;
2130
2179
  `;
2131
- function useVoiceCall() {
2180
+ function useVoiceCall({ skillSlug } = {}) {
2132
2181
  const [callState, setCallState] = useState9("idle");
2133
2182
  const [sessionId, setSessionId] = useState9(null);
2134
2183
  const [error, setError] = useState9(null);
@@ -2169,12 +2218,18 @@ function useVoiceCall() {
2169
2218
  } catch {
2170
2219
  }
2171
2220
  }, []);
2172
- const startCall = useCallback6(() => {
2173
- setCallState("connecting");
2221
+ const startCall = useCallback6(async () => {
2174
2222
  setError(null);
2175
2223
  setTranscript([]);
2176
2224
  setAiPartial("");
2177
2225
  setUserPartial("");
2226
+ try {
2227
+ if (tokenNeedsRefresh(getToken2())) {
2228
+ await refreshToken();
2229
+ }
2230
+ } catch {
2231
+ }
2232
+ setCallState("connecting");
2178
2233
  }, []);
2179
2234
  const stopCall = useCallback6(() => {
2180
2235
  webViewRef.current?.injectJavaScript?.("window.hangUp && window.hangUp(); true;");
@@ -2194,10 +2249,11 @@ function useVoiceCall() {
2194
2249
  const token = getToken2();
2195
2250
  const tenant = getTenant5();
2196
2251
  const actorId = getUser2()?.actorId || "";
2252
+ const skillParam = skillSlug ? `&skillSlug=${encodeURIComponent(skillSlug)}` : "";
2197
2253
  return {
2198
- uri: `${getVoiceApiUrl2()}/app?mode=mini&embedded=rn&authToken=${encodeURIComponent(token || "")}&tenant=${encodeURIComponent(tenant)}&actorId=${encodeURIComponent(actorId)}`
2254
+ uri: `${getVoiceApiUrl2()}/app?mode=mini&embedded=rn&authToken=${encodeURIComponent(token || "")}&tenant=${encodeURIComponent(tenant)}&actorId=${encodeURIComponent(actorId)}${skillParam}`
2199
2255
  };
2200
- }, []);
2256
+ }, [skillSlug]);
2201
2257
  return {
2202
2258
  callState,
2203
2259
  sessionId,
@@ -2264,7 +2320,7 @@ function HighlightedText({
2264
2320
  const segments = useMemo5(() => highlightSegments(text), [text]);
2265
2321
  return /* @__PURE__ */ jsx15(Text10, { style: baseStyle, children: segments.map((seg, i) => /* @__PURE__ */ jsx15(Text10, { style: seg.important ? importantStyle : void 0, children: seg.text }, i)) });
2266
2322
  }
2267
- function VoiceCallSheet({ visible, onClose }) {
2323
+ function VoiceCallSheet({ visible, onClose, skillSlug }) {
2268
2324
  const {
2269
2325
  callState,
2270
2326
  aiPartial,
@@ -2279,7 +2335,7 @@ function VoiceCallSheet({ visible, onClose }) {
2279
2335
  getWebViewSource,
2280
2336
  webViewRef,
2281
2337
  injectedTrackHook
2282
- } = useVoiceCall();
2338
+ } = useVoiceCall({ skillSlug });
2283
2339
  const scrollRef = useRef6(null);
2284
2340
  useEffect8(() => {
2285
2341
  if (visible && callState === "idle") startCall();