@vinaverse/react-native 0.1.0-alpha.4 → 0.1.0-alpha.5

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.cjs CHANGED
@@ -1,14 +1,16 @@
1
1
  'use strict';
2
2
 
3
+ var agent = require('@vinaverse/react/agent');
3
4
  var flashList = require('@shopify/flash-list');
4
5
  var react$1 = require('@vinaverse/react');
5
6
  var reactNative = require('react-native');
7
+ var agent$1 = require('@vinaverse/client/agent');
6
8
  var react = require('react');
7
9
  var jsxRuntime = require('react/jsx-runtime');
8
10
  var tokens = require('@vinaverse/tokens');
9
11
  var conversation = require('@vinaverse/client/conversation');
10
12
 
11
- // src/cards/CardCarousel.tsx
13
+ // src/index.ts
12
14
  var ChatContext = react.createContext(null);
13
15
  function ChatContextProvider(props) {
14
16
  return /* @__PURE__ */ jsxRuntime.jsx(ChatContext.Provider, { value: props.value, children: props.children });
@@ -191,6 +193,12 @@ function ratingText(valuePpm, locale) {
191
193
  return value.toFixed(1);
192
194
  }
193
195
  }
196
+ function allowedImage(url, hosts) {
197
+ const safe = safeImage(url);
198
+ if (safe === null || !hosts) return safe;
199
+ const host = /^https:\/\/([^/:?#]+)/i.exec(safe)?.[1]?.toLowerCase();
200
+ return host && hosts.some((h) => h.toLowerCase() === host) ? safe : null;
201
+ }
194
202
  function safeImage(url) {
195
203
  return url && /^https:\/\//i.test(url) ? url : null;
196
204
  }
@@ -338,10 +346,13 @@ function LinksCard(props) {
338
346
  function ListingCard(props) {
339
347
  const card = cardOf(props.card);
340
348
  const { locale, t } = react$1.useCopy();
341
- const { slots, timeZone } = useChat();
349
+ const { slots, timeZone, handlers, imagePolicy } = useChat();
342
350
  const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
343
351
  const [failed, setFailed] = react.useState(null);
344
- const photoUrl = safeImage(card.images?.[0]?.url ?? card.image_url);
352
+ const photoUrl = allowedImage(
353
+ card.images?.[0]?.url ?? card.image_url,
354
+ imagePolicy?.hosts
355
+ );
345
356
  const hero = photoUrl === failed ? null : photoUrl;
346
357
  const Img = slots.Image ?? reactNative.Image;
347
358
  const rating = card.rating ? `\u2605 ${ratingText(card.rating.value_ppm, locale)} (${card.rating.count})` : null;
@@ -355,11 +366,55 @@ function ListingCard(props) {
355
366
  const next = card.next_available ? t("nextAvailable", {
356
367
  time: react$1.formatTimeRange(card.next_available, void 0, locale, timeZone)
357
368
  }) : null;
369
+ const ref = card.ref;
370
+ const onOpenTarget = handlers.current?.onOpenTarget;
371
+ const open = ref && onOpenTarget && !props.restored ? () => onOpenTarget({ type: ref.type, id: ref.id }, props.card) : null;
358
372
  const photo = {
359
373
  width: "100%",
360
- aspectRatio: 1.6,
374
+ aspectRatio: imagePolicy?.aspectRatio ?? 1.6,
361
375
  borderRadius: radius2.md
362
376
  };
377
+ const top = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
378
+ hero ? /* @__PURE__ */ jsxRuntime.jsx(
379
+ Img,
380
+ {
381
+ source: { uri: hero },
382
+ onError: () => setFailed(hero),
383
+ style: { ...photo, backgroundColor: colors2.surface },
384
+ accessible: false,
385
+ testID: `vinaverse-photo-${card.id}`
386
+ }
387
+ ) : imagePolicy?.Placeholder ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: photo, testID: `vinaverse-placeholder-${card.id}`, children: /* @__PURE__ */ jsxRuntime.jsx(imagePolicy.Placeholder, { title: card.title }) }) : /* @__PURE__ */ jsxRuntime.jsx(
388
+ reactNative.View,
389
+ {
390
+ accessible: false,
391
+ importantForAccessibility: "no-hide-descendants",
392
+ testID: `vinaverse-monogram-${card.id}`,
393
+ style: {
394
+ ...photo,
395
+ backgroundColor: colors2.surface,
396
+ alignItems: "center",
397
+ justifyContent: "center"
398
+ },
399
+ children: /* @__PURE__ */ jsxRuntime.jsx(
400
+ reactNative.Text,
401
+ {
402
+ style: {
403
+ color: colors2.textMuted,
404
+ fontSize: font2.size.xl,
405
+ fontWeight: "600"
406
+ },
407
+ children: monogram(card.title)
408
+ }
409
+ )
410
+ }
411
+ ),
412
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: space2.xxs }, children: [
413
+ /* @__PURE__ */ jsxRuntime.jsx(Title, { numberOfLines: 2, children: card.title }),
414
+ card.subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Body, { muted: true, children: card.subtitle }) : null,
415
+ rating || price ? /* @__PURE__ */ jsxRuntime.jsx(Body, { muted: true, children: [rating, price].filter(Boolean).join(" \xB7 ") }) : null
416
+ ] })
417
+ ] });
363
418
  return /* @__PURE__ */ jsxRuntime.jsxs(
364
419
  CardFrame,
365
420
  {
@@ -367,45 +422,17 @@ function ListingCard(props) {
367
422
  restored: props.restored,
368
423
  testID: `vinaverse-card-${card.id}`,
369
424
  children: [
370
- hero ? /* @__PURE__ */ jsxRuntime.jsx(
371
- Img,
425
+ open ? /* @__PURE__ */ jsxRuntime.jsx(
426
+ reactNative.Pressable,
372
427
  {
373
- source: { uri: hero },
374
- onError: () => setFailed(hero),
375
- style: { ...photo, backgroundColor: colors2.surface },
376
- accessible: false,
377
- testID: `vinaverse-photo-${card.id}`
428
+ accessibilityRole: "link",
429
+ accessibilityLabel: card.title,
430
+ onPress: open,
431
+ style: { gap: space2.sm },
432
+ testID: `vinaverse-open-${card.id}`,
433
+ children: top
378
434
  }
379
- ) : /* @__PURE__ */ jsxRuntime.jsx(
380
- reactNative.View,
381
- {
382
- accessible: false,
383
- importantForAccessibility: "no-hide-descendants",
384
- testID: `vinaverse-monogram-${card.id}`,
385
- style: {
386
- ...photo,
387
- backgroundColor: colors2.surface,
388
- alignItems: "center",
389
- justifyContent: "center"
390
- },
391
- children: /* @__PURE__ */ jsxRuntime.jsx(
392
- reactNative.Text,
393
- {
394
- style: {
395
- color: colors2.textMuted,
396
- fontSize: font2.size.xl,
397
- fontWeight: "600"
398
- },
399
- children: monogram(card.title)
400
- }
401
- )
402
- }
403
- ),
404
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: space2.xxs }, children: [
405
- /* @__PURE__ */ jsxRuntime.jsx(Title, { numberOfLines: 2, children: card.title }),
406
- card.subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Body, { muted: true, children: card.subtitle }) : null,
407
- rating || price ? /* @__PURE__ */ jsxRuntime.jsx(Body, { muted: true, children: [rating, price].filter(Boolean).join(" \xB7 ") }) : null
408
- ] }),
435
+ ) : top,
409
436
  card.body ? /* @__PURE__ */ jsxRuntime.jsx(Body, { children: card.body }) : null,
