@vinaverse/react-native 0.1.0-alpha.11 → 0.1.0-alpha.13

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.
Files changed (56) hide show
  1. package/README.md +16 -3
  2. package/dist/ask.cjs +10 -5
  3. package/dist/ask.d.cts +35 -5
  4. package/dist/ask.d.ts +35 -5
  5. package/dist/ask.js +3 -2
  6. package/dist/chat.cjs +69 -69
  7. package/dist/chat.d.cts +44 -8
  8. package/dist/chat.d.ts +44 -8
  9. package/dist/chat.js +3 -3
  10. package/dist/{chunk-XAG2P447.cjs → chunk-272ACDLI.cjs} +248 -104
  11. package/dist/chunk-272ACDLI.cjs.map +1 -0
  12. package/dist/{chunk-EFEXTVIT.js → chunk-35A77CNF.js} +11 -3
  13. package/dist/chunk-35A77CNF.js.map +1 -0
  14. package/dist/{chunk-NMKJ7LIJ.cjs → chunk-35T5QPMT.cjs} +31 -7
  15. package/dist/chunk-35T5QPMT.cjs.map +1 -0
  16. package/dist/{chunk-7KPCO443.js → chunk-GFD4T3IA.js} +28 -4
  17. package/dist/chunk-GFD4T3IA.js.map +1 -0
  18. package/dist/{chunk-2J2LIJLW.js → chunk-MCVW6TZD.js} +113 -33
  19. package/dist/chunk-MCVW6TZD.js.map +1 -0
  20. package/dist/{chunk-KSHDF3MG.js → chunk-QSMMC4RZ.js} +216 -74
  21. package/dist/chunk-QSMMC4RZ.js.map +1 -0
  22. package/dist/{chunk-POJP52GE.js → chunk-TY4TISCR.js} +4 -4
  23. package/dist/chunk-TY4TISCR.js.map +1 -0
  24. package/dist/{chunk-6QYDPGRS.cjs → chunk-UMCOOQT6.cjs} +11 -3
  25. package/dist/chunk-UMCOOQT6.cjs.map +1 -0
  26. package/dist/{chunk-MG32E4JI.cjs → chunk-VWAAUCE3.cjs} +11 -11
  27. package/dist/chunk-VWAAUCE3.cjs.map +1 -0
  28. package/dist/{chunk-4PXQ7DEY.cjs → chunk-YVCMKSJG.cjs} +140 -60
  29. package/dist/chunk-YVCMKSJG.cjs.map +1 -0
  30. package/dist/{context-gHCLFsJh.d.ts → context-B4ctXjm5.d.cts} +22 -1
  31. package/dist/{context-gHCLFsJh.d.cts → context-B4ctXjm5.d.ts} +22 -1
  32. package/dist/history.cjs +52 -48
  33. package/dist/history.cjs.map +1 -1
  34. package/dist/history.d.cts +12 -3
  35. package/dist/history.d.ts +12 -3
  36. package/dist/history.js +13 -9
  37. package/dist/history.js.map +1 -1
  38. package/dist/index.cjs +83 -78
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +3 -3
  41. package/dist/index.d.ts +3 -3
  42. package/dist/index.js +6 -5
  43. package/dist/index.js.map +1 -1
  44. package/dist/launcher.cjs +10 -10
  45. package/dist/launcher.js +2 -2
  46. package/package.json +7 -7
  47. package/dist/chunk-2J2LIJLW.js.map +0 -1
  48. package/dist/chunk-4PXQ7DEY.cjs.map +0 -1
  49. package/dist/chunk-6QYDPGRS.cjs.map +0 -1
  50. package/dist/chunk-7KPCO443.js.map +0 -1
  51. package/dist/chunk-EFEXTVIT.js.map +0 -1
  52. package/dist/chunk-KSHDF3MG.js.map +0 -1
  53. package/dist/chunk-MG32E4JI.cjs.map +0 -1
  54. package/dist/chunk-NMKJ7LIJ.cjs.map +0 -1
  55. package/dist/chunk-POJP52GE.js.map +0 -1
  56. package/dist/chunk-XAG2P447.cjs.map +0 -1
