@webority/chat-react 0.54.0 → 0.56.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.
- package/README.md +3 -2
- package/dist/chat-react.js +1 -1
- package/dist/types/useChat.d.ts +5 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -87,7 +87,7 @@ response arrived, or `aborted` when the request was cancelled.
|
|
|
87
87
|
| `deleteConversation(id)` | `DELETE {base}/conversations/{id}` |
|
|
88
88
|
| `rateMessage(conversationId, messageId, { rating, note })` | `PUT {base}/conversations/{conversationId}/messages/{messageId}/rating` |
|
|
89
89
|
|
|
90
|
-
`onEvent(name, data)` is called with `delta`, `reset` and `actions` as they arrive.
|
|
90
|
+
`onEvent(name, data)` is called with `delta`, `reset`, `tool` and `actions` as they arrive.
|
|
91
91
|
`createSseParser(onEvent)` is exported too, for reading the same stream elsewhere.
|
|
92
92
|
|
|
93
93
|
## The server contract
|
|
@@ -113,6 +113,7 @@ Otherwise the response is `text/event-stream`:
|
|
|
113
113
|
|---|---|---|
|
|
114
114
|
| `delta` | `{ text }` | Appends the text. |
|
|
115
115
|
| `reset` | `{}` | Discards the text shown so far. |
|
|
116
|
+
| `tool` | `{ toolName }` | A tool call started; the name only, never its arguments. `useChat` shows it as the turn's `step`. |
|
|
116
117
|
| `actions` | `{ actions: [{ token, kind, ticketId, ticketNumber, category, subject, body, expiresDateTimeUtc }] }` | Holds support cards; `kind` is `RaiseTicket`, `Reply` or `Reopen`. |
|
|
117
118
|
| `done` | `{ conversationId, messageId, reply, messagesLeftToday }` | Ends the turn. `reply` is the reply of record and replaces the streamed text. `messageId` is null when storing failed; `messagesLeftToday` is null with no daily limit. |
|
|
118
119
|
| `error` | `{ code, message }` | Ends the turn with a failure. |
|
|
@@ -160,7 +161,7 @@ and `renderReply` are not in the element.
|
|
|
160
161
|
|
|
161
162
|
| Field | What it is |
|
|
162
163
|
|---|---|
|
|
163
|
-
| `turns` | `[{ id, question, answer, streaming, error, actions, messageId, rating, ratingNote }]`, oldest first |
|
|
164
|
+
| `turns` | `[{ id, question, answer, streaming, step, error, actions, messageId, rating, ratingNote }]`, oldest first; `step` is the name of the tool the assistant is running, or null (set by a `tool` event, cleared when text arrives, on a reset and when the turn ends), for the product to map to its own sentence |
|
|
164
165
|
| `conversationId` | The conversation being continued, or null |
|
|
165
166
|
| `messagesLeftToday` | From the last answer or `refreshAllowance()`; null with no daily limit |
|
|
166
167
|
| `streaming` | An answer is arriving |
|
package/dist/chat-react.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ChatApiError as e,DEFAULT_CHAT_LABELS as t,createChatClient as n,createSseParser as r,linkRoute as i,mergeLabels as a,parseBlocks as o,parseInline as s}from"@webority/ui-elements/chat-core";import{AppActionIcon as c,AppAlert as l,AppButton as u,AppCard as d,AppConfirmDialog as f,AppDataTable as p,AppEmptyState as m,AppIcon as h,AppLink as g,AppSidebarMenu as _,AppSkeleton as v,AppSpinner as y,AppTextarea as b,useFloatingStack as x}from"@webority/ui-react";import{Fragment as S,useCallback as C,useEffect as w,useId as T,useRef as E,useState as D}from"react";import{Fragment as O,jsx as k,jsxs as A}from"react/jsx-runtime";import{createPortal as j}from"react-dom";function M(e,t){return e.kind===`Reply`?t.actionReply(e.ticketNumber):e.kind===`Reopen`?t.actionReopen(e.ticketNumber):t.actionRaiseTicket}function N(e,t,n){return e===`Reply`?n.actionDoneReply(t):e===`Reopen`?n.actionDoneReopen(t):n.actionDoneRaiseTicket(t)}function P({action:e,confirmAction:t,onViewTicket:n,labels:r}){let i=a(r),[o,s]=D(`ready`),[c,f]=D(null),[p,m]=D(``);if(o===`dismissed`)return k(`p`,{className:`wui-text-sm text-body-secondary mb-0`,children:i.actionDismissed});if(o===`done`)return k(l,{variant:`success`,role:`status`,size:`sm`,action:n?k(u,{size:`sm`,variant:`ghost`,onClick:()=>n(c),children:i.viewTicket}):void 0,children:N(e.kind,c.number,i)});async function h(){s(`sending`),m(``);try{f(await t(e.token)),s(`done`)}catch(e){m(e?.message||i.actionFailed),s(`ready`)}}let g=o===`sending`,_=new Date(e.expiresDateTimeUtc).getTime()<=Date.now(),v=e.kind===`RaiseTicket`?[e.category,e.subject].filter(Boolean).join(` · `):``;return k(d,{padding:`sm`,className:`border`,title:M(e,i),subtitle:v||void 0,children:A(`div`,{className:`d-flex flex-column gap-2`,children:[e.kind!==`Reopen`&&e.body&&k(`p`,{className:`wui-text-sm mb-0`,style:{whiteSpace:`pre-wrap`,overflowWrap:`anywhere`},children:e.body}),_?k(`p`,{className:`wui-text-sm text-body-secondary mb-0`,children:i.actionExpired}):A(O,{children:[k(`p`,{className:`wui-text-xs text-body-secondary mb-0`,children:i.actionNote}),p&&k(l,{size:`sm`,variant:`danger`,children:p}),A(`div`,{className:`d-flex flex-wrap gap-2`,children:[k(u,{size:`sm`,loading:g,onClick:h,children:i.actionConfirm}),k(u,{size:`sm`,variant:`secondary`,disabled:g,onClick:()=>s(`dismissed`),children:i.actionDismiss})]})]})]})})}function F({onSubmit:e,busy:t=!1,disabled:n=!1,maxLength:r=4e3,inputRef:i,labels:o}){let s=a(o),c=T(),[l,d]=D(``),f=t||n||!l.trim();function p(){f||(e(l.trim()),d(``))}return A(`form`,{className:`d-flex align-items-end gap-2`,"aria-label":s.composer,onSubmit:e=>{e.preventDefault(),p()},children:[k(b,{id:`${c}-message`,"aria-label":s.composer,"aria-describedby":`${c}-hint`,placeholder:s.placeholder,rows:1,autoGrow:!0,maxLength:r,value:l,disabled:n,ref:i,fieldClassName:`flex-grow-1 mb-0`,onChange:e=>d(e.target.value),onKeyDown:e=>{e.key===`Enter`&&!e.shiftKey&&!e.nativeEvent.isComposing&&(e.preventDefault(),p())}}),k(`span`,{id:`${c}-hint`,className:`visually-hidden`,children:s.composerHint}),k(u,{type:`submit`,leftIcon:`send`,disabled:f,children:k(`span`,{className:`visually-hidden`,children:s.send})})]})}var I=new Intl.DateTimeFormat(`en-IN`,{day:`2-digit`,month:`numeric`,year:`numeric`,hour:`numeric`,minute:`2-digit`,hour12:!0,timeZoneName:`short`}),L=[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`];function R(e){if(!e)return`-`;let t=new Date(e);if(Number.isNaN(t.getTime()))return`-`;let n=Object.fromEntries(I.formatToParts(t).map(e=>[e.type,e.value]));return`${n.day} ${L[Number(n.month)-1]} ${n.year}, ${n.hour}:${n.minute} ${n.dayPeriod.toLowerCase()} ${n.timeZoneName}`}function z({client:e,onOpen:t,onDeleted:n,pageSize:r=20,labels:i}){let o=a(i),[s,p]=D([]),[h,g]=D(!0),[_,y]=D(``),[b,x]=D(!1),[S,T]=D(0),[E,O]=D(null),[j,M]=D(!1),[N,P]=D(``),F=C(async t=>{g(!0),y(``);try{let n=await e.listConversations({skip:t,take:r});p(e=>t===0?n:[...e,...n]),x(n.length===r)}catch(e){y(e?.message||o.loadFailed)}finally{g(!1)}},[e,r,o.loadFailed]);w(()=>{F(0)},[F,S]);async function I(){M(!0),P(``);try{await e.deleteConversation(E.publicId),p(e=>e.filter(e=>e.publicId!==E.publicId)),n?.(E.publicId),O(null)}catch(e){P(e?.message||o.deleteFailed)}finally{M(!1)}}return!h&&!_&&s.length===0?k(m,{icon:`chat`,size:`sm`,title:o.conversationsEmptyTitle,text:o.conversationsEmptyText}):A(`div`,{className:`d-flex flex-column gap-3`,children:[_&&k(l,{variant:`danger`,action:k(u,{size:`sm`,variant:`secondary`,onClick:()=>T(e=>e+1),children:o.retry}),children:_}),s.length>0&&k(`ul`,{className:`list-unstyled d-flex flex-column gap-2 mb-0`,"aria-label":o.conversations,children:s.map(e=>k(`li`,{children:A(d,{padding:`sm`,className:`border`,bodyClassName:`d-flex align-items-center gap-2`,children:[k(u,{variant:`ghost`,className:`flex-grow-1 text-start`,style:{minWidth:0},onClick:()=>t(e),children:A(`span`,{className:`d-flex flex-column`,style:{minWidth:0},children:[k(`span`,{className:`text-truncate fw-semibold`,children:e.title||o.untitled}),k(`time`,{dateTime:e.lastMessageDateTimeUtc,className:`wui-text-xs text-body-secondary`,children:R(e.lastMessageDateTimeUtc)})]})}),k(c,{icon:`trash-2`,danger:!0,label:o.deleteConversation,onClick:()=>{P(``),O(e)}})]})},e.publicId))}),h&&A(`div`,{className:`d-flex flex-column gap-2`,"aria-hidden":`true`,children:[k(v,{height:44}),k(v,{height:44}),k(v,{height:44})]}),!h&&b&&k(u,{variant:`secondary`,className:`align-self-center`,onClick:()=>F(s.length),children:o.loadMore}),k(f,{open:E!==null,title:o.deleteConfirmTitle,message:N?k(l,{variant:`danger`,children:N}):o.deleteConfirmMessage,confirmLabel:o.deleteConfirm,cancelLabel:o.cancel,danger:!0,loading:j,onConfirm:I,onClose:()=>{j||O(null)}})]})}var B={zIndex:`var(--wui-z-fixed)`,bottom:0},V={end:{insetInlineEnd:0},start:{insetInlineStart:0}};function H({onClick:e,open:t=!1,position:n=`end`,icon:r=`sparkles`,compact:i=!1,className:o,style:s,labels:c}){let l=a(c);return i?k(U,{onClick:e,open:t,position:n,icon:r,className:o,style:s,label:l.launcher}):k(`span`,{"data-support-capture":`exclude`,className:[`position-fixed m-3`,o].filter(Boolean).join(` `),style:{...B,...V[n],...s},children:k(u,{leftIcon:r,className:`rounded-pill`,onClick:e,"aria-haspopup":`dialog`,"aria-expanded":t,children:l.launcher})})}function U({onClick:e,open:t,position:n,icon:r,className:i,style:a,label:o}){let s=x(n);return s?j(k(`span`,{className:[`wui-floating-stack-item wui-floating-stack-item--primary`,i].filter(Boolean).join(` `),style:a,children:k(u,{className:`wui-floating-btn`,onClick:e,"aria-label":o,"aria-haspopup":`dialog`,"aria-expanded":t,tooltip:o,tooltipPlacement:n===`start`?`end`:`start`,children:k(h,{name:r,size:24})})}),s):null}function W({tokens:e,resolveLink:t,linkComponent:n}){return e.map(e=>{switch(e.type){case`code`:return k(`code`,{children:e.text},e.key);case`strong`:return k(`strong`,{children:k(W,{tokens:e.children,resolveLink:t,linkComponent:n})},e.key);case`em`:return k(`em`,{children:k(W,{tokens:e.children,resolveLink:t,linkComponent:n})},e.key);case`link`:{let r=k(W,{tokens:e.children,resolveLink:t,linkComponent:n}),a=i(e.href,t);return a?k(g,{href:a,linkComponent:n,animation:`static`,children:r},e.key):k(`span`,{children:r},e.key)}default:return k(S,{children:e.text},e.key)}})}function G({text:e,resolveLink:t,linkComponent:n}){return k(W,{tokens:s(e),resolveLink:t,linkComponent:n})}function K({block:e,resolveLink:t,linkComponent:n}){let r=e=>k(G,{text:e,resolveLink:t,linkComponent:n});switch(e.type){case`heading`:return k(`p`,{className:`fw-semibold mb-2`,children:r(e.text)});case`rule`:return k(`hr`,{className:`my-2`});case`code`:return k(`pre`,{className:`wui-text-sm mb-2`,children:k(`code`,{children:e.text})});case`list`:{let t=e.items.map((e,t)=>k(`li`,{children:r(e)},t));return e.ordered?k(`ol`,{className:`mb-2 ps-4`,start:e.start===1?void 0:e.start,children:t}):k(`ul`,{className:`mb-2 ps-4`,children:t})}case`table`:return k(p,{columns:e.header.map((e,t)=>({key:`c${t}`,label:r(e),render:e=>r(e.cells[t]??``)})),rows:e.rows.map((e,t)=>({id:t,cells:e})),density:`compact`,empty:null,wrapperClassName:`mb-2`});default:return k(`p`,{className:`mb-2`,children:e.lines.map((e,t)=>A(`span`,{children:[t>0&&k(`br`,{}),r(e)]},t))})}}function q({text:e,resolveLink:t,linkComponent:n,className:r}){let i=o(e);return k(`div`,{className:[`wui-text-sm text-break`,r].filter(Boolean).join(` `),children:i.map(e=>k(K,{block:e,resolveLink:t,linkComponent:n},e.key))})}function J({rating:e=null,onRate:t,noteMaxLength:n=1e3,labels:r}){let i=a(r),o=T(),[s,c]=D(!1),[d,f]=D(!1),[p,m]=D(``),[h,g]=D(``),[_,v]=D(!1);async function y(e,n){c(!0),g(``);try{await t(e,n),f(e===`Down`&&n===void 0),v(e===`Up`||n!==void 0),n!==void 0&&m(``)}catch{g(i.ratingFailed)}finally{c(!1)}}return A(`div`,{className:`d-flex flex-column gap-2`,children:[A(`div`,{className:`d-flex flex-wrap align-items-center gap-1`,children:[k(u,{size:`sm`,variant:e===`Up`?`subtle`:`ghost`,leftIcon:`check`,"aria-pressed":e===`Up`,disabled:s,onClick:()=>y(`Up`),children:i.rateUp}),k(u,{size:`sm`,variant:e===`Down`?`subtle`:`ghost`,leftIcon:`x`,"aria-pressed":e===`Down`,disabled:s,onClick:()=>y(`Down`),children:i.rateDown}),_&&!d&&k(`span`,{className:`wui-text-xs text-body-secondary`,role:`status`,children:i.rated})]}),h&&k(l,{size:`sm`,variant:`danger`,children:h}),d&&A(`form`,{className:`d-flex flex-column gap-2`,onSubmit:e=>{e.preventDefault(),y(`Down`,p)},children:[k(b,{id:`${o}-note`,label:i.ratingNote,hint:i.ratingNoteHint,rows:2,maxLength:n,value:p,onChange:e=>m(e.target.value)}),A(`div`,{className:`d-flex gap-2`,children:[k(u,{type:`submit`,size:`sm`,loading:s,children:i.ratingNoteSend}),k(u,{size:`sm`,variant:`secondary`,disabled:s,onClick:()=>{f(!1),v(!0)},children:i.ratingNoteSkip})]})]})]})}var Y={maxWidth:`85%`,whiteSpace:`pre-wrap`,overflowWrap:`anywhere`};function X({turn:e,onRate:t,resolveLink:n,linkComponent:r,confirmAction:i,onViewTicket:o,renderReply:s,labels:c}){let u=a(c),f=e.streaming&&!e.answer;return A(`li`,{className:`d-flex flex-column gap-2`,children:[e.question&&A(`div`,{className:`align-self-end`,style:Y,children:[A(`span`,{className:`visually-hidden`,children:[u.you,`: `]}),k(d,{padding:`sm`,variant:`muted`,className:`wui-text-sm`,children:e.question})]}),A(`div`,{className:`d-flex flex-column gap-2`,style:{minWidth:0},children:[A(`span`,{className:`visually-hidden`,children:[u.assistant,`: `]}),k(`div`,{"aria-live":`polite`,"aria-busy":e.streaming,children:f?A(`span`,{className:`d-inline-flex align-items-center gap-2 wui-text-sm text-body-secondary`,children:[k(y,{size:`sm`,label:u.thinking}),k(`span`,{"aria-hidden":`true`,children:u.thinking})]}):e.answer&&(s?s(e,{streaming:!!e.streaming}):k(q,{text:e.answer,resolveLink:n,linkComponent:r}))}),e.error&&k(l,{size:`sm`,variant:`danger`,children:e.error.message}),i&&!e.streaming&&e.actions.map(e=>k(P,{action:e,confirmAction:i,onViewTicket:o,labels:c},e.token)),t&&e.messageId&&!e.streaming&&k(J,{rating:e.rating,onRate:(n,r)=>t(e.id,n,r),labels:c})]})]})}function Z({turns:e,empty:t,onRate:n,resolveLink:r,linkComponent:i,confirmAction:o,onViewTicket:s,renderReply:c,labels:l}){let u=a(l),d=E(null),f=e[e.length-1];return w(()=>{d.current?.scrollIntoView({block:`end`})},[e.length,f?.answer,f?.error,f?.actions]),e.length===0?t??k(m,{icon:`sparkles`,size:`sm`,title:u.emptyTitle,text:u.emptyText}):A(O,{children:[k(`ol`,{className:`list-unstyled d-flex flex-column gap-3 mb-0`,"aria-label":u.thread,children:e.map(e=>k(X,{turn:e,onRate:n,resolveLink:r,linkComponent:i,confirmAction:o,onViewTicket:s,renderReply:c,labels:l},e.id))}),k(`div`,{ref:d})]})}function Q({chat:e,open:t,onClose:n,title:r,titleIcon:i=`sparkles`,headerActions:o,empty:s,disclaimer:u,maxLength:d=4e3,allowanceThreshold:f=10,width:p=`440px`,resolveLink:m,linkComponent:g,confirmAction:v,onViewTicket:b,rating:x=!0,renderReply:S,labels:C}){let T=a(C),D=E(null),{refreshAllowance:O}=e;w(()=>{t&&O().catch(()=>{})},[t,O]);let j=e.messagesLeftToday,M=r??T.panelTitle;return k(_,{show:t,onHide:n,side:`right`,width:p,header:A(`div`,{className:`drawer-header`,children:[A(`div`,{className:`drawer-title`,children:[i&&k(`span`,{className:`drawer-title-ic`,children:k(h,{name:i,size:18})}),M]}),A(`div`,{className:`d-flex align-items-center gap-1`,children:[o,k(c,{icon:`plus`,label:T.newChat,onClick:e.newConversation,disabled:e.turns.length===0&&!e.loading}),k(c,{icon:`x`,label:T.close,onClick:n})]})]}),footer:A(`div`,{className:`d-flex flex-column gap-2`,children:[j!==null&&j<=f&&k(`p`,{className:`wui-text-xs text-body-secondary mb-0`,role:`status`,children:T.allowance(j)}),k(F,{onSubmit:e.submit,busy:e.streaming||e.loading,disabled:j===0,maxLength:d,inputRef:D,labels:C}),k(`p`,{className:`wui-text-xs text-body-secondary mb-0`,children:u??T.disclaimer})]}),ariaLabel:typeof M==`string`?M:T.panelTitle,onEntered:()=>D.current?.focus(),children:e.loading?k(`div`,{className:`d-flex justify-content-center py-4`,children:k(y,{label:T.loadingConversation})}):e.loadError?k(l,{variant:`danger`,children:e.loadError.message}):k(Z,{turns:e.turns,empty:s,onRate:x?e.rate:void 0,resolveLink:m,linkComponent:g,confirmAction:v,onViewTicket:b,renderReply:S,labels:C})})}function $(e){return{code:e?.code||`unknown`,message:e?.message||`Something went wrong. Please try again.`,resetsAtUtc:e?.resetsAtUtc??null}}function ee(e){if(!e)return[];let t=JSON.parse(e);return Array.isArray(t)?t:[]}function te(e){let t=[];for(let n of e??[]){if(n.role===`User`){t.push({id:n.publicId,question:n.content,answer:``,streaming:!1,error:null,actions:[],messageId:null,rating:null,ratingNote:null});continue}let e=t[t.length-1],r={answer:n.content,actions:ee(n.actionsJson),messageId:n.publicId,rating:n.rating??null,ratingNote:n.ratingNote??null};e&&e.messageId===null&&e.answer===``?Object.assign(e,r):t.push({id:n.publicId,question:``,streaming:!1,error:null,...r})}return t}function ne({client:e,pageContext:t}){let[n,r]=D([]),[i,a]=D(null),[o,s]=D(null),[c,l]=D(!1),[u,d]=D(null),f=E(null),p=E(0),m=E(0),h=E(t);h.current=t;let g=E(null);g.current=i;let _=E(n);_.current=n;let v=C(()=>{p.current+=1,f.current?.abort(),f.current=null},[]);w(()=>v,[v]);let y=C((e,t)=>{r(n=>n.map(n=>n.id===e?{...n,...typeof t==`function`?t(n):t}:n))},[]),b=C(async t=>{let n=typeof t==`string`?t.trim():``;if(!n||f.current)return;let i=p.current,o=new AbortController;f.current=o,m.current+=1;let c=`turn-${m.current}`;r(e=>[...e,{id:c,question:n,answer:``,streaming:!0,error:null,actions:[],messageId:null,rating:null,ratingNote:null}]);let l=()=>i===p.current&&!o.signal.aborted;try{let t=await e.sendMessage({message:n,conversationId:g.current,pageContext:h.current},{signal:o.signal,onEvent:(e,t)=>{l()&&(e===`delta`?y(c,e=>({answer:e.answer+(t?.text??``)})):e===`reset`?y(c,{answer:``}):e===`actions`&&y(c,{actions:Array.isArray(t?.actions)?t.actions:[]}))}});if(!l())return;y(c,{answer:t.reply,messageId:t.messageId??null,streaming:!1}),g.current=t.conversationId,a(t.conversationId),s(t.messagesLeftToday??null)}catch(e){if(!l())return;y(c,{answer:``,actions:[],streaming:!1,error:$(e)}),e?.code===`daily-limit`&&s(0),e?.code===`conversation-not-found`&&(g.current=null,a(null))}finally{f.current===o&&(f.current=null)}},[e,y]),x=C(async t=>{v();let n=p.current;r([]),a(null),d(null),l(!0);try{let i=await e.getConversation(t);if(n!==p.current)return;r(te(i.messages)),a(i.publicId)}catch(e){if(n!==p.current)return;d($(e))}finally{n===p.current&&l(!1)}},[v,e]),S=C(()=>{v(),r([]),a(null),d(null),l(!1)},[v]),T=C(()=>{v(),r([]),a(null),s(null),d(null),l(!1)},[v]),O=C(async()=>{let t=p.current,n=await e.getAllowance();t===p.current&&s(n?.messagesLeftToday??null)},[e]),k=C(async(t,n,r)=>{let i=_.current.find(e=>e.id===t),a=g.current;if(!i?.messageId||!a)throw Error(`Only a stored reply can be rated.`);let o={rating:i.rating,ratingNote:i.ratingNote},s=typeof r==`string`&&r.trim()?r.trim():null;y(t,{rating:n,ratingNote:s});try{await e.rateMessage(a,i.messageId,{rating:n,note:s})}catch(e){throw y(t,o),e}},[e,y]);return{turns:n,conversationId:i,messagesLeftToday:o,streaming:n.some(e=>e.streaming),loading:c,loadError:u,submit:b,openConversation:x,newConversation:S,rate:k,refreshAllowance:O,reset:T}}export{P as ActionCard,e as ChatApiError,F as ChatComposer,z as ChatConversationList,H as ChatLauncher,q as ChatMarkdown,X as ChatMessage,Q as ChatPanel,Z as ChatThread,t as DEFAULT_CHAT_LABELS,J as ReplyRating,n as createChatClient,r as createSseParser,ne as useChat};
|
|
1
|
+
import{ChatApiError as e,DEFAULT_CHAT_LABELS as t,createChatClient as n,createSseParser as r,linkRoute as i,mergeLabels as a,parseBlocks as o,parseInline as s}from"@webority/ui-elements/chat-core";import{AppActionIcon as c,AppAlert as l,AppButton as u,AppCard as d,AppConfirmDialog as f,AppDataTable as p,AppEmptyState as m,AppIcon as h,AppLink as g,AppSidebarMenu as _,AppSkeleton as v,AppSpinner as y,AppTextarea as b,useFloatingStack as x}from"@webority/ui-react";import{Fragment as S,useCallback as C,useEffect as w,useId as T,useRef as E,useState as D}from"react";import{Fragment as O,jsx as k,jsxs as A}from"react/jsx-runtime";import{createPortal as j}from"react-dom";function M(e,t){return e.kind===`Reply`?t.actionReply(e.ticketNumber):e.kind===`Reopen`?t.actionReopen(e.ticketNumber):t.actionRaiseTicket}function N(e,t,n){return e===`Reply`?n.actionDoneReply(t):e===`Reopen`?n.actionDoneReopen(t):n.actionDoneRaiseTicket(t)}function P({action:e,confirmAction:t,onViewTicket:n,labels:r}){let i=a(r),[o,s]=D(`ready`),[c,f]=D(null),[p,m]=D(``);if(o===`dismissed`)return k(`p`,{className:`wui-text-sm text-body-secondary mb-0`,children:i.actionDismissed});if(o===`done`)return k(l,{variant:`success`,role:`status`,size:`sm`,action:n?k(u,{size:`sm`,variant:`ghost`,onClick:()=>n(c),children:i.viewTicket}):void 0,children:N(e.kind,c.number,i)});async function h(){s(`sending`),m(``);try{f(await t(e.token)),s(`done`)}catch(e){m(e?.message||i.actionFailed),s(`ready`)}}let g=o===`sending`,_=new Date(e.expiresDateTimeUtc).getTime()<=Date.now(),v=e.kind===`RaiseTicket`?[e.category,e.subject].filter(Boolean).join(` · `):``;return k(d,{padding:`sm`,className:`border`,title:M(e,i),subtitle:v||void 0,children:A(`div`,{className:`d-flex flex-column gap-2`,children:[e.kind!==`Reopen`&&e.body&&k(`p`,{className:`wui-text-sm mb-0`,style:{whiteSpace:`pre-wrap`,overflowWrap:`anywhere`},children:e.body}),_?k(`p`,{className:`wui-text-sm text-body-secondary mb-0`,children:i.actionExpired}):A(O,{children:[k(`p`,{className:`wui-text-xs text-body-secondary mb-0`,children:i.actionNote}),p&&k(l,{size:`sm`,variant:`danger`,children:p}),A(`div`,{className:`d-flex flex-wrap gap-2`,children:[k(u,{size:`sm`,loading:g,onClick:h,children:i.actionConfirm}),k(u,{size:`sm`,variant:`secondary`,disabled:g,onClick:()=>s(`dismissed`),children:i.actionDismiss})]})]})]})})}function F({onSubmit:e,busy:t=!1,disabled:n=!1,maxLength:r=4e3,inputRef:i,labels:o}){let s=a(o),c=T(),[l,d]=D(``),f=t||n||!l.trim();function p(){f||(e(l.trim()),d(``))}return A(`form`,{className:`d-flex align-items-end gap-2`,"aria-label":s.composer,onSubmit:e=>{e.preventDefault(),p()},children:[k(b,{id:`${c}-message`,"aria-label":s.composer,"aria-describedby":`${c}-hint`,placeholder:s.placeholder,rows:1,autoGrow:!0,maxLength:r,value:l,disabled:n,ref:i,fieldClassName:`flex-grow-1 mb-0`,onChange:e=>d(e.target.value),onKeyDown:e=>{e.key===`Enter`&&!e.shiftKey&&!e.nativeEvent.isComposing&&(e.preventDefault(),p())}}),k(`span`,{id:`${c}-hint`,className:`visually-hidden`,children:s.composerHint}),k(u,{type:`submit`,leftIcon:`send`,disabled:f,children:k(`span`,{className:`visually-hidden`,children:s.send})})]})}var I=new Intl.DateTimeFormat(`en-IN`,{day:`2-digit`,month:`numeric`,year:`numeric`,hour:`numeric`,minute:`2-digit`,hour12:!0,timeZoneName:`short`}),L=[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`];function R(e){if(!e)return`-`;let t=new Date(e);if(Number.isNaN(t.getTime()))return`-`;let n=Object.fromEntries(I.formatToParts(t).map(e=>[e.type,e.value]));return`${n.day} ${L[Number(n.month)-1]} ${n.year}, ${n.hour}:${n.minute} ${n.dayPeriod.toLowerCase()} ${n.timeZoneName}`}function z({client:e,onOpen:t,onDeleted:n,pageSize:r=20,labels:i}){let o=a(i),[s,p]=D([]),[h,g]=D(!0),[_,y]=D(``),[b,x]=D(!1),[S,T]=D(0),[E,O]=D(null),[j,M]=D(!1),[N,P]=D(``),F=C(async t=>{g(!0),y(``);try{let n=await e.listConversations({skip:t,take:r});p(e=>t===0?n:[...e,...n]),x(n.length===r)}catch(e){y(e?.message||o.loadFailed)}finally{g(!1)}},[e,r,o.loadFailed]);w(()=>{F(0)},[F,S]);async function I(){M(!0),P(``);try{await e.deleteConversation(E.publicId),p(e=>e.filter(e=>e.publicId!==E.publicId)),n?.(E.publicId),O(null)}catch(e){P(e?.message||o.deleteFailed)}finally{M(!1)}}return!h&&!_&&s.length===0?k(m,{icon:`chat`,size:`sm`,title:o.conversationsEmptyTitle,text:o.conversationsEmptyText}):A(`div`,{className:`d-flex flex-column gap-3`,children:[_&&k(l,{variant:`danger`,action:k(u,{size:`sm`,variant:`secondary`,onClick:()=>T(e=>e+1),children:o.retry}),children:_}),s.length>0&&k(`ul`,{className:`list-unstyled d-flex flex-column gap-2 mb-0`,"aria-label":o.conversations,children:s.map(e=>k(`li`,{children:A(d,{padding:`sm`,className:`border`,bodyClassName:`d-flex align-items-center gap-2`,children:[k(u,{variant:`ghost`,className:`flex-grow-1 text-start`,style:{minWidth:0},onClick:()=>t(e),children:A(`span`,{className:`d-flex flex-column`,style:{minWidth:0},children:[k(`span`,{className:`text-truncate fw-semibold`,children:e.title||o.untitled}),k(`time`,{dateTime:e.lastMessageDateTimeUtc,className:`wui-text-xs text-body-secondary`,children:R(e.lastMessageDateTimeUtc)})]})}),k(c,{icon:`trash-2`,danger:!0,label:o.deleteConversation,onClick:()=>{P(``),O(e)}})]})},e.publicId))}),h&&A(`div`,{className:`d-flex flex-column gap-2`,"aria-hidden":`true`,children:[k(v,{height:44}),k(v,{height:44}),k(v,{height:44})]}),!h&&b&&k(u,{variant:`secondary`,className:`align-self-center`,onClick:()=>F(s.length),children:o.loadMore}),k(f,{open:E!==null,title:o.deleteConfirmTitle,message:N?k(l,{variant:`danger`,children:N}):o.deleteConfirmMessage,confirmLabel:o.deleteConfirm,cancelLabel:o.cancel,danger:!0,loading:j,onConfirm:I,onClose:()=>{j||O(null)}})]})}var B={zIndex:`var(--wui-z-fixed)`,bottom:0},V={end:{insetInlineEnd:0},start:{insetInlineStart:0}};function H({onClick:e,open:t=!1,position:n=`end`,icon:r=`sparkles`,compact:i=!1,className:o,style:s,labels:c}){let l=a(c);return i?k(U,{onClick:e,open:t,position:n,icon:r,className:o,style:s,label:l.launcher}):k(`span`,{"data-support-capture":`exclude`,className:[`position-fixed m-3`,o].filter(Boolean).join(` `),style:{...B,...V[n],...s},children:k(u,{leftIcon:r,className:`rounded-pill`,onClick:e,"aria-haspopup":`dialog`,"aria-expanded":t,children:l.launcher})})}function U({onClick:e,open:t,position:n,icon:r,className:i,style:a,label:o}){let s=x(n);return s?j(k(`span`,{className:[`wui-floating-stack-item wui-floating-stack-item--primary`,i].filter(Boolean).join(` `),style:a,children:k(u,{className:`wui-floating-btn`,onClick:e,"aria-label":o,"aria-haspopup":`dialog`,"aria-expanded":t,tooltip:o,tooltipPlacement:n===`start`?`end`:`start`,children:k(h,{name:r,size:24})})}),s):null}function W({tokens:e,resolveLink:t,linkComponent:n}){return e.map(e=>{switch(e.type){case`code`:return k(`code`,{children:e.text},e.key);case`strong`:return k(`strong`,{children:k(W,{tokens:e.children,resolveLink:t,linkComponent:n})},e.key);case`em`:return k(`em`,{children:k(W,{tokens:e.children,resolveLink:t,linkComponent:n})},e.key);case`link`:{let r=k(W,{tokens:e.children,resolveLink:t,linkComponent:n}),a=i(e.href,t);return a?k(g,{href:a,linkComponent:n,animation:`static`,children:r},e.key):k(`span`,{children:r},e.key)}default:return k(S,{children:e.text},e.key)}})}function G({text:e,resolveLink:t,linkComponent:n}){return k(W,{tokens:s(e),resolveLink:t,linkComponent:n})}function K({block:e,resolveLink:t,linkComponent:n}){let r=e=>k(G,{text:e,resolveLink:t,linkComponent:n});switch(e.type){case`heading`:return k(`p`,{className:`fw-semibold mb-2`,children:r(e.text)});case`rule`:return k(`hr`,{className:`my-2`});case`code`:return k(`pre`,{className:`wui-text-sm mb-2`,children:k(`code`,{children:e.text})});case`list`:{let t=e.items.map((e,t)=>k(`li`,{children:r(e)},t));return e.ordered?k(`ol`,{className:`mb-2 ps-4`,start:e.start===1?void 0:e.start,children:t}):k(`ul`,{className:`mb-2 ps-4`,children:t})}case`table`:return k(p,{columns:e.header.map((e,t)=>({key:`c${t}`,label:r(e),render:e=>r(e.cells[t]??``)})),rows:e.rows.map((e,t)=>({id:t,cells:e})),density:`compact`,empty:null,wrapperClassName:`mb-2`});default:return k(`p`,{className:`mb-2`,children:e.lines.map((e,t)=>A(`span`,{children:[t>0&&k(`br`,{}),r(e)]},t))})}}function q({text:e,resolveLink:t,linkComponent:n,className:r}){let i=o(e);return k(`div`,{className:[`wui-text-sm text-break`,r].filter(Boolean).join(` `),children:i.map(e=>k(K,{block:e,resolveLink:t,linkComponent:n},e.key))})}function J({rating:e=null,onRate:t,noteMaxLength:n=1e3,labels:r}){let i=a(r),o=T(),[s,c]=D(!1),[d,f]=D(!1),[p,m]=D(``),[h,g]=D(``),[_,v]=D(!1);async function y(e,n){c(!0),g(``);try{await t(e,n),f(e===`Down`&&n===void 0),v(e===`Up`||n!==void 0),n!==void 0&&m(``)}catch{g(i.ratingFailed)}finally{c(!1)}}return A(`div`,{className:`d-flex flex-column gap-2`,children:[A(`div`,{className:`d-flex flex-wrap align-items-center gap-1`,children:[k(u,{size:`sm`,variant:e===`Up`?`subtle`:`ghost`,leftIcon:`check`,"aria-pressed":e===`Up`,disabled:s,onClick:()=>y(`Up`),children:i.rateUp}),k(u,{size:`sm`,variant:e===`Down`?`subtle`:`ghost`,leftIcon:`x`,"aria-pressed":e===`Down`,disabled:s,onClick:()=>y(`Down`),children:i.rateDown}),_&&!d&&k(`span`,{className:`wui-text-xs text-body-secondary`,role:`status`,children:i.rated})]}),h&&k(l,{size:`sm`,variant:`danger`,children:h}),d&&A(`form`,{className:`d-flex flex-column gap-2`,onSubmit:e=>{e.preventDefault(),y(`Down`,p)},children:[k(b,{id:`${o}-note`,label:i.ratingNote,hint:i.ratingNoteHint,rows:2,maxLength:n,value:p,onChange:e=>m(e.target.value)}),A(`div`,{className:`d-flex gap-2`,children:[k(u,{type:`submit`,size:`sm`,loading:s,children:i.ratingNoteSend}),k(u,{size:`sm`,variant:`secondary`,disabled:s,onClick:()=>{f(!1),v(!0)},children:i.ratingNoteSkip})]})]})]})}var Y={maxWidth:`85%`,whiteSpace:`pre-wrap`,overflowWrap:`anywhere`};function X({turn:e,onRate:t,resolveLink:n,linkComponent:r,confirmAction:i,onViewTicket:o,renderReply:s,labels:c}){let u=a(c),f=e.streaming&&!e.answer;return A(`li`,{className:`d-flex flex-column gap-2`,children:[e.question&&A(`div`,{className:`align-self-end`,style:Y,children:[A(`span`,{className:`visually-hidden`,children:[u.you,`: `]}),k(d,{padding:`sm`,variant:`muted`,className:`wui-text-sm`,children:e.question})]}),A(`div`,{className:`d-flex flex-column gap-2`,style:{minWidth:0},children:[A(`span`,{className:`visually-hidden`,children:[u.assistant,`: `]}),k(`div`,{"aria-live":`polite`,"aria-busy":e.streaming,children:f?A(`span`,{className:`d-inline-flex align-items-center gap-2 wui-text-sm text-body-secondary`,children:[k(y,{size:`sm`,label:u.thinking}),k(`span`,{"aria-hidden":`true`,children:u.thinking})]}):e.answer&&(s?s(e,{streaming:!!e.streaming}):k(q,{text:e.answer,resolveLink:n,linkComponent:r}))}),e.error&&k(l,{size:`sm`,variant:`danger`,children:e.error.message}),i&&!e.streaming&&e.actions.map(e=>k(P,{action:e,confirmAction:i,onViewTicket:o,labels:c},e.token)),t&&e.messageId&&!e.streaming&&k(J,{rating:e.rating,onRate:(n,r)=>t(e.id,n,r),labels:c})]})]})}function Z({turns:e,empty:t,onRate:n,resolveLink:r,linkComponent:i,confirmAction:o,onViewTicket:s,renderReply:c,labels:l}){let u=a(l),d=E(null),f=e[e.length-1];return w(()=>{d.current?.scrollIntoView({block:`end`})},[e.length,f?.answer,f?.error,f?.actions]),e.length===0?t??k(m,{icon:`sparkles`,size:`sm`,title:u.emptyTitle,text:u.emptyText}):A(O,{children:[k(`ol`,{className:`list-unstyled d-flex flex-column gap-3 mb-0`,"aria-label":u.thread,children:e.map(e=>k(X,{turn:e,onRate:n,resolveLink:r,linkComponent:i,confirmAction:o,onViewTicket:s,renderReply:c,labels:l},e.id))}),k(`div`,{ref:d})]})}function Q({chat:e,open:t,onClose:n,title:r,titleIcon:i=`sparkles`,headerActions:o,empty:s,disclaimer:u,maxLength:d=4e3,allowanceThreshold:f=10,width:p=`440px`,resolveLink:m,linkComponent:g,confirmAction:v,onViewTicket:b,rating:x=!0,renderReply:S,labels:C}){let T=a(C),D=E(null),{refreshAllowance:O}=e;w(()=>{t&&O().catch(()=>{})},[t,O]);let j=e.messagesLeftToday,M=r??T.panelTitle;return k(_,{show:t,onHide:n,side:`right`,width:p,header:A(`div`,{className:`drawer-header`,children:[A(`div`,{className:`drawer-title`,children:[i&&k(`span`,{className:`drawer-title-ic`,children:k(h,{name:i,size:18})}),M]}),A(`div`,{className:`d-flex align-items-center gap-1`,children:[o,k(c,{icon:`plus`,label:T.newChat,onClick:e.newConversation,disabled:e.turns.length===0&&!e.loading}),k(c,{icon:`x`,label:T.close,onClick:n})]})]}),footer:A(`div`,{className:`d-flex flex-column gap-2`,children:[j!==null&&j<=f&&k(`p`,{className:`wui-text-xs text-body-secondary mb-0`,role:`status`,children:T.allowance(j)}),k(F,{onSubmit:e.submit,busy:e.streaming||e.loading,disabled:j===0,maxLength:d,inputRef:D,labels:C}),k(`p`,{className:`wui-text-xs text-body-secondary mb-0`,children:u??T.disclaimer})]}),ariaLabel:typeof M==`string`?M:T.panelTitle,onEntered:()=>D.current?.focus(),children:e.loading?k(`div`,{className:`d-flex justify-content-center py-4`,children:k(y,{label:T.loadingConversation})}):e.loadError?k(l,{variant:`danger`,children:e.loadError.message}):k(Z,{turns:e.turns,empty:s,onRate:x?e.rate:void 0,resolveLink:m,linkComponent:g,confirmAction:v,onViewTicket:b,renderReply:S,labels:C})})}function $(e){return{code:e?.code||`unknown`,message:e?.message||`Something went wrong. Please try again.`,resetsAtUtc:e?.resetsAtUtc??null}}function ee(e){if(!e)return[];let t=JSON.parse(e);return Array.isArray(t)?t:[]}function te(e){let t=[];for(let n of e??[]){if(n.role===`User`){t.push({id:n.publicId,question:n.content,answer:``,streaming:!1,step:null,error:null,actions:[],messageId:null,rating:null,ratingNote:null});continue}let e=t[t.length-1],r={answer:n.content,actions:ee(n.actionsJson),messageId:n.publicId,rating:n.rating??null,ratingNote:n.ratingNote??null};e&&e.messageId===null&&e.answer===``?Object.assign(e,r):t.push({id:n.publicId,question:``,streaming:!1,step:null,error:null,...r})}return t}function ne({client:e,pageContext:t}){let[n,r]=D([]),[i,a]=D(null),[o,s]=D(null),[c,l]=D(!1),[u,d]=D(null),f=E(null),p=E(0),m=E(0),h=E(t);h.current=t;let g=E(null);g.current=i;let _=E(n);_.current=n;let v=C(()=>{p.current+=1,f.current?.abort(),f.current=null},[]);w(()=>v,[v]);let y=C((e,t)=>{r(n=>n.map(n=>n.id===e?{...n,...typeof t==`function`?t(n):t}:n))},[]),b=C(async t=>{let n=typeof t==`string`?t.trim():``;if(!n||f.current)return;let i=p.current,o=new AbortController;f.current=o,m.current+=1;let c=`turn-${m.current}`;r(e=>[...e,{id:c,question:n,answer:``,streaming:!0,step:null,error:null,actions:[],messageId:null,rating:null,ratingNote:null}]);let l=()=>i===p.current&&!o.signal.aborted;try{let t=await e.sendMessage({message:n,conversationId:g.current,pageContext:h.current},{signal:o.signal,onEvent:(e,t)=>{l()&&(e===`delta`?y(c,e=>({answer:e.answer+(t?.text??``),step:null})):e===`reset`?y(c,{answer:``,step:null}):e===`tool`?y(c,{step:typeof t?.toolName==`string`&&t.toolName?t.toolName:null}):e===`actions`&&y(c,{actions:Array.isArray(t?.actions)?t.actions:[]}))}});if(!l())return;y(c,{answer:t.reply,messageId:t.messageId??null,streaming:!1,step:null}),g.current=t.conversationId,a(t.conversationId),s(t.messagesLeftToday??null)}catch(e){if(!l())return;y(c,{answer:``,actions:[],streaming:!1,step:null,error:$(e)}),e?.code===`daily-limit`&&s(0),e?.code===`conversation-not-found`&&(g.current=null,a(null))}finally{f.current===o&&(f.current=null)}},[e,y]),x=C(async t=>{v();let n=p.current;r([]),a(null),d(null),l(!0);try{let i=await e.getConversation(t);if(n!==p.current)return;r(te(i.messages)),a(i.publicId)}catch(e){if(n!==p.current)return;d($(e))}finally{n===p.current&&l(!1)}},[v,e]),S=C(()=>{v(),r([]),a(null),d(null),l(!1)},[v]),T=C(()=>{v(),r([]),a(null),s(null),d(null),l(!1)},[v]),O=C(async()=>{let t=p.current,n=await e.getAllowance();t===p.current&&s(n?.messagesLeftToday??null)},[e]),k=C(async(t,n,r)=>{let i=_.current.find(e=>e.id===t),a=g.current;if(!i?.messageId||!a)throw Error(`Only a stored reply can be rated.`);let o={rating:i.rating,ratingNote:i.ratingNote},s=typeof r==`string`&&r.trim()?r.trim():null;y(t,{rating:n,ratingNote:s});try{await e.rateMessage(a,i.messageId,{rating:n,note:s})}catch(e){throw y(t,o),e}},[e,y]);return{turns:n,conversationId:i,messagesLeftToday:o,streaming:n.some(e=>e.streaming),loading:c,loadError:u,submit:b,openConversation:x,newConversation:S,rate:k,refreshAllowance:O,reset:T}}export{P as ActionCard,e as ChatApiError,F as ChatComposer,z as ChatConversationList,H as ChatLauncher,q as ChatMarkdown,X as ChatMessage,Q as ChatPanel,Z as ChatThread,t as DEFAULT_CHAT_LABELS,J as ReplyRating,n as createChatClient,r as createSseParser,ne as useChat};
|
package/dist/types/useChat.d.ts
CHANGED
|
@@ -17,6 +17,11 @@ export type ChatTurn = {
|
|
|
17
17
|
*/
|
|
18
18
|
answer: string;
|
|
19
19
|
streaming: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* The name of the tool the assistant is running, or null. Set when a tool starts;
|
|
22
|
+
* cleared when answer text arrives, on a reset and when the turn ends.
|
|
23
|
+
*/
|
|
24
|
+
step: string | null;
|
|
20
25
|
error: ChatTurnError | null;
|
|
21
26
|
actions: import('@webority/ui-elements/chat-core').SupportAction[];
|
|
22
27
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/chat-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.56.0",
|
|
4
4
|
"description": "Webority AI assistant for React portals: a streaming chat client, a store-agnostic chat hook, and the launcher, panel, thread, rating and support action card, built on @webority/ui-react.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"react": "^19.0.0",
|
|
31
31
|
"react-bootstrap": "^2.10.0",
|
|
32
32
|
"react-dom": "^19.0.0",
|
|
33
|
-
"@webority/ui-elements": "0.
|
|
34
|
-
"@webority/ui-react": "0.
|
|
33
|
+
"@webority/ui-elements": "0.56.0",
|
|
34
|
+
"@webority/ui-react": "0.56.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@testing-library/dom": "10.4.2",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"typescript": "^7.0.2",
|
|
50
50
|
"vite": "^8.0.12",
|
|
51
51
|
"vitest": "5.0.1",
|
|
52
|
-
"@webority/ui-elements": "0.
|
|
53
|
-
"@webority/ui-react": "0.
|
|
52
|
+
"@webority/ui-elements": "0.56.0",
|
|
53
|
+
"@webority/ui-react": "0.56.0"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "vite build && tsc -p tsconfig.json",
|