410
437
  /* @__PURE__ */ jsxRuntime.jsx(Badges, { badges: next ? [next, ...card.badges ?? []] : card.badges }),
411
438
  props.restored ? null : /* @__PURE__ */ jsxRuntime.jsx(CardActions, { ...props })
@@ -1297,7 +1324,8 @@ var CardView = react.memo(function CardView2(props) {
1297
1324
  const { card, busy } = props.item;
1298
1325
  const Default = defaultCard(card.kind);
1299
1326
  const Custom = slots.cards?.[card.kind];
1300
- const own = { card, busy, restored: props.item.restored === true };
1327
+ const read = react.useMemo(() => agent$1.readCard(card.card), [card]);
1328
+ const own = { card, read, busy, restored: props.item.restored === true };
1301
1329
  return Custom ? /* @__PURE__ */ jsxRuntime.jsx(Custom, { ...own, Default }) : /* @__PURE__ */ jsxRuntime.jsx(Default, { ...own });
1302
1330
  });
1303
1331
  var keyOf = (item) => item.key;
@@ -1351,6 +1379,38 @@ var ActivityLine = react.memo(function ActivityLine2() {
1351
1379
  }
1352
1380
  );
1353
1381
  });
1382
+ function useNativeLifecycle(store) {
1383
+ react.useEffect(() => {
1384
+ const apply = (state) => store.setForeground(state === "active");
1385
+ apply(reactNative.AppState.currentState);
1386
+ const subscription = reactNative.AppState.addEventListener("change", apply);
1387
+ return () => subscription.remove();
1388
+ }, [store]);
1389
+ }
1390
+ function useReducedMotion() {
1391
+ const [reduced, setReduced] = react.useState(false);
1392
+ react.useEffect(() => {
1393
+ let live = true;
1394
+ reactNative.AccessibilityInfo.isReduceMotionEnabled().then(
1395
+ (value) => {
1396
+ if (live) setReduced(value);
1397
+ },
1398
+ () => void 0
1399
+ );
1400
+ const subscription = reactNative.AccessibilityInfo.addEventListener(
1401
+ "reduceMotionChanged",
1402
+ setReduced
1403
+ );
1404
+ return () => {
1405
+ live = false;
1406
+ subscription.remove();
1407
+ };
1408
+ }, []);
1409
+ return reduced;
1410
+ }
1411
+ function announce(text) {
1412
+ if (text) reactNative.AccessibilityInfo.announceForAccessibility(text);
1413
+ }
1354
1414
  var Avatar = react.memo(function Avatar2(props) {
1355
1415
  const { colors: colors2, size: size3, font: font2 } = useTheme();
1356
1416
  const { slots } = useChat();
@@ -1463,37 +1523,6 @@ var ChatHeader = react.memo(function ChatHeader2() {
1463
1523
  };
1464
1524
  return Header ? /* @__PURE__ */ jsxRuntime.jsx(Header, { ...props, Default: DefaultHeader }) : /* @__PURE__ */ jsxRuntime.jsx(DefaultHeader, { ...props });
1465
1525
  });
1466
- var ChatSkeleton = react.memo(function ChatSkeleton2() {
1467
- const { colors: colors2, radius: radius2, space: space2 } = useTheme();
1468
- const row = (width, end) => /* @__PURE__ */ jsxRuntime.jsx(
1469
- reactNative.View,
1470
- {
1471
- style: {
1472
- alignSelf: end ? "flex-end" : "flex-start",
1473
- width,
1474
- height: 40,
1475
- borderRadius: radius2.lg,
1476
- backgroundColor: colors2.surface,
1477
- marginHorizontal: space2.lg,
1478
- marginTop: space2.md
1479
- }
1480
- }
1481
- );
1482
- return /* @__PURE__ */ jsxRuntime.jsxs(
1483
- reactNative.View,
1484
- {
1485
- accessible: true,
1486
- accessibilityRole: "progressbar",
1487
- accessibilityState: { busy: true },
1488
- testID: "vinaverse-skeleton",
1489
- children: [
1490
- row("62%", false),
1491
- row("45%", true),
1492
- row("70%", false)
1493
- ]
1494
- }
1495
- );
1496
- });
1497
1526
  function DefaultComposer(props) {
1498
1527
  const { t, direction } = react$1.useCopy();
1499
1528
  const { colors: colors2, radius: radius2, space: space2, font: font2, size: size3 } = useTheme();
@@ -1635,23 +1664,37 @@ var DisclosureBar = react.memo(function DisclosureBar2() {
1635
1664
  }
1636
1665
  );
1637
1666
  });
1638
- function FailureActionButton(props) {
1639
- const { handlers, failureAction } = useChat();
1640
- const { t } = react$1.useCopy();
1641
- if (!failureAction) return null;
1642
- return /* @__PURE__ */ jsxRuntime.jsx(
1643
- Button,
1667
+ var ChatSkeleton = react.memo(function ChatSkeleton2() {
1668
+ const { colors: colors2, radius: radius2, space: space2 } = useTheme();
1669
+ const row = (width, end) => /* @__PURE__ */ jsxRuntime.jsx(
1670
+ reactNative.View,
1644
1671
  {
1645
- label: failureAction.label ?? t("talkToPerson"),
1646
- variant: "text",
1647
- onPress: () => handlers.current.failureAction?.onPress(props.state)
1672
+ style: {
1673
+ alignSelf: end ? "flex-end" : "flex-start",
1674
+ width,
1675
+ height: 40,
1676
+ borderRadius: radius2.lg,
1677
+ backgroundColor: colors2.surface,
1678
+ marginHorizontal: space2.lg,
1679
+ marginTop: space2.md
1680
+ }
1648
1681
  }
1649
1682
  );
1650
- }
1651
- function FailureSlot(props) {
1652
- const { slot: Slot, state, Default } = props;
1653
- return Slot ? /* @__PURE__ */ jsxRuntime.jsx(Slot, { state, Default }) : /* @__PURE__ */ jsxRuntime.jsx(Default, { state });
1654
- }
1683
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1684
+ reactNative.View,
1685
+ {
1686
+ accessible: true,
1687
+ accessibilityRole: "progressbar",
1688
+ accessibilityState: { busy: true },
1689
+ testID: "vinaverse-skeleton",
1690
+ children: [
1691
+ row("62%", false),
1692
+ row("45%", true),
1693
+ row("70%", false)
1694
+ ]
1695
+ }
1696
+ );
1697
+ });
1655
1698
  function DefaultBubble(props) {
1656
1699
  const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
1657
1700
  const role = props.item.role;
@@ -1773,37 +1816,22 @@ function Frame(props) {
1773
1816
  }
1774
1817
  );
