@webority/mobile-chat 0.0.0-stage → 0.4.0

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 (71) hide show
  1. package/LICENSE.txt +9 -0
  2. package/README.md +146 -2
  3. package/lib/commonjs/client.js +1 -0
  4. package/lib/commonjs/components/ActionCard.js +1 -0
  5. package/lib/commonjs/components/ChatComposer.js +1 -0
  6. package/lib/commonjs/components/ChatConversationList.js +1 -0
  7. package/lib/commonjs/components/ChatLauncher.js +1 -0
  8. package/lib/commonjs/components/ChatMarkdown.js +1 -0
  9. package/lib/commonjs/components/ChatMessage.js +1 -0
  10. package/lib/commonjs/components/ChatScreen.js +1 -0
  11. package/lib/commonjs/components/ChatThread.js +1 -0
  12. package/lib/commonjs/components/ReplyRating.js +1 -0
  13. package/lib/commonjs/index.js +1 -0
  14. package/lib/commonjs/labels.js +1 -0
  15. package/lib/commonjs/markdownParse.js +1 -0
  16. package/lib/commonjs/package.json +1 -0
  17. package/lib/commonjs/sse.js +1 -0
  18. package/lib/commonjs/types.js +1 -0
  19. package/lib/commonjs/useChat.js +1 -0
  20. package/lib/module/client.js +1 -0
  21. package/lib/module/components/ActionCard.js +1 -0
  22. package/lib/module/components/ChatComposer.js +1 -0
  23. package/lib/module/components/ChatConversationList.js +1 -0
  24. package/lib/module/components/ChatLauncher.js +1 -0
  25. package/lib/module/components/ChatMarkdown.js +1 -0
  26. package/lib/module/components/ChatMessage.js +1 -0
  27. package/lib/module/components/ChatScreen.js +1 -0
  28. package/lib/module/components/ChatThread.js +1 -0
  29. package/lib/module/components/ReplyRating.js +1 -0
  30. package/lib/module/index.js +1 -0
  31. package/lib/module/labels.js +1 -0
  32. package/lib/module/markdownParse.js +1 -0
  33. package/lib/module/package.json +1 -0
  34. package/lib/module/sse.js +1 -0
  35. package/lib/module/types.js +1 -0
  36. package/lib/module/useChat.js +1 -0
  37. package/lib/typescript/commonjs/client.d.ts +73 -0
  38. package/lib/typescript/commonjs/components/ActionCard.d.ts +23 -0
  39. package/lib/typescript/commonjs/components/ChatComposer.d.ts +20 -0
  40. package/lib/typescript/commonjs/components/ChatConversationList.d.ts +23 -0
  41. package/lib/typescript/commonjs/components/ChatLauncher.d.ts +22 -0
  42. package/lib/typescript/commonjs/components/ChatMarkdown.d.ts +23 -0
  43. package/lib/typescript/commonjs/components/ChatMessage.d.ts +35 -0
  44. package/lib/typescript/commonjs/components/ChatScreen.d.ts +30 -0
  45. package/lib/typescript/commonjs/components/ChatThread.d.ts +17 -0
  46. package/lib/typescript/commonjs/components/ReplyRating.d.ts +19 -0
  47. package/lib/typescript/commonjs/index.d.ts +28 -0
  48. package/lib/typescript/commonjs/labels.d.ts +63 -0
  49. package/lib/typescript/commonjs/markdownParse.d.ts +59 -0
  50. package/lib/typescript/commonjs/package.json +1 -0
  51. package/lib/typescript/commonjs/sse.d.ts +23 -0
  52. package/lib/typescript/commonjs/types.d.ts +71 -0
  53. package/lib/typescript/commonjs/useChat.d.ts +63 -0
  54. package/lib/typescript/module/client.d.ts +73 -0
  55. package/lib/typescript/module/components/ActionCard.d.ts +23 -0
  56. package/lib/typescript/module/components/ChatComposer.d.ts +20 -0
  57. package/lib/typescript/module/components/ChatConversationList.d.ts +23 -0
  58. package/lib/typescript/module/components/ChatLauncher.d.ts +22 -0
  59. package/lib/typescript/module/components/ChatMarkdown.d.ts +23 -0
  60. package/lib/typescript/module/components/ChatMessage.d.ts +35 -0
  61. package/lib/typescript/module/components/ChatScreen.d.ts +30 -0
  62. package/lib/typescript/module/components/ChatThread.d.ts +17 -0
  63. package/lib/typescript/module/components/ReplyRating.d.ts +19 -0
  64. package/lib/typescript/module/index.d.ts +28 -0
  65. package/lib/typescript/module/labels.d.ts +63 -0
  66. package/lib/typescript/module/markdownParse.d.ts +59 -0
  67. package/lib/typescript/module/package.json +1 -0
  68. package/lib/typescript/module/sse.d.ts +23 -0
  69. package/lib/typescript/module/types.d.ts +71 -0
  70. package/lib/typescript/module/useChat.d.ts +63 -0
  71. package/package.json +93 -4
