@vinaverse/react-native 0.0.0-stage → 0.1.0-alpha.11

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 (57) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +326 -2
  3. package/dist/ask.cjs +62 -0
  4. package/dist/ask.cjs.map +1 -0
  5. package/dist/ask.d.cts +38 -0
  6. package/dist/ask.d.ts +38 -0
  7. package/dist/ask.js +5 -0
  8. package/dist/ask.js.map +1 -0
  9. package/dist/chat.cjs +274 -0
  10. package/dist/chat.cjs.map +1 -0
  11. package/dist/chat.d.cts +292 -0
  12. package/dist/chat.d.ts +292 -0
  13. package/dist/chat.js +5 -0
  14. package/dist/chat.js.map +1 -0
  15. package/dist/chunk-2J2LIJLW.js +624 -0
  16. package/dist/chunk-2J2LIJLW.js.map +1 -0
  17. package/dist/chunk-4PXQ7DEY.cjs +680 -0
  18. package/dist/chunk-4PXQ7DEY.cjs.map +1 -0
  19. package/dist/chunk-6QYDPGRS.cjs +97 -0
  20. package/dist/chunk-6QYDPGRS.cjs.map +1 -0
  21. package/dist/chunk-7KPCO443.js +74 -0
  22. package/dist/chunk-7KPCO443.js.map +1 -0
  23. package/dist/chunk-EFEXTVIT.js +88 -0
  24. package/dist/chunk-EFEXTVIT.js.map +1 -0
  25. package/dist/chunk-KSHDF3MG.js +1996 -0
  26. package/dist/chunk-KSHDF3MG.js.map +1 -0
  27. package/dist/chunk-MG32E4JI.cjs +268 -0
  28. package/dist/chunk-MG32E4JI.cjs.map +1 -0
  29. package/dist/chunk-NMKJ7LIJ.cjs +76 -0
  30. package/dist/chunk-NMKJ7LIJ.cjs.map +1 -0
  31. package/dist/chunk-POJP52GE.js +261 -0
  32. package/dist/chunk-POJP52GE.js.map +1 -0
  33. package/dist/chunk-XAG2P447.cjs +2035 -0
  34. package/dist/chunk-XAG2P447.cjs.map +1 -0
  35. package/dist/context-gHCLFsJh.d.cts +212 -0
  36. package/dist/context-gHCLFsJh.d.ts +212 -0
  37. package/dist/history.cjs +673 -0
  38. package/dist/history.cjs.map +1 -0
  39. package/dist/history.d.cts +177 -0
  40. package/dist/history.d.ts +177 -0
  41. package/dist/history.js +618 -0
  42. package/dist/history.js.map +1 -0
  43. package/dist/index.cjs +304 -0
  44. package/dist/index.cjs.map +1 -0
  45. package/dist/index.d.cts +12 -0
  46. package/dist/index.d.ts +12 -0
  47. package/dist/index.js +7 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/launcher.cjs +86 -0
  50. package/dist/launcher.cjs.map +1 -0
  51. package/dist/launcher.d.cts +122 -0
  52. package/dist/launcher.d.ts +122 -0
  53. package/dist/launcher.js +5 -0
  54. package/dist/launcher.js.map +1 -0
  55. package/dist/theme-BhiEGv64.d.cts +35 -0
  56. package/dist/theme-BhiEGv64.d.ts +35 -0
  57. package/package.json +311 -3