1775
1818
  }
1776
- function useNativeLifecycle(store) {
1777
- react.useEffect(() => {
1778
- const apply = (state) => store.setForeground(state === "active");
1779
- apply(reactNative.AppState.currentState);
1780
- const subscription = reactNative.AppState.addEventListener("change", apply);
1781
- return () => subscription.remove();
1782
- }, [store]);
1783
- }
1784
- function useReducedMotion() {
1785
- const [reduced, setReduced] = react.useState(false);
1786
- react.useEffect(() => {
1787
- let live = true;
1788
- reactNative.AccessibilityInfo.isReduceMotionEnabled().then(
1789
- (value) => {
1790
- if (live) setReduced(value);
1791
- },
1792
- () => void 0
1793
- );
1794
- const subscription = reactNative.AccessibilityInfo.addEventListener(
1795
- "reduceMotionChanged",
1796
- setReduced
1797
- );
1798
- return () => {
1799
- live = false;
1800
- subscription.remove();
1801
- };
1802
- }, []);
1803
- return reduced;
1819
+ function FailureActionButton(props) {
1820
+ const { handlers, failureAction } = useChat();
1821
+ const { t } = react$1.useCopy();
1822
+ if (!failureAction) return null;
1823
+ return /* @__PURE__ */ jsxRuntime.jsx(
1824
+ Button,
1825
+ {
1826
+ label: failureAction.label ?? t("talkToPerson"),
1827
+ variant: "text",
1828
+ onPress: () => handlers.current.failureAction?.onPress(props.state)
1829
+ }
1830
+ );
1804
1831
  }
1805
- function announce(text) {
1806
- if (text) reactNative.AccessibilityInfo.announceForAccessibility(text);
1832
+ function FailureSlot(props) {
1833
+ const { slot: Slot, state, Default } = props;
1834
+ return Slot ? /* @__PURE__ */ jsxRuntime.jsx(Slot, { state, Default }) : /* @__PURE__ */ jsxRuntime.jsx(Default, { state });
1807
1835
  }
1808
1836
  var SystemRow = react.memo(function SystemRow2(props) {
1809
1837
  const { t } = react$1.useCopy();
@@ -1971,17 +1999,27 @@ var Row = react.memo(function Row2(props) {
1971
1999
  return item.state === "streaming" ? /* @__PURE__ */ jsxRuntime.jsx(StreamingBubble, { item, showAuthor: props.showAuthor }) : /* @__PURE__ */ jsxRuntime.jsx(MessageBubble, { item, showAuthor: props.showAuthor });
1972
2000
  });
1973
2001
  var MessageList = react.memo(function MessageList2() {
1974
- const { store, slots } = useChat();
2002
+ const { store, slots, starters, handlers, imagePolicy } = useChat();
1975
2003
  const { t } = react$1.useCopy();
1976
2004
  const { space: space2 } = useTheme();
1977
2005
  const reducedMotion = useReducedMotion();
1978
2006
  const items = react$1.useConversationSelector(store, (v) => v.items);
2007
+ usePrefetchPhotos(items, handlers.current?.prefetchImage, imagePolicy?.hosts);
1979
2008
  const opening = react$1.useConversationSelector(
1980
2009
  store,
1981
2010
  (v) => v.status === "starting" || v.status === "connecting"
1982
2011
  );
1983
2012
  const agentName = react$1.useConversationSelector(store, (v) => v.agent.name);
1984
- const welcome = react$1.useConversationSelector(store, (v) => v.welcome);
2013
+ const served = react$1.useConversationSelector(store, (v) => v.welcome);
2014
+ const welcome = react.useMemo(
2015
+ () => served ?? (starters?.length ? {
2016
+ text: t("welcomeFallback", {
2017
+ agent: agentName ?? t("agentFallbackName")
2018
+ }),
2019
+ chips: [...starters]
2020
+ } : null),
2021
+ [served, starters, t, agentName]
2022
+ );
1985
2023
  useAnnounceReplies(items);
1986
2024
  const Group = slots.CardGroup;
1987
2025
  const runsRef = react.useRef(/* @__PURE__ */ new Map());
@@ -2007,8 +2045,12 @@ var MessageList = react.memo(function MessageList2() {
2007
2045
  []
2008
2046
  );
2009
2047
  const onChip = react.useCallback(
2010
- (message) => void store.send(message),
2011
- [store]
2048
+ (message) => {
2049
+ store.send(message);
2050
+ const chip = welcome?.chips.find((c) => c.message === message);
2051
+ if (chip) handlers.current?.onChip?.(chip);
2052
+ },
2053
+ [store, welcome, handlers]
2012
2054
  );
2013
2055
  const Empty = slots.EmptyState;
2014
2056
  const empty = opening ? /* @__PURE__ */ jsxRuntime.jsx(ChatSkeleton, {}) : Empty ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -2040,10 +2082,25 @@ var MessageList = react.memo(function MessageList2() {
2040
2082
  const List = slots.ListComponent;
2041
2083
  return List ? /* @__PURE__ */ jsxRuntime.jsx(List, { ...props }) : /* @__PURE__ */ jsxRuntime.jsx(flashList.FlashList, { ...props });
2042
2084
  });
2085
+ function usePrefetchPhotos(items, prefetch, hosts) {
2086
+ const done = react.useRef(/* @__PURE__ */ new Set());
2087
+ react.useEffect(() => {
2088
+ if (!prefetch) return;
2089
+ for (const item of items) {
2090
+ if (item.type !== "card") continue;
2091
+ const card = item.card.card;
2092
+ const url = allowedImage(card.images?.[0]?.url ?? card.image_url, hosts);
2093
+ if (url && !done.current.has(url)) {
2094
+ done.current.add(url);
2095
+ prefetch(url);
2096
+ }
2097
+ }
2098
+ }, [items, prefetch, hosts]);
2099
+ }
2043
2100
  function useAnnounceReplies(items) {
2044
2101
  const seen = react.useRef(void 0);
2045
2102
  const { t } = react$1.useCopy();
2046
- const { store } = useChat();
2103
+ const { store, handlers } = useChat();
2047
2104
  react.useEffect(() => {
2048
2105
  const last = items.at(-1);
2049
2106
  if (seen.current === void 0) {
@@ -2056,8 +2113,8 @@ function useAnnounceReplies(items) {
2056
2113
  const author = last.role === "staff" ? t("staffLabel", {
2057
2114
  by: store.getSnapshot().takeover?.by || t("staffFallbackRole")
2058
2115
  }) : store.getSnapshot().agent.name ?? t("agentFallbackName");
2059
- announce(`${author}: ${last.text}`);
2060
- }, [items, t, store]);
2116
+ (handlers.current?.announce ?? announce)(`${author}: ${last.text}`);
2117
+ }, [items, t, store, handlers]);
2061
2118
  }
2062
2119
  var CONNECTING_DELAY = tokens.motion.bannerDelay;
2063
2120
  var StatusBanner = react.memo(function StatusBanner2() {
@@ -2158,7 +2215,14 @@ function Banner(props) {
2158
2215
  );
2159
2216
  }
2160
2217
  function VinaverseChatProvider(props) {
2161
- const { conversation, components, agentAvatar, timeZone } = props;
2218
+ const {
2219
+ conversation,
2220
+ components,
2221
+ agentAvatar,
2222
+ timeZone,
2223
+ starters,
2224
+ imagePolicy
2225
+ } = props;
2162
2226
  const handlers = react.useRef(props);
2163
2227
  handlers.current = props;
2164
2228
  const slots = useStableSlots(components);
@@ -2175,9 +2239,19 @@ function VinaverseChatProvider(props) {
2175
2239
  slots,
2176
2240
  agentAvatar,
2177
2241
  timeZone,
2178
- failureAction
2242
+ failureAction,
2243
+ starters,
2244
+ imagePolicy
2179
2245
  }),
2180
- [conversation, slots, agentAvatar, timeZone, failureAction]
2246
+ [
2247
+ conversation,
2248
+ slots,
2249
+ agentAvatar,
2250
+ timeZone,
2251
+ failureAction,
2252
+ starters,
2253
+ imagePolicy
2254
+ ]
2181
2255
  );
2182
2256
  useNativeLifecycle(conversation);
2183
2257
  react.useEffect(() => {
@@ -2203,7 +2277,9 @@ function VinaverseChat(props) {
2203
2277
  keyboardVerticalOffset: props.keyboardVerticalOffset ?? 0,
2204
2278
  keyboardBehavior: props.keyboardBehavior ?? (reactNative.Platform.OS === "ios" ? "padding" : "none"),
2205
2279
  style: props.style,
2206
- onError: props.onWarning
2280
+ onError: props.onWarning,
2281
+ suggestion: props.suggestion ?? null,
2282
+ footer: props.footer
2207
2283
  }
2208
2284
  ) });
2209
2285
  }
@@ -2229,11 +2305,27 @@ function Layout(props) {
2229
2305
  /* @__PURE__ */ jsxRuntime.jsx(DisclosureBar, {}),
2230
2306
  /* @__PURE__ */ jsxRuntime.jsx(StatusBanner, {}),
2231
2307
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(MessageList, {}) }),
2232
- /* @__PURE__ */ jsxRuntime.jsx(Composer, { ...reportError ? { onError: reportError } : {} })
2308
+ props.suggestion ? /* @__PURE__ */ jsxRuntime.jsx(Suggestion, { chip: props.suggestion }) : null,
2309
+ /* @__PURE__ */ jsxRuntime.jsx(Composer, { ...reportError ? { onError: reportError } : {} }),
2310
+ props.footer
2233
2311
  ]
2234
2312
  }
2235
2313
  );
2236
2314
  }
