@holaboss/client 0.2.0-beta.7 → 0.2.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +53 -16
- package/dist/index.d.ts +53 -16
- package/dist/index.js +5 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
var
|
|
3
|
-
`))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&
|
|
4
|
-
`)}:null},
|
|
2
|
+
var z=Object.defineProperty;var we=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames;var Te=Object.prototype.hasOwnProperty;var Pe=(e,t)=>{for(var o in t)z(e,o,{get:t[o],enumerable:!0})},Se=(e,t,o,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of xe(t))!Te.call(e,n)&&n!==o&&z(e,n,{get:()=>t[n],enumerable:!(s=we(t,n))||s.enumerable});return e};var ke=e=>Se(z({},"__esModule",{value:!0}),e);var Ve={};Pe(Ve,{Chat:()=>ie,GRID_MAX_COLS:()=>L,GRID_MAX_ROWS:()=>N,HolaProvider:()=>se,PortalConflict:()=>v,bandOfKind:()=>B,bandOfType:()=>q,createApiTransport:()=>re,createHolaClient:()=>le,createPortalData:()=>ye,createWidgetTransport:()=>F,deriveFields:()=>J,footprintOf:()=>X,footprintWith:()=>j,inferSpec:()=>V,layoutSegments:()=>Y,mergeMessage:()=>Z,parseDashboardSpec:()=>Q,renderModel:()=>K,textDeltaOf:()=>I,thinkingDeltaOf:()=>W,toolEventOf:()=>M,useArtifactUrl:()=>$,useChat:()=>U,useHolaClient:()=>O,useThread:()=>he});module.exports=ke(Ve);var ee=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},I=e=>{let t=ee(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},W=e=>{let t=ee(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},M=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var Re=/^ /,k=e=>{if(e&&typeof e=="object"){let t=e,o=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:o,retryable:t.retryable}:{error:o}}return{error:"request_failed"}},Ce=e=>{let t="message",o=[];for(let s of e.split(`
|
|
3
|
+
`))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&o.push(s.slice(5).replace(Re,"")));return o.length>0?{event:t,data:o.join(`
|
|
4
|
+
`)}:null},He=(e,t,o)=>{let s;try{s=JSON.parse(e.data)}catch{return}if(e.event==="session"){let n=s.sessionId;n&&(o(n),t.onSession?.(n));return}if(e.event==="turn_event"){t.onEvent?.(s);let n=I(s);n&&t.onText?.(n);let l=W(s);l&&t.onThinking?.(l);let r=M(s);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let n=s;o(n.sessionId),t.onComplete?.(n);return}e.event==="chat_error"&&t.onError?.(k(s))},h=async(e,t)=>{let o=await fetch(e,{headers:t});if(!o.ok)throw new Error(`hola: request failed (${o.status})`);return await o.json()},te=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},ne=async(e,t,o,s)=>{let n=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:s});if(!(n.ok&&n.body)){t.onError?.({error:`stream_failed_${n.status}`});return}let l=n.body.getReader(),r=new TextDecoder,a="",i=()=>{let u=a.indexOf(`
|
|
5
5
|
|
|
6
|
-
`);for(;u!==-1;){let b=a.slice(0,u);a=a.slice(u+2);let c=
|
|
6
|
+
`);for(;u!==-1;){let b=a.slice(0,u);a=a.slice(u+2);let c=Ce(b);c&&He(c,t,o),u=a.indexOf(`
|
|
7
7
|
|
|
8
|
-
`)}},d=async()=>{let u=await l.read();if(!u.done)return a+=o.decode(u.value,{stream:!0}),i(),d()};await d()};var Te=/\/+$/,M="/api/v1/end-user",X=e=>{let t=e.baseUrl.replace(Te,""),r=()=>typeof e.token=="function"?e.token():e.token,s=o=>e.project?`${o}project=${encodeURIComponent(e.project)}`:"",n=async()=>({"content-type":"application/json",authorization:`Bearer ${await r()}`}),l=async o=>{let a=await fetch(`${t}${M}/chat${s("?")}`,{method:"POST",headers:await n(),body:JSON.stringify({message:o})});if(!a.ok)throw new Error(`hola: message failed (${a.status})`);return{reply:(await a.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>h(`${t}${M}/chat/info${s("?")}`,await n()),sendMessage:l,streamMessage:async(o,a)=>{try{let i=await l(o);a.onText?.(i.reply),a.onComplete?.(i)}catch(i){a.onError?.(H(i))}},subscribe:(o,a)=>{let i=a?.since??"0",d=!1,u,b=a?.intervalMs??3e3,c=async()=>{try{let m=await h(`${t}${M}/chat/thread?since=${encodeURIComponent(i)}${s("&")}`,await n());for(let g of m.messages)o.onMessage(g);i=m.cursor,o.onHealthy?.()}catch(m){o.onError?.(H(m))}d||(u=setTimeout(c,b))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},requestHuman:async()=>{await fetch(`${t}${M}/chat/request-human${s("?")}`,{method:"POST",headers:await n(),body:"{}"})}}};var $=require("react"),A=(e,t)=>{let[r,s]=(0,$.useState)("");return(0,$.useEffect)(()=>{if(!e){s("");return}let n=Uint8Array.from(atob(e),o=>o.charCodeAt(0)),l=URL.createObjectURL(new Blob([n],{type:t}));return s(l),()=>URL.revokeObjectURL(l)},[e,t]),r};var F=require("react");var W=require("react"),ee=require("react/jsx-runtime"),Z=(0,W.createContext)(null),K=({client:e,children:t})=>(0,ee.jsx)(Z.Provider,{value:e,children:t}),D=()=>{let e=(0,W.useContext)(Z);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};var w=require("react"),U=e=>{let[t,r]=(0,w.useState)([]),[s,n]=(0,w.useState)(""),[l,o]=(0,w.useState)(!1),[a,i]=(0,w.useState)(null),d=(0,w.useRef)(0),u=(0,w.useCallback)(()=>{let c=d.current;return d.current+=1,c},[]);(0,w.useEffect)(()=>{let c=!0;return e.getInfo().then(p=>{c&&p.greeting&&r([{id:u(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{c=!1}},[e,u]);let b=(0,w.useCallback)(()=>{let c=s.trim();if(!c||l)return;n(""),i(null),o(!0);let p=u(),m=u();r(f=>[...f,{id:p,role:"user",text:c},{id:m,role:"assistant",text:"",streaming:!0}]);let g=f=>{r(T=>T.map(_=>_.id===m?f(_):_))};e.streamMessage(c,{onText:f=>g(T=>({...T,text:T.text+f})),onComplete:f=>{g(T=>({...T,text:f.reply||T.text,artifacts:f.artifacts,streaming:!1})),o(!1)},onError:f=>{g(T=>({...T,streaming:!1})),i(f.error),o(!1)}}).catch(f=>{g(T=>({...T,streaming:!1})),i(f instanceof Error?f.message:"stream_failed"),o(!1)})},[s,l,e,u]);return{messages:t,input:s,setInput:n,send:b,sending:l,error:a}};var y=require("react/jsx-runtime"),P={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},Pe=({artifact:e})=>{let t=A(e.dataBase64??"",e.mimeType),r=e.url??t;return r?e.mimeType.startsWith("image/")?(0,y.jsx)("a",{href:r,rel:"noopener noreferrer",target:"_blank",children:(0,y.jsx)("img",{alt:e.filename,src:r,style:P.image})}):(0,y.jsx)("a",{download:e.filename,href:r,style:P.link,children:e.filename}):null},te=()=>{let e=D(),{messages:t,input:r,setInput:s,send:n,sending:l,error:o}=U(e),a=(0,F.useRef)(null);(0,F.useEffect)(()=>{let d=a.current;d&&(d.scrollTop=d.scrollHeight)},[t]);let i=d=>{d.preventDefault(),n()};return(0,y.jsxs)("div",{style:P.root,children:[(0,y.jsx)("div",{ref:a,style:P.log,children:t.map(d=>(0,y.jsxs)("div",{style:d.role==="user"?P.user:P.assistant,children:[d.text||(d.streaming?"\u2026":""),d.artifacts&&d.artifacts.length>0?(0,y.jsx)("div",{style:P.artifacts,children:d.artifacts.map(u=>(0,y.jsx)(Pe,{artifact:u},u.filename))}):null]},d.id))}),o?(0,y.jsx)("div",{style:P.error,children:o}):null,(0,y.jsxs)("form",{onSubmit:i,style:P.form,children:[(0,y.jsx)("input",{"aria-label":"Message",onChange:d=>s(d.target.value),placeholder:"Type a message\u2026",style:P.input,value:r}),(0,y.jsx)("button",{disabled:l||r.trim().length===0,style:P.button,type:"submit",children:"Send"})]})]})};var ke=/\/+$/,O=e=>{let t=e.sessionId,r=e.baseUrl.replace(ke,""),s=o=>`${r}/endpoint/${encodeURIComponent(e.token)}${o}`,n={"content-type":"application/json"},l=(o,a)=>({url:s(o),headers:n,body:JSON.stringify({message:a,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>h(s("/config"),{}),sendMessage:async o=>{let a=await Y(l("/message",o));return t=a.sessionId,a},streamMessage:(o,a,i)=>Q(l("/message/stream",o),a,d=>{t=d},i),subscribe:(o,a)=>{let i=a?.since??"0",d=!1,u,b=a?.intervalMs??3e3,c=async()=>{let m=t;if(m)try{let g=await h(s(`/messages?session=${encodeURIComponent(m)}&since=${encodeURIComponent(i)}`),{});for(let f of g.messages)o.onMessage(f);i=g.cursor,o.onHealthy?.()}catch(g){o.onError?.(H(g))}d||(u=setTimeout(c,b))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:n,body:JSON.stringify({sessionId:t})})}}};var re=e=>"transport"in e?e.transport:O(e);var He=/^https?:\/\//i,Se=/^\d{4}-\d{2}-\d{2}(?:[T ]|$)/,Ce=e=>e.replace(/_/g," ").replace(/\b\w/g,t=>t.toUpperCase());function ve(e){if(typeof e=="number")return"number";if(typeof e=="boolean")return"boolean";if(typeof e=="string"){if(He.test(e))return"url";if(Se.test(e))return"date"}return"text"}function N(e){let t=[],r=new Map;for(let s of e)for(let[n,l]of Object.entries(s.data??{})){if(l==null)continue;let o=r.get(n);o||(o=new Map,r.set(n,o),t.push(n));let a=ve(l);o.set(a,(o.get(a)??0)+1)}return t.map(s=>{let n=r.get(s)??new Map,l="text",o=-1;for(let[a,i]of n)i>o&&(o=i,l=a);return{key:s,label:Ce(s),type:l}})}var Re=e=>typeof e=="number"?e:Number(e)||0,ne=e=>/rate|position|rank|score|percent|avg/i.test(e),Ie=/posted|published|sent|completed|shipped|launched|_at|date/i,Ee=new Set(["service","order","end_user_id"]);function j(e,t){let r=e.filter(i=>!Ee.has(i.key)),s=r.filter(i=>i.type==="number").slice(0,3),n=r.find(i=>i.key==="status")??r.find(i=>i.type==="select"),l=r.find(i=>i.type==="date"&&Ie.test(i.key))??r.find(i=>i.type==="date"),o=r.filter(i=>i.type==="number"&&!ne(i.key)).map(i=>({field:i,total:t.reduce((d,u)=>d+Re(u.data[i.key]),0)})).sort((i,d)=>d.total-i.total).find(i=>i.total>0)?.field??null,a=[{type:"stat",label:"Items",agg:"count"}];for(let i of s){let d=ne(i.key);a.push({type:"stat",metric:i.key,agg:d?"avg":"sum",label:d?`Avg ${i.label.toLowerCase()}`:i.label})}return l&&a.push({type:"line",x:l.key,y:o?.key,agg:"sum",title:o?`${o.label} by day`:"Items by day"}),n&&a.push({type:"donut",groupBy:n.key,title:n.label}),a.push({type:"table",columns:r.map(i=>i.key)}),{version:1,widgets:a}}function G(e){let t=[];for(let r of e){let s=t.length>0?t[t.length-1]:void 0;r.kind==="stat"?s?.kind==="stats"?s.items.push(r):t.push({kind:"stats",items:[r]}):r.kind==="series"||r.kind==="donut"?s?.kind==="charts"?s.items.push(r):t.push({kind:"charts",items:[r]}):t.push({kind:"block",item:r})}return t}var oe=new Set(["service","order","end_user_id"]),se=e=>typeof e=="number"?e:Number(e)||0,ae=e=>e==null?"":String(e);function ie(e,t){if(e==="count")return t.length;if(t.length===0)return 0;if(e==="last")return t[t.length-1]??0;if(e==="min")return Math.min(...t);if(e==="max")return Math.max(...t);let r=t.reduce((s,n)=>s+n,0);return e==="avg"?Math.round(r/t.length):r}function Me(e,t,r,s){let n=new Map;for(let l of e){let o=new Date(ae(l.data[t]));if(Number.isNaN(o.getTime()))continue;let a=o.toISOString().slice(0,10),i=n.get(a)??[];i.push(r?se(l.data[r]):1),n.set(a,i)}return[...n.entries()].sort((l,o)=>l[0]<o[0]?-1:1).map(([l,o])=>({label:new Date(`${l}T00:00:00`).toLocaleDateString(void 0,{month:"short",day:"numeric"}),value:r?ie(s,o):o.length}))}function $e(e,t){let r=new Map;for(let s of e){let n=ae(s.data[t]).trim();n&&r.set(n,(r.get(n)??0)+1)}return[...r.entries()].sort((s,n)=>n[1]-s[1]).map(([s,n])=>({label:s,value:n}))}function Ae(e,t,r,s){switch(e.type){case"stat":{let n=e.metric?t.get(e.metric):void 0;if(e.metric&&!n)return null;if(!n)return{kind:"stat",label:e.label??"Items",value:s.length};let l=s.map(o=>se(o.data[n.key]));return{kind:"stat",label:e.label??n.label,value:ie(e.agg??"sum",l)}}case"line":case"bar":{if(!t.has(e.x)||e.y&&!t.has(e.y))return null;let n=Me(s,e.x,e.y,e.agg??"sum");return n.length<2?null:{kind:"series",draw:e.type,title:e.title??"By day",points:n}}case"donut":{if(!t.has(e.groupBy))return null;let n=$e(s,e.groupBy);return n.length===0?null:{kind:"donut",title:e.title??t.get(e.groupBy)?.label??"Breakdown",points:n}}case"table":{let n=e.columns?e.columns.map(l=>t.get(l)).filter(l=>l!==void 0&&!oe.has(l.key)):r;return n.length===0?null:{kind:"table",title:e.title,columns:n,rows:s}}case"text":return{kind:"text",title:e.title,body:e.body};default:return null}}function B(e,t,r){let s=t.filter(o=>!oe.has(o.key)),n=new Map(t.map(o=>[o.key,o])),l=[];for(let o of e.widgets){let a=Ae(o,n,s,r);a&&l.push({...a,w:o.w})}return l}function z(e,t,r){return e&&e>=1?Math.min(4,Math.round(e)):t==="stat"?1:t==="chart"?r?4:2:4}var We=new Set(["sum","avg","count","min","max","last"]),le=e=>typeof e=="string"&&We.has(e)?e:void 0,k=e=>typeof e=="string"&&e!==""?e:void 0,C=e=>typeof e=="number"&&e>=1?Math.min(4,Math.round(e)):void 0;function De(e){if(typeof e!="object"||e===null)return null;let t=e;switch(t.type){case"stat":return{type:"stat",label:k(t.label),metric:k(t.metric),agg:le(t.agg),w:C(t.w)};case"line":case"bar":{let r=k(t.x);return r?{type:t.type,title:k(t.title),x:r,y:k(t.y),agg:le(t.agg),w:C(t.w)}:null}case"donut":{let r=k(t.groupBy);return r?{type:"donut",title:k(t.title),groupBy:r,w:C(t.w)}:null}case"table":{let r=Array.isArray(t.columns)?t.columns.filter(s=>typeof s=="string"):void 0;return{type:"table",title:k(t.title),columns:r,w:C(t.w)}}case"text":{let r=k(t.body);return r?{type:"text",title:k(t.title),body:r,w:C(t.w)}:null}default:return null}}function J(e){let t=e;if(typeof e=="string"){if(e.trim()==="")return null;try{t=JSON.parse(e)}catch{return null}}if(typeof t!="object"||t===null)return null;let r=t.widgets;if(!Array.isArray(r))return null;let s=r.map(De).filter(l=>l!==null);if(s.length===0)return null;let n=t.version;return{version:typeof n=="number"?n:1,title:k(t.title),widgets:s}}var Ue=/\/+$/,S="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function Fe(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var de=e=>{let t=e.baseUrl.replace(Ue,""),r=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await r()}`}),n=async(o,a)=>{try{let i=await fetch(`${t}${S}${o}`,{method:a.method,headers:await s(),...a.body===void 0?{}:{body:JSON.stringify(a.body)}});if(i.status===409){let d=await i.json().catch(()=>({}));throw new v(d.message??"Someone else saved this first.")}if(!i.ok){let d=await i.text().catch(()=>""),u=`${a.method} ${o} \u2192 ${String(i.status)}`;try{let b=JSON.parse(d);b.message&&(u=b.message)}catch{u=d?`${u}: ${d}`:u}throw H(new Error(u))}return await i.json()}catch(i){throw H(i)}},l=o=>`/tables/${encodeURIComponent(o)}`;return{listTables:async()=>(await h(`${t}${S}/tables`,await s())).tables,listRows:async(o,a)=>await h(`${t}${S}${l(o)}/rows?limit=${String(a??100)}`,await s()),insertRow:async(o,a)=>{let{rowIds:i}=await n(`${l(o)}/rows`,{method:"POST",body:{rows:[a]}}),d=i[0];if(!d)throw H(new Error("The row was not created."));return d},patchRow:async(o,a,i)=>(await n(`${l(o)}/rows/${encodeURIComponent(a)}`,{method:"PATCH",body:i})).updated,deleteRow:async(o,a)=>(await n(`/${encodeURIComponent(o)}/${encodeURIComponent(a)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await h(`${t}${S}/tables/files/rows?limit=200`,await s())).rows.map(a=>({id:a.rowId,filename:String(a.data.filename??""),source:String(a.data.source??"client"),campaign:String(a.data.campaign??""),size:typeof a.data.size=="number"?a.data.size:0,createdAt:a.createdAt})),uploadFile:async(o,a)=>{let i=new Uint8Array(await o.arrayBuffer());return await n("/files",{method:"POST",body:{filename:o.name,mime:o.type||"application/octet-stream",data:Fe(i),...a===void 0?{}:{message:a}}})},fileLink:async o=>await h(`${t}${S}/files/${encodeURIComponent(o)}/url`,await s()),deleteFile:async o=>(await n(`/files/${encodeURIComponent(o)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await h(`${t}${S}/documents`,await s())).documents,readDocument:async o=>(await h(`${t}${S}/documents/${encodeURIComponent(o)}`,await s())).document,saveDocument:async(o,a)=>await n(`/documents/${encodeURIComponent(o)}`,{method:"PATCH",body:a})}};var x=require("react"),ue="greeting";function V(e,t){if(e.some(n=>n.id===t.id))return e;let r=e.filter(n=>n.id!==ue);return[...t.author==="client"?r.filter(n=>!(n.pending&&n.author==="client"&&n.text===t.text)):r,t]}var pe=(e,t)=>{let[r,s]=(0,x.useState)([]),[n,l]=(0,x.useState)(""),[o,a]=(0,x.useState)(!1),[i,d]=(0,x.useState)(null),u=(0,x.useRef)(0),b=t?.pollMs??2e3;(0,x.useEffect)(()=>{let p=!0;return e.getInfo().then(m=>{p&&m.greeting&&s(g=>g.length===0?[{id:ue,cursor:"",author:"agent",text:m.greeting}]:g)}).catch(()=>{}),()=>{p=!1}},[e]),(0,x.useEffect)(()=>e.subscribe?.({onMessage:p=>s(m=>V(m,p)),onError:p=>d(p.error),onHealthy:()=>d(null)},{intervalMs:b}),[e,b]);let c=(0,x.useCallback)(()=>{let p=n.trim();if(!p||o)return;l(""),d(null),a(!0),u.current+=1;let m={id:`pending-${u.current}`,cursor:"",author:"client",text:p,pending:!0};s(g=>[...g,m]),e.sendMessage(p).then(()=>a(!1)).catch(g=>{d(g instanceof Error?g.message:"send_failed"),a(!1)})},[n,o,e]);return{messages:r,input:n,setInput:l,send:c,sending:o,error:i}};0&&(module.exports={Chat,HolaProvider,PortalConflict,createApiTransport,createHolaClient,createPortalData,createWidgetTransport,deriveFields,inferSpec,layoutGroups,mergeMessage,parseDashboardSpec,renderModel,textDeltaOf,thinkingDeltaOf,toolEventOf,useArtifactUrl,useChat,useHolaClient,useThread,widgetSpan});
|
|
8
|
+
`)}},d=async()=>{let u=await l.read();if(!u.done)return a+=r.decode(u.value,{stream:!0}),i(),d()};await d()};var ve=/\/+$/,E="/api/v1/end-user",re=e=>{let t=e.baseUrl.replace(ve,""),o=()=>typeof e.token=="function"?e.token():e.token,s=r=>e.project?`${r}project=${encodeURIComponent(e.project)}`:"",n=async()=>({"content-type":"application/json",authorization:`Bearer ${await o()}`}),l=async r=>{let a=await fetch(`${t}${E}/chat${s("?")}`,{method:"POST",headers:await n(),body:JSON.stringify({message:r})});if(!a.ok)throw new Error(`hola: message failed (${a.status})`);return{reply:(await a.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>h(`${t}${E}/chat/info${s("?")}`,await n()),sendMessage:l,streamMessage:async(r,a)=>{try{let i=await l(r);a.onText?.(i.reply),a.onComplete?.(i)}catch(i){a.onError?.(k(i))}},subscribe:(r,a)=>{let i=a?.since??"0",d=!1,u,b=a?.intervalMs??3e3,c=async()=>{try{let m=await h(`${t}${E}/chat/thread?since=${encodeURIComponent(i)}${s("&")}`,await n());for(let g of m.messages)r.onMessage(g);i=m.cursor,r.onHealthy?.()}catch(m){r.onError?.(k(m))}d||(u=setTimeout(c,b))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},requestHuman:async()=>{await fetch(`${t}${E}/chat/request-human${s("?")}`,{method:"POST",headers:await n(),body:"{}"})}}};var A=require("react"),$=(e,t)=>{let[o,s]=(0,A.useState)("");return(0,A.useEffect)(()=>{if(!e){s("");return}let n=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),l=URL.createObjectURL(new Blob([n],{type:t}));return s(l),()=>URL.revokeObjectURL(l)},[e,t]),o};var _=require("react");var D=require("react"),ae=require("react/jsx-runtime"),oe=(0,D.createContext)(null),se=({client:e,children:t})=>(0,ae.jsx)(oe.Provider,{value:e,children:t}),O=()=>{let e=(0,D.useContext)(oe);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};var w=require("react"),U=e=>{let[t,o]=(0,w.useState)([]),[s,n]=(0,w.useState)(""),[l,r]=(0,w.useState)(!1),[a,i]=(0,w.useState)(null),d=(0,w.useRef)(0),u=(0,w.useCallback)(()=>{let c=d.current;return d.current+=1,c},[]);(0,w.useEffect)(()=>{let c=!0;return e.getInfo().then(p=>{c&&p.greeting&&o([{id:u(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{c=!1}},[e,u]);let b=(0,w.useCallback)(()=>{let c=s.trim();if(!c||l)return;n(""),i(null),r(!0);let p=u(),m=u();o(f=>[...f,{id:p,role:"user",text:c},{id:m,role:"assistant",text:"",streaming:!0}]);let g=f=>{o(T=>T.map(G=>G.id===m?f(G):G))};e.streamMessage(c,{onText:f=>g(T=>({...T,text:T.text+f})),onComplete:f=>{g(T=>({...T,text:f.reply||T.text,artifacts:f.artifacts,streaming:!1})),r(!1)},onError:f=>{g(T=>({...T,streaming:!1})),i(f.error),r(!1)}}).catch(f=>{g(T=>({...T,streaming:!1})),i(f instanceof Error?f.message:"stream_failed"),r(!1)})},[s,l,e,u]);return{messages:t,input:s,setInput:n,send:b,sending:l,error:a}};var y=require("react/jsx-runtime"),P={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},Ie=({artifact:e})=>{let t=$(e.dataBase64??"",e.mimeType),o=e.url??t;return o?e.mimeType.startsWith("image/")?(0,y.jsx)("a",{href:o,rel:"noopener noreferrer",target:"_blank",children:(0,y.jsx)("img",{alt:e.filename,src:o,style:P.image})}):(0,y.jsx)("a",{download:e.filename,href:o,style:P.link,children:e.filename}):null},ie=()=>{let e=O(),{messages:t,input:o,setInput:s,send:n,sending:l,error:r}=U(e),a=(0,_.useRef)(null);(0,_.useEffect)(()=>{let d=a.current;d&&(d.scrollTop=d.scrollHeight)},[t]);let i=d=>{d.preventDefault(),n()};return(0,y.jsxs)("div",{style:P.root,children:[(0,y.jsx)("div",{ref:a,style:P.log,children:t.map(d=>(0,y.jsxs)("div",{style:d.role==="user"?P.user:P.assistant,children:[d.text||(d.streaming?"\u2026":""),d.artifacts&&d.artifacts.length>0?(0,y.jsx)("div",{style:P.artifacts,children:d.artifacts.map(u=>(0,y.jsx)(Ie,{artifact:u},u.filename))}):null]},d.id))}),r?(0,y.jsx)("div",{style:P.error,children:r}):null,(0,y.jsxs)("form",{onSubmit:i,style:P.form,children:[(0,y.jsx)("input",{"aria-label":"Message",onChange:d=>s(d.target.value),placeholder:"Type a message\u2026",style:P.input,value:o}),(0,y.jsx)("button",{disabled:l||o.trim().length===0,style:P.button,type:"submit",children:"Send"})]})]})};var We=/\/+$/,F=e=>{let t=e.sessionId,o=e.baseUrl.replace(We,""),s=r=>`${o}/endpoint/${encodeURIComponent(e.token)}${r}`,n={"content-type":"application/json"},l=(r,a)=>({url:s(r),headers:n,body:JSON.stringify({message:a,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>h(s("/config"),{}),sendMessage:async r=>{let a=await te(l("/message",r));return t=a.sessionId,a},streamMessage:(r,a,i)=>ne(l("/message/stream",r),a,d=>{t=d},i),subscribe:(r,a)=>{let i=a?.since??"0",d=!1,u,b=a?.intervalMs??3e3,c=async()=>{let m=t;if(m)try{let g=await h(s(`/messages?session=${encodeURIComponent(m)}&since=${encodeURIComponent(i)}`),{});for(let f of g.messages)r.onMessage(f);i=g.cursor,r.onHealthy?.()}catch(g){r.onError?.(k(g))}d||(u=setTimeout(c,b))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:n,body:JSON.stringify({sessionId:t})})}}};var le=e=>"transport"in e?e.transport:F(e);var Me=/^https?:\/\//i,Ee=/^\d{4}-\d{2}-\d{2}(?:[T ]|$)/,Ae=e=>e.replace(/_/g," ").replace(/\b\w/g,t=>t.toUpperCase());function $e(e){if(typeof e=="number")return"number";if(typeof e=="boolean")return"boolean";if(typeof e=="string"){if(Me.test(e))return"url";if(Ee.test(e))return"date"}return"text"}function J(e){let t=[],o=new Map;for(let s of e)for(let[n,l]of Object.entries(s.data??{})){if(l==null)continue;let r=o.get(n);r||(r=new Map,o.set(n,r),t.push(n));let a=$e(l);r.set(a,(r.get(a)??0)+1)}return t.map(s=>{let n=o.get(s)??new Map,l="text",r=-1;for(let[a,i]of n)i>r&&(r=i,l=a);return{key:s,label:Ae(s),type:l}})}var De=e=>typeof e=="number"?e:Number(e)||0,de=e=>/rate|position|rank|score|percent|avg/i.test(e),Oe=/posted|published|sent|completed|shipped|launched|_at|date/i,Ue=new Set(["service","order","end_user_id"]);function V(e,t){let o=e.filter(i=>!Ue.has(i.key)),s=o.filter(i=>i.type==="number").slice(0,3),n=o.find(i=>i.key==="status")??o.find(i=>i.type==="select"),l=o.find(i=>i.type==="date"&&Oe.test(i.key))??o.find(i=>i.type==="date"),r=o.filter(i=>i.type==="number"&&!de(i.key)).map(i=>({field:i,total:t.reduce((d,u)=>d+De(u.data[i.key]),0)})).sort((i,d)=>d.total-i.total).find(i=>i.total>0)?.field??null,a=[{type:"stat",label:"Items",agg:"count"}];for(let i of s){let d=de(i.key);a.push({type:"stat",metric:i.key,agg:d?"avg":"sum",label:d?`Avg ${i.label.toLowerCase()}`:i.label})}return l&&a.push({type:"line",x:l.key,y:r?.key,agg:"sum",title:r?`${r.label} by day`:"Items by day"}),n&&a.push({type:"donut",groupBy:n.key,title:n.label}),a.push({type:"table",columns:o.map(i=>i.key)}),{version:1,widgets:a}}var ue={stat:{span:1,h:1},chart:{span:2,h:2},block:{span:0,h:0}},L=6,N=4,_e=e=>Math.max(1,Math.min(L,Math.round(e))),Fe=e=>Math.max(1,Math.min(N,Math.round(e)));function B(e){return e==="stat"?"stat":e==="series"||e==="donut"?"chart":"block"}function q(e){return e==="stat"?"stat":e==="line"||e==="bar"||e==="donut"?"chart":"block"}function X(e){return ue[e]}function j(e,t,o){let s=ue[e];return{span:t===void 0?s.span:_e(t),h:o===void 0?s.h:Fe(o)}}function Y(e){let t=[],o=null;for(let s of e){let n=B(s.kind);if(n==="block"){t.push({kind:"block",widget:s}),o=null;continue}let l=j(n,s.w,s.h),r={widget:s,span:l.span,h:l.h,band:n};o?o.cells.push(r):(o={kind:"grid",cells:[r]},t.push(o))}return t}var pe=new Set(["service","order","end_user_id"]),ce=e=>typeof e=="number"?e:Number(e)||0,ge=e=>e==null?"":String(e);function me(e,t){if(e==="count")return t.length;if(t.length===0)return 0;if(e==="last")return t[t.length-1]??0;if(e==="min")return Math.min(...t);if(e==="max")return Math.max(...t);let o=t.reduce((s,n)=>s+n,0);return e==="avg"?Math.round(o/t.length):o}function Le(e,t,o,s){let n=new Map;for(let l of e){let r=new Date(ge(l.data[t]));if(Number.isNaN(r.getTime()))continue;let a=r.toISOString().slice(0,10),i=n.get(a)??[];i.push(o?ce(l.data[o]):1),n.set(a,i)}return[...n.entries()].sort((l,r)=>l[0]<r[0]?-1:1).map(([l,r])=>({label:new Date(`${l}T00:00:00`).toLocaleDateString(void 0,{month:"short",day:"numeric"}),value:o?me(s,r):r.length}))}function Ne(e,t){let o=new Map;for(let s of e){let n=ge(s.data[t]).trim();n&&o.set(n,(o.get(n)??0)+1)}return[...o.entries()].sort((s,n)=>n[1]-s[1]).map(([s,n])=>({label:s,value:n}))}function Be(e,t,o,s){switch(e.type){case"stat":{let n=e.metric?t.get(e.metric):void 0;if(e.metric&&!n)return null;if(!n)return{kind:"stat",label:e.label??"Items",value:s.length};let l=s.map(r=>ce(r.data[n.key]));return{kind:"stat",label:e.label??n.label,value:me(e.agg??"sum",l)}}case"line":case"bar":{if(!t.has(e.x)||e.y&&!t.has(e.y))return null;let n=Le(s,e.x,e.y,e.agg??"sum");return n.length<2?null:{kind:"series",draw:e.type,title:e.title??"By day",points:n}}case"donut":{if(!t.has(e.groupBy))return null;let n=Ne(s,e.groupBy);return n.length===0?null:{kind:"donut",title:e.title??t.get(e.groupBy)?.label??"Breakdown",points:n}}case"table":{let n=e.columns?e.columns.map(l=>t.get(l)).filter(l=>l!==void 0&&!pe.has(l.key)):o;return n.length===0?null:{kind:"table",title:e.title,columns:n,rows:s}}case"text":return{kind:"text",title:e.title,body:e.body};default:return null}}function K(e,t,o){let s=t.filter(r=>!pe.has(r.key)),n=new Map(t.map(r=>[r.key,r])),l=[];for(let r of e.widgets){let a=Be(r,n,s,o);a&&l.push({...a,w:r.w,h:r.h})}return l}var je=new Set(["sum","avg","count","min","max","last"]),fe=e=>typeof e=="string"&&je.has(e)?e:void 0,S=e=>typeof e=="string"&&e!==""?e:void 0,C=e=>typeof e=="number"&&e>=1?Math.min(6,Math.round(e)):void 0,H=e=>typeof e=="number"&&e>=1?Math.min(4,Math.round(e)):void 0;function Ge(e){if(typeof e!="object"||e===null)return null;let t=e;switch(t.type){case"stat":return{type:"stat",label:S(t.label),metric:S(t.metric),agg:fe(t.agg),w:C(t.w),h:H(t.h)};case"line":case"bar":{let o=S(t.x);return o?{type:t.type,title:S(t.title),x:o,y:S(t.y),agg:fe(t.agg),w:C(t.w),h:H(t.h)}:null}case"donut":{let o=S(t.groupBy);return o?{type:"donut",title:S(t.title),groupBy:o,w:C(t.w),h:H(t.h)}:null}case"table":{let o=Array.isArray(t.columns)?t.columns.filter(s=>typeof s=="string"):void 0;return{type:"table",title:S(t.title),columns:o,w:C(t.w),h:H(t.h)}}case"text":{let o=S(t.body);return o?{type:"text",title:S(t.title),body:o,w:C(t.w),h:H(t.h)}:null}default:return null}}function Q(e){let t=e;if(typeof e=="string"){if(e.trim()==="")return null;try{t=JSON.parse(e)}catch{return null}}if(typeof t!="object"||t===null)return null;let o=t.widgets;if(!Array.isArray(o))return null;let s=o.map(Ge).filter(l=>l!==null);if(s.length===0)return null;let n=t.version;return{version:typeof n=="number"?n:1,title:S(t.title),widgets:s}}var ze=/\/+$/,R="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function Je(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var ye=e=>{let t=e.baseUrl.replace(ze,""),o=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await o()}`}),n=async(r,a)=>{try{let i=await fetch(`${t}${R}${r}`,{method:a.method,headers:await s(),...a.body===void 0?{}:{body:JSON.stringify(a.body)}});if(i.status===409){let d=await i.json().catch(()=>({}));throw new v(d.message??"Someone else saved this first.")}if(!i.ok){let d=await i.text().catch(()=>""),u=`${a.method} ${r} \u2192 ${String(i.status)}`;try{let b=JSON.parse(d);b.message&&(u=b.message)}catch{u=d?`${u}: ${d}`:u}throw k(new Error(u))}return await i.json()}catch(i){throw k(i)}},l=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await h(`${t}${R}/tables`,await s())).tables,listRows:async(r,a)=>await h(`${t}${R}${l(r)}/rows?limit=${String(a??100)}`,await s()),insertRow:async(r,a)=>{let{rowIds:i}=await n(`${l(r)}/rows`,{method:"POST",body:{rows:[a]}}),d=i[0];if(!d)throw k(new Error("The row was not created."));return d},patchRow:async(r,a,i)=>(await n(`${l(r)}/rows/${encodeURIComponent(a)}`,{method:"PATCH",body:i})).updated,deleteRow:async(r,a)=>(await n(`/${encodeURIComponent(r)}/${encodeURIComponent(a)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await h(`${t}${R}/tables/files/rows?limit=200`,await s())).rows.map(a=>({id:a.rowId,filename:String(a.data.filename??""),source:String(a.data.source??"client"),campaign:String(a.data.campaign??""),size:typeof a.data.size=="number"?a.data.size:0,createdAt:a.createdAt})),uploadFile:async(r,a)=>{let i=new Uint8Array(await r.arrayBuffer());return await n("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:Je(i),...a===void 0?{}:{message:a}}})},fileLink:async r=>await h(`${t}${R}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await n(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await h(`${t}${R}/documents`,await s())).documents,readDocument:async r=>(await h(`${t}${R}/documents/${encodeURIComponent(r)}`,await s())).document,saveDocument:async(r,a)=>await n(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:a})}};var x=require("react"),be="greeting";function Z(e,t){if(e.some(n=>n.id===t.id))return e;let o=e.filter(n=>n.id!==be);return[...t.author==="client"?o.filter(n=>!(n.pending&&n.author==="client"&&n.text===t.text)):o,t]}var he=(e,t)=>{let[o,s]=(0,x.useState)([]),[n,l]=(0,x.useState)(""),[r,a]=(0,x.useState)(!1),[i,d]=(0,x.useState)(null),u=(0,x.useRef)(0),b=t?.pollMs??2e3;(0,x.useEffect)(()=>{let p=!0;return e.getInfo().then(m=>{p&&m.greeting&&s(g=>g.length===0?[{id:be,cursor:"",author:"agent",text:m.greeting}]:g)}).catch(()=>{}),()=>{p=!1}},[e]),(0,x.useEffect)(()=>e.subscribe?.({onMessage:p=>s(m=>Z(m,p)),onError:p=>d(p.error),onHealthy:()=>d(null)},{intervalMs:b}),[e,b]);let c=(0,x.useCallback)(()=>{let p=n.trim();if(!p||r)return;l(""),d(null),a(!0),u.current+=1;let m={id:`pending-${u.current}`,cursor:"",author:"client",text:p,pending:!0};s(g=>[...g,m]),e.sendMessage(p).then(()=>a(!1)).catch(g=>{d(g instanceof Error?g.message:"send_failed"),a(!1)})},[n,r,e]);return{messages:o,input:n,setInput:l,send:c,sending:r,error:i}};0&&(module.exports={Chat,GRID_MAX_COLS,GRID_MAX_ROWS,HolaProvider,PortalConflict,bandOfKind,bandOfType,createApiTransport,createHolaClient,createPortalData,createWidgetTransport,deriveFields,footprintOf,footprintWith,inferSpec,layoutSegments,mergeMessage,parseDashboardSpec,renderModel,textDeltaOf,thinkingDeltaOf,toolEventOf,useArtifactUrl,useChat,useHolaClient,useThread});
|
package/dist/index.d.cts
CHANGED
|
@@ -307,6 +307,7 @@ interface StatWidget {
|
|
|
307
307
|
metric?: string | undefined;
|
|
308
308
|
agg?: Agg | undefined;
|
|
309
309
|
w?: number | undefined;
|
|
310
|
+
h?: number | undefined;
|
|
310
311
|
}
|
|
311
312
|
/** A time series: `y` (a numeric column) aggregated per day of `x` (a date column); omit `y` to count
|
|
312
313
|
* rows per day. `line` and `bar` differ only in how the renderer draws the same points. */
|
|
@@ -317,6 +318,7 @@ interface SeriesWidget {
|
|
|
317
318
|
y?: string | undefined;
|
|
318
319
|
agg?: Agg | undefined;
|
|
319
320
|
w?: number | undefined;
|
|
321
|
+
h?: number | undefined;
|
|
320
322
|
}
|
|
321
323
|
/** A breakdown: the count of rows per distinct value of `groupBy`. */
|
|
322
324
|
interface DonutWidget {
|
|
@@ -324,6 +326,7 @@ interface DonutWidget {
|
|
|
324
326
|
title?: string | undefined;
|
|
325
327
|
groupBy: string;
|
|
326
328
|
w?: number | undefined;
|
|
329
|
+
h?: number | undefined;
|
|
327
330
|
}
|
|
328
331
|
/** A row table: `columns` (by key, in order) or every non-plumbing column when omitted. */
|
|
329
332
|
interface TableWidget {
|
|
@@ -331,6 +334,7 @@ interface TableWidget {
|
|
|
331
334
|
title?: string | undefined;
|
|
332
335
|
columns?: string[] | undefined;
|
|
333
336
|
w?: number | undefined;
|
|
337
|
+
h?: number | undefined;
|
|
334
338
|
}
|
|
335
339
|
/** Static prose — a note above or between the data widgets. */
|
|
336
340
|
interface TextWidget {
|
|
@@ -338,6 +342,7 @@ interface TextWidget {
|
|
|
338
342
|
title?: string | undefined;
|
|
339
343
|
body: string;
|
|
340
344
|
w?: number | undefined;
|
|
345
|
+
h?: number | undefined;
|
|
341
346
|
}
|
|
342
347
|
type Widget = StatWidget | SeriesWidget | DonutWidget | TableWidget | TextWidget;
|
|
343
348
|
interface DashboardSpec {
|
|
@@ -363,57 +368,89 @@ type ResolvedWidget = {
|
|
|
363
368
|
label: string;
|
|
364
369
|
value: number;
|
|
365
370
|
w?: number | undefined;
|
|
371
|
+
h?: number | undefined;
|
|
366
372
|
} | {
|
|
367
373
|
kind: "series";
|
|
368
374
|
draw: "line" | "bar";
|
|
369
375
|
title: string;
|
|
370
376
|
points: Point[];
|
|
371
377
|
w?: number | undefined;
|
|
378
|
+
h?: number | undefined;
|
|
372
379
|
} | {
|
|
373
380
|
kind: "donut";
|
|
374
381
|
title: string;
|
|
375
382
|
points: Point[];
|
|
376
383
|
w?: number | undefined;
|
|
384
|
+
h?: number | undefined;
|
|
377
385
|
} | {
|
|
378
386
|
kind: "table";
|
|
379
387
|
title?: string | undefined;
|
|
380
388
|
columns: PortalField[];
|
|
381
389
|
rows: PortalRow[];
|
|
382
390
|
w?: number | undefined;
|
|
391
|
+
h?: number | undefined;
|
|
383
392
|
} | {
|
|
384
393
|
kind: "text";
|
|
385
394
|
title?: string | undefined;
|
|
386
395
|
body: string;
|
|
387
396
|
w?: number | undefined;
|
|
397
|
+
h?: number | undefined;
|
|
388
398
|
};
|
|
389
399
|
/** Resolve a whole spec against the data. The result is render-ready and free of widgets that would
|
|
390
400
|
* draw nothing. */
|
|
391
401
|
declare function renderModel(spec: DashboardSpec, fields: PortalField[], rows: PortalRow[]): ResolvedWidget[];
|
|
392
|
-
/** The column span (1–4, in a 4-col grid) a widget occupies: its explicit `w` clamped, else a sensible
|
|
393
|
-
* default by its layout run — a stat is a quarter (1); a chart is full (4) on its own row or a half
|
|
394
|
-
* (2) when it shares one; a table or note is full (4). Apps map the number to their own grid classes,
|
|
395
|
-
* so the operator console and the customer portal lay a dashboard out identically. */
|
|
396
|
-
declare function widgetSpan(explicit: number | undefined, run: "stat" | "chart" | "block", alone: boolean): number;
|
|
397
402
|
|
|
403
|
+
type Band = "stat" | "chart" | "block";
|
|
398
404
|
type StatView = Extract<ResolvedWidget, {
|
|
399
405
|
kind: "stat";
|
|
400
406
|
}>;
|
|
401
407
|
type ChartView = Extract<ResolvedWidget, {
|
|
402
408
|
kind: "series" | "donut";
|
|
403
409
|
}>;
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
+
declare const GRID_MAX_COLS = 6;
|
|
411
|
+
declare const GRID_MAX_ROWS = 4;
|
|
412
|
+
/** The band a resolved widget belongs to. */
|
|
413
|
+
declare function bandOfKind(kind: ResolvedWidget["kind"]): Band;
|
|
414
|
+
/** The band a spec widget TYPE belongs to — for the editor canvas, which lays out widgets before they
|
|
415
|
+
* resolve (a half-configured widget still needs a cell). Mirrors bandOfKind. */
|
|
416
|
+
declare function bandOfType(type: WidgetType): Band;
|
|
417
|
+
/** A band's fixed bento footprint — `span` cells wide × `h` cells tall. The editable canvas lays out
|
|
418
|
+
* spec widgets before they resolve, so it reads this directly to match the rendered dashboard. */
|
|
419
|
+
declare function footprintOf(band: Band): {
|
|
420
|
+
span: number;
|
|
421
|
+
h: number;
|
|
422
|
+
};
|
|
423
|
+
/** A widget's effective footprint: its hand-dragged `w`×`h` override (clamped to the grid), else the
|
|
424
|
+
* band default. The renderers and the editable canvas both size cells through this, so a resized
|
|
425
|
+
* widget looks the same everywhere. */
|
|
426
|
+
declare function footprintWith(band: Band, w: number | undefined, h: number | undefined): {
|
|
427
|
+
span: number;
|
|
428
|
+
h: number;
|
|
429
|
+
};
|
|
430
|
+
/** A resolved widget placed in the grid — the view-model plus its cell footprint (column span `span`,
|
|
431
|
+
* row span `h`) and band, ready to render. */
|
|
432
|
+
interface LaidOutWidget {
|
|
433
|
+
widget: ResolvedWidget;
|
|
434
|
+
span: number;
|
|
435
|
+
h: number;
|
|
436
|
+
band: Band;
|
|
437
|
+
}
|
|
438
|
+
/** A run of the dashboard: either a fixed-cell BENTO grid (stats + charts, whose cells are squares
|
|
439
|
+
* that align to the grid), or a full-width BLOCK (a table/note, whose height is its content). Splitting
|
|
440
|
+
* them keeps the fixed grid strict — a variable-height table never gets crammed into a fixed cell —
|
|
441
|
+
* while everything that snaps to a cell, does. Order is preserved. */
|
|
442
|
+
type LayoutSegment = {
|
|
443
|
+
kind: "grid";
|
|
444
|
+
cells: LaidOutWidget[];
|
|
410
445
|
} | {
|
|
411
446
|
kind: "block";
|
|
412
|
-
|
|
447
|
+
widget: ResolvedWidget;
|
|
413
448
|
};
|
|
414
|
-
/**
|
|
415
|
-
*
|
|
416
|
-
|
|
449
|
+
/** Arrange resolved widgets into bento segments: consecutive stats/charts coalesce into one grid (a
|
|
450
|
+
* flat run of cells — the renderer flows them into the 4-cell rows); each table/note becomes its own
|
|
451
|
+
* full-width block. This is the arrangement every surface renders, so a dashboard reads identically
|
|
452
|
+
* everywhere. */
|
|
453
|
+
declare function layoutSegments(resolved: ResolvedWidget[]): LayoutSegment[];
|
|
417
454
|
|
|
418
455
|
/** Extract a streamed text delta from a raw pi turn event, or null. */
|
|
419
456
|
declare const textDeltaOf: (event: unknown) => string | null;
|
|
@@ -471,4 +508,4 @@ declare const useThread: (client: HolaClient, options?: {
|
|
|
471
508
|
pollMs?: number;
|
|
472
509
|
}) => UseThread;
|
|
473
510
|
|
|
474
|
-
export { type Agg, type ApiTransportConfig, type ChartView, Chat, type ChatMessage, type CreateHolaClientOptions, type DashboardSpec, type DonutWidget, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, type
|
|
511
|
+
export { type Agg, type ApiTransportConfig, type Band, type ChartView, Chat, type ChatMessage, type CreateHolaClientOptions, type DashboardSpec, type DonutWidget, GRID_MAX_COLS, GRID_MAX_ROWS, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, type LaidOutWidget, type LayoutSegment, type Point, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ResolvedWidget, type SeriesWidget, type StatView, type StatWidget, type TableWidget, type TextWidget, type ThreadMessage, type UseChat, type UseThread, type Widget, type WidgetTransportConfig, type WidgetType, bandOfKind, bandOfType, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, deriveFields, footprintOf, footprintWith, inferSpec, layoutSegments, mergeMessage, parseDashboardSpec, renderModel, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
|
package/dist/index.d.ts
CHANGED
|
@@ -307,6 +307,7 @@ interface StatWidget {
|
|
|
307
307
|
metric?: string | undefined;
|
|
308
308
|
agg?: Agg | undefined;
|
|
309
309
|
w?: number | undefined;
|
|
310
|
+
h?: number | undefined;
|
|
310
311
|
}
|
|
311
312
|
/** A time series: `y` (a numeric column) aggregated per day of `x` (a date column); omit `y` to count
|
|
312
313
|
* rows per day. `line` and `bar` differ only in how the renderer draws the same points. */
|
|
@@ -317,6 +318,7 @@ interface SeriesWidget {
|
|
|
317
318
|
y?: string | undefined;
|
|
318
319
|
agg?: Agg | undefined;
|
|
319
320
|
w?: number | undefined;
|
|
321
|
+
h?: number | undefined;
|
|
320
322
|
}
|
|
321
323
|
/** A breakdown: the count of rows per distinct value of `groupBy`. */
|
|
322
324
|
interface DonutWidget {
|
|
@@ -324,6 +326,7 @@ interface DonutWidget {
|
|
|
324
326
|
title?: string | undefined;
|
|
325
327
|
groupBy: string;
|
|
326
328
|
w?: number | undefined;
|
|
329
|
+
h?: number | undefined;
|
|
327
330
|
}
|
|
328
331
|
/** A row table: `columns` (by key, in order) or every non-plumbing column when omitted. */
|
|
329
332
|
interface TableWidget {
|
|
@@ -331,6 +334,7 @@ interface TableWidget {
|
|
|
331
334
|
title?: string | undefined;
|
|
332
335
|
columns?: string[] | undefined;
|
|
333
336
|
w?: number | undefined;
|
|
337
|
+
h?: number | undefined;
|
|
334
338
|
}
|
|
335
339
|
/** Static prose — a note above or between the data widgets. */
|
|
336
340
|
interface TextWidget {
|
|
@@ -338,6 +342,7 @@ interface TextWidget {
|
|
|
338
342
|
title?: string | undefined;
|
|
339
343
|
body: string;
|
|
340
344
|
w?: number | undefined;
|
|
345
|
+
h?: number | undefined;
|
|
341
346
|
}
|
|
342
347
|
type Widget = StatWidget | SeriesWidget | DonutWidget | TableWidget | TextWidget;
|
|
343
348
|
interface DashboardSpec {
|
|
@@ -363,57 +368,89 @@ type ResolvedWidget = {
|
|
|
363
368
|
label: string;
|
|
364
369
|
value: number;
|
|
365
370
|
w?: number | undefined;
|
|
371
|
+
h?: number | undefined;
|
|
366
372
|
} | {
|
|
367
373
|
kind: "series";
|
|
368
374
|
draw: "line" | "bar";
|
|
369
375
|
title: string;
|
|
370
376
|
points: Point[];
|
|
371
377
|
w?: number | undefined;
|
|
378
|
+
h?: number | undefined;
|
|
372
379
|
} | {
|
|
373
380
|
kind: "donut";
|
|
374
381
|
title: string;
|
|
375
382
|
points: Point[];
|
|
376
383
|
w?: number | undefined;
|
|
384
|
+
h?: number | undefined;
|
|
377
385
|
} | {
|
|
378
386
|
kind: "table";
|
|
379
387
|
title?: string | undefined;
|
|
380
388
|
columns: PortalField[];
|
|
381
389
|
rows: PortalRow[];
|
|
382
390
|
w?: number | undefined;
|
|
391
|
+
h?: number | undefined;
|
|
383
392
|
} | {
|
|
384
393
|
kind: "text";
|
|
385
394
|
title?: string | undefined;
|
|
386
395
|
body: string;
|
|
387
396
|
w?: number | undefined;
|
|
397
|
+
h?: number | undefined;
|
|
388
398
|
};
|
|
389
399
|
/** Resolve a whole spec against the data. The result is render-ready and free of widgets that would
|
|
390
400
|
* draw nothing. */
|
|
391
401
|
declare function renderModel(spec: DashboardSpec, fields: PortalField[], rows: PortalRow[]): ResolvedWidget[];
|
|
392
|
-
/** The column span (1–4, in a 4-col grid) a widget occupies: its explicit `w` clamped, else a sensible
|
|
393
|
-
* default by its layout run — a stat is a quarter (1); a chart is full (4) on its own row or a half
|
|
394
|
-
* (2) when it shares one; a table or note is full (4). Apps map the number to their own grid classes,
|
|
395
|
-
* so the operator console and the customer portal lay a dashboard out identically. */
|
|
396
|
-
declare function widgetSpan(explicit: number | undefined, run: "stat" | "chart" | "block", alone: boolean): number;
|
|
397
402
|
|
|
403
|
+
type Band = "stat" | "chart" | "block";
|
|
398
404
|
type StatView = Extract<ResolvedWidget, {
|
|
399
405
|
kind: "stat";
|
|
400
406
|
}>;
|
|
401
407
|
type ChartView = Extract<ResolvedWidget, {
|
|
402
408
|
kind: "series" | "donut";
|
|
403
409
|
}>;
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
+
declare const GRID_MAX_COLS = 6;
|
|
411
|
+
declare const GRID_MAX_ROWS = 4;
|
|
412
|
+
/** The band a resolved widget belongs to. */
|
|
413
|
+
declare function bandOfKind(kind: ResolvedWidget["kind"]): Band;
|
|
414
|
+
/** The band a spec widget TYPE belongs to — for the editor canvas, which lays out widgets before they
|
|
415
|
+
* resolve (a half-configured widget still needs a cell). Mirrors bandOfKind. */
|
|
416
|
+
declare function bandOfType(type: WidgetType): Band;
|
|
417
|
+
/** A band's fixed bento footprint — `span` cells wide × `h` cells tall. The editable canvas lays out
|
|
418
|
+
* spec widgets before they resolve, so it reads this directly to match the rendered dashboard. */
|
|
419
|
+
declare function footprintOf(band: Band): {
|
|
420
|
+
span: number;
|
|
421
|
+
h: number;
|
|
422
|
+
};
|
|
423
|
+
/** A widget's effective footprint: its hand-dragged `w`×`h` override (clamped to the grid), else the
|
|
424
|
+
* band default. The renderers and the editable canvas both size cells through this, so a resized
|
|
425
|
+
* widget looks the same everywhere. */
|
|
426
|
+
declare function footprintWith(band: Band, w: number | undefined, h: number | undefined): {
|
|
427
|
+
span: number;
|
|
428
|
+
h: number;
|
|
429
|
+
};
|
|
430
|
+
/** A resolved widget placed in the grid — the view-model plus its cell footprint (column span `span`,
|
|
431
|
+
* row span `h`) and band, ready to render. */
|
|
432
|
+
interface LaidOutWidget {
|
|
433
|
+
widget: ResolvedWidget;
|
|
434
|
+
span: number;
|
|
435
|
+
h: number;
|
|
436
|
+
band: Band;
|
|
437
|
+
}
|
|
438
|
+
/** A run of the dashboard: either a fixed-cell BENTO grid (stats + charts, whose cells are squares
|
|
439
|
+
* that align to the grid), or a full-width BLOCK (a table/note, whose height is its content). Splitting
|
|
440
|
+
* them keeps the fixed grid strict — a variable-height table never gets crammed into a fixed cell —
|
|
441
|
+
* while everything that snaps to a cell, does. Order is preserved. */
|
|
442
|
+
type LayoutSegment = {
|
|
443
|
+
kind: "grid";
|
|
444
|
+
cells: LaidOutWidget[];
|
|
410
445
|
} | {
|
|
411
446
|
kind: "block";
|
|
412
|
-
|
|
447
|
+
widget: ResolvedWidget;
|
|
413
448
|
};
|
|
414
|
-
/**
|
|
415
|
-
*
|
|
416
|
-
|
|
449
|
+
/** Arrange resolved widgets into bento segments: consecutive stats/charts coalesce into one grid (a
|
|
450
|
+
* flat run of cells — the renderer flows them into the 4-cell rows); each table/note becomes its own
|
|
451
|
+
* full-width block. This is the arrangement every surface renders, so a dashboard reads identically
|
|
452
|
+
* everywhere. */
|
|
453
|
+
declare function layoutSegments(resolved: ResolvedWidget[]): LayoutSegment[];
|
|
417
454
|
|
|
418
455
|
/** Extract a streamed text delta from a raw pi turn event, or null. */
|
|
419
456
|
declare const textDeltaOf: (event: unknown) => string | null;
|
|
@@ -471,4 +508,4 @@ declare const useThread: (client: HolaClient, options?: {
|
|
|
471
508
|
pollMs?: number;
|
|
472
509
|
}) => UseThread;
|
|
473
510
|
|
|
474
|
-
export { type Agg, type ApiTransportConfig, type ChartView, Chat, type ChatMessage, type CreateHolaClientOptions, type DashboardSpec, type DonutWidget, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, type
|
|
511
|
+
export { type Agg, type ApiTransportConfig, type Band, type ChartView, Chat, type ChatMessage, type CreateHolaClientOptions, type DashboardSpec, type DonutWidget, GRID_MAX_COLS, GRID_MAX_ROWS, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, type LaidOutWidget, type LayoutSegment, type Point, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ResolvedWidget, type SeriesWidget, type StatView, type StatWidget, type TableWidget, type TextWidget, type ThreadMessage, type UseChat, type UseThread, type Widget, type WidgetTransportConfig, type WidgetType, bandOfKind, bandOfType, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, deriveFields, footprintOf, footprintWith, inferSpec, layoutSegments, mergeMessage, parseDashboardSpec, renderModel, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
var
|
|
3
|
-
`))
|
|
4
|
-
`)}:null},
|
|
2
|
+
var j=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},M=e=>{let t=j(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},E=e=>{let t=j(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},A=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var pe=/^ /,T=e=>{if(e&&typeof e=="object"){let t=e,s=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:s,retryable:t.retryable}:{error:s}}return{error:"request_failed"}},ce=e=>{let t="message",s=[];for(let o of e.split(`
|
|
3
|
+
`))o.startsWith(":")||(o.startsWith("event:")?t=o.slice(6).trim():o.startsWith("data:")&&s.push(o.slice(5).replace(pe,"")));return s.length>0?{event:t,data:s.join(`
|
|
4
|
+
`)}:null},ge=(e,t,s)=>{let o;try{o=JSON.parse(e.data)}catch{return}if(e.event==="session"){let r=o.sessionId;r&&(s(r),t.onSession?.(r));return}if(e.event==="turn_event"){t.onEvent?.(o);let r=M(o);r&&t.onText?.(r);let l=E(o);l&&t.onThinking?.(l);let n=A(o);n&&t.onTool?.(n);return}if(e.event==="chat_completed"){let r=o;s(r.sessionId),t.onComplete?.(r);return}e.event==="chat_error"&&t.onError?.(T(o))},b=async(e,t)=>{let s=await fetch(e,{headers:t});if(!s.ok)throw new Error(`hola: request failed (${s.status})`);return await s.json()},G=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},z=async(e,t,s,o)=>{let r=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:o});if(!(r.ok&&r.body)){t.onError?.({error:`stream_failed_${r.status}`});return}let l=r.body.getReader(),n=new TextDecoder,a="",i=()=>{let u=a.indexOf(`
|
|
5
5
|
|
|
6
|
-
`);for(;u!==-1;){let y=a.slice(0,u);a=a.slice(u+2);let c=
|
|
6
|
+
`);for(;u!==-1;){let y=a.slice(0,u);a=a.slice(u+2);let c=ce(y);c&&ge(c,t,s),u=a.indexOf(`
|
|
7
7
|
|
|
8
|
-
`)}},d=async()=>{let u=await l.read();if(!u.done)return a+=r.decode(u.value,{stream:!0}),i(),d()};await d()};var ie=/\/+$/,S="/api/v1/end-user",le=e=>{let t=e.baseUrl.replace(ie,""),n=()=>typeof e.token=="function"?e.token():e.token,s=r=>e.project?`${r}project=${encodeURIComponent(e.project)}`:"",o=async()=>({"content-type":"application/json",authorization:`Bearer ${await n()}`}),l=async r=>{let a=await fetch(`${t}${S}/chat${s("?")}`,{method:"POST",headers:await o(),body:JSON.stringify({message:r})});if(!a.ok)throw new Error(`hola: message failed (${a.status})`);return{reply:(await a.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>b(`${t}${S}/chat/info${s("?")}`,await o()),sendMessage:l,streamMessage:async(r,a)=>{try{let i=await l(r);a.onText?.(i.reply),a.onComplete?.(i)}catch(i){a.onError?.(T(i))}},subscribe:(r,a)=>{let i=a?.since??"0",d=!1,u,y=a?.intervalMs??3e3,c=async()=>{try{let m=await b(`${t}${S}/chat/thread?since=${encodeURIComponent(i)}${s("&")}`,await o());for(let g of m.messages)r.onMessage(g);i=m.cursor,r.onHealthy?.()}catch(m){r.onError?.(T(m))}d||(u=setTimeout(c,y))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},requestHuman:async()=>{await fetch(`${t}${S}/chat/request-human${s("?")}`,{method:"POST",headers:await o(),body:"{}"})}}};import{useEffect as de,useState as ue}from"react";var A=(e,t)=>{let[n,s]=ue("");return de(()=>{if(!e){s("");return}let o=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),l=URL.createObjectURL(new Blob([o],{type:t}));return s(l),()=>URL.revokeObjectURL(l)},[e,t]),n};import{useEffect as be,useRef as he}from"react";import{createContext as pe,useContext as ce}from"react";import{jsx as me}from"react/jsx-runtime";var N=pe(null),ge=({client:e,children:t})=>me(N.Provider,{value:e,children:t}),W=()=>{let e=ce(N);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};import{useCallback as j,useEffect as fe,useRef as ye,useState as C}from"react";var D=e=>{let[t,n]=C([]),[s,o]=C(""),[l,r]=C(!1),[a,i]=C(null),d=ye(0),u=j(()=>{let c=d.current;return d.current+=1,c},[]);fe(()=>{let c=!0;return e.getInfo().then(p=>{c&&p.greeting&&n([{id:u(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{c=!1}},[e,u]);let y=j(()=>{let c=s.trim();if(!c||l)return;o(""),i(null),r(!0);let p=u(),m=u();n(f=>[...f,{id:p,role:"user",text:c},{id:m,role:"assistant",text:"",streaming:!0}]);let g=f=>{n(h=>h.map(I=>I.id===m?f(I):I))};e.streamMessage(c,{onText:f=>g(h=>({...h,text:h.text+f})),onComplete:f=>{g(h=>({...h,text:f.reply||h.text,artifacts:f.artifacts,streaming:!1})),r(!1)},onError:f=>{g(h=>({...h,streaming:!1})),i(f.error),r(!1)}}).catch(f=>{g(h=>({...h,streaming:!1})),i(f instanceof Error?f.message:"stream_failed"),r(!1)})},[s,l,e,u]);return{messages:t,input:s,setInput:o,send:y,sending:l,error:a}};import{jsx as P,jsxs as U}from"react/jsx-runtime";var w={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},we=({artifact:e})=>{let t=A(e.dataBase64??"",e.mimeType),n=e.url??t;return n?e.mimeType.startsWith("image/")?P("a",{href:n,rel:"noopener noreferrer",target:"_blank",children:P("img",{alt:e.filename,src:n,style:w.image})}):P("a",{download:e.filename,href:n,style:w.link,children:e.filename}):null},xe=()=>{let e=W(),{messages:t,input:n,setInput:s,send:o,sending:l,error:r}=D(e),a=he(null);be(()=>{let d=a.current;d&&(d.scrollTop=d.scrollHeight)},[t]);let i=d=>{d.preventDefault(),o()};return U("div",{style:w.root,children:[P("div",{ref:a,style:w.log,children:t.map(d=>U("div",{style:d.role==="user"?w.user:w.assistant,children:[d.text||(d.streaming?"\u2026":""),d.artifacts&&d.artifacts.length>0?P("div",{style:w.artifacts,children:d.artifacts.map(u=>P(we,{artifact:u},u.filename))}):null]},d.id))}),r?P("div",{style:w.error,children:r}):null,U("form",{onSubmit:i,style:w.form,children:[P("input",{"aria-label":"Message",onChange:d=>s(d.target.value),placeholder:"Type a message\u2026",style:w.input,value:n}),P("button",{disabled:l||n.trim().length===0,style:w.button,type:"submit",children:"Send"})]})]})};var Te=/\/+$/,F=e=>{let t=e.sessionId,n=e.baseUrl.replace(Te,""),s=r=>`${n}/endpoint/${encodeURIComponent(e.token)}${r}`,o={"content-type":"application/json"},l=(r,a)=>({url:s(r),headers:o,body:JSON.stringify({message:a,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>b(s("/config"),{}),sendMessage:async r=>{let a=await _(l("/message",r));return t=a.sessionId,a},streamMessage:(r,a,i)=>L(l("/message/stream",r),a,d=>{t=d},i),subscribe:(r,a)=>{let i=a?.since??"0",d=!1,u,y=a?.intervalMs??3e3,c=async()=>{let m=t;if(m)try{let g=await b(s(`/messages?session=${encodeURIComponent(m)}&since=${encodeURIComponent(i)}`),{});for(let f of g.messages)r.onMessage(f);i=g.cursor,r.onHealthy?.()}catch(g){r.onError?.(T(g))}d||(u=setTimeout(c,y))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:o,body:JSON.stringify({sessionId:t})})}}};var Pe=e=>"transport"in e?e.transport:F(e);var ke=/^https?:\/\//i,He=/^\d{4}-\d{2}-\d{2}(?:[T ]|$)/,Se=e=>e.replace(/_/g," ").replace(/\b\w/g,t=>t.toUpperCase());function Ce(e){if(typeof e=="number")return"number";if(typeof e=="boolean")return"boolean";if(typeof e=="string"){if(ke.test(e))return"url";if(He.test(e))return"date"}return"text"}function G(e){let t=[],n=new Map;for(let s of e)for(let[o,l]of Object.entries(s.data??{})){if(l==null)continue;let r=n.get(o);r||(r=new Map,n.set(o,r),t.push(o));let a=Ce(l);r.set(a,(r.get(a)??0)+1)}return t.map(s=>{let o=n.get(s)??new Map,l="text",r=-1;for(let[a,i]of o)i>r&&(r=i,l=a);return{key:s,label:Se(s),type:l}})}var ve=e=>typeof e=="number"?e:Number(e)||0,B=e=>/rate|position|rank|score|percent|avg/i.test(e),Re=/posted|published|sent|completed|shipped|launched|_at|date/i,Ie=new Set(["service","order","end_user_id"]);function z(e,t){let n=e.filter(i=>!Ie.has(i.key)),s=n.filter(i=>i.type==="number").slice(0,3),o=n.find(i=>i.key==="status")??n.find(i=>i.type==="select"),l=n.find(i=>i.type==="date"&&Re.test(i.key))??n.find(i=>i.type==="date"),r=n.filter(i=>i.type==="number"&&!B(i.key)).map(i=>({field:i,total:t.reduce((d,u)=>d+ve(u.data[i.key]),0)})).sort((i,d)=>d.total-i.total).find(i=>i.total>0)?.field??null,a=[{type:"stat",label:"Items",agg:"count"}];for(let i of s){let d=B(i.key);a.push({type:"stat",metric:i.key,agg:d?"avg":"sum",label:d?`Avg ${i.label.toLowerCase()}`:i.label})}return l&&a.push({type:"line",x:l.key,y:r?.key,agg:"sum",title:r?`${r.label} by day`:"Items by day"}),o&&a.push({type:"donut",groupBy:o.key,title:o.label}),a.push({type:"table",columns:n.map(i=>i.key)}),{version:1,widgets:a}}function J(e){let t=[];for(let n of e){let s=t.length>0?t[t.length-1]:void 0;n.kind==="stat"?s?.kind==="stats"?s.items.push(n):t.push({kind:"stats",items:[n]}):n.kind==="series"||n.kind==="donut"?s?.kind==="charts"?s.items.push(n):t.push({kind:"charts",items:[n]}):t.push({kind:"block",item:n})}return t}var V=new Set(["service","order","end_user_id"]),q=e=>typeof e=="number"?e:Number(e)||0,Y=e=>e==null?"":String(e);function Q(e,t){if(e==="count")return t.length;if(t.length===0)return 0;if(e==="last")return t[t.length-1]??0;if(e==="min")return Math.min(...t);if(e==="max")return Math.max(...t);let n=t.reduce((s,o)=>s+o,0);return e==="avg"?Math.round(n/t.length):n}function Ee(e,t,n,s){let o=new Map;for(let l of e){let r=new Date(Y(l.data[t]));if(Number.isNaN(r.getTime()))continue;let a=r.toISOString().slice(0,10),i=o.get(a)??[];i.push(n?q(l.data[n]):1),o.set(a,i)}return[...o.entries()].sort((l,r)=>l[0]<r[0]?-1:1).map(([l,r])=>({label:new Date(`${l}T00:00:00`).toLocaleDateString(void 0,{month:"short",day:"numeric"}),value:n?Q(s,r):r.length}))}function Me(e,t){let n=new Map;for(let s of e){let o=Y(s.data[t]).trim();o&&n.set(o,(n.get(o)??0)+1)}return[...n.entries()].sort((s,o)=>o[1]-s[1]).map(([s,o])=>({label:s,value:o}))}function $e(e,t,n,s){switch(e.type){case"stat":{let o=e.metric?t.get(e.metric):void 0;if(e.metric&&!o)return null;if(!o)return{kind:"stat",label:e.label??"Items",value:s.length};let l=s.map(r=>q(r.data[o.key]));return{kind:"stat",label:e.label??o.label,value:Q(e.agg??"sum",l)}}case"line":case"bar":{if(!t.has(e.x)||e.y&&!t.has(e.y))return null;let o=Ee(s,e.x,e.y,e.agg??"sum");return o.length<2?null:{kind:"series",draw:e.type,title:e.title??"By day",points:o}}case"donut":{if(!t.has(e.groupBy))return null;let o=Me(s,e.groupBy);return o.length===0?null:{kind:"donut",title:e.title??t.get(e.groupBy)?.label??"Breakdown",points:o}}case"table":{let o=e.columns?e.columns.map(l=>t.get(l)).filter(l=>l!==void 0&&!V.has(l.key)):n;return o.length===0?null:{kind:"table",title:e.title,columns:o,rows:s}}case"text":return{kind:"text",title:e.title,body:e.body};default:return null}}function X(e,t,n){let s=t.filter(r=>!V.has(r.key)),o=new Map(t.map(r=>[r.key,r])),l=[];for(let r of e.widgets){let a=$e(r,o,s,n);a&&l.push({...a,w:r.w})}return l}function Z(e,t,n){return e&&e>=1?Math.min(4,Math.round(e)):t==="stat"?1:t==="chart"?n?4:2:4}var Ae=new Set(["sum","avg","count","min","max","last"]),K=e=>typeof e=="string"&&Ae.has(e)?e:void 0,x=e=>typeof e=="string"&&e!==""?e:void 0,H=e=>typeof e=="number"&&e>=1?Math.min(4,Math.round(e)):void 0;function We(e){if(typeof e!="object"||e===null)return null;let t=e;switch(t.type){case"stat":return{type:"stat",label:x(t.label),metric:x(t.metric),agg:K(t.agg),w:H(t.w)};case"line":case"bar":{let n=x(t.x);return n?{type:t.type,title:x(t.title),x:n,y:x(t.y),agg:K(t.agg),w:H(t.w)}:null}case"donut":{let n=x(t.groupBy);return n?{type:"donut",title:x(t.title),groupBy:n,w:H(t.w)}:null}case"table":{let n=Array.isArray(t.columns)?t.columns.filter(s=>typeof s=="string"):void 0;return{type:"table",title:x(t.title),columns:n,w:H(t.w)}}case"text":{let n=x(t.body);return n?{type:"text",title:x(t.title),body:n,w:H(t.w)}:null}default:return null}}function ee(e){let t=e;if(typeof e=="string"){if(e.trim()==="")return null;try{t=JSON.parse(e)}catch{return null}}if(typeof t!="object"||t===null)return null;let n=t.widgets;if(!Array.isArray(n))return null;let s=n.map(We).filter(l=>l!==null);if(s.length===0)return null;let o=t.version;return{version:typeof o=="number"?o:1,title:x(t.title),widgets:s}}var De=/\/+$/,k="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function Ue(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var Fe=e=>{let t=e.baseUrl.replace(De,""),n=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await n()}`}),o=async(r,a)=>{try{let i=await fetch(`${t}${k}${r}`,{method:a.method,headers:await s(),...a.body===void 0?{}:{body:JSON.stringify(a.body)}});if(i.status===409){let d=await i.json().catch(()=>({}));throw new v(d.message??"Someone else saved this first.")}if(!i.ok){let d=await i.text().catch(()=>""),u=`${a.method} ${r} \u2192 ${String(i.status)}`;try{let y=JSON.parse(d);y.message&&(u=y.message)}catch{u=d?`${u}: ${d}`:u}throw T(new Error(u))}return await i.json()}catch(i){throw T(i)}},l=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await b(`${t}${k}/tables`,await s())).tables,listRows:async(r,a)=>await b(`${t}${k}${l(r)}/rows?limit=${String(a??100)}`,await s()),insertRow:async(r,a)=>{let{rowIds:i}=await o(`${l(r)}/rows`,{method:"POST",body:{rows:[a]}}),d=i[0];if(!d)throw T(new Error("The row was not created."));return d},patchRow:async(r,a,i)=>(await o(`${l(r)}/rows/${encodeURIComponent(a)}`,{method:"PATCH",body:i})).updated,deleteRow:async(r,a)=>(await o(`/${encodeURIComponent(r)}/${encodeURIComponent(a)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await b(`${t}${k}/tables/files/rows?limit=200`,await s())).rows.map(a=>({id:a.rowId,filename:String(a.data.filename??""),source:String(a.data.source??"client"),campaign:String(a.data.campaign??""),size:typeof a.data.size=="number"?a.data.size:0,createdAt:a.createdAt})),uploadFile:async(r,a)=>{let i=new Uint8Array(await r.arrayBuffer());return await o("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:Ue(i),...a===void 0?{}:{message:a}}})},fileLink:async r=>await b(`${t}${k}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await o(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await b(`${t}${k}/documents`,await s())).documents,readDocument:async r=>(await b(`${t}${k}/documents/${encodeURIComponent(r)}`,await s())).document,saveDocument:async(r,a)=>await o(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:a})}};import{useCallback as Oe,useEffect as te,useRef as _e,useState as R}from"react";var re="greeting";function ne(e,t){if(e.some(o=>o.id===t.id))return e;let n=e.filter(o=>o.id!==re);return[...t.author==="client"?n.filter(o=>!(o.pending&&o.author==="client"&&o.text===t.text)):n,t]}var Le=(e,t)=>{let[n,s]=R([]),[o,l]=R(""),[r,a]=R(!1),[i,d]=R(null),u=_e(0),y=t?.pollMs??2e3;te(()=>{let p=!0;return e.getInfo().then(m=>{p&&m.greeting&&s(g=>g.length===0?[{id:re,cursor:"",author:"agent",text:m.greeting}]:g)}).catch(()=>{}),()=>{p=!1}},[e]),te(()=>e.subscribe?.({onMessage:p=>s(m=>ne(m,p)),onError:p=>d(p.error),onHealthy:()=>d(null)},{intervalMs:y}),[e,y]);let c=Oe(()=>{let p=o.trim();if(!p||r)return;l(""),d(null),a(!0),u.current+=1;let m={id:`pending-${u.current}`,cursor:"",author:"client",text:p,pending:!0};s(g=>[...g,m]),e.sendMessage(p).then(()=>a(!1)).catch(g=>{d(g instanceof Error?g.message:"send_failed"),a(!1)})},[o,r,e]);return{messages:n,input:o,setInput:l,send:c,sending:r,error:i}};export{xe as Chat,ge as HolaProvider,v as PortalConflict,le as createApiTransport,Pe as createHolaClient,Fe as createPortalData,F as createWidgetTransport,G as deriveFields,z as inferSpec,J as layoutGroups,ne as mergeMessage,ee as parseDashboardSpec,X as renderModel,E as textDeltaOf,M as thinkingDeltaOf,$ as toolEventOf,A as useArtifactUrl,D as useChat,W as useHolaClient,Le as useThread,Z as widgetSpan};
|
|
8
|
+
`)}},d=async()=>{let u=await l.read();if(!u.done)return a+=n.decode(u.value,{stream:!0}),i(),d()};await d()};var me=/\/+$/,C="/api/v1/end-user",fe=e=>{let t=e.baseUrl.replace(me,""),s=()=>typeof e.token=="function"?e.token():e.token,o=n=>e.project?`${n}project=${encodeURIComponent(e.project)}`:"",r=async()=>({"content-type":"application/json",authorization:`Bearer ${await s()}`}),l=async n=>{let a=await fetch(`${t}${C}/chat${o("?")}`,{method:"POST",headers:await r(),body:JSON.stringify({message:n})});if(!a.ok)throw new Error(`hola: message failed (${a.status})`);return{reply:(await a.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>b(`${t}${C}/chat/info${o("?")}`,await r()),sendMessage:l,streamMessage:async(n,a)=>{try{let i=await l(n);a.onText?.(i.reply),a.onComplete?.(i)}catch(i){a.onError?.(T(i))}},subscribe:(n,a)=>{let i=a?.since??"0",d=!1,u,y=a?.intervalMs??3e3,c=async()=>{try{let m=await b(`${t}${C}/chat/thread?since=${encodeURIComponent(i)}${o("&")}`,await r());for(let g of m.messages)n.onMessage(g);i=m.cursor,n.onHealthy?.()}catch(m){n.onError?.(T(m))}d||(u=setTimeout(c,y))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},requestHuman:async()=>{await fetch(`${t}${C}/chat/request-human${o("?")}`,{method:"POST",headers:await r(),body:"{}"})}}};import{useEffect as ye,useState as be}from"react";var $=(e,t)=>{let[s,o]=be("");return ye(()=>{if(!e){o("");return}let r=Uint8Array.from(atob(e),n=>n.charCodeAt(0)),l=URL.createObjectURL(new Blob([r],{type:t}));return o(l),()=>URL.revokeObjectURL(l)},[e,t]),s};import{useEffect as ke,useRef as Re}from"react";import{createContext as he,useContext as we}from"react";import{jsx as Te}from"react/jsx-runtime";var J=he(null),xe=({client:e,children:t})=>Te(J.Provider,{value:e,children:t}),D=()=>{let e=we(J);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};import{useCallback as V,useEffect as Pe,useRef as Se,useState as H}from"react";var O=e=>{let[t,s]=H([]),[o,r]=H(""),[l,n]=H(!1),[a,i]=H(null),d=Se(0),u=V(()=>{let c=d.current;return d.current+=1,c},[]);Pe(()=>{let c=!0;return e.getInfo().then(p=>{c&&p.greeting&&s([{id:u(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{c=!1}},[e,u]);let y=V(()=>{let c=o.trim();if(!c||l)return;r(""),i(null),n(!0);let p=u(),m=u();s(f=>[...f,{id:p,role:"user",text:c},{id:m,role:"assistant",text:"",streaming:!0}]);let g=f=>{s(h=>h.map(W=>W.id===m?f(W):W))};e.streamMessage(c,{onText:f=>g(h=>({...h,text:h.text+f})),onComplete:f=>{g(h=>({...h,text:f.reply||h.text,artifacts:f.artifacts,streaming:!1})),n(!1)},onError:f=>{g(h=>({...h,streaming:!1})),i(f.error),n(!1)}}).catch(f=>{g(h=>({...h,streaming:!1})),i(f instanceof Error?f.message:"stream_failed"),n(!1)})},[o,l,e,u]);return{messages:t,input:o,setInput:r,send:y,sending:l,error:a}};import{jsx as P,jsxs as U}from"react/jsx-runtime";var w={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},Ce=({artifact:e})=>{let t=$(e.dataBase64??"",e.mimeType),s=e.url??t;return s?e.mimeType.startsWith("image/")?P("a",{href:s,rel:"noopener noreferrer",target:"_blank",children:P("img",{alt:e.filename,src:s,style:w.image})}):P("a",{download:e.filename,href:s,style:w.link,children:e.filename}):null},He=()=>{let e=D(),{messages:t,input:s,setInput:o,send:r,sending:l,error:n}=O(e),a=Re(null);ke(()=>{let d=a.current;d&&(d.scrollTop=d.scrollHeight)},[t]);let i=d=>{d.preventDefault(),r()};return U("div",{style:w.root,children:[P("div",{ref:a,style:w.log,children:t.map(d=>U("div",{style:d.role==="user"?w.user:w.assistant,children:[d.text||(d.streaming?"\u2026":""),d.artifacts&&d.artifacts.length>0?P("div",{style:w.artifacts,children:d.artifacts.map(u=>P(Ce,{artifact:u},u.filename))}):null]},d.id))}),n?P("div",{style:w.error,children:n}):null,U("form",{onSubmit:i,style:w.form,children:[P("input",{"aria-label":"Message",onChange:d=>o(d.target.value),placeholder:"Type a message\u2026",style:w.input,value:s}),P("button",{disabled:l||s.trim().length===0,style:w.button,type:"submit",children:"Send"})]})]})};var ve=/\/+$/,_=e=>{let t=e.sessionId,s=e.baseUrl.replace(ve,""),o=n=>`${s}/endpoint/${encodeURIComponent(e.token)}${n}`,r={"content-type":"application/json"},l=(n,a)=>({url:o(n),headers:r,body:JSON.stringify({message:a,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>b(o("/config"),{}),sendMessage:async n=>{let a=await G(l("/message",n));return t=a.sessionId,a},streamMessage:(n,a,i)=>z(l("/message/stream",n),a,d=>{t=d},i),subscribe:(n,a)=>{let i=a?.since??"0",d=!1,u,y=a?.intervalMs??3e3,c=async()=>{let m=t;if(m)try{let g=await b(o(`/messages?session=${encodeURIComponent(m)}&since=${encodeURIComponent(i)}`),{});for(let f of g.messages)n.onMessage(f);i=g.cursor,n.onHealthy?.()}catch(g){n.onError?.(T(g))}d||(u=setTimeout(c,y))},p=()=>{d=!0,u&&clearTimeout(u)};return a?.signal?.addEventListener("abort",p),u=setTimeout(c,0),p},requestHuman:async()=>{t&&await fetch(o("/request-human"),{method:"POST",headers:r,body:JSON.stringify({sessionId:t})})}}};var Ie=e=>"transport"in e?e.transport:_(e);var We=/^https?:\/\//i,Me=/^\d{4}-\d{2}-\d{2}(?:[T ]|$)/,Ee=e=>e.replace(/_/g," ").replace(/\b\w/g,t=>t.toUpperCase());function Ae(e){if(typeof e=="number")return"number";if(typeof e=="boolean")return"boolean";if(typeof e=="string"){if(We.test(e))return"url";if(Me.test(e))return"date"}return"text"}function q(e){let t=[],s=new Map;for(let o of e)for(let[r,l]of Object.entries(o.data??{})){if(l==null)continue;let n=s.get(r);n||(n=new Map,s.set(r,n),t.push(r));let a=Ae(l);n.set(a,(n.get(a)??0)+1)}return t.map(o=>{let r=s.get(o)??new Map,l="text",n=-1;for(let[a,i]of r)i>n&&(n=i,l=a);return{key:o,label:Ee(o),type:l}})}var $e=e=>typeof e=="number"?e:Number(e)||0,X=e=>/rate|position|rank|score|percent|avg/i.test(e),De=/posted|published|sent|completed|shipped|launched|_at|date/i,Oe=new Set(["service","order","end_user_id"]);function Y(e,t){let s=e.filter(i=>!Oe.has(i.key)),o=s.filter(i=>i.type==="number").slice(0,3),r=s.find(i=>i.key==="status")??s.find(i=>i.type==="select"),l=s.find(i=>i.type==="date"&&De.test(i.key))??s.find(i=>i.type==="date"),n=s.filter(i=>i.type==="number"&&!X(i.key)).map(i=>({field:i,total:t.reduce((d,u)=>d+$e(u.data[i.key]),0)})).sort((i,d)=>d.total-i.total).find(i=>i.total>0)?.field??null,a=[{type:"stat",label:"Items",agg:"count"}];for(let i of o){let d=X(i.key);a.push({type:"stat",metric:i.key,agg:d?"avg":"sum",label:d?`Avg ${i.label.toLowerCase()}`:i.label})}return l&&a.push({type:"line",x:l.key,y:n?.key,agg:"sum",title:n?`${n.label} by day`:"Items by day"}),r&&a.push({type:"donut",groupBy:r.key,title:r.label}),a.push({type:"table",columns:s.map(i=>i.key)}),{version:1,widgets:a}}var K={stat:{span:1,h:1},chart:{span:2,h:2},block:{span:0,h:0}},F=6,L=4,Ue=e=>Math.max(1,Math.min(F,Math.round(e))),_e=e=>Math.max(1,Math.min(L,Math.round(e)));function N(e){return e==="stat"?"stat":e==="series"||e==="donut"?"chart":"block"}function Q(e){return e==="stat"?"stat":e==="line"||e==="bar"||e==="donut"?"chart":"block"}function Z(e){return K[e]}function B(e,t,s){let o=K[e];return{span:t===void 0?o.span:Ue(t),h:s===void 0?o.h:_e(s)}}function ee(e){let t=[],s=null;for(let o of e){let r=N(o.kind);if(r==="block"){t.push({kind:"block",widget:o}),s=null;continue}let l=B(r,o.w,o.h),n={widget:o,span:l.span,h:l.h,band:r};s?s.cells.push(n):(s={kind:"grid",cells:[n]},t.push(s))}return t}var te=new Set(["service","order","end_user_id"]),ne=e=>typeof e=="number"?e:Number(e)||0,re=e=>e==null?"":String(e);function oe(e,t){if(e==="count")return t.length;if(t.length===0)return 0;if(e==="last")return t[t.length-1]??0;if(e==="min")return Math.min(...t);if(e==="max")return Math.max(...t);let s=t.reduce((o,r)=>o+r,0);return e==="avg"?Math.round(s/t.length):s}function Fe(e,t,s,o){let r=new Map;for(let l of e){let n=new Date(re(l.data[t]));if(Number.isNaN(n.getTime()))continue;let a=n.toISOString().slice(0,10),i=r.get(a)??[];i.push(s?ne(l.data[s]):1),r.set(a,i)}return[...r.entries()].sort((l,n)=>l[0]<n[0]?-1:1).map(([l,n])=>({label:new Date(`${l}T00:00:00`).toLocaleDateString(void 0,{month:"short",day:"numeric"}),value:s?oe(o,n):n.length}))}function Le(e,t){let s=new Map;for(let o of e){let r=re(o.data[t]).trim();r&&s.set(r,(s.get(r)??0)+1)}return[...s.entries()].sort((o,r)=>r[1]-o[1]).map(([o,r])=>({label:o,value:r}))}function Ne(e,t,s,o){switch(e.type){case"stat":{let r=e.metric?t.get(e.metric):void 0;if(e.metric&&!r)return null;if(!r)return{kind:"stat",label:e.label??"Items",value:o.length};let l=o.map(n=>ne(n.data[r.key]));return{kind:"stat",label:e.label??r.label,value:oe(e.agg??"sum",l)}}case"line":case"bar":{if(!t.has(e.x)||e.y&&!t.has(e.y))return null;let r=Fe(o,e.x,e.y,e.agg??"sum");return r.length<2?null:{kind:"series",draw:e.type,title:e.title??"By day",points:r}}case"donut":{if(!t.has(e.groupBy))return null;let r=Le(o,e.groupBy);return r.length===0?null:{kind:"donut",title:e.title??t.get(e.groupBy)?.label??"Breakdown",points:r}}case"table":{let r=e.columns?e.columns.map(l=>t.get(l)).filter(l=>l!==void 0&&!te.has(l.key)):s;return r.length===0?null:{kind:"table",title:e.title,columns:r,rows:o}}case"text":return{kind:"text",title:e.title,body:e.body};default:return null}}function se(e,t,s){let o=t.filter(n=>!te.has(n.key)),r=new Map(t.map(n=>[n.key,n])),l=[];for(let n of e.widgets){let a=Ne(n,r,o,s);a&&l.push({...a,w:n.w,h:n.h})}return l}var Be=new Set(["sum","avg","count","min","max","last"]),ae=e=>typeof e=="string"&&Be.has(e)?e:void 0,x=e=>typeof e=="string"&&e!==""?e:void 0,k=e=>typeof e=="number"&&e>=1?Math.min(6,Math.round(e)):void 0,R=e=>typeof e=="number"&&e>=1?Math.min(4,Math.round(e)):void 0;function je(e){if(typeof e!="object"||e===null)return null;let t=e;switch(t.type){case"stat":return{type:"stat",label:x(t.label),metric:x(t.metric),agg:ae(t.agg),w:k(t.w),h:R(t.h)};case"line":case"bar":{let s=x(t.x);return s?{type:t.type,title:x(t.title),x:s,y:x(t.y),agg:ae(t.agg),w:k(t.w),h:R(t.h)}:null}case"donut":{let s=x(t.groupBy);return s?{type:"donut",title:x(t.title),groupBy:s,w:k(t.w),h:R(t.h)}:null}case"table":{let s=Array.isArray(t.columns)?t.columns.filter(o=>typeof o=="string"):void 0;return{type:"table",title:x(t.title),columns:s,w:k(t.w),h:R(t.h)}}case"text":{let s=x(t.body);return s?{type:"text",title:x(t.title),body:s,w:k(t.w),h:R(t.h)}:null}default:return null}}function ie(e){let t=e;if(typeof e=="string"){if(e.trim()==="")return null;try{t=JSON.parse(e)}catch{return null}}if(typeof t!="object"||t===null)return null;let s=t.widgets;if(!Array.isArray(s))return null;let o=s.map(je).filter(l=>l!==null);if(o.length===0)return null;let r=t.version;return{version:typeof r=="number"?r:1,title:x(t.title),widgets:o}}var Ge=/\/+$/,S="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function ze(e){let t="";for(let o=0;o<e.length;o+=32768)t+=String.fromCharCode(...e.subarray(o,o+32768));return btoa(t)}var Je=e=>{let t=e.baseUrl.replace(Ge,""),s=()=>typeof e.token=="function"?e.token():e.token,o=async()=>({"content-type":"application/json",authorization:`Bearer ${await s()}`}),r=async(n,a)=>{try{let i=await fetch(`${t}${S}${n}`,{method:a.method,headers:await o(),...a.body===void 0?{}:{body:JSON.stringify(a.body)}});if(i.status===409){let d=await i.json().catch(()=>({}));throw new v(d.message??"Someone else saved this first.")}if(!i.ok){let d=await i.text().catch(()=>""),u=`${a.method} ${n} \u2192 ${String(i.status)}`;try{let y=JSON.parse(d);y.message&&(u=y.message)}catch{u=d?`${u}: ${d}`:u}throw T(new Error(u))}return await i.json()}catch(i){throw T(i)}},l=n=>`/tables/${encodeURIComponent(n)}`;return{listTables:async()=>(await b(`${t}${S}/tables`,await o())).tables,listRows:async(n,a)=>await b(`${t}${S}${l(n)}/rows?limit=${String(a??100)}`,await o()),insertRow:async(n,a)=>{let{rowIds:i}=await r(`${l(n)}/rows`,{method:"POST",body:{rows:[a]}}),d=i[0];if(!d)throw T(new Error("The row was not created."));return d},patchRow:async(n,a,i)=>(await r(`${l(n)}/rows/${encodeURIComponent(a)}`,{method:"PATCH",body:i})).updated,deleteRow:async(n,a)=>(await r(`/${encodeURIComponent(n)}/${encodeURIComponent(a)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await b(`${t}${S}/tables/files/rows?limit=200`,await o())).rows.map(a=>({id:a.rowId,filename:String(a.data.filename??""),source:String(a.data.source??"client"),campaign:String(a.data.campaign??""),size:typeof a.data.size=="number"?a.data.size:0,createdAt:a.createdAt})),uploadFile:async(n,a)=>{let i=new Uint8Array(await n.arrayBuffer());return await r("/files",{method:"POST",body:{filename:n.name,mime:n.type||"application/octet-stream",data:ze(i),...a===void 0?{}:{message:a}}})},fileLink:async n=>await b(`${t}${S}/files/${encodeURIComponent(n)}/url`,await o()),deleteFile:async n=>(await r(`/files/${encodeURIComponent(n)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await b(`${t}${S}/documents`,await o())).documents,readDocument:async n=>(await b(`${t}${S}/documents/${encodeURIComponent(n)}`,await o())).document,saveDocument:async(n,a)=>await r(`/documents/${encodeURIComponent(n)}`,{method:"PATCH",body:a})}};import{useCallback as Ve,useEffect as le,useRef as qe,useState as I}from"react";var de="greeting";function ue(e,t){if(e.some(r=>r.id===t.id))return e;let s=e.filter(r=>r.id!==de);return[...t.author==="client"?s.filter(r=>!(r.pending&&r.author==="client"&&r.text===t.text)):s,t]}var Xe=(e,t)=>{let[s,o]=I([]),[r,l]=I(""),[n,a]=I(!1),[i,d]=I(null),u=qe(0),y=t?.pollMs??2e3;le(()=>{let p=!0;return e.getInfo().then(m=>{p&&m.greeting&&o(g=>g.length===0?[{id:de,cursor:"",author:"agent",text:m.greeting}]:g)}).catch(()=>{}),()=>{p=!1}},[e]),le(()=>e.subscribe?.({onMessage:p=>o(m=>ue(m,p)),onError:p=>d(p.error),onHealthy:()=>d(null)},{intervalMs:y}),[e,y]);let c=Ve(()=>{let p=r.trim();if(!p||n)return;l(""),d(null),a(!0),u.current+=1;let m={id:`pending-${u.current}`,cursor:"",author:"client",text:p,pending:!0};o(g=>[...g,m]),e.sendMessage(p).then(()=>a(!1)).catch(g=>{d(g instanceof Error?g.message:"send_failed"),a(!1)})},[r,n,e]);return{messages:s,input:r,setInput:l,send:c,sending:n,error:i}};export{He as Chat,F as GRID_MAX_COLS,L as GRID_MAX_ROWS,xe as HolaProvider,v as PortalConflict,N as bandOfKind,Q as bandOfType,fe as createApiTransport,Ie as createHolaClient,Je as createPortalData,_ as createWidgetTransport,q as deriveFields,Z as footprintOf,B as footprintWith,Y as inferSpec,ee as layoutSegments,ue as mergeMessage,ie as parseDashboardSpec,se as renderModel,M as textDeltaOf,E as thinkingDeltaOf,A as toolEventOf,$ as useArtifactUrl,O as useChat,D as useHolaClient,Xe as useThread};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holaboss/client",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.8",
|
|
4
4
|
"description": "Injectable client SDK for embedding a Holaboss AI employee (chat + configurable dashboards today; artifacts next) into your own web app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|