package/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
package/README.md CHANGED
@@ -1,3 +1,327 @@
1
- # Temporary Holding Version
1
+ # @vinaverse/react-native
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A ready, themeable conversation with your Vinaverse agent for React Native and Expo apps. Drop it into a screen,
4
+ give it a conversation whose session comes from your backend, and it draws everything the realtime protocol defines:
5
+ streaming replies, cards (photo cards for people and places, services, bookings, questions, proposals, next steps,
6
+ your own help pages), held and unsent messages, a
7
+ person from your business taking over, and every way a session ends. No native module; New Architecture; FlashList 2.3.2 or later.
8
+
9
+ ```tsx
10
+ import { createConversation } from '@vinaverse/client/conversation';
11
+ import { CardCarousel, VinaverseChat } from '@vinaverse/react-native';
12
+
13
+ // Outside the screen: it connects early and survives navigation.
14
+ const conversation = createConversation({
15
+ getSession: (request) => api.vinaverseSession(request), // your backend creates it with its service key
16
+ locale: 'es-US',
17
+ deepLinkPrefixes: ['yourapp://bookings/'],
18
+ });
19
+ // Opt-in: a turn's listing cards in one swipeable row (without it they stack). Module level: a stable object.
20
+ const components = { CardGroup: CardCarousel };
21
+
22
+ export function AgentScreen() {
23
+ return (
24
+ <VinaverseChat
25
+ conversation={conversation}
26
+ locale="es-US"
27
+ theme={{ light: { primary: '#7A2E8E', personBubble: '#7A2E8E' } }}
28
+ components={components}
29
+ onProposal={(card, decision) => api.decideProposal(card, decision)} // your backend, the person's token
30
+ onOpenLink={(link) => router.push(link)}
31
+ />
32
+ );
33
+ }
34
+ ```
35
+
36
+ ## Hosting the agent (the runtime)
37
+
38
+ An app that hosts the agent writes its backend once (`createAgentRuntime`, `@vinaverse/client/agent`) and draws the
39
+ parts; which conversation, keeping it across screens, waiting the backend out, a conversation that ended, the backend
40
+ out of reach, status, name, ratings and sign-out are the runtime's.
41
+
42
+ ```tsx
43
+ import { createAgentRuntime } from '@vinaverse/client/agent';
44
+ import { AgentChat, CardCarousel } from '@vinaverse/react-native/chat';
45
+ import { AgentLauncher, AgentRuntimeProvider } from '@vinaverse/react-native/launcher';
46
+ import { AskAboutRow } from '@vinaverse/react-native/ask';
47
+ import { AgentHistoryList, AgentTranscript } from '@vinaverse/react-native/history'; // the history screens
48
+
49
+ const agent = createAgentRuntime({ backend: { openSession, refreshSecret, status }, conversation: { locale } });
50
+ const components = { CardGroup: CardCarousel, Image: ExpoImage }; // module level: stable
51
+ const imagePolicy = { hosts: ['images.example.com'], aspectRatio: 1.6, Placeholder: BrandTile }; // stable too
52
+
53
+ <AgentRuntimeProvider runtime={agent}>
54
+ <Tabs />
55
+ <AgentLauncher subject={user?.id ?? null} mode={side} Mark={YourMark}
56
+ route={currentTab} routes={['home', 'explore']} hidden={sheetOpen} // never with the keyboard up
57
+ storage={preferences} onOpen={({ route, mode, entryPoint }) => openChat(route, mode, entryPoint)}
58
+ style={{ position: 'absolute', right: 16, bottom: tabBarHeight + 16 }} />
59
+ </AgentRuntimeProvider>
60
+
61
+ // The chat screen
62
+ <AgentChat
63
+ agentKey={{ subject: user?.id ?? null, mode: side, scope: businessId }}
64
+ place={{ screen: 'professional', entity: { type: 'professional', id } }}
65
+ entryPoint={entryPoint} // 'launcher', 'ask', 'deep_link': session_opened says it
66
+ askAbout={`Tell me about ${name}`} // offered when the session was told another place
67
+ starters={startersFor(screen)} // when the session brings no welcome
68
+ components={components}
69
+ onOpenTarget={(t) => router.push(routeFor(t))} // a card about a professional, a space, a booking
70
+ onOpenLink={openLink}
71
+ onContact={() => router.push('/contact')} // a person, when the agent fails or is unavailable
72
+ onOpenPrivacy={() => router.push('/privacy')}
73
+ prefetchImage={(url) => Image.prefetch(url)}
74
+ imagePolicy={imagePolicy}
75
+ />
76
+
77
+ // On the app's own page about a professional
78
+ <AskAboutRow title={`Ask ${agentName} about ${name}`} onPress={({ entryPoint }) => openChat('professional', side, entryPoint)} />
79
+ ```
80
+
81
+ - **Parts:** `AgentChat` draws the conversation, a conversation that ended (`components.Ended`: `onStartNew`,
82
+ `reason`, `code`, `onContact`), the backend out of reach (`components.Unreachable`: `onRetry`, `reason`,
83
+ `onContact`), no agent at all (`components.UnavailableScreen`, the whole screen: off, or the session refused;
84
+ `reason`, `code`, `onRetry`, `onContact`), and under the composer thumbs on the newest whole reply seen live and the
85
+ privacy link (`components.Footer`, `null` to hide). Each slot replaces its screen entirely, layout and words, and gets
86
+ the kit's part as `Default` to wrap instead; to change only the words, pass `copy`. A person to contact
87
+ (`onContact`) reaches every full-screen slot. `opener` is a first question as a chip the person taps (a flow that asks for itself), until they say
88
+ anything; nothing is ever sent in their name. So a setup flow starts when the person taps it, never on its own:
89
+ starting it automatically would put words in their mouth, which the kit declines by design. The opener chip's test id
90
+ is `vinaverse-opener` (`openerTestID` for your own); the chip asking about where the person moved is
91
+ `vinaverse-suggestion` (`VinaverseChat`'s `suggestionTestID`). `AgentLauncher` is a round button with
92
+ a greeting bubble once per person and mode (`storage`: your preferences store; `greeting`: words or words by mode,
93
+ read through `announce`; `storageKey` maps the record to one your app already keeps, so nobody is greeted twice),
94
+ your `Mark` inside, floating where `placement` says (`edge`, `bottom` above your tab bar, `inset`; `style` after it),
95
+ with `appearance` (a `ring`, the `shadow`, the bubble's `tail`), shown on your `routes` only, never with the keyboard
96
+ up (`hideWithKeyboard`; on iOS it goes as the keyboard starts to rise, on Android once it is up), stepping aside when you say (`hidden`), and only when the status offers it; its button,
97
+ bubble and entrance are slots (`LauncherButton`, `LauncherGreeting`, `LauncherEntrance`: a short fade and scale,
98
+ none with reduced motion). Its `onOpen` and the ask row's `onPress` give `entryPoint` (`launcher`, `ask`): pass it to
99
+ `AgentChat`'s `entryPoint` (or `deep_link`, or your own words) and `session_opened` says where the chat was opened
100
+ from. Every agent part asks the runtime to `sweep()` when the app comes back to the foreground. `AskAboutRow` is "ask the agent about this" on
101
+ your own pages, decided once as the page draws, from the status already read: it is never added under a drawn page
102
+ (an agent whose status arrives later shows from the next visit), and `appearance` gives it a tint (`background`) and the
103
+ `chevron` or none, and `testID` its test id.
104
+ - **Policies and adapters:** `imagePolicy` (photo hosts, frame, the stand-in for a missing photo, given the card's `title` and `kind`) applies to every card
105
+ and every fetch ahead; `announce` reads finished replies and the greeting your way; `onChip` hears a tapped chip.
106
+ Card renderers get the card read once (`read`). A listing with no rating draws `copy.ratingNone` in the rating's place (empty by default; "New", say).
107
+ - **History** (`@vinaverse/react-native/history` only, never `.`, so an app without a history screen ships none
108
+ of it): `AgentHistoryList` lists the person's past conversations, newest activity first, a page at a time as
109
+ they scroll (title, when, how many messages, whether it ended); a row opens (`onOpen`: show `AgentTranscript`), an
110
+ open one continues (`onContinue(conversation, live)`: pinned under its own key, `useAgentHistory().keyOf`, show
111
+ `AgentChat` with it), and deletes after the person confirms (`confirmDelete`, default the platform's alert with the
112
+ copy's words; a refused delete is said on the row and through `onWarning`). `AgentTranscript` reads one back, the
113
+ newest page first and earlier pages above (a button, and the top of the list), drawn with the chat's own bubbles and
114
+ cards over a read-only store, so a restored card takes nothing (no proposal confirmed, no link opened, no answer),
115
+ a guardrail's refusal reads as it did live ("Not answered" under the person's message) and a proposal shows the
116
+ outcome it had; `agentKey` + `onContinue` add "Continue", except for a conversation that `ended` (pass the row's
117
+ `state === 'ended'`, as the list offers Continue only for an open one). Slots: `Row`, `Empty`, `Failure`, `ListComponent` on the
118
+ list; `Header`, `Failure`, `ListComponent`, `MessageBubble`, `Image`, `CardGroup` and `cards` on the transcript.
119
+ Both show when the status offers history (`useAgentFeatures().history`); your backend serves the two routes
120
+ (`backendRoutes`' `list` and `read`), else they say history is not available.
121
+
122
+ ```tsx
123
+ import { AgentHistoryList, AgentTranscript } from '@vinaverse/react-native/history';
124
+
125
+ <AgentHistoryList subject={user?.id ?? null} mode={side} timeZone={tz}
126
+ onOpen={(c) => router.push({ pathname: `/history/${c.id}`, params: { ended: String(c.state === 'ended') } })}
127
+ onContinue={(c) => router.push({ pathname: '/chat', params: { mode: c.experience_mode, scope: c.business_scope_id } })} />
128
+ <AgentTranscript conversationId={id} ended={ended === 'true'} agentKey={{ subject: user?.id ?? null, mode: side }} onContinue={() => router.push('/chat')} />
129
+ ```
130
+ - **Hooks** for your own parts: `useAgentConversation`, `useAgentStatus`, `useAgentStatusState` (`{ status, reading,
131
+ failed }`: "still reading" apart from "could not read"), `useAgentFeatures`, `useAgentProfile`, `useAgentName`,
132
+ `useAgentLauncher`, `useAgentRuntime`; `useAgentHistory`, `useAgentTranscript` (`@vinaverse/react-native/history`).
133
+ - **Events** for analytics through `onEvent`: sessions, ratings, chips, targets and links opened, the launcher shown,
134
+ opened, greeted and its greeting closed.
135
+ - **StrictMode, in development only:** React mounts, unmounts and mounts again, so a conversation still opening is let
136
+ go with the first unmount and the second mount asks your backend for another session: two `openSession` calls per
137
+ opening in development, one in a release build. The first request's answer is dropped, never shown.
138
+
139
+ ## Install
140
+
141
+ ```sh
142
+ npm install @vinaverse/react-native@alpha @vinaverse/client@alpha @shopify/flash-list@^2.3.2
143
+ ```
144
+
145
+ Alphas are published under the `alpha` tag. Keep every `@vinaverse/*` package on the same version, so your app and the
146
+ kit share one copy of `@vinaverse/client`.
147
+
148
+ **Import by part.** The package has an entry per part: `@vinaverse/react-native/chat` (the conversation, `AgentChat`,
149
+ `VinaverseChat`, cards, theme), `/launcher` (`AgentLauncher` and its parts) and `/ask` (`AskAboutRow`). The launcher and
150
+ the ask row each re-export the runtime bindings (`AgentRuntimeProvider`, the hooks) and the theme. The entries share
151
+ their modules, so two of them still mean one theme and one chat context. `@vinaverse/react-native` itself still exports
152
+ everything.
153
+
154
+ What it buys, honestly. Metro without tree shaking (its default) puts every module an app imports statically, anywhere,
155
+ into the one bundle it writes for iOS or Android. Measured on the kit alone (minified, brotli, peers excluded):
156
+
157
+ | The app imports | Ships |
158
+ |---|---|
159
+ | `@vinaverse/react-native` | 14.9 kB |
160
+ | `/launcher` and `/ask`, no chat screen | 2.9 kB |
161
+ | `/launcher`, `/ask` and `/chat` (a chat screen anywhere) | 15.1 kB |
162
+
163
+ - **The bundle** shrinks only for an app that never imports `/chat`. An app with a chat screen ships the whole kit
164
+ whichever way it imports it; the separate entries add about 0.1 kB.
165
+ - **Start-up** is where an app with a chat screen gains: a screen that imports only `/launcher` runs the kit's 2.5 kB
166
+ of launcher code when it loads, not all 15 kB (with `@vinaverse/react-native`, using the launcher loads every part).
167
+ Inline requires (`inlineRequires`) defer a module until its first use, but they do not split the kit's own entry:
168
+ only the per-part imports do.
169
+ - **Both** need the chat screen loaded lazily (`React.lazy`, or Expo Router's async routes). On iOS and Android a
170
+ production Metro bundle is still one file, so lazy loading defers when the chat's code runs, never whether it ships;
171
+ on the web, async routes put it in a bundle of its own.
172
+
173
+ Loading the chat screen lazily. On the web, Expo Router's async routes (`"asyncRoutes"` in the `expo-router` plugin's
174
+ options in `app.json`, e.g. `{ "web": true, "default": "development" }`) put each route's code in a bundle of its own,
175
+ loaded the first time it is visited. That setting makes routes async on iOS and Android in development only, so a
176
+ production iOS or Android build gets no deferral from it: there, `React.lazy` is what defers the chat:
177
+
178
+ ```tsx
179
+ import { lazy, Suspense } from 'react';
180
+ import { AgentLauncher, AgentRuntimeProvider } from '@vinaverse/react-native/launcher'; // the tabs: 2.5 kB of the kit
181
+
182
+ // Only this file imports /chat, and only when the screen is first drawn.
183
+ const ChatScreen = lazy(() => import('./ChatScreen')); // ChatScreen.tsx: import { AgentChat } from '@vinaverse/react-native/chat'
184
+
185
+ export function Chat() {
186
+ return (
187
+ <Suspense fallback={null}>
188
+ <ChatScreen />
189
+ </Suspense>
190
+ );
191
+ }
192
+ ```
193
+
194
+ What is lost without it: a screen that imports `/chat` statically, or anything that does, loads and runs the whole chat
195
+ (about 13 kB of the kit, its cards and list) when that screen's module first runs; with inline requires, when it is
196
+ first used. Nothing breaks either way, and the bundle is the same on iOS and Android.
197
+
198
+ **One build under Metro.** Every `@vinaverse/*` entry lists the `react-native` condition first, which React Native's
199
+ and Expo's Metro configs set for iOS and Android: Metro takes the CommonJS build for an `import`, a `require`, and a
200
+ `require` Babel writes for you (an `export const X = ImportedBinding` becomes a getter that requires), so an app holds
201
+ one copy of the kit and one of each React context. Before 0.1.0-alpha.8 an ES import and a require resolved to
202
+ different builds, and a card drawn from the second found no chat around it; a `metro.config.js` that forces the `import`
203
+ condition for `@vinaverse/*` is no longer needed (harmless if kept). Jest with jest-expo resolves the same CommonJS
204
+ build, untransformed. Web bundlers (no `react-native` condition) take the ES module build for an import: import the kit
205
+ with `import` statements there.
206
+
207
+ **Size on iOS and Android comes from the per-part entries, not tree shaking.** Metro takes the CommonJS build there,
208
+ which no tree shaking (Expo's production tree shaking included) can trim: a screen ships and runs every module of the
209
+ entry it imports. So import by part (`/launcher`, `/ask`, `/chat`) to keep the kit's start-up work to the parts a
210
+ screen draws. Tree shaking (webpack, Rollup, esbuild, Expo on the web) applies on the web only, where an import takes
211
+ the ES module build.
212
+
213
+ ## Peer dependencies
214
+
215
+ | Package | Range | Why |
216
+ |---|---|---|
217
+ | `react` | `>=19.1` | hooks over `useSyncExternalStore` |
218
+ | `react-native` | `>=0.81` | the New Architecture (FlashList v2 needs it) |
219
+ | `@shopify/flash-list` | `>=2.3.2 <3` | the chat list: `maintainVisibleContentPosition` with `startRenderingFromBottom` and `autoscrollToBottomThreshold`; the kit is tested on 2.3.2 |
220
+
221
+ ## What you can change, and what you cannot
222
+
223
+ - **Change:** colors (light and dark), font family, radii and spacing (`theme`); every string (`copy`, in en, es and
224
+ pt-BR built in); the header, bubbles, composer, empty state, images, the list (LegendList works) and any card kind
225
+ (`components`, each slot gets the default as `Default` to wrap it); or compose your own layout from the exported
226
+ parts inside `VinaverseChatProvider`, or your own UI on `@vinaverse/react`'s hooks.
227
+ - **Cannot:** remove, cover or reword the AI disclosure (the server's text, always shown under the header); remove
228
+ "Powered by Vinaverse" (v1); show a person from your business as the agent or the agent as a person; call the agent
229
+ an assistant; draw a proposal as done before your backend's outcome, or confirm one without `onProposal`. The kit
230
+ draws these outside every slot.
231
+
232
+ ## Sessions and the app's lifecycle
233
+
234
+ - **The session comes from your backend**, never a fixed secret: `getSession(request)` when the conversation opens and
235
+ on the person's next action after a session ended, and `refreshSecret(sessionId)` for a fresh client secret (on a
236
+ refused secret, 4401, and before reopening after the background). The app never holds a service key.
237
+ - **Background**: the kit tells the conversation when the app goes to the background and comes back (`AppState`).
238
+ By default the connection stays, without pings; `createConversation({ closeInBackground: true, refreshSecret })`
239
+ closes it in the background and opens it again, with a fresh secret, in the foreground. Nothing typed is lost.
240
+ - **A session's end**: pass `expires_at` on from the session your backend created. Past it (the tenant's tool token
241
+ with it), the person's next message gets a new session for the same conversation from `getSession`; the old one is
242
+ never reopened with a fresh secret. A reconnect keeps everything drawn: the session's transcript merges by id.
243
+ - **Modes**: a customer and a professional each get their own conversation, `createConversationsByMode`, and the
244
+ screen draws `conversations.select(mode)`. Each mode keeps its own history on the device, and `getSession` gets the
245
+ `mode` so your backend creates the session for that mode's agent.
246
+ - **Language**: the agent answers in `subject.locale`, which your backend sets when it creates the session; the kit's
247
+ `locale` prop is only for its own words, dates and money (and is passed to `getSession` as a hint you may ignore).
248
+
249
+ ## Handlers
250
+
251
+ | Prop | When |
252
+ |---|---|
253
+ | `onProposal(card, 'confirm' \| 'decline')` | the person decided a proposal: apply it with your backend and the person's token, return the outcome |
254
+ | `onOpenLink(link, card)` | a card's link (it already passed the deep-link rule): route by the page it names |
255
+ | `onCardAction(card, action)` | a card action without a link (also sent to the session) |
256
+ | `onPickPhoto(question, card)` | a photo question: pick, upload to your backend, return `{ upload_ref }` |
257
+ | `failureAction` | `{ label?, onPress(state) }`: a way out on a failed turn and on "couldn't connect" ([When the agent fails](#when-the-agent-fails)) |
258
+ | `onWarning(message, { code })` | warnings for your logger, `code` one of `theme` (hard to read), `copy` (says "assistant"), `send_failed`, `answer_failed`, `proposal_failed`, `photo_failed`, so you count them without the text |
259
+
260
+ ## When the agent fails
261
+
262
+ Two failures need a way out for the person: a turn that failed before any reply ("Something went wrong.") and no
263
+ session to talk to ("Couldn't connect.", with "Try again"). Give one action for both, drawn as a button by the kit's
264
+ own parts, so a person is always the way out:
265
+
266
+ ```tsx
267
+ <VinaverseChat
268
+ conversation={conversation}
269
+ failureAction={{ onPress: (state) => openSupport(state.conversationId) }} // "Talk to a person", in the person's language
270
+ />
271
+ ```
272
+
273
+ `label` replaces "Talk to a person" (the copy's `talkToPerson`, which `copy` can also change). Or replace either part:
274
+ `components.Error` (the failed turn) and `components.Unavailable` (the banner) each get the `state` and the kit's own
275
+ part as `Default`. With neither, the kit draws both as before.
276
+
277
+ | `state` | |
278
+ |---|---|
279
+ | `kind` | `turn_failed` or `unavailable` |
280
+ | `reason` | the turn's error code (`internal`, `timeout`, …), or `unavailable` / `unauthorized` |
281
+ | `retry` | try again, when it can be: the person's text once more, for a turn the server allows to retry (once); a new session for `unavailable`. Undefined otherwise |
282
+ | `conversationId` | the conversation, once a session named it: for your support records |
283
+
284
+ ## Listings, carousels, links and the welcome
285
+
286
+ - **Professionals and spaces** draw as photo cards: the hero photo (through your `Image` slot, so `expo-image`'s cache
287
+ applies), the name's monogram when there is none or it fails, rating with its count, "From $45" and the next open
288
+ time, said as people say it ("Next: tomorrow, 10:00 AM"; `copy.today` and `copy.tomorrow`, else the date). The
289
+ photo frame is `imagePolicy.aspectRatio` (default 1.6); `'natural'` draws a card on its own in its photo's own
290
+ shape, held within `naturalAspect` (square to 2:1 by default), while a card in a carousel keeps the fixed frame.
291
+ - **Bookings, services and every other card** show their `status` (a booking's "Confirmed") first among the badges,
292
+ and a tap opens what the card is about (its `ref`) through `onOpenTarget`, as a listing does.
293
+ - **A carousel** for the cards a turn draws together: add the kit's `CardCarousel`. A turn's cards of one carousel
294
+ group join one row where the first of them is, even with another card between them. Without it they draw one under
295
+ another; it is opt-in so an app that does not use it does not ship it.
296
+
297
+ ```tsx
298
+ import { CardCarousel, VinaverseChat } from '@vinaverse/react-native';
299
+ const components = { Image: ExpoImage, CardGroup: CardCarousel }; // module level: a stable object
300
+ <VinaverseChat conversation={conversation} components={components} onOpenLink={openLink} />;
301
+ ```
302
+
303
+ - **Links**: when your scope keeps the agent from answering, it shows your own help, support, policy, contact or
304
+ feedback pages. Each opens through `onOpenLink` (an https page too: your in-app browser), a deep link only when it
305
+ passes your deep-link prefixes.
306
+ - **The welcome**: pass `welcome` on from the session your backend created; the kit greets an empty conversation with
307
+ it and its chips (no model call). A chip sends its message as the person's own. An `EmptyState` slot gets both.
308
+
309
+ ## The keyboard
310
+
311
+ `keyboardBehavior` keeps the composer above the keyboard: `padding` on iOS by default, nothing on Android (the window
312
+ resizes). An edge-to-edge Android app whose window does not resize passes `padding`; an app that manages the keyboard
313
+ itself passes `none`. This is checked on devices in the release's stabilization phase, not by the unit tests.
314
+
315
+ ## Performance and accessibility
316
+
317
+ A streamed reply re-renders only its own bubble, at most once per frame; a new message renders one row; typing renders
318
+ only the composer (held by render-count tests). The list is anchored at the bottom without being inverted, so screen
319
+ readers read it oldest first; finished replies are announced once; every control has a 44 pt target and a role;
320
+ right-to-left languages, large text and reduced motion are respected.
321
+
322
+ More: [docs/architecture/ui-kits.md](../../docs/architecture/ui-kits.md). An Expo example:
323
+ [examples/expo-chat](../../examples/expo-chat).
324
+
325
+ ## License
326
+
327
+ Apache-2.0
package/dist/ask.cjs ADDED
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ var chunkNMKJ7LIJ_cjs = require('./chunk-NMKJ7LIJ.cjs');
4
+ var chunk6QYDPGRS_cjs = require('./chunk-6QYDPGRS.cjs');
5
+ var agent = require('@vinaverse/react/agent');
6
+
7
+
8
+
9
+ Object.defineProperty(exports, "AskAboutRow", {
10
+ enumerable: true,
11
+ get: function () { return chunkNMKJ7LIJ_cjs.AskAboutRow; }
12
+ });
13
+ Object.defineProperty(exports, "ThemeProvider", {
14
+ enumerable: true,
15
+ get: function () { return chunk6QYDPGRS_cjs.ThemeProvider; }
16
+ });
17
+ Object.defineProperty(exports, "useTheme", {
18
+ enumerable: true,
19
+ get: function () { return chunk6QYDPGRS_cjs.useTheme; }
20
+ });
21
+ Object.defineProperty(exports, "AgentRuntimeProvider", {
22
+ enumerable: true,
23
+ get: function () { return agent.AgentRuntimeProvider; }
24
+ });
25
+ Object.defineProperty(exports, "forgetGreeting", {
26
+ enumerable: true,
27
+ get: function () { return agent.forgetGreeting; }
28
+ });
29
+ Object.defineProperty(exports, "greetedKey", {
30
+ enumerable: true,
31
+ get: function () { return agent.greetedKey; }
32
+ });
33
+ Object.defineProperty(exports, "useAgentConversation", {
34
+ enumerable: true,
35
+ get: function () { return agent.useAgentConversation; }
36
+ });
37
+ Object.defineProperty(exports, "useAgentFeatures", {
38
+ enumerable: true,
39
+ get: function () { return agent.useAgentFeatures; }
40
+ });
41
+ Object.defineProperty(exports, "useAgentLauncher", {
42
+ enumerable: true,
43
+ get: function () { return agent.useAgentLauncher; }
44
+ });
45
+ Object.defineProperty(exports, "useAgentName", {
46
+ enumerable: true,
47
+ get: function () { return agent.useAgentName; }
48
+ });
49
+ Object.defineProperty(exports, "useAgentProfile", {
50
+ enumerable: true,
51
+ get: function () { return agent.useAgentProfile; }
52
+ });
53
+ Object.defineProperty(exports, "useAgentRuntime", {
54
+ enumerable: true,
55
+ get: function () { return agent.useAgentRuntime; }
56
+ });
57
+ Object.defineProperty(exports, "useAgentStatus", {
58
+ enumerable: true,
59
+ get: function () { return agent.useAgentStatus; }
60
+ });
61
+ //# sourceMappingURL=ask.cjs.map
62
+ //# sourceMappingURL=ask.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"ask.cjs","sourcesContent":[]}
package/dist/ask.d.cts ADDED
@@ -0,0 +1,38 @@
1
+ export { AgentRuntimeProvider, GreetingStorage, UseAgentConversation, UseAgentLauncher, forgetGreeting, greetedKey, useAgentConversation, useAgentFeatures, useAgentLauncher, useAgentName, useAgentProfile, useAgentRuntime, useAgentStatus } from '@vinaverse/react/agent';
2
+ import { CopyOverrides } from '@vinaverse/react';
3
+ import { ComponentType, ReactNode } from 'react';
4
+ import { a as ThemeOverrides, C as ColorSchemeSetting } from './theme-BhiEGv64.cjs';
5
+ export { T as Theme, b as ThemeProvider, u as useTheme } from './theme-BhiEGv64.cjs';
6
+ import '@vinaverse/tokens';
7
+
8
+ /** The row's look beyond the theme. */
9
+ interface AskAboutRowAppearance {
10
+ /** A tint behind the row; default the theme's `surface`. */
11
+ readonly background?: string;
12
+ /** The chevron at the row's end. Default true. */
13
+ readonly chevron?: boolean;
14
+ }
15
+ interface AskAboutRowProps {
16
+ /** "Ask Example Agent about Riley Sample". */
17
+ readonly title: string;
18
+ readonly body?: string;
19
+ /** The person asks: open the chat with `entryPoint` (`ask`), so the session says where it was opened from. */
20
+ readonly onPress: (from: {
21
+ readonly entryPoint: 'ask';
22
+ }) => void;
23
+ /** The app's brand mark at the row's start; default the agent name's first letter. */
24
+ readonly Mark?: ComponentType<{
25
+ readonly size: number;
26
+ readonly color: string;
27
+ }>;
28
+ readonly appearance?: AskAboutRowAppearance;
29
+ readonly locale?: string;
30
+ readonly copy?: CopyOverrides;
31
+ readonly theme?: ThemeOverrides;
32
+ readonly colorScheme?: ColorSchemeSetting;
33
+ /** The row's test id. Default `vinaverse-ask-about`. */
34
+ readonly testID?: string;
35
+ }
36
+ declare function AskAboutRow(props: AskAboutRowProps): ReactNode;
37
+
38
+ export { AskAboutRow, type AskAboutRowAppearance, type AskAboutRowProps, ColorSchemeSetting, ThemeOverrides };