2315
+ function Suggestion(props) {
2316
+ const { store, handlers } = useChat();
2317
+ const { space: space2 } = useTheme();
2318
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { paddingHorizontal: space2.lg, alignItems: "flex-start" }, children: /* @__PURE__ */ jsxRuntime.jsx(
2319
+ Button,
2320
+ {
2321
+ label: props.chip.label,
2322
+ onPress: () => {
2323
+ store.send(props.chip.message);
2324
+ handlers.current?.onChip?.(props.chip);
2325
+ }
2326
+ }
2327
+ ) });
2328
+ }
2237
2329
  function useStableSlots(slots) {
2238
2330
  const held = react.useRef({});
2239
2331
  const next = slots ?? {};
@@ -2252,8 +2344,631 @@ function sameSlots(a, b) {
2252
2344
  }
2253
2345
  return true;
2254
2346
  }
2347
+ function AgentChat(props) {
2348
+ const {
2349
+ agentKey,
2350
+ place,
2351
+ entryPoint,
2352
+ askAbout,
2353
+ onContact,
2354
+ onOpenPrivacy,
2355
+ components,
2356
+ agentName,
2357
+ opener,
2358
+ ...chat
2359
+ } = props;
2360
+ const runtime = agent.useAgentRuntime();
2361
+ const { conversation, state, startNew } = agent.useAgentConversation(agentKey, {
2362
+ place: place ?? null,
2363
+ entryPoint: entryPoint ?? null
2364
+ });
2365
+ const store = conversation.store;
2366
+ const spoke = react$1.useConversationSelector(
2367
+ store,
2368
+ (v) => v.items.some((i) => i.type === "message" && i.role === "person")
2369
+ );
2370
+ const { Ended, Unreachable, UnavailableScreen, Footer, ...slots } = components ?? {};
2371
+ const copy = react.useMemo(
2372
+ () => agentName ? { ...props.copy, agentFallbackName: agentName } : props.copy,
2373
+ [agentName, props.copy]
2374
+ );
2375
+ const events = {
2376
+ onOpenTarget: chat.onOpenTarget ? (target, card) => {
2377
+ runtime.emit({
2378
+ type: "target_opened",
2379
+ key: conversation.key,
2380
+ cardId: card.id,
2381
+ kind: target.type
2382
+ });
2383
+ chat.onOpenTarget?.(target, card);
2384
+ } : void 0,
2385
+ onOpenLink: chat.onOpenLink ? (link, card) => {
2386
+ runtime.emit({
2387
+ type: "link_opened",
2388
+ key: conversation.key,
2389
+ cardId: card.id,
2390
+ kind: card.kind
2391
+ });
2392
+ chat.onOpenLink?.(link, card);
2393
+ } : void 0,
2394
+ onChip: (chip) => {
2395
+ runtime.emit({ type: "chip_tapped", key: conversation.key });
2396
+ chat.onChip?.(chip);
2397
+ }
2398
+ };
2399
+ const shared = {
2400
+ ...chat,
2401
+ ...events.onOpenTarget ? { onOpenTarget: events.onOpenTarget } : {},
2402
+ ...events.onOpenLink ? { onOpenLink: events.onOpenLink } : {},
2403
+ onChip: events.onChip,
2404
+ ...copy ? { copy } : {},
2405
+ ...onContact && !chat.failureAction ? { failureAction: { onPress: () => onContact() } } : {},
2406
+ conversation: store,
2407
+ components: slots
2408
+ };
2409
+ if (state.failure === "ended")
2410
+ return /* @__PURE__ */ jsxRuntime.jsx(VinaverseChatProvider, { ...shared, children: /* @__PURE__ */ jsxRuntime.jsx(Centered, { children: Ended ? /* @__PURE__ */ jsxRuntime.jsx(
2411
+ Ended,
2412
+ {
2413
+ onStartNew: startNew,
2414
+ code: state.code,
2415
+ Default: DefaultEnded
2416
+ }
2417
+ ) : /* @__PURE__ */ jsxRuntime.jsx(DefaultEnded, { onStartNew: startNew, code: state.code }) }) });
2418
+ if (state.failure === "unreachable")
2419
+ return /* @__PURE__ */ jsxRuntime.jsx(VinaverseChatProvider, { ...shared, children: /* @__PURE__ */ jsxRuntime.jsx(Centered, { children: Unreachable ? /* @__PURE__ */ jsxRuntime.jsx(
2420
+ Unreachable,
2421
+ {
2422
+ onRetry: conversation.retry,
2423
+ reason: state.reason,
2424
+ Default: DefaultUnreachable
2425
+ }
2426
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
2427
+ DefaultUnreachable,
2428
+ {
2429
+ onRetry: conversation.retry,
2430
+ reason: state.reason
2431
+ }
2432
+ ) }) });
2433
+ if (state.failure === "unavailable") {
2434
+ const own = {
2435
+ reason: state.reason,
2436
+ code: state.code,
2437
+ onContact,
2438
+ onRetry: conversation.retry
2439
+ };
2440
+ return /* @__PURE__ */ jsxRuntime.jsx(VinaverseChatProvider, { ...shared, children: /* @__PURE__ */ jsxRuntime.jsx(Centered, { children: UnavailableScreen ? /* @__PURE__ */ jsxRuntime.jsx(UnavailableScreen, { ...own, Default: DefaultUnavailableScreen }) : /* @__PURE__ */ jsxRuntime.jsx(DefaultUnavailableScreen, { ...own }) }) });
2441
+ }
2442
+ return /* @__PURE__ */ jsxRuntime.jsx(
2443
+ VinaverseChat,
2444
+ {
2445
+ ...shared,
2446
+ suggestion: opener && !spoke ? opener : state.moved && askAbout ? { label: askAbout, message: askAbout } : null,
2447
+ footer: Footer === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
2448
+ FooterFor,
2449
+ {
2450
+ store,
2451
+ state,
2452
+ rate: conversation.rate,
2453
+ Footer,
2454
+ onOpenPrivacy
2455
+ }
2456
+ )
2457
+ }
2458
+ );
2459
+ }
2460
+ function FooterFor(props) {
2461
+ const turnId = react$1.useConversationSelector(props.store, newestWholeReply);
2462
+ const own = {
2463
+ turnId,
2464
+ rating: turnId ? props.state.ratings.get(turnId) : void 0,
2465
+ onRate: (rating) => {
2466
+ if (turnId) props.rate(turnId, rating);
2467
+ },
2468
+ onOpenPrivacy: props.onOpenPrivacy
2469
+ };
2470
+ const { Footer } = props;
2471
+ return Footer ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { ...own, Default: AgentFooter }) : /* @__PURE__ */ jsxRuntime.jsx(AgentFooter, { ...own });
2472
+ }
2473
+ function newestWholeReply(view) {
2474
+ for (let i = view.items.length - 1; i >= 0; i--) {
2475
+ const item = view.items[i];
2476
+ if (item?.type !== "message" || item.role !== "agent") continue;
2477
+ const whole = item.state === "sent" && !item.partial && !item.refusal && item.key.startsWith("a:");
2478
+ return whole ? item.key.slice(2) : null;
2479
+ }
2480
+ return null;
2481
+ }
2482
+ function Centered(props) {
2483
+ const { colors: colors2, space: space2 } = useTheme();
2484
+ return /* @__PURE__ */ jsxRuntime.jsx(
2485
+ reactNative.View,
2486
+ {
2487
+ style: {
2488
+ flex: 1,
2489
+ alignItems: "center",
2490
+ justifyContent: "center",
2491
+ padding: space2.xl,
2492
+ backgroundColor: colors2.background
2493
+ },
2494
+ children: props.children
2495
+ }
2496
+ );
2497
+ }
2498
+ function Notice(props) {
2499
+ const { colors: colors2, space: space2, font: font2 } = useTheme();
2500
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2501
+ reactNative.View,
2502
+ {
2503
+ accessibilityRole: "alert",
2504
+ style: { alignItems: "center", gap: space2.md },
2505
+ children: [
2506
+ /* @__PURE__ */ jsxRuntime.jsx(
2507
+ reactNative.Text,
2508
+ {
2509
+ style: {
2510
+ color: colors2.text,
2511
+ fontSize: font2.size.lg,
2512
+ fontWeight: "600",
2513
+ textAlign: "center"
2514
+ },
2515
+ children: props.title
2516
+ }
2517
+ ),
2518
+ /* @__PURE__ */ jsxRuntime.jsx(
2519
+ reactNative.Text,
2520
+ {
2521
+ style: {
2522
+ color: colors2.textMuted,
2523
+ fontSize: font2.size.md,
2524
+ textAlign: "center"
2525
+ },
2526
+ children: props.body
2527
+ }
2528
+ ),
2529
+ props.action && props.onPress ? /* @__PURE__ */ jsxRuntime.jsx(
2530
+ Button,
2531
+ {
2532
+ label: props.action,
2533
+ variant: "primary",
2534
+ onPress: props.onPress
2535
+ }
2536
+ ) : null
2537
+ ]
2538
+ }
2539
+ );
2540
+ }
2541
+ function DefaultEnded(props) {
2542
+ const { t } = react$1.useCopy();
2543
+ return /* @__PURE__ */ jsxRuntime.jsx(
2544
+ Notice,
2545
+ {
2546
+ title: t("agentEnded"),
2547
+ body: t("agentEndedBody"),
2548
+ action: t("agentStartOver"),
2549
+ onPress: props.onStartNew
2550
+ }
2551
+ );
2552
+ }
2553
+ function DefaultUnavailableScreen(props) {
2554
+ const { t } = react$1.useCopy();
2555
+ const name = agent.useAgentName(t("agentFallbackName"));
2556
+ const title = t("agentOff", { agent: name });
2557
+ return /* @__PURE__ */ jsxRuntime.jsx(
2558
+ Notice,
2559
+ {
2560
+ title: title.charAt(0).toUpperCase() + title.slice(1),
2561
+ body: t("agentOffBody"),
2562
+ ...props.onContact ? { action: t("talkToPerson"), onPress: props.onContact } : {}
2563
+ }
2564
+ );
2565
+ }
2566
+ function DefaultUnreachable(props) {
2567
+ const { t } = react$1.useCopy();
2568
+ const name = agent.useAgentName(t("agentFallbackName"));
2569
+ return /* @__PURE__ */ jsxRuntime.jsx(
2570
+ Notice,
2571
+ {
2572
+ title: t("agentUnreachable", { agent: name }),
2573
+ body: t("agentUnreachableBody"),
2574
+ action: t("tryAgain"),
2575
+ onPress: props.onRetry
2576
+ }
2577
+ );
2578
+ }
2579
+ function AgentFooter(props) {
2580
+ const { t } = react$1.useCopy();
2581
+ const { colors: colors2, space: space2, font: font2 } = useTheme();
2582
+ const muted = { color: colors2.textMuted, fontSize: font2.size.sm };
2583
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2584
+ reactNative.View,
2585
+ {
2586
+ style: {
2587
+ flexDirection: "row",
2588
+ flexWrap: "wrap",
2589
+ alignItems: "center",
2590
+ gap: space2.xs,
2591
+ paddingHorizontal: space2.lg
2592
+ },
2593
+ children: [
2594
+ props.turnId === null ? null : props.rating ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: muted, children: t("helpfulThanks") }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2595
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: muted, children: t("helpful") }),
2596
+ /* @__PURE__ */ jsxRuntime.jsx(
2597
+ Button,
2598
+ {
2599
+ label: t("helpfulYes"),
2600
+ variant: "text",
2601
+ onPress: () => props.onRate("up")
2602
+ }
2603
+ ),
2604
+ /* @__PURE__ */ jsxRuntime.jsx(
2605
+ Button,
2606
+ {
2607
+ label: t("helpfulNo"),
2608
+ variant: "text",
2609
+ onPress: () => props.onRate("down")
2610
+ }
2611
+ )
2612
+ ] }),
2613
+ props.onOpenPrivacy ? /* @__PURE__ */ jsxRuntime.jsx(
2614
+ Button,
2615
+ {
2616
+ label: t("privacy"),
2617
+ variant: "text",
2618
+ role: "link",
2619
+ onPress: props.onOpenPrivacy
2620
+ }
2621
+ ) : null
2622
+ ]
2623
+ }
2624
+ );
2625
+ }
2626
+ var LAUNCHER_SIZE = 56;
2627
+ function AgentLauncher(props) {
2628
+ return /* @__PURE__ */ jsxRuntime.jsx(react$1.CopyProvider, { locale: props.locale, copy: props.copy, children: /* @__PURE__ */ jsxRuntime.jsx(ThemeProvider, { theme: props.theme, colorScheme: props.colorScheme, children: /* @__PURE__ */ jsxRuntime.jsx(Launcher, { ...props }) }) });
2629
+ }
2630
+ function useKeyboardShown() {
2631
+ const [shown, setShown] = react.useState(false);
2632
+ react.useEffect(() => {
2633
+ const show = reactNative.Keyboard.addListener("keyboardDidShow", () => setShown(true));
2634
+ const hide = reactNative.Keyboard.addListener("keyboardDidHide", () => setShown(false));
2635
+ return () => {
2636
+ show.remove();
2637
+ hide.remove();
2638
+ };
2639
+ }, []);
2640
+ return shown;
2641
+ }
2642
+ function Launcher(props) {
2643
+ const { t } = react$1.useCopy();
2644
+ const name = agent.useAgentName(t("agentFallbackName"));
2645
+ const keyboard = useKeyboardShown();
2646
+ const reducedMotion = useReducedMotion();
2647
+ const { route = null, routes, mode } = props;
2648
+ const visible = props.hidden !== true && !(keyboard && props.hideWithKeyboard !== false) && (!routes || route !== null && routes.includes(route));
2649
+ const launcher = agent.useAgentLauncher({
2650
+ subject: props.subject,
2651
+ mode,
2652
+ visible,
2653
+ onOpen: () => props.onOpen({ route, mode }),
2654
+ ...props.storage ? { storage: props.storage } : {},
2655
+ ...props.storageKey ? { storageKey: props.storageKey } : {},
2656
+ ...props.delayMs === void 0 ? {} : { delayMs: props.delayMs }
2657
+ });
2658
+ const text = typeof props.greeting === "function" ? props.greeting(mode) : props.greeting ?? t("launcherGreeting", { agent: name });
2659
+ const say = props.announce ?? announce;
2660
+ const said = react.useRef(false);
2661
+ react.useEffect(() => {
2662
+ if (!launcher.greets) {
2663
+ said.current = false;
2664
+ return;
2665
+ }
2666
+ if (said.current) return;
2667
+ said.current = true;
2668
+ say(text);
2669
+ }, [launcher.greets, text, say]);
2670
+ if (!launcher.shows) return null;
2671
+ const edge = props.placement?.edge ?? "right";
2672
+ const look = props.appearance ?? {};
2673
+ const greeting = {
2674
+ text,
2675
+ closeLabel: t("close"),
2676
+ onOpen: launcher.open,
2677
+ onClose: launcher.dismiss,
2678
+ tail: look.tail !== false,
2679
+ edge
2680
+ };
2681
+ const button = {
2682
+ label: t("launcherOpen", { agent: name }),
2683
+ name,
2684
+ onPress: launcher.open,
2685
+ Mark: props.Mark,
2686
+ ring: look.ring ?? null,
2687
+ shadow: look.shadow !== false
2688
+ };
2689
+ const { Greeting, Button: Button3, Entrance } = props.components ?? {};
2690
+ const enter = (part, children) => Entrance ? /* @__PURE__ */ jsxRuntime.jsx(
2691
+ Entrance,
2692
+ {
2693
+ part,
2694
+ reducedMotion,
2695
+ Default: LauncherEntrance,
2696
+ children
2697
+ }
2698
+ ) : /* @__PURE__ */ jsxRuntime.jsx(LauncherEntrance, { part, reducedMotion, children });
2699
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2700
+ reactNative.View,
2701
+ {
2702
+ pointerEvents: "box-none",
2703
+ style: [
2704
+ {
2705
+ position: "absolute",
2706
+ bottom: props.placement?.bottom ?? 16,
2707
+ [edge]: props.placement?.inset ?? 16,
2708
+ alignItems: edge === "right" ? "flex-end" : "flex-start",
2709
+ gap: 8
2710
+ },
2711
+ props.style
2712
+ ],
2713
+ testID: "vinaverse-launcher",
2714
+ children: [
2715
+ launcher.greets ? enter(
2716
+ "greeting",
2717
+ Greeting ? /* @__PURE__ */ jsxRuntime.jsx(Greeting, { ...greeting, Default: LauncherGreeting }) : /* @__PURE__ */ jsxRuntime.jsx(LauncherGreeting, { ...greeting })
2718
+ ) : null,
2719
+ enter(
2720
+ "button",
2721
+ Button3 ? /* @__PURE__ */ jsxRuntime.jsx(Button3, { ...button, Default: LauncherButton }) : /* @__PURE__ */ jsxRuntime.jsx(LauncherButton, { ...button })
2722
+ )
2723
+ ]
2724
+ }
2725
+ );
2726
+ }
2727
+ function LauncherEntrance(props) {
2728
+ const { motion: motion3 } = useTheme();
2729
+ const shown = react.useRef(new reactNative.Animated.Value(props.reducedMotion ? 1 : 0)).current;
2730
+ react.useEffect(() => {
2731
+ if (props.reducedMotion) {
2732
+ shown.setValue(1);
2733
+ return;
2734
+ }
2735
+ const run = reactNative.Animated.timing(shown, {
2736
+ toValue: 1,
2737
+ duration: motion3.normal,
2738
+ useNativeDriver: true
2739
+ });
2740
+ run.start();
2741
+ return () => run.stop();
2742
+ }, [shown, props.reducedMotion, motion3.normal]);
2743
+ return /* @__PURE__ */ jsxRuntime.jsx(
2744
+ reactNative.Animated.View,
2745
+ {
2746
+ pointerEvents: "box-none",
2747
+ style: {
2748
+ opacity: shown,
2749
+ transform: [
2750
+ {
2751
+ scale: shown.interpolate({
2752
+ inputRange: [0, 1],
2753
+ outputRange: [0.9, 1]
2754
+ })
2755
+ }
2756
+ ]
2757
+ },
2758
+ testID: `vinaverse-launcher-entrance-${props.part}`,
2759
+ children: props.children
2760
+ }
2761
+ );
2762
+ }
2763
+ function LauncherGreeting(props) {
2764
+ const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
2765
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2766
+ reactNative.View,
2767
+ {
2768
+ style: {
2769
+ maxWidth: 296,
2770
+ flexDirection: "row",
2771
+ alignItems: "flex-start",
2772
+ borderRadius: radius2.lg,
2773
+ backgroundColor: colors2.text
2774
+ },
2775
+ testID: "vinaverse-launcher-greeting",
2776
+ children: [
2777
+ props.tail ? /* @__PURE__ */ jsxRuntime.jsx(
2778
+ reactNative.View,
2779
+ {
2780
+ accessible: false,
2781
+ style: {
2782
+ position: "absolute",
2783
+ bottom: -5,
2784
+ [props.edge]: LAUNCHER_SIZE / 2 - 6,
2785
+ width: 12,
2786
+ height: 12,
2787
+ backgroundColor: colors2.text,
2788
+ transform: [{ rotate: "45deg" }]
2789
+ },
2790
+ testID: "vinaverse-launcher-tail"
2791
+ }
2792
+ ) : null,
2793
+ /* @__PURE__ */ jsxRuntime.jsx(
2794
+ reactNative.Pressable,
2795
+ {
2796
+ accessibilityRole: "button",
2797
+ accessibilityLabel: props.text,
2798
+ onPress: props.onOpen,
2799
+ style: {
2800
+ flexShrink: 1,
2801
+ paddingVertical: space2.sm,
2802
+ paddingLeft: space2.md
2803
+ },
2804
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: colors2.background, fontSize: font2.size.md }, children: props.text })
2805
+ }
2806
+ ),
2807
+ /* @__PURE__ */ jsxRuntime.jsx(
2808
+ reactNative.Pressable,
2809
+ {
2810
+ accessibilityRole: "button",
2811
+ accessibilityLabel: props.closeLabel,
2812
+ onPress: props.onClose,
2813
+ style: {
2814
+ minWidth: 44,
2815
+ minHeight: 44,
2816
+ alignItems: "center",
2817
+ justifyContent: "center"
2818
+ },
2819
+ testID: "vinaverse-launcher-close",
2820
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: colors2.background, fontSize: font2.size.md }, children: "\xD7" })
2821
+ }
2822
+ )
2823
+ ]
2824
+ }
2825
+ );
2826
+ }
2827
+ function LauncherButton(props) {
2828
+ const { colors: colors2, font: font2 } = useTheme();
2829
+ const { Mark, ring } = props;
2830
+ return /* @__PURE__ */ jsxRuntime.jsx(
2831
+ reactNative.Pressable,
2832
+ {
2833
+ accessibilityRole: "button",
2834
+ accessibilityLabel: props.label,
2835
+ onPress: props.onPress,
2836
+ style: ({ pressed }) => ({
2837
+ width: LAUNCHER_SIZE,
2838
+ height: LAUNCHER_SIZE,
2839
+ borderRadius: LAUNCHER_SIZE / 2,
2840
+ alignItems: "center",
2841
+ justifyContent: "center",
2842
+ backgroundColor: colors2.primary,
2843
+ opacity: pressed ? 0.85 : 1,
2844
+ ...ring ? { borderWidth: ring.width, borderColor: ring.color } : {},
2845
+ ...props.shadow ? {
2846
+ elevation: 6,
2847
+ shadowColor: "#000",
2848
+ shadowOpacity: 0.2,
2849
+ shadowRadius: 8,
2850
+ shadowOffset: { width: 0, height: 3 }
2851
+ } : {}
2852
+ }),
2853
+ testID: "vinaverse-launcher-button",
2854
+ children: Mark ? /* @__PURE__ */ jsxRuntime.jsx(Mark, { size: 34, color: colors2.onPrimary }) : /* @__PURE__ */ jsxRuntime.jsx(
2855
+ reactNative.Text,
2856
+ {
2857
+ style: {
2858
+ color: colors2.onPrimary,
2859
+ fontSize: font2.size.lg,
2860
+ fontWeight: "600"
2861
+ },
2862
+ children: (props.name.trim()[0] ?? "?").toUpperCase()
2863
+ }
2864
+ )
2865
+ }
2866
+ );
2867
+ }
2868
+ function AskAboutRow(props) {
2869
+ return /* @__PURE__ */ jsxRuntime.jsx(react$1.CopyProvider, { locale: props.locale, copy: props.copy, children: /* @__PURE__ */ jsxRuntime.jsx(ThemeProvider, { theme: props.theme, colorScheme: props.colorScheme, children: /* @__PURE__ */ jsxRuntime.jsx(Row3, { ...props }) }) });
2870
+ }
2871
+ function Row3(props) {
2872
+ const { t } = react$1.useCopy();
2873
+ const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
2874
+ const name = agent.useAgentName(t("agentFallbackName"));
2875
+ const { available } = agent.useAgentFeatures();
2876
+ const [shows, setShows] = react.useState(null);
2877
+ if (shows === null && available) setShows(true);
2878
+ if (!shows) return null;
2879
+ const { Mark } = props;
2880
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2881
+ reactNative.Pressable,
2882
+ {
2883
+ accessibilityRole: "button",
2884
+ accessibilityLabel: props.body ? `${props.title}. ${props.body}` : props.title,
2885
+ onPress: props.onPress,
2886
+ style: ({ pressed }) => ({
2887
+ flexDirection: "row",
2888
+ alignItems: "center",
2889
+ gap: space2.md,
2890
+ padding: space2.md,
2891
+ borderRadius: radius2.lg,
2892
+ backgroundColor: colors2.surface,
2893
+ opacity: pressed ? 0.85 : 1
2894
+ }),
2895
+ testID: "vinaverse-ask-about",
2896
+ children: [
2897
+ /* @__PURE__ */ jsxRuntime.jsx(
2898
+ reactNative.View,
2899
+ {
2900
+ style: {
2901
+ width: 36,
2902
+ height: 36,
2903
+ borderRadius: 18,
2904
+ alignItems: "center",
2905
+ justifyContent: "center",
2906
+ backgroundColor: colors2.primary
2907
+ },
2908
+ children: Mark ? /* @__PURE__ */ jsxRuntime.jsx(Mark, { size: 22, color: colors2.onPrimary }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: colors2.onPrimary, fontWeight: "600" }, children: (name.trim()[0] ?? "?").toUpperCase() })
2909
+ }
2910
+ ),
2911
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1, gap: space2.xxs }, children: [
2912
+ /* @__PURE__ */ jsxRuntime.jsx(
2913
+ reactNative.Text,
2914
+ {
2915
+ style: {
2916
+ color: colors2.text,
2917
+ fontSize: font2.size.md,
2918
+ fontWeight: "600"
2919
+ },
2920
+ children: props.title
2921
+ }
2922
+ ),
2923
+ props.body ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: colors2.textMuted, fontSize: font2.size.sm }, children: props.body }) : null
2924
+ ] }),
2925
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: colors2.textMuted, fontSize: font2.size.lg }, children: "\u203A" })
2926
+ ]
2927
+ }
2928
+ );
2929
+ }
2255
2930
 
