@bikdotai/bik-component-library 0.0.870 → 0.0.871

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.
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),Fe=require("../../assets/icons/arrow_down.svg.js"),f=require("../../assets/icons/bik_ai_stars_icon.svg.js"),le=require("../../assets/icons/cross.svg.js"),Oe=require("../../assets/icons/refresh.svg.js"),i=require("react"),p=require("../../constants/Theme.js"),T=require("../TypographyStyle.js"),qe=require("./CopilotFeedbackModal.js"),o=require("./CopilotPanel.styled.js"),We=require("./copilotRegenerate.js"),Ne=require("./copilotSelectionPrompt.js"),$e=require("./utils/markdown.js"),De=require("./utils/toolCtas.js"),se=require("../dot-pulse/DotPulse.js"),Ue=r=>r&&typeof r=="object"&&"default"in r?r:{default:r},Ve=Ue(i),Ke=96,Xe=24,Ge=1800,ze=async r=>{try{if(navigator.clipboard?.writeText)return await navigator.clipboard.writeText(r),!0}catch{}try{const l=document.createElement("textarea");l.value=r,l.setAttribute("readonly",""),l.style.position="fixed",l.style.top="-1000px",l.style.opacity="0",document.body.appendChild(l),l.select();const S=document.execCommand("copy");return document.body.removeChild(l),S}catch{return!1}},Je=48,Ze=r=>{if(!r||!Number.isFinite(r))return"";const l=new Date(r);return Number.isNaN(l.getTime())?"":l.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"})},Qe=()=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"16",height:"16",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("path",{d:"M12 19V5"}),e.jsx("path",{d:"M6 11l6-6 6 6"})]}),Ye=()=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("path",{d:"M5 12h14"}),e.jsx("path",{d:"M13 6l6 6-6 6"})]}),ce=({size:r=16})=>e.jsx("svg",{viewBox:"0 0 24 24",width:r,height:r,fill:"currentColor","aria-hidden":"true",children:e.jsx("rect",{x:"7",y:"7",width:"10",height:"10",rx:"2"})}),et=()=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("rect",{x:"9",y:"9",width:"11",height:"11",rx:"2"}),e.jsx("path",{d:"M15 5.5A1.5 1.5 0 0 0 13.5 4H5.5A1.5 1.5 0 0 0 4 5.5v8A1.5 1.5 0 0 0 5.5 15"})]}),tt=()=>e.jsx("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor",strokeWidth:"2.2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:e.jsx("path",{d:"M5 13l4 4L19 7"})}),ot=()=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("path",{d:"M20 12a8 8 0 1 1-2.34-5.66"}),e.jsx("path",{d:"M20 4v4h-4"})]}),rt=({filled:r=!1})=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:r?"currentColor":"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("path",{d:"M7 10v11H4a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h3Z"}),e.jsx("path",{d:"M7 10l4.5-7a2 2 0 0 1 3.7 1.3L14.5 9H19a2 2 0 0 1 2 2.3l-1.1 7A2 2 0 0 1 18 20H7"})]}),nt=({filled:r=!1})=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:r?"currentColor":"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("path",{d:"M7 14V3H4a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h3Z"}),e.jsx("path",{d:"M7 14l4.5 7a2 2 0 0 0 3.7-1.3L14.5 15H19a2 2 0 0 0 2-2.3l-1.1-7A2 2 0 0 0 18 4H7"})]}),it=({messages:r,onSend:l,onClose:S,title:k="Copilot",badge:q,subtitle:W,avatar:de,placeholder:ae="Write your message...",disclaimer:N="AI can make mistakes, Verify the responses.",emptyStateTitle:he,emptyStateSubtitle:$="Here are some things you can try",suggestions:v,onSuggestionClick:ue,showRefresh:pe=!0,onRefresh:D,onRegenerate:U,onFeedback:M,dislikeTitle:xe,dislikeSubtitle:je,dislikeReasons:fe,dislikeNotePlaceholder:ke,dislikeSubmitLabel:Ce,isThinking:C=!1,thinkingLabel:V="Thinking",isStreaming:we=!1,onStop:K,interruptedLabel:X="This message was interrupted",followUpsLabel:G="Follow ups",onToolCtaClick:ye,hasMoreHistory:be=!1,isLoadingHistory:w=!1,onLoadOlderMessages:z,historyLoadingLabel:ge="Loading earlier messages",inputDisabled:d=!1,attachment:a,onAttachmentRemove:J,errorMessage:Z,width:Te,height:Se,className:ve,style:Me})=>{const[R,Q]=i.useState(""),[Re,Y]=i.useState(!1),[L,A]=i.useState(!0),[y,ee]=i.useState(null),[u,Le]=i.useState({}),[b,I]=i.useState(null),B=i.useRef(null),x=i.useRef(null),m=i.useRef(null),_=i.useRef(null),H=i.useRef(0),P=i.useRef(null),g=r.filter(t=>(t.content.length>0||t.wasInterrupted)&&!We.isCopilotRegeneratePrompt(t.content)),te=g.length===0&&!C,h=C||we,oe=!d&&!h&&(R.trim().length>0||!!a),j=h&&!!K,E=g[g.length-1],F=!h&&E?.role==="assistant"?E.followUps:void 0,O=!te&&!L;i.useEffect(()=>{L&&m.current?.scrollIntoView({behavior:"smooth"})},[r,C,L]),i.useEffect(()=>{const t=_.current,n=P.current;!t||n===null||w||(P.current=null,t.scrollTop=t.scrollHeight-n,H.current=t.scrollTop)},[r,w]);const Ae=()=>{const t=_.current;if(!t)return;const n=t.scrollHeight-t.scrollTop-t.clientHeight;t.scrollTop<=Je&&be&&!w&&z&&(P.current=t.scrollHeight-t.scrollTop,z());const c=t.scrollTop<H.current-1;if(H.current=t.scrollTop,n<=Xe){A(!0);return}c&&A(!1)},Ie=()=>{A(!0),m.current?.scrollIntoView({behavior:"smooth"})};i.useEffect(()=>()=>{x.current&&window.clearTimeout(x.current)},[]);const Be=async t=>{await ze(t.content)&&(x.current&&window.clearTimeout(x.current),ee(t.id),x.current=window.setTimeout(()=>ee(null),Ge))},re=(t,n)=>{const c=u[t]===n;if(Le(s=>({...s,[t]:s[t]===n?void 0:n})),c){n==="dislike"&&I(s=>s===t?null:s);return}if(n==="like"){M?.(t,"like");return}b&&b!==t&&M?.(b,"dislike"),I(t)},ne=(t,n)=>{I(c=>c===t?null:c),M?.(t,"dislike",n)},me=()=>{d||h||U?.()},ie=()=>{const t=R.trim();if(!t&&!a||d||h)return;l(a?Ne.buildCopilotSelectionPrompt(a,t):t),Q(""),J?.();const n=B.current;n&&(n.style.height="auto")};i.useEffect(()=>{a&&B.current?.focus()},[a]);const _e=t=>{Q(t.target.value);const n=t.target;n.style.height="auto",n.style.height=`${Math.min(n.scrollHeight,Ke)}px`},He=t=>{t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),ie())},Pe=t=>{d||h||(ue?.(t),l(t.text))},Ee=t=>{d||h||l(t)};return e.jsxs(o.PanelContainer,{className:ve,style:{width:Te,height:Se,...Me},role:"dialog","aria-label":k,children:[e.jsxs(o.PanelHeader,{children:[e.jsx(o.HeaderAvatar,{children:de??e.jsx(f.default,{width:18,height:18})}),e.jsxs(o.HeaderText,{children:[e.jsxs(o.HeaderTitleRow,{children:[e.jsx(T.TitleSmall,{children:k}),q&&e.jsx(o.HeaderBadge,{children:q})]}),W&&e.jsx(T.BodyCaption,{color:p.COLORS.content.secondary,children:W})]}),e.jsxs(o.HeaderActions,{children:[pe&&D&&e.jsx(o.IconAction,{type:"button",onClick:D,"aria-label":"Start a new conversation",children:e.jsx(Oe.default,{width:16,height:16})}),e.jsx(o.IconAction,{type:"button",onClick:S,"aria-label":"Close copilot",children:e.jsx(le.default,{width:18,height:18})})]})]}),e.jsxs(o.MessagesViewport,{children:[e.jsx(o.MessagesArea,{ref:_,onScroll:Ae,role:"region","aria-label":"Conversation",children:te?e.jsxs(o.EmptyState,{children:[e.jsx(o.EmptyStateIcon,{children:e.jsx(f.default,{width:28,height:28})}),e.jsx(T.BodySecondary,{style:{fontWeight:600},children:he||k}),$&&e.jsx(T.BodyCaption,{color:p.COLORS.content.secondary,children:$}),v&&v.length>0&&e.jsx(o.SuggestionList,{children:v.map(t=>e.jsxs(o.SuggestionChip,{type:"button",disabled:d||h,onClick:()=>Pe(t),children:[e.jsx(f.default,{width:14,height:14}),t.text]},t.id))})]}):e.jsxs(e.Fragment,{children:[w&&e.jsxs(o.HistoryLoader,{role:"status","aria-live":"polite",children:[e.jsx(se.DotPulse,{color:p.COLORS.content.placeholder,size:4}),ge]}),g.map(t=>{const n=t.role==="user"?Ze(t.timestamp):"";return e.jsxs(Ve.default.Fragment,{children:[t.content.length>0&&e.jsx(o.MessageBubble,{$role:t.role,children:t.role==="assistant"?e.jsx("div",{dangerouslySetInnerHTML:{__html:$e.renderCopilotMarkdown(t.content)}}):e.jsx("div",{style:{whiteSpace:"pre-wrap"},children:t.content})}),t.role==="assistant"&&!t.isStreaming&&(()=>{const c=De.toolResultCtas(t.toolResults);return c.length===0?null:e.jsx(o.ToolCtaList,{children:c.map(s=>e.jsx(o.ToolCtaLink,{href:s.url,target:"_blank",rel:"noopener noreferrer",onClick:()=>ye?.({tool:s.tool,url:s.url}),children:s.label},`${s.tool}-${s.url}`))})})(),n&&e.jsx(o.MessageTimestamp,{children:n}),t.wasInterrupted&&X&&e.jsxs(o.InterruptedNote,{children:[e.jsx(ce,{size:12}),X]}),t.role==="assistant"&&t.content.length>0&&!t.isStreaming&&e.jsxs(o.MessageActions,{children:[e.jsx(o.MessageAction,{type:"button",onClick:()=>{Be(t)},"data-copied":y===t.id,title:y===t.id?"Copied":"Copy","aria-label":y===t.id?"Response copied to clipboard":"Copy response",children:y===t.id?e.jsx(tt,{}):e.jsx(et,{})}),U&&t.id===E?.id&&e.jsx(o.MessageAction,{type:"button",onClick:me,disabled:d||h,title:"Regenerate","aria-label":"Regenerate response",children:e.jsx(ot,{})}),e.jsx(o.MessageAction,{type:"button",onClick:()=>re(t.id,"like"),"data-selected":u[t.id]==="like","aria-pressed":u[t.id]==="like",title:"Good response","aria-label":"Good response",children:e.jsx(rt,{filled:u[t.id]==="like"})}),e.jsx(o.MessageAction,{type:"button",onClick:()=>re(t.id,"dislike"),"data-selected":u[t.id]==="dislike","aria-pressed":u[t.id]==="dislike",title:"Bad response","aria-label":"Bad response",children:e.jsx(nt,{filled:u[t.id]==="dislike"})})]}),b===t.id&&e.jsx(qe.CopilotFeedbackModal,{title:xe,subtitle:je??`Help us improve ${k}`,reasons:fe,notePlaceholder:ke,submitLabel:Ce,onSubmit:c=>ne(t.id,c),onDismiss:()=>ne(t.id)})]},t.id)}),F&&F.length>0&&e.jsxs(o.FollowUpList,{children:[G&&e.jsx(o.FollowUpsLabel,{children:G}),F.map(t=>e.jsxs(o.FollowUpChip,{type:"button",disabled:d,onClick:()=>Ee(t),children:[e.jsx(Ye,{}),t]},t))]}),C&&e.jsxs(o.ThinkingBubble,{role:"status","aria-live":"polite",children:[e.jsx(f.default,{width:14,height:14,color:p.COLORS.content.brand}),V&&e.jsx(o.ThinkingLabel,{children:V}),e.jsx(se.DotPulse,{color:p.COLORS.background.brand,size:5})]}),e.jsx("div",{ref:m})]})}),e.jsx(o.ScrollFade,{$visible:O,"aria-hidden":"true"}),e.jsx(o.JumpToLatestButton,{type:"button",$visible:O,onClick:Ie,tabIndex:O?0:-1,"aria-label":"Scroll to the latest message",children:e.jsx(Fe.default,{width:16,height:16})})]}),Z&&e.jsx(o.ErrorBanner,{role:"alert",children:Z}),e.jsxs(o.InputArea,{children:[a&&e.jsxs(o.AttachmentChip,{children:[e.jsx(f.default,{width:14,height:14,color:p.COLORS.content.brand}),e.jsx(o.AttachmentText,{title:a,children:a}),e.jsx(o.AttachmentRemove,{type:"button",onClick:J,"aria-label":"Remove the selected text",children:e.jsx(le.default,{width:12,height:12})})]}),e.jsxs(o.InputWrapper,{$focused:Re,children:[e.jsx(o.MessageInput,{ref:B,rows:1,value:R,placeholder:ae,disabled:d,onChange:_e,onKeyDown:He,onFocus:()=>Y(!0),onBlur:()=>Y(!1)}),e.jsx(o.SendButton,{type:"button",$active:j||oe,disabled:!j&&!oe,onClick:j?K:ie,"aria-label":j?"Stop generating":"Send message",children:j?e.jsx(ce,{}):e.jsx(Qe,{})})]})]}),N&&e.jsx(o.Disclaimer,{children:N})]})};exports.CopilotPanel=it;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),Oe=require("../../assets/icons/arrow_down.svg.js"),f=require("../../assets/icons/bik_ai_stars_icon.svg.js"),se=require("../../assets/icons/cross.svg.js"),qe=require("../../assets/icons/refresh.svg.js"),i=require("react"),p=require("../../constants/Theme.js"),T=require("../TypographyStyle.js"),We=require("./CopilotFeedbackModal.js"),o=require("./CopilotPanel.styled.js"),Ne=require("./copilotRegenerate.js"),$e=require("./copilotSelectionPrompt.js"),Ue=require("./utils/markdown.js"),Ve=require("./utils/toolCtas.js"),ce=require("../dot-pulse/DotPulse.js"),De=r=>r&&typeof r=="object"&&"default"in r?r:{default:r},Ke=De(i),Xe=96,Ge=24,Je=1800,ze=async r=>{try{if(navigator.clipboard?.writeText)return await navigator.clipboard.writeText(r),!0}catch{}try{const l=document.createElement("textarea");l.value=r,l.setAttribute("readonly",""),l.style.position="fixed",l.style.top="-1000px",l.style.opacity="0",document.body.appendChild(l),l.select();const S=document.execCommand("copy");return document.body.removeChild(l),S}catch{return!1}},Ze=48,Qe=r=>{if(!r||!Number.isFinite(r))return"";const l=new Date(r);return Number.isNaN(l.getTime())?"":l.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"})},Ye=()=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"16",height:"16",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("path",{d:"M12 19V5"}),e.jsx("path",{d:"M6 11l6-6 6 6"})]}),et=()=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("path",{d:"M5 12h14"}),e.jsx("path",{d:"M13 6l6 6-6 6"})]}),de=({size:r=16})=>e.jsx("svg",{viewBox:"0 0 24 24",width:r,height:r,fill:"currentColor","aria-hidden":"true",children:e.jsx("rect",{x:"7",y:"7",width:"10",height:"10",rx:"2"})}),tt=()=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("rect",{x:"9",y:"9",width:"11",height:"11",rx:"2"}),e.jsx("path",{d:"M15 5.5A1.5 1.5 0 0 0 13.5 4H5.5A1.5 1.5 0 0 0 4 5.5v8A1.5 1.5 0 0 0 5.5 15"})]}),ot=()=>e.jsx("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor",strokeWidth:"2.2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:e.jsx("path",{d:"M5 13l4 4L19 7"})}),rt=()=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("path",{d:"M20 12a8 8 0 1 1-2.34-5.66"}),e.jsx("path",{d:"M20 4v4h-4"})]}),nt=({filled:r=!1})=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:r?"currentColor":"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("path",{d:"M7 10v11H4a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h3Z"}),e.jsx("path",{d:"M7 10l4.5-7a2 2 0 0 1 3.7 1.3L14.5 9H19a2 2 0 0 1 2 2.3l-1.1 7A2 2 0 0 1 18 20H7"})]}),it=({filled:r=!1})=>e.jsxs("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:r?"currentColor":"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[e.jsx("path",{d:"M7 14V3H4a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h3Z"}),e.jsx("path",{d:"M7 14l4.5 7a2 2 0 0 0 3.7-1.3L14.5 15H19a2 2 0 0 0 2-2.3l-1.1-7A2 2 0 0 0 18 4H7"})]}),lt=({messages:r,onSend:l,onClose:S,title:C="Copilot",badge:O,subtitle:q,avatar:ae,placeholder:ue="Write your message...",disclaimer:W="AI can make mistakes, Verify the responses.",emptyStateTitle:he,emptyStateSubtitle:N="Here are some things you can try",suggestions:v,onSuggestionClick:pe,showRefresh:xe=!0,onRefresh:$,onRegenerate:U,onFeedback:R,dislikeTitle:je,dislikeSubtitle:fe,dislikeReasons:Ce,dislikeNotePlaceholder:we,dislikeSubmitLabel:ke,isThinking:w=!1,thinkingLabel:V="Thinking",isStreaming:ye=!1,onStop:D,interruptedLabel:K="This message was interrupted",followUpsLabel:X="Follow ups",onToolCtaClick:be,hasMoreHistory:ge=!1,isLoadingHistory:k=!1,onLoadOlderMessages:G,historyLoadingLabel:Te="Loading earlier messages",inputDisabled:d=!1,attachment:u,onAttachmentRemove:J,errorMessage:z,width:Se,height:ve,className:Re,style:Me})=>{const[M,Z]=i.useState(""),[Le,Q]=i.useState(!1),[L,A]=i.useState(!0),[y,Y]=i.useState(null),[ee,Ae]=i.useState({}),[h,I]=i.useState(null),b=i.useRef(null),te=i.useRef(!1),x=i.useRef(null),m=i.useRef(null),B=i.useRef(null),_=i.useRef(0),E=i.useRef(null),g=r.filter(t=>(t.content.length>0||t.wasInterrupted)&&!Ne.isCopilotRegeneratePrompt(t.content)),oe=g.length===0&&!w,s=w||ye,re=!d&&!s&&(M.trim().length>0||!!u),j=s&&!!D,H=g[g.length-1],P=!s&&H?.role==="assistant"?H.followUps:void 0,F=!oe&&!L;i.useEffect(()=>{L&&m.current?.scrollIntoView({behavior:"smooth"})},[r,w,L]),i.useEffect(()=>{const t=B.current,n=E.current;!t||n===null||k||(E.current=null,t.scrollTop=t.scrollHeight-n,_.current=t.scrollTop)},[r,k]);const Ie=()=>{const t=B.current;if(!t)return;const n=t.scrollHeight-t.scrollTop-t.clientHeight;t.scrollTop<=Ze&&ge&&!k&&G&&(E.current=t.scrollHeight-t.scrollTop,G());const a=t.scrollTop<_.current-1;if(_.current=t.scrollTop,n<=Ge){A(!0);return}a&&A(!1)},me=()=>{A(!0),m.current?.scrollIntoView({behavior:"smooth"})};i.useEffect(()=>()=>{x.current&&window.clearTimeout(x.current)},[]);const Be=async t=>{await ze(t.content)&&(x.current&&window.clearTimeout(x.current),Y(t.id),x.current=window.setTimeout(()=>Y(null),Je))},ne=(t,n)=>{const a=ee[t]===n;if(Ae(c=>({...c,[t]:c[t]===n?void 0:n})),a){n==="dislike"&&I(c=>c===t?null:c);return}if(n==="like"){R?.(t,"like");return}h&&h!==t&&R?.(h,"dislike"),I(t)},ie=(t,n)=>{I(a=>a===t?null:a),R?.(t,"dislike",n)},_e=()=>{d||s||U?.()},le=()=>{const t=M.trim();if(!t&&!u||d||s)return;l(u?$e.buildCopilotSelectionPrompt(u,t):t),Z(""),J?.();const n=b.current;n&&(n.style.height="auto")};i.useEffect(()=>{u&&b.current?.focus()},[u]),i.useEffect(()=>{const t=te.current&&!s;te.current=s,t&&!d&&!h&&b.current?.focus()},[s,d,h]);const Ee=t=>{Z(t.target.value);const n=t.target;n.style.height="auto",n.style.height=`${Math.min(n.scrollHeight,Xe)}px`},He=t=>{t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),le())},Pe=t=>{d||s||(pe?.(t),l(t.text))},Fe=t=>{d||s||l(t)};return e.jsxs(o.PanelContainer,{className:Re,style:{width:Se,height:ve,...Me},role:"dialog","aria-label":C,children:[e.jsxs(o.PanelHeader,{children:[e.jsx(o.HeaderAvatar,{children:ae??e.jsx(f.default,{width:18,height:18})}),e.jsxs(o.HeaderText,{children:[e.jsxs(o.HeaderTitleRow,{children:[e.jsx(T.TitleSmall,{children:C}),O&&e.jsx(o.HeaderBadge,{children:O})]}),q&&e.jsx(T.BodyCaption,{color:p.COLORS.content.secondary,children:q})]}),e.jsxs(o.HeaderActions,{children:[xe&&$&&e.jsx(o.IconAction,{type:"button",onClick:$,"aria-label":"Start a new conversation",children:e.jsx(qe.default,{width:16,height:16})}),e.jsx(o.IconAction,{type:"button",onClick:S,"aria-label":"Close copilot",children:e.jsx(se.default,{width:18,height:18})})]})]}),e.jsxs(o.MessagesViewport,{children:[e.jsx(o.MessagesArea,{ref:B,onScroll:Ie,role:"region","aria-label":"Conversation",children:oe?e.jsxs(o.EmptyState,{children:[e.jsx(o.EmptyStateIcon,{children:e.jsx(f.default,{width:28,height:28})}),e.jsx(T.BodySecondary,{style:{fontWeight:600},children:he||C}),N&&e.jsx(T.BodyCaption,{color:p.COLORS.content.secondary,children:N}),v&&v.length>0&&e.jsx(o.SuggestionList,{children:v.map(t=>e.jsxs(o.SuggestionChip,{type:"button",disabled:d||s,onClick:()=>Pe(t),children:[e.jsx(f.default,{width:14,height:14}),t.text]},t.id))})]}):e.jsxs(e.Fragment,{children:[k&&e.jsxs(o.HistoryLoader,{role:"status","aria-live":"polite",children:[e.jsx(ce.DotPulse,{color:p.COLORS.content.placeholder,size:4}),Te]}),g.map(t=>{const n=t.role==="user"?Qe(t.timestamp):"";return e.jsxs(Ke.default.Fragment,{children:[t.content.length>0&&e.jsx(o.MessageBubble,{$role:t.role,children:t.role==="assistant"?e.jsx("div",{dangerouslySetInnerHTML:{__html:Ue.renderCopilotMarkdown(t.content)}}):e.jsx("div",{style:{whiteSpace:"pre-wrap"},children:t.content})}),t.role==="assistant"&&!t.isStreaming&&(()=>{const a=Ve.toolResultCtas(t.toolResults);return a.length===0?null:e.jsx(o.ToolCtaList,{children:a.map(c=>e.jsx(o.ToolCtaLink,{href:c.url,target:"_blank",rel:"noopener noreferrer",onClick:()=>be?.({tool:c.tool,url:c.url}),children:c.label},`${c.tool}-${c.url}`))})})(),n&&e.jsx(o.MessageTimestamp,{children:n}),t.wasInterrupted&&K&&e.jsxs(o.InterruptedNote,{children:[e.jsx(de,{size:12}),K]}),t.role==="assistant"&&t.content.length>0&&!t.isStreaming&&e.jsxs(o.MessageActions,{children:[e.jsx(o.MessageAction,{type:"button",onClick:()=>{Be(t)},"data-copied":y===t.id,title:y===t.id?"Copied":"Copy","aria-label":y===t.id?"Response copied to clipboard":"Copy response",children:y===t.id?e.jsx(ot,{}):e.jsx(tt,{})}),U&&t.id===H?.id&&e.jsx(o.MessageAction,{type:"button",onClick:_e,disabled:d||s,title:"Regenerate","aria-label":"Regenerate response",children:e.jsx(rt,{})}),!t.isRestored&&ee[t.id]===void 0&&e.jsxs(e.Fragment,{children:[e.jsx(o.MessageAction,{type:"button",onClick:()=>ne(t.id,"like"),title:"Good response","aria-label":"Good response",children:e.jsx(nt,{})}),e.jsx(o.MessageAction,{type:"button",onClick:()=>ne(t.id,"dislike"),title:"Bad response","aria-label":"Bad response",children:e.jsx(it,{})})]})]}),h===t.id&&e.jsx(We.CopilotFeedbackModal,{title:je,subtitle:fe??`Help us improve ${C}`,reasons:Ce,notePlaceholder:we,submitLabel:ke,onSubmit:a=>ie(t.id,a),onDismiss:()=>ie(t.id)})]},t.id)}),P&&P.length>0&&e.jsxs(o.FollowUpList,{children:[X&&e.jsx(o.FollowUpsLabel,{children:X}),P.map(t=>e.jsxs(o.FollowUpChip,{type:"button",disabled:d,onClick:()=>Fe(t),children:[e.jsx(et,{}),t]},t))]}),w&&e.jsxs(o.ThinkingBubble,{role:"status","aria-live":"polite",children:[e.jsx(f.default,{width:14,height:14,color:p.COLORS.content.brand}),V&&e.jsx(o.ThinkingLabel,{children:V}),e.jsx(ce.DotPulse,{color:p.COLORS.background.brand,size:5})]}),e.jsx("div",{ref:m})]})}),e.jsx(o.ScrollFade,{$visible:F,"aria-hidden":"true"}),e.jsx(o.JumpToLatestButton,{type:"button",$visible:F,onClick:me,tabIndex:F?0:-1,"aria-label":"Scroll to the latest message",children:e.jsx(Oe.default,{width:16,height:16})})]}),z&&e.jsx(o.ErrorBanner,{role:"alert",children:z}),e.jsxs(o.InputArea,{children:[u&&e.jsxs(o.AttachmentChip,{children:[e.jsx(f.default,{width:14,height:14,color:p.COLORS.content.brand}),e.jsx(o.AttachmentText,{title:u,children:u}),e.jsx(o.AttachmentRemove,{type:"button",onClick:J,"aria-label":"Remove the selected text",children:e.jsx(se.default,{width:12,height:12})})]}),e.jsxs(o.InputWrapper,{$focused:Le,children:[e.jsx(o.MessageInput,{ref:b,rows:1,value:M,placeholder:ue,disabled:d,onChange:Ee,onKeyDown:He,onFocus:()=>Q(!0),onBlur:()=>Q(!1)}),e.jsx(o.SendButton,{type:"button",$active:j||re,disabled:!j&&!re,onClick:j?D:le,"aria-label":j?"Stop generating":"Send message",children:j?e.jsx(de,{}):e.jsx(Ye,{})})]})]}),W&&e.jsx(o.Disclaimer,{children:W})]})};exports.CopilotPanel=lt;
2
2
  //# sourceMappingURL=CopilotPanel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CopilotPanel.js","sources":["../../../../src/components/bik-copilot/CopilotPanel.tsx"],"sourcesContent":["import ArrowDownIcon from '@src/assets/icons/arrow_down.svg';\nimport AiStarsIcon from '@src/assets/icons/bik_ai_stars_icon.svg';\nimport CrossIcon from '@src/assets/icons/cross.svg';\nimport RefreshIcon from '@src/assets/icons/refresh.svg';\nimport React, { useEffect, useRef, useState } from 'react';\nimport { COLORS } from '@src/constants/Theme';\nimport { DotPulse } from '../dot-pulse';\nimport { BodyCaption, BodySecondary, TitleSmall } from '../TypographyStyle';\nimport { CopilotFeedbackModal } from './CopilotFeedbackModal';\nimport {\n\tAttachmentChip,\n\tAttachmentRemove,\n\tAttachmentText,\n\tDisclaimer,\n\tEmptyState,\n\tEmptyStateIcon,\n\tErrorBanner,\n\tFollowUpChip,\n\tFollowUpList,\n\tFollowUpsLabel,\n\tHeaderActions,\n\tHeaderAvatar,\n\tHeaderBadge,\n\tHeaderText,\n\tHeaderTitleRow,\n\tHistoryLoader,\n\tIconAction,\n\tInputArea,\n\tInputWrapper,\n\tInterruptedNote,\n\tJumpToLatestButton,\n\tMessageAction,\n\tMessageActions,\n\tMessageBubble,\n\tMessageInput,\n\tMessagesArea,\n\tMessagesViewport,\n\tMessageTimestamp,\n\tPanelContainer,\n\tPanelHeader,\n\tScrollFade,\n\tSendButton,\n\tSuggestionChip,\n\tSuggestionList,\n\tThinkingBubble,\n\tThinkingLabel,\n\tToolCtaLink,\n\tToolCtaList,\n} from './CopilotPanel.styled';\nimport { isCopilotRegeneratePrompt } from './copilotRegenerate';\nimport { buildCopilotSelectionPrompt } from './copilotSelectionPrompt';\nimport { CopilotFeedbackDetail, CopilotPanelProps } from './model';\nimport { renderCopilotMarkdown } from './utils/markdown';\nimport { toolResultCtas } from './utils/toolCtas';\n\nconst MAX_INPUT_HEIGHT_PX = 96;\n\n/** Slack at the bottom of the scroller that still counts as \"at the latest\". */\nconst AT_BOTTOM_TOLERANCE_PX = 24;\n\n/** How long the copy button shows its confirmation before reverting. */\nconst COPIED_FEEDBACK_MS = 1800;\n\n/**\n * Writes text to the clipboard.\n *\n * `navigator.clipboard` is missing outside a secure context and can reject when\n * the document is not focused, so a hidden-textarea `execCommand` fallback keeps\n * the button working there rather than failing silently.\n */\nconst writeToClipboard = async (text: string): Promise<boolean> => {\n\ttry {\n\t\tif (navigator.clipboard?.writeText) {\n\t\t\tawait navigator.clipboard.writeText(text);\n\t\t\treturn true;\n\t\t}\n\t} catch {\n\t\t// Fall through to the legacy path.\n\t}\n\n\ttry {\n\t\tconst textarea = document.createElement('textarea');\n\t\ttextarea.value = text;\n\t\t// Off-screen rather than hidden: `display: none` cannot be selected.\n\t\ttextarea.setAttribute('readonly', '');\n\t\ttextarea.style.position = 'fixed';\n\t\ttextarea.style.top = '-1000px';\n\t\ttextarea.style.opacity = '0';\n\t\tdocument.body.appendChild(textarea);\n\t\ttextarea.select();\n\t\tconst copied = document.execCommand('copy');\n\t\tdocument.body.removeChild(textarea);\n\t\treturn copied;\n\t} catch {\n\t\treturn false;\n\t}\n};\n\n/** How close to the top counts as asking for the previous page. */\nconst NEAR_TOP_TOLERANCE_PX = 48;\n\nconst formatMessageTime = (timestamp: number): string => {\n\tif (!timestamp || !Number.isFinite(timestamp)) {\n\t\treturn '';\n\t}\n\tconst date = new Date(timestamp);\n\tif (Number.isNaN(date.getTime())) {\n\t\treturn '';\n\t}\n\treturn date.toLocaleTimeString('en-US', {\n\t\thour: 'numeric',\n\t\tminute: '2-digit',\n\t});\n};\n\nconst SendArrowIcon = () => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"16\"\n\t\theight=\"16\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"2\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M12 19V5\" />\n\t\t<path d=\"M6 11l6-6 6 6\" />\n\t</svg>\n);\n\nconst ArrowRightIcon = () => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"2\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M5 12h14\" />\n\t\t<path d=\"M13 6l6 6-6 6\" />\n\t</svg>\n);\n\nconst StopIcon = ({ size = 16 }: { size?: number }) => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth={size}\n\t\theight={size}\n\t\tfill=\"currentColor\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<rect x=\"7\" y=\"7\" width=\"10\" height=\"10\" rx=\"2\" />\n\t</svg>\n);\n\nconst CopyIcon = () => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"1.8\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\" />\n\t\t<path d=\"M15 5.5A1.5 1.5 0 0 0 13.5 4H5.5A1.5 1.5 0 0 0 4 5.5v8A1.5 1.5 0 0 0 5.5 15\" />\n\t</svg>\n);\n\nconst CheckIcon = () => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"2.2\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M5 13l4 4L19 7\" />\n\t</svg>\n);\n\nconst RegenerateIcon = () => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"1.8\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M20 12a8 8 0 1 1-2.34-5.66\" />\n\t\t<path d=\"M20 4v4h-4\" />\n\t</svg>\n);\n\nconst ThumbUpIcon = ({ filled = false }: { filled?: boolean }) => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill={filled ? 'currentColor' : 'none'}\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"1.8\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M7 10v11H4a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h3Z\" />\n\t\t<path d=\"M7 10l4.5-7a2 2 0 0 1 3.7 1.3L14.5 9H19a2 2 0 0 1 2 2.3l-1.1 7A2 2 0 0 1 18 20H7\" />\n\t</svg>\n);\n\nconst ThumbDownIcon = ({ filled = false }: { filled?: boolean }) => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill={filled ? 'currentColor' : 'none'}\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"1.8\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M7 14V3H4a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h3Z\" />\n\t\t<path d=\"M7 14l4.5 7a2 2 0 0 0 3.7-1.3L14.5 15H19a2 2 0 0 0 2-2.3l-1.1-7A2 2 0 0 0 18 4H7\" />\n\t</svg>\n);\n\n/**\n * Chat panel used by the copilot. Fully presentational: it renders the\n * transcript it is given and reports user intent through callbacks, so it works\n * against any backend.\n */\nexport const CopilotPanel: React.FC<CopilotPanelProps> = ({\n\tmessages,\n\tonSend,\n\tonClose,\n\ttitle = 'Copilot',\n\tbadge,\n\tsubtitle,\n\tavatar,\n\tplaceholder = 'Write your message...',\n\tdisclaimer = 'AI can make mistakes, Verify the responses.',\n\temptyStateTitle,\n\temptyStateSubtitle = 'Here are some things you can try',\n\tsuggestions,\n\tonSuggestionClick,\n\tshowRefresh = true,\n\tonRefresh,\n\tonRegenerate,\n\tonFeedback,\n\tdislikeTitle,\n\tdislikeSubtitle,\n\tdislikeReasons,\n\tdislikeNotePlaceholder,\n\tdislikeSubmitLabel,\n\tisThinking = false,\n\tthinkingLabel = 'Thinking',\n\tisStreaming = false,\n\tonStop,\n\tinterruptedLabel = 'This message was interrupted',\n\tfollowUpsLabel = 'Follow ups',\n\tonToolCtaClick,\n\thasMoreHistory = false,\n\tisLoadingHistory = false,\n\tonLoadOlderMessages,\n\thistoryLoadingLabel = 'Loading earlier messages',\n\tinputDisabled = false,\n\tattachment,\n\tonAttachmentRemove,\n\terrorMessage,\n\twidth,\n\theight,\n\tclassName,\n\tstyle,\n}) => {\n\tconst [input, setInput] = useState('');\n\tconst [isInputFocused, setIsInputFocused] = useState(false);\n\tconst [isAtLatest, setIsAtLatest] = useState(true);\n\t// Which reply is showing \"Copied\" — per message, so copying one does not tick\n\t// the button under another.\n\tconst [copiedMessageId, setCopiedMessageId] = useState<string | null>(null);\n\n\tconst [feedback, setFeedback] = useState<\n\t\tRecord<string, 'like' | 'dislike' | undefined>\n\t>({});\n\t// Which reply is being asked about. A thumbs-down opens the prompt and the\n\t// rating is reported once it is answered or dismissed -- so pressing the\n\t// thumb is never lost, and one thumb never becomes two ratings.\n\tconst [dislikePromptId, setDislikePromptId] = useState<string | null>(null);\n\tconst inputRef = useRef<HTMLTextAreaElement>(null);\n\tconst copiedTimeoutRef = useRef<number | null>(null);\n\tconst bottomRef = useRef<HTMLDivElement>(null);\n\tconst scrollerRef = useRef<HTMLDivElement>(null);\n\tconst lastScrollTopRef = useRef(0);\n\t// Distance from the bottom at the moment an older page was asked for. Older\n\t// messages land *above* the viewport, so without restoring against this the\n\t// content under the reader jumps down by the height of what arrived.\n\tconst anchorFromBottomRef = useRef<number | null>(null);\n\n\t// An assistant bubble is pushed the moment a turn starts, before any text has\n\t// arrived; the thinking indicator stands in for it until then, so rendering it\n\t// too would show an empty balloon next to the dots. A turn stopped before its\n\t// first token keeps its place, carrying only the interrupted note.\n\tconst visibleMessages = messages.filter(\n\t\t(message) =>\n\t\t\t(message.content.length > 0 || message.wasInterrupted) &&\n\t\t\t!isCopilotRegeneratePrompt(message.content),\n\t);\n\tconst isEmpty = visibleMessages.length === 0 && !isThinking;\n\tconst isTurnInFlight = isThinking || isStreaming;\n\tconst canSend =\n\t\t!inputDisabled &&\n\t\t!isTurnInFlight &&\n\t\t(input.trim().length > 0 || Boolean(attachment));\n\t// Stopping is the only thing the button can do mid-answer, so it becomes the\n\t// stop button rather than sitting there disabled.\n\tconst canStop = isTurnInFlight && Boolean(onStop);\n\t// Only the newest reply's suggestions are on offer — older ones answer a\n\t// question the conversation has already moved past.\n\tconst newestMessage = visibleMessages[visibleMessages.length - 1];\n\tconst followUps =\n\t\t!isTurnInFlight && newestMessage?.role === 'assistant'\n\t\t\t? newestMessage.followUps\n\t\t\t: undefined;\n\t// A long answer that the reader has scrolled up from: the tail is clipped, so\n\t// the fade and the arrow say there is more below.\n\tconst showScrollAffordance = !isEmpty && !isAtLatest;\n\n\t// Following the conversation is the default, but a reader who has scrolled up\n\t// mid-answer must not be yanked back down by the next chunk.\n\tuseEffect(() => {\n\t\tif (!isAtLatest) {\n\t\t\treturn;\n\t\t}\n\t\tbottomRef.current?.scrollIntoView({ behavior: 'smooth' });\n\t}, [messages, isThinking, isAtLatest]);\n\n\t// Older messages have landed: put the reader back on the message they were\n\t// looking at, which is now that much further down.\n\tuseEffect(() => {\n\t\tconst scroller = scrollerRef.current;\n\t\tconst anchor = anchorFromBottomRef.current;\n\t\tif (!scroller || anchor === null || isLoadingHistory) {\n\t\t\treturn;\n\t\t}\n\t\tanchorFromBottomRef.current = null;\n\t\tscroller.scrollTop = scroller.scrollHeight - anchor;\n\t\tlastScrollTopRef.current = scroller.scrollTop;\n\t}, [messages, isLoadingHistory]);\n\n\tconst handleScroll = () => {\n\t\tconst scroller = scrollerRef.current;\n\t\tif (!scroller) {\n\t\t\treturn;\n\t\t}\n\t\tconst distanceFromBottom =\n\t\t\tscroller.scrollHeight - scroller.scrollTop - scroller.clientHeight;\n\n\t\t// Reaching the top asks for the page before this one. The threshold gives\n\t\t// the request a head start, so the page is usually there by the time the\n\t\t// reader arrives.\n\t\tif (\n\t\t\tscroller.scrollTop <= NEAR_TOP_TOLERANCE_PX &&\n\t\t\thasMoreHistory &&\n\t\t\t!isLoadingHistory &&\n\t\t\tonLoadOlderMessages\n\t\t) {\n\t\t\tanchorFromBottomRef.current = scroller.scrollHeight - scroller.scrollTop;\n\t\t\tonLoadOlderMessages();\n\t\t}\n\t\t// Scrolling *up* is the only thing that detaches from the latest message:\n\t\t// growing content also pushes the bottom away, and auto-scroll itself only\n\t\t// ever moves down, so distance alone would flap mid-stream.\n\t\tconst scrolledUp = scroller.scrollTop < lastScrollTopRef.current - 1;\n\t\tlastScrollTopRef.current = scroller.scrollTop;\n\n\t\tif (distanceFromBottom <= AT_BOTTOM_TOLERANCE_PX) {\n\t\t\tsetIsAtLatest(true);\n\t\t\treturn;\n\t\t}\n\t\tif (scrolledUp) {\n\t\t\tsetIsAtLatest(false);\n\t\t}\n\t};\n\n\tconst handleJumpToLatest = () => {\n\t\tsetIsAtLatest(true);\n\t\tbottomRef.current?.scrollIntoView({ behavior: 'smooth' });\n\t};\n\n\tuseEffect(\n\t\t() => () => {\n\t\t\tif (copiedTimeoutRef.current) {\n\t\t\t\twindow.clearTimeout(copiedTimeoutRef.current);\n\t\t\t}\n\t\t},\n\t\t[],\n\t);\n\n\tconst handleCopyMessage = async (\n\t\tmessage: CopilotPanelProps['messages'][number],\n\t) => {\n\t\t// The raw markdown, not the rendered HTML — that is what a reader wants to\n\t\t// paste into a doc or a ticket.\n\t\tconst copied = await writeToClipboard(message.content);\n\t\tif (!copied) {\n\t\t\treturn;\n\t\t}\n\t\tif (copiedTimeoutRef.current) {\n\t\t\twindow.clearTimeout(copiedTimeoutRef.current);\n\t\t}\n\t\tsetCopiedMessageId(message.id);\n\t\tcopiedTimeoutRef.current = window.setTimeout(\n\t\t\t() => setCopiedMessageId(null),\n\t\t\tCOPIED_FEEDBACK_MS,\n\t\t);\n\t};\n\n\tconst handleFeedback = (messageId: string, vote: 'like' | 'dislike') => {\n\t\t// Pressing the same thumb again clears the vote. Nothing is reported for\n\t\t// that: the backend records ratings, not their absence.\n\t\tconst cleared = feedback[messageId] === vote;\n\t\tsetFeedback((previous) => ({\n\t\t\t...previous,\n\t\t\t[messageId]: previous[messageId] === vote ? undefined : vote,\n\t\t}));\n\t\t// Everything below sits outside the updater on purpose: React may run an\n\t\t// updater twice, which would report the same vote twice.\n\t\tif (cleared) {\n\t\t\tif (vote === 'dislike') {\n\t\t\t\tsetDislikePromptId((current) =>\n\t\t\t\t\tcurrent === messageId ? null : current,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (vote === 'like') {\n\t\t\tonFeedback?.(messageId, 'like');\n\t\t\treturn;\n\t\t}\n\t\t// A thumbs-down asks what went wrong first. Moving the prompt to another\n\t\t// reply still records the one it is leaving.\n\t\tif (dislikePromptId && dislikePromptId !== messageId) {\n\t\t\tonFeedback?.(dislikePromptId, 'dislike');\n\t\t}\n\t\tsetDislikePromptId(messageId);\n\t};\n\n\tconst reportDislike = (messageId: string, detail?: CopilotFeedbackDetail) => {\n\t\tsetDislikePromptId((current) => (current === messageId ? null : current));\n\t\tonFeedback?.(messageId, 'dislike', detail);\n\t};\n\n\tconst handleRegenerate = () => {\n\t\tif (inputDisabled || isTurnInFlight) {\n\t\t\treturn;\n\t\t}\n\t\tonRegenerate?.();\n\t};\n\n\tconst handleSend = () => {\n\t\tconst trimmed = input.trim();\n\t\tif ((!trimmed && !attachment) || inputDisabled || isTurnInFlight) {\n\t\t\treturn;\n\t\t}\n\t\t// The endpoint has no context field, so a quoted selection has to ride\n\t\t// inside the prompt itself.\n\t\tonSend(\n\t\t\tattachment ? buildCopilotSelectionPrompt(attachment, trimmed) : trimmed,\n\t\t);\n\t\tsetInput('');\n\t\t// The quote belongs to the turn that used it; the next question starts\n\t\t// clean unless the merchant selects something again.\n\t\tonAttachmentRemove?.();\n\t\tconst textarea = inputRef.current;\n\t\tif (textarea) {\n\t\t\ttextarea.style.height = 'auto';\n\t\t}\n\t};\n\n\t// A selection is attached so the merchant can ask about it, so the caret\n\t// should already be waiting for the question.\n\tuseEffect(() => {\n\t\tif (attachment) {\n\t\t\tinputRef.current?.focus();\n\t\t}\n\t}, [attachment]);\n\n\tconst handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n\t\tsetInput(event.target.value);\n\t\tconst textarea = event.target;\n\t\ttextarea.style.height = 'auto';\n\t\ttextarea.style.height = `${Math.min(\n\t\t\ttextarea.scrollHeight,\n\t\t\tMAX_INPUT_HEIGHT_PX,\n\t\t)}px`;\n\t};\n\n\tconst handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n\t\tif (event.key === 'Enter' && !event.shiftKey) {\n\t\t\tevent.preventDefault();\n\t\t\thandleSend();\n\t\t}\n\t};\n\n\tconst handleSuggestionClick = (\n\t\tsuggestion: NonNullable<CopilotPanelProps['suggestions']>[number],\n\t) => {\n\t\tif (inputDisabled || isTurnInFlight) {\n\t\t\treturn;\n\t\t}\n\t\tonSuggestionClick?.(suggestion);\n\t\tonSend(suggestion.text);\n\t};\n\n\tconst handleFollowUpClick = (followUp: string) => {\n\t\tif (inputDisabled || isTurnInFlight) {\n\t\t\treturn;\n\t\t}\n\t\tonSend(followUp);\n\t};\n\n\treturn (\n\t\t<PanelContainer\n\t\t\tclassName={className}\n\t\t\tstyle={{ width, height, ...style }}\n\t\t\trole=\"dialog\"\n\t\t\taria-label={title}\n\t\t>\n\t\t\t<PanelHeader>\n\t\t\t\t<HeaderAvatar>\n\t\t\t\t\t{avatar ?? <AiStarsIcon width={18} height={18} />}\n\t\t\t\t</HeaderAvatar>\n\t\t\t\t<HeaderText>\n\t\t\t\t\t<HeaderTitleRow>\n\t\t\t\t\t\t<TitleSmall>{title}</TitleSmall>\n\t\t\t\t\t\t{badge && <HeaderBadge>{badge}</HeaderBadge>}\n\t\t\t\t\t</HeaderTitleRow>\n\t\t\t\t\t{subtitle && (\n\t\t\t\t\t\t<BodyCaption color={COLORS.content.secondary}>\n\t\t\t\t\t\t\t{subtitle}\n\t\t\t\t\t\t</BodyCaption>\n\t\t\t\t\t)}\n\t\t\t\t</HeaderText>\n\t\t\t\t<HeaderActions>\n\t\t\t\t\t{showRefresh && onRefresh && (\n\t\t\t\t\t\t<IconAction\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={onRefresh}\n\t\t\t\t\t\t\taria-label=\"Start a new conversation\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<RefreshIcon width={16} height={16} />\n\t\t\t\t\t\t</IconAction>\n\t\t\t\t\t)}\n\t\t\t\t\t<IconAction\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={onClose}\n\t\t\t\t\t\taria-label=\"Close copilot\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<CrossIcon width={18} height={18} />\n\t\t\t\t\t</IconAction>\n\t\t\t\t</HeaderActions>\n\t\t\t</PanelHeader>\n\n\t\t\t<MessagesViewport>\n\t\t\t\t<MessagesArea\n\t\t\t\t\tref={scrollerRef}\n\t\t\t\t\tonScroll={handleScroll}\n\t\t\t\t\trole=\"region\"\n\t\t\t\t\taria-label=\"Conversation\"\n\t\t\t\t>\n\t\t\t\t\t{isEmpty ? (\n\t\t\t\t\t\t<EmptyState>\n\t\t\t\t\t\t\t<EmptyStateIcon>\n\t\t\t\t\t\t\t\t<AiStarsIcon width={28} height={28} />\n\t\t\t\t\t\t\t</EmptyStateIcon>\n\t\t\t\t\t\t\t<BodySecondary style={{ fontWeight: 600 }}>\n\t\t\t\t\t\t\t\t{emptyStateTitle || title}\n\t\t\t\t\t\t\t</BodySecondary>\n\t\t\t\t\t\t\t{emptyStateSubtitle && (\n\t\t\t\t\t\t\t\t<BodyCaption color={COLORS.content.secondary}>\n\t\t\t\t\t\t\t\t\t{emptyStateSubtitle}\n\t\t\t\t\t\t\t\t</BodyCaption>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{suggestions && suggestions.length > 0 && (\n\t\t\t\t\t\t\t\t<SuggestionList>\n\t\t\t\t\t\t\t\t\t{suggestions.map((suggestion) => (\n\t\t\t\t\t\t\t\t\t\t<SuggestionChip\n\t\t\t\t\t\t\t\t\t\t\tkey={suggestion.id}\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={inputDisabled || isTurnInFlight}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => handleSuggestionClick(suggestion)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<AiStarsIcon width={14} height={14} />\n\t\t\t\t\t\t\t\t\t\t\t{suggestion.text}\n\t\t\t\t\t\t\t\t\t\t</SuggestionChip>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</SuggestionList>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</EmptyState>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t{isLoadingHistory && (\n\t\t\t\t\t\t\t\t<HistoryLoader role=\"status\" aria-live=\"polite\">\n\t\t\t\t\t\t\t\t\t<DotPulse color={COLORS.content.placeholder} size={4} />\n\t\t\t\t\t\t\t\t\t{historyLoadingLabel}\n\t\t\t\t\t\t\t\t</HistoryLoader>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{visibleMessages.map((message) => {\n\t\t\t\t\t\t\t\tconst sentAt =\n\t\t\t\t\t\t\t\t\tmessage.role === 'user'\n\t\t\t\t\t\t\t\t\t\t? formatMessageTime(message.timestamp)\n\t\t\t\t\t\t\t\t\t\t: '';\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<React.Fragment key={message.id}>\n\t\t\t\t\t\t\t\t\t\t{message.content.length > 0 && (\n\t\t\t\t\t\t\t\t\t\t\t<MessageBubble $role={message.role}>\n\t\t\t\t\t\t\t\t\t\t\t\t{message.role === 'assistant' ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdangerouslySetInnerHTML={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t__html: renderCopilotMarkdown(message.content),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div style={{ whiteSpace: 'pre-wrap' }}>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{message.content}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</MessageBubble>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t{/* Tool CTAs (e.g. the support-portal link) — only once the\n\t\t\t\t\t\t\t\t\t\t reply has finished, so a button cannot flash mid-stream. */}\n\t\t\t\t\t\t\t\t\t\t{message.role === 'assistant' &&\n\t\t\t\t\t\t\t\t\t\t\t!message.isStreaming &&\n\t\t\t\t\t\t\t\t\t\t\t(() => {\n\t\t\t\t\t\t\t\t\t\t\t\tconst ctas = toolResultCtas(message.toolResults);\n\t\t\t\t\t\t\t\t\t\t\t\tif (ctas.length === 0) {\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<ToolCtaList>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{ctas.map((cta) => (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ToolCtaLink\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tkey={`${cta.tool}-${cta.url}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\thref={cta.url}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonToolCtaClick?.({\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttool: cta.tool,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\turl: cta.url,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{cta.label}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</ToolCtaLink>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</ToolCtaList>\n\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t})()}\n\t\t\t\t\t\t\t\t\t\t{sentAt && <MessageTimestamp>{sentAt}</MessageTimestamp>}\n\t\t\t\t\t\t\t\t\t\t{message.wasInterrupted && interruptedLabel && (\n\t\t\t\t\t\t\t\t\t\t\t<InterruptedNote>\n\t\t\t\t\t\t\t\t\t\t\t\t<StopIcon size={12} />\n\t\t\t\t\t\t\t\t\t\t\t\t{interruptedLabel}\n\t\t\t\t\t\t\t\t\t\t\t</InterruptedNote>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t{/* Only under a finished reply: mid-stream the text is still\n\t\t\t\t\t\t\t\t\t\t changing, so copying it would capture a partial answer. */}\n\t\t\t\t\t\t\t\t\t\t{message.role === 'assistant' &&\n\t\t\t\t\t\t\t\t\t\t\tmessage.content.length > 0 &&\n\t\t\t\t\t\t\t\t\t\t\t!message.isStreaming && (\n\t\t\t\t\t\t\t\t\t\t\t\t<MessageActions>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<MessageAction\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => void handleCopyMessage(message)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata-copied={copiedMessageId === message.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle={\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcopiedMessageId === message.id ? 'Copied' : 'Copy'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label={\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcopiedMessageId === message.id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Response copied to clipboard'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Copy response'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{copiedMessageId === message.id ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<CheckIcon />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<CopyIcon />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</MessageAction>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/* Only under the newest reply: regenerating appends a\n\t\t\t\t\t\t\t\t\t\t\t\t\t fresh turn at the end, so offering it against an\n\t\t\t\t\t\t\t\t\t\t\t\t\t older answer would not replace that answer. */}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{onRegenerate && message.id === newestMessage?.id && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<MessageAction\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={handleRegenerate}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={inputDisabled || isTurnInFlight}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Regenerate\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Regenerate response\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<RegenerateIcon />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</MessageAction>\n\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<MessageAction\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => handleFeedback(message.id, 'like')}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata-selected={feedback[message.id] === 'like'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={feedback[message.id] === 'like'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Good response\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Good response\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ThumbUpIcon\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfilled={feedback[message.id] === 'like'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</MessageAction>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<MessageAction\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\thandleFeedback(message.id, 'dislike')\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata-selected={feedback[message.id] === 'dislike'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-pressed={feedback[message.id] === 'dislike'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Bad response\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Bad response\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ThumbDownIcon\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfilled={feedback[message.id] === 'dislike'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</MessageAction>\n\t\t\t\t\t\t\t\t\t\t\t\t</MessageActions>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t{dislikePromptId === message.id && (\n\t\t\t\t\t\t\t\t\t\t\t<CopilotFeedbackModal\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={dislikeTitle}\n\t\t\t\t\t\t\t\t\t\t\t\tsubtitle={dislikeSubtitle ?? `Help us improve ${title}`}\n\t\t\t\t\t\t\t\t\t\t\t\treasons={dislikeReasons}\n\t\t\t\t\t\t\t\t\t\t\t\tnotePlaceholder={dislikeNotePlaceholder}\n\t\t\t\t\t\t\t\t\t\t\t\tsubmitLabel={dislikeSubmitLabel}\n\t\t\t\t\t\t\t\t\t\t\t\tonSubmit={(detail) => reportDislike(message.id, detail)}\n\t\t\t\t\t\t\t\t\t\t\t\tonDismiss={() => reportDislike(message.id)}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</React.Fragment>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t{followUps && followUps.length > 0 && (\n\t\t\t\t\t\t\t\t<FollowUpList>\n\t\t\t\t\t\t\t\t\t{followUpsLabel && (\n\t\t\t\t\t\t\t\t\t\t<FollowUpsLabel>{followUpsLabel}</FollowUpsLabel>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t{followUps.map((followUp) => (\n\t\t\t\t\t\t\t\t\t\t<FollowUpChip\n\t\t\t\t\t\t\t\t\t\t\tkey={followUp}\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={inputDisabled}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => handleFollowUpClick(followUp)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<ArrowRightIcon />\n\t\t\t\t\t\t\t\t\t\t\t{followUp}\n\t\t\t\t\t\t\t\t\t\t</FollowUpChip>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</FollowUpList>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{isThinking && (\n\t\t\t\t\t\t\t\t<ThinkingBubble role=\"status\" aria-live=\"polite\">\n\t\t\t\t\t\t\t\t\t<AiStarsIcon\n\t\t\t\t\t\t\t\t\t\twidth={14}\n\t\t\t\t\t\t\t\t\t\theight={14}\n\t\t\t\t\t\t\t\t\t\tcolor={COLORS.content.brand}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t{thinkingLabel && (\n\t\t\t\t\t\t\t\t\t\t<ThinkingLabel>{thinkingLabel}</ThinkingLabel>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t<DotPulse color={COLORS.background.brand} size={5} />\n\t\t\t\t\t\t\t\t</ThinkingBubble>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<div ref={bottomRef} />\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</MessagesArea>\n\n\t\t\t\t<ScrollFade $visible={showScrollAffordance} aria-hidden=\"true\" />\n\t\t\t\t<JumpToLatestButton\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t$visible={showScrollAffordance}\n\t\t\t\t\tonClick={handleJumpToLatest}\n\t\t\t\t\ttabIndex={showScrollAffordance ? 0 : -1}\n\t\t\t\t\taria-label=\"Scroll to the latest message\"\n\t\t\t\t>\n\t\t\t\t\t<ArrowDownIcon width={16} height={16} />\n\t\t\t\t</JumpToLatestButton>\n\t\t\t</MessagesViewport>\n\n\t\t\t{errorMessage && <ErrorBanner role=\"alert\">{errorMessage}</ErrorBanner>}\n\n\t\t\t<InputArea>\n\t\t\t\t{attachment && (\n\t\t\t\t\t<AttachmentChip>\n\t\t\t\t\t\t<AiStarsIcon width={14} height={14} color={COLORS.content.brand} />\n\t\t\t\t\t\t<AttachmentText title={attachment}>{attachment}</AttachmentText>\n\t\t\t\t\t\t<AttachmentRemove\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={onAttachmentRemove}\n\t\t\t\t\t\t\taria-label=\"Remove the selected text\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<CrossIcon width={12} height={12} />\n\t\t\t\t\t\t</AttachmentRemove>\n\t\t\t\t\t</AttachmentChip>\n\t\t\t\t)}\n\t\t\t\t<InputWrapper $focused={isInputFocused}>\n\t\t\t\t\t<MessageInput\n\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\trows={1}\n\t\t\t\t\t\tvalue={input}\n\t\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\t\tdisabled={inputDisabled}\n\t\t\t\t\t\tonChange={handleInputChange}\n\t\t\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\t\t\tonFocus={() => setIsInputFocused(true)}\n\t\t\t\t\t\tonBlur={() => setIsInputFocused(false)}\n\t\t\t\t\t/>\n\t\t\t\t\t<SendButton\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t$active={canStop || canSend}\n\t\t\t\t\t\tdisabled={!canStop && !canSend}\n\t\t\t\t\t\tonClick={canStop ? onStop : handleSend}\n\t\t\t\t\t\taria-label={canStop ? 'Stop generating' : 'Send message'}\n\t\t\t\t\t>\n\t\t\t\t\t\t{canStop ? <StopIcon /> : <SendArrowIcon />}\n\t\t\t\t\t</SendButton>\n\t\t\t\t</InputWrapper>\n\t\t\t</InputArea>\n\n\t\t\t{disclaimer && <Disclaimer>{disclaimer}</Disclaimer>}\n\t\t</PanelContainer>\n\t);\n};\n\nexport default CopilotPanel;\n"],"names":["MAX_INPUT_HEIGHT_PX","AT_BOTTOM_TOLERANCE_PX","COPIED_FEEDBACK_MS","writeToClipboard","text","textarea","copied","NEAR_TOP_TOLERANCE_PX","formatMessageTime","timestamp","date","SendArrowIcon","jsxs","jsx","ArrowRightIcon","StopIcon","size","CopyIcon","CheckIcon","RegenerateIcon","ThumbUpIcon","filled","ThumbDownIcon","CopilotPanel","messages","onSend","onClose","title","badge","subtitle","avatar","placeholder","disclaimer","emptyStateTitle","emptyStateSubtitle","suggestions","onSuggestionClick","showRefresh","onRefresh","onRegenerate","onFeedback","dislikeTitle","dislikeSubtitle","dislikeReasons","dislikeNotePlaceholder","dislikeSubmitLabel","isThinking","thinkingLabel","isStreaming","onStop","interruptedLabel","followUpsLabel","onToolCtaClick","hasMoreHistory","isLoadingHistory","onLoadOlderMessages","historyLoadingLabel","inputDisabled","attachment","onAttachmentRemove","errorMessage","width","height","className","style","input","setInput","useState","isInputFocused","setIsInputFocused","isAtLatest","setIsAtLatest","copiedMessageId","setCopiedMessageId","feedback","setFeedback","dislikePromptId","setDislikePromptId","inputRef","useRef","copiedTimeoutRef","bottomRef","scrollerRef","lastScrollTopRef","anchorFromBottomRef","visibleMessages","message","isCopilotRegeneratePrompt","isEmpty","isTurnInFlight","canSend","canStop","newestMessage","followUps","showScrollAffordance","useEffect","scroller","anchor","handleScroll","distanceFromBottom","scrolledUp","handleJumpToLatest","handleCopyMessage","handleFeedback","messageId","vote","cleared","previous","current","reportDislike","detail","handleRegenerate","handleSend","trimmed","buildCopilotSelectionPrompt","handleInputChange","event","handleKeyDown","handleSuggestionClick","suggestion","handleFollowUpClick","followUp","PanelContainer","PanelHeader","HeaderAvatar","AiStarsIcon","HeaderText","HeaderTitleRow","TitleSmall","HeaderBadge","BodyCaption","COLORS","HeaderActions","IconAction","RefreshIcon","CrossIcon","MessagesViewport","MessagesArea","EmptyState","EmptyStateIcon","BodySecondary","SuggestionList","SuggestionChip","Fragment","HistoryLoader","DotPulse","sentAt","React","MessageBubble","renderCopilotMarkdown","ctas","toolResultCtas","ToolCtaList","cta","ToolCtaLink","MessageTimestamp","InterruptedNote","MessageActions","MessageAction","CopilotFeedbackModal","FollowUpList","FollowUpsLabel","FollowUpChip","ThinkingBubble","ThinkingLabel","ScrollFade","JumpToLatestButton","ArrowDownIcon","ErrorBanner","InputArea","AttachmentChip","AttachmentText","AttachmentRemove","InputWrapper","MessageInput","SendButton","Disclaimer"],"mappings":"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"}