@@ -1,12 +1,13 @@
1
- import { useTheme, useReducedMotion, announce } from './chunk-EFEXTVIT.js';
2
- import { useContext, memo, useState, useMemo, useRef, useCallback, useEffect, createContext } from 'react';
1
+ import { useTheme, useReducedMotion, announce } from './chunk-35A77CNF.js';
2
+ import { useContext, memo, useState, useMemo, useEffect, useRef, useCallback, createContext } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { Pressable, ActivityIndicator, Text, View, Image, Linking, TextInput, useWindowDimensions } from 'react-native';
5
- import { useCopy, formatMoney, formatTimeRange, formatDayTime, useConversationSelector, formatDate, useStreamingText } from '@vinaverse/react';
5
+ import { useCopy, formatMoney, formatTimeRange, formatDayTime, useConversationSelector, formatDate, useStreamingText, formatDateTime } from '@vinaverse/react';
6
6
  import { answerProblem, codePoints } from '@vinaverse/client/conversation';
7
7
  import { readCard } from '@vinaverse/client/agent';
8
8
  import { FlashList } from '@shopify/flash-list';
9
9
  import { contrastRatio, colors, size } from '@vinaverse/tokens';
10
+ import { isDisclosure } from '@vinaverse/tokens/copy';
10
11
 