2931
+ Object.defineProperty(exports, "AgentRuntimeProvider", {
2932
+ enumerable: true,
2933
+ get: function () { return agent.AgentRuntimeProvider; }
2934
+ });
2935
+ Object.defineProperty(exports, "greetedKey", {
2936
+ enumerable: true,
2937
+ get: function () { return agent.greetedKey; }
2938
+ });
2939
+ Object.defineProperty(exports, "useAgentConversation", {
2940
+ enumerable: true,
2941
+ get: function () { return agent.useAgentConversation; }
2942
+ });
2943
+ Object.defineProperty(exports, "useAgentFeatures", {
2944
+ enumerable: true,
2945
+ get: function () { return agent.useAgentFeatures; }
2946
+ });
2947
+ Object.defineProperty(exports, "useAgentLauncher", {
2948
+ enumerable: true,
2949
+ get: function () { return agent.useAgentLauncher; }
2950
+ });
2951
+ Object.defineProperty(exports, "useAgentName", {
2952
+ enumerable: true,
2953
+ get: function () { return agent.useAgentName; }
2954
+ });
2955
+ Object.defineProperty(exports, "useAgentProfile", {
2956
+ enumerable: true,
2957
+ get: function () { return agent.useAgentProfile; }
2958
+ });
2959
+ Object.defineProperty(exports, "useAgentRuntime", {
2960
+ enumerable: true,
2961
+ get: function () { return agent.useAgentRuntime; }
2962
+ });
2963
+ Object.defineProperty(exports, "useAgentStatus", {
2964
+ enumerable: true,
2965
+ get: function () { return agent.useAgentStatus; }
2966
+ });
2256
2967
  exports.ActivityLine = ActivityLine;