1
+ {"version":3,"file":"CopilotPanel.js","sources":["../../../../src/components/bik-copilot/CopilotPanel.tsx"],"sourcesContent":["import ArrowDownIcon from '@src/assets/icons/arrow_down.svg';\nimport AiStarsIcon from '@src/assets/icons/bik_ai_stars_icon.svg';\nimport CrossIcon from '@src/assets/icons/cross.svg';\nimport RefreshIcon from '@src/assets/icons/refresh.svg';\nimport React, { useEffect, useRef, useState } from 'react';\nimport { COLORS } from '@src/constants/Theme';\nimport { DotPulse } from '../dot-pulse';\nimport { BodyCaption, BodySecondary, TitleSmall } from '../TypographyStyle';\nimport { CopilotFeedbackModal } from './CopilotFeedbackModal';\nimport {\n\tAttachmentChip,\n\tAttachmentRemove,\n\tAttachmentText,\n\tDisclaimer,\n\tEmptyState,\n\tEmptyStateIcon,\n\tErrorBanner,\n\tFollowUpChip,\n\tFollowUpList,\n\tFollowUpsLabel,\n\tHeaderActions,\n\tHeaderAvatar,\n\tHeaderBadge,\n\tHeaderText,\n\tHeaderTitleRow,\n\tHistoryLoader,\n\tIconAction,\n\tInputArea,\n\tInputWrapper,\n\tInterruptedNote,\n\tJumpToLatestButton,\n\tMessageAction,\n\tMessageActions,\n\tMessageBubble,\n\tMessageInput,\n\tMessagesArea,\n\tMessagesViewport,\n\tMessageTimestamp,\n\tPanelContainer,\n\tPanelHeader,\n\tScrollFade,\n\tSendButton,\n\tSuggestionChip,\n\tSuggestionList,\n\tThinkingBubble,\n\tThinkingLabel,\n\tToolCtaLink,\n\tToolCtaList,\n} from './CopilotPanel.styled';\nimport { isCopilotRegeneratePrompt } from './copilotRegenerate';\nimport { buildCopilotSelectionPrompt } from './copilotSelectionPrompt';\nimport { CopilotFeedbackDetail, CopilotPanelProps } from './model';\nimport { renderCopilotMarkdown } from './utils/markdown';\nimport { toolResultCtas } from './utils/toolCtas';\n\nconst MAX_INPUT_HEIGHT_PX = 96;\n\n/** Slack at the bottom of the scroller that still counts as \"at the latest\". */\nconst AT_BOTTOM_TOLERANCE_PX = 24;\n\n/** How long the copy button shows its confirmation before reverting. */\nconst COPIED_FEEDBACK_MS = 1800;\n\n/**\n * Writes text to the clipboard.\n *\n * `navigator.clipboard` is missing outside a secure context and can reject when\n * the document is not focused, so a hidden-textarea `execCommand` fallback keeps\n * the button working there rather than failing silently.\n */\nconst writeToClipboard = async (text: string): Promise<boolean> => {\n\ttry {\n\t\tif (navigator.clipboard?.writeText) {\n\t\t\tawait navigator.clipboard.writeText(text);\n\t\t\treturn true;\n\t\t}\n\t} catch {\n\t\t// Fall through to the legacy path.\n\t}\n\n\ttry {\n\t\tconst textarea = document.createElement('textarea');\n\t\ttextarea.value = text;\n\t\t// Off-screen rather than hidden: `display: none` cannot be selected.\n\t\ttextarea.setAttribute('readonly', '');\n\t\ttextarea.style.position = 'fixed';\n\t\ttextarea.style.top = '-1000px';\n\t\ttextarea.style.opacity = '0';\n\t\tdocument.body.appendChild(textarea);\n\t\ttextarea.select();\n\t\tconst copied = document.execCommand('copy');\n\t\tdocument.body.removeChild(textarea);\n\t\treturn copied;\n\t} catch {\n\t\treturn false;\n\t}\n};\n\n/** How close to the top counts as asking for the previous page. */\nconst NEAR_TOP_TOLERANCE_PX = 48;\n\nconst formatMessageTime = (timestamp: number): string => {\n\tif (!timestamp || !Number.isFinite(timestamp)) {\n\t\treturn '';\n\t}\n\tconst date = new Date(timestamp);\n\tif (Number.isNaN(date.getTime())) {\n\t\treturn '';\n\t}\n\treturn date.toLocaleTimeString('en-US', {\n\t\thour: 'numeric',\n\t\tminute: '2-digit',\n\t});\n};\n\nconst SendArrowIcon = () => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"16\"\n\t\theight=\"16\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"2\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M12 19V5\" />\n\t\t<path d=\"M6 11l6-6 6 6\" />\n\t</svg>\n);\n\nconst ArrowRightIcon = () => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"2\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M5 12h14\" />\n\t\t<path d=\"M13 6l6 6-6 6\" />\n\t</svg>\n);\n\nconst StopIcon = ({ size = 16 }: { size?: number }) => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth={size}\n\t\theight={size}\n\t\tfill=\"currentColor\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<rect x=\"7\" y=\"7\" width=\"10\" height=\"10\" rx=\"2\" />\n\t</svg>\n);\n\nconst CopyIcon = () => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"1.8\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\" />\n\t\t<path d=\"M15 5.5A1.5 1.5 0 0 0 13.5 4H5.5A1.5 1.5 0 0 0 4 5.5v8A1.5 1.5 0 0 0 5.5 15\" />\n\t</svg>\n);\n\nconst CheckIcon = () => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"2.2\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M5 13l4 4L19 7\" />\n\t</svg>\n);\n\nconst RegenerateIcon = () => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"1.8\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M20 12a8 8 0 1 1-2.34-5.66\" />\n\t\t<path d=\"M20 4v4h-4\" />\n\t</svg>\n);\n\nconst ThumbUpIcon = ({ filled = false }: { filled?: boolean }) => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill={filled ? 'currentColor' : 'none'}\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"1.8\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M7 10v11H4a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h3Z\" />\n\t\t<path d=\"M7 10l4.5-7a2 2 0 0 1 3.7 1.3L14.5 9H19a2 2 0 0 1 2 2.3l-1.1 7A2 2 0 0 1 18 20H7\" />\n\t</svg>\n);\n\nconst ThumbDownIcon = ({ filled = false }: { filled?: boolean }) => (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"14\"\n\t\theight=\"14\"\n\t\tfill={filled ? 'currentColor' : 'none'}\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"1.8\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M7 14V3H4a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h3Z\" />\n\t\t<path d=\"M7 14l4.5 7a2 2 0 0 0 3.7-1.3L14.5 15H19a2 2 0 0 0 2-2.3l-1.1-7A2 2 0 0 0 18 4H7\" />\n\t</svg>\n);\n\n/**\n * Chat panel used by the copilot. Fully presentational: it renders the\n * transcript it is given and reports user intent through callbacks, so it works\n * against any backend.\n */\nexport const CopilotPanel: React.FC<CopilotPanelProps> = ({\n\tmessages,\n\tonSend,\n\tonClose,\n\ttitle = 'Copilot',\n\tbadge,\n\tsubtitle,\n\tavatar,\n\tplaceholder = 'Write your message...',\n\tdisclaimer = 'AI can make mistakes, Verify the responses.',\n\temptyStateTitle,\n\temptyStateSubtitle = 'Here are some things you can try',\n\tsuggestions,\n\tonSuggestionClick,\n\tshowRefresh = true,\n\tonRefresh,\n\tonRegenerate,\n\tonFeedback,\n\tdislikeTitle,\n\tdislikeSubtitle,\n\tdislikeReasons,\n\tdislikeNotePlaceholder,\n\tdislikeSubmitLabel,\n\tisThinking = false,\n\tthinkingLabel = 'Thinking',\n\tisStreaming = false,\n\tonStop,\n\tinterruptedLabel = 'This message was interrupted',\n\tfollowUpsLabel = 'Follow ups',\n\tonToolCtaClick,\n\thasMoreHistory = false,\n\tisLoadingHistory = false,\n\tonLoadOlderMessages,\n\thistoryLoadingLabel = 'Loading earlier messages',\n\tinputDisabled = false,\n\tattachment,\n\tonAttachmentRemove,\n\terrorMessage,\n\twidth,\n\theight,\n\tclassName,\n\tstyle,\n}) => {\n\tconst [input, setInput] = useState('');\n\tconst [isInputFocused, setIsInputFocused] = useState(false);\n\tconst [isAtLatest, setIsAtLatest] = useState(true);\n\t// Which reply is showing \"Copied\" — per message, so copying one does not tick\n\t// the button under another.\n\tconst [copiedMessageId, setCopiedMessageId] = useState<string | null>(null);\n\n\tconst [feedback, setFeedback] = useState<\n\t\tRecord<string, 'like' | 'dislike' | undefined>\n\t>({});\n\t// Which reply is being asked about. A thumbs-down opens the prompt and the\n\t// rating is reported once it is answered or dismissed -- so pressing the\n\t// thumb is never lost, and one thumb never becomes two ratings.\n\tconst [dislikePromptId, setDislikePromptId] = useState<string | null>(null);\n\tconst inputRef = useRef<HTMLTextAreaElement>(null);\n\tconst wasTurnInFlightRef = useRef(false);\n\tconst copiedTimeoutRef = useRef<number | null>(null);\n\tconst bottomRef = useRef<HTMLDivElement>(null);\n\tconst scrollerRef = useRef<HTMLDivElement>(null);\n\tconst lastScrollTopRef = useRef(0);\n\t// Distance from the bottom at the moment an older page was asked for. Older\n\t// messages land *above* the viewport, so without restoring against this the\n\t// content under the reader jumps down by the height of what arrived.\n\tconst anchorFromBottomRef = useRef<number | null>(null);\n\n\t// An assistant bubble is pushed the moment a turn starts, before any text has\n\t// arrived; the thinking indicator stands in for it until then, so rendering it\n\t// too would show an empty balloon next to the dots. A turn stopped before its\n\t// first token keeps its place, carrying only the interrupted note.\n\tconst visibleMessages = messages.filter(\n\t\t(message) =>\n\t\t\t(message.content.length > 0 || message.wasInterrupted) &&\n\t\t\t!isCopilotRegeneratePrompt(message.content),\n\t);\n\tconst isEmpty = visibleMessages.length === 0 && !isThinking;\n\tconst isTurnInFlight = isThinking || isStreaming;\n\tconst canSend =\n\t\t!inputDisabled &&\n\t\t!isTurnInFlight &&\n\t\t(input.trim().length > 0 || Boolean(attachment));\n\t// Stopping is the only thing the button can do mid-answer, so it becomes the\n\t// stop button rather than sitting there disabled.\n\tconst canStop = isTurnInFlight && Boolean(onStop);\n\t// Only the newest reply's suggestions are on offer — older ones answer a\n\t// question the conversation has already moved past.\n\tconst newestMessage = visibleMessages[visibleMessages.length - 1];\n\tconst followUps =\n\t\t!isTurnInFlight && newestMessage?.role === 'assistant'\n\t\t\t? newestMessage.followUps\n\t\t\t: undefined;\n\t// A long answer that the reader has scrolled up from: the tail is clipped, so\n\t// the fade and the arrow say there is more below.\n\tconst showScrollAffordance = !isEmpty && !isAtLatest;\n\n\t// Following the conversation is the default, but a reader who has scrolled up\n\t// mid-answer must not be yanked back down by the next chunk.\n\tuseEffect(() => {\n\t\tif (!isAtLatest) {\n\t\t\treturn;\n\t\t}\n\t\tbottomRef.current?.scrollIntoView({ behavior: 'smooth' });\n\t}, [messages, isThinking, isAtLatest]);\n\n\t// Older messages have landed: put the reader back on the message they were\n\t// looking at, which is now that much further down.\n\tuseEffect(() => {\n\t\tconst scroller = scrollerRef.current;\n\t\tconst anchor = anchorFromBottomRef.current;\n\t\tif (!scroller || anchor === null || isLoadingHistory) {\n\t\t\treturn;\n\t\t}\n\t\tanchorFromBottomRef.current = null;\n\t\tscroller.scrollTop = scroller.scrollHeight - anchor;\n\t\tlastScrollTopRef.current = scroller.scrollTop;\n\t}, [messages, isLoadingHistory]);\n\n\tconst handleScroll = () => {\n\t\tconst scroller = scrollerRef.current;\n\t\tif (!scroller) {\n\t\t\treturn;\n\t\t}\n\t\tconst distanceFromBottom =\n\t\t\tscroller.scrollHeight - scroller.scrollTop - scroller.clientHeight;\n\n\t\t// Reaching the top asks for the page before this one. The threshold gives\n\t\t// the request a head start, so the page is usually there by the time the\n\t\t// reader arrives.\n\t\tif (\n\t\t\tscroller.scrollTop <= NEAR_TOP_TOLERANCE_PX &&\n\t\t\thasMoreHistory &&\n\t\t\t!isLoadingHistory &&\n\t\t\tonLoadOlderMessages\n\t\t) {\n\t\t\tanchorFromBottomRef.current = scroller.scrollHeight - scroller.scrollTop;\n\t\t\tonLoadOlderMessages();\n\t\t}\n\t\t// Scrolling *up* is the only thing that detaches from the latest message:\n\t\t// growing content also pushes the bottom away, and auto-scroll itself only\n\t\t// ever moves down, so distance alone would flap mid-stream.\n\t\tconst scrolledUp = scroller.scrollTop < lastScrollTopRef.current - 1;\n\t\tlastScrollTopRef.current = scroller.scrollTop;\n\n\t\tif (distanceFromBottom <= AT_BOTTOM_TOLERANCE_PX) {\n\t\t\tsetIsAtLatest(true);\n\t\t\treturn;\n\t\t}\n\t\tif (scrolledUp) {\n\t\t\tsetIsAtLatest(false);\n\t\t}\n\t};\n\n\tconst handleJumpToLatest = () => {\n\t\tsetIsAtLatest(true);\n\t\tbottomRef.current?.scrollIntoView({ behavior: 'smooth' });\n\t};\n\n\tuseEffect(\n\t\t() => () => {\n\t\t\tif (copiedTimeoutRef.current) {\n\t\t\t\twindow.clearTimeout(copiedTimeoutRef.current);\n\t\t\t}\n\t\t},\n\t\t[],\n\t);\n\n\tconst handleCopyMessage = async (\n\t\tmessage: CopilotPanelProps['messages'][number],\n\t) => {\n\t\t// The raw markdown, not the rendered HTML — that is what a reader wants to\n\t\t// paste into a doc or a ticket.\n\t\tconst copied = await writeToClipboard(message.content);\n\t\tif (!copied) {\n\t\t\treturn;\n\t\t}\n\t\tif (copiedTimeoutRef.current) {\n\t\t\twindow.clearTimeout(copiedTimeoutRef.current);\n\t\t}\n\t\tsetCopiedMessageId(message.id);\n\t\tcopiedTimeoutRef.current = window.setTimeout(\n\t\t\t() => setCopiedMessageId(null),\n\t\t\tCOPIED_FEEDBACK_MS,\n\t\t);\n\t};\n\n\tconst handleFeedback = (messageId: string, vote: 'like' | 'dislike') => {\n\t\t// Pressing the same thumb again clears the vote. Nothing is reported for\n\t\t// that: the backend records ratings, not their absence.\n\t\tconst cleared = feedback[messageId] === vote;\n\t\tsetFeedback((previous) => ({\n\t\t\t...previous,\n\t\t\t[messageId]: previous[messageId] === vote ? undefined : vote,\n\t\t}));\n\t\t// Everything below sits outside the updater on purpose: React may run an\n\t\t// updater twice, which would report the same vote twice.\n\t\tif (cleared) {\n\t\t\tif (vote === 'dislike') {\n\t\t\t\tsetDislikePromptId((current) =>\n\t\t\t\t\tcurrent === messageId ? null : current,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (vote === 'like') {\n\t\t\tonFeedback?.(messageId, 'like');\n\t\t\treturn;\n\t\t}\n\t\t// A thumbs-down asks what went wrong first. Moving the prompt to another\n\t\t// reply still records the one it is leaving.\n\t\tif (dislikePromptId && dislikePromptId !== messageId) {\n\t\t\tonFeedback?.(dislikePromptId, 'dislike');\n\t\t}\n\t\tsetDislikePromptId(messageId);\n\t};\n\n\tconst reportDislike = (messageId: string, detail?: CopilotFeedbackDetail) => {\n\t\tsetDislikePromptId((current) => (current === messageId ? null : current));\n\t\tonFeedback?.(messageId, 'dislike', detail);\n\t};\n\n\tconst handleRegenerate = () => {\n\t\tif (inputDisabled || isTurnInFlight) {\n\t\t\treturn;\n\t\t}\n\t\tonRegenerate?.();\n\t};\n\n\tconst handleSend = () => {\n\t\tconst trimmed = input.trim();\n\t\tif ((!trimmed && !attachment) || inputDisabled || isTurnInFlight) {\n\t\t\treturn;\n\t\t}\n\t\t// The endpoint has no context field, so a quoted selection has to ride\n\t\t// inside the prompt itself.\n\t\tonSend(\n\t\t\tattachment ? buildCopilotSelectionPrompt(attachment, trimmed) : trimmed,\n\t\t);\n\t\tsetInput('');\n\t\t// The quote belongs to the turn that used it; the next question starts\n\t\t// clean unless the merchant selects something again.\n\t\tonAttachmentRemove?.();\n\t\tconst textarea = inputRef.current;\n\t\tif (textarea) {\n\t\t\ttextarea.style.height = 'auto';\n\t\t}\n\t};\n\n\t// A selection is attached so the merchant can ask about it, so the caret\n\t// should already be waiting for the question.\n\tuseEffect(() => {\n\t\tif (attachment) {\n\t\t\tinputRef.current?.focus();\n\t\t}\n\t}, [attachment]);\n\n\tuseEffect(() => {\n\t\tconst turnJustEnded = wasTurnInFlightRef.current && !isTurnInFlight;\n\t\twasTurnInFlightRef.current = isTurnInFlight;\n\t\tif (turnJustEnded && !inputDisabled && !dislikePromptId) {\n\t\t\tinputRef.current?.focus();\n\t\t}\n\t}, [isTurnInFlight, inputDisabled, dislikePromptId]);\n\n\tconst handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n\t\tsetInput(event.target.value);\n\t\tconst textarea = event.target;\n\t\ttextarea.style.height = 'auto';\n\t\ttextarea.style.height = `${Math.min(\n\t\t\ttextarea.scrollHeight,\n\t\t\tMAX_INPUT_HEIGHT_PX,\n\t\t)}px`;\n\t};\n\n\tconst handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n\t\tif (event.key === 'Enter' && !event.shiftKey) {\n\t\t\tevent.preventDefault();\n\t\t\thandleSend();\n\t\t}\n\t};\n\n\tconst handleSuggestionClick = (\n\t\tsuggestion: NonNullable<CopilotPanelProps['suggestions']>[number],\n\t) => {\n\t\tif (inputDisabled || isTurnInFlight) {\n\t\t\treturn;\n\t\t}\n\t\tonSuggestionClick?.(suggestion);\n\t\tonSend(suggestion.text);\n\t};\n\n\tconst handleFollowUpClick = (followUp: string) => {\n\t\tif (inputDisabled || isTurnInFlight) {\n\t\t\treturn;\n\t\t}\n\t\tonSend(followUp);\n\t};\n\n\treturn (\n\t\t<PanelContainer\n\t\t\tclassName={className}\n\t\t\tstyle={{ width, height, ...style }}\n\t\t\trole=\"dialog\"\n\t\t\taria-label={title}\n\t\t>\n\t\t\t<PanelHeader>\n\t\t\t\t<HeaderAvatar>\n\t\t\t\t\t{avatar ?? <AiStarsIcon width={18} height={18} />}\n\t\t\t\t</HeaderAvatar>\n\t\t\t\t<HeaderText>\n\t\t\t\t\t<HeaderTitleRow>\n\t\t\t\t\t\t<TitleSmall>{title}</TitleSmall>\n\t\t\t\t\t\t{badge && <HeaderBadge>{badge}</HeaderBadge>}\n\t\t\t\t\t</HeaderTitleRow>\n\t\t\t\t\t{subtitle && (\n\t\t\t\t\t\t<BodyCaption color={COLORS.content.secondary}>\n\t\t\t\t\t\t\t{subtitle}\n\t\t\t\t\t\t</BodyCaption>\n\t\t\t\t\t)}\n\t\t\t\t</HeaderText>\n\t\t\t\t<HeaderActions>\n\t\t\t\t\t{showRefresh && onRefresh && (\n\t\t\t\t\t\t<IconAction\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={onRefresh}\n\t\t\t\t\t\t\taria-label=\"Start a new conversation\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<RefreshIcon width={16} height={16} />\n\t\t\t\t\t\t</IconAction>\n\t\t\t\t\t)}\n\t\t\t\t\t<IconAction\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={onClose}\n\t\t\t\t\t\taria-label=\"Close copilot\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<CrossIcon width={18} height={18} />\n\t\t\t\t\t</IconAction>\n\t\t\t\t</HeaderActions>\n\t\t\t</PanelHeader>\n\n\t\t\t<MessagesViewport>\n\t\t\t\t<MessagesArea\n\t\t\t\t\tref={scrollerRef}\n\t\t\t\t\tonScroll={handleScroll}\n\t\t\t\t\trole=\"region\"\n\t\t\t\t\taria-label=\"Conversation\"\n\t\t\t\t>\n\t\t\t\t\t{isEmpty ? (\n\t\t\t\t\t\t<EmptyState>\n\t\t\t\t\t\t\t<EmptyStateIcon>\n\t\t\t\t\t\t\t\t<AiStarsIcon width={28} height={28} />\n\t\t\t\t\t\t\t</EmptyStateIcon>\n\t\t\t\t\t\t\t<BodySecondary style={{ fontWeight: 600 }}>\n\t\t\t\t\t\t\t\t{emptyStateTitle || title}\n\t\t\t\t\t\t\t</BodySecondary>\n\t\t\t\t\t\t\t{emptyStateSubtitle && (\n\t\t\t\t\t\t\t\t<BodyCaption color={COLORS.content.secondary}>\n\t\t\t\t\t\t\t\t\t{emptyStateSubtitle}\n\t\t\t\t\t\t\t\t</BodyCaption>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{suggestions && suggestions.length > 0 && (\n\t\t\t\t\t\t\t\t<SuggestionList>\n\t\t\t\t\t\t\t\t\t{suggestions.map((suggestion) => (\n\t\t\t\t\t\t\t\t\t\t<SuggestionChip\n\t\t\t\t\t\t\t\t\t\t\tkey={suggestion.id}\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={inputDisabled || isTurnInFlight}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => handleSuggestionClick(suggestion)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<AiStarsIcon width={14} height={14} />\n\t\t\t\t\t\t\t\t\t\t\t{suggestion.text}\n\t\t\t\t\t\t\t\t\t\t</SuggestionChip>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</SuggestionList>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</EmptyState>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t{isLoadingHistory && (\n\t\t\t\t\t\t\t\t<HistoryLoader role=\"status\" aria-live=\"polite\">\n\t\t\t\t\t\t\t\t\t<DotPulse color={COLORS.content.placeholder} size={4} />\n\t\t\t\t\t\t\t\t\t{historyLoadingLabel}\n\t\t\t\t\t\t\t\t</HistoryLoader>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{visibleMessages.map((message) => {\n\t\t\t\t\t\t\t\tconst sentAt =\n\t\t\t\t\t\t\t\t\tmessage.role === 'user'\n\t\t\t\t\t\t\t\t\t\t? formatMessageTime(message.timestamp)\n\t\t\t\t\t\t\t\t\t\t: '';\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<React.Fragment key={message.id}>\n\t\t\t\t\t\t\t\t\t\t{message.content.length > 0 && (\n\t\t\t\t\t\t\t\t\t\t\t<MessageBubble $role={message.role}>\n\t\t\t\t\t\t\t\t\t\t\t\t{message.role === 'assistant' ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdangerouslySetInnerHTML={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t__html: renderCopilotMarkdown(message.content),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div style={{ whiteSpace: 'pre-wrap' }}>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{message.content}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</MessageBubble>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t{/* Tool CTAs (e.g. the support-portal link) — only once the\n\t\t\t\t\t\t\t\t\t\t reply has finished, so a button cannot flash mid-stream. */}\n\t\t\t\t\t\t\t\t\t\t{message.role === 'assistant' &&\n\t\t\t\t\t\t\t\t\t\t\t!message.isStreaming &&\n\t\t\t\t\t\t\t\t\t\t\t(() => {\n\t\t\t\t\t\t\t\t\t\t\t\tconst ctas = toolResultCtas(message.toolResults);\n\t\t\t\t\t\t\t\t\t\t\t\tif (ctas.length === 0) {\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<ToolCtaList>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{ctas.map((cta) => (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ToolCtaLink\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tkey={`${cta.tool}-${cta.url}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\thref={cta.url}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonToolCtaClick?.({\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttool: cta.tool,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\turl: cta.url,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{cta.label}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</ToolCtaLink>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</ToolCtaList>\n\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t})()}\n\t\t\t\t\t\t\t\t\t\t{sentAt && <MessageTimestamp>{sentAt}</MessageTimestamp>}\n\t\t\t\t\t\t\t\t\t\t{message.wasInterrupted && interruptedLabel && (\n\t\t\t\t\t\t\t\t\t\t\t<InterruptedNote>\n\t\t\t\t\t\t\t\t\t\t\t\t<StopIcon size={12} />\n\t\t\t\t\t\t\t\t\t\t\t\t{interruptedLabel}\n\t\t\t\t\t\t\t\t\t\t\t</InterruptedNote>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t{/* Only under a finished reply: mid-stream the text is still\n\t\t\t\t\t\t\t\t\t\t changing, so copying it would capture a partial answer. */}\n\t\t\t\t\t\t\t\t\t\t{message.role === 'assistant' &&\n\t\t\t\t\t\t\t\t\t\t\tmessage.content.length > 0 &&\n\t\t\t\t\t\t\t\t\t\t\t!message.isStreaming && (\n\t\t\t\t\t\t\t\t\t\t\t\t<MessageActions>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<MessageAction\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => void handleCopyMessage(message)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata-copied={copiedMessageId === message.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle={\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcopiedMessageId === message.id ? 'Copied' : 'Copy'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label={\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcopiedMessageId === message.id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'Response copied to clipboard'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'Copy response'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{copiedMessageId === message.id ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<CheckIcon />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<CopyIcon />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</MessageAction>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/* Only under the newest reply: regenerating appends a\n\t\t\t\t\t\t\t\t\t\t\t\t\t fresh turn at the end, so offering it against an\n\t\t\t\t\t\t\t\t\t\t\t\t\t older answer would not replace that answer. */}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{onRegenerate && message.id === newestMessage?.id && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<MessageAction\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={handleRegenerate}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={inputDisabled || isTurnInFlight}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Regenerate\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Regenerate response\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<RegenerateIcon />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</MessageAction>\n\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{!message.isRestored &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfeedback[message.id] === undefined && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<MessageAction\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\thandleFeedback(message.id, 'like')\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Good response\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Good response\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ThumbUpIcon />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</MessageAction>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<MessageAction\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\thandleFeedback(message.id, 'dislike')\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Bad response\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Bad response\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ThumbDownIcon />\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</MessageAction>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t</MessageActions>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t{dislikePromptId === message.id && (\n\t\t\t\t\t\t\t\t\t\t\t<CopilotFeedbackModal\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={dislikeTitle}\n\t\t\t\t\t\t\t\t\t\t\t\tsubtitle={dislikeSubtitle ?? `Help us improve ${title}`}\n\t\t\t\t\t\t\t\t\t\t\t\treasons={dislikeReasons}\n\t\t\t\t\t\t\t\t\t\t\t\tnotePlaceholder={dislikeNotePlaceholder}\n\t\t\t\t\t\t\t\t\t\t\t\tsubmitLabel={dislikeSubmitLabel}\n\t\t\t\t\t\t\t\t\t\t\t\tonSubmit={(detail) => reportDislike(message.id, detail)}\n\t\t\t\t\t\t\t\t\t\t\t\tonDismiss={() => reportDislike(message.id)}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</React.Fragment>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t{followUps && followUps.length > 0 && (\n\t\t\t\t\t\t\t\t<FollowUpList>\n\t\t\t\t\t\t\t\t\t{followUpsLabel && (\n\t\t\t\t\t\t\t\t\t\t<FollowUpsLabel>{followUpsLabel}</FollowUpsLabel>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t{followUps.map((followUp) => (\n\t\t\t\t\t\t\t\t\t\t<FollowUpChip\n\t\t\t\t\t\t\t\t\t\t\tkey={followUp}\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={inputDisabled}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => handleFollowUpClick(followUp)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<ArrowRightIcon />\n\t\t\t\t\t\t\t\t\t\t\t{followUp}\n\t\t\t\t\t\t\t\t\t\t</FollowUpChip>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</FollowUpList>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{isThinking && (\n\t\t\t\t\t\t\t\t<ThinkingBubble role=\"status\" aria-live=\"polite\">\n\t\t\t\t\t\t\t\t\t<AiStarsIcon\n\t\t\t\t\t\t\t\t\t\twidth={14}\n\t\t\t\t\t\t\t\t\t\theight={14}\n\t\t\t\t\t\t\t\t\t\tcolor={COLORS.content.brand}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t{thinkingLabel && (\n\t\t\t\t\t\t\t\t\t\t<ThinkingLabel>{thinkingLabel}</ThinkingLabel>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t<DotPulse color={COLORS.background.brand} size={5} />\n\t\t\t\t\t\t\t\t</ThinkingBubble>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<div ref={bottomRef} />\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</MessagesArea>\n\n\t\t\t\t<ScrollFade $visible={showScrollAffordance} aria-hidden=\"true\" />\n\t\t\t\t<JumpToLatestButton\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t$visible={showScrollAffordance}\n\t\t\t\t\tonClick={handleJumpToLatest}\n\t\t\t\t\ttabIndex={showScrollAffordance ? 0 : -1}\n\t\t\t\t\taria-label=\"Scroll to the latest message\"\n\t\t\t\t>\n\t\t\t\t\t<ArrowDownIcon width={16} height={16} />\n\t\t\t\t</JumpToLatestButton>\n\t\t\t</MessagesViewport>\n\n\t\t\t{errorMessage && <ErrorBanner role=\"alert\">{errorMessage}</ErrorBanner>}\n\n\t\t\t<InputArea>\n\t\t\t\t{attachment && (\n\t\t\t\t\t<AttachmentChip>\n\t\t\t\t\t\t<AiStarsIcon width={14} height={14} color={COLORS.content.brand} />\n\t\t\t\t\t\t<AttachmentText title={attachment}>{attachment}</AttachmentText>\n\t\t\t\t\t\t<AttachmentRemove\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={onAttachmentRemove}\n\t\t\t\t\t\t\taria-label=\"Remove the selected text\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<CrossIcon width={12} height={12} />\n\t\t\t\t\t\t</AttachmentRemove>\n\t\t\t\t\t</AttachmentChip>\n\t\t\t\t)}\n\t\t\t\t<InputWrapper $focused={isInputFocused}>\n\t\t\t\t\t<MessageInput\n\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\trows={1}\n\t\t\t\t\t\tvalue={input}\n\t\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\t\tdisabled={inputDisabled}\n\t\t\t\t\t\tonChange={handleInputChange}\n\t\t\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\t\t\tonFocus={() => setIsInputFocused(true)}\n\t\t\t\t\t\tonBlur={() => setIsInputFocused(false)}\n\t\t\t\t\t/>\n\t\t\t\t\t<SendButton\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t$active={canStop || canSend}\n\t\t\t\t\t\tdisabled={!canStop && !canSend}\n\t\t\t\t\t\tonClick={canStop ? onStop : handleSend}\n\t\t\t\t\t\taria-label={canStop ? 'Stop generating' : 'Send message'}\n\t\t\t\t\t>\n\t\t\t\t\t\t{canStop ? <StopIcon /> : <SendArrowIcon />}\n\t\t\t\t\t</SendButton>\n\t\t\t\t</InputWrapper>\n\t\t\t</InputArea>\n\n\t\t\t{disclaimer && <Disclaimer>{disclaimer}</Disclaimer>}\n\t\t</PanelContainer>\n\t);\n};\n\nexport default CopilotPanel;\n"],"names":["MAX_INPUT_HEIGHT_PX","AT_BOTTOM_TOLERANCE_PX","COPIED_FEEDBACK_MS","writeToClipboard","text","textarea","copied","NEAR_TOP_TOLERANCE_PX","formatMessageTime","timestamp","date","SendArrowIcon","jsxs","jsx","ArrowRightIcon","StopIcon","size","CopyIcon","CheckIcon","RegenerateIcon","ThumbUpIcon","filled","ThumbDownIcon","CopilotPanel","messages","onSend","onClose","title","badge","subtitle","avatar","placeholder","disclaimer","emptyStateTitle","emptyStateSubtitle","suggestions","onSuggestionClick","showRefresh","onRefresh","onRegenerate","onFeedback","dislikeTitle","dislikeSubtitle","dislikeReasons","dislikeNotePlaceholder","dislikeSubmitLabel","isThinking","thinkingLabel","isStreaming","onStop","interruptedLabel","followUpsLabel","onToolCtaClick","hasMoreHistory","isLoadingHistory","onLoadOlderMessages","historyLoadingLabel","inputDisabled","attachment","onAttachmentRemove","errorMessage","width","height","className","style","input","setInput","useState","isInputFocused","setIsInputFocused","isAtLatest","setIsAtLatest","copiedMessageId","setCopiedMessageId","feedback","setFeedback","dislikePromptId","setDislikePromptId","inputRef","useRef","wasTurnInFlightRef","copiedTimeoutRef","bottomRef","scrollerRef","lastScrollTopRef","anchorFromBottomRef","visibleMessages","message","isCopilotRegeneratePrompt","isEmpty","isTurnInFlight","canSend","canStop","newestMessage","followUps","showScrollAffordance","useEffect","scroller","anchor","handleScroll","distanceFromBottom","scrolledUp","handleJumpToLatest","handleCopyMessage","handleFeedback","messageId","vote","cleared","previous","current","reportDislike","detail","handleRegenerate","handleSend","trimmed","buildCopilotSelectionPrompt","turnJustEnded","handleInputChange","event","handleKeyDown","handleSuggestionClick","suggestion","handleFollowUpClick","followUp","PanelContainer","PanelHeader","HeaderAvatar","AiStarsIcon","HeaderText","HeaderTitleRow","TitleSmall","HeaderBadge","BodyCaption","COLORS","HeaderActions","IconAction","RefreshIcon","CrossIcon","MessagesViewport","MessagesArea","EmptyState","EmptyStateIcon","BodySecondary","SuggestionList","SuggestionChip","Fragment","HistoryLoader","DotPulse","sentAt","React","MessageBubble","renderCopilotMarkdown","ctas","toolResultCtas","ToolCtaList","cta","ToolCtaLink","MessageTimestamp","InterruptedNote","MessageActions","MessageAction","CopilotFeedbackModal","FollowUpList","FollowUpsLabel","FollowUpChip","ThinkingBubble","ThinkingLabel","ScrollFade","JumpToLatestButton","ArrowDownIcon","ErrorBanner","InputArea","AttachmentChip","AttachmentText","AttachmentRemove","InputWrapper","MessageInput","SendButton","Disclaimer"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react"),Ie=require("./copilotRegenerate.js"),F=require("./services/copilotDb.js"),me=require("./services/copilotFeedbackClient.js"),re=require("./services/copilotMessagesClient.js"),z=require("./services/copilotPageSession.js"),U=require("./services/copilotStreamClient.js"),se=require("./useCopilotIcebreakers.js"),ne="Something went wrong. Please try again.",he=({storeId:l,agent:w=se.DEFAULT_COPILOT_AGENT,route:R=se.DEFAULT_COPILOT_ROUTE,messagesPath:X,feedbackPath:Y,user:Z,loadHistory:J=!0,systemPrompt:K,sessionId:g,persist:i=!0,onError:b,onResponseComplete:Q,onSessionIdChange:D,baseUrl:M,endpointPath:V,headers:O,authToken:P,getAuthToken:H,fetchImpl:_})=>{const[G,N]=s.useState([]),[oe,W]=s.useState(g??null),[T,y]=s.useState(!1),[ce,ee]=s.useState(!0),[ue,k]=s.useState(),[ae,le]=s.useState(),[ie,L]=s.useState(!1),[B,p]=s.useState(!1),f=s.useRef([]),$=s.useRef(void 0),I=s.useRef(null),u=s.useRef(null),m=s.useRef(g??null),q=s.useRef(R);q.current=R;const h=s.useRef(1),v=s.useRef(!1),a=s.useCallback(e=>{const t=e(f.current);f.current=t,N(t)},[]),E=s.useCallback(e=>{$.current=e,le(e)},[]),C=s.useCallback(()=>{const e=m.current;!i||!e||F.persistCopilotTurn({route:q.current,sessionId:e,messages:f.current,previousResponseId:$.current,storeId:l,agent:w})},[w,i,l]),x=s.useRef({baseUrl:M,messagesPath:X,feedbackPath:Y,headers:O,authToken:P,getAuthToken:H,fetchImpl:_,storeId:l,loadHistory:J,user:Z});x.current={baseUrl:M,messagesPath:X,feedbackPath:Y,headers:O,authToken:P,getAuthToken:H,fetchImpl:_,storeId:l,loadHistory:J,user:Z};const A=s.useCallback(async e=>{const t=x.current,r=m.current;return!t.loadHistory||!r?null:re.fetchCopilotMessages({baseUrl:t.baseUrl,endpointPath:t.messagesPath,headers:t.headers,authToken:t.authToken,getAuthToken:t.getAuthToken,fetchImpl:t.fetchImpl,storeId:t.storeId,sessionId:r,page:e,pageSize:re.COPILOT_HISTORY_PAGE_SIZE})},[]);s.useEffect(()=>{let e=!1;return(async()=>{u.current?.(),u.current=null,I.current=null,y(!1),k(void 0),ee(!0),p(!1),v.current=!1,L(!1);const r=g??(i?await z.resolveCopilotSessionIdForRoute({route:R,storeId:l}):z.getCopilotSessionIdForRoute({route:R,storeId:l})),n=i?await F.loadCopilotSession(R,r):null;if(!e){if(m.current=r,W(r),f.current=n?.messages??[],N(f.current),E(n?.previousResponseId),ee(!1),D?.(r),f.current.length>0){h.current=2,p(x.current.loadHistory);return}if(!g){h.current=1,p(!1);return}h.current=1;try{const o=await A(1);if(e||!o)return;h.current=2,a(()=>o.messages),p(o.hasMore)}catch{p(!1)}}})(),()=>{e=!0}},[R,l,g,i,E,a,A]);const fe=s.useCallback(()=>{if(v.current||!B)return;v.current=!0,L(!0);const e=h.current;(async()=>{try{const t=await A(e);if(!t){p(!1);return}h.current=e+1,p(t.hasMore),a(r=>{const n=new Set(r.flatMap(c=>c.messageId?[c.id,c.messageId]:[c.id])),o=t.messages.filter(c=>!n.has(c.id));return o.length>0?[...o,...r]:r})}catch(t){b?.(t instanceof Error?t:new Error(String(t)))}finally{v.current=!1,L(!1)}})()},[a,B,b,A]);s.useEffect(()=>{i&&F.pruneCopilotSessions()},[i]),s.useEffect(()=>()=>u.current?.(),[]);const S=s.useCallback(e=>{const t=I.current;I.current=null,t&&a(r=>r.map(n=>{if(n.id!==t)return n;const o={...n,isStreaming:!1};return e?e(o):o}).filter(n=>n.id!==t||n.content.length>0||n.wasInterrupted===!0))},[a]),te=s.useCallback(e=>{const t=I.current;if(e.type==="STREAM"){const r=U.readCopilotEventContent(e).text;if(!t||!r)return;a(n=>n.map(o=>o.id===t?{...o,content:o.content+r,messageId:e.messageId??o.messageId}:o));return}if(e.type==="TEXT"){const{text:r,followUps:n,toolResults:o,responseId:c}=U.readCopilotEventContent(e);S(d=>({...d,content:r,messageId:e.messageId??d.messageId,...n?{followUps:n}:{},...o?{toolResults:o}:{}})),E(c),C(),Q?.({text:r,responseId:c,followUps:n});return}e.type==="ERROR"&&(k(U.readCopilotEventContent(e).text||ne),S(),C())},[a,S,Q,C,E]),j=s.useCallback(e=>{const t=e.trim(),r=m.current;if(!t||!r||T)return;k(void 0);const n=Date.now(),o=`copilot-a-${n}`;I.current=o,a(c=>[...c,{id:`copilot-u-${n}`,role:"user",content:t,timestamp:n},{id:o,role:"assistant",content:"",timestamp:n+1,isStreaming:!0}]),y(!0),C(),u.current=U.streamCopilotChat({baseUrl:M,endpointPath:V,headers:O,authToken:P,getAuthToken:H,fetchImpl:_,storeId:l,agent:w,route:R,userPrompt:t,sessionId:r,previousResponseId:$.current,systemPrompt:K},{onEvent:te,onComplete:()=>{S(),y(!1),u.current=null,C()},onError:c=>{k(c.message||ne),S(),y(!1),u.current=null,C(),b?.(c)},onAbort:()=>{S(),y(!1),u.current=null,C()}})},[a,P,M,V,_,w,S,H,te,O,T,b,R,C,l,K]),de=s.useCallback(()=>{f.current.some(t=>t.role==="assistant"&&t.content.length>0)&&j(Ie.COPILOT_REGENERATE_PROMPT)},[j]),Re=s.useCallback((e,t,r)=>{const n=x.current,o=m.current;if(!o)return;const c=f.current.find(d=>(d.messageId??d.id)===e);me.sendCopilotFeedback({baseUrl:n.baseUrl,endpointPath:n.feedbackPath,headers:n.headers,authToken:n.authToken,getAuthToken:n.getAuthToken,fetchImpl:n.fetchImpl,storeId:n.storeId,sessionId:o,messageId:e,feedback:t,route:q.current,reply:c?.content,user:n.user,reasons:r?.reasons,comment:r?.comment}).catch(d=>{b?.(d instanceof Error?d:new Error(String(d)))})},[b]),pe=s.useCallback(()=>{if(!u.current)return;const e=I.current;e&&a(t=>t.map(r=>r.id===e?{...r,wasInterrupted:!0}:r)),u.current(),u.current=null},[a]),Ce=s.useCallback(()=>{u.current?.(),u.current=null,I.current=null,y(!1),k(void 0),h.current=1,v.current=!1,L(!1),p(!1);const e=q.current,t=m.current,r=g??z.resetCopilotSessionForRoute({route:e,storeId:l});m.current=r,W(r),f.current=[],N([]),E(void 0),i&&t&&F.deleteCopilotSession(e,t),D?.(r)},[D,i,g,E,l]),ge=s.useMemo(()=>T&&!G.some(e=>e.isStreaming&&e.content),[T,G]);return{messages:G,sessionId:oe,previousResponseId:ae,isStreaming:T,isThinking:ge,isRestoring:ce,isLoadingHistory:ie,hasMoreHistory:B,error:ue,sendMessage:j,regenerate:de,submitFeedback:Re,loadOlderMessages:fe,stopStreaming:pe,startNewConversation:Ce}};exports.useCopilotStreamChat=he;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react"),me=require("./copilotRegenerate.js"),F=require("./services/copilotDb.js"),he=require("./services/copilotFeedbackClient.js"),re=require("./services/copilotMessagesClient.js"),z=require("./services/copilotPageSession.js"),U=require("./services/copilotStreamClient.js"),se=require("./useCopilotIcebreakers.js"),ne="Something went wrong. Please try again.",oe=u=>u.map(R=>R.role==="assistant"?{...R,isRestored:!0}:R),Se=({storeId:u,agent:R=se.DEFAULT_COPILOT_AGENT,route:p=se.DEFAULT_COPILOT_ROUTE,messagesPath:X,feedbackPath:Y,user:Z,loadHistory:J=!0,systemPrompt:K,sessionId:I,persist:l=!0,onError:y,onResponseComplete:Q,onSessionIdChange:D,baseUrl:P,endpointPath:V,headers:M,authToken:O,getAuthToken:H,fetchImpl:_})=>{const[G,N]=n.useState([]),[ce,W]=n.useState(I??null),[k,E]=n.useState(!1),[ue,ee]=n.useState(!0),[ae,v]=n.useState(),[ie,le]=n.useState(),[fe,L]=n.useState(!1),[B,C]=n.useState(!1),f=n.useRef([]),$=n.useRef(void 0),m=n.useRef(null),a=n.useRef(null),h=n.useRef(I??null),q=n.useRef(p);q.current=p;const S=n.useRef(1),w=n.useRef(!1),i=n.useCallback(e=>{const t=e(f.current);f.current=t,N(t)},[]),T=n.useCallback(e=>{$.current=e,le(e)},[]),g=n.useCallback(()=>{const e=h.current;!l||!e||F.persistCopilotTurn({route:q.current,sessionId:e,messages:f.current,previousResponseId:$.current,storeId:u,agent:R})},[R,l,u]),x=n.useRef({baseUrl:P,messagesPath:X,feedbackPath:Y,headers:M,authToken:O,getAuthToken:H,fetchImpl:_,storeId:u,loadHistory:J,user:Z});x.current={baseUrl:P,messagesPath:X,feedbackPath:Y,headers:M,authToken:O,getAuthToken:H,fetchImpl:_,storeId:u,loadHistory:J,user:Z};const A=n.useCallback(async e=>{const t=x.current,s=h.current;if(!t.loadHistory||!s)return null;const r=await re.fetchCopilotMessages({baseUrl:t.baseUrl,endpointPath:t.messagesPath,headers:t.headers,authToken:t.authToken,getAuthToken:t.getAuthToken,fetchImpl:t.fetchImpl,storeId:t.storeId,sessionId:s,page:e,pageSize:re.COPILOT_HISTORY_PAGE_SIZE});return{...r,messages:oe(r.messages)}},[]);n.useEffect(()=>{let e=!1;return(async()=>{a.current?.(),a.current=null,m.current=null,E(!1),v(void 0),ee(!0),C(!1),w.current=!1,L(!1);const s=I??(l?await z.resolveCopilotSessionIdForRoute({route:p,storeId:u}):z.getCopilotSessionIdForRoute({route:p,storeId:u})),r=l?await F.loadCopilotSession(p,s):null;if(!e){if(h.current=s,W(s),f.current=oe(r?.messages??[]),N(f.current),T(r?.previousResponseId),ee(!1),D?.(s),f.current.length>0){S.current=2,C(x.current.loadHistory);return}if(!I){S.current=1,C(!1);return}S.current=1;try{const o=await A(1);if(e||!o)return;S.current=2,i(()=>o.messages),C(o.hasMore)}catch{C(!1)}}})(),()=>{e=!0}},[p,u,I,l,T,i,A]);const de=n.useCallback(()=>{if(w.current||!B)return;w.current=!0,L(!0);const e=S.current;(async()=>{try{const t=await A(e);if(!t){C(!1);return}S.current=e+1,C(t.hasMore),i(s=>{const r=new Set(s.flatMap(c=>c.messageId?[c.id,c.messageId]:[c.id])),o=t.messages.filter(c=>!r.has(c.id));return o.length>0?[...o,...s]:s})}catch(t){y?.(t instanceof Error?t:new Error(String(t)))}finally{w.current=!1,L(!1)}})()},[i,B,y,A]);n.useEffect(()=>{l&&F.pruneCopilotSessions()},[l]),n.useEffect(()=>()=>a.current?.(),[]);const b=n.useCallback(e=>{const t=m.current;m.current=null,t&&i(s=>s.map(r=>{if(r.id!==t)return r;const o={...r,isStreaming:!1};return e?e(o):o}).filter(r=>r.id!==t||r.content.length>0||r.wasInterrupted===!0))},[i]),te=n.useCallback(e=>{const t=m.current;if(e.type==="STREAM"){const s=U.readCopilotEventContent(e).text;if(!t||!s)return;i(r=>r.map(o=>o.id===t?{...o,content:o.content+s,messageId:e.messageId??o.messageId}:o));return}if(e.type==="TEXT"){const{text:s,followUps:r,toolResults:o,responseId:c}=U.readCopilotEventContent(e);b(d=>({...d,content:s,messageId:e.messageId??d.messageId,...r?{followUps:r}:{},...o?{toolResults:o}:{}})),T(c),g(),Q?.({text:s,responseId:c,followUps:r});return}e.type==="ERROR"&&(v(U.readCopilotEventContent(e).text||ne),b(),g())},[i,b,Q,g,T]),j=n.useCallback(e=>{const t=e.trim(),s=h.current;if(!t||!s||k)return;v(void 0);const r=Date.now(),o=`copilot-a-${r}`;m.current=o,i(c=>[...c,{id:`copilot-u-${r}`,role:"user",content:t,timestamp:r},{id:o,role:"assistant",content:"",timestamp:r+1,isStreaming:!0}]),E(!0),g(),a.current=U.streamCopilotChat({baseUrl:P,endpointPath:V,headers:M,authToken:O,getAuthToken:H,fetchImpl:_,storeId:u,agent:R,route:p,userPrompt:t,sessionId:s,previousResponseId:$.current,systemPrompt:K},{onEvent:te,onComplete:()=>{b(),E(!1),a.current=null,g()},onError:c=>{v(c.message||ne),b(),E(!1),a.current=null,g(),y?.(c)},onAbort:()=>{b(),E(!1),a.current=null,g()}})},[i,O,P,V,_,R,b,H,te,M,k,y,p,g,u,K]),Re=n.useCallback(()=>{f.current.some(t=>t.role==="assistant"&&t.content.length>0)&&j(me.COPILOT_REGENERATE_PROMPT)},[j]),pe=n.useCallback((e,t,s)=>{const r=x.current,o=h.current;if(!o)return;const c=f.current.find(d=>(d.messageId??d.id)===e);he.sendCopilotFeedback({baseUrl:r.baseUrl,endpointPath:r.feedbackPath,headers:r.headers,authToken:r.authToken,getAuthToken:r.getAuthToken,fetchImpl:r.fetchImpl,storeId:r.storeId,sessionId:o,messageId:e,feedback:t,route:q.current,reply:c?.content,user:r.user,reasons:s?.reasons,comment:s?.comment}).catch(d=>{y?.(d instanceof Error?d:new Error(String(d)))})},[y]),Ce=n.useCallback(()=>{if(!a.current)return;const e=m.current;e&&i(t=>t.map(s=>s.id===e?{...s,wasInterrupted:!0}:s)),a.current(),a.current=null},[i]),ge=n.useCallback(()=>{a.current?.(),a.current=null,m.current=null,E(!1),v(void 0),S.current=1,w.current=!1,L(!1),C(!1);const e=q.current,t=h.current,s=I??z.resetCopilotSessionForRoute({route:e,storeId:u});h.current=s,W(s),f.current=[],N([]),T(void 0),l&&t&&F.deleteCopilotSession(e,t),D?.(s)},[D,l,I,T,u]),Ie=n.useMemo(()=>k&&!G.some(e=>e.isStreaming&&e.content),[k,G]);return{messages:G,sessionId:ce,previousResponseId:ie,isStreaming:k,isThinking:Ie,isRestoring:ue,isLoadingHistory:fe,hasMoreHistory:B,error:ae,sendMessage:j,regenerate:Re,submitFeedback:pe,loadOlderMessages:de,stopStreaming:Ce,startNewConversation:ge}};exports.useCopilotStreamChat=Se;
2
2
  //# sourceMappingURL=useCopilotStreamChat.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCopilotStreamChat.js","sources":["../../../../src/components/bik-copilot/useCopilotStreamChat.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { COPILOT_REGENERATE_PROMPT } from './copilotRegenerate';\nimport {\n\tCopilotFeedbackDetail,\n\tCopilotFeedbackVote,\n\tCopilotMessage,\n\tCopilotStreamEvent,\n\tUseCopilotStreamChatOptions,\n\tUseCopilotStreamChatResult,\n} from './model';\nimport {\n\tdeleteCopilotSession,\n\tloadCopilotSession,\n\tpersistCopilotTurn,\n\tpruneCopilotSessions,\n} from './services/copilotDb';\nimport { sendCopilotFeedback } from './services/copilotFeedbackClient';\nimport {\n\tCOPILOT_HISTORY_PAGE_SIZE,\n\tfetchCopilotMessages,\n} from './services/copilotMessagesClient';\nimport {\n\tgetCopilotSessionIdForRoute,\n\tresetCopilotSessionForRoute,\n\tresolveCopilotSessionIdForRoute,\n} from './services/copilotPageSession';\nimport {\n\treadCopilotEventContent,\n\tstreamCopilotChat,\n} from './services/copilotStreamClient';\nimport {\n\tDEFAULT_COPILOT_AGENT,\n\tDEFAULT_COPILOT_ROUTE,\n} from './useCopilotIcebreakers';\n\nconst GENERIC_ERROR = 'Something went wrong. Please try again.';\n\n/**\n * Runs a merchant copilot conversation against the streaming\n * `/bik-ai/copilot-chatbot` endpoint.\n *\n * Owns three things the endpoint's contract requires:\n *\n * 1. **The session id.** One per route. Navigating away and back to the same\n * route continues that conversation, and so does a refresh: the ids live in\n * memory but are recovered from the stored records, which is what keeps them\n * lined up with the history the backend keys by them (see\n * `copilotPageSession`).\n * 2. **`previousResponseId`.** Absent on the first turn; from then on it is the\n * `responseId` of the last `TEXT` event, which is what makes OpenAI replay the\n * earlier turns server side. A turn that breaks halfway returns no\n * `responseId`, and the backend means that — the next turn starts fresh.\n * 3. **The transcript**, stored in IndexedDB under `{sessionId}:{route}` and\n * written every turn, so leaving the page mid-conversation loses nothing.\n */\nexport const useCopilotStreamChat = ({\n\tstoreId,\n\tagent = DEFAULT_COPILOT_AGENT,\n\troute = DEFAULT_COPILOT_ROUTE,\n\tmessagesPath,\n\tfeedbackPath,\n\tuser,\n\tloadHistory = true,\n\tsystemPrompt,\n\tsessionId: pinnedSessionId,\n\tpersist = true,\n\tonError,\n\tonResponseComplete,\n\tonSessionIdChange,\n\tbaseUrl,\n\tendpointPath,\n\theaders,\n\tauthToken,\n\tgetAuthToken,\n\tfetchImpl,\n}: UseCopilotStreamChatOptions): UseCopilotStreamChatResult => {\n\tconst [messages, setMessages] = useState<CopilotMessage[]>([]);\n\tconst [sessionId, setSessionId] = useState<string | null>(\n\t\tpinnedSessionId ?? null,\n\t);\n\tconst [isStreaming, setIsStreaming] = useState(false);\n\tconst [isRestoring, setIsRestoring] = useState(true);\n\tconst [error, setError] = useState<string | undefined>();\n\tconst [previousResponseId, setPreviousResponseId] = useState<\n\t\tstring | undefined\n\t>();\n\tconst [isLoadingHistory, setIsLoadingHistory] = useState(false);\n\tconst [hasMoreHistory, setHasMoreHistory] = useState(false);\n\n\t// Async stream handlers run long after the render that created them, so the\n\t// live values live in refs and state is only the render mirror.\n\tconst messagesRef = useRef<CopilotMessage[]>([]);\n\tconst previousResponseIdRef = useRef<string | undefined>(undefined);\n\tconst streamingIdRef = useRef<string | null>(null);\n\tconst abortRef = useRef<(() => void) | null>(null);\n\tconst sessionIdRef = useRef<string | null>(pinnedSessionId ?? null);\n\t// The active route, for the async handlers that write the transcript.\n\tconst routeRef = useRef<string>(route);\n\trouteRef.current = route;\n\t// Next page of stored history to ask for. Starts at 2 when the local cache\n\t// already holds page 1's worth, which is the steady state.\n\tconst nextHistoryPageRef = useRef(1);\n\tconst isLoadingHistoryRef = useRef(false);\n\n\tconst applyMessages = useCallback(\n\t\t(updater: (previous: CopilotMessage[]) => CopilotMessage[]) => {\n\t\t\tconst next = updater(messagesRef.current);\n\t\t\tmessagesRef.current = next;\n\t\t\tsetMessages(next);\n\t\t},\n\t\t[],\n\t);\n\n\tconst setThreadResponseId = useCallback((responseId: string | undefined) => {\n\t\tpreviousResponseIdRef.current = responseId;\n\t\tsetPreviousResponseId(responseId);\n\t}, []);\n\n\tconst save = useCallback(() => {\n\t\tconst id = sessionIdRef.current;\n\t\tif (!persist || !id) {\n\t\t\treturn;\n\t\t}\n\t\tvoid persistCopilotTurn({\n\t\t\troute: routeRef.current,\n\t\t\tsessionId: id,\n\t\t\tmessages: messagesRef.current,\n\t\t\tpreviousResponseId: previousResponseIdRef.current,\n\t\t\tstoreId,\n\t\t\tagent,\n\t\t});\n\t}, [agent, persist, storeId]);\n\n\t// Transport config for the async readers below. Held in a ref rather than in\n\t// the effect's deps: a host that passes an inline `headers` object would\n\t// otherwise re-run restore on every render and wipe the transcript.\n\tconst transportRef = useRef({\n\t\tbaseUrl,\n\t\tmessagesPath,\n\t\tfeedbackPath,\n\t\theaders,\n\t\tauthToken,\n\t\tgetAuthToken,\n\t\tfetchImpl,\n\t\tstoreId,\n\t\tloadHistory,\n\t\tuser,\n\t});\n\ttransportRef.current = {\n\t\tbaseUrl,\n\t\tmessagesPath,\n\t\tfeedbackPath,\n\t\theaders,\n\t\tauthToken,\n\t\tgetAuthToken,\n\t\tfetchImpl,\n\t\tstoreId,\n\t\tloadHistory,\n\t\tuser,\n\t};\n\n\tconst readHistoryPage = useCallback(async (page: number) => {\n\t\tconst transport = transportRef.current;\n\t\tconst id = sessionIdRef.current;\n\t\tif (!transport.loadHistory || !id) {\n\t\t\treturn null;\n\t\t}\n\t\treturn fetchCopilotMessages({\n\t\t\tbaseUrl: transport.baseUrl,\n\t\t\tendpointPath: transport.messagesPath,\n\t\t\theaders: transport.headers,\n\t\t\tauthToken: transport.authToken,\n\t\t\tgetAuthToken: transport.getAuthToken,\n\t\t\tfetchImpl: transport.fetchImpl,\n\t\t\tstoreId: transport.storeId,\n\t\t\tsessionId: id,\n\t\t\tpage,\n\t\t\tpageSize: COPILOT_HISTORY_PAGE_SIZE,\n\t\t});\n\t}, []);\n\n\t// Resolve the conversation for this route and read it back from IndexedDB.\n\t// Runs again when `route` changes, because the route is half of a\n\t// conversation's identity — a different page is a different chat.\n\tuseEffect(() => {\n\t\tlet cancelled = false;\n\n\t\tconst restore = async () => {\n\t\t\t// Chunks from the previous route's turn must not land in this one.\n\t\t\tabortRef.current?.();\n\t\t\tabortRef.current = null;\n\t\t\tstreamingIdRef.current = null;\n\t\t\tsetIsStreaming(false);\n\t\t\tsetError(undefined);\n\t\t\tsetIsRestoring(true);\n\t\t\tsetHasMoreHistory(false);\n\t\t\tisLoadingHistoryRef.current = false;\n\t\t\tsetIsLoadingHistory(false);\n\n\t\t\tconst id =\n\t\t\t\tpinnedSessionId ??\n\t\t\t\t(persist\n\t\t\t\t\t? await resolveCopilotSessionIdForRoute({ route, storeId })\n\t\t\t\t\t: getCopilotSessionIdForRoute({ route, storeId }));\n\t\t\tconst record = persist ? await loadCopilotSession(route, id) : null;\n\t\t\tif (cancelled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsessionIdRef.current = id;\n\t\t\tsetSessionId(id);\n\t\t\tmessagesRef.current = record?.messages ?? [];\n\t\t\tsetMessages(messagesRef.current);\n\t\t\tsetThreadResponseId(record?.previousResponseId);\n\t\t\tsetIsRestoring(false);\n\t\t\tonSessionIdChange?.(id);\n\n\t\t\t// The cache holds at most one page, so what is on screen already *is*\n\t\t\t// page 1 and the reader's first scroll-up wants page 2.\n\t\t\tif (messagesRef.current.length > 0) {\n\t\t\t\tnextHistoryPageRef.current = 2;\n\t\t\t\tsetHasMoreHistory(transportRef.current.loadHistory);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Nothing cached and nothing to scroll, so page 1 has to be fetched for\n\t\t\t// the history to be reachable at all — but only for an id the host\n\t\t\t// pinned. An id this library resolved came out of the local records, so\n\t\t\t// an empty cache for it means an empty conversation, and a freshly\n\t\t\t// minted one has nothing behind it by definition. Asking anyway would\n\t\t\t// put a pointless request on the start of every new conversation.\n\t\t\tif (!pinnedSessionId) {\n\t\t\t\tnextHistoryPageRef.current = 1;\n\t\t\t\tsetHasMoreHistory(false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tnextHistoryPageRef.current = 1;\n\t\t\ttry {\n\t\t\t\tconst firstPage = await readHistoryPage(1);\n\t\t\t\tif (cancelled || !firstPage) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tnextHistoryPageRef.current = 2;\n\t\t\t\tapplyMessages(() => firstPage.messages);\n\t\t\t\tsetHasMoreHistory(firstPage.hasMore);\n\t\t\t} catch {\n\t\t\t\t// A session the backend has never heard of is the normal case for a\n\t\t\t\t// host-pinned id on its first use; the empty state covers it.\n\t\t\t\tsetHasMoreHistory(false);\n\t\t\t}\n\t\t};\n\n\t\tvoid restore();\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t\t// onSessionIdChange is event-like: re-running restore because the host\n\t\t// passed a new inline callback would wipe the transcript.\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [\n\t\troute,\n\t\tstoreId,\n\t\tpinnedSessionId,\n\t\tpersist,\n\t\tsetThreadResponseId,\n\t\tapplyMessages,\n\t\treadHistoryPage,\n\t]);\n\n\t/**\n\t * Prepends the page before the oldest message on screen.\n\t *\n\t * Guarded by a ref rather than the state flag because a scroll handler can\n\t * fire many times before React re-renders, and each of those would otherwise\n\t * start its own request for the same page.\n\t */\n\tconst loadOlderMessages = useCallback(() => {\n\t\tif (isLoadingHistoryRef.current || !hasMoreHistory) {\n\t\t\treturn;\n\t\t}\n\t\tisLoadingHistoryRef.current = true;\n\t\tsetIsLoadingHistory(true);\n\n\t\tconst page = nextHistoryPageRef.current;\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst older = await readHistoryPage(page);\n\t\t\t\tif (!older) {\n\t\t\t\t\tsetHasMoreHistory(false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tnextHistoryPageRef.current = page + 1;\n\t\t\t\tsetHasMoreHistory(older.hasMore);\n\t\t\t\tapplyMessages((previous) => {\n\t\t\t\t\tconst known = new Set(\n\t\t\t\t\t\tprevious.flatMap((message) =>\n\t\t\t\t\t\t\tmessage.messageId\n\t\t\t\t\t\t\t\t? [message.id, message.messageId]\n\t\t\t\t\t\t\t\t: [message.id],\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tconst fresh = older.messages.filter(\n\t\t\t\t\t\t(message) => !known.has(message.id),\n\t\t\t\t\t);\n\t\t\t\t\treturn fresh.length > 0 ? [...fresh, ...previous] : previous;\n\t\t\t\t});\n\t\t\t} catch (historyError) {\n\t\t\t\t// Left silent in the transcript on purpose: failing to reach further\n\t\t\t\t// back is not a failure of the conversation on screen. Another\n\t\t\t\t// scroll-up retries the same page.\n\t\t\t\tonError?.(\n\t\t\t\t\thistoryError instanceof Error\n\t\t\t\t\t\t? historyError\n\t\t\t\t\t\t: new Error(String(historyError)),\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tisLoadingHistoryRef.current = false;\n\t\t\t\tsetIsLoadingHistory(false);\n\t\t\t}\n\t\t})();\n\t}, [applyMessages, hasMoreHistory, onError, readHistoryPage]);\n\n\t// Records pile up one per route per page load otherwise, since every refresh\n\t// writes under a new key.\n\tuseEffect(() => {\n\t\tif (persist) {\n\t\t\tvoid pruneCopilotSessions();\n\t\t}\n\t}, [persist]);\n\n\t// Abort an in-flight turn if the copilot unmounts.\n\tuseEffect(() => () => abortRef.current?.(), []);\n\n\tconst finishStreamingBubble = useCallback(\n\t\t(mutate?: (message: CopilotMessage) => CopilotMessage) => {\n\t\t\tconst streamingId = streamingIdRef.current;\n\t\t\tstreamingIdRef.current = null;\n\t\t\tif (!streamingId) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tapplyMessages((previous) =>\n\t\t\t\tprevious\n\t\t\t\t\t.map((message) => {\n\t\t\t\t\t\tif (message.id !== streamingId) {\n\t\t\t\t\t\t\treturn message;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst settled: CopilotMessage = {\n\t\t\t\t\t\t\t...message,\n\t\t\t\t\t\t\tisStreaming: false,\n\t\t\t\t\t\t};\n\t\t\t\t\t\treturn mutate ? mutate(settled) : settled;\n\t\t\t\t\t})\n\t\t\t\t\t// An assistant bubble that never received text would render as an\n\t\t\t\t\t// empty balloon; the error banner carries the information instead.\n\t\t\t\t\t// A stopped turn keeps its bubble either way — the interrupted note\n\t\t\t\t\t// is the only trace a turn the reader cut short ever happened.\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(message) =>\n\t\t\t\t\t\t\tmessage.id !== streamingId ||\n\t\t\t\t\t\t\tmessage.content.length > 0 ||\n\t\t\t\t\t\t\tmessage.wasInterrupted === true,\n\t\t\t\t\t),\n\t\t\t);\n\t\t},\n\t\t[applyMessages],\n\t);\n\n\tconst handleEvent = useCallback(\n\t\t(event: CopilotStreamEvent) => {\n\t\t\tconst streamingId = streamingIdRef.current;\n\n\t\t\tif (event.type === 'STREAM') {\n\t\t\t\tconst chunk = readCopilotEventContent(event).text;\n\t\t\t\tif (!streamingId || !chunk) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tapplyMessages((previous) =>\n\t\t\t\t\tprevious.map((message) =>\n\t\t\t\t\t\tmessage.id === streamingId\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t...message,\n\t\t\t\t\t\t\t\t\tcontent: message.content + chunk,\n\t\t\t\t\t\t\t\t\tmessageId: event.messageId ?? message.messageId,\n\t\t\t\t\t\t\t }\n\t\t\t\t\t\t\t: message,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (event.type === 'TEXT') {\n\t\t\t\t// TEXT carries the whole message, so it replaces the accumulated\n\t\t\t\t// chunks rather than appending — that keeps the bubble correct even if\n\t\t\t\t// a STREAM frame was dropped or duplicated. `responseId` rides inside\n\t\t\t\t// the content payload (as `lastResponseId`).\n\t\t\t\tconst {\n\t\t\t\t\ttext: fullText,\n\t\t\t\t\tfollowUps,\n\t\t\t\t\ttoolResults,\n\t\t\t\t\tresponseId,\n\t\t\t\t} = readCopilotEventContent(event);\n\t\t\t\tfinishStreamingBubble((message) => ({\n\t\t\t\t\t...message,\n\t\t\t\t\tcontent: fullText,\n\t\t\t\t\tmessageId: event.messageId ?? message.messageId,\n\t\t\t\t\t...(followUps ? { followUps } : {}),\n\t\t\t\t\t// Kept on the message, not just handled in passing, so the\n\t\t\t\t\t// action button is still there after a reload.\n\t\t\t\t\t...(toolResults ? { toolResults } : {}),\n\t\t\t\t}));\n\t\t\t\tsetThreadResponseId(responseId);\n\t\t\t\tsave();\n\t\t\t\tonResponseComplete?.({\n\t\t\t\t\ttext: fullText,\n\t\t\t\t\tresponseId,\n\t\t\t\t\tfollowUps,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (event.type === 'ERROR') {\n\t\t\t\tsetError(readCopilotEventContent(event).text || GENERIC_ERROR);\n\t\t\t\tfinishStreamingBubble();\n\t\t\t\tsave();\n\t\t\t}\n\n\t\t\t// Unknown types (TOOL_STATUS is declared server side but not emitted)\n\t\t\t// are ignored so a new event type cannot break an existing client.\n\t\t},\n\t\t[\n\t\t\tapplyMessages,\n\t\t\tfinishStreamingBubble,\n\t\t\tonResponseComplete,\n\t\t\tsave,\n\t\t\tsetThreadResponseId,\n\t\t],\n\t);\n\n\tconst sendMessage = useCallback(\n\t\t(message: string) => {\n\t\t\tconst userPrompt = message.trim();\n\t\t\tconst id = sessionIdRef.current;\n\t\t\tif (!userPrompt || !id || isStreaming) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetError(undefined);\n\t\t\tconst now = Date.now();\n\t\t\tconst assistantBubbleId = `copilot-a-${now}`;\n\t\t\tstreamingIdRef.current = assistantBubbleId;\n\n\t\t\tapplyMessages((previous) => [\n\t\t\t\t...previous,\n\t\t\t\t{\n\t\t\t\t\tid: `copilot-u-${now}`,\n\t\t\t\t\trole: 'user',\n\t\t\t\t\tcontent: userPrompt,\n\t\t\t\t\ttimestamp: now,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: assistantBubbleId,\n\t\t\t\t\trole: 'assistant',\n\t\t\t\t\tcontent: '',\n\t\t\t\t\ttimestamp: now + 1,\n\t\t\t\t\tisStreaming: true,\n\t\t\t\t},\n\t\t\t]);\n\t\t\tsetIsStreaming(true);\n\t\t\tsave();\n\n\t\t\tabortRef.current = streamCopilotChat(\n\t\t\t\t{\n\t\t\t\t\tbaseUrl,\n\t\t\t\t\tendpointPath,\n\t\t\t\t\theaders,\n\t\t\t\t\tauthToken,\n\t\t\t\t\tgetAuthToken,\n\t\t\t\t\tfetchImpl,\n\t\t\t\t\tstoreId,\n\t\t\t\t\tagent,\n\t\t\t\t\troute,\n\t\t\t\t\tuserPrompt,\n\t\t\t\t\tsessionId: id,\n\t\t\t\t\tpreviousResponseId: previousResponseIdRef.current,\n\t\t\t\t\tsystemPrompt,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tonEvent: handleEvent,\n\t\t\t\t\tonComplete: () => {\n\t\t\t\t\t\t// STOP closes the stream without a frame, so this is where a turn\n\t\t\t\t\t\t// ends. TEXT has usually already settled the bubble.\n\t\t\t\t\t\tfinishStreamingBubble();\n\t\t\t\t\t\tsetIsStreaming(false);\n\t\t\t\t\t\tabortRef.current = null;\n\t\t\t\t\t\tsave();\n\t\t\t\t\t},\n\t\t\t\t\tonError: (streamError) => {\n\t\t\t\t\t\tsetError(streamError.message || GENERIC_ERROR);\n\t\t\t\t\t\tfinishStreamingBubble();\n\t\t\t\t\t\tsetIsStreaming(false);\n\t\t\t\t\t\tabortRef.current = null;\n\t\t\t\t\t\tsave();\n\t\t\t\t\t\tonError?.(streamError);\n\t\t\t\t\t},\n\t\t\t\t\tonAbort: () => {\n\t\t\t\t\t\tfinishStreamingBubble();\n\t\t\t\t\t\tsetIsStreaming(false);\n\t\t\t\t\t\tabortRef.current = null;\n\t\t\t\t\t\tsave();\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\t[\n\t\t\tapplyMessages,\n\t\t\tauthToken,\n\t\t\tbaseUrl,\n\t\t\tendpointPath,\n\t\t\tfetchImpl,\n\t\t\tagent,\n\t\t\tfinishStreamingBubble,\n\t\t\tgetAuthToken,\n\t\t\thandleEvent,\n\t\t\theaders,\n\t\t\tisStreaming,\n\t\t\tonError,\n\t\t\troute,\n\t\t\tsave,\n\t\t\tstoreId,\n\t\t\tsystemPrompt,\n\t\t],\n\t);\n\n\tconst regenerate = useCallback(() => {\n\t\tconst hasReply = messagesRef.current.some(\n\t\t\t(message) => message.role === 'assistant' && message.content.length > 0,\n\t\t);\n\t\tif (!hasReply) {\n\t\t\treturn;\n\t\t}\n\t\tsendMessage(COPILOT_REGENERATE_PROMPT);\n\t}, [sendMessage]);\n\n\t/**\n\t * Records a thumbs-up or thumbs-down on one reply.\n\t *\n\t * Fire-and-forget: a rating is a side note to the conversation, so a failed\n\t * post is reported through `onError` and otherwise ignored -- the thumb stays\n\t * lit either way rather than flickering back on a network hiccup.\n\t *\n\t * The reply's own text and the route ride along, so whoever reads the feedback\n\t * sees what was rated without looking the message up.\n\t */\n\tconst submitFeedback = useCallback(\n\t\t(\n\t\t\tmessageId: string,\n\t\t\tfeedback: CopilotFeedbackVote,\n\t\t\tdetail?: CopilotFeedbackDetail,\n\t\t) => {\n\t\t\tconst transport = transportRef.current;\n\t\t\tconst id = sessionIdRef.current;\n\t\t\tif (!id) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// A live turn's bubble carries only a local id until the final event\n\t\t\t// lands, so match on either and let the backend see whichever it gets.\n\t\t\tconst rated = messagesRef.current.find(\n\t\t\t\t(message) => (message.messageId ?? message.id) === messageId,\n\t\t\t);\n\t\t\tvoid sendCopilotFeedback({\n\t\t\t\tbaseUrl: transport.baseUrl,\n\t\t\t\tendpointPath: transport.feedbackPath,\n\t\t\t\theaders: transport.headers,\n\t\t\t\tauthToken: transport.authToken,\n\t\t\t\tgetAuthToken: transport.getAuthToken,\n\t\t\t\tfetchImpl: transport.fetchImpl,\n\t\t\t\tstoreId: transport.storeId,\n\t\t\t\tsessionId: id,\n\t\t\t\tmessageId,\n\t\t\t\tfeedback,\n\t\t\t\troute: routeRef.current,\n\t\t\t\treply: rated?.content,\n\t\t\t\tuser: transport.user,\n\t\t\t\treasons: detail?.reasons,\n\t\t\t\tcomment: detail?.comment,\n\t\t\t}).catch((feedbackError) => {\n\t\t\t\tonError?.(\n\t\t\t\t\tfeedbackError instanceof Error\n\t\t\t\t\t\t? feedbackError\n\t\t\t\t\t\t: new Error(String(feedbackError)),\n\t\t\t\t);\n\t\t\t});\n\t\t},\n\t\t[onError],\n\t);\n\n\tconst stopStreaming = useCallback(() => {\n\t\tif (!abortRef.current) {\n\t\t\treturn;\n\t\t}\n\t\t// Flagged here rather than in `onAbort`, because an abort that lands between\n\t\t// frames ends the stream through `onComplete` instead. Whichever fires, the\n\t\t// reader stopped this turn, and whatever text had arrived stays.\n\t\tconst streamingId = streamingIdRef.current;\n\t\tif (streamingId) {\n\t\t\tapplyMessages((previous) =>\n\t\t\t\tprevious.map((message) =>\n\t\t\t\t\tmessage.id === streamingId\n\t\t\t\t\t\t? { ...message, wasInterrupted: true }\n\t\t\t\t\t\t: message,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tabortRef.current();\n\t\tabortRef.current = null;\n\t}, [applyMessages]);\n\n\tconst startNewConversation = useCallback(() => {\n\t\tabortRef.current?.();\n\t\tabortRef.current = null;\n\t\tstreamingIdRef.current = null;\n\t\tsetIsStreaming(false);\n\t\tsetError(undefined);\n\t\t// A new conversation has no history behind it; the old session's pages\n\t\t// belong to the old session id.\n\t\tnextHistoryPageRef.current = 1;\n\t\tisLoadingHistoryRef.current = false;\n\t\tsetIsLoadingHistory(false);\n\t\tsetHasMoreHistory(false);\n\n\t\tconst activeRoute = routeRef.current;\n\t\t// The conversation being cleared, so its stored transcript can go with it.\n\t\tconst clearedId = sessionIdRef.current;\n\t\t// A host-pinned id is the host's to change, so only clear its transcript.\n\t\tconst nextId =\n\t\t\tpinnedSessionId ??\n\t\t\tresetCopilotSessionForRoute({ route: activeRoute, storeId });\n\t\tsessionIdRef.current = nextId;\n\t\tsetSessionId(nextId);\n\t\tmessagesRef.current = [];\n\t\tsetMessages([]);\n\t\tsetThreadResponseId(undefined);\n\n\t\t// \"Refresh chat\" wipes the record, not just the screen: the cleared\n\t\t// conversation is deleted from IndexedDB so reopening the tab starts fresh\n\t\t// instead of resuming it. Nothing is written for the new session here — its\n\t\t// first record is stamped when the first turn is sent, which is also what\n\t\t// keeps an untouched refresh from leaving an empty record behind.\n\t\tif (persist && clearedId) {\n\t\t\tvoid deleteCopilotSession(activeRoute, clearedId);\n\t\t}\n\t\tonSessionIdChange?.(nextId);\n\t}, [\n\t\tonSessionIdChange,\n\t\tpersist,\n\t\tpinnedSessionId,\n\t\tsetThreadResponseId,\n\t\tstoreId,\n\t]);\n\n\t// The thinking bubble is only for the wait before the first token; once text\n\t// starts arriving the bubble itself shows progress.\n\tconst isThinking = useMemo(\n\t\t() =>\n\t\t\tisStreaming &&\n\t\t\t!messages.some((message) => message.isStreaming && message.content),\n\t\t[isStreaming, messages],\n\t);\n\n\treturn {\n\t\tmessages,\n\t\tsessionId,\n\t\tpreviousResponseId,\n\t\tisStreaming,\n\t\tisThinking,\n\t\tisRestoring,\n\t\tisLoadingHistory,\n\t\thasMoreHistory,\n\t\terror,\n\t\tsendMessage,\n\t\tregenerate,\n\t\tsubmitFeedback,\n\t\tloadOlderMessages,\n\t\tstopStreaming,\n\t\tstartNewConversation,\n\t};\n};\n"],"names":["GENERIC_ERROR","useCopilotStreamChat","storeId","agent","DEFAULT_COPILOT_AGENT","route","DEFAULT_COPILOT_ROUTE","messagesPath","feedbackPath","user","loadHistory","systemPrompt","pinnedSessionId","persist","onError","onResponseComplete","onSessionIdChange","baseUrl","endpointPath","headers","authToken","getAuthToken","fetchImpl","messages","setMessages","useState","sessionId","setSessionId","isStreaming","setIsStreaming","isRestoring","setIsRestoring","error","setError","previousResponseId","setPreviousResponseId","isLoadingHistory","setIsLoadingHistory","hasMoreHistory","setHasMoreHistory","messagesRef","useRef","previousResponseIdRef","streamingIdRef","abortRef","sessionIdRef","routeRef","nextHistoryPageRef","isLoadingHistoryRef","applyMessages","useCallback","updater","next","setThreadResponseId","responseId","save","id","persistCopilotTurn","transportRef","readHistoryPage","page","transport","fetchCopilotMessages","COPILOT_HISTORY_PAGE_SIZE","useEffect","cancelled","resolveCopilotSessionIdForRoute","getCopilotSessionIdForRoute","record","loadCopilotSession","firstPage","loadOlderMessages","older","previous","known","message","fresh","historyError","pruneCopilotSessions","finishStreamingBubble","mutate","streamingId","settled","handleEvent","event","chunk","readCopilotEventContent","fullText","followUps","toolResults","sendMessage","userPrompt","now","assistantBubbleId","streamCopilotChat","streamError","regenerate","COPILOT_REGENERATE_PROMPT","submitFeedback","messageId","feedback","detail","rated","sendCopilotFeedback","feedbackError","stopStreaming","startNewConversation","activeRoute","clearedId","nextId","resetCopilotSessionForRoute","deleteCopilotSession","isThinking","useMemo"],"mappings":"6ZAmCMA,GAAgB,0CAoBTC,GAAuB,CAAC,CACpC,QAAAC,EACA,MAAAC,EAAQC,GAAAA,sBACR,MAAAC,EAAQC,GAAAA,sBACR,aAAAC,EACA,aAAAC,EACA,KAAAC,EACA,YAAAC,EAAc,GACd,aAAAC,EACA,UAAWC,EACX,QAAAC,EAAU,GACV,QAAAC,EACA,mBAAAC,EACA,kBAAAC,EACA,QAAAC,EACA,aAAAC,EACA,QAAAC,EACA,UAAAC,EACA,aAAAC,EACA,UAAAC,CACD,IAA+D,CAC9D,KAAM,CAACC,EAAUC,CAAW,EAAIC,EAAAA,SAA2B,CAAA,CAAE,EACvD,CAACC,GAAWC,CAAY,EAAIF,EAAAA,SACjCb,GAAmB,IAAA,EAEd,CAACgB,EAAaC,CAAc,EAAIJ,EAAAA,SAAS,EAAK,EAC9C,CAACK,GAAaC,EAAc,EAAIN,EAAAA,SAAS,EAAI,EAC7C,CAACO,GAAOC,CAAQ,EAAIR,WAAA,EACpB,CAACS,GAAoBC,EAAqB,EAAIV,WAAA,EAG9C,CAACW,GAAkBC,CAAmB,EAAIZ,EAAAA,SAAS,EAAK,EACxD,CAACa,EAAgBC,CAAiB,EAAId,EAAAA,SAAS,EAAK,EAIpDe,EAAcC,EAAAA,OAAyB,EAAE,EACzCC,EAAwBD,EAAAA,OAA2B,MAAS,EAC5DE,EAAiBF,EAAAA,OAAsB,IAAI,EAC3CG,EAAWH,EAAAA,OAA4B,IAAI,EAC3CI,EAAeJ,EAAAA,OAAsB7B,GAAmB,IAAI,EAE5DkC,EAAWL,EAAAA,OAAepC,CAAK,EACrCyC,EAAS,QAAUzC,EAGnB,MAAM0C,EAAqBN,EAAAA,OAAO,CAAC,EAC7BO,EAAsBP,EAAAA,OAAO,EAAK,EAElCQ,EAAgBC,EAAAA,YACpBC,GAA8D,CAC9D,MAAMC,EAAOD,EAAQX,EAAY,OAAO,EACxCA,EAAY,QAAUY,EACtB5B,EAAY4B,CAAI,CACjB,EACA,CAAA,CAAC,EAGIC,EAAsBH,cAAaI,GAAmC,CAC3EZ,EAAsB,QAAUY,EAChCnB,GAAsBmB,CAAU,CACjC,EAAG,CAAA,CAAE,EAECC,EAAOL,EAAAA,YAAY,IAAM,CAC9B,MAAMM,EAAKX,EAAa,QACpB,CAAChC,GAAW,CAAC2C,GAGZC,qBAAmB,CACvB,MAAOX,EAAS,QAChB,UAAWU,EACX,SAAUhB,EAAY,QACtB,mBAAoBE,EAAsB,QAC1C,QAAAxC,EACA,MAAAC,CAAA,CACA,CACF,EAAG,CAACA,EAAOU,EAASX,CAAO,CAAC,EAKtBwD,EAAejB,EAAAA,OAAO,CAC3B,QAAAxB,EACA,aAAAV,EACA,aAAAC,EACA,QAAAW,EACA,UAAAC,EACA,aAAAC,EACA,UAAAC,EACA,QAAApB,EACA,YAAAQ,EACA,KAAAD,CAAA,CACA,EACDiD,EAAa,QAAU,CACtB,QAAAzC,EACA,aAAAV,EACA,aAAAC,EACA,QAAAW,EACA,UAAAC,EACA,aAAAC,EACA,UAAAC,EACA,QAAApB,EACA,YAAAQ,EACA,KAAAD,CAAA,EAGD,MAAMkD,EAAkBT,cAAY,MAAOU,GAAiB,CAC3D,MAAMC,EAAYH,EAAa,QACzBF,EAAKX,EAAa,QACxB,MAAI,CAACgB,EAAU,aAAe,CAACL,EACvB,KAEDM,wBAAqB,CAC3B,QAASD,EAAU,QACnB,aAAcA,EAAU,aACxB,QAASA,EAAU,QACnB,UAAWA,EAAU,UACrB,aAAcA,EAAU,aACxB,UAAWA,EAAU,UACrB,QAASA,EAAU,QACnB,UAAWL,EACX,KAAAI,EACA,SAAUG,GAAAA,yBAAA,CACV,CACF,EAAG,CAAA,CAAE,EAKLC,EAAAA,UAAU,IAAM,CACf,IAAIC,EAAY,GAoEhB,OAlEgB,SAAY,CAE3BrB,EAAS,UAAA,EACTA,EAAS,QAAU,KACnBD,EAAe,QAAU,KACzBd,EAAe,EAAK,EACpBI,EAAS,MAAS,EAClBF,GAAe,EAAI,EACnBQ,EAAkB,EAAK,EACvBS,EAAoB,QAAU,GAC9BX,EAAoB,EAAK,EAEzB,MAAMmB,EACL5C,IACCC,EACE,MAAMqD,EAAAA,gCAAgC,CAAE,MAAA7D,EAAO,QAAAH,CAAA,CAAS,EACxDiE,EAAAA,4BAA4B,CAAE,MAAA9D,EAAO,QAAAH,EAAS,GAC5CkE,EAASvD,EAAU,MAAMwD,EAAAA,mBAAmBhE,EAAOmD,CAAE,EAAI,KAC/D,GAAI,CAAAS,EAcJ,IAVApB,EAAa,QAAUW,EACvB7B,EAAa6B,CAAE,EACfhB,EAAY,QAAU4B,GAAQ,UAAY,CAAA,EAC1C5C,EAAYgB,EAAY,OAAO,EAC/Ba,EAAoBe,GAAQ,kBAAkB,EAC9CrC,GAAe,EAAK,EACpBf,IAAoBwC,CAAE,EAIlBhB,EAAY,QAAQ,OAAS,EAAG,CACnCO,EAAmB,QAAU,EAC7BR,EAAkBmB,EAAa,QAAQ,WAAW,EAClD,MACD,CAQA,GAAI,CAAC9C,EAAiB,CACrBmC,EAAmB,QAAU,EAC7BR,EAAkB,EAAK,EACvB,MACD,CAEAQ,EAAmB,QAAU,EAC7B,GAAI,CACH,MAAMuB,EAAY,MAAMX,EAAgB,CAAC,EACzC,GAAIM,GAAa,CAACK,EACjB,OAEDvB,EAAmB,QAAU,EAC7BE,EAAc,IAAMqB,EAAU,QAAQ,EACtC/B,EAAkB+B,EAAU,OAAO,CACpC,MAAQ,CAGP/B,EAAkB,EAAK,CACxB,EACD,GAEK,EACE,IAAM,CACZ0B,EAAY,EACb,CAID,EAAG,CACF5D,EACAH,EACAU,EACAC,EACAwC,EACAJ,EACAU,CAAA,CACA,EASD,MAAMY,GAAoBrB,EAAAA,YAAY,IAAM,CAC3C,GAAIF,EAAoB,SAAW,CAACV,EACnC,OAEDU,EAAoB,QAAU,GAC9BX,EAAoB,EAAI,EAExB,MAAMuB,EAAOb,EAAmB,SAC1B,SAAY,CACjB,GAAI,CACH,MAAMyB,EAAQ,MAAMb,EAAgBC,CAAI,EACxC,GAAI,CAACY,EAAO,CACXjC,EAAkB,EAAK,EACvB,MACD,CACAQ,EAAmB,QAAUa,EAAO,EACpCrB,EAAkBiC,EAAM,OAAO,EAC/BvB,EAAewB,GAAa,CAC3B,MAAMC,EAAQ,IAAI,IACjBD,EAAS,QAASE,GACjBA,EAAQ,UACL,CAACA,EAAQ,GAAIA,EAAQ,SAAS,EAC9B,CAACA,EAAQ,EAAE,CAAA,CACf,EAEKC,EAAQJ,EAAM,SAAS,OAC3BG,GAAY,CAACD,EAAM,IAAIC,EAAQ,EAAE,CAAA,EAEnC,OAAOC,EAAM,OAAS,EAAI,CAAC,GAAGA,EAAO,GAAGH,CAAQ,EAAIA,CACrD,CAAC,CACF,OAASI,EAAc,CAItB/D,IACC+D,aAAwB,MACrBA,EACA,IAAI,MAAM,OAAOA,CAAY,CAAC,CAAA,CAEnC,QAAA,CACC7B,EAAoB,QAAU,GAC9BX,EAAoB,EAAK,CAC1B,CACD,GAAA,CACD,EAAG,CAACY,EAAeX,EAAgBxB,EAAS6C,CAAe,CAAC,EAI5DK,EAAAA,UAAU,IAAM,CACXnD,GACEiE,uBAAA,CAEP,EAAG,CAACjE,CAAO,CAAC,EAGZmD,EAAAA,UAAU,IAAM,IAAMpB,EAAS,UAAA,EAAa,CAAA,CAAE,EAE9C,MAAMmC,EAAwB7B,EAAAA,YAC5B8B,GAAyD,CACzD,MAAMC,EAActC,EAAe,QACnCA,EAAe,QAAU,KACpBsC,GAGLhC,EAAewB,GACdA,EACE,IAAKE,GAAY,CACjB,GAAIA,EAAQ,KAAOM,EAClB,OAAON,EAER,MAAMO,EAA0B,CAC/B,GAAGP,EACH,YAAa,EAAA,EAEd,OAAOK,EAASA,EAAOE,CAAO,EAAIA,CACnC,CAAC,EAKA,OACCP,GACAA,EAAQ,KAAOM,GACfN,EAAQ,QAAQ,OAAS,GACzBA,EAAQ,iBAAmB,EAAA,CAC7B,CAEH,EACA,CAAC1B,CAAa,CAAA,EAGTkC,GAAcjC,EAAAA,YAClBkC,GAA8B,CAC9B,MAAMH,EAActC,EAAe,QAEnC,GAAIyC,EAAM,OAAS,SAAU,CAC5B,MAAMC,EAAQC,EAAAA,wBAAwBF,CAAK,EAAE,KAC7C,GAAI,CAACH,GAAe,CAACI,EACpB,OAEDpC,EAAewB,GACdA,EAAS,IAAKE,GACbA,EAAQ,KAAOM,EACZ,CACA,GAAGN,EACH,QAASA,EAAQ,QAAUU,EAC3B,UAAWD,EAAM,WAAaT,EAAQ,SAAA,EAEtCA,CAAA,CACJ,EAED,MACD,CAEA,GAAIS,EAAM,OAAS,OAAQ,CAK1B,KAAM,CACL,KAAMG,EACN,UAAAC,EACA,YAAAC,EACA,WAAAnC,CAAA,EACGgC,EAAAA,wBAAwBF,CAAK,EACjCL,EAAuBJ,IAAa,CACnC,GAAGA,EACH,QAASY,EACT,UAAWH,EAAM,WAAaT,EAAQ,UACtC,GAAIa,EAAY,CAAE,UAAAA,CAAA,EAAc,CAAA,EAGhC,GAAIC,EAAc,CAAE,YAAAA,GAAgB,CAAA,CAAC,EACpC,EACFpC,EAAoBC,CAAU,EAC9BC,EAAA,EACAxC,IAAqB,CACpB,KAAMwE,EACN,WAAAjC,EACA,UAAAkC,CAAA,CACA,EACD,MACD,CAEIJ,EAAM,OAAS,UAClBnD,EAASqD,EAAAA,wBAAwBF,CAAK,EAAE,MAAQpF,EAAa,EAC7D+E,EAAA,EACAxB,EAAA,EAKF,EACA,CACCN,EACA8B,EACAhE,EACAwC,EACAF,CAAA,CACD,EAGKqC,EAAcxC,EAAAA,YAClByB,GAAoB,CACpB,MAAMgB,EAAahB,EAAQ,KAAA,EACrBnB,EAAKX,EAAa,QACxB,GAAI,CAAC8C,GAAc,CAACnC,GAAM5B,EACzB,OAGDK,EAAS,MAAS,EAClB,MAAM2D,EAAM,KAAK,IAAA,EACXC,EAAoB,aAAaD,CAAG,GAC1CjD,EAAe,QAAUkD,EAEzB5C,EAAewB,GAAa,CAC3B,GAAGA,EACH,CACC,GAAI,aAAamB,CAAG,GACpB,KAAM,OACN,QAASD,EACT,UAAWC,CAAA,EAEZ,CACC,GAAIC,EACJ,KAAM,YACN,QAAS,GACT,UAAWD,EAAM,EACjB,YAAa,EAAA,CACd,CACA,EACD/D,EAAe,EAAI,EACnB0B,EAAA,EAEAX,EAAS,QAAUkD,EAAAA,kBAClB,CACC,QAAA7E,EACA,aAAAC,EACA,QAAAC,EACA,UAAAC,EACA,aAAAC,EACA,UAAAC,EACA,QAAApB,EACA,MAAAC,EACA,MAAAE,EACA,WAAAsF,EACA,UAAWnC,EACX,mBAAoBd,EAAsB,QAC1C,aAAA/B,CAAA,EAED,CACC,QAASwE,GACT,WAAY,IAAM,CAGjBJ,EAAA,EACAlD,EAAe,EAAK,EACpBe,EAAS,QAAU,KACnBW,EAAA,CACD,EACA,QAAUwC,GAAgB,CACzB9D,EAAS8D,EAAY,SAAW/F,EAAa,EAC7C+E,EAAA,EACAlD,EAAe,EAAK,EACpBe,EAAS,QAAU,KACnBW,EAAA,EACAzC,IAAUiF,CAAW,CACtB,EACA,QAAS,IAAM,CACdhB,EAAA,EACAlD,EAAe,EAAK,EACpBe,EAAS,QAAU,KACnBW,EAAA,CACD,CAAA,CACD,CAEF,EACA,CACCN,EACA7B,EACAH,EACAC,EACAI,EACAnB,EACA4E,EACA1D,EACA8D,GACAhE,EACAS,EACAd,EACAT,EACAkD,EACArD,EACAS,CAAA,CACD,EAGKqF,GAAa9C,EAAAA,YAAY,IAAM,CACnBV,EAAY,QAAQ,KACnCmC,GAAYA,EAAQ,OAAS,aAAeA,EAAQ,QAAQ,OAAS,CAAA,GAKvEe,EAAYO,GAAAA,yBAAyB,CACtC,EAAG,CAACP,CAAW,CAAC,EAYVQ,GAAiBhD,EAAAA,YACtB,CACCiD,EACAC,EACAC,IACI,CACJ,MAAMxC,EAAYH,EAAa,QACzBF,EAAKX,EAAa,QACxB,GAAI,CAACW,EACJ,OAID,MAAM8C,EAAQ9D,EAAY,QAAQ,KAChCmC,IAAaA,EAAQ,WAAaA,EAAQ,MAAQwB,CAAA,EAE/CI,uBAAoB,CACxB,QAAS1C,EAAU,QACnB,aAAcA,EAAU,aACxB,QAASA,EAAU,QACnB,UAAWA,EAAU,UACrB,aAAcA,EAAU,aACxB,UAAWA,EAAU,UACrB,QAASA,EAAU,QACnB,UAAWL,EACX,UAAA2C,EACA,SAAAC,EACA,MAAOtD,EAAS,QAChB,MAAOwD,GAAO,QACd,KAAMzC,EAAU,KAChB,QAASwC,GAAQ,QACjB,QAASA,GAAQ,OAAA,CACjB,EAAE,MAAOG,GAAkB,CAC3B1F,IACC0F,aAAyB,MACtBA,EACA,IAAI,MAAM,OAAOA,CAAa,CAAC,CAAA,CAEpC,CAAC,CACF,EACA,CAAC1F,CAAO,CAAA,EAGH2F,GAAgBvD,EAAAA,YAAY,IAAM,CACvC,GAAI,CAACN,EAAS,QACb,OAKD,MAAMqC,EAActC,EAAe,QAC/BsC,GACHhC,EAAewB,GACdA,EAAS,IAAKE,GACbA,EAAQ,KAAOM,EACZ,CAAE,GAAGN,EAAS,eAAgB,IAC9BA,CAAA,CACJ,EAGF/B,EAAS,QAAA,EACTA,EAAS,QAAU,IACpB,EAAG,CAACK,CAAa,CAAC,EAEZyD,GAAuBxD,EAAAA,YAAY,IAAM,CAC9CN,EAAS,UAAA,EACTA,EAAS,QAAU,KACnBD,EAAe,QAAU,KACzBd,EAAe,EAAK,EACpBI,EAAS,MAAS,EAGlBc,EAAmB,QAAU,EAC7BC,EAAoB,QAAU,GAC9BX,EAAoB,EAAK,EACzBE,EAAkB,EAAK,EAEvB,MAAMoE,EAAc7D,EAAS,QAEvB8D,EAAY/D,EAAa,QAEzBgE,EACLjG,GACAkG,EAAAA,4BAA4B,CAAE,MAAOH,EAAa,QAAAzG,EAAS,EAC5D2C,EAAa,QAAUgE,EACvBlF,EAAakF,CAAM,EACnBrE,EAAY,QAAU,CAAA,EACtBhB,EAAY,CAAA,CAAE,EACd6B,EAAoB,MAAS,EAOzBxC,GAAW+F,GACTG,EAAAA,qBAAqBJ,EAAaC,CAAS,EAEjD5F,IAAoB6F,CAAM,CAC3B,EAAG,CACF7F,EACAH,EACAD,EACAyC,EACAnD,CAAA,CACA,EAIK8G,GAAaC,EAAAA,QAClB,IACCrF,GACA,CAACL,EAAS,KAAMoD,GAAYA,EAAQ,aAAeA,EAAQ,OAAO,EACnE,CAAC/C,EAAaL,CAAQ,CAAA,EAGvB,MAAO,CACN,SAAAA,EACA,UAAAG,GACA,mBAAAQ,GACA,YAAAN,EACA,WAAAoF,GACA,YAAAlF,GACA,iBAAAM,GACA,eAAAE,EACA,MAAAN,GACA,YAAA0D,EACA,WAAAM,GACA,eAAAE,GACA,kBAAA3B,GACA,cAAAkC,GACA,qBAAAC,EAAA,CAEF"}
1
+ {"version":3,"file":"useCopilotStreamChat.js","sources":["../../../../src/components/bik-copilot/useCopilotStreamChat.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { COPILOT_REGENERATE_PROMPT } from './copilotRegenerate';\nimport {\n\tCopilotFeedbackDetail,\n\tCopilotFeedbackVote,\n\tCopilotMessage,\n\tCopilotStreamEvent,\n\tUseCopilotStreamChatOptions,\n\tUseCopilotStreamChatResult,\n} from './model';\nimport {\n\tdeleteCopilotSession,\n\tloadCopilotSession,\n\tpersistCopilotTurn,\n\tpruneCopilotSessions,\n} from './services/copilotDb';\nimport { sendCopilotFeedback } from './services/copilotFeedbackClient';\nimport {\n\tCOPILOT_HISTORY_PAGE_SIZE,\n\tfetchCopilotMessages,\n} from './services/copilotMessagesClient';\nimport {\n\tgetCopilotSessionIdForRoute,\n\tresetCopilotSessionForRoute,\n\tresolveCopilotSessionIdForRoute,\n} from './services/copilotPageSession';\nimport {\n\treadCopilotEventContent,\n\tstreamCopilotChat,\n} from './services/copilotStreamClient';\nimport {\n\tDEFAULT_COPILOT_AGENT,\n\tDEFAULT_COPILOT_ROUTE,\n} from './useCopilotIcebreakers';\n\nconst GENERIC_ERROR = 'Something went wrong. Please try again.';\n\n/**\n * Runs a merchant copilot conversation against the streaming\n * `/bik-ai/copilot-chatbot` endpoint.\n *\n * Owns three things the endpoint's contract requires:\n *\n * 1. **The session id.** One per route. Navigating away and back to the same\n * route continues that conversation, and so does a refresh: the ids live in\n * memory but are recovered from the stored records, which is what keeps them\n * lined up with the history the backend keys by them (see\n * `copilotPageSession`).\n * 2. **`previousResponseId`.** Absent on the first turn; from then on it is the\n * `responseId` of the last `TEXT` event, which is what makes OpenAI replay the\n * earlier turns server side. A turn that breaks halfway returns no\n * `responseId`, and the backend means that — the next turn starts fresh.\n * 3. **The transcript**, stored in IndexedDB under `{sessionId}:{route}` and\n * written every turn, so leaving the page mid-conversation loses nothing.\n */\nconst asRestored = (messages: CopilotMessage[]): CopilotMessage[] =>\n\tmessages.map((message) =>\n\t\tmessage.role === 'assistant' ? { ...message, isRestored: true } : message,\n\t);\n\nexport const useCopilotStreamChat = ({\n\tstoreId,\n\tagent = DEFAULT_COPILOT_AGENT,\n\troute = DEFAULT_COPILOT_ROUTE,\n\tmessagesPath,\n\tfeedbackPath,\n\tuser,\n\tloadHistory = true,\n\tsystemPrompt,\n\tsessionId: pinnedSessionId,\n\tpersist = true,\n\tonError,\n\tonResponseComplete,\n\tonSessionIdChange,\n\tbaseUrl,\n\tendpointPath,\n\theaders,\n\tauthToken,\n\tgetAuthToken,\n\tfetchImpl,\n}: UseCopilotStreamChatOptions): UseCopilotStreamChatResult => {\n\tconst [messages, setMessages] = useState<CopilotMessage[]>([]);\n\tconst [sessionId, setSessionId] = useState<string | null>(\n\t\tpinnedSessionId ?? null,\n\t);\n\tconst [isStreaming, setIsStreaming] = useState(false);\n\tconst [isRestoring, setIsRestoring] = useState(true);\n\tconst [error, setError] = useState<string | undefined>();\n\tconst [previousResponseId, setPreviousResponseId] = useState<\n\t\tstring | undefined\n\t>();\n\tconst [isLoadingHistory, setIsLoadingHistory] = useState(false);\n\tconst [hasMoreHistory, setHasMoreHistory] = useState(false);\n\n\t// Async stream handlers run long after the render that created them, so the\n\t// live values live in refs and state is only the render mirror.\n\tconst messagesRef = useRef<CopilotMessage[]>([]);\n\tconst previousResponseIdRef = useRef<string | undefined>(undefined);\n\tconst streamingIdRef = useRef<string | null>(null);\n\tconst abortRef = useRef<(() => void) | null>(null);\n\tconst sessionIdRef = useRef<string | null>(pinnedSessionId ?? null);\n\t// The active route, for the async handlers that write the transcript.\n\tconst routeRef = useRef<string>(route);\n\trouteRef.current = route;\n\t// Next page of stored history to ask for. Starts at 2 when the local cache\n\t// already holds page 1's worth, which is the steady state.\n\tconst nextHistoryPageRef = useRef(1);\n\tconst isLoadingHistoryRef = useRef(false);\n\n\tconst applyMessages = useCallback(\n\t\t(updater: (previous: CopilotMessage[]) => CopilotMessage[]) => {\n\t\t\tconst next = updater(messagesRef.current);\n\t\t\tmessagesRef.current = next;\n\t\t\tsetMessages(next);\n\t\t},\n\t\t[],\n\t);\n\n\tconst setThreadResponseId = useCallback((responseId: string | undefined) => {\n\t\tpreviousResponseIdRef.current = responseId;\n\t\tsetPreviousResponseId(responseId);\n\t}, []);\n\n\tconst save = useCallback(() => {\n\t\tconst id = sessionIdRef.current;\n\t\tif (!persist || !id) {\n\t\t\treturn;\n\t\t}\n\t\tvoid persistCopilotTurn({\n\t\t\troute: routeRef.current,\n\t\t\tsessionId: id,\n\t\t\tmessages: messagesRef.current,\n\t\t\tpreviousResponseId: previousResponseIdRef.current,\n\t\t\tstoreId,\n\t\t\tagent,\n\t\t});\n\t}, [agent, persist, storeId]);\n\n\t// Transport config for the async readers below. Held in a ref rather than in\n\t// the effect's deps: a host that passes an inline `headers` object would\n\t// otherwise re-run restore on every render and wipe the transcript.\n\tconst transportRef = useRef({\n\t\tbaseUrl,\n\t\tmessagesPath,\n\t\tfeedbackPath,\n\t\theaders,\n\t\tauthToken,\n\t\tgetAuthToken,\n\t\tfetchImpl,\n\t\tstoreId,\n\t\tloadHistory,\n\t\tuser,\n\t});\n\ttransportRef.current = {\n\t\tbaseUrl,\n\t\tmessagesPath,\n\t\tfeedbackPath,\n\t\theaders,\n\t\tauthToken,\n\t\tgetAuthToken,\n\t\tfetchImpl,\n\t\tstoreId,\n\t\tloadHistory,\n\t\tuser,\n\t};\n\n\tconst readHistoryPage = useCallback(async (page: number) => {\n\t\tconst transport = transportRef.current;\n\t\tconst id = sessionIdRef.current;\n\t\tif (!transport.loadHistory || !id) {\n\t\t\treturn null;\n\t\t}\n\t\tconst historyPage = await fetchCopilotMessages({\n\t\t\tbaseUrl: transport.baseUrl,\n\t\t\tendpointPath: transport.messagesPath,\n\t\t\theaders: transport.headers,\n\t\t\tauthToken: transport.authToken,\n\t\t\tgetAuthToken: transport.getAuthToken,\n\t\t\tfetchImpl: transport.fetchImpl,\n\t\t\tstoreId: transport.storeId,\n\t\t\tsessionId: id,\n\t\t\tpage,\n\t\t\tpageSize: COPILOT_HISTORY_PAGE_SIZE,\n\t\t});\n\t\treturn { ...historyPage, messages: asRestored(historyPage.messages) };\n\t}, []);\n\n\t// Resolve the conversation for this route and read it back from IndexedDB.\n\t// Runs again when `route` changes, because the route is half of a\n\t// conversation's identity — a different page is a different chat.\n\tuseEffect(() => {\n\t\tlet cancelled = false;\n\n\t\tconst restore = async () => {\n\t\t\t// Chunks from the previous route's turn must not land in this one.\n\t\t\tabortRef.current?.();\n\t\t\tabortRef.current = null;\n\t\t\tstreamingIdRef.current = null;\n\t\t\tsetIsStreaming(false);\n\t\t\tsetError(undefined);\n\t\t\tsetIsRestoring(true);\n\t\t\tsetHasMoreHistory(false);\n\t\t\tisLoadingHistoryRef.current = false;\n\t\t\tsetIsLoadingHistory(false);\n\n\t\t\tconst id =\n\t\t\t\tpinnedSessionId ??\n\t\t\t\t(persist\n\t\t\t\t\t? await resolveCopilotSessionIdForRoute({ route, storeId })\n\t\t\t\t\t: getCopilotSessionIdForRoute({ route, storeId }));\n\t\t\tconst record = persist ? await loadCopilotSession(route, id) : null;\n\t\t\tif (cancelled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsessionIdRef.current = id;\n\t\t\tsetSessionId(id);\n\t\t\tmessagesRef.current = asRestored(record?.messages ?? []);\n\t\t\tsetMessages(messagesRef.current);\n\t\t\tsetThreadResponseId(record?.previousResponseId);\n\t\t\tsetIsRestoring(false);\n\t\t\tonSessionIdChange?.(id);\n\n\t\t\t// The cache holds at most one page, so what is on screen already *is*\n\t\t\t// page 1 and the reader's first scroll-up wants page 2.\n\t\t\tif (messagesRef.current.length > 0) {\n\t\t\t\tnextHistoryPageRef.current = 2;\n\t\t\t\tsetHasMoreHistory(transportRef.current.loadHistory);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Nothing cached and nothing to scroll, so page 1 has to be fetched for\n\t\t\t// the history to be reachable at all — but only for an id the host\n\t\t\t// pinned. An id this library resolved came out of the local records, so\n\t\t\t// an empty cache for it means an empty conversation, and a freshly\n\t\t\t// minted one has nothing behind it by definition. Asking anyway would\n\t\t\t// put a pointless request on the start of every new conversation.\n\t\t\tif (!pinnedSessionId) {\n\t\t\t\tnextHistoryPageRef.current = 1;\n\t\t\t\tsetHasMoreHistory(false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tnextHistoryPageRef.current = 1;\n\t\t\ttry {\n\t\t\t\tconst firstPage = await readHistoryPage(1);\n\t\t\t\tif (cancelled || !firstPage) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tnextHistoryPageRef.current = 2;\n\t\t\t\tapplyMessages(() => firstPage.messages);\n\t\t\t\tsetHasMoreHistory(firstPage.hasMore);\n\t\t\t} catch {\n\t\t\t\t// A session the backend has never heard of is the normal case for a\n\t\t\t\t// host-pinned id on its first use; the empty state covers it.\n\t\t\t\tsetHasMoreHistory(false);\n\t\t\t}\n\t\t};\n\n\t\tvoid restore();\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t\t// onSessionIdChange is event-like: re-running restore because the host\n\t\t// passed a new inline callback would wipe the transcript.\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [\n\t\troute,\n\t\tstoreId,\n\t\tpinnedSessionId,\n\t\tpersist,\n\t\tsetThreadResponseId,\n\t\tapplyMessages,\n\t\treadHistoryPage,\n\t]);\n\n\t/**\n\t * Prepends the page before the oldest message on screen.\n\t *\n\t * Guarded by a ref rather than the state flag because a scroll handler can\n\t * fire many times before React re-renders, and each of those would otherwise\n\t * start its own request for the same page.\n\t */\n\tconst loadOlderMessages = useCallback(() => {\n\t\tif (isLoadingHistoryRef.current || !hasMoreHistory) {\n\t\t\treturn;\n\t\t}\n\t\tisLoadingHistoryRef.current = true;\n\t\tsetIsLoadingHistory(true);\n\n\t\tconst page = nextHistoryPageRef.current;\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst older = await readHistoryPage(page);\n\t\t\t\tif (!older) {\n\t\t\t\t\tsetHasMoreHistory(false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tnextHistoryPageRef.current = page + 1;\n\t\t\t\tsetHasMoreHistory(older.hasMore);\n\t\t\t\tapplyMessages((previous) => {\n\t\t\t\t\tconst known = new Set(\n\t\t\t\t\t\tprevious.flatMap((message) =>\n\t\t\t\t\t\t\tmessage.messageId\n\t\t\t\t\t\t\t\t? [message.id, message.messageId]\n\t\t\t\t\t\t\t\t: [message.id],\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tconst fresh = older.messages.filter(\n\t\t\t\t\t\t(message) => !known.has(message.id),\n\t\t\t\t\t);\n\t\t\t\t\treturn fresh.length > 0 ? [...fresh, ...previous] : previous;\n\t\t\t\t});\n\t\t\t} catch (historyError) {\n\t\t\t\t// Left silent in the transcript on purpose: failing to reach further\n\t\t\t\t// back is not a failure of the conversation on screen. Another\n\t\t\t\t// scroll-up retries the same page.\n\t\t\t\tonError?.(\n\t\t\t\t\thistoryError instanceof Error\n\t\t\t\t\t\t? historyError\n\t\t\t\t\t\t: new Error(String(historyError)),\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tisLoadingHistoryRef.current = false;\n\t\t\t\tsetIsLoadingHistory(false);\n\t\t\t}\n\t\t})();\n\t}, [applyMessages, hasMoreHistory, onError, readHistoryPage]);\n\n\t// Records pile up one per route per page load otherwise, since every refresh\n\t// writes under a new key.\n\tuseEffect(() => {\n\t\tif (persist) {\n\t\t\tvoid pruneCopilotSessions();\n\t\t}\n\t}, [persist]);\n\n\t// Abort an in-flight turn if the copilot unmounts.\n\tuseEffect(() => () => abortRef.current?.(), []);\n\n\tconst finishStreamingBubble = useCallback(\n\t\t(mutate?: (message: CopilotMessage) => CopilotMessage) => {\n\t\t\tconst streamingId = streamingIdRef.current;\n\t\t\tstreamingIdRef.current = null;\n\t\t\tif (!streamingId) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tapplyMessages((previous) =>\n\t\t\t\tprevious\n\t\t\t\t\t.map((message) => {\n\t\t\t\t\t\tif (message.id !== streamingId) {\n\t\t\t\t\t\t\treturn message;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst settled: CopilotMessage = {\n\t\t\t\t\t\t\t...message,\n\t\t\t\t\t\t\tisStreaming: false,\n\t\t\t\t\t\t};\n\t\t\t\t\t\treturn mutate ? mutate(settled) : settled;\n\t\t\t\t\t})\n\t\t\t\t\t// An assistant bubble that never received text would render as an\n\t\t\t\t\t// empty balloon; the error banner carries the information instead.\n\t\t\t\t\t// A stopped turn keeps its bubble either way — the interrupted note\n\t\t\t\t\t// is the only trace a turn the reader cut short ever happened.\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(message) =>\n\t\t\t\t\t\t\tmessage.id !== streamingId ||\n\t\t\t\t\t\t\tmessage.content.length > 0 ||\n\t\t\t\t\t\t\tmessage.wasInterrupted === true,\n\t\t\t\t\t),\n\t\t\t);\n\t\t},\n\t\t[applyMessages],\n\t);\n\n\tconst handleEvent = useCallback(\n\t\t(event: CopilotStreamEvent) => {\n\t\t\tconst streamingId = streamingIdRef.current;\n\n\t\t\tif (event.type === 'STREAM') {\n\t\t\t\tconst chunk = readCopilotEventContent(event).text;\n\t\t\t\tif (!streamingId || !chunk) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tapplyMessages((previous) =>\n\t\t\t\t\tprevious.map((message) =>\n\t\t\t\t\t\tmessage.id === streamingId\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t...message,\n\t\t\t\t\t\t\t\t\tcontent: message.content + chunk,\n\t\t\t\t\t\t\t\t\tmessageId: event.messageId ?? message.messageId,\n\t\t\t\t\t\t\t }\n\t\t\t\t\t\t\t: message,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (event.type === 'TEXT') {\n\t\t\t\t// TEXT carries the whole message, so it replaces the accumulated\n\t\t\t\t// chunks rather than appending — that keeps the bubble correct even if\n\t\t\t\t// a STREAM frame was dropped or duplicated. `responseId` rides inside\n\t\t\t\t// the content payload (as `lastResponseId`).\n\t\t\t\tconst {\n\t\t\t\t\ttext: fullText,\n\t\t\t\t\tfollowUps,\n\t\t\t\t\ttoolResults,\n\t\t\t\t\tresponseId,\n\t\t\t\t} = readCopilotEventContent(event);\n\t\t\t\tfinishStreamingBubble((message) => ({\n\t\t\t\t\t...message,\n\t\t\t\t\tcontent: fullText,\n\t\t\t\t\tmessageId: event.messageId ?? message.messageId,\n\t\t\t\t\t...(followUps ? { followUps } : {}),\n\t\t\t\t\t// Kept on the message, not just handled in passing, so the\n\t\t\t\t\t// action button is still there after a reload.\n\t\t\t\t\t...(toolResults ? { toolResults } : {}),\n\t\t\t\t}));\n\t\t\t\tsetThreadResponseId(responseId);\n\t\t\t\tsave();\n\t\t\t\tonResponseComplete?.({\n\t\t\t\t\ttext: fullText,\n\t\t\t\t\tresponseId,\n\t\t\t\t\tfollowUps,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (event.type === 'ERROR') {\n\t\t\t\tsetError(readCopilotEventContent(event).text || GENERIC_ERROR);\n\t\t\t\tfinishStreamingBubble();\n\t\t\t\tsave();\n\t\t\t}\n\n\t\t\t// Unknown types (TOOL_STATUS is declared server side but not emitted)\n\t\t\t// are ignored so a new event type cannot break an existing client.\n\t\t},\n\t\t[\n\t\t\tapplyMessages,\n\t\t\tfinishStreamingBubble,\n\t\t\tonResponseComplete,\n\t\t\tsave,\n\t\t\tsetThreadResponseId,\n\t\t],\n\t);\n\n\tconst sendMessage = useCallback(\n\t\t(message: string) => {\n\t\t\tconst userPrompt = message.trim();\n\t\t\tconst id = sessionIdRef.current;\n\t\t\tif (!userPrompt || !id || isStreaming) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetError(undefined);\n\t\t\tconst now = Date.now();\n\t\t\tconst assistantBubbleId = `copilot-a-${now}`;\n\t\t\tstreamingIdRef.current = assistantBubbleId;\n\n\t\t\tapplyMessages((previous) => [\n\t\t\t\t...previous,\n\t\t\t\t{\n\t\t\t\t\tid: `copilot-u-${now}`,\n\t\t\t\t\trole: 'user',\n\t\t\t\t\tcontent: userPrompt,\n\t\t\t\t\ttimestamp: now,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: assistantBubbleId,\n\t\t\t\t\trole: 'assistant',\n\t\t\t\t\tcontent: '',\n\t\t\t\t\ttimestamp: now + 1,\n\t\t\t\t\tisStreaming: true,\n\t\t\t\t},\n\t\t\t]);\n\t\t\tsetIsStreaming(true);\n\t\t\tsave();\n\n\t\t\tabortRef.current = streamCopilotChat(\n\t\t\t\t{\n\t\t\t\t\tbaseUrl,\n\t\t\t\t\tendpointPath,\n\t\t\t\t\theaders,\n\t\t\t\t\tauthToken,\n\t\t\t\t\tgetAuthToken,\n\t\t\t\t\tfetchImpl,\n\t\t\t\t\tstoreId,\n\t\t\t\t\tagent,\n\t\t\t\t\troute,\n\t\t\t\t\tuserPrompt,\n\t\t\t\t\tsessionId: id,\n\t\t\t\t\tpreviousResponseId: previousResponseIdRef.current,\n\t\t\t\t\tsystemPrompt,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tonEvent: handleEvent,\n\t\t\t\t\tonComplete: () => {\n\t\t\t\t\t\t// STOP closes the stream without a frame, so this is where a turn\n\t\t\t\t\t\t// ends. TEXT has usually already settled the bubble.\n\t\t\t\t\t\tfinishStreamingBubble();\n\t\t\t\t\t\tsetIsStreaming(false);\n\t\t\t\t\t\tabortRef.current = null;\n\t\t\t\t\t\tsave();\n\t\t\t\t\t},\n\t\t\t\t\tonError: (streamError) => {\n\t\t\t\t\t\tsetError(streamError.message || GENERIC_ERROR);\n\t\t\t\t\t\tfinishStreamingBubble();\n\t\t\t\t\t\tsetIsStreaming(false);\n\t\t\t\t\t\tabortRef.current = null;\n\t\t\t\t\t\tsave();\n\t\t\t\t\t\tonError?.(streamError);\n\t\t\t\t\t},\n\t\t\t\t\tonAbort: () => {\n\t\t\t\t\t\tfinishStreamingBubble();\n\t\t\t\t\t\tsetIsStreaming(false);\n\t\t\t\t\t\tabortRef.current = null;\n\t\t\t\t\t\tsave();\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\t[\n\t\t\tapplyMessages,\n\t\t\tauthToken,\n\t\t\tbaseUrl,\n\t\t\tendpointPath,\n\t\t\tfetchImpl,\n\t\t\tagent,\n\t\t\tfinishStreamingBubble,\n\t\t\tgetAuthToken,\n\t\t\thandleEvent,\n\t\t\theaders,\n\t\t\tisStreaming,\n\t\t\tonError,\n\t\t\troute,\n\t\t\tsave,\n\t\t\tstoreId,\n\t\t\tsystemPrompt,\n\t\t],\n\t);\n\n\tconst regenerate = useCallback(() => {\n\t\tconst hasReply = messagesRef.current.some(\n\t\t\t(message) => message.role === 'assistant' && message.content.length > 0,\n\t\t);\n\t\tif (!hasReply) {\n\t\t\treturn;\n\t\t}\n\t\tsendMessage(COPILOT_REGENERATE_PROMPT);\n\t}, [sendMessage]);\n\n\t/**\n\t * Records a thumbs-up or thumbs-down on one reply.\n\t *\n\t * Fire-and-forget: a rating is a side note to the conversation, so a failed\n\t * post is reported through `onError` and otherwise ignored -- the thumb stays\n\t * lit either way rather than flickering back on a network hiccup.\n\t *\n\t * The reply's own text and the route ride along, so whoever reads the feedback\n\t * sees what was rated without looking the message up.\n\t */\n\tconst submitFeedback = useCallback(\n\t\t(\n\t\t\tmessageId: string,\n\t\t\tfeedback: CopilotFeedbackVote,\n\t\t\tdetail?: CopilotFeedbackDetail,\n\t\t) => {\n\t\t\tconst transport = transportRef.current;\n\t\t\tconst id = sessionIdRef.current;\n\t\t\tif (!id) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// A live turn's bubble carries only a local id until the final event\n\t\t\t// lands, so match on either and let the backend see whichever it gets.\n\t\t\tconst rated = messagesRef.current.find(\n\t\t\t\t(message) => (message.messageId ?? message.id) === messageId,\n\t\t\t);\n\t\t\tvoid sendCopilotFeedback({\n\t\t\t\tbaseUrl: transport.baseUrl,\n\t\t\t\tendpointPath: transport.feedbackPath,\n\t\t\t\theaders: transport.headers,\n\t\t\t\tauthToken: transport.authToken,\n\t\t\t\tgetAuthToken: transport.getAuthToken,\n\t\t\t\tfetchImpl: transport.fetchImpl,\n\t\t\t\tstoreId: transport.storeId,\n\t\t\t\tsessionId: id,\n\t\t\t\tmessageId,\n\t\t\t\tfeedback,\n\t\t\t\troute: routeRef.current,\n\t\t\t\treply: rated?.content,\n\t\t\t\tuser: transport.user,\n\t\t\t\treasons: detail?.reasons,\n\t\t\t\tcomment: detail?.comment,\n\t\t\t}).catch((feedbackError) => {\n\t\t\t\tonError?.(\n\t\t\t\t\tfeedbackError instanceof Error\n\t\t\t\t\t\t? feedbackError\n\t\t\t\t\t\t: new Error(String(feedbackError)),\n\t\t\t\t);\n\t\t\t});\n\t\t},\n\t\t[onError],\n\t);\n\n\tconst stopStreaming = useCallback(() => {\n\t\tif (!abortRef.current) {\n\t\t\treturn;\n\t\t}\n\t\t// Flagged here rather than in `onAbort`, because an abort that lands between\n\t\t// frames ends the stream through `onComplete` instead. Whichever fires, the\n\t\t// reader stopped this turn, and whatever text had arrived stays.\n\t\tconst streamingId = streamingIdRef.current;\n\t\tif (streamingId) {\n\t\t\tapplyMessages((previous) =>\n\t\t\t\tprevious.map((message) =>\n\t\t\t\t\tmessage.id === streamingId\n\t\t\t\t\t\t? { ...message, wasInterrupted: true }\n\t\t\t\t\t\t: message,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tabortRef.current();\n\t\tabortRef.current = null;\n\t}, [applyMessages]);\n\n\tconst startNewConversation = useCallback(() => {\n\t\tabortRef.current?.();\n\t\tabortRef.current = null;\n\t\tstreamingIdRef.current = null;\n\t\tsetIsStreaming(false);\n\t\tsetError(undefined);\n\t\t// A new conversation has no history behind it; the old session's pages\n\t\t// belong to the old session id.\n\t\tnextHistoryPageRef.current = 1;\n\t\tisLoadingHistoryRef.current = false;\n\t\tsetIsLoadingHistory(false);\n\t\tsetHasMoreHistory(false);\n\n\t\tconst activeRoute = routeRef.current;\n\t\t// The conversation being cleared, so its stored transcript can go with it.\n\t\tconst clearedId = sessionIdRef.current;\n\t\t// A host-pinned id is the host's to change, so only clear its transcript.\n\t\tconst nextId =\n\t\t\tpinnedSessionId ??\n\t\t\tresetCopilotSessionForRoute({ route: activeRoute, storeId });\n\t\tsessionIdRef.current = nextId;\n\t\tsetSessionId(nextId);\n\t\tmessagesRef.current = [];\n\t\tsetMessages([]);\n\t\tsetThreadResponseId(undefined);\n\n\t\t// \"Refresh chat\" wipes the record, not just the screen: the cleared\n\t\t// conversation is deleted from IndexedDB so reopening the tab starts fresh\n\t\t// instead of resuming it. Nothing is written for the new session here — its\n\t\t// first record is stamped when the first turn is sent, which is also what\n\t\t// keeps an untouched refresh from leaving an empty record behind.\n\t\tif (persist && clearedId) {\n\t\t\tvoid deleteCopilotSession(activeRoute, clearedId);\n\t\t}\n\t\tonSessionIdChange?.(nextId);\n\t}, [\n\t\tonSessionIdChange,\n\t\tpersist,\n\t\tpinnedSessionId,\n\t\tsetThreadResponseId,\n\t\tstoreId,\n\t]);\n\n\t// The thinking bubble is only for the wait before the first token; once text\n\t// starts arriving the bubble itself shows progress.\n\tconst isThinking = useMemo(\n\t\t() =>\n\t\t\tisStreaming &&\n\t\t\t!messages.some((message) => message.isStreaming && message.content),\n\t\t[isStreaming, messages],\n\t);\n\n\treturn {\n\t\tmessages,\n\t\tsessionId,\n\t\tpreviousResponseId,\n\t\tisStreaming,\n\t\tisThinking,\n\t\tisRestoring,\n\t\tisLoadingHistory,\n\t\thasMoreHistory,\n\t\terror,\n\t\tsendMessage,\n\t\tregenerate,\n\t\tsubmitFeedback,\n\t\tloadOlderMessages,\n\t\tstopStreaming,\n\t\tstartNewConversation,\n\t};\n};\n"],"names":["GENERIC_ERROR","asRestored","messages","message","useCopilotStreamChat","storeId","agent","DEFAULT_COPILOT_AGENT","route","DEFAULT_COPILOT_ROUTE","messagesPath","feedbackPath","user","loadHistory","systemPrompt","pinnedSessionId","persist","onError","onResponseComplete","onSessionIdChange","baseUrl","endpointPath","headers","authToken","getAuthToken","fetchImpl","setMessages","useState","sessionId","setSessionId","isStreaming","setIsStreaming","isRestoring","setIsRestoring","error","setError","previousResponseId","setPreviousResponseId","isLoadingHistory","setIsLoadingHistory","hasMoreHistory","setHasMoreHistory","messagesRef","useRef","previousResponseIdRef","streamingIdRef","abortRef","sessionIdRef","routeRef","nextHistoryPageRef","isLoadingHistoryRef","applyMessages","useCallback","updater","next","setThreadResponseId","responseId","save","id","persistCopilotTurn","transportRef","readHistoryPage","page","transport","historyPage","fetchCopilotMessages","COPILOT_HISTORY_PAGE_SIZE","useEffect","cancelled","resolveCopilotSessionIdForRoute","getCopilotSessionIdForRoute","record","loadCopilotSession","firstPage","loadOlderMessages","older","previous","known","fresh","historyError","pruneCopilotSessions","finishStreamingBubble","mutate","streamingId","settled","handleEvent","event","chunk","readCopilotEventContent","fullText","followUps","toolResults","sendMessage","userPrompt","now","assistantBubbleId","streamCopilotChat","streamError","regenerate","COPILOT_REGENERATE_PROMPT","submitFeedback","messageId","feedback","detail","rated","sendCopilotFeedback","feedbackError","stopStreaming","startNewConversation","activeRoute","clearedId","nextId","resetCopilotSessionForRoute","deleteCopilotSession","isThinking","useMemo"],"mappings":"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"}