11
12
  var ChatContext = /* @__PURE__ */ createContext(
12
13
  null
@@ -131,7 +132,7 @@ function Body(props) {
131
132
  Text,
132
133
  {
133
134
  style: {
134
- color: props.danger ? colors.danger : props.muted ? colors.textMuted : colors.text,
135
+ color: props.danger ? colors.danger : props.muted ? colors.textSecondary : colors.text,
135
136
  fontSize: props.muted ? font.size.sm : font.size.md,
136
137
  lineHeight: (props.muted ? font.size.sm : font.size.md) * font.lineHeight,
137
138
  ...font.family ? { fontFamily: font.family } : {}
@@ -206,7 +207,7 @@ function InfoCard(props) {
206
207
  /* @__PURE__ */ jsxs(View, { style: { flex: 1, gap: space.xxs }, children: [
207
208
  /* @__PURE__ */ jsx(Title, { numberOfLines: 2, children: card.title }),
208
209
  card.subtitle ? /* @__PURE__ */ jsx(Body, { muted: true, children: card.subtitle }) : null,
209
- /* @__PURE__ */ jsx(Text, { style: { color: colors.textMuted, fontSize: font.size.sm }, children: [
210
+ /* @__PURE__ */ jsx(Text, { style: { color: colors.textSecondary, fontSize: font.size.sm }, children: [
210
211
  rating && `\u2605 ${rating}${card.rating ? ` (${card.rating.count})` : ""}`,
211
212
  price,
212
213
  when
@@ -393,7 +394,7 @@ function ListingCard(props) {
393
394
  Text,
394
395
  {
395
396
  style: {
396
- color: colors.textMuted,
397
+ color: colors.textSecondary,
397
398
  fontSize: font.size.xl,
398
399
  fontWeight: "600"
399
400
  },
@@ -565,7 +566,7 @@ function ProposalCard(props) {
565
566
  Text,
566
567
  {
567
568
  style: {
568
- color: colors.textMuted,
569
+ color: colors.textSecondary,
569
570
  fontSize: font.size.sm,
570
571
  minWidth: 72
571
572
  },
@@ -584,7 +585,7 @@ function ProposalCard(props) {
584
585
  Text,
585
586
  {
586
587
  style: {
587
- color: props.card.outcome === "confirmed" || proposal.status === "confirmed" ? colors.success : colors.textMuted,
588
+ color: props.card.outcome === "confirmed" || proposal.status === "confirmed" ? colors.success : colors.textSecondary,
588
589
  fontSize: font.size.sm,
589
590
  fontWeight: "600"
590
591
  },
@@ -672,7 +673,7 @@ function QuestionCard(props) {
672
673
  restored: props.restored,
673
674
  testID: `vinaverse-question-${held.id}`,
674
675
  children: [
675
- question.progress ? /* @__PURE__ */ jsx(Text, { style: { color: colors.textMuted, fontSize: font.size.xs }, children: t("questionStep", {
676
+ question.progress ? /* @__PURE__ */ jsx(Text, { style: { color: colors.textSecondary, fontSize: font.size.xs }, children: t("questionStep", {
676
677
  step: question.progress.step,
677
678
  total: question.progress.total
678
679
  }) }) : null,
@@ -830,7 +831,7 @@ function ChoiceAnswer(props) {
830
831
  const q = search.trim().toLowerCase();
831
832
  return list && q ? all.filter((o) => o.label.toLowerCase().includes(q)) : all;
832
833
  }, [question.options, search, list]);
833
- const shown = list ? options.slice(0, 50) : options;
834
+ const shown2 = list ? options.slice(0, 50) : options;
834
835
  const pick = (value) => {
835
836
  if (none && value === none.value) return onChange({ values: [value] });
836
837
  const rest = values.filter((v) => v !== none?.value);
@@ -863,7 +864,7 @@ function ChoiceAnswer(props) {
863
864
  }
864
865
  }
865
866
  ) : null,
866
- shown.map((o) => {
867
+ shown2.map((o) => {
867
868
  const image = question.kind === "image_choice" ? safeImage(o.image_url) : null;
868
869
  return /* @__PURE__ */ jsx(
869
870
  Button,
@@ -985,7 +986,7 @@ function CharactersLeft(props) {
985
986
  {
986
987
  accessibilityLiveRegion: left < 20 ? "polite" : "none",
987
988
  style: {
988
- color: left < 0 ? colors.danger : colors.textMuted,
989
+ color: left < 0 ? colors.danger : colors.textSecondary,
989
990
  fontSize: font.size.xs
990
991
  },
991
992
  children: t("questionCharactersLeft", { count: left })
@@ -1027,7 +1028,7 @@ function NumberAnswer(props) {
1027
1028
  const { colors, space, font } = useTheme();
1028
1029
  const value = props.value ?? range.min;
1029
1030
  const clamp = (n) => Math.min(range.max, Math.max(range.min, Number(n.toFixed(6))));
1030
- const shown = `${value}${props.question.number?.unit ? ` ${props.question.number.unit}` : ""}`;
1031
+ const shown2 = `${value}${props.question.number?.unit ? ` ${props.question.number.unit}` : ""}`;
1031
1032
  return /* @__PURE__ */ jsxs(
1032
1033
  View,
1033
1034
  {
@@ -1038,7 +1039,7 @@ function NumberAnswer(props) {
1038
1039
  min: range.min,
1039
1040
  max: range.max,
1040
1041
  now: value,
1041
- text: shown
1042
+ text: shown2
1042
1043
  },
1043
1044
  accessibilityActions: [{ name: "increment" }, { name: "decrement" }],
1044
1045
  testID: "vinaverse-question-number",
@@ -1066,7 +1067,7 @@ function NumberAnswer(props) {
1066
1067
  minWidth: 64,
1067
1068
  textAlign: "center"
1068
1069
  },
1069
- children: shown
1070
+ children: shown2
1070
1071
  }
1071
1072
  ),
1072
1073
  /* @__PURE__ */ jsx(
@@ -1100,8 +1101,8 @@ function ScaleAnswer(props) {
1100
1101
  n
1101
1102
  )) }),
1102
1103
  /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", justifyContent: "space-between" }, children: [
1103
- /* @__PURE__ */ jsx(Text, { style: { color: colors.textMuted, fontSize: font.size.xs }, children: scale.min_label }),
1104
- /* @__PURE__ */ jsx(Text, { style: { color: colors.textMuted, fontSize: font.size.xs }, children: scale.max_label })
1104
+ /* @__PURE__ */ jsx(Text, { style: { color: colors.textSecondary, fontSize: font.size.xs }, children: scale.min_label }),
1105
+ /* @__PURE__ */ jsx(Text, { style: { color: colors.textSecondary, fontSize: font.size.xs }, children: scale.max_label })
1105
1106
  ] })
1106
1107
  ] });
1107
1108
  }
@@ -1496,7 +1497,8 @@ var ChatHeader = /* @__PURE__ */ memo(function ChatHeader2() {
1496
1497
  return Header ? /* @__PURE__ */ jsx(Header, { ...props, Default: DefaultHeader }) : /* @__PURE__ */ jsx(DefaultHeader, { ...props });
1497
1498
  });
1498
1499
  var DisclosureBar = /* @__PURE__ */ memo(function DisclosureBar2() {
1499
- const { store } = useChat();
1500
+ const { store, disclosure: given } = useChat();
1501
+ const held = given && isDisclosure(given) ? given : void 0;
1500
1502
  const { t } = useCopy();
1501
1503
  const theme = useTheme();
1502
1504
  const { space, font } = theme;
@@ -1507,12 +1509,20 @@ var DisclosureBar = /* @__PURE__ */ memo(function DisclosureBar2() {
1507
1509
  theme.colors.disclosureBackground
1508
1510
  ) >= 4.5;
1509
1511
  const pair = legible ? theme.colors : colors(theme.scheme);
1510
- const text = disclosure ?? t("disclosureFallback");
1511
- return /* @__PURE__ */ jsx(
1512
+ const text = disclosure ?? held ?? t("disclosureFallback");
1513
+ const { rest, brand } = splitBrand(text);
1514
+ const centered = {
1515
+ color: pair.disclosureText,
1516
+ // never below the disclosure's minimum, whatever the tenant's type scale
1517
+ fontSize: Math.max(font.size.xs, size.disclosureFontMin),
1518
+ lineHeight: Math.max(font.size.xs, size.disclosureFontMin) * font.lineHeight,
1519
+ textAlign: "center"
1520
+ };
1521
+ return /* @__PURE__ */ jsxs(
1512
1522
  Pressable,
1513
1523
  {
1514
1524
  accessibilityRole: "text",
1515
- accessibilityLabel: text,
1525
+ accessibilityLabel: brand ? [rest, `${brand}.`].filter(Boolean).join(" ") : text,
1516
1526
  onPress: () => setOpen((o) => !o),
1517
1527
  testID: "vinaverse-disclosure",
1518
1528
  style: {
@@ -1520,22 +1530,41 @@ var DisclosureBar = /* @__PURE__ */ memo(function DisclosureBar2() {
1520
1530
  paddingHorizontal: space.lg,
1521
1531
  paddingVertical: space.xs
1522
1532
  },
1523
- children: /* @__PURE__ */ jsx(
1524
- Text,
1525
- {
1526
- numberOfLines: open ? void 0 : 2,
1527
- style: {
1528
- color: pair.disclosureText,
1529
- // never below the disclosure's minimum, whatever the tenant's type scale
1530
- fontSize: Math.max(font.size.xs, size.disclosureFontMin),
1531
- lineHeight: Math.max(font.size.xs, size.disclosureFontMin) * font.lineHeight
1532
- },
1533
- children: text
1534
- }
1535
- )
1533
+ children: [
1534
+ rest ? /* @__PURE__ */ jsx(
1535
+ Text,
1536
+ {
1537
+ numberOfLines: open ? void 0 : 2,
1538
+ style: held && !open ? [centered, { minHeight: 2 * centered.lineHeight }] : centered,
1539
+ children: rest
1540
+ }
1541
+ ) : null,
1542
+ brand ? (
1543
+ // Its own line, one unit that never breaks ("Powered by / Vinaverse"), the brand's name in emphasis.
1544
+ /* @__PURE__ */ jsxs(
1545
+ Text,
1546
+ {
1547
+ numberOfLines: 1,
1548
+ adjustsFontSizeToFit: true,
1549
+ minimumFontScale: size.disclosureFontMin / centered.fontSize,
1550
+ style: centered,
1551
+ testID: "vinaverse-powered-by",
1552
+ children: [
1553
+ nobreak(brand.slice(0, -9)),
1554
+ /* @__PURE__ */ jsx(Text, { style: { fontWeight: "600" }, children: "Vinaverse" })
1555
+ ]
1556
+ }
1557
+ )
1558
+ ) : null
1559
+ ]
1536
1560
  }
1537
1561
  );
1538
1562
  });
1563
+ var nobreak = (s) => s.replace(/ /g, "\xA0");
1564
+ function splitBrand(text) {
1565
+ const m = /^([\s\S]*?[.!?])?\s*([^.!?]*Vinaverse)[.!]?$/.exec(text.trim());
1566
+ return m ? { rest: m[1] ?? "", brand: m[2].trim() } : { rest: text.trim(), brand: "" };
1567
+ }
1539
1568
  var ChatSkeleton = /* @__PURE__ */ memo(function ChatSkeleton2() {
1540
1569
  const { colors, radius, space } = useTheme();
1541
1570
  const row = (width, end) => /* @__PURE__ */ jsx(
@@ -1621,10 +1650,11 @@ var StreamingBubble = /* @__PURE__ */ memo(
1621
1650
  );
1622
1651
  function Frame(props) {
1623
1652
  const { item, text } = props;
1624
- const { store, slots } = useChat();
1625
- const { t } = useCopy();
1653
+ const { store, slots, timeZone, messageTimes } = useChat();
1654
+ const { t, locale } = useCopy();
1626
1655
  const { colors, space, font } = useTheme();
1627
1656
  const author = useAuthor(item);
1657
+ const when = item.at && messageTimes !== null ? formatDateTime(item.at, locale, timeZone) : null;
1628
1658
  const mine = item.role === "person";
1629
1659
  const status = item.state === "sending" ? t("messageSending") : item.state === "held" ? t("messageHeld") : item.state === "not_sent" ? item.rejection?.message || t("messageNotSent") : item.state === "delivered" ? t("messageDelivered") : item.partial ? t("messageCutOff") : item.refusal && mine ? t("messageRefused") : null;
1630
1660
  const Bubble = slots.MessageBubble;
@@ -1633,7 +1663,7 @@ function Frame(props) {
1633
1663
  View,
1634
1664
  {
1635
1665
  accessible: true,
1636
- accessibilityLabel: [author, text, status].filter(Boolean).join(". "),
1666
+ accessibilityLabel: [author, text, status, when].filter(Boolean).join(". "),
1637
1667
  style: {
1638
1668
  alignSelf: mine ? "flex-end" : "flex-start",
1639
1669
  maxWidth: "85%",
@@ -1646,7 +1676,7 @@ function Frame(props) {
1646
1676
  Text,
1647
1677
  {
1648
1678
  style: {
1649
- color: item.role === "staff" ? colors.staffAccent : colors.textMuted,
1679
+ color: item.role === "staff" ? colors.staffAccent : colors.textSecondary,
1650
1680
  fontSize: font.size.xs,
1651
1681
  marginBottom: space.xxs
1652
1682
  },
@@ -1669,7 +1699,7 @@ function Frame(props) {
1669
1699
  Text,
1670
1700
  {
1671
1701
  style: {
1672
- color: item.state === "not_sent" ? colors.danger : colors.textMuted,
1702
+ color: item.state === "not_sent" ? colors.danger : colors.textSecondary,
1673
1703
  fontSize: font.size.xs
1674
1704
  },
1675
1705
  children: status
@@ -1827,6 +1857,34 @@ var Welcome = /* @__PURE__ */ memo(function Welcome2(props) {
1827
1857
  }
1828
1858
  );
1829
1859
  });
1860
+ function messageTimeLabel(at, locale, timeZone, now) {
1861
+ return Number.isNaN(Date.parse(at)) ? "" : formatDayTime(at, TODAY, locale, timeZone, now);
1862
+ }
1863
+ var TODAY = { today: "{time}", tomorrow: "" };
1864
+ var MessageTime = /* @__PURE__ */ memo(function MessageTime2(props) {
1865
+ const { slots, timeZone } = useChat();
1866
+ const { locale } = useCopy();
1867
+ const { colors, space, font } = useTheme();
1868
+ const label = messageTimeLabel(props.at, locale, timeZone);
1869
+ if (!label) return null;
1870
+ const Slot = slots.MessageTime;
1871
+ if (Slot) return /* @__PURE__ */ jsx(Slot, { at: props.at, label });
1872
+ return /* @__PURE__ */ jsx(
1873
+ Text,
1874
+ {
1875
+ accessibilityElementsHidden: true,
1876
+ importantForAccessibility: "no",
1877
+ testID: "vinaverse-message-time",
1878
+ style: {
1879
+ alignSelf: "center",
1880
+ color: colors.textSecondary,
1881
+ fontSize: font.size.xs,
1882
+ marginTop: space.md
1883
+ },
1884
+ children: label
1885
+ }
1886
+ );
1887
+ });
1830
1888
  var keyOf2 = (item) => item.key;
1831
1889
  function itemType(item) {
1832
1890
  if (item.type === "card") return `card:${item.card.kind}`;
@@ -1834,6 +1892,16 @@ function itemType(item) {
1834
1892
  return item.state === "streaming" ? "streaming" : `message:${item.role}`;
1835
1893
  return "system";
1836
1894
  }
1895
+ function timeOver(items, index, gap) {
1896
+ const item = items[index];
1897
+ if (gap === null || item?.type !== "message") return void 0;
1898
+ let before;
1899
+ for (let i = index - 1; i >= 0 && !before; i--) {
1900
+ const earlier = items[i];
1901
+ if (earlier?.type === "message") before = earlier.at;
1902
+ }
1903
+ return item.at && (!before || Date.parse(item.at) - Date.parse(before) > (gap ?? 10) * 6e4) ? item.at : void 0;
1904
+ }
1837
1905
  function authorOf(item) {
1838
1906
  return item?.type === "message" ? item.role : null;
1839
1907
  }
@@ -1870,30 +1938,79 @@ var Row = /* @__PURE__ */ memo(function Row2(props) {
1870
1938
  if (props.run && Group) return /* @__PURE__ */ jsx(Group, { items: props.run });
1871
1939
  if (item.type === "card") return /* @__PURE__ */ jsx(CardView, { item });
1872
1940
  if (item.type === "system") return /* @__PURE__ */ jsx(SystemRow, { item });
1873
- return item.state === "streaming" ? /* @__PURE__ */ jsx(StreamingBubble, { item, showAuthor: props.showAuthor }) : /* @__PURE__ */ jsx(MessageBubble, { item, showAuthor: props.showAuthor });
1941
+ const bubble = item.state === "streaming" ? /* @__PURE__ */ jsx(StreamingBubble, { item, showAuthor: props.showAuthor }) : /* @__PURE__ */ jsx(MessageBubble, { item, showAuthor: props.showAuthor });
1942
+ return props.time ? /* @__PURE__ */ jsxs(Fragment, { children: [
1943
+ /* @__PURE__ */ jsx(MessageTime, { at: props.time }),
1944
+ bubble
1945
+ ] }) : bubble;
1874
1946
  });
1947
+ var opened = /* @__PURE__ */ new WeakSet();
1948
+ var shown = /* @__PURE__ */ new WeakMap();
1949
+ var looked = /* @__PURE__ */ new WeakMap();
1875
1950
  var MessageList = /* @__PURE__ */ memo(function MessageList2() {
1876
- const { store, slots, starters, handlers, imagePolicy } = useChat();
1951
+ const {
1952
+ store,
1953
+ slots,
1954
+ starters,
1955
+ handlers,
1956
+ imagePolicy,
1957
+ welcomeChips,
1958
+ messageTimes,
1959
+ listHeader
1960
+ } = useChat();
1877
1961
  const { t } = useCopy();
1878
1962
  const { space } = useTheme();
1879
1963
  const reducedMotion = useReducedMotion();
1880
1964
  const items = useConversationSelector(store, (v) => v.items);
1881
- usePrefetchPhotos(items, handlers.current?.prefetchImage, imagePolicy?.hosts);
1882
- const opening = useConversationSelector(
1965
+ usePrefetchPhotos(
1966
+ store,
1967
+ items,
1968
+ handlers.current?.prefetchImage,
1969
+ imagePolicy?.hosts
1970
+ );
1971
+ const restoring = useConversationSelector(
1972
+ store,
1973
+ (v) => v.history === "restoring"
1974
+ );
1975
+ const fetching = useConversationSelector(
1976
+ store,
1977
+ (v) => v.status === "starting" || v.status === "connecting" || v.status === "reconnecting"
1978
+ );
1979
+ const live = useConversationSelector(
1980
+ store,
1981
+ (v) => v.status === "ready" || v.status === "sse"
1982
+ );
1983
+ const over = useConversationSelector(
1883
1984
  store,
1884
- (v) => v.status === "starting" || v.status === "connecting"
1985
+ (v) => v.status === "ended" || v.status === "closed"
1885
1986
  );
1987
+ const [settled, setSettled] = useState(null);
1988
+ useEffect(() => setSettled(store), [store]);
1989
+ const waiting = restoring || fetching || !live && !over && settled !== store;
1990
+ if (live) opened.add(store);
1886
1991
  const agentName = useConversationSelector(store, (v) => v.agent.name);
1887
1992
  const served = useConversationSelector(store, (v) => v.welcome);
1888
- const welcome = useMemo(
1889
- () => served ?? (starters?.length ? {
1993
+ const welcome = useMemo(() => {
1994
+ const w = restoring ? null : served && opened.has(store) ? served : waiting ? null : served ?? {
1890
1995
  text: t("welcomeFallback", {
1891
1996
  agent: agentName ?? t("agentFallbackName")
1892
1997
  }),
1893
- chips: [...starters]
1894
- } : null),
1895
- [served, starters, t, agentName]
1896
- );
1998
+ chips: starters ? [...starters] : []
1999
+ };
2000
+ return w && welcomeChips === false ? { ...w, chips: [] } : w;
2001
+ }, [
2002
+ store,
2003
+ live,
2004
+ restoring,
2005
+ served,
2006
+ waiting,
2007
+ starters,
2008
+ t,
2009
+ agentName,
2010
+ welcomeChips
2011
+ ]);
2012
+ if (welcome) shown.set(store, welcome);
2013
+ const said = welcome ?? shown.get(store) ?? null;
1897
2014
  useAnnounceReplies(items);
1898
2015
  const Group = slots.CardGroup;
1899
2016
  const runsRef = useRef(/* @__PURE__ */ new Map());
@@ -1906,6 +2023,8 @@ var MessageList = /* @__PURE__ */ memo(function MessageList2() {
1906
2023
  itemsRef.current = items;
1907
2024
  const groupingRef = useRef(grouping);
1908
2025
  groupingRef.current = grouping;
2026
+ const gapRef = useRef(messageTimes);
2027
+ gapRef.current = messageTimes;
1909
2028
  const renderItem = useCallback(
1910
2029
  ({ item, index }) => /* @__PURE__ */ jsx(
1911
2030
  Row,
@@ -1913,7 +2032,8 @@ var MessageList = /* @__PURE__ */ memo(function MessageList2() {
1913
2032
  item,
1914
2033
  showAuthor: authorOf(item) !== authorOf(itemsRef.current[index - 1]),
1915
2034
  run: groupingRef.current?.runs.get(item.key),
1916
- inside: groupingRef.current?.inside.has(item.key) === true
2035
+ inside: groupingRef.current?.inside.has(item.key) === true,
2036
+ time: timeOver(itemsRef.current, index, gapRef.current)
1917
2037
  }
1918
2038
  ),
1919
2039
  []
@@ -1921,20 +2041,23 @@ var MessageList = /* @__PURE__ */ memo(function MessageList2() {
1921
2041
  const onChip = useCallback(
1922
2042
  (message) => {
1923
2043
  store.send(message);
1924
- const chip = welcome?.chips.find((c) => c.message === message);
2044
+ const chip = said?.chips.find((c) => c.message === message);
1925
2045
  if (chip) handlers.current?.onChip?.(chip);
1926
2046
  },
1927
- [store, welcome, handlers]
2047
+ [store, said, handlers]
1928
2048
  );
1929
2049
  const Empty = slots.EmptyState;
1930
- const empty = opening ? /* @__PURE__ */ jsx(ChatSkeleton, {}) : Empty ? /* @__PURE__ */ jsx(
1931
- Empty,
1932
- {
1933
- agentName: agentName ?? t("agentFallbackName"),
1934
- welcome,
1935
- onChip
1936
- }
1937
- ) : welcome ? /* @__PURE__ */ jsx(Welcome, { welcome, onChip }) : null;
2050
+ const empty = useMemo(
2051
+ () => !said ? /* @__PURE__ */ jsx(ChatSkeleton, {}) : Empty ? /* @__PURE__ */ jsx(
2052
+ Empty,
2053
+ {
2054
+ agentName: agentName ?? t("agentFallbackName"),
2055
+ welcome: said,
2056
+ onChip
2057
+ }
2058
+ ) : /* @__PURE__ */ jsx(Welcome, { welcome: said, onChip }),
2059
+ [said, Empty, agentName, t, onChip]
2060
+ );
1938
2061
  const props = useMemo(
1939
2062
  () => ({
1940
2063
  data: items,
@@ -1946,30 +2069,49 @@ var MessageList = /* @__PURE__ */ memo(function MessageList2() {
1946
2069
  autoscrollToBottomThreshold: 0.2,
1947
2070
  animateAutoScrollToBottom: !reducedMotion
1948
2071
  },
2072
+ ListHeaderComponent: listHeader ?? null,
1949
2073
  ListFooterComponent: /* @__PURE__ */ jsx(ActivityLine, {}),
1950
2074
  ListEmptyComponent: empty,
1951
2075
  keyboardShouldPersistTaps: "handled",
1952
- contentContainerStyle: { paddingVertical: space.sm }
2076
+ contentContainerStyle: { paddingVertical: space.sm },
2077
+ extraData: messageTimes
1953
2078
  }),
1954
- [items, renderItem, reducedMotion, empty, space.sm]
2079
+ [
2080
+ items,
2081
+ renderItem,
2082
+ reducedMotion,
2083
+ empty,
2084
+ space.sm,
2085
+ messageTimes,
2086
+ listHeader
2087
+ ]
1955
2088
  );
1956
2089
  const List = slots.ListComponent;
1957
- return List ? /* @__PURE__ */ jsx(List, { ...props }) : /* @__PURE__ */ jsx(FlashList, { ...props });
2090
+ return useMemo(
2091
+ () => List ? /* @__PURE__ */ jsx(List, { ...props }) : /* @__PURE__ */ jsx(FlashList, { ...props }),
2092
+ [List, props]
2093
+ );
1958
2094
  });
1959
- function usePrefetchPhotos(items, prefetch, hosts) {
1960
- const done = useRef(/* @__PURE__ */ new Set());
2095
+ var PREFETCH_ON_OPENING = 3;
2096
+ function usePrefetchPhotos(store, items, prefetch, hosts) {
1961
2097
  useEffect(() => {
1962
2098
  if (!prefetch) return;
1963
- for (const item of items) {
1964
- if (item.type !== "card") continue;
2099
+ const seen = looked.get(store) ?? /* @__PURE__ */ new Set();
2100
+ looked.set(store, seen);
2101
+ let left = seen.size ? Number.POSITIVE_INFINITY : PREFETCH_ON_OPENING;
2102
+ for (let i = items.length - 1; i >= 0 && left > 0; i--) {
2103
+ const item = items[i];
2104
+ if (item?.type !== "card") continue;
2105
+ if (seen.has(item.key)) break;
2106
+ seen.add(item.key);
1965
2107
  const card = item.card.card;
1966
2108
  const url = allowedImage(card.images?.[0]?.url ?? card.image_url, hosts);
1967
- if (url && !done.current.has(url)) {
1968
- done.current.add(url);
2109
+ if (url) {
1969
2110
  prefetch(url);
2111
+ left--;
1970
2112
  }
1971
2113
  }
1972
- }, [items, prefetch, hosts]);
2114
+ }, [store, items, prefetch, hosts]);
1973
2115
  }
1974
2116
  function useAnnounceReplies(items) {
1975
2117
  const seen = useRef(void 0);
@@ -1991,6 +2133,6 @@ function useAnnounceReplies(items) {
1991
2133
  }, [items, t, store, handlers]);
1992
2134
  }
1993
2135
 
1994
- export { ActivityLine, Avatar, Button, CardActions, CardCarousel, CardFrame, CardView, ChatContextProvider, ChatHeader, ChatSkeleton, DefaultBubble, DefaultHeader, DefaultTurnFailed, DisclosureBar, FailureActionButton, FailureSlot, InfoCard, LinksCard, ListingCard, MessageBubble, MessageList, NextStepCard, ProposalCard, QuestionCard, Row, StreamingBubble, SystemRow, TimeSlotsCard, Welcome, allowedImage, answerText, carousels, defaultCard, fieldText, itemType, monogram, ratingText, useChat };
1995
- //# sourceMappingURL=chunk-KSHDF3MG.js.map
1996
- //# sourceMappingURL=chunk-KSHDF3MG.js.map
2136
+ export { ActivityLine, Avatar, Body, Button, CardActions, CardCarousel, CardFrame, CardView, ChatContextProvider, ChatHeader, ChatSkeleton, DefaultBubble, DefaultHeader, DefaultTurnFailed, DisclosureBar, FailureActionButton, FailureSlot, InfoCard, LinksCard, ListingCard, MessageBubble, MessageList, NextStepCard, ProposalCard, QuestionCard, Row, StreamingBubble, SystemRow, TimeSlotsCard, Title, Welcome, allowedImage, answerText, carousels, defaultCard, fieldText, itemType, monogram, ratingText, useChat };
2137
+ //# sourceMappingURL=chunk-QSMMC4RZ.js.map
2138
+ //# sourceMappingURL=chunk-QSMMC4RZ.js.map