2968
+ exports.AgentChat = AgentChat;
2969
+ exports.AgentFooter = AgentFooter;
2970
+ exports.AgentLauncher = AgentLauncher;
2971
+ exports.AskAboutRow = AskAboutRow;
2257
2972
  exports.Avatar = Avatar;
2258
2973
  exports.Button = Button;
2259
2974
  exports.CardActions = CardActions;
@@ -2265,12 +2980,19 @@ exports.ChatSkeleton = ChatSkeleton;
2265
2980
  exports.Composer = Composer;
2266
2981
  exports.DefaultBubble = DefaultBubble;
2267
2982
  exports.DefaultComposer = DefaultComposer;
2983
+ exports.DefaultEnded = DefaultEnded;
2268
2984
  exports.DefaultHeader = DefaultHeader;
2269
2985
  exports.DefaultTurnFailed = DefaultTurnFailed;
2270
2986
  exports.DefaultUnavailable = DefaultUnavailable;
2987
+ exports.DefaultUnavailableScreen = DefaultUnavailableScreen;
2988
+ exports.DefaultUnreachable = DefaultUnreachable;
2271
2989
  exports.DisclosureBar = DisclosureBar;
2272
2990
  exports.FailureActionButton = FailureActionButton;
2273
2991
  exports.InfoCard = InfoCard;