package/LICENSE.txt ADDED
@@ -0,0 +1,9 @@
1
+ Copyright (c) Webority Technologies. All rights reserved.
2
+
3
+ This software and its NuGet packages are the proprietary and confidential property
4
+ of Webority Technologies. They are published publicly only for the convenience of
5
+ Webority's own projects and internal consumption.
6
+
7
+ No license or right to use, copy, modify, merge, publish, distribute, sublicense,
8
+ or create derivative works is granted to any third party. Unauthorized use,
9
+ reproduction, or distribution is prohibited.
package/README.md CHANGED
@@ -1,3 +1,147 @@
1
- # Temporary Holding Version
1
+ # @webority/mobile-chat
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
+ An AI assistant for a Webority React Native app: a client that streams the assistant's reply, a
4
+ chat hook with no store of its own, and the chat screen and its parts (the conversation with
5
+ Markdown replies, the question box, past chats with delete, reply ratings, support action cards,
6
+ the questions left today and an optional floating launcher). It talks to the chat endpoints a
7
+ product hosts with `Webority.Ai.Chat.AspNetCore`, the same contract as the web package
8
+ `@webority/chat-react`, and it is built on `@webority/mobile-ui`, so it follows the app's theme.
9
+
10
+ ```bash
11
+ npm install @webority/mobile-chat
12
+ ```
13
+
14
+ ## Peers
15
+
16
+ | Package | Why |
17
+ |---|---|
18
+ | `@webority/mobile-core`, `@webority/mobile-ui` | the HTTP client the app passes in, dates, theme and components |
19
+ | `react-native-keyboard-controller` | the question box rises with the keyboard (mobile-ui needs it too) |
20
+ | `react-native-safe-area-context` | the question box and the launcher clear the home indicator (mobile-ui needs it too) |
21
+
22
+ All are required, and an app on mobile-ui already has them.
23
+
24
+ ## Wire it up
25
+
26
+ Give the client the app's own signed-in axios instance. It already carries the bearer token, the
27
+ tenant header and the refresh on 401, so chat calls sign in like every other call. The package
28
+ never handles auth.
29
+
30
+ ```tsx
31
+ import { userClient } from '@webority/mobile-core';
32
+ import { createChatClient, useChat } from '@webority/mobile-chat';
33
+ import { createSupportClient } from '@webority/mobile-support';
34
+
35
+ const chatClient = createChatClient(userClient, { base: '/api/assistant' });
36
+ const supportClient = createSupportClient(userClient, '/api/support');
37
+
38
+ // Reply links look like [label](scheme:kind/id); map the ones the app knows to a screen.
39
+ const resolveLink = (href: string) => {
40
+ const [scheme, rest] = href.split(':');
41
+ if (scheme === 'ticket') return { screen: 'SupportTicket', params: { id: rest.split('/')[1] } };
42
+ return null;
43
+ };
44
+
45
+ // Above the navigator, so the conversation survives leaving the chat screen.
46
+ export const AssistantProvider = ({ children }) => {
47
+ const chat = useChat({ client: chatClient, pageContext: { screen: currentRouteName() } });
48
+ return <AssistantContext.Provider value={chat}>{children}</AssistantContext.Provider>;
49
+ };
50
+
51
+ // A stack screen.
52
+ export const AssistantScreen = ({ navigation }) => (
53
+ <ChatScreen
54
+ chat={useAssistant()}
55
+ title="Ask the assistant"
56
+ headerActions={<Button title="All chats" size="sm" variant="ghost" onPress={() => navigation.navigate('PastChats')} />}
57
+ resolveLink={resolveLink}
58
+ onLinkPress={(target) => navigation.navigate(target.screen, target.params)}
59
+ confirmAction={supportClient.confirmAction}
60
+ onViewTicket={(ticket) => navigation.navigate('SupportTicket', { id: ticket.id })}
61
+ />
62
+ );
63
+ ```
64
+
65
+ Call `chat.reset()` when the person signs out or switches account, so nothing of one person's chat
66
+ shows to the next. Call `chat.refreshAllowance()` when the chat opens if the allowance should show
67
+ before the first question.
68
+
69
+ ### Streaming
70
+
71
+ The reply streams through axios's XMLHttpRequest adapter, the React Native default: React
72
+ Native's XMLHttpRequest hands over text as it arrives once a progress listener is attached, and
73
+ the client attaches one through `onDownloadProgress`. Incremental delivery on iOS has not yet been
74
+ confirmed on a device; where it buffers, the reply appears whole when it ends. Give the POST a timeout long enough for a
75
+ whole answer (the app's own client may default to a short one). An instance forced onto axios's
76
+ fetch adapter still works, but the reply then appears whole at the end.
77
+
78
+ ## The client
79
+
80
+ `createChatClient(http, { base })` takes an axios instance (`get`, `post`, `put`, `delete`
81
+ resolving to `{ data }`) and the prefix the API maps the chat endpoints under. Every method
82
+ rejects with a `ChatApiError` carrying `code`, `status`, `resetsAtUtc` and a `message` written for
83
+ the person, which the components show as is. `code` is the server's code, `network` when no
84
+ response arrived, `aborted` when the request was cancelled, or `invalid-response`.
85
+
86
+ | Method | Request |
87
+ |---|---|
88
+ | `sendMessage({ message, conversationId, pageContext }, { signal, onEvent })` | `POST {base}/chat`, streamed; resolves with the `done` event |
89
+ | `getAllowance()` | `GET {base}/allowance` |
90
+ | `listConversations({ skip, take })` | `GET {base}/conversations?skip=&take=` |
91
+ | `getConversation(id)` | `GET {base}/conversations/{id}` |
92
+ | `deleteConversation(id)` | `DELETE {base}/conversations/{id}` |
93
+ | `rateMessage(conversationId, messageId, { rating, note })` | `PUT {base}/conversations/{conversationId}/messages/{messageId}/rating` |
94
+
95
+ `onEvent(name, data)` is called with `delta { text }`, `reset {}` and `actions { actions }` as they
96
+ arrive. `createSseParser(onEvent)` is exported too, for reading the same stream elsewhere. The
97
+ server contract (refusal codes, the event stream, the read endpoints) is the one
98
+ `@webority/chat-react` documents.
99
+
100
+ ## The hook
101
+
102
+ `useChat({ client, pageContext })` returns `turns` (`[{ id, question, answer, streaming, error,
103
+ actions, messageId, rating, ratingNote }]`, oldest first), `conversationId`, `messagesLeftToday`,
104
+ `streaming`, `loading`, `loadError`, and `submit(message)`, `openConversation(id)`,
105
+ `newConversation()`, `rate(turnId, rating, note?)`, `refreshAllowance()` and `reset()`.
106
+ `pageContext` is read at the moment each question is sent.
107
+
108
+ An answer still arriving is cancelled on `reset`, `newConversation`, `openConversation` and
109
+ unmount. A failed turn keeps the question, drops the half-streamed text and carries `error`
110
+ (`{ code, message, resetsAtUtc }`). After `daily-limit`, `messagesLeftToday` becomes 0; after
111
+ `conversation-not-found`, the next question starts a new conversation. A question asked while an
112
+ answer is arriving is ignored.
113
+
114
+ ## Components
115
+
116
+ Every component takes `labels`, any subset of `DEFAULT_CHAT_LABELS`. The keys are the web
117
+ package's, so a product passes one label set to both; `composerHint` is not shown on a phone.
118
+
119
+ - **`ChatScreen`**: the whole chat for one navigator screen. `chat` (the `useChat` result),
120
+ `title` (default "Assistant"), `headerActions` (before New chat, e.g. "All chats"), `empty`
121
+ (shown before the first question), `disclaimer` (null hides it), `maxLength` (default 4000),
122
+ `allowanceThreshold` (show the questions left at this many or fewer, default 10), `rating`
123
+ (default true), `resolveLink`, `onLinkPress`, `confirmAction`, `onViewTicket`, `renderReply`.
124
+ New chat is disabled until there is something to clear; the question box is disabled with no
125
+ questions left, and the thread follows the newest text while an answer streams.
126
+ - **`ChatThread`**, **`ChatMessage`**, **`ChatComposer`**: its parts, for an app that lays out its
127
+ own chat. The composer's return key adds a new line; only the send button sends.
128
+ - **`ChatMarkdown`**: renders a reply from a small, safe subset of Markdown: paragraphs, bold,
129
+ italics, inline code, bullet and numbered lists, simple tables (scrolling sideways), headings,
130
+ code blocks and rules. A link shows only when its href has the form `scheme:kind/id` and
131
+ `resolveLink(href)` returns something; pressing it calls `onLinkPress(target)`. A web address
132
+ never opens, and shows its label as plain text.
133
+ - **`ReplyRating`**: Helpful and Not helpful under a stored reply; Not helpful then offers an
134
+ optional note (`noteMaxLength`, default 1000).
135
+ - **`ActionCard`**: one support action under a reply, with Confirm and Not now. It moves through
136
+ sending to done (with View ticket when `onViewTicket` is given); an expired action shows
137
+ without a button. `confirmAction(token)` is mobile-support's `supportClient.confirmAction`.
138
+ - **`ChatConversationList`**: past chats, newest first: `client`, `onOpen(conversation)`,
139
+ `onDeleted(id)`, `pageSize` (default 20). Show more, pull to refresh, delete after a confirm,
140
+ and empty and error states with Try again.
141
+ - **`ChatLauncher`**: an optional floating button (`onPress`, `position` `end` or `start`,
142
+ `bottomOffset` to clear a tab bar, `compact`). Most apps open the chat from a header action or a
143
+ Help row instead.
144
+
145
+ `renderReply(turn, { streaming })` on `ChatScreen`, `ChatThread` or `ChatMessage` draws each
146
+ assistant reply your way (charts, maths); the question, thinking state, errors, action cards and
147
+ rating stay the library's.
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.createChatClient=exports.ChatApiError=void 0;var _sse=require("./sse.js");const FALLBACK_MESSAGE="Something went wrong. Please try again.";class ChatApiError extends Error{constructor(e,t,r,s=null){super(t),this.name="ChatApiError",this.code=e,this.status=r,this.resetsAtUtc=s}}exports.ChatApiError=ChatApiError;const parseBody=e=>{if("string"==typeof e)try{return JSON.parse(e)}catch{return null}return e??null},isCancel=(e,t)=>Boolean(t?.aborted)||"ERR_CANCELED"===e?.code||"CanceledError"===e?.name||"AbortError"===e?.name,toChatApiError=(e,t)=>{if(e instanceof ChatApiError)return e;const r=e;if(isCancel(r,t))return new ChatApiError("aborted","The request was cancelled.");const s=r?.response;if(!s)return new ChatApiError("network",FALLBACK_MESSAGE);const n=parseBody(s.data),a="string"==typeof n?.code&&n.code?n.code:"unknown",o="string"==typeof n?.message&&n.message?n.message:FALLBACK_MESSAGE,i="string"==typeof n?.resetsAtUtc?n.resetsAtUtc:null,c="number"==typeof s.status?s.status:void 0;return new ChatApiError(a,o,c,i)},call=async e=>{try{return(await e()).data}catch(e){throw toChatApiError(e)}},receivedText=e=>{const t=e?.event,r=t?.target;if(r&&"string"==typeof r.responseText&&("number"!=typeof r.status||!(r.status<200||r.status>=300)))return r.responseText},createChatClient=(e,{base:t})=>{if("string"!=typeof t||""===t.trim())throw new TypeError('createChatClient needs a base path, e.g. { base: "/api/assistant" }.');const r=t.replace(/\/+$/,""),conversationUrl=e=>`${r}/conversations/${encodeURIComponent(e)}`;return{sendMessage:async({message:t,conversationId:s,pageContext:n},{signal:a,onEvent:o}={})=>{let i=null,c=null;const l=(0,_sse.createSseParser)(({name:e,data:t})=>{i||c||("done"===e?i=t:"error"===e?c=t??{}:"delta"!==e&&"reset"!==e&&"actions"!==e||o?.(e,t))});let p=0;const read=e=>{"string"!=typeof e||e.length<=p||(l.push(e.slice(p)),p=e.length)},u={message:t};let h;s&&(u.conversationId=s),null!=n&&(u.pageContext=n);try{h=await e.post(`${r}/chat`,u,{responseType:"text",headers:{Accept:"text/event-stream"},signal:a,onDownloadProgress:e=>read(receivedText(e))})}catch(e){throw toChatApiError(e,a)}read(h.data),l.end();const C=c;if(C)throw new ChatApiError(C.code||"unknown",C.message||FALLBACK_MESSAGE);const g=i;if(!g)throw new ChatApiError("invalid-response",FALLBACK_MESSAGE,h.status);return g},getAllowance:()=>call(()=>e.get(`${r}/allowance`)),listConversations:({skip:t,take:s}={})=>{const n=[];return null!=t&&n.push(`skip=${t}`),null!=s&&n.push(`take=${s}`),call(()=>e.get(`${r}/conversations${n.length?`?${n.join("&")}`:""}`))},getConversation:t=>call(()=>e.get(conversationUrl(t))),deleteConversation:async t=>{await call(()=>e.delete(conversationUrl(t)))},rateMessage:async(t,r,{rating:s,note:n})=>{const a="string"==typeof n?n.trim():"";await call(()=>e.put(`${conversationUrl(t)}/messages/${encodeURIComponent(r)}/rating`,{rating:s,note:a||null}))}}};exports.createChatClient=createChatClient;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ActionCard=void 0;var _mobileUi=require("@webority/mobile-ui"),_react=require("react"),_reactNative=require("react-native"),_labels=require("../labels.js"),_jsxRuntime=require("react/jsx-runtime");const titleOf=(e,i)=>"Reply"===e.kind?i.actionReply(e.ticketNumber??""):"Reopen"===e.kind?i.actionReopen(e.ticketNumber??""):i.actionRaiseTicket,doneText=(e,i,t)=>"Reply"===e.kind?t.actionDoneReply(i):"Reopen"===e.kind?t.actionDoneReopen(i):t.actionDoneRaiseTicket(i),ActionCard=({action:e,confirmAction:i,onViewTicket:t,labels:n,testID:s="chat-action"})=>{const a=(0,_labels.mergeLabels)(n),o=(0,_mobileUi.useTheme)(),[r,l]=(0,_react.useState)("ready"),[c,d]=(0,_react.useState)(null),[m,x]=(0,_react.useState)("");if("dismissed"===r)return(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"bodySmall",color:"secondary",testID:s,children:a.actionDismissed});if("done"===r&&c)return(0,_jsxRuntime.jsx)(_mobileUi.Banner,{variant:"success",message:doneText(e,c.number,a),actions:t?[{label:a.viewTicket,onPress:()=>t(c)}]:void 0,testID:s});const u="sending"===r,_=new Date(e.expiresDateTimeUtc).getTime()<=Date.now(),b="RaiseTicket"===e.kind?[e.category,e.subject].filter(Boolean).join(", "):"";return(0,_jsxRuntime.jsx)(_mobileUi.Card,{variant:"outlined",padding:"sm",testID:s,children:(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{gap:o.spacing.sm},children:[(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"label",children:titleOf(e,a)}),""!==b&&(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"bodySmall",color:"secondary",children:b}),"Reopen"!==e.kind&&e.body?(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"bodySmall",children:e.body}):null,_?(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"bodySmall",color:"secondary",children:a.actionExpired}):(0,_jsxRuntime.jsxs)(_jsxRuntime.Fragment,{children:[(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"caption",color:"secondary",children:a.actionNote}),""!==m&&(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"bodySmall",color:"error",accessibilityLiveRegion:"polite",children:m}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{flexDirection:"row",flexWrap:"wrap",gap:o.spacing.sm},children:[(0,_jsxRuntime.jsx)(_mobileUi.Button,{title:a.actionConfirm,size:"sm",loading:u,disabled:u,onPress:()=>{(async()=>{l("sending"),x("");try{d(await i(e.token)),l("done")}catch(e){const i=e?.message;x("string"==typeof i&&i?i:a.actionFailed),l("ready")}})()},testID:`${s}-confirm`}),(0,_jsxRuntime.jsx)(_mobileUi.Button,{title:a.actionDismiss,size:"sm",variant:"outline",disabled:u,onPress:()=>l("dismissed"),testID:`${s}-dismiss`})]})]})]})})};exports.ActionCard=ActionCard;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ChatComposer=void 0;var _mobileUi=require("@webority/mobile-ui"),_react=require("react"),_reactNative=require("react-native"),_labels=require("../labels.js"),_jsxRuntime=require("react/jsx-runtime");const ChatComposer=({onSubmit:e,busy:t=!1,disabled:i=!1,maxLength:s=4e3,labels:r,testID:a="chat-composer"})=>{const l=(0,_labels.mergeLabels)(r),o=(0,_mobileUi.useTheme)(),[n,c]=(0,_react.useState)(""),m=n.trim(),u=t||i||""===m;return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{flexDirection:"row",alignItems:"flex-end",gap:o.spacing.sm},testID:a,children:[(0,_jsxRuntime.jsx)(_reactNative.View,{style:{flex:1},children:(0,_jsxRuntime.jsx)(_mobileUi.Input,{accessibilityLabel:l.composer,placeholder:l.placeholder,value:n,onChangeText:c,multiline:!0,maxLength:s,disabled:i,testID:`${a}-input`})}),(0,_jsxRuntime.jsx)(_mobileUi.IconButton,{icon:"send",accessibilityLabel:l.send,variant:"solid",tone:"primary",disabled:u,loading:t,onPress:()=>{u||(c(""),e(m))},testID:`${a}-send`})]})};exports.ChatComposer=ChatComposer;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ChatConversationList=void 0;var _mobileCore=require("@webority/mobile-core"),_mobileUi=require("@webority/mobile-ui"),_react=require("react"),_reactNative=require("react-native"),_labels=require("../labels.js"),_jsxRuntime=require("react/jsx-runtime");const messageOf=(e,t)=>{const i=e?.message;return"string"==typeof i&&i?i:t},ChatConversationList=({client:e,onOpen:t,onDeleted:i,pageSize:s=20,labels:a,style:n,testID:l="chat-conversations"})=>{const r=(0,_labels.mergeLabels)(a),o=(0,_mobileUi.useTheme)(),[c,m]=(0,_react.useState)([]),[u,_]=(0,_react.useState)(!0),[b,d]=(0,_react.useState)(!1),[x,j]=(0,_react.useState)(""),[g,h]=(0,_react.useState)(!1),[p,v]=(0,_react.useState)(null),[y,f]=(0,_react.useState)(!1),[C,R]=(0,_react.useState)(""),I=r.loadFailed,S=(0,_react.useCallback)(async t=>{_(!0),j("");try{const i=await e.listConversations({skip:t,take:s});m(e=>0===t?i:[...e,...i]),h(i.length===s)}catch(e){j(messageOf(e,I))}finally{_(!1)}},[e,s,I]);(0,_react.useEffect)(()=>{S(0)},[S]);const U=!u&&""===x&&0===c.length;return(0,_jsxRuntime.jsxs)(_reactNative.ScrollView,{style:n,contentContainerStyle:{padding:o.spacing.md,gap:o.spacing.sm,flexGrow:1},refreshControl:(0,_jsxRuntime.jsx)(_reactNative.RefreshControl,{refreshing:b,onRefresh:()=>{(async()=>{d(!0),await S(0),d(!1)})()}}),accessibilityLabel:r.conversations,testID:l,children:[""!==x&&(0,_jsxRuntime.jsx)(_mobileUi.Banner,{variant:"error",message:x,actions:[{label:r.retry,onPress:()=>{S(0)}}],testID:`${l}-error`}),U&&(0,_jsxRuntime.jsx)(_mobileUi.EmptyState,{size:"sm",title:r.conversationsEmptyTitle,description:r.conversationsEmptyText,testID:`${l}-empty`}),c.map(e=>(0,_jsxRuntime.jsx)(_mobileUi.Card,{variant:"outlined",padding:"sm",onPress:()=>t(e),accessibilityLabel:e.title||r.untitled,testID:`${l}-item-${e.publicId}`,children:(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{flexDirection:"row",alignItems:"center",gap:o.spacing.sm},children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{flex:1,minWidth:0},children:[(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"label",numberOfLines:1,children:e.title||r.untitled}),(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"caption",color:"secondary",children:(0,_mobileCore.formatDateTime)(e.lastMessageDateTimeUtc)})]}),(0,_jsxRuntime.jsx)(_mobileUi.IconButton,{icon:"trash",tone:"error",variant:"ghost",accessibilityLabel:r.deleteConversation,onPress:()=>{R(""),v(e)},testID:`${l}-delete-${e.publicId}`})]})},e.publicId)),u&&!b&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{gap:o.spacing.sm},accessibilityElementsHidden:!0,children:[(0,_jsxRuntime.jsx)(_mobileUi.Skeleton,{height:56}),(0,_jsxRuntime.jsx)(_mobileUi.Skeleton,{height:56}),(0,_jsxRuntime.jsx)(_mobileUi.Skeleton,{height:56})]}),!u&&g&&(0,_jsxRuntime.jsx)(_mobileUi.Button,{title:r.loadMore,variant:"outline",onPress:()=>{S(c.length)},testID:`${l}-more`}),(0,_jsxRuntime.jsx)(_mobileUi.Dialog,{visible:null!==p,onClose:()=>{y||v(null)},variant:"warning",title:r.deleteConfirmTitle,message:C||r.deleteConfirmMessage,dismissOnAction:!1,actions:[{label:r.cancel,tone:"neutral",disabled:y,onPress:()=>v(null)},{label:r.deleteConfirm,tone:"error",loading:y,onPress:()=>{(async()=>{if(p){f(!0),R("");try{await e.deleteConversation(p.publicId),m(e=>e.filter(e=>e.publicId!==p.publicId)),i?.(p.publicId),v(null)}catch(e){R(messageOf(e,r.deleteFailed))}finally{f(!1)}}})()}}],testID:`${l}-confirm`})]})};exports.ChatConversationList=ChatConversationList;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ChatLauncher=void 0;var _mobileUi=require("@webority/mobile-ui"),_labels=require("../labels.js"),_jsxRuntime=require("react/jsx-runtime");const ChatLauncher=({onPress:e,position:t="end",bottomOffset:o,compact:s=!1,labels:i,testID:r="chat-launcher"})=>{const a=(0,_labels.mergeLabels)(i),n=(0,_mobileUi.useTheme)();return(0,_jsxRuntime.jsx)(_mobileUi.FloatingActionButton,{icon:(0,_jsxRuntime.jsx)(_mobileUi.AppIcon,{name:"messages",color:n.colors.onPrimary}),label:s?void 0:a.launcher,accessibilityLabel:a.launcher,position:"end"===t?"bottomRight":"bottomLeft",bottomOffset:o,onPress:e,testID:r})};exports.ChatLauncher=ChatLauncher;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ChatMarkdown=ChatMarkdown;var _mobileUi=require("@webority/mobile-ui"),_react=require("react"),_reactNative=require("react-native"),_markdownParse=require("../markdownParse.js"),_jsxRuntime=require("react/jsx-runtime");const MONO=_reactNative.Platform.select({ios:"Menlo",android:"monospace",default:"monospace"}),createStyles=e=>_reactNative.StyleSheet.create({root:{gap:e.spacing.sm},listRow:{flexDirection:"row",gap:e.spacing.xs},marker:{minWidth:18},listText:{flex:1},code:{backgroundColor:e.colors.background.secondary,borderRadius:e.radius.sm,padding:e.spacing.sm},codeText:{fontFamily:MONO,fontSize:e.typography.fontSize.sm},inlineCode:{fontFamily:MONO,backgroundColor:e.colors.background.secondary,color:e.colors.text.primary},link:{color:e.colors.text.link,textDecorationLine:"underline"},strong:{fontWeight:e.typography.fontWeight.bold},em:{fontStyle:"italic"},rule:{height:_reactNative.StyleSheet.hairlineWidth,backgroundColor:e.colors.border.primary},table:{borderWidth:_reactNative.StyleSheet.hairlineWidth,borderColor:e.colors.border.primary},row:{flexDirection:"row"},headerRow:{backgroundColor:e.colors.background.secondary},cell:{minWidth:96,maxWidth:220,padding:e.spacing.xs,borderRightWidth:_reactNative.StyleSheet.hairlineWidth,borderBottomWidth:_reactNative.StyleSheet.hairlineWidth,borderColor:e.colors.border.primary}});function Inline({tokens:e,styles:t,resolveLink:r,onLinkPress:i}){return(0,_jsxRuntime.jsx)(_jsxRuntime.Fragment,{children:e.map(e=>{switch(e.type){case"text":return(0,_jsxRuntime.jsx)(_reactNative.Text,{children:e.text},e.key);case"code":return(0,_jsxRuntime.jsx)(_reactNative.Text,{style:t.inlineCode,children:e.text},e.key);case"strong":case"em":return(0,_jsxRuntime.jsx)(_reactNative.Text,{style:"strong"===e.type?t.strong:t.em,children:(0,_jsxRuntime.jsx)(Inline,{tokens:e.children,styles:t,resolveLink:r,onLinkPress:i})},e.key);default:{const s=i?(0,_markdownParse.linkTarget)(e.href,r):null,n=(0,_jsxRuntime.jsx)(Inline,{tokens:e.children,styles:t});return null!==s&&i?(0,_jsxRuntime.jsx)(_reactNative.Text,{accessibilityRole:"link",style:t.link,onPress:()=>i(s),children:n},e.key):(0,_jsxRuntime.jsx)(_reactNative.Text,{children:n},e.key)}}})})}function ChatMarkdown({text:e,resolveLink:t,onLinkPress:r,testID:i}){const s=(0,_mobileUi.useTheme)(),n=(0,_react.useMemo)(()=>createStyles(s),[s]),o=(0,_react.useMemo)(()=>(0,_markdownParse.parseBlocks)(e),[e]),inline=e=>(0,_jsxRuntime.jsx)(Inline,{tokens:(0,_markdownParse.parseInline)(e),styles:n,resolveLink:t,onLinkPress:r});return(0,_jsxRuntime.jsx)(_reactNative.View,{style:n.root,testID:i,children:o.map(e=>{switch(e.type){case"paragraph":return(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"body",children:inline(e.lines.join("\n"))},e.key);case"heading":return(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"body",weight:"bold",accessibilityRole:"header",children:inline(e.text)},e.key);case"list":return(0,_jsxRuntime.jsx)(_reactNative.View,{children:e.items.map((t,r)=>(0,_jsxRuntime.jsxs)(_reactNative.View,{style:n.listRow,children:[(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"body",style:n.marker,children:e.ordered?`${e.start+r}.`:"•"}),(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"body",style:n.listText,children:inline(t)})]},r))},e.key);case"table":return(0,_jsxRuntime.jsx)(_reactNative.ScrollView,{horizontal:!0,showsHorizontalScrollIndicator:!1,children:(0,_jsxRuntime.jsx)(_reactNative.View,{style:n.table,children:[e.header,...e.rows].map((t,r)=>(0,_jsxRuntime.jsx)(_reactNative.View,{style:[n.row,0===r&&n.headerRow],children:e.header.map((e,i)=>(0,_jsxRuntime.jsx)(_reactNative.View,{style:n.cell,children:(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"bodySmall",weight:0===r?"semibold":void 0,children:inline(t[i]??"")})},i))},r))})},e.key);case"code":return(0,_jsxRuntime.jsx)(_reactNative.ScrollView,{horizontal:!0,style:n.code,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{selectable:!0,style:[n.codeText,{color:s.colors.text.primary}],children:e.text})},e.key);default:return(0,_jsxRuntime.jsx)(_reactNative.View,{style:n.rule},e.key)}})})}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ChatMessage=ChatMessage;var _mobileUi=require("@webority/mobile-ui"),_reactNative=require("react-native"),_labels=require("../labels.js"),_ActionCard=require("./ActionCard.js"),_ChatMarkdown=require("./ChatMarkdown.js"),_ReplyRating=require("./ReplyRating.js"),_jsxRuntime=require("react/jsx-runtime");function ChatMessage({turn:e,onRate:i,resolveLink:s,onLinkPress:t,confirmAction:a,onViewTicket:n,renderReply:r,labels:l,testID:o="chat-message"}){const c=(0,_labels.mergeLabels)(l),m=(0,_mobileUi.useTheme)(),u=e.streaming&&!e.answer;return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{gap:m.spacing.sm},testID:o,children:[""!==e.question&&(0,_jsxRuntime.jsx)(_reactNative.View,{accessible:!0,accessibilityLabel:`${c.you}: ${e.question}`,style:{alignSelf:"flex-end",maxWidth:"85%",backgroundColor:m.colors.primaryMuted,borderRadius:m.radius.lg,paddingHorizontal:m.spacing.md,paddingVertical:m.spacing.sm},children:(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"body",children:e.question})}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{gap:m.spacing.sm},children:[(0,_jsxRuntime.jsx)(_reactNative.View,{accessibilityLabel:u?c.thinking:void 0,accessibilityLiveRegion:"polite",accessibilityState:{busy:e.streaming},children:u?(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{flexDirection:"row",alignItems:"center",gap:m.spacing.sm},children:[(0,_jsxRuntime.jsx)(_mobileUi.Spinner,{size:"sm",accessibilityLabel:c.thinking}),(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"bodySmall",color:"secondary",children:c.thinking})]}):""!==e.answer?r?r(e,{streaming:e.streaming}):(0,_jsxRuntime.jsx)(_ChatMarkdown.ChatMarkdown,{text:e.answer,resolveLink:s,onLinkPress:t}):null}),e.error&&(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"bodySmall",color:"error",accessibilityLiveRegion:"polite",children:e.error.message}),a&&!e.streaming&&e.actions.map(e=>(0,_jsxRuntime.jsx)(_ActionCard.ActionCard,{action:e,confirmAction:a,onViewTicket:n,labels:l},e.token)),i&&e.messageId&&!e.streaming&&(0,_jsxRuntime.jsx)(_ReplyRating.ReplyRating,{rating:e.rating,onRate:(s,t)=>i(e.id,s,t),labels:l})]})]})}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ChatScreen=ChatScreen;var _mobileUi=require("@webority/mobile-ui"),_reactNative=require("react-native"),_reactNativeKeyboardController=require("react-native-keyboard-controller"),_reactNativeSafeAreaContext=require("react-native-safe-area-context"),_labels=require("../labels.js"),_ChatComposer=require("./ChatComposer.js"),_ChatThread=require("./ChatThread.js"),_jsxRuntime=require("react/jsx-runtime");function ChatScreen({chat:e,title:t,headerActions:r,empty:i,disclaimer:a,maxLength:s=4e3,allowanceThreshold:o=10,rating:n=!0,testID:l="chat-screen",...d}){const c=(0,_labels.mergeLabels)(d.labels),m=(0,_mobileUi.useTheme)(),x=(0,_reactNativeSafeAreaContext.useSafeAreaInsets)(),u=e.messagesLeftToday,b=void 0===a?c.disclaimer:a;return(0,_jsxRuntime.jsxs)(_reactNativeKeyboardController.KeyboardAvoidingView,{behavior:"padding",style:{flex:1,backgroundColor:m.colors.background.primary},testID:l,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{flexDirection:"row",alignItems:"center",gap:m.spacing.sm,paddingHorizontal:m.spacing.md,paddingVertical:m.spacing.sm,borderBottomWidth:1,borderBottomColor:m.colors.border.secondary},children:[(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"title",accessibilityRole:"header",style:{flex:1},numberOfLines:1,children:t??c.panelTitle}),r,(0,_jsxRuntime.jsx)(_mobileUi.Button,{title:c.newChat,size:"sm",variant:"soft",leftIcon:"add",disabled:0===e.turns.length&&!e.loading,onPress:e.newConversation,testID:`${l}-new`})]}),e.loading?(0,_jsxRuntime.jsx)(_reactNative.View,{style:{flex:1,alignItems:"center",justifyContent:"center"},children:(0,_jsxRuntime.jsx)(_mobileUi.Spinner,{message:c.loadingConversation,testID:`${l}-loading`})}):(0,_jsxRuntime.jsx)(_ChatThread.ChatThread,{turns:e.turns,style:{flex:1},onRate:n?e.rate:void 0,empty:i??(0,_jsxRuntime.jsx)(_mobileUi.EmptyState,{fill:!0,size:"sm",title:c.emptyTitle,description:c.emptyText,testID:`${l}-empty`}),testID:`${l}-thread`,...d}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{gap:m.spacing.xs,paddingHorizontal:m.spacing.md,paddingTop:m.spacing.sm,paddingBottom:m.spacing.sm+x.bottom,borderTopWidth:1,borderTopColor:m.colors.border.secondary},children:[e.loadError&&(0,_jsxRuntime.jsx)(_mobileUi.Banner,{variant:"error",message:e.loadError.message,testID:`${l}-load-error`}),null!==u&&u<=o&&(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"caption",color:0===u?"error":"secondary",accessibilityLiveRegion:"polite",testID:`${l}-allowance`,children:c.allowance(u)}),(0,_jsxRuntime.jsx)(_ChatComposer.ChatComposer,{onSubmit:t=>{e.submit(t)},busy:e.streaming,disabled:0===u||e.loading,maxLength:s,labels:d.labels,testID:`${l}-composer`}),b?(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"caption",color:"tertiary",testID:`${l}-disclaimer`,children:b}):null]})]})}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ChatThread=ChatThread;var _mobileUi=require("@webority/mobile-ui"),_react=require("react"),_reactNative=require("react-native"),_labels=require("../labels.js"),_ChatMessage=require("./ChatMessage.js"),_jsxRuntime=require("react/jsx-runtime");function ChatThread({turns:e,empty:t,style:a,testID:r="chat-thread",...s}){const i=(0,_mobileUi.useTheme)(),l=(0,_labels.mergeLabels)(s.labels),n=(0,_react.useRef)(null);return(0,_jsxRuntime.jsx)(_reactNative.ScrollView,{ref:n,style:a,accessibilityLabel:l.thread,keyboardShouldPersistTaps:"handled",contentContainerStyle:{padding:i.spacing.md,gap:i.spacing.lg,flexGrow:1},onContentSizeChange:()=>n.current?.scrollToEnd({animated:!0}),testID:r,children:0===e.length?t:e.map(e=>(0,_jsxRuntime.jsx)(_ChatMessage.ChatMessage,{turn:e,testID:`${r}-${e.id}`,...s},e.id))})}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ReplyRating=void 0;var _mobileUi=require("@webority/mobile-ui"),_react=require("react"),_reactNative=require("react-native"),_labels=require("../labels.js"),_jsxRuntime=require("react/jsx-runtime");const ReplyRating=({rating:e=null,onRate:t,noteMaxLength:i=1e3,labels:s,testID:a="chat-rating"})=>{const n=(0,_labels.mergeLabels)(s),l=(0,_mobileUi.useTheme)(),[r,o]=(0,_react.useState)(!1),[c,u]=(0,_react.useState)(!1),[x,m]=(0,_react.useState)(""),[_,d]=(0,_react.useState)(""),[p,b]=(0,_react.useState)(!1),save=async(e,i)=>{o(!0),d("");try{await t(e,i),u("Down"===e&&void 0===i),b("Up"===e||void 0!==i),void 0!==i&&m("")}catch{d(n.ratingFailed)}finally{o(!1)}};return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{gap:l.spacing.sm},testID:a,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{flexDirection:"row",flexWrap:"wrap",alignItems:"center",gap:l.spacing.xs},children:[(0,_jsxRuntime.jsx)(_mobileUi.Button,{title:n.rateUp,size:"sm",leftIcon:"like",variant:"Up"===e?"soft":"ghost",accessibilityState:{selected:"Up"===e,disabled:r},disabled:r,onPress:()=>{save("Up")},testID:`${a}-up`}),(0,_jsxRuntime.jsx)(_mobileUi.Button,{title:n.rateDown,size:"sm",leftIcon:"dislike",variant:"Down"===e?"soft":"ghost",accessibilityState:{selected:"Down"===e,disabled:r},disabled:r,onPress:()=>{save("Down")},testID:`${a}-down`}),p&&!c&&(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"caption",color:"secondary",accessibilityLiveRegion:"polite",children:n.rated})]}),""!==_&&(0,_jsxRuntime.jsx)(_mobileUi.Text,{variant:"bodySmall",color:"error",accessibilityLiveRegion:"polite",children:_}),c&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{gap:l.spacing.sm},children:[(0,_jsxRuntime.jsx)(_mobileUi.Input,{label:n.ratingNote,helperText:n.ratingNoteHint,multiline:!0,maxLength:i,value:x,onChangeText:m,testID:`${a}-note`}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:{flexDirection:"row",gap:l.spacing.sm},children:[(0,_jsxRuntime.jsx)(_mobileUi.Button,{title:n.ratingNoteSend,size:"sm",loading:r,disabled:r,onPress:()=>{save("Down",x)},testID:`${a}-note-send`}),(0,_jsxRuntime.jsx)(_mobileUi.Button,{title:n.ratingNoteSkip,size:"sm",variant:"outline",disabled:r,onPress:()=>{u(!1),b(!0)},testID:`${a}-note-skip`})]})]})]})};exports.ReplyRating=ReplyRating;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"ActionCard",{enumerable:!0,get:function(){return _ActionCard.ActionCard}}),Object.defineProperty(exports,"ChatApiError",{enumerable:!0,get:function(){return _client.ChatApiError}}),Object.defineProperty(exports,"ChatComposer",{enumerable:!0,get:function(){return _ChatComposer.ChatComposer}}),Object.defineProperty(exports,"ChatConversationList",{enumerable:!0,get:function(){return _ChatConversationList.ChatConversationList}}),Object.defineProperty(exports,"ChatLauncher",{enumerable:!0,get:function(){return _ChatLauncher.ChatLauncher}}),Object.defineProperty(exports,"ChatMarkdown",{enumerable:!0,get:function(){return _ChatMarkdown.ChatMarkdown}}),Object.defineProperty(exports,"ChatMessage",{enumerable:!0,get:function(){return _ChatMessage.ChatMessage}}),Object.defineProperty(exports,"ChatScreen",{enumerable:!0,get:function(){return _ChatScreen.ChatScreen}}),Object.defineProperty(exports,"ChatThread",{enumerable:!0,get:function(){return _ChatThread.ChatThread}}),Object.defineProperty(exports,"DEFAULT_CHAT_LABELS",{enumerable:!0,get:function(){return _labels.DEFAULT_CHAT_LABELS}}),Object.defineProperty(exports,"ReplyRating",{enumerable:!0,get:function(){return _ReplyRating.ReplyRating}}),Object.defineProperty(exports,"createChatClient",{enumerable:!0,get:function(){return _client.createChatClient}}),Object.defineProperty(exports,"createSseParser",{enumerable:!0,get:function(){return _sse.createSseParser}}),Object.defineProperty(exports,"useChat",{enumerable:!0,get:function(){return _useChat.useChat}});var _client=require("./client.js"),_ActionCard=require("./components/ActionCard.js"),_ChatComposer=require("./components/ChatComposer.js"),_ChatConversationList=require("./components/ChatConversationList.js"),_ChatLauncher=require("./components/ChatLauncher.js"),_ChatMarkdown=require("./components/ChatMarkdown.js"),_ChatMessage=require("./components/ChatMessage.js"),_ChatScreen=require("./components/ChatScreen.js"),_ChatThread=require("./components/ChatThread.js"),_ReplyRating=require("./components/ReplyRating.js"),_labels=require("./labels.js"),_sse=require("./sse.js"),_useChat=require("./useChat.js");
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.mergeLabels=exports.DEFAULT_CHAT_LABELS=void 0;const DEFAULT_CHAT_LABELS=exports.DEFAULT_CHAT_LABELS=Object.freeze({launcher:"Ask the assistant",panelTitle:"Assistant",newChat:"New chat",close:"Close",thread:"Conversation",you:"You",assistant:"Assistant",thinking:"Thinking",emptyTitle:"How can I help?",emptyText:"Ask a question about what you see on this page.",composer:"Your question",placeholder:"Ask a question",send:"Send",composerHint:"Press Enter to send, Shift and Enter for a new line.",allowance:e=>0===e?"You have no questions left today.":1===e?"You have 1 question left today.":`You have ${e} questions left today.`,disclaimer:"Answers are written by AI and can be wrong. Check anything important.",loadingConversation:"Loading the conversation",rateUp:"Helpful",rateDown:"Not helpful",rated:"Thanks for the feedback.",ratingNote:"What was wrong with this answer?",ratingNoteHint:"Optional.",ratingNoteSend:"Send",ratingNoteSkip:"Skip",ratingFailed:"Your rating did not save. Please try again.",actionRaiseTicket:"New support ticket",actionReply:e=>`Reply on ${e}`,actionReopen:e=>`Reopen ${e}`,actionNote:"Nothing is sent until you confirm.",actionConfirm:"Confirm",actionDismiss:"Not now",actionDismissed:"Not sent.",actionExpired:"This request expired and was not sent. Ask again if you still need it.",actionFailed:"We could not send this. Please try again.",actionDoneRaiseTicket:e=>`Ticket ${e} raised.`,actionDoneReply:e=>`Your reply was added to ${e}.`,actionDoneReopen:e=>`${e} is open again.`,viewTicket:"View ticket",conversations:"Past chats",conversationsEmptyTitle:"No past chats",conversationsEmptyText:"Your conversations with the assistant show here.",untitled:"Untitled chat",deleteConversation:"Delete chat",deleteConfirmTitle:"Delete this chat?",deleteConfirmMessage:"The conversation is removed for good.",deleteConfirm:"Delete",deleteFailed:"We could not delete this chat. Please try again.",cancel:"Cancel",loadMore:"Show more",loadFailed:"We could not load your chats. Please try again.",retry:"Try again"}),mergeLabels=e=>e?{...DEFAULT_CHAT_LABELS,...e}:DEFAULT_CHAT_LABELS;exports.mergeLabels=mergeLabels;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseInline=exports.parseBlocks=exports.linkTarget=void 0;const LINK_HREF=/^[a-z][a-z0-9+.-]*:[a-z0-9_-]+\/[a-z0-9_.~-]+$/i,FENCE=/^\s*```/,BULLET=/^\s*[-*+]\s+(.*)$/,ORDERED=/^\s*(\d{1,9})[.)]\s+(.*)$/,HEADING=/^\s{0,3}#{1,6}\s+(.*?)\s*#*\s*$/,RULE=/^\s*([-*_])(\s*\1){2,}\s*$/,TABLE_SEPARATOR=/^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?\s*$/,isTableRow=e=>e.includes("|"),tableCells=e=>{let t=e.trim();return t.startsWith("|")&&(t=t.slice(1)),t.endsWith("|")&&(t=t.slice(0,-1)),t.split("|").map(e=>e.trim())},startsTable=(e,t)=>isTableRow(e[t])&&t+1<e.length&&TABLE_SEPARATOR.test(e[t+1]),startsBlock=(e,t)=>{const s=e[t];return FENCE.test(s)||BULLET.test(s)||ORDERED.test(s)||HEADING.test(s)||RULE.test(s)||startsTable(e,t)},parseBlocks=e=>{const t=String(e??"").replace(/\r\n?/g,"\n").split("\n"),s=[];let n=0;for(;n<t.length;){const e=t[n];if(!e.trim()){n+=1;continue}if(FENCE.test(e)){const e=[];for(n+=1;n<t.length&&!FENCE.test(t[n]);)e.push(t[n]),n+=1;n+=1,s.push({type:"code",text:e.join("\n")});continue}if(startsTable(t,n)){const i=tableCells(e),r=[];for(n+=2;n<t.length&&t[n].trim()&&isTableRow(t[n]);)r.push(tableCells(t[n])),n+=1;s.push({type:"table",header:i,rows:r});continue}if(RULE.test(e)){s.push({type:"rule"}),n+=1;continue}const i=HEADING.exec(e);if(i){s.push({type:"heading",text:i[1]}),n+=1;continue}const r=ORDERED.exec(e);if(r||BULLET.test(e)){const e=Boolean(r),i=e?ORDERED:BULLET,l=[];for(;n<t.length&&t[n].trim();){const s=i.exec(t[n]);if(s)l.push(e?s[2]:s[1]);else{if(startsBlock(t,n))break;l[l.length-1]+=` ${t[n].trim()}`}n+=1}s.push({type:"list",ordered:e,start:r?Number(r[1]):1,items:l});continue}const l=[e];for(n+=1;n<t.length&&t[n].trim()&&!startsBlock(t,n);)l.push(t[n]),n+=1;s.push({type:"paragraph",lines:l})}return s.map((e,t)=>({...e,key:t}))};exports.parseBlocks=parseBlocks;const isWordChar=e=>Boolean(e)&&/[\p{L}\p{N}]/u.test(e??""),findClose=(e,t,s)=>{let n=e.indexOf(s,t);for(;-1!==n;){const i=e.slice(t,n),r=1===s.length&&(e[n+1]===s||e[n-1]===s),l="_"===s[0]&&isWordChar(e[n+s.length]);if(i.trim()&&!/^\s/.test(i)&&!/\s$/.test(i)&&!r&&!l)return n;n=e.indexOf(s,n+1)}return-1},parseInline=e=>{const t=[];let s="";const flush=()=>{s&&t.push({type:"text",text:s}),s=""};let n=0;for(;n<e.length;){const i=e[n];if("`"===i){const s=e.indexOf("`",n+1);if(s>n+1){flush(),t.push({type:"code",text:e.slice(n+1,s)}),n=s+1;continue}}if(("*"===i||"_"===i)&&e[n+1]===i){const r=i+i,l=findClose(e,n+2,r);if(-1!==l&&("_"!==i||!isWordChar(e[n-1]))){flush(),t.push({type:"strong",children:parseInline(e.slice(n+2,l))}),n=l+2;continue}s+=r,n+=2;continue}if("*"===i||"_"===i){const s=findClose(e,n+1,i);if(-1!==s&&("_"!==i||!isWordChar(e[n-1]))){flush(),t.push({type:"em",children:parseInline(e.slice(n+1,s))}),n=s+1;continue}}if("["===i){const s=/^\[([^\]\n]+)\]\(([^)\s]+)\)/.exec(e.slice(n));if(s){flush(),t.push({type:"link",href:s[2],children:parseInline(s[1])}),n+=s[0].length;continue}}s+=i,n+=1}return flush(),t.map((e,t)=>({...e,key:t}))};exports.parseInline=parseInline;const linkTarget=(e,t)=>t&&LINK_HREF.test(e)?t(e)??null:null;exports.linkTarget=linkTarget;
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.createSseParser=void 0;const createSseParser=e=>{let t="";const dispatch=t=>{let r="message";const s=[];for(const e of t.split("\n")){if(""===e||e.startsWith(":"))continue;const t=e.indexOf(":"),n=-1===t?e:e.slice(0,t);let a=-1===t?"":e.slice(t+1);a.startsWith(" ")&&(a=a.slice(1)),"event"===n?r=a:"data"===n&&s.push(a)}if(0===s.length)return;let n;try{n=JSON.parse(s.join("\n"))}catch{return void e({name:"error",data:{code:"invalid-response",message:null}})}e({name:r,data:n})};return{push(e){if(!e)return;t=(t+e).replace(/\r\n/g,"\n").replace(/\r(?!$)/g,"\n");const r=t.split("\n\n");t=r.pop()??"";for(const e of r)dispatch(e)},end(){const e=t.replace(/\r$/,"");t="",e.trim()&&dispatch(e)}}};exports.createSseParser=createSseParser;
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.useChat=void 0;var _react=require("react");const toError=e=>{const t=e;return{code:"string"==typeof t?.code&&t.code?t.code:"unknown",message:"string"==typeof t?.message&&t.message?t.message:"Something went wrong. Please try again.",resetsAtUtc:"string"==typeof t?.resetsAtUtc?t.resetsAtUtc:null}},parseActions=e=>{if(!e)return[];const t=JSON.parse(e);return Array.isArray(t)?t:[]},emptyTurn=(e,t)=>({id:e,question:t,answer:"",streaming:!1,error:null,actions:[],messageId:null,rating:null,ratingNote:null}),turnsFromConversation=e=>{const t=[];for(const r of e??[]){if("User"===r.role){t.push(emptyTurn(r.publicId,r.content));continue}const e=t[t.length-1],n={answer:r.content,actions:parseActions(r.actionsJson),messageId:r.publicId,rating:r.rating??null,ratingNote:r.ratingNote??null};e&&null===e.messageId&&""===e.answer?Object.assign(e,n):t.push({...emptyTurn(r.publicId,""),...n})}return t},useChat=({client:e,pageContext:t})=>{const[r,n]=(0,_react.useState)([]),[s,a]=(0,_react.useState)(null),[c,o]=(0,_react.useState)(null),[u,l]=(0,_react.useState)(!1),[i,g]=(0,_react.useState)(null),d=(0,_react.useRef)(null),m=(0,_react.useRef)(0),f=(0,_react.useRef)(0),y=(0,_react.useRef)(t);y.current=t;const p=(0,_react.useRef)(null);p.current=s;const _=(0,_react.useRef)(r);_.current=r;const w=(0,_react.useCallback)(()=>{m.current+=1,d.current?.abort(),d.current=null},[]);(0,_react.useEffect)(()=>w,[w]);const C=(0,_react.useCallback)((e,t)=>{n(r=>r.map(r=>r.id===e?{...r,..."function"==typeof t?t(r):t}:r))},[]),b=(0,_react.useCallback)(async t=>{const r="string"==typeof t?t.trim():"";if(!r||d.current)return;const s=m.current,c=new AbortController;d.current=c,f.current+=1;const u=`turn-${f.current}`;n(e=>[...e,{...emptyTurn(u,r),streaming:!0}]);const live=()=>s===m.current&&!c.signal.aborted;try{const t=await e.sendMessage({message:r,conversationId:p.current,pageContext:y.current},{signal:c.signal,onEvent:(e,t)=>{if(!live())return;const r=t;"delta"===e?C(u,e=>({answer:e.answer+(r?.text??"")})):"reset"===e?C(u,{answer:""}):"actions"===e&&C(u,{actions:Array.isArray(r?.actions)?r.actions:[]})}});if(!live())return;C(u,{answer:t.reply,messageId:t.messageId??null,streaming:!1}),p.current=t.conversationId,a(t.conversationId),o(t.messagesLeftToday??null)}catch(e){if(!live())return;const t=toError(e);C(u,{answer:"",actions:[],streaming:!1,error:t}),"daily-limit"===t.code&&o(0),"conversation-not-found"===t.code&&(p.current=null,a(null))}finally{d.current===c&&(d.current=null)}},[e,C]),I=(0,_react.useCallback)(async t=>{w();const r=m.current;n([]),a(null),g(null),l(!0);try{const s=await e.getConversation(t);if(r!==m.current)return;n(turnsFromConversation(s.messages)),a(s.publicId)}catch(e){if(r!==m.current)return;g(toError(e))}finally{r===m.current&&l(!1)}},[w,e]),h=(0,_react.useCallback)(()=>{w(),n([]),a(null),g(null),l(!1)},[w]),v=(0,_react.useCallback)(()=>{w(),n([]),a(null),o(null),g(null),l(!1)},[w]),A=(0,_react.useCallback)(async()=>{const t=m.current,r=await e.getAllowance();t===m.current&&o(r?.messagesLeftToday??null)},[e]),k=(0,_react.useCallback)(async(t,r,n)=>{const s=_.current.find(e=>e.id===t),a=p.current;if(!s?.messageId||!a)throw new Error("Only a stored reply can be rated.");const c={rating:s.rating,ratingNote:s.ratingNote},o="string"==typeof n&&n.trim()?n.trim():null;C(t,{rating:r,ratingNote:o});try{await e.rateMessage(a,s.messageId,{rating:r,note:o})}catch(e){throw C(t,c),e}},[e,C]);return{turns:r,conversationId:s,messagesLeftToday:c,streaming:r.some(e=>e.streaming),loading:u,loadError:i,submit:b,openConversation:I,newConversation:h,rate:k,refreshAllowance:A,reset:v}};exports.useChat=useChat;
@@ -0,0 +1 @@
1
+ "use strict";import{createSseParser}from"./sse.js";const FALLBACK_MESSAGE="Something went wrong. Please try again.";export class ChatApiError extends Error{constructor(e,t,r,s=null){super(t),this.name="ChatApiError",this.code=e,this.status=r,this.resetsAtUtc=s}}const parseBody=e=>{if("string"==typeof e)try{return JSON.parse(e)}catch{return null}return e??null},isCancel=(e,t)=>Boolean(t?.aborted)||"ERR_CANCELED"===e?.code||"CanceledError"===e?.name||"AbortError"===e?.name,toChatApiError=(e,t)=>{if(e instanceof ChatApiError)return e;const r=e;if(isCancel(r,t))return new ChatApiError("aborted","The request was cancelled.");const s=r?.response;if(!s)return new ChatApiError("network",FALLBACK_MESSAGE);const n=parseBody(s.data),a="string"==typeof n?.code&&n.code?n.code:"unknown",o="string"==typeof n?.message&&n.message?n.message:FALLBACK_MESSAGE,i="string"==typeof n?.resetsAtUtc?n.resetsAtUtc:null,c="number"==typeof s.status?s.status:void 0;return new ChatApiError(a,o,c,i)},call=async e=>{try{return(await e()).data}catch(e){throw toChatApiError(e)}},receivedText=e=>{const t=e?.event,r=t?.target;if(r&&"string"==typeof r.responseText&&("number"!=typeof r.status||!(r.status<200||r.status>=300)))return r.responseText};export const createChatClient=(e,{base:t})=>{if("string"!=typeof t||""===t.trim())throw new TypeError('createChatClient needs a base path, e.g. { base: "/api/assistant" }.');const r=t.replace(/\/+$/,""),conversationUrl=e=>`${r}/conversations/${encodeURIComponent(e)}`;return{sendMessage:async({message:t,conversationId:s,pageContext:n},{signal:a,onEvent:o}={})=>{let i=null,c=null;const l=createSseParser(({name:e,data:t})=>{i||c||("done"===e?i=t:"error"===e?c=t??{}:"delta"!==e&&"reset"!==e&&"actions"!==e||o?.(e,t))});let p=0;const read=e=>{"string"!=typeof e||e.length<=p||(l.push(e.slice(p)),p=e.length)},u={message:t};let g;s&&(u.conversationId=s),null!=n&&(u.pageContext=n);try{g=await e.post(`${r}/chat`,u,{responseType:"text",headers:{Accept:"text/event-stream"},signal:a,onDownloadProgress:e=>read(receivedText(e))})}catch(e){throw toChatApiError(e,a)}read(g.data),l.end();const h=c;if(h)throw new ChatApiError(h.code||"unknown",h.message||FALLBACK_MESSAGE);const d=i;if(!d)throw new ChatApiError("invalid-response",FALLBACK_MESSAGE,g.status);return d},getAllowance:()=>call(()=>e.get(`${r}/allowance`)),listConversations:({skip:t,take:s}={})=>{const n=[];return null!=t&&n.push(`skip=${t}`),null!=s&&n.push(`take=${s}`),call(()=>e.get(`${r}/conversations${n.length?`?${n.join("&")}`:""}`))},getConversation:t=>call(()=>e.get(conversationUrl(t))),deleteConversation:async t=>{await call(()=>e.delete(conversationUrl(t)))},rateMessage:async(t,r,{rating:s,note:n})=>{const a="string"==typeof n?n.trim():"";await call(()=>e.put(`${conversationUrl(t)}/messages/${encodeURIComponent(r)}/rating`,{rating:s,note:a||null}))}}};
@@ -0,0 +1 @@
1
+ "use strict";import{Banner,Button,Card,Text,useTheme}from"@webority/mobile-ui";import{useState}from"react";import{View}from"react-native";import{mergeLabels}from"../labels.js";import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";const titleOf=(e,t)=>"Reply"===e.kind?t.actionReply(e.ticketNumber??""):"Reopen"===e.kind?t.actionReopen(e.ticketNumber??""):t.actionRaiseTicket,doneText=(e,t,i)=>"Reply"===e.kind?i.actionDoneReply(t):"Reopen"===e.kind?i.actionDoneReopen(t):i.actionDoneRaiseTicket(t);export const ActionCard=({action:e,confirmAction:t,onViewTicket:i,labels:n,testID:s="chat-action"})=>{const a=mergeLabels(n),o=useTheme(),[r,c]=useState("ready"),[l,d]=useState(null),[m,x]=useState("");if("dismissed"===r)return _jsx(Text,{variant:"bodySmall",color:"secondary",testID:s,children:a.actionDismissed});if("done"===r&&l)return _jsx(Banner,{variant:"success",message:doneText(e,l.number,a),actions:i?[{label:a.viewTicket,onPress:()=>i(l)}]:void 0,testID:s});const p="sending"===r,y=new Date(e.expiresDateTimeUtc).getTime()<=Date.now(),u="RaiseTicket"===e.kind?[e.category,e.subject].filter(Boolean).join(", "):"";return _jsx(Card,{variant:"outlined",padding:"sm",testID:s,children:_jsxs(View,{style:{gap:o.spacing.sm},children:[_jsx(Text,{variant:"label",children:titleOf(e,a)}),""!==u&&_jsx(Text,{variant:"bodySmall",color:"secondary",children:u}),"Reopen"!==e.kind&&e.body?_jsx(Text,{variant:"bodySmall",children:e.body}):null,y?_jsx(Text,{variant:"bodySmall",color:"secondary",children:a.actionExpired}):_jsxs(_Fragment,{children:[_jsx(Text,{variant:"caption",color:"secondary",children:a.actionNote}),""!==m&&_jsx(Text,{variant:"bodySmall",color:"error",accessibilityLiveRegion:"polite",children:m}),_jsxs(View,{style:{flexDirection:"row",flexWrap:"wrap",gap:o.spacing.sm},children:[_jsx(Button,{title:a.actionConfirm,size:"sm",loading:p,disabled:p,onPress:()=>{(async()=>{c("sending"),x("");try{d(await t(e.token)),c("done")}catch(e){const t=e?.message;x("string"==typeof t&&t?t:a.actionFailed),c("ready")}})()},testID:`${s}-confirm`}),_jsx(Button,{title:a.actionDismiss,size:"sm",variant:"outline",disabled:p,onPress:()=>c("dismissed"),testID:`${s}-dismiss`})]})]})]})})};
@@ -0,0 +1 @@
1
+ "use strict";import{IconButton,Input,useTheme}from"@webority/mobile-ui";import{useState}from"react";import{View}from"react-native";import{mergeLabels}from"../labels.js";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export const ChatComposer=({onSubmit:e,busy:s=!1,disabled:t=!1,maxLength:i=4e3,labels:o,testID:r="chat-composer"})=>{const a=mergeLabels(o),n=useTheme(),[l,m]=useState(""),c=l.trim(),p=s||t||""===c;return _jsxs(View,{style:{flexDirection:"row",alignItems:"flex-end",gap:n.spacing.sm},testID:r,children:[_jsx(View,{style:{flex:1},children:_jsx(Input,{accessibilityLabel:a.composer,placeholder:a.placeholder,value:l,onChangeText:m,multiline:!0,maxLength:i,disabled:t,testID:`${r}-input`})}),_jsx(IconButton,{icon:"send",accessibilityLabel:a.send,variant:"solid",tone:"primary",disabled:p,loading:s,onPress:()=>{p||(m(""),e(c))},testID:`${r}-send`})]})};
@@ -0,0 +1 @@
1
+ "use strict";import{formatDateTime}from"@webority/mobile-core";import{Banner,Button,Card,Dialog,EmptyState,IconButton,Skeleton,Text,useTheme}from"@webority/mobile-ui";import{useCallback,useEffect,useState}from"react";import{RefreshControl,ScrollView,View}from"react-native";import{mergeLabels}from"../labels.js";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";const messageOf=(e,t)=>{const s=e?.message;return"string"==typeof s&&s?s:t};export const ChatConversationList=({client:e,onOpen:t,onDeleted:s,pageSize:i=20,labels:n,style:a,testID:l="chat-conversations"})=>{const r=mergeLabels(n),o=useTheme(),[c,m]=useState([]),[d,u]=useState(!0),[p,g]=useState(!1),[f,b]=useState(""),[h,x]=useState(!1),[y,j]=useState(null),[S,I]=useState(!1),[_,v]=useState(""),C=r.loadFailed,D=useCallback(async t=>{u(!0),b("");try{const s=await e.listConversations({skip:t,take:i});m(e=>0===t?s:[...e,...s]),x(s.length===i)}catch(e){b(messageOf(e,C))}finally{u(!1)}},[e,i,C]);useEffect(()=>{D(0)},[D]);const w=!d&&""===f&&0===c.length;return _jsxs(ScrollView,{style:a,contentContainerStyle:{padding:o.spacing.md,gap:o.spacing.sm,flexGrow:1},refreshControl:_jsx(RefreshControl,{refreshing:p,onRefresh:()=>{(async()=>{g(!0),await D(0),g(!1)})()}}),accessibilityLabel:r.conversations,testID:l,children:[""!==f&&_jsx(Banner,{variant:"error",message:f,actions:[{label:r.retry,onPress:()=>{D(0)}}],testID:`${l}-error`}),w&&_jsx(EmptyState,{size:"sm",title:r.conversationsEmptyTitle,description:r.conversationsEmptyText,testID:`${l}-empty`}),c.map(e=>_jsx(Card,{variant:"outlined",padding:"sm",onPress:()=>t(e),accessibilityLabel:e.title||r.untitled,testID:`${l}-item-${e.publicId}`,children:_jsxs(View,{style:{flexDirection:"row",alignItems:"center",gap:o.spacing.sm},children:[_jsxs(View,{style:{flex:1,minWidth:0},children:[_jsx(Text,{variant:"label",numberOfLines:1,children:e.title||r.untitled}),_jsx(Text,{variant:"caption",color:"secondary",children:formatDateTime(e.lastMessageDateTimeUtc)})]}),_jsx(IconButton,{icon:"trash",tone:"error",variant:"ghost",accessibilityLabel:r.deleteConversation,onPress:()=>{v(""),j(e)},testID:`${l}-delete-${e.publicId}`})]})},e.publicId)),d&&!p&&_jsxs(View,{style:{gap:o.spacing.sm},accessibilityElementsHidden:!0,children:[_jsx(Skeleton,{height:56}),_jsx(Skeleton,{height:56}),_jsx(Skeleton,{height:56})]}),!d&&h&&_jsx(Button,{title:r.loadMore,variant:"outline",onPress:()=>{D(c.length)},testID:`${l}-more`}),_jsx(Dialog,{visible:null!==y,onClose:()=>{S||j(null)},variant:"warning",title:r.deleteConfirmTitle,message:_||r.deleteConfirmMessage,dismissOnAction:!1,actions:[{label:r.cancel,tone:"neutral",disabled:S,onPress:()=>j(null)},{label:r.deleteConfirm,tone:"error",loading:S,onPress:()=>{(async()=>{if(y){I(!0),v("");try{await e.deleteConversation(y.publicId),m(e=>e.filter(e=>e.publicId!==y.publicId)),s?.(y.publicId),j(null)}catch(e){v(messageOf(e,r.deleteFailed))}finally{I(!1)}}})()}}],testID:`${l}-confirm`})]})};
@@ -0,0 +1 @@
1
+ "use strict";import{AppIcon,FloatingActionButton,useTheme}from"@webority/mobile-ui";import{mergeLabels}from"../labels.js";import{jsx as _jsx}from"react/jsx-runtime";export const ChatLauncher=({onPress:t,position:o="end",bottomOffset:e,compact:s=!1,labels:n,testID:r="chat-launcher"})=>{const i=mergeLabels(n),a=useTheme();return _jsx(FloatingActionButton,{icon:_jsx(AppIcon,{name:"messages",color:a.colors.onPrimary}),label:s?void 0:i.launcher,accessibilityLabel:i.launcher,position:"end"===o?"bottomRight":"bottomLeft",bottomOffset:e,onPress:t,testID:r})};
@@ -0,0 +1 @@
1
+ "use strict";import{Text,useTheme}from"@webority/mobile-ui";import{useMemo}from"react";import{Platform,Text as RNText,ScrollView,StyleSheet,View}from"react-native";import{linkTarget,parseBlocks,parseInline}from"../markdownParse.js";import{jsx as _jsx,Fragment as _Fragment,jsxs as _jsxs}from"react/jsx-runtime";const MONO=Platform.select({ios:"Menlo",android:"monospace",default:"monospace"}),createStyles=e=>StyleSheet.create({root:{gap:e.spacing.sm},listRow:{flexDirection:"row",gap:e.spacing.xs},marker:{minWidth:18},listText:{flex:1},code:{backgroundColor:e.colors.background.secondary,borderRadius:e.radius.sm,padding:e.spacing.sm},codeText:{fontFamily:MONO,fontSize:e.typography.fontSize.sm},inlineCode:{fontFamily:MONO,backgroundColor:e.colors.background.secondary,color:e.colors.text.primary},link:{color:e.colors.text.link,textDecorationLine:"underline"},strong:{fontWeight:e.typography.fontWeight.bold},em:{fontStyle:"italic"},rule:{height:StyleSheet.hairlineWidth,backgroundColor:e.colors.border.primary},table:{borderWidth:StyleSheet.hairlineWidth,borderColor:e.colors.border.primary},row:{flexDirection:"row"},headerRow:{backgroundColor:e.colors.background.secondary},cell:{minWidth:96,maxWidth:220,padding:e.spacing.xs,borderRightWidth:StyleSheet.hairlineWidth,borderBottomWidth:StyleSheet.hairlineWidth,borderColor:e.colors.border.primary}});function Inline({tokens:e,styles:r,resolveLink:t,onLinkPress:o}){return _jsx(_Fragment,{children:e.map(e=>{switch(e.type){case"text":return _jsx(RNText,{children:e.text},e.key);case"code":return _jsx(RNText,{style:r.inlineCode,children:e.text},e.key);case"strong":case"em":return _jsx(RNText,{style:"strong"===e.type?r.strong:r.em,children:_jsx(Inline,{tokens:e.children,styles:r,resolveLink:t,onLinkPress:o})},e.key);default:{const s=o?linkTarget(e.href,t):null,i=_jsx(Inline,{tokens:e.children,styles:r});return _jsx(RNText,null!==s&&o?{accessibilityRole:"link",style:r.link,onPress:()=>o(s),children:i}:{children:i},e.key)}}})})}export function ChatMarkdown({text:e,resolveLink:r,onLinkPress:t,testID:o}){const s=useTheme(),i=useMemo(()=>createStyles(s),[s]),l=useMemo(()=>parseBlocks(e),[e]),inline=e=>_jsx(Inline,{tokens:parseInline(e),styles:i,resolveLink:r,onLinkPress:t});return _jsx(View,{style:i.root,testID:o,children:l.map(e=>{switch(e.type){case"paragraph":return _jsx(Text,{variant:"body",children:inline(e.lines.join("\n"))},e.key);case"heading":return _jsx(Text,{variant:"body",weight:"bold",accessibilityRole:"header",children:inline(e.text)},e.key);case"list":return _jsx(View,{children:e.items.map((r,t)=>_jsxs(View,{style:i.listRow,children:[_jsx(Text,{variant:"body",style:i.marker,children:e.ordered?`${e.start+t}.`:"•"}),_jsx(Text,{variant:"body",style:i.listText,children:inline(r)})]},t))},e.key);case"table":return _jsx(ScrollView,{horizontal:!0,showsHorizontalScrollIndicator:!1,children:_jsx(View,{style:i.table,children:[e.header,...e.rows].map((r,t)=>_jsx(View,{style:[i.row,0===t&&i.headerRow],children:e.header.map((e,o)=>_jsx(View,{style:i.cell,children:_jsx(Text,{variant:"bodySmall",weight:0===t?"semibold":void 0,children:inline(r[o]??"")})},o))},t))})},e.key);case"code":return _jsx(ScrollView,{horizontal:!0,style:i.code,children:_jsx(RNText,{selectable:!0,style:[i.codeText,{color:s.colors.text.primary}],children:e.text})},e.key);default:return _jsx(View,{style:i.rule},e.key)}})})}
@@ -0,0 +1 @@
1
+ "use strict";import{Spinner,Text,useTheme}from"@webority/mobile-ui";import{View}from"react-native";import{mergeLabels}from"../labels.js";import{ActionCard}from"./ActionCard.js";import{ChatMarkdown}from"./ChatMarkdown.js";import{ReplyRating}from"./ReplyRating.js";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export function ChatMessage({turn:e,onRate:i,resolveLink:s,onLinkPress:r,confirmAction:n,onViewTicket:t,renderReply:a,labels:o,testID:l="chat-message"}){const c=mergeLabels(o),m=useTheme(),g=e.streaming&&!e.answer;return _jsxs(View,{style:{gap:m.spacing.sm},testID:l,children:[""!==e.question&&_jsx(View,{accessible:!0,accessibilityLabel:`${c.you}: ${e.question}`,style:{alignSelf:"flex-end",maxWidth:"85%",backgroundColor:m.colors.primaryMuted,borderRadius:m.radius.lg,paddingHorizontal:m.spacing.md,paddingVertical:m.spacing.sm},children:_jsx(Text,{variant:"body",children:e.question})}),_jsxs(View,{style:{gap:m.spacing.sm},children:[_jsx(View,{accessibilityLabel:g?c.thinking:void 0,accessibilityLiveRegion:"polite",accessibilityState:{busy:e.streaming},children:g?_jsxs(View,{style:{flexDirection:"row",alignItems:"center",gap:m.spacing.sm},children:[_jsx(Spinner,{size:"sm",accessibilityLabel:c.thinking}),_jsx(Text,{variant:"bodySmall",color:"secondary",children:c.thinking})]}):""!==e.answer?a?a(e,{streaming:e.streaming}):_jsx(ChatMarkdown,{text:e.answer,resolveLink:s,onLinkPress:r}):null}),e.error&&_jsx(Text,{variant:"bodySmall",color:"error",accessibilityLiveRegion:"polite",children:e.error.message}),n&&!e.streaming&&e.actions.map(e=>_jsx(ActionCard,{action:e,confirmAction:n,onViewTicket:t,labels:o},e.token)),i&&e.messageId&&!e.streaming&&_jsx(ReplyRating,{rating:e.rating,onRate:(s,r)=>i(e.id,s,r),labels:o})]})]})}
@@ -0,0 +1 @@
1
+ "use strict";import{Banner,Button,EmptyState,Spinner,Text,useTheme}from"@webority/mobile-ui";import{View}from"react-native";import{KeyboardAvoidingView}from"react-native-keyboard-controller";import{useSafeAreaInsets}from"react-native-safe-area-context";import{mergeLabels}from"../labels.js";import{ChatComposer}from"./ChatComposer.js";import{ChatThread}from"./ChatThread.js";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export function ChatScreen({chat:e,title:t,headerActions:r,empty:s,disclaimer:a,maxLength:o=4e3,allowanceThreshold:i=10,rating:n=!0,testID:l="chat-screen",...d}){const m=mergeLabels(d.labels),c=useTheme(),p=useSafeAreaInsets(),g=e.messagesLeftToday,h=void 0===a?m.disclaimer:a;return _jsxs(KeyboardAvoidingView,{behavior:"padding",style:{flex:1,backgroundColor:c.colors.background.primary},testID:l,children:[_jsxs(View,{style:{flexDirection:"row",alignItems:"center",gap:c.spacing.sm,paddingHorizontal:c.spacing.md,paddingVertical:c.spacing.sm,borderBottomWidth:1,borderBottomColor:c.colors.border.secondary},children:[_jsx(Text,{variant:"title",accessibilityRole:"header",style:{flex:1},numberOfLines:1,children:t??m.panelTitle}),r,_jsx(Button,{title:m.newChat,size:"sm",variant:"soft",leftIcon:"add",disabled:0===e.turns.length&&!e.loading,onPress:e.newConversation,testID:`${l}-new`})]}),e.loading?_jsx(View,{style:{flex:1,alignItems:"center",justifyContent:"center"},children:_jsx(Spinner,{message:m.loadingConversation,testID:`${l}-loading`})}):_jsx(ChatThread,{turns:e.turns,style:{flex:1},onRate:n?e.rate:void 0,empty:s??_jsx(EmptyState,{fill:!0,size:"sm",title:m.emptyTitle,description:m.emptyText,testID:`${l}-empty`}),testID:`${l}-thread`,...d}),_jsxs(View,{style:{gap:c.spacing.xs,paddingHorizontal:c.spacing.md,paddingTop:c.spacing.sm,paddingBottom:c.spacing.sm+p.bottom,borderTopWidth:1,borderTopColor:c.colors.border.secondary},children:[e.loadError&&_jsx(Banner,{variant:"error",message:e.loadError.message,testID:`${l}-load-error`}),null!==g&&g<=i&&_jsx(Text,{variant:"caption",color:0===g?"error":"secondary",accessibilityLiveRegion:"polite",testID:`${l}-allowance`,children:m.allowance(g)}),_jsx(ChatComposer,{onSubmit:t=>{e.submit(t)},busy:e.streaming,disabled:0===g||e.loading,maxLength:o,labels:d.labels,testID:`${l}-composer`}),h?_jsx(Text,{variant:"caption",color:"tertiary",testID:`${l}-disclaimer`,children:h}):null]})]})}
@@ -0,0 +1 @@
1
+ "use strict";import{useTheme}from"@webority/mobile-ui";import{useRef}from"react";import{ScrollView}from"react-native";import{mergeLabels}from"../labels.js";import{ChatMessage}from"./ChatMessage.js";import{jsx as _jsx}from"react/jsx-runtime";export function ChatThread({turns:e,empty:t,style:r,testID:s="chat-thread",...a}){const i=useTheme(),o=mergeLabels(a.labels),l=useRef(null);return _jsx(ScrollView,{ref:l,style:r,accessibilityLabel:o.thread,keyboardShouldPersistTaps:"handled",contentContainerStyle:{padding:i.spacing.md,gap:i.spacing.lg,flexGrow:1},onContentSizeChange:()=>l.current?.scrollToEnd({animated:!0}),testID:s,children:0===e.length?t:e.map(e=>_jsx(ChatMessage,{turn:e,testID:`${s}-${e.id}`,...a},e.id))})}
@@ -0,0 +1 @@
1
+ "use strict";import{Button,Input,Text,useTheme}from"@webority/mobile-ui";import{useState}from"react";import{View}from"react-native";import{mergeLabels}from"../labels.js";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export const ReplyRating=({rating:e=null,onRate:t,noteMaxLength:s=1e3,labels:i,testID:a="chat-rating"})=>{const n=mergeLabels(i),o=useTheme(),[l,r]=useState(!1),[c,d]=useState(!1),[p,x]=useState(""),[g,m]=useState(""),[u,b]=useState(!1),save=async(e,s)=>{r(!0),m("");try{await t(e,s),d("Down"===e&&void 0===s),b("Up"===e||void 0!==s),void 0!==s&&x("")}catch{m(n.ratingFailed)}finally{r(!1)}};return _jsxs(View,{style:{gap:o.spacing.sm},testID:a,children:[_jsxs(View,{style:{flexDirection:"row",flexWrap:"wrap",alignItems:"center",gap:o.spacing.xs},children:[_jsx(Button,{title:n.rateUp,size:"sm",leftIcon:"like",variant:"Up"===e?"soft":"ghost",accessibilityState:{selected:"Up"===e,disabled:l},disabled:l,onPress:()=>{save("Up")},testID:`${a}-up`}),_jsx(Button,{title:n.rateDown,size:"sm",leftIcon:"dislike",variant:"Down"===e?"soft":"ghost",accessibilityState:{selected:"Down"===e,disabled:l},disabled:l,onPress:()=>{save("Down")},testID:`${a}-down`}),u&&!c&&_jsx(Text,{variant:"caption",color:"secondary",accessibilityLiveRegion:"polite",children:n.rated})]}),""!==g&&_jsx(Text,{variant:"bodySmall",color:"error",accessibilityLiveRegion:"polite",children:g}),c&&_jsxs(View,{style:{gap:o.spacing.sm},children:[_jsx(Input,{label:n.ratingNote,helperText:n.ratingNoteHint,multiline:!0,maxLength:s,value:p,onChangeText:x,testID:`${a}-note`}),_jsxs(View,{style:{flexDirection:"row",gap:o.spacing.sm},children:[_jsx(Button,{title:n.ratingNoteSend,size:"sm",loading:l,disabled:l,onPress:()=>{save("Down",p)},testID:`${a}-note-send`}),_jsx(Button,{title:n.ratingNoteSkip,size:"sm",variant:"outline",disabled:l,onPress:()=>{d(!1),b(!0)},testID:`${a}-note-skip`})]})]})]})};
@@ -0,0 +1 @@
1
+ "use strict";export{ChatApiError,createChatClient}from"./client.js";export{ActionCard}from"./components/ActionCard.js";export{ChatComposer}from"./components/ChatComposer.js";export{ChatConversationList}from"./components/ChatConversationList.js";export{ChatLauncher}from"./components/ChatLauncher.js";export{ChatMarkdown}from"./components/ChatMarkdown.js";export{ChatMessage}from"./components/ChatMessage.js";export{ChatScreen}from"./components/ChatScreen.js";export{ChatThread}from"./components/ChatThread.js";export{ReplyRating}from"./components/ReplyRating.js";export{DEFAULT_CHAT_LABELS}from"./labels.js";export{createSseParser}from"./sse.js";export{useChat}from"./useChat.js";
@@ -0,0 +1 @@
1
+ "use strict";export const DEFAULT_CHAT_LABELS=Object.freeze({launcher:"Ask the assistant",panelTitle:"Assistant",newChat:"New chat",close:"Close",thread:"Conversation",you:"You",assistant:"Assistant",thinking:"Thinking",emptyTitle:"How can I help?",emptyText:"Ask a question about what you see on this page.",composer:"Your question",placeholder:"Ask a question",send:"Send",composerHint:"Press Enter to send, Shift and Enter for a new line.",allowance:e=>0===e?"You have no questions left today.":1===e?"You have 1 question left today.":`You have ${e} questions left today.`,disclaimer:"Answers are written by AI and can be wrong. Check anything important.",loadingConversation:"Loading the conversation",rateUp:"Helpful",rateDown:"Not helpful",rated:"Thanks for the feedback.",ratingNote:"What was wrong with this answer?",ratingNoteHint:"Optional.",ratingNoteSend:"Send",ratingNoteSkip:"Skip",ratingFailed:"Your rating did not save. Please try again.",actionRaiseTicket:"New support ticket",actionReply:e=>`Reply on ${e}`,actionReopen:e=>`Reopen ${e}`,actionNote:"Nothing is sent until you confirm.",actionConfirm:"Confirm",actionDismiss:"Not now",actionDismissed:"Not sent.",actionExpired:"This request expired and was not sent. Ask again if you still need it.",actionFailed:"We could not send this. Please try again.",actionDoneRaiseTicket:e=>`Ticket ${e} raised.`,actionDoneReply:e=>`Your reply was added to ${e}.`,actionDoneReopen:e=>`${e} is open again.`,viewTicket:"View ticket",conversations:"Past chats",conversationsEmptyTitle:"No past chats",conversationsEmptyText:"Your conversations with the assistant show here.",untitled:"Untitled chat",deleteConversation:"Delete chat",deleteConfirmTitle:"Delete this chat?",deleteConfirmMessage:"The conversation is removed for good.",deleteConfirm:"Delete",deleteFailed:"We could not delete this chat. Please try again.",cancel:"Cancel",loadMore:"Show more",loadFailed:"We could not load your chats. Please try again.",retry:"Try again"});export const mergeLabels=e=>e?{...DEFAULT_CHAT_LABELS,...e}:DEFAULT_CHAT_LABELS;
@@ -0,0 +1 @@
1
+ "use strict";const LINK_HREF=/^[a-z][a-z0-9+.-]*:[a-z0-9_-]+\/[a-z0-9_.~-]+$/i,FENCE=/^\s*```/,BULLET=/^\s*[-*+]\s+(.*)$/,ORDERED=/^\s*(\d{1,9})[.)]\s+(.*)$/,HEADING=/^\s{0,3}#{1,6}\s+(.*?)\s*#*\s*$/,RULE=/^\s*([-*_])(\s*\1){2,}\s*$/,TABLE_SEPARATOR=/^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?\s*$/,isTableRow=t=>t.includes("|"),tableCells=t=>{let e=t.trim();return e.startsWith("|")&&(e=e.slice(1)),e.endsWith("|")&&(e=e.slice(0,-1)),e.split("|").map(t=>t.trim())},startsTable=(t,e)=>isTableRow(t[e])&&e+1<t.length&&TABLE_SEPARATOR.test(t[e+1]),startsBlock=(t,e)=>{const s=t[e];return FENCE.test(s)||BULLET.test(s)||ORDERED.test(s)||HEADING.test(s)||RULE.test(s)||startsTable(t,e)};export const parseBlocks=t=>{const e=String(t??"").replace(/\r\n?/g,"\n").split("\n"),s=[];let n=0;for(;n<e.length;){const t=e[n];if(!t.trim()){n+=1;continue}if(FENCE.test(t)){const t=[];for(n+=1;n<e.length&&!FENCE.test(e[n]);)t.push(e[n]),n+=1;n+=1,s.push({type:"code",text:t.join("\n")});continue}if(startsTable(e,n)){const i=tableCells(t),r=[];for(n+=2;n<e.length&&e[n].trim()&&isTableRow(e[n]);)r.push(tableCells(e[n])),n+=1;s.push({type:"table",header:i,rows:r});continue}if(RULE.test(t)){s.push({type:"rule"}),n+=1;continue}const i=HEADING.exec(t);if(i){s.push({type:"heading",text:i[1]}),n+=1;continue}const r=ORDERED.exec(t);if(r||BULLET.test(t)){const t=Boolean(r),i=t?ORDERED:BULLET,l=[];for(;n<e.length&&e[n].trim();){const s=i.exec(e[n]);if(s)l.push(t?s[2]:s[1]);else{if(startsBlock(e,n))break;l[l.length-1]+=` ${e[n].trim()}`}n+=1}s.push({type:"list",ordered:t,start:r?Number(r[1]):1,items:l});continue}const l=[t];for(n+=1;n<e.length&&e[n].trim()&&!startsBlock(e,n);)l.push(e[n]),n+=1;s.push({type:"paragraph",lines:l})}return s.map((t,e)=>({...t,key:e}))};const isWordChar=t=>Boolean(t)&&/[\p{L}\p{N}]/u.test(t??""),findClose=(t,e,s)=>{let n=t.indexOf(s,e);for(;-1!==n;){const i=t.slice(e,n),r=1===s.length&&(t[n+1]===s||t[n-1]===s),l="_"===s[0]&&isWordChar(t[n+s.length]);if(i.trim()&&!/^\s/.test(i)&&!/\s$/.test(i)&&!r&&!l)return n;n=t.indexOf(s,n+1)}return-1};export const parseInline=t=>{const e=[];let s="";const flush=()=>{s&&e.push({type:"text",text:s}),s=""};let n=0;for(;n<t.length;){const i=t[n];if("`"===i){const s=t.indexOf("`",n+1);if(s>n+1){flush(),e.push({type:"code",text:t.slice(n+1,s)}),n=s+1;continue}}if(("*"===i||"_"===i)&&t[n+1]===i){const r=i+i,l=findClose(t,n+2,r);if(-1!==l&&("_"!==i||!isWordChar(t[n-1]))){flush(),e.push({type:"strong",children:parseInline(t.slice(n+2,l))}),n=l+2;continue}s+=r,n+=2;continue}if("*"===i||"_"===i){const s=findClose(t,n+1,i);if(-1!==s&&("_"!==i||!isWordChar(t[n-1]))){flush(),e.push({type:"em",children:parseInline(t.slice(n+1,s))}),n=s+1;continue}}if("["===i){const s=/^\[([^\]\n]+)\]\(([^)\s]+)\)/.exec(t.slice(n));if(s){flush(),e.push({type:"link",href:s[2],children:parseInline(s[1])}),n+=s[0].length;continue}}s+=i,n+=1}return flush(),e.map((t,e)=>({...t,key:e}))};export const linkTarget=(t,e)=>e&&LINK_HREF.test(t)?e(t)??null:null;
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1 @@
1
+ "use strict";export const createSseParser=e=>{let t="";const dispatch=t=>{let n="message";const r=[];for(const e of t.split("\n")){if(""===e||e.startsWith(":"))continue;const t=e.indexOf(":"),s=-1===t?e:e.slice(0,t);let a=-1===t?"":e.slice(t+1);a.startsWith(" ")&&(a=a.slice(1)),"event"===s?n=a:"data"===s&&r.push(a)}if(0===r.length)return;let s;try{s=JSON.parse(r.join("\n"))}catch{return void e({name:"error",data:{code:"invalid-response",message:null}})}e({name:n,data:s})};return{push(e){if(!e)return;t=(t+e).replace(/\r\n/g,"\n").replace(/\r(?!$)/g,"\n");const n=t.split("\n\n");t=n.pop()??"";for(const e of n)dispatch(e)},end(){const e=t.replace(/\r$/,"");t="",e.trim()&&dispatch(e)}}};
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1 @@
1
+ "use strict";import{useCallback,useEffect,useRef,useState}from"react";const toError=e=>{const t=e;return{code:"string"==typeof t?.code&&t.code?t.code:"unknown",message:"string"==typeof t?.message&&t.message?t.message:"Something went wrong. Please try again.",resetsAtUtc:"string"==typeof t?.resetsAtUtc?t.resetsAtUtc:null}},parseActions=e=>{if(!e)return[];const t=JSON.parse(e);return Array.isArray(t)?t:[]},emptyTurn=(e,t)=>({id:e,question:t,answer:"",streaming:!1,error:null,actions:[],messageId:null,rating:null,ratingNote:null}),turnsFromConversation=e=>{const t=[];for(const n of e??[]){if("User"===n.role){t.push(emptyTurn(n.publicId,n.content));continue}const e=t[t.length-1],r={answer:n.content,actions:parseActions(n.actionsJson),messageId:n.publicId,rating:n.rating??null,ratingNote:n.ratingNote??null};e&&null===e.messageId&&""===e.answer?Object.assign(e,r):t.push({...emptyTurn(n.publicId,""),...r})}return t};export const useChat=({client:e,pageContext:t})=>{const[n,r]=useState([]),[s,a]=useState(null),[o,u]=useState(null),[c,l]=useState(!1),[i,g]=useState(null),f=useRef(null),m=useRef(0),d=useRef(0),y=useRef(t);y.current=t;const p=useRef(null);p.current=s;const w=useRef(n);w.current=n;const b=useCallback(()=>{m.current+=1,f.current?.abort(),f.current=null},[]);useEffect(()=>b,[b]);const C=useCallback((e,t)=>{r(n=>n.map(n=>n.id===e?{...n,..."function"==typeof t?t(n):t}:n))},[]),I=useCallback(async t=>{const n="string"==typeof t?t.trim():"";if(!n||f.current)return;const s=m.current,o=new AbortController;f.current=o,d.current+=1;const c=`turn-${d.current}`;r(e=>[...e,{...emptyTurn(c,n),streaming:!0}]);const live=()=>s===m.current&&!o.signal.aborted;try{const t=await e.sendMessage({message:n,conversationId:p.current,pageContext:y.current},{signal:o.signal,onEvent:(e,t)=>{if(!live())return;const n=t;"delta"===e?C(c,e=>({answer:e.answer+(n?.text??"")})):"reset"===e?C(c,{answer:""}):"actions"===e&&C(c,{actions:Array.isArray(n?.actions)?n.actions:[]})}});if(!live())return;C(c,{answer:t.reply,messageId:t.messageId??null,streaming:!1}),p.current=t.conversationId,a(t.conversationId),u(t.messagesLeftToday??null)}catch(e){if(!live())return;const t=toError(e);C(c,{answer:"",actions:[],streaming:!1,error:t}),"daily-limit"===t.code&&u(0),"conversation-not-found"===t.code&&(p.current=null,a(null))}finally{f.current===o&&(f.current=null)}},[e,C]),A=useCallback(async t=>{b();const n=m.current;r([]),a(null),g(null),l(!0);try{const s=await e.getConversation(t);if(n!==m.current)return;r(turnsFromConversation(s.messages)),a(s.publicId)}catch(e){if(n!==m.current)return;g(toError(e))}finally{n===m.current&&l(!1)}},[b,e]),h=useCallback(()=>{b(),r([]),a(null),g(null),l(!1)},[b]),v=useCallback(()=>{b(),r([]),a(null),u(null),g(null),l(!1)},[b]),k=useCallback(async()=>{const t=m.current,n=await e.getAllowance();t===m.current&&u(n?.messagesLeftToday??null)},[e]),E=useCallback(async(t,n,r)=>{const s=w.current.find(e=>e.id===t),a=p.current;if(!s?.messageId||!a)throw new Error("Only a stored reply can be rated.");const o={rating:s.rating,ratingNote:s.ratingNote},u="string"==typeof r&&r.trim()?r.trim():null;C(t,{rating:n,ratingNote:u});try{await e.rateMessage(a,s.messageId,{rating:n,note:u})}catch(e){throw C(t,o),e}},[e,C]);return{turns:n,conversationId:s,messagesLeftToday:o,streaming:n.some(e=>e.streaming),loading:c,loadError:i,submit:I,openConversation:A,newConversation:h,rate:E,refreshAllowance:k,reset:v}};
@@ -0,0 +1,73 @@
1
+ import type { ChatRatingInput, ChatStreamOptions, ChatTurnInput, ChatTurnResult, Conversation, ConversationSummary } from './types';
2
+ /** A failed chat request. `message` is written for the person and is safe to show as is. */
3
+ export declare class ChatApiError extends Error {
4
+ /**
5
+ * The server's code (`daily-limit`, `assistant-unavailable`, ...), `network` when no response
6
+ * arrived, `aborted` when the request was cancelled, `invalid-response`, or `unknown`.
7
+ */
8
+ readonly code: string;
9
+ /** The HTTP status, when a response arrived. */
10
+ readonly status: number | undefined;
11
+ /** For `daily-limit`: when the allowance comes back, in UTC. */
12
+ readonly resetsAtUtc: string | null;
13
+ constructor(code: string, message: string, status?: number, resetsAtUtc?: string | null);
14
+ }
15
+ /** The axios config fields the chat client sets. */
16
+ export interface ChatHttpRequestConfig {
17
+ headers?: Record<string, string>;
18
+ responseType?: 'text';
19
+ signal?: AbortSignal;
20
+ onDownloadProgress?: (progress: unknown) => void;
21
+ }
22
+ /**
23
+ * The slice of an HTTP client the chat client needs. The app's signed-in axios instance
24
+ * (mobile-core's `userClient`, or its own) satisfies it as is, so the bearer token, tenant header
25
+ * and refresh on 401 apply to chat calls too. Declared structurally so no axios type leaks into
26
+ * this package's declarations.
27
+ */
28
+ export interface ChatHttpClient {
29
+ get(url: string, config?: ChatHttpRequestConfig): Promise<{
30
+ data: unknown;
31
+ }>;
32
+ post(url: string, body?: unknown, config?: ChatHttpRequestConfig): Promise<{
33
+ data: unknown;
34
+ status?: number;
35
+ }>;
36
+ put(url: string, body?: unknown, config?: ChatHttpRequestConfig): Promise<{
37
+ data: unknown;
38
+ }>;
39
+ delete(url: string, config?: ChatHttpRequestConfig): Promise<{
40
+ data: unknown;
41
+ }>;
42
+ }
43
+ export interface ChatClient {
44
+ /** `POST {base}/chat`, streamed; resolves with the `done` event. */
45
+ sendMessage(input: ChatTurnInput, options?: ChatStreamOptions): Promise<ChatTurnResult>;
46
+ /** `GET {base}/allowance`. */
47
+ getAllowance(): Promise<{
48
+ messagesLeftToday: number | null;
49
+ }>;
50
+ /** `GET {base}/conversations?skip=&take=`, newest first. */
51
+ listConversations(query?: {
52
+ skip?: number;
53
+ take?: number;
54
+ }): Promise<ConversationSummary[]>;
55
+ /** `GET {base}/conversations/{id}`. */
56
+ getConversation(id: string): Promise<Conversation>;
57
+ /** `DELETE {base}/conversations/{id}`. */
58
+ deleteConversation(id: string): Promise<void>;
59
+ /** `PUT {base}/conversations/{conversationId}/messages/{messageId}/rating`. */
60
+ rateMessage(conversationId: string, messageId: string, input: ChatRatingInput): Promise<void>;
61
+ }
62
+ /**
63
+ * Binds the chat HTTP contract to the app's own signed-in axios instance. The package never
64
+ * handles auth: tokens, tenant headers and refresh stay in the instance.
65
+ *
66
+ * @param http The app's axios instance, e.g. mobile-core's `userClient`.
67
+ * @param options `base` is the prefix the API maps the chat endpoints under, e.g.
68
+ * `/api/assistant` for `POST /api/assistant/chat`.
69
+ */
70
+ export declare const createChatClient: (http: ChatHttpClient, { base }: {
71
+ base: string;
72
+ }) => ChatClient;
73
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1,23 @@
1
+ import type React from 'react';
2
+ import { type ChatLabels } from '../labels';
3
+ import type { SupportAction, SupportActionTicket } from '../types';
4
+ export interface ActionCardProps {
5
+ action: SupportAction;
6
+ /**
7
+ * Sends the person's yes, e.g. mobile-support's `supportClient.confirmAction`; resolves to the
8
+ * ticket raised, replied on or reopened. A rejection's `message` is shown when present.
9
+ */
10
+ confirmAction: (token: string) => Promise<SupportActionTicket>;
11
+ /** Shows a View ticket button once confirmed. */
12
+ onViewTicket?: (ticket: SupportActionTicket) => void;
13
+ labels?: Partial<ChatLabels>;
14
+ testID?: string;
15
+ }
16
+ /**
17
+ * One support action the assistant prepared, shown under its reply. Nothing reaches support until
18
+ * the person confirms. The card moves from ready to sending to done (with View ticket), or to
19
+ * dismissed on Not now. An expired action (a reopened conversation) still shows what was offered,
20
+ * without a button that could only fail.
21
+ */
22
+ export declare const ActionCard: React.FC<ActionCardProps>;
23
+ //# sourceMappingURL=ActionCard.d.ts.map
@@ -0,0 +1,20 @@
1
+ import type React from 'react';
2
+ import { type ChatLabels } from '../labels';
3
+ export interface ChatComposerProps {
4
+ /** Called with the trimmed question; the box clears once it is handed over. */
5
+ onSubmit: (message: string) => unknown;
6
+ /** An answer is arriving: the question can be typed but not sent. */
7
+ busy?: boolean;
8
+ /** No question can be asked, e.g. none left today. */
9
+ disabled?: boolean;
10
+ /** The server's message limit. Default 4000. */
11
+ maxLength?: number;
12
+ labels?: Partial<ChatLabels>;
13
+ testID?: string;
14
+ }
15
+ /**
16
+ * The question box and its send button. The return key adds a new line, as phone keyboards
17
+ * expect; only the button sends.
18
+ */
19
+ export declare const ChatComposer: React.FC<ChatComposerProps>;
20
+ //# sourceMappingURL=ChatComposer.d.ts.map
@@ -0,0 +1,23 @@
1
+ import type React from 'react';
2
+ import { type StyleProp, type ViewStyle } from 'react-native';
3
+ import type { ChatClient } from '../client';
4
+ import { type ChatLabels } from '../labels';
5
+ import type { ConversationSummary } from '../types';
6
+ export interface ChatConversationListProps {
7
+ client: ChatClient;
8
+ /** E.g. go to the chat and call `chat.openConversation(conversation.publicId)`. */
9
+ onOpen: (conversation: ConversationSummary) => void;
10
+ /** Called after a chat is deleted, e.g. to start a new chat when the open one was deleted. */
11
+ onDeleted?: (id: string) => void;
12
+ /** Default 20. */
13
+ pageSize?: number;
14
+ labels?: Partial<ChatLabels>;
15
+ style?: StyleProp<ViewStyle>;
16
+ testID?: string;
17
+ }
18
+ /**
19
+ * The person's past chats, newest first. It pages with Show more, refreshes on pull, opens one
20
+ * through `onOpen`, and deletes one after a confirm.
21
+ */
22
+ export declare const ChatConversationList: React.FC<ChatConversationListProps>;
23
+ //# sourceMappingURL=ChatConversationList.d.ts.map
@@ -0,0 +1,22 @@
1
+ import type React from 'react';
2
+ import { type ChatLabels } from '../labels';
3
+ export interface ChatLauncherProps {
4
+ onPress: () => void;
5
+ /** `end` (the default) or `start`: the bottom corner it sits in. */
6
+ position?: 'end' | 'start';
7
+ /**
8
+ * Distance above the bottom safe area, so it clears a tab bar or another floating control.
9
+ * Defaults to the theme's floating action button offset.
10
+ */
11
+ bottomOffset?: number;
12
+ /** Show only the icon, with the label as its accessible name. */
13
+ compact?: boolean;
14
+ labels?: Partial<ChatLabels>;
15
+ testID?: string;
16
+ }
17
+ /**
18
+ * An optional floating button that opens the chat. A phone app usually opens the chat from a
19
+ * header action or a Help row instead; use this where a floating entry point fits the screen.
20
+ */
21
+ export declare const ChatLauncher: React.FC<ChatLauncherProps>;
22
+ //# sourceMappingURL=ChatLauncher.d.ts.map