@vinaverse/react-native 0.1.0-alpha.2 → 0.1.0-alpha.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/README.md +57 -2
- package/dist/index.cjs +457 -98
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +143 -47
- package/dist/index.d.ts +143 -47
- package/dist/index.js +448 -100
- package/dist/index.js.map +1 -1
- package/package.json +37 -5
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FlashList } from '@shopify/flash-list';
|
|
2
|
+
import { useCopy, useConversationSelector, useStreamingText, formatMoney, formatTimeRange, formatDate, bannedWordsIn, CopyProvider } from '@vinaverse/react';
|
|
3
|
+
import { Pressable, ActivityIndicator, Text, View, Image, TextInput, AccessibilityInfo, Linking, useColorScheme, useWindowDimensions, AppState, Platform, KeyboardAvoidingView } from 'react-native';
|
|
2
4
|
import { createContext, memo, useCallback, useState, useRef, useMemo, useEffect, useContext } from 'react';
|
|
3
5
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
6
|
import { contrastRatio, colors, size, motion, radius, space, font, contrastProblems } from '@vinaverse/tokens';
|
|
5
|
-
import { useCopy, useConversationSelector, useStreamingText, formatTimeRange, formatMoney, formatDate, bannedWordsIn, CopyProvider } from '@vinaverse/react';
|
|
6
7
|
import { codePoints, MAX_TEXT, answerProblem } from '@vinaverse/client/conversation';
|
|
7
|
-
import { FlashList } from '@shopify/flash-list';
|
|
8
8
|
|
|
9
|
-
// src/cards/
|
|
9
|
+
// src/cards/CardCarousel.tsx
|
|
10
10
|
var ChatContext = createContext(null);
|
|
11
11
|
function ChatContextProvider(props) {
|
|
12
12
|
return /* @__PURE__ */ jsx(ChatContext.Provider, { value: props.value, children: props.children });
|
|
@@ -55,6 +55,61 @@ function ThemeProvider(props) {
|
|
|
55
55
|
function useTheme() {
|
|
56
56
|
return useContext(ThemeContext);
|
|
57
57
|
}
|
|
58
|
+
var Button = memo(function Button2(props) {
|
|
59
|
+
const { colors: colors2, radius: radius2, space: space2, size: size3, font: font2 } = useTheme();
|
|
60
|
+
const variant = props.variant ?? "secondary";
|
|
61
|
+
const inactive = props.disabled === true || props.busy === true;
|
|
62
|
+
const filled = variant === "primary" || props.selected === true;
|
|
63
|
+
const background = filled ? colors2.primary : variant === "secondary" ? colors2.surface : "transparent";
|
|
64
|
+
const foreground = filled ? colors2.onPrimary : colors2.primary;
|
|
65
|
+
const checkable = props.role === "radio" || props.role === "checkbox";
|
|
66
|
+
return /* @__PURE__ */ jsxs(
|
|
67
|
+
Pressable,
|
|
68
|
+
{
|
|
69
|
+
accessibilityRole: props.role ?? "button",
|
|
70
|
+
accessibilityLabel: props.label,
|
|
71
|
+
...props.hint ? { accessibilityHint: props.hint } : {},
|
|
72
|
+
accessibilityState: {
|
|
73
|
+
disabled: inactive,
|
|
74
|
+
busy: props.busy === true,
|
|
75
|
+
...checkable ? props.role === "radio" ? { selected: props.selected === true } : { checked: props.selected === true } : {}
|
|
76
|
+
},
|
|
77
|
+
disabled: inactive,
|
|
78
|
+
onPress: props.onPress,
|
|
79
|
+
...props.testID ? { testID: props.testID } : {},
|
|
80
|
+
style: ({ pressed }) => ({
|
|
81
|
+
minHeight: size3.touchTarget,
|
|
82
|
+
minWidth: size3.touchTarget,
|
|
83
|
+
paddingHorizontal: space2.md,
|
|
84
|
+
paddingVertical: space2.sm,
|
|
85
|
+
borderRadius: radius2.md,
|
|
86
|
+
borderWidth: variant === "text" ? 0 : 1,
|
|
87
|
+
borderColor: filled ? colors2.primary : colors2.border,
|
|
88
|
+
backgroundColor: background,
|
|
89
|
+
opacity: inactive ? 0.55 : pressed ? 0.8 : 1,
|
|
90
|
+
alignItems: "center",
|
|
91
|
+
justifyContent: "center",
|
|
92
|
+
flexDirection: "row",
|
|
93
|
+
gap: space2.xs
|
|
94
|
+
}),
|
|
95
|
+
children: [
|
|
96
|
+
props.busy ? /* @__PURE__ */ jsx(ActivityIndicator, { color: foreground }) : null,
|
|
97
|
+
props.children ?? /* @__PURE__ */ jsx(
|
|
98
|
+
Text,
|
|
99
|
+
{
|
|
100
|
+
style: {
|
|
101
|
+
color: foreground,
|
|
102
|
+
fontSize: font2.size.md,
|
|
103
|
+
fontWeight: "500",
|
|
104
|
+
...font2.family ? { fontFamily: font2.family } : {}
|
|
105
|
+
},
|
|
106
|
+
children: props.label
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
});
|
|
58
113
|
function cardOf(held) {
|
|
59
114
|
return held.card;
|
|
60
115
|
}
|
|
@@ -123,64 +178,20 @@ function useLink(link, held) {
|
|
|
123
178
|
if (!link || !store.checkLink(link)) return null;
|
|
124
179
|
return () => handlers.current?.onOpenLink?.(link, held);
|
|
125
180
|
}
|
|
181
|
+
function ratingText(valuePpm, locale) {
|
|
182
|
+
const value = valuePpm / 1e6;
|
|
183
|
+
try {
|
|
184
|
+
return new Intl.NumberFormat(locale, {
|
|
185
|
+
minimumFractionDigits: 1,
|
|
186
|
+
maximumFractionDigits: 1
|
|
187
|
+
}).format(value);
|
|
188
|
+
} catch {
|
|
189
|
+
return value.toFixed(1);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
126
192
|
function safeImage(url) {
|
|
127
193
|
return url && /^https:\/\//i.test(url) ? url : null;
|
|
128
194
|
}
|
|
129
|
-
var Button = memo(function Button2(props) {
|
|
130
|
-
const { colors: colors2, radius: radius2, space: space2, size: size3, font: font2 } = useTheme();
|
|
131
|
-
const variant = props.variant ?? "secondary";
|
|
132
|
-
const inactive = props.disabled === true || props.busy === true;
|
|
133
|
-
const filled = variant === "primary" || props.selected === true;
|
|
134
|
-
const background = filled ? colors2.primary : variant === "secondary" ? colors2.surface : "transparent";
|
|
135
|
-
const foreground = filled ? colors2.onPrimary : colors2.primary;
|
|
136
|
-
const checkable = props.role === "radio" || props.role === "checkbox";
|
|
137
|
-
return /* @__PURE__ */ jsxs(
|
|
138
|
-
Pressable,
|
|
139
|
-
{
|
|
140
|
-
accessibilityRole: props.role ?? "button",
|
|
141
|
-
accessibilityLabel: props.label,
|
|
142
|
-
...props.hint ? { accessibilityHint: props.hint } : {},
|
|
143
|
-
accessibilityState: {
|
|
144
|
-
disabled: inactive,
|
|
145
|
-
busy: props.busy === true,
|
|
146
|
-
...checkable ? props.role === "radio" ? { selected: props.selected === true } : { checked: props.selected === true } : {}
|
|
147
|
-
},
|
|
148
|
-
disabled: inactive,
|
|
149
|
-
onPress: props.onPress,
|
|
150
|
-
...props.testID ? { testID: props.testID } : {},
|
|
151
|
-
style: ({ pressed }) => ({
|
|
152
|
-
minHeight: size3.touchTarget,
|
|
153
|
-
minWidth: size3.touchTarget,
|
|
154
|
-
paddingHorizontal: space2.md,
|
|
155
|
-
paddingVertical: space2.sm,
|
|
156
|
-
borderRadius: radius2.md,
|
|
157
|
-
borderWidth: variant === "text" ? 0 : 1,
|
|
158
|
-
borderColor: filled ? colors2.primary : colors2.border,
|
|
159
|
-
backgroundColor: background,
|
|
160
|
-
opacity: inactive ? 0.55 : pressed ? 0.8 : 1,
|
|
161
|
-
alignItems: "center",
|
|
162
|
-
justifyContent: "center",
|
|
163
|
-
flexDirection: "row",
|
|
164
|
-
gap: space2.xs
|
|
165
|
-
}),
|
|
166
|
-
children: [
|
|
167
|
-
props.busy ? /* @__PURE__ */ jsx(ActivityIndicator, { color: foreground }) : null,
|
|
168
|
-
props.children ?? /* @__PURE__ */ jsx(
|
|
169
|
-
Text,
|
|
170
|
-
{
|
|
171
|
-
style: {
|
|
172
|
-
color: foreground,
|
|
173
|
-
fontSize: font2.size.md,
|
|
174
|
-
fontWeight: "500",
|
|
175
|
-
...font2.family ? { fontFamily: font2.family } : {}
|
|
176
|
-
},
|
|
177
|
-
children: props.label
|
|
178
|
-
}
|
|
179
|
-
)
|
|
180
|
-
]
|
|
181
|
-
}
|
|
182
|
-
);
|
|
183
|
-
});
|
|
184
195
|
function InfoCard(props) {
|
|
185
196
|
const card = cardOf(props.card);
|
|
186
197
|
const { locale, t } = useCopy();
|
|
@@ -189,7 +200,7 @@ function InfoCard(props) {
|
|
|
189
200
|
const image = safeImage(card.image_url);
|
|
190
201
|
const Img = slots.Image ?? Image;
|
|
191
202
|
const money = card.price ?? card.price_from;
|
|
192
|
-
const rating = card.rating ? (card.rating.value_ppm
|
|
203
|
+
const rating = card.rating ? ratingText(card.rating.value_ppm, locale) : null;
|
|
193
204
|
const amount = money ? formatMoney(money.amount, money.currency, locale) : null;
|
|
194
205
|
const price = amount && !card.price ? t("priceFrom", { price: amount }) : amount;
|
|
195
206
|
const when = card.starts_at ? formatTimeRange(card.starts_at, void 0, locale, timeZone) : null;
|
|
@@ -232,27 +243,32 @@ function InfoCard(props) {
|
|
|
232
243
|
] })
|
|
233
244
|
] }),
|
|
234
245
|
card.body ? /* @__PURE__ */ jsx(Body, { children: card.body }) : null,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
{
|
|
238
|
-
style: {
|
|
239
|
-
color: colors2.text,
|
|
240
|
-
backgroundColor: colors2.surface,
|
|
241
|
-
borderRadius: radius2.pill,
|
|
242
|
-
paddingHorizontal: space2.sm,
|
|
243
|
-
paddingVertical: space2.xxs,
|
|
244
|
-
fontSize: font2.size.xs
|
|
245
|
-
},
|
|
246
|
-
children: b
|
|
247
|
-
},
|
|
248
|
-
b
|
|
249
|
-
)) }) : null,
|
|
250
|
-
props.restored ? null : /* @__PURE__ */ jsx(Actions, { ...props })
|
|
246
|
+
/* @__PURE__ */ jsx(Badges, { badges: card.badges }),
|
|
247
|
+
props.restored ? null : /* @__PURE__ */ jsx(CardActions, { ...props })
|
|
251
248
|
]
|
|
252
249
|
}
|
|
253
250
|
);
|
|
254
251
|
}
|
|
255
|
-
function
|
|
252
|
+
function Badges(props) {
|
|
253
|
+
const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
|
|
254
|
+
if (!props.badges?.length) return null;
|
|
255
|
+
return /* @__PURE__ */ jsx(View, { style: { flexDirection: "row", flexWrap: "wrap", gap: space2.xs }, children: props.badges.map((b) => /* @__PURE__ */ jsx(
|
|
256
|
+
Text,
|
|
257
|
+
{
|
|
258
|
+
style: {
|
|
259
|
+
color: colors2.text,
|
|
260
|
+
backgroundColor: colors2.surface,
|
|
261
|
+
borderRadius: radius2.pill,
|
|
262
|
+
paddingHorizontal: space2.sm,
|
|
263
|
+
paddingVertical: space2.xxs,
|
|
264
|
+
fontSize: font2.size.xs
|
|
265
|
+
},
|
|
266
|
+
children: b
|
|
267
|
+
},
|
|
268
|
+
b
|
|
269
|
+
)) });
|
|
270
|
+
}
|
|
271
|
+
function CardActions(props) {
|
|
256
272
|
const card = cardOf(props.card);
|
|
257
273
|
const { space: space2 } = useTheme();
|
|
258
274
|
if (!card.actions?.length) return null;
|
|
@@ -284,6 +300,120 @@ function Action(props) {
|
|
|
284
300
|
}
|
|
285
301
|
);
|
|
286
302
|
}
|
|
303
|
+
function LinksCard(props) {
|
|
304
|
+
const card = cardOf(props.card);
|
|
305
|
+
const { store, handlers } = useChat();
|
|
306
|
+
const links = (card.links ?? []).filter(
|
|
307
|
+
(l) => /^https:\/\//i.test(l.url) || store.checkLink(l.url)
|
|
308
|
+
);
|
|
309
|
+
return /* @__PURE__ */ jsxs(
|
|
310
|
+
CardFrame,
|
|
311
|
+
{
|
|
312
|
+
label: [card.title, ...links.map((l) => l.label)].join(", "),
|
|
313
|
+
restored: props.restored,
|
|
314
|
+
testID: `vinaverse-card-${card.id}`,
|
|
315
|
+
children: [
|
|
316
|
+
/* @__PURE__ */ jsx(Title, { children: card.title }),
|
|
317
|
+
links.map((l) => /* @__PURE__ */ jsx(
|
|
318
|
+
Button,
|
|
319
|
+
{
|
|
320
|
+
label: l.label,
|
|
321
|
+
role: "link",
|
|
322
|
+
variant: "secondary",
|
|
323
|
+
disabled: props.restored,
|
|
324
|
+
onPress: () => {
|
|
325
|
+
const open = handlers.current?.onOpenLink;
|
|
326
|
+
if (open) open(l.url, props.card);
|
|
327
|
+
else void Linking.openURL(l.url);
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
l.url
|
|
331
|
+
))
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
function ListingCard(props) {
|
|
337
|
+
const card = cardOf(props.card);
|
|
338
|
+
const { locale, t } = useCopy();
|
|
339
|
+
const { slots, timeZone } = useChat();
|
|
340
|
+
const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
|
|
341
|
+
const [failed, setFailed] = useState(null);
|
|
342
|
+
const photoUrl = safeImage(card.images?.[0]?.url ?? card.image_url);
|
|
343
|
+
const hero = photoUrl === failed ? null : photoUrl;
|
|
344
|
+
const Img = slots.Image ?? Image;
|
|
345
|
+
const rating = card.rating ? `\u2605 ${ratingText(card.rating.value_ppm, locale)} (${card.rating.count})` : null;
|
|
346
|
+
const price = card.price_from ? t("priceFrom", {
|
|
347
|
+
price: formatMoney(
|
|
348
|
+
card.price_from.amount,
|
|
349
|
+
card.price_from.currency,
|
|
350
|
+
locale
|
|
351
|
+
)
|
|
352
|
+
}) : null;
|
|
353
|
+
const next = card.next_available ? t("nextAvailable", {
|
|
354
|
+
time: formatTimeRange(card.next_available, void 0, locale, timeZone)
|
|
355
|
+
}) : null;
|
|
356
|
+
const photo = {
|
|
357
|
+
width: "100%",
|
|
358
|
+
aspectRatio: 1.6,
|
|
359
|
+
borderRadius: radius2.md
|
|
360
|
+
};
|
|
361
|
+
return /* @__PURE__ */ jsxs(
|
|
362
|
+
CardFrame,
|
|
363
|
+
{
|
|
364
|
+
label: [card.title, card.subtitle, rating, price, next].filter(Boolean).join(", "),
|
|
365
|
+
restored: props.restored,
|
|
366
|
+
testID: `vinaverse-card-${card.id}`,
|
|
367
|
+
children: [
|
|
368
|
+
hero ? /* @__PURE__ */ jsx(
|
|
369
|
+
Img,
|
|
370
|
+
{
|
|
371
|
+
source: { uri: hero },
|
|
372
|
+
onError: () => setFailed(hero),
|
|
373
|
+
style: { ...photo, backgroundColor: colors2.surface },
|
|
374
|
+
accessible: false,
|
|
375
|
+
testID: `vinaverse-photo-${card.id}`
|
|
376
|
+
}
|
|
377
|
+
) : /* @__PURE__ */ jsx(
|
|
378
|
+
View,
|
|
379
|
+
{
|
|
380
|
+
accessible: false,
|
|
381
|
+
importantForAccessibility: "no-hide-descendants",
|
|
382
|
+
testID: `vinaverse-monogram-${card.id}`,
|
|
383
|
+
style: {
|
|
384
|
+
...photo,
|
|
385
|
+
backgroundColor: colors2.surface,
|
|
386
|
+
alignItems: "center",
|
|
387
|
+
justifyContent: "center"
|
|
388
|
+
},
|
|
389
|
+
children: /* @__PURE__ */ jsx(
|
|
390
|
+
Text,
|
|
391
|
+
{
|
|
392
|
+
style: {
|
|
393
|
+
color: colors2.textMuted,
|
|
394
|
+
fontSize: font2.size.xl,
|
|
395
|
+
fontWeight: "600"
|
|
396
|
+
},
|
|
397
|
+
children: monogram(card.title)
|
|
398
|
+
}
|
|
399
|
+
)
|
|
400
|
+
}
|
|
401
|
+
),
|
|
402
|
+
/* @__PURE__ */ jsxs(View, { style: { gap: space2.xxs }, children: [
|
|
403
|
+
/* @__PURE__ */ jsx(Title, { numberOfLines: 2, children: card.title }),
|
|
404
|
+
card.subtitle ? /* @__PURE__ */ jsx(Body, { muted: true, children: card.subtitle }) : null,
|
|
405
|
+
rating || price ? /* @__PURE__ */ jsx(Body, { muted: true, children: [rating, price].filter(Boolean).join(" \xB7 ") }) : null
|
|
406
|
+
] }),
|
|
407
|
+
card.body ? /* @__PURE__ */ jsx(Body, { children: card.body }) : null,
|
|
408
|
+
/* @__PURE__ */ jsx(Badges, { badges: next ? [next, ...card.badges ?? []] : card.badges }),
|
|
409
|
+
props.restored ? null : /* @__PURE__ */ jsx(CardActions, { ...props })
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
function monogram(title) {
|
|
415
|
+
return title.split(/\s+/).filter(Boolean).slice(0, 2).map((w) => [...w][0]?.toUpperCase() ?? "").join("");
|
|
416
|
+
}
|
|
287
417
|
function NextStepCard(props) {
|
|
288
418
|
const card = cardOf(props.card);
|
|
289
419
|
const action = card.actions?.[0];
|
|
@@ -1152,7 +1282,10 @@ var DEFAULTS = {
|
|
|
1152
1282
|
proposal: ProposalCard,
|
|
1153
1283
|
question: QuestionCard,
|
|
1154
1284
|
next_step: NextStepCard,
|
|
1155
|
-
time_slots: TimeSlotsCard
|
|
1285
|
+
time_slots: TimeSlotsCard,
|
|
1286
|
+
professional: ListingCard,
|
|
1287
|
+
workspace: ListingCard,
|
|
1288
|
+
links: LinksCard
|
|
1156
1289
|
};
|
|
1157
1290
|
function defaultCard(kind) {
|
|
1158
1291
|
return DEFAULTS[kind] ?? InfoCard;
|
|
@@ -1165,6 +1298,35 @@ var CardView = memo(function CardView2(props) {
|
|
|
1165
1298
|
const own = { card, busy, restored: props.item.restored === true };
|
|
1166
1299
|
return Custom ? /* @__PURE__ */ jsx(Custom, { ...own, Default }) : /* @__PURE__ */ jsx(Default, { ...own });
|
|
1167
1300
|
});
|
|
1301
|
+
var keyOf = (item) => item.key;
|
|
1302
|
+
var kindOf = (item) => item.card.kind;
|
|
1303
|
+
function CardCarousel(props) {
|
|
1304
|
+
const { width } = useWindowDimensions();
|
|
1305
|
+
const { t } = useCopy();
|
|
1306
|
+
const size3 = Math.min(Math.round(width * 0.8), 360);
|
|
1307
|
+
return /* @__PURE__ */ jsx(
|
|
1308
|
+
View,
|
|
1309
|
+
{
|
|
1310
|
+
accessibilityRole: "list",
|
|
1311
|
+
accessibilityLabel: t("cardsInRow", {
|
|
1312
|
+
count: String(props.items.length)
|
|
1313
|
+
}),
|
|
1314
|
+
children: /* @__PURE__ */ jsx(
|
|
1315
|
+
FlashList,
|
|
1316
|
+
{
|
|
1317
|
+
horizontal: true,
|
|
1318
|
+
data: props.items,
|
|
1319
|
+
keyExtractor: keyOf,
|
|
1320
|
+
getItemType: kindOf,
|
|
1321
|
+
renderItem: ({ item }) => /* @__PURE__ */ jsx(View, { style: { width: size3 }, children: /* @__PURE__ */ jsx(CardView, { item }) }),
|
|
1322
|
+
snapToInterval: size3,
|
|
1323
|
+
decelerationRate: "fast",
|
|
1324
|
+
showsHorizontalScrollIndicator: false
|
|
1325
|
+
}
|
|
1326
|
+
)
|
|
1327
|
+
}
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1168
1330
|
var ActivityLine = memo(function ActivityLine2() {
|
|
1169
1331
|
const { store } = useChat();
|
|
1170
1332
|
const { t } = useCopy();
|
|
@@ -1471,6 +1633,23 @@ var DisclosureBar = memo(function DisclosureBar2() {
|
|
|
1471
1633
|
}
|
|
1472
1634
|
);
|
|
1473
1635
|
});
|
|
1636
|
+
function FailureActionButton(props) {
|
|
1637
|
+
const { handlers, failureAction } = useChat();
|
|
1638
|
+
const { t } = useCopy();
|
|
1639
|
+
if (!failureAction) return null;
|
|
1640
|
+
return /* @__PURE__ */ jsx(
|
|
1641
|
+
Button,
|
|
1642
|
+
{
|
|
1643
|
+
label: failureAction.label ?? t("talkToPerson"),
|
|
1644
|
+
variant: "text",
|
|
1645
|
+
onPress: () => handlers.current.failureAction?.onPress(props.state)
|
|
1646
|
+
}
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
function FailureSlot(props) {
|
|
1650
|
+
const { slot: Slot, state, Default } = props;
|
|
1651
|
+
return Slot ? /* @__PURE__ */ jsx(Slot, { state, Default }) : /* @__PURE__ */ jsx(Default, { state });
|
|
1652
|
+
}
|
|
1474
1653
|
function DefaultBubble(props) {
|
|
1475
1654
|
const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
|
|
1476
1655
|
const role = props.item.role;
|
|
@@ -1626,9 +1805,29 @@ function announce(text) {
|
|
|
1626
1805
|
}
|
|
1627
1806
|
var SystemRow = memo(function SystemRow2(props) {
|
|
1628
1807
|
const { t } = useCopy();
|
|
1629
|
-
const { store } = useChat();
|
|
1630
|
-
const
|
|
1808
|
+
const { store, slots } = useChat();
|
|
1809
|
+
const conversationId = useConversationSelector(
|
|
1810
|
+
store,
|
|
1811
|
+
(v) => v.conversationId
|
|
1812
|
+
);
|
|
1631
1813
|
const { event, data } = props.item;
|
|
1814
|
+
if (event === "turn_failed") {
|
|
1815
|
+
const key = props.item.key;
|
|
1816
|
+
const state = {
|
|
1817
|
+
kind: "turn_failed",
|
|
1818
|
+
reason: typeof data.code === "string" ? data.code : null,
|
|
1819
|
+
retry: data.retry === true ? () => void store.retryTurn(key) : void 0,
|
|
1820
|
+
conversationId
|
|
1821
|
+
};
|
|
1822
|
+
return /* @__PURE__ */ jsx(
|
|
1823
|
+
FailureSlot,
|
|
1824
|
+
{
|
|
1825
|
+
slot: slots.Error,
|
|
1826
|
+
state,
|
|
1827
|
+
Default: DefaultTurnFailed
|
|
1828
|
+
}
|
|
1829
|
+
);
|
|
1830
|
+
}
|
|
1632
1831
|
const agent = store.getSnapshot().agent.name ?? t("agentFallbackName");
|
|
1633
1832
|
const text = (() => {
|
|
1634
1833
|
switch (event) {
|
|
@@ -1642,18 +1841,30 @@ var SystemRow = memo(function SystemRow2(props) {
|
|
|
1642
1841
|
return data.expected_response ? t("handoffCreated", { expected: data.expected_response }) : t("handoffCreatedNoTime");
|
|
1643
1842
|
case "flow_completed":
|
|
1644
1843
|
return data.result === "ok" ? t("flowSent") : data.result === "failed" ? t("flowNotSent") : null;
|
|
1645
|
-
case "turn_failed":
|
|
1646
|
-
return t("turnFailed");
|
|
1647
1844
|
default:
|
|
1648
1845
|
return null;
|
|
1649
1846
|
}
|
|
1650
1847
|
})();
|
|
1651
1848
|
if (!text) return null;
|
|
1849
|
+
return /* @__PURE__ */ jsx(Line, { text });
|
|
1850
|
+
});
|
|
1851
|
+
function DefaultTurnFailed(props) {
|
|
1852
|
+
const { t } = useCopy();
|
|
1853
|
+
const { failureAction } = useChat();
|
|
1854
|
+
const text = t("turnFailed");
|
|
1855
|
+
if (!failureAction) return /* @__PURE__ */ jsx(Line, { text, danger: true });
|
|
1856
|
+
return /* @__PURE__ */ jsxs(View, { style: { alignItems: "center" }, children: [
|
|
1857
|
+
/* @__PURE__ */ jsx(Line, { text, danger: true }),
|
|
1858
|
+
/* @__PURE__ */ jsx(FailureActionButton, { state: props.state })
|
|
1859
|
+
] });
|
|
1860
|
+
}
|
|
1861
|
+
function Line(props) {
|
|
1862
|
+
const { colors: colors2, space: space2, font: font2 } = useTheme();
|
|
1652
1863
|
return /* @__PURE__ */ jsx(
|
|
1653
1864
|
View,
|
|
1654
1865
|
{
|
|
1655
1866
|
accessible: true,
|
|
1656
|
-
accessibilityLabel: text,
|
|
1867
|
+
accessibilityLabel: props.text,
|
|
1657
1868
|
style: {
|
|
1658
1869
|
alignItems: "center",
|
|
1659
1870
|
paddingHorizontal: space2.xl,
|
|
@@ -1663,17 +1874,54 @@ var SystemRow = memo(function SystemRow2(props) {
|
|
|
1663
1874
|
Text,
|
|
1664
1875
|
{
|
|
1665
1876
|
style: {
|
|
1666
|
-
color:
|
|
1877
|
+
color: props.danger ? colors2.danger : colors2.textMuted,
|
|
1667
1878
|
fontSize: font2.size.sm,
|
|
1668
1879
|
textAlign: "center"
|
|
1669
1880
|
},
|
|
1670
|
-
children: text
|
|
1881
|
+
children: props.text
|
|
1671
1882
|
}
|
|
1672
1883
|
)
|
|
1673
1884
|
}
|
|
1674
1885
|
);
|
|
1886
|
+
}
|
|
1887
|
+
var Welcome = memo(function Welcome2(props) {
|
|
1888
|
+
const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
|
|
1889
|
+
return /* @__PURE__ */ jsxs(
|
|
1890
|
+
View,
|
|
1891
|
+
{
|
|
1892
|
+
style: { padding: space2.lg, gap: space2.md },
|
|
1893
|
+
testID: "vinaverse-welcome",
|
|
1894
|
+
children: [
|
|
1895
|
+
/* @__PURE__ */ jsx(
|
|
1896
|
+
Text,
|
|
1897
|
+
{
|
|
1898
|
+
style: {
|
|
1899
|
+
alignSelf: "flex-start",
|
|
1900
|
+
maxWidth: "85%",
|
|
1901
|
+
color: colors2.agentText,
|
|
1902
|
+
backgroundColor: colors2.agentBubble,
|
|
1903
|
+
borderRadius: radius2.lg,
|
|
1904
|
+
paddingHorizontal: space2.md,
|
|
1905
|
+
paddingVertical: space2.sm,
|
|
1906
|
+
fontSize: font2.size.md,
|
|
1907
|
+
lineHeight: font2.size.md * font2.lineHeight
|
|
1908
|
+
},
|
|
1909
|
+
children: props.welcome.text
|
|
1910
|
+
}
|
|
1911
|
+
),
|
|
1912
|
+
props.welcome.chips.length ? /* @__PURE__ */ jsx(View, { style: { flexDirection: "row", flexWrap: "wrap", gap: space2.sm }, children: props.welcome.chips.map((chip) => /* @__PURE__ */ jsx(
|
|
1913
|
+
Button,
|
|
1914
|
+
{
|
|
1915
|
+
label: chip.label,
|
|
1916
|
+
onPress: () => props.onChip(chip.message)
|
|
1917
|
+
},
|
|
1918
|
+
chip.label
|
|
1919
|
+
)) }) : null
|
|
1920
|
+
]
|
|
1921
|
+
}
|
|
1922
|
+
);
|
|
1675
1923
|
});
|
|
1676
|
-
var
|
|
1924
|
+
var keyOf2 = (item) => item.key;
|
|
1677
1925
|
function itemType(item) {
|
|
1678
1926
|
if (item.type === "card") return `card:${item.card.kind}`;
|
|
1679
1927
|
if (item.type === "message")
|
|
@@ -1683,8 +1931,39 @@ function itemType(item) {
|
|
|
1683
1931
|
function authorOf(item) {
|
|
1684
1932
|
return item?.type === "message" ? item.role : null;
|
|
1685
1933
|
}
|
|
1934
|
+
function carousels(items, previous) {
|
|
1935
|
+
const runs = /* @__PURE__ */ new Map();
|
|
1936
|
+
const inside = /* @__PURE__ */ new Set();
|
|
1937
|
+
const groupOf = (item) => {
|
|
1938
|
+
if (item?.type !== "card") return null;
|
|
1939
|
+
const group = item.card.card.group;
|
|
1940
|
+
return group?.layout === "carousel" && typeof group.id === "string" ? group.id : null;
|
|
1941
|
+
};
|
|
1942
|
+
for (let i = 0; i < items.length; i++) {
|
|
1943
|
+
const id = groupOf(items[i]);
|
|
1944
|
+
if (id === null) continue;
|
|
1945
|
+
let end = i + 1;
|
|
1946
|
+
while (end < items.length && groupOf(items[end]) === id) end++;
|
|
1947
|
+
if (end - i > 1) {
|
|
1948
|
+
const run = items.slice(i, end);
|
|
1949
|
+
const key = run[0]?.key;
|
|
1950
|
+
const before = previous.get(key);
|
|
1951
|
+
runs.set(
|
|
1952
|
+
key,
|
|
1953
|
+
before && before.length === run.length && before.every((c, n) => c === run[n]) ? before : run
|
|
1954
|
+
);
|
|
1955
|
+
for (const card of run.slice(1)) inside.add(card.key);
|
|
1956
|
+
}
|
|
1957
|
+
i = end - 1;
|
|
1958
|
+
}
|
|
1959
|
+
return { runs, inside };
|
|
1960
|
+
}
|
|
1686
1961
|
var Row = memo(function Row2(props) {
|
|
1687
1962
|
const { item } = props;
|
|
1963
|
+
const { slots } = useChat();
|
|
1964
|
+
const Group = slots.CardGroup;
|
|
1965
|
+
if (props.inside) return null;
|
|
1966
|
+
if (props.run && Group) return /* @__PURE__ */ jsx(Group, { items: props.run });
|
|
1688
1967
|
if (item.type === "card") return /* @__PURE__ */ jsx(CardView, { item });
|
|
1689
1968
|
if (item.type === "system") return /* @__PURE__ */ jsx(SystemRow, { item });
|
|
1690
1969
|
return item.state === "streaming" ? /* @__PURE__ */ jsx(StreamingBubble, { item, showAuthor: props.showAuthor }) : /* @__PURE__ */ jsx(MessageBubble, { item, showAuthor: props.showAuthor });
|
|
@@ -1700,26 +1979,49 @@ var MessageList = memo(function MessageList2() {
|
|
|
1700
1979
|
(v) => v.status === "starting" || v.status === "connecting"
|
|
1701
1980
|
);
|
|
1702
1981
|
const agentName = useConversationSelector(store, (v) => v.agent.name);
|
|
1982
|
+
const welcome = useConversationSelector(store, (v) => v.welcome);
|
|
1703
1983
|
useAnnounceReplies(items);
|
|
1984
|
+
const Group = slots.CardGroup;
|
|
1985
|
+
const runsRef = useRef(/* @__PURE__ */ new Map());
|
|
1986
|
+
const grouping = useMemo(
|
|
1987
|
+
() => Group ? carousels(items, runsRef.current) : null,
|
|
1988
|
+
[items, Group]
|
|
1989
|
+
);
|
|
1990
|
+
runsRef.current = grouping?.runs ?? /* @__PURE__ */ new Map();
|
|
1704
1991
|
const itemsRef = useRef(items);
|
|
1705
1992
|
itemsRef.current = items;
|
|
1993
|
+
const groupingRef = useRef(grouping);
|
|
1994
|
+
groupingRef.current = grouping;
|
|
1706
1995
|
const renderItem = useCallback(
|
|
1707
1996
|
({ item, index }) => /* @__PURE__ */ jsx(
|
|
1708
1997
|
Row,
|
|
1709
1998
|
{
|
|
1710
1999
|
item,
|
|
1711
|
-
showAuthor: authorOf(item) !== authorOf(itemsRef.current[index - 1])
|
|
2000
|
+
showAuthor: authorOf(item) !== authorOf(itemsRef.current[index - 1]),
|
|
2001
|
+
run: groupingRef.current?.runs.get(item.key),
|
|
2002
|
+
inside: groupingRef.current?.inside.has(item.key) === true
|
|
1712
2003
|
}
|
|
1713
2004
|
),
|
|
1714
2005
|
[]
|
|
1715
2006
|
);
|
|
2007
|
+
const onChip = useCallback(
|
|
2008
|
+
(message) => void store.send(message),
|
|
2009
|
+
[store]
|
|
2010
|
+
);
|
|
1716
2011
|
const Empty = slots.EmptyState;
|
|
1717
|
-
const empty = opening ? /* @__PURE__ */ jsx(ChatSkeleton, {}) : Empty ? /* @__PURE__ */ jsx(
|
|
2012
|
+
const empty = opening ? /* @__PURE__ */ jsx(ChatSkeleton, {}) : Empty ? /* @__PURE__ */ jsx(
|
|
2013
|
+
Empty,
|
|
2014
|
+
{
|
|
2015
|
+
agentName: agentName ?? t("agentFallbackName"),
|
|
2016
|
+
welcome,
|
|
2017
|
+
onChip
|
|
2018
|
+
}
|
|
2019
|
+
) : welcome ? /* @__PURE__ */ jsx(Welcome, { welcome, onChip }) : null;
|
|
1718
2020
|
const props = useMemo(
|
|
1719
2021
|
() => ({
|
|
1720
2022
|
data: items,
|
|
1721
2023
|
renderItem,
|
|
1722
|
-
keyExtractor:
|
|
2024
|
+
keyExtractor: keyOf2,
|
|
1723
2025
|
getItemType: itemType,
|
|
1724
2026
|
maintainVisibleContentPosition: {
|
|
1725
2027
|
startRenderingFromBottom: true,
|
|
@@ -1757,10 +2059,23 @@ function useAnnounceReplies(items) {
|
|
|
1757
2059
|
}
|
|
1758
2060
|
var CONNECTING_DELAY = motion.bannerDelay;
|
|
1759
2061
|
var StatusBanner = memo(function StatusBanner2() {
|
|
1760
|
-
const { store } = useChat();
|
|
2062
|
+
const { store, slots } = useChat();
|
|
1761
2063
|
const { t } = useCopy();
|
|
1762
|
-
const { colors: colors2, space: space2, font: font2 } = useTheme();
|
|
1763
2064
|
const banner = useConversationSelector(store, (v) => v.banner);
|
|
2065
|
+
const status = useConversationSelector(store, (v) => v.status);
|
|
2066
|
+
const conversationId = useConversationSelector(
|
|
2067
|
+
store,
|
|
2068
|
+
(v) => v.conversationId
|
|
2069
|
+
);
|
|
2070
|
+
const unavailable = useMemo(
|
|
2071
|
+
() => ({
|
|
2072
|
+
kind: "unavailable",
|
|
2073
|
+
reason: status,
|
|
2074
|
+
retry: () => store.start(),
|
|
2075
|
+
conversationId
|
|
2076
|
+
}),
|
|
2077
|
+
[store, status, conversationId]
|
|
2078
|
+
);
|
|
1764
2079
|
const [late, setLate] = useState(false);
|
|
1765
2080
|
useEffect(() => {
|
|
1766
2081
|
setLate(false);
|
|
@@ -1769,12 +2084,20 @@ var StatusBanner = memo(function StatusBanner2() {
|
|
|
1769
2084
|
return () => clearTimeout(timer);
|
|
1770
2085
|
}, [banner]);
|
|
1771
2086
|
if (banner === null || banner === "connecting" && !late) return null;
|
|
2087
|
+
if (banner === "unavailable")
|
|
2088
|
+
return /* @__PURE__ */ jsx(
|
|
2089
|
+
FailureSlot,
|
|
2090
|
+
{
|
|
2091
|
+
slot: slots.Unavailable,
|
|
2092
|
+
state: unavailable,
|
|
2093
|
+
Default: DefaultUnavailable
|
|
2094
|
+
}
|
|
2095
|
+
);
|
|
1772
2096
|
const text = {
|
|
1773
2097
|
connecting: t("bannerConnecting"),
|
|
1774
2098
|
reconnecting: t("bannerReconnecting"),
|
|
1775
2099
|
offline: t("bannerOffline"),
|
|
1776
|
-
continued_elsewhere: t("bannerContinuedElsewhere")
|
|
1777
|
-
unavailable: t("bannerUnavailable")
|
|
2100
|
+
continued_elsewhere: t("bannerContinuedElsewhere")
|
|
1778
2101
|
}[banner];
|
|
1779
2102
|
const action = banner === "continued_elsewhere" ? /* @__PURE__ */ jsx(
|
|
1780
2103
|
Button,
|
|
@@ -1783,7 +2106,7 @@ var StatusBanner = memo(function StatusBanner2() {
|
|
|
1783
2106
|
variant: "text",
|
|
1784
2107
|
onPress: () => void store.continueHere()
|
|
1785
2108
|
}
|
|
1786
|
-
) : banner === "offline"
|
|
2109
|
+
) : banner === "offline" ? /* @__PURE__ */ jsx(
|
|
1787
2110
|
Button,
|
|
1788
2111
|
{
|
|
1789
2112
|
label: t("tryAgain"),
|
|
@@ -1791,6 +2114,18 @@ var StatusBanner = memo(function StatusBanner2() {
|
|
|
1791
2114
|
onPress: () => store.start()
|
|
1792
2115
|
}
|
|
1793
2116
|
) : null;
|
|
2117
|
+
return /* @__PURE__ */ jsx(Banner, { text, children: action });
|
|
2118
|
+
});
|
|
2119
|
+
function DefaultUnavailable(props) {
|
|
2120
|
+
const { t } = useCopy();
|
|
2121
|
+
const { retry } = props.state;
|
|
2122
|
+
return /* @__PURE__ */ jsxs(Banner, { text: t("bannerUnavailable"), children: [
|
|
2123
|
+
retry ? /* @__PURE__ */ jsx(Button, { label: t("tryAgain"), variant: "text", onPress: retry }) : null,
|
|
2124
|
+
/* @__PURE__ */ jsx(FailureActionButton, { state: props.state })
|
|
2125
|
+
] });
|
|
2126
|
+
}
|
|
2127
|
+
function Banner(props) {
|
|
2128
|
+
const { colors: colors2, space: space2, font: font2 } = useTheme();
|
|
1794
2129
|
return /* @__PURE__ */ jsxs(
|
|
1795
2130
|
View,
|
|
1796
2131
|
{
|
|
@@ -1812,22 +2147,35 @@ var StatusBanner = memo(function StatusBanner2() {
|
|
|
1812
2147
|
Text,
|
|
1813
2148
|
{
|
|
1814
2149
|
style: { color: colors2.text, fontSize: font2.size.sm, flexShrink: 1 },
|
|
1815
|
-
children: text
|
|
2150
|
+
children: props.text
|
|
1816
2151
|
}
|
|
1817
2152
|
),
|
|
1818
|
-
|
|
2153
|
+
props.children
|
|
1819
2154
|
]
|
|
1820
2155
|
}
|
|
1821
2156
|
);
|
|
1822
|
-
}
|
|
2157
|
+
}
|
|
1823
2158
|
function VinaverseChatProvider(props) {
|
|
1824
2159
|
const { conversation, components, agentAvatar, timeZone } = props;
|
|
1825
2160
|
const handlers = useRef(props);
|
|
1826
2161
|
handlers.current = props;
|
|
1827
2162
|
const slots = useStableSlots(components);
|
|
2163
|
+
const hasAction = props.failureAction !== void 0;
|
|
2164
|
+
const actionLabel = props.failureAction?.label;
|
|
2165
|
+
const failureAction = useMemo(
|
|
2166
|
+
() => hasAction ? { label: actionLabel } : null,
|
|
2167
|
+
[hasAction, actionLabel]
|
|
2168
|
+
);
|
|
1828
2169
|
const value = useMemo(
|
|
1829
|
-
() => ({
|
|
1830
|
-
|
|
2170
|
+
() => ({
|
|
2171
|
+
store: conversation,
|
|
2172
|
+
handlers,
|
|
2173
|
+
slots,
|
|
2174
|
+
agentAvatar,
|
|
2175
|
+
timeZone,
|
|
2176
|
+
failureAction
|
|
2177
|
+
}),
|
|
2178
|
+
[conversation, slots, agentAvatar, timeZone, failureAction]
|
|
1831
2179
|
);
|
|
1832
2180
|
useNativeLifecycle(conversation);
|
|
1833
2181
|
useEffect(() => {
|
|
@@ -1903,6 +2251,6 @@ function sameSlots(a, b) {
|
|
|
1903
2251
|
return true;
|
|
1904
2252
|
}
|
|
1905
2253
|
|
|
1906
|
-
export { ActivityLine, Avatar, Button, CardFrame, CardView, ChatHeader, ChatSkeleton, Composer, DefaultBubble, DefaultComposer, DefaultHeader, DisclosureBar, InfoCard, MessageBubble, MessageList, NextStepCard, ProposalCard, QuestionCard, Row, StatusBanner, StreamingBubble, SystemRow, ThemeProvider, TimeSlotsCard, VinaverseChat, VinaverseChatProvider, announce, answerText, createTheme, defaultCard, fieldText, itemType, themeProblems, useChat, useNativeLifecycle, useReducedMotion, useTheme };
|
|
2254
|
+
export { ActivityLine, Avatar, Button, CardActions, CardCarousel, CardFrame, CardView, ChatHeader, ChatSkeleton, Composer, DefaultBubble, DefaultComposer, DefaultHeader, DefaultTurnFailed, DefaultUnavailable, DisclosureBar, FailureActionButton, InfoCard, LinksCard, ListingCard, MessageBubble, MessageList, NextStepCard, ProposalCard, QuestionCard, Row, StatusBanner, StreamingBubble, SystemRow, ThemeProvider, TimeSlotsCard, VinaverseChat, VinaverseChatProvider, Welcome, announce, answerText, carousels, createTheme, defaultCard, fieldText, itemType, monogram, ratingText, themeProblems, useChat, useNativeLifecycle, useReducedMotion, useTheme };
|
|
1907
2255
|
//# sourceMappingURL=index.js.map
|
|
1908
2256
|
//# sourceMappingURL=index.js.map
|