2992
+ exports.LAUNCHER_SIZE = LAUNCHER_SIZE;
2993
+ exports.LauncherButton = LauncherButton;
2994
+ exports.LauncherEntrance = LauncherEntrance;
2995
+ exports.LauncherGreeting = LauncherGreeting;
2274
2996
  exports.LinksCard = LinksCard;
2275
2997
  exports.ListingCard = ListingCard;
2276
2998
  exports.MessageBubble = MessageBubble;
@@ -2287,6 +3009,7 @@ exports.TimeSlotsCard = TimeSlotsCard;
2287
3009
  exports.VinaverseChat = VinaverseChat;
2288
3010
  exports.VinaverseChatProvider = VinaverseChatProvider;
2289
3011
  exports.Welcome = Welcome;
3012
+ exports.allowedImage = allowedImage;
2290
3013
  exports.announce = announce;
2291
3014
  exports.answerText = answerText;
2292
3015
  exports.carousels = carousels;
@@ -2295,9 +3018,11 @@ exports.defaultCard = defaultCard;
2295
3018
  exports.fieldText = fieldText;
2296
3019
  exports.itemType = itemType;
2297
3020
  exports.monogram = monogram;
3021
+ exports.newestWholeReply = newestWholeReply;
2298
3022
  exports.ratingText = ratingText;
2299
3023
  exports.themeProblems = themeProblems;
2300
3024
  exports.useChat = useChat;
3025
+ exports.useKeyboardShown = useKeyboardShown;
2301
3026
  exports.useNativeLifecycle = useNativeLifecycle;
2302
3027
  exports.useReducedMotion = useReducedMotion;
2303
3028
  exports.useTheme = useTheme;