@miiflow/assistant-ui 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{WelcomeScreen-XVZqMpVW.d.ts → WelcomeScreen-CVxniqqK.d.ts} +15 -4
- package/dist/{avatar-C13xc3tA.d.ts → avatar-DnLePg72.d.ts} +2 -2
- package/dist/branding-NieTEGQf.d.ts +32 -0
- package/dist/chunk-ERJKVQQT.js +132 -0
- package/dist/chunk-ERJKVQQT.js.map +1 -0
- package/dist/chunk-IUCKTDTZ.js +2 -0
- package/dist/chunk-IUCKTDTZ.js.map +1 -0
- package/dist/chunk-NG4HKXYB.js +2 -0
- package/dist/chunk-NG4HKXYB.js.map +1 -0
- package/dist/chunk-NKLA5PPB.js +2 -0
- package/dist/chunk-NKLA5PPB.js.map +1 -0
- package/dist/chunk-QECTWT2H.js +2 -0
- package/dist/chunk-QECTWT2H.js.map +1 -0
- package/dist/client/index.d.ts +3 -3
- package/dist/client/index.js +7 -5
- package/dist/client/index.js.map +1 -1
- package/dist/composer/index.d.ts +180 -0
- package/dist/composer/index.js +2 -0
- package/dist/composer/index.js.map +1 -0
- package/dist/context/index.d.ts +16 -3
- package/dist/context/index.js +1 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +1 -1
- package/dist/index.d.ts +14 -5
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/{message-BrHFdwb2.d.ts → message-D2ElX5k7.d.ts} +2 -2
- package/dist/primitives/index.d.ts +3 -3
- package/dist/{streaming-DURrv9_E.d.ts → streaming-B_u_jno6.d.ts} +33 -122
- package/dist/styled/index.d.ts +169 -66
- package/dist/styled/index.js +1 -1
- package/dist/styles-no-preflight.css +1 -1
- package/dist/styles.css +1 -1
- package/dist/types-DeHbndy0.d.ts +48 -0
- package/package.json +18 -2
- package/dist/branding-SzYU4ncD.d.ts +0 -18
- package/dist/chunk-EI3GOLHT.js +0 -128
- package/dist/chunk-EI3GOLHT.js.map +0 -1
- package/dist/chunk-NSTK5EUQ.js +0 -2
- package/dist/chunk-NSTK5EUQ.js.map +0 -1
- package/dist/chunk-O2ILSWHS.js +0 -2
- package/dist/chunk-O2ILSWHS.js.map +0 -1
- package/dist/chunk-OCKHJ4WO.js +0 -2
- package/dist/chunk-OCKHJ4WO.js.map +0 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {$getRoot,KEY_ENTER_COMMAND,COMMAND_PRIORITY_HIGH,DecoratorNode,$isTextNode,$createTextNode,$isElementNode}from'lexical';import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {useLexicalComposerContext}from'@lexical/react/LexicalComposerContext';import {LexicalTypeaheadMenuPlugin,MenuOption}from'@lexical/react/LexicalTypeaheadMenuPlugin';import {forwardRef,useMemo,useRef,useEffect,useCallback,useImperativeHandle,useState,useLayoutEffect}from'react';import {createPortal}from'react-dom';import {LexicalComposer}from'@lexical/react/LexicalComposer';import {ContentEditable}from'@lexical/react/LexicalContentEditable';import {LexicalErrorBoundary}from'@lexical/react/LexicalErrorBoundary';import {HistoryPlugin}from'@lexical/react/LexicalHistoryPlugin';import {OnChangePlugin}from'@lexical/react/LexicalOnChangePlugin';import {RichTextPlugin}from'@lexical/react/LexicalRichTextPlugin';import {clsx}from'clsx';import {twMerge}from'tailwind-merge';var Z="color-mix(in srgb, currentColor 16%, transparent)",j="color-mix(in srgb, currentColor 8%, transparent)",ee="color-mix(in srgb, currentColor 6%, transparent)",te="color-mix(in srgb, currentColor 12%, transparent)",W={flexShrink:0,display:"inline-flex",alignItems:"center",fontSize:10,fontWeight:700,letterSpacing:.5,textTransform:"uppercase",padding:"2px 5px",borderRadius:3,background:Z,lineHeight:1};function N({id:t,kind:e,label:n,description:l,icon:r,tag:i,variant:u="chip",selected:c=false,onMouseEnter:f,onMouseDown:C,htmlId:m}){let g=n??t,x=i!==void 0?jsx("span",{style:{display:"inline-flex",flexShrink:0,alignItems:"center"},children:i}):jsx("span",{style:W,children:e});return u==="chip"?jsxs("span",{"data-chat-command-chip":"true","data-command-id":t,"data-command-kind":e,title:`${e}: ${t}`,style:{display:"inline-flex",alignItems:"center",gap:5,verticalAlign:"middle",background:j,borderRadius:999,padding:"2px 9px 2px 4px",margin:"0 1px",whiteSpace:"nowrap",fontWeight:500,lineHeight:1},children:[x,r&&jsx("span",{style:{display:"inline-flex",flexShrink:0},children:r}),jsx("span",{children:g})]}):jsxs("div",{role:"option","aria-selected":c,id:m,onMouseEnter:f,onMouseDown:C,"data-command-id":t,"data-command-kind":e,style:{display:"flex",alignItems:"center",gap:8,padding:"6px 8px",borderRadius:6,cursor:"pointer",background:c?te:"transparent",transition:"background 80ms ease"},onMouseOver:a=>{c||(a.currentTarget.style.background=ee);},onMouseOut:a=>{c||(a.currentTarget.style.background="transparent");},children:[jsx("span",{style:W,children:e}),r&&jsx("span",{style:{display:"inline-flex",flexShrink:0},children:r}),jsxs("div",{style:{flex:1,minWidth:0,lineHeight:1.3},children:[jsx("div",{style:{fontSize:12,fontWeight:500,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:g}),l&&jsx("div",{style:{fontSize:11,opacity:.65,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:l})]})]})}var $="data-chat-command-token",L="/",_=class t extends DecoratorNode{constructor(e,n,l,r=L,i){super(i),this.__id=e,this.__kind=n,this.__label=l,this.__prefix=r;}static getType(){return "command-token"}static clone(e){return new t(e.__id,e.__kind,e.__label,e.__prefix,e.__key)}static importJSON(e){return new t(e.commandId,e.commandKind,e.commandLabel,e.commandPrefix??L)}exportJSON(){return {...super.exportJSON(),commandId:this.__id,commandKind:this.__kind,commandLabel:this.__label,commandPrefix:this.__prefix,type:"command-token",version:2}}getCommandId(){return this.__id}getCommandKind(){return this.__kind}getCommandLabel(){return this.__label}getCommandPrefix(){return this.__prefix}getEncodedText(){return `${this.__prefix}${this.__id}:${this.__kind}`}createDOM(){let e=document.createElement("span");return e.setAttribute($,"true"),e}updateDOM(){return false}exportDOM(){let e=document.createElement("span");return e.setAttribute($,"true"),e.setAttribute("data-command-id",this.__id),e.setAttribute("data-command-kind",this.__kind),e.setAttribute("data-command-prefix",this.__prefix),e.textContent=this.getEncodedText(),{element:e}}static importDOM(){return {span:e=>e.hasAttribute($)?{conversion:oe,priority:1}:null}}decorate(){return jsx("span",{contentEditable:false,style:{userSelect:"none"},children:jsx(N,{id:this.__id,kind:this.__kind,label:this.__label,variant:"chip"})})}isInline(){return true}isKeyboardSelectable(){return true}getTextContent(){return this.getEncodedText()}};function oe(t){let e=t.getAttribute("data-command-id"),n=t.getAttribute("data-command-kind");if(!e||!n)return null;let l=t.getAttribute("data-command-prefix")??L,r=t.textContent?.replace(new RegExp(`^[\\${l}]`),"")??e;return {node:I(e,n,r,l)}}function I(t,e,n,l=L){return new _(t,e,n,l)}function S(t){return t instanceof _}var H=/(^|\s|\(|\[)([/@])([^\n:]+?):([\w-]+)(?=$|[\s.,!?)\]])/g;function re(t){let e=[];H.lastIndex=0;let n;for(;(n=H.exec(t))!==null;){let l=n[1]??"",r=n[2],i=n[3],u=n[4],c=n.index+l.length,f=n[0].slice(l.length);e.push({id:i,kind:u,prefix:r,raw:f,index:c,endIndex:c+f.length});}return e}var ge="/",xe=75,K=class extends MenuOption{constructor(e){super(`${e.kind}:${e.id}`),this.command=e;}};function Ce(t){let e=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`(^|\\s|\\()(${e}((?:[^${e}\\s]){0,${xe}}))$`)}function z({commandProvider:t,menuRenderer:e,onMenuStateChange:n}){let[l]=useLexicalComposerContext(),[r,i]=useState(null),[u,c]=useState([]),f=t?.trigger??ge,C=useMemo(()=>Ce(f),[f]);useEffect(()=>{n?.(r!=null);},[r,n]),useEffect(()=>{if(!t||r==null){c([]);return}let s=false;return Promise.resolve(t.fetch(r)).then(d=>{s||c(d);}),()=>{s=true;}},[t,r]);let m=useMemo(()=>u.map(s=>new K(s)),[u]),g=useCallback(s=>{let d=C.exec(s);if(!d)return null;let o=d[1]??"",p=d[3]??"";return {leadOffset:d.index+o.length,matchingString:p,replaceableString:d[2]??""}},[C]),x=t?.singletonKinds,a=useCallback((s,d,o)=>{l.update(()=>{let p=s.command.kind;if(x?.includes(p)??false){let w=[],R=h=>{if(S(h)&&h.getCommandKind()===p){w.push({node:h,index:0});return}if($isElementNode(h))for(let v of h.getChildren())R(v);};R($getRoot());for(let h of w){let v=h.node.getNextSibling();if(h.node.remove(),v&&$isTextNode(v)){let G=v.getTextContent();if(G.startsWith(" ")){let V=G.slice(1);V.length===0?v.remove():v.setTextContent(V);}}}}let E=I(s.command.id,p,s.command.label,f),k=$createTextNode(" ");d&&d.replace(E).insertAfter(k),k.select(),o();});},[l,f,x]);return t?jsx(LexicalTypeaheadMenuPlugin,{onQueryChange:i,onSelectOption:a,triggerFn:g,options:m,menuRenderFn:(s,{selectedIndex:d,selectOptionAndCleanUp:o,setHighlightedIndex:p})=>e?jsx(Fragment,{children:e({anchorElement:s.current,options:u,selectedIndex:d??0,setSelectedIndex:p,selectOption:b=>{let E=m.find(k=>k.command.id===b.id&&k.command.kind===b.kind);E&&o(E);},closeMenu:()=>i(null)})}):jsx(he,{anchorElement:s.current,options:m,selectedIndex:d??0,setHighlightedIndex:p,selectOptionAndCleanUp:o,visible:u.length>0||r!=null,queryString:r})}):null}function he({anchorElement:t,options:e,selectedIndex:n,setHighlightedIndex:l,selectOptionAndCleanUp:r,visible:i,queryString:u}){let c=useRef(null),[f,C]=useState(false),m=6;if(useEffect(()=>{let a=c.current;if(!a||e.length===0)return;let s=a.querySelector(`#command-typeahead-item-${n}`);if(!s)return;let d=a.getBoundingClientRect(),o=s.getBoundingClientRect();o.top<d.top?a.scrollTop-=d.top-o.top:o.bottom>d.bottom&&(a.scrollTop+=o.bottom-d.bottom);},[n,e.length]),useLayoutEffect(()=>{if(!t||!i||!c.current)return;let a=()=>{let s=c.current;if(!s)return;let d=t.getBoundingClientRect(),o=s.offsetHeight,b=window.innerHeight-d.top,k=d.top-d.height,w=o+m>b,R=o+m<=k;C(w&&R);};return a(),window.addEventListener("resize",a),window.addEventListener("scroll",a,true),()=>{window.removeEventListener("resize",a),window.removeEventListener("scroll",a,true);}},[t,i,e.length]),!t||!i)return null;let g=t.offsetHeight||20,x=f?`translateY(calc(-100% - ${g+m}px))`:"none";return createPortal(jsx("div",{ref:c,role:"listbox",style:{position:"absolute",top:0,left:0,transform:x,zIndex:99999,minWidth:320,maxWidth:520,maxHeight:280,overflowY:"auto",borderRadius:8,border:"1px solid rgba(0,0,0,0.1)",background:"#ffffff",boxShadow:"0 8px 24px rgba(0,0,0,0.15)",padding:4,fontSize:12,color:"#1f2937"},children:e.length===0?jsxs("div",{style:{padding:"6px 10px",fontSize:11,color:"#6b7280"},children:["No matching commands",u?` for "${u}"`:""]}):e.map((a,s)=>{let d=s===n,o=a.command;return jsx(N,{variant:"row",id:o.id,kind:o.kind,label:o.label,description:o.description,icon:o.icon,selected:d,htmlId:`command-typeahead-item-${s}`,onMouseEnter:()=>l(s),onMouseDown:p=>{p.preventDefault(),p.stopPropagation(),l(s),r(a);}},a.key)})}),t)}function O(...t){return twMerge(clsx(t))}var He={paragraph:"chat-composer-paragraph"};function q(t){let e="",n=[];return t.getEditorState().read(()=>{let l=$getRoot();e=l.getTextContent();let r=i=>{if(S(i)&&n.push({id:i.getCommandId(),kind:i.getCommandKind(),label:i.getCommandLabel()}),$isElementNode(i))for(let u of i.getChildren())r(u);};r(l);}),{text:e,tokens:n}}var De=forwardRef(function({placeholder:e="Type a message...",disabled:n=false,className:l,inputClassName:r,children:i,onChange:u,onSubmit:c,commandProvider:f,commandProviders:C},m){let g=useMemo(()=>({namespace:"ChatComposer",theme:He,nodes:[_],editable:!n,onError:x=>{console.error("[chat-ui composer]",x);}}),[]);return jsx(LexicalComposer,{initialConfig:g,children:jsx(Ae,{placeholder:e,disabled:n,className:l,inputClassName:r,onChange:u,onSubmit:c,commandProvider:f,commandProviders:C,imperativeHandle:m,children:i})})});function Ae({placeholder:t,disabled:e,className:n,inputClassName:l,children:r,onChange:i,onSubmit:u,commandProvider:c,commandProviders:f,imperativeHandle:C}){let[m]=useLexicalComposerContext(),g=useRef(u);g.current=u;let x=useRef(false);useEffect(()=>{m.setEditable(!e);},[m,e]);let a=useCallback(()=>{let o=q(m);o.text.trim().length===0&&o.tokens.length===0||g.current(o);},[m]);useImperativeHandle(C,()=>({clear:()=>{m.update(()=>{$getRoot().clear();});},submit:a,focus:()=>m.focus()}),[m,a]),useEffect(()=>m.registerCommand(KEY_ENTER_COMMAND,o=>{if(x.current)return false;let p=o;return p?.shiftKey?false:(p?.preventDefault(),a(),true)},COMMAND_PRIORITY_HIGH),[m,a]);let s=useCallback(o=>{x.current=o;},[]),d=useCallback(o=>{if(!i)return;let p=q(m);i(p);},[m,i]);return jsxs("div",{className:O("relative w-full",n),children:[jsx(RichTextPlugin,{contentEditable:jsx(ContentEditable,{className:O("outline-none w-full text-sm leading-relaxed","min-h-[24px] max-h-[200px] overflow-y-auto","text-gray-900 dark:text-zinc-100",e&&"opacity-50 cursor-not-allowed",l)}),placeholder:jsx("div",{className:O("pointer-events-none absolute top-0 left-0 text-sm text-gray-400 dark:text-zinc-500","select-none"),children:t}),ErrorBoundary:LexicalErrorBoundary}),jsx(OnChangePlugin,{onChange:d}),jsx(HistoryPlugin,{}),(f&&f.length>0?f:c?[c]:[]).map((p,b)=>jsx(z,{commandProvider:p,onMenuStateChange:s},`${p.trigger??"/"}-${b}`)),r]})}export{O as a,N as b,_ as c,I as d,S as e,H as f,re as g,z as h,De as i};//# sourceMappingURL=chunk-IUCKTDTZ.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-IUCKTDTZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/composer/CommandTokenView.tsx","../src/composer/CommandTokenNode.tsx","../src/composer/CommandTokenPlugin.tsx","../src/utils/cn.ts","../src/composer/LexicalChatInput.tsx"],"names":["TAG_BG","CHIP_BG","ROW_HOVER_BG","ROW_SELECTED_BG","tagStyle","CommandTokenView","id","kind","label","description","icon","tag","variant","selected","onMouseEnter","onMouseDown","htmlId","display","tagNode","jsx","jsxs","e","TOKEN_DATA_ATTR","DEFAULT_PREFIX","CommandTokenNode","_CommandTokenNode","DecoratorNode","prefix","key","node","serialized","span","element","domNode","convertCommandTokenElement","$createCommandTokenNode","$isCommandTokenNode","COMMAND_TOKEN_REGEX","findInlineCommandTokens","text","matches","m","leading","start","raw","DEFAULT_TRIGGER","QUERY_LENGTH_LIMIT","CommandTypeaheadOption","MenuOption","command","buildTriggerRegex","trigger","escaped","CommandTokenPlugin","commandProvider","menuRenderer","onMenuStateChange","editor","useLexicalComposerContext","queryString","setQueryString","useState","results","setResults","triggerRegex","useMemo","useEffect","cancelled","cmds","options","cmd","checkForMatch","useCallback","match","matchingString","singletonKinds","onSelectOption","selectedOption","nodeToReplace","closeMenu","stale","walk","n","$isElementNode","c","$getRoot","entry","next","$isTextNode","nextText","trimmed","tokenNode","trailingSpace","$createTextNode","LexicalTypeaheadMenuPlugin","anchorElementRef","selectedIndex","selectOptionAndCleanUp","setHighlightedIndex","Fragment","option","o","DefaultCommandMenu","anchorElement","visible","menuRef","useRef","flipped","setFlipped","VERTICAL_GAP","menu","selectedEl","menuRect","itemRect","useLayoutEffect","reposition","menuEl","anchorRect","menuHeight","spaceBelow","spaceAbove","wouldOverflowBottom","fitsAbove","anchorHeight","transform","createPortal","i","isSelected","cn","inputs","twMerge","clsx","EDITOR_THEME","readPayload","tokens","root","collect","child","LexicalChatInput","forwardRef","placeholder","disabled","className","inputClassName","children","onChange","onSubmit","commandProviders","ref","initialConfig","error","LexicalComposer","ChatInputBody","imperativeHandle","onSubmitRef","commandMenuOpenRef","submit","payload","useImperativeHandle","KEY_ENTER_COMMAND","event","keyboard","COMMAND_PRIORITY_HIGH","handleCommandMenuStateChange","isOpen","handleChange","_state","RichTextPlugin","ContentEditable","LexicalErrorBoundary","OnChangePlugin","HistoryPlugin"],"mappings":"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","file":"chunk-IUCKTDTZ.js","sourcesContent":["import type { CSSProperties, ReactNode } from \"react\";\n\n/**\n * Single source of truth for slash-command token visuals.\n *\n * Used in three places:\n * - Typeahead popover row (variant=\"row\")\n * - Inline chip in the editor (variant=\"chip\", inside CommandTokenNode.decorate)\n * - Inline chip in message history (variant=\"chip\", inside MarkdownContent)\n *\n * Renders a kind tag + label so the user gets the same visual identity at\n * every step: pick → type → send → re-read. Adapts to the surrounding text\n * color via `currentColor` so it survives light bubbles, dark bubbles, and\n * any branded chat surface without bespoke palettes.\n */\nexport interface CommandTokenViewProps {\n /** Slug-style id (wire format `/<id>:<kind>`). */\n id: string;\n /** Token category, e.g. \"skill\", \"mode\". Rendered as the leading tag. */\n kind: string;\n /** Display name; falls back to `id` if not provided. */\n label?: string;\n /** Optional secondary line; only rendered for variant=\"row\". */\n description?: string;\n /** Optional leading icon (slotted before the label, after the tag). */\n icon?: ReactNode;\n /** Optional override for the leading tag (where `kind` renders by default).\n * When set, replaces the uppercase kind pill — e.g. swap \"AD-ACCOUNT\" for\n * the platform logo. */\n tag?: ReactNode;\n /** Compact inline pill (\"chip\") vs. full popover row (\"row\"). */\n variant?: \"chip\" | \"row\";\n /** When true, applies the row's \"selected\" styling. */\n selected?: boolean;\n /** Optional click handler (popover row uses it; chip doesn't by default). */\n onMouseEnter?: () => void;\n onMouseDown?: (e: React.MouseEvent) => void;\n /** Pass-through id for ARIA wiring (used by typeahead). */\n htmlId?: string;\n}\n\nconst TAG_BG = \"color-mix(in srgb, currentColor 16%, transparent)\";\nconst CHIP_BG = \"color-mix(in srgb, currentColor 8%, transparent)\";\nconst ROW_HOVER_BG = \"color-mix(in srgb, currentColor 6%, transparent)\";\nconst ROW_SELECTED_BG = \"color-mix(in srgb, currentColor 12%, transparent)\";\n\nconst tagStyle: CSSProperties = {\n flexShrink: 0,\n display: \"inline-flex\",\n alignItems: \"center\",\n fontSize: 10,\n fontWeight: 700,\n letterSpacing: 0.5,\n textTransform: \"uppercase\",\n padding: \"2px 5px\",\n borderRadius: 3,\n background: TAG_BG,\n lineHeight: 1,\n};\n\nexport function CommandTokenView({\n id,\n kind,\n label,\n description,\n icon,\n tag,\n variant = \"chip\",\n selected = false,\n onMouseEnter,\n onMouseDown,\n htmlId,\n}: CommandTokenViewProps) {\n const display = label ?? id;\n\n // When the caller supplies a tag node (e.g. a platform logo for an\n // ad-account chip), render it raw — no uppercase pill background. The\n // default kind text still uses tagStyle.\n const tagNode =\n tag !== undefined ? (\n <span style={{ display: \"inline-flex\", flexShrink: 0, alignItems: \"center\" }}>{tag}</span>\n ) : (\n <span style={tagStyle}>{kind}</span>\n );\n\n if (variant === \"chip\") {\n return (\n <span\n data-chat-command-chip=\"true\"\n data-command-id={id}\n data-command-kind={kind}\n title={`${kind}: ${id}`}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 5,\n verticalAlign: \"middle\",\n background: CHIP_BG,\n borderRadius: 999,\n padding: \"2px 9px 2px 4px\",\n margin: \"0 1px\",\n whiteSpace: \"nowrap\",\n fontWeight: 500,\n lineHeight: 1,\n }}\n >\n {tagNode}\n {icon && (\n <span style={{ display: \"inline-flex\", flexShrink: 0 }}>{icon}</span>\n )}\n <span>{display}</span>\n </span>\n );\n }\n\n // variant === \"row\"\n return (\n <div\n role=\"option\"\n aria-selected={selected}\n id={htmlId}\n onMouseEnter={onMouseEnter}\n onMouseDown={onMouseDown}\n data-command-id={id}\n data-command-kind={kind}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"6px 8px\",\n borderRadius: 6,\n cursor: \"pointer\",\n background: selected ? ROW_SELECTED_BG : \"transparent\",\n transition: \"background 80ms ease\",\n }}\n onMouseOver={(e) => {\n if (!selected) {\n (e.currentTarget as HTMLDivElement).style.background = ROW_HOVER_BG;\n }\n }}\n onMouseOut={(e) => {\n if (!selected) {\n (e.currentTarget as HTMLDivElement).style.background = \"transparent\";\n }\n }}\n >\n <span style={tagStyle}>{kind}</span>\n {icon && (\n <span style={{ display: \"inline-flex\", flexShrink: 0 }}>{icon}</span>\n )}\n <div style={{ flex: 1, minWidth: 0, lineHeight: 1.3 }}>\n <div\n style={{\n fontSize: 12,\n fontWeight: 500,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {display}\n </div>\n {description && (\n <div\n style={{\n fontSize: 11,\n opacity: 0.65,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {description}\n </div>\n )}\n </div>\n </div>\n );\n}\n","import {\n DecoratorNode,\n type DOMConversionMap,\n type DOMConversionOutput,\n type DOMExportOutput,\n type LexicalNode,\n type NodeKey,\n type SerializedLexicalNode,\n type Spread,\n} from \"lexical\";\nimport type { ReactNode } from \"react\";\n\nimport { CommandTokenView } from \"./CommandTokenView\";\n\nexport type SerializedCommandTokenNode = Spread<\n {\n commandId: string;\n commandKind: string;\n commandLabel: string;\n /** Trigger character that produced the chip (e.g. \"/\" or \"@\"). Optional in\n * v1 payloads — defaulted to \"/\" on import for backwards compatibility. */\n commandPrefix?: string;\n type: \"command-token\";\n version: 1 | 2;\n },\n SerializedLexicalNode\n>;\n\nconst TOKEN_DATA_ATTR = \"data-chat-command-token\";\nconst DEFAULT_PREFIX = \"/\";\n\nexport class CommandTokenNode extends DecoratorNode<ReactNode> {\n __id: string;\n __kind: string;\n __label: string;\n __prefix: string;\n\n constructor(\n id: string,\n kind: string,\n label: string,\n prefix: string = DEFAULT_PREFIX,\n key?: NodeKey,\n ) {\n super(key);\n this.__id = id;\n this.__kind = kind;\n this.__label = label;\n this.__prefix = prefix;\n }\n\n static getType(): string {\n return \"command-token\";\n }\n\n static clone(node: CommandTokenNode): CommandTokenNode {\n return new CommandTokenNode(\n node.__id,\n node.__kind,\n node.__label,\n node.__prefix,\n node.__key,\n );\n }\n\n static importJSON(serialized: SerializedCommandTokenNode): CommandTokenNode {\n return new CommandTokenNode(\n serialized.commandId,\n serialized.commandKind,\n serialized.commandLabel,\n serialized.commandPrefix ?? DEFAULT_PREFIX,\n );\n }\n\n exportJSON(): SerializedCommandTokenNode {\n return {\n ...super.exportJSON(),\n commandId: this.__id,\n commandKind: this.__kind,\n commandLabel: this.__label,\n commandPrefix: this.__prefix,\n type: \"command-token\",\n version: 2,\n };\n }\n\n getCommandId(): string {\n return this.__id;\n }\n\n getCommandKind(): string {\n return this.__kind;\n }\n\n getCommandLabel(): string {\n return this.__label;\n }\n\n getCommandPrefix(): string {\n return this.__prefix;\n }\n\n /** Wire format. Backend parses `<prefix><id>:<kind>` from the message body. */\n getEncodedText(): string {\n return `${this.__prefix}${this.__id}:${this.__kind}`;\n }\n\n createDOM(): HTMLElement {\n const span = document.createElement(\"span\");\n span.setAttribute(TOKEN_DATA_ATTR, \"true\");\n return span;\n }\n\n updateDOM(): false {\n return false;\n }\n\n exportDOM(): DOMExportOutput {\n const element = document.createElement(\"span\");\n element.setAttribute(TOKEN_DATA_ATTR, \"true\");\n element.setAttribute(\"data-command-id\", this.__id);\n element.setAttribute(\"data-command-kind\", this.__kind);\n element.setAttribute(\"data-command-prefix\", this.__prefix);\n element.textContent = this.getEncodedText();\n return { element };\n }\n\n static importDOM(): DOMConversionMap | null {\n return {\n span: (domNode: HTMLElement) => {\n if (!domNode.hasAttribute(TOKEN_DATA_ATTR)) return null;\n return { conversion: convertCommandTokenElement, priority: 1 };\n },\n };\n }\n\n decorate(): ReactNode {\n return (\n <span contentEditable={false} style={{ userSelect: \"none\" }}>\n <CommandTokenView\n id={this.__id}\n kind={this.__kind}\n label={this.__label}\n variant=\"chip\"\n />\n </span>\n );\n }\n\n isInline(): boolean {\n return true;\n }\n\n isKeyboardSelectable(): boolean {\n return true;\n }\n\n /** Plain-text projection (clipboard, root.getTextContent, etc.). */\n getTextContent(): string {\n return this.getEncodedText();\n }\n}\n\nfunction convertCommandTokenElement(domNode: HTMLElement): DOMConversionOutput | null {\n const id = domNode.getAttribute(\"data-command-id\");\n const kind = domNode.getAttribute(\"data-command-kind\");\n if (!id || !kind) return null;\n const prefix = domNode.getAttribute(\"data-command-prefix\") ?? DEFAULT_PREFIX;\n const label =\n domNode.textContent?.replace(new RegExp(`^[\\\\${prefix}]`), \"\") ?? id;\n return { node: $createCommandTokenNode(id, kind, label, prefix) };\n}\n\nexport function $createCommandTokenNode(\n id: string,\n kind: string,\n label: string,\n prefix: string = DEFAULT_PREFIX,\n): CommandTokenNode {\n return new CommandTokenNode(id, kind, label, prefix);\n}\n\nexport function $isCommandTokenNode(\n node: LexicalNode | null | undefined,\n): node is CommandTokenNode {\n return node instanceof CommandTokenNode;\n}\n\n/**\n * Matches `<prefix><id>:<kind>` substrings emitted by `CommandTokenNode.getEncodedText()`,\n * where `<prefix>` is `/` (skill / mode chips) or `@` (ad-account chips).\n *\n * Bounded by start-of-string, whitespace, or an opening bracket on the left,\n * and end-of-string, whitespace, or punctuation on the right — so it doesn't\n * fire on URLs like `https://example.com/path:foo` or emails like\n * `user@host.com:port`.\n *\n * The id capture allows spaces (skill names may have them); the kind capture\n * allows word chars and hyphens (e.g. `ad-account`).\n */\nexport const COMMAND_TOKEN_REGEX =\n /(^|\\s|\\(|\\[)([/@])([^\\n:]+?):([\\w-]+)(?=$|[\\s.,!?)\\]])/g;\n\nexport interface InlineCommandTokenMatch {\n id: string;\n kind: string;\n /** Trigger character that introduced the chip (`/` or `@`). */\n prefix: string;\n /** The full matched substring including the leading prefix (no leading boundary). */\n raw: string;\n /** Index in the original string where the leading prefix sits. */\n index: number;\n /** End index (exclusive) of the matched token. */\n endIndex: number;\n}\n\n/**\n * Find all `<prefix><id>:<kind>` tokens in a plain-text string.\n *\n * Returns matches in document order. Use this when rendering message\n * history to replace token substrings with a visual chip.\n */\nexport function findInlineCommandTokens(text: string): InlineCommandTokenMatch[] {\n const matches: InlineCommandTokenMatch[] = [];\n COMMAND_TOKEN_REGEX.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = COMMAND_TOKEN_REGEX.exec(text)) !== null) {\n const leading = m[1] ?? \"\";\n const prefix = m[2];\n const id = m[3];\n const kind = m[4];\n const start = m.index + leading.length;\n const raw = m[0].slice(leading.length);\n matches.push({\n id,\n kind,\n prefix,\n raw,\n index: start,\n endIndex: start + raw.length,\n });\n }\n return matches;\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport {\n LexicalTypeaheadMenuPlugin,\n MenuOption,\n type MenuTextMatch,\n} from \"@lexical/react/LexicalTypeaheadMenuPlugin\";\nimport {\n $createTextNode,\n $getRoot,\n $isElementNode,\n $isTextNode,\n type ElementNode,\n type LexicalNode,\n type TextNode,\n} from \"lexical\";\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { $createCommandTokenNode, $isCommandTokenNode } from \"./CommandTokenNode\";\nimport { CommandTokenView } from \"./CommandTokenView\";\nimport type { ChatComposerCommand, CommandProvider } from \"./types\";\n\nconst DEFAULT_TRIGGER = \"/\";\nconst QUERY_LENGTH_LIMIT = 75;\n\nclass CommandTypeaheadOption extends MenuOption {\n command: ChatComposerCommand;\n\n constructor(command: ChatComposerCommand) {\n super(`${command.kind}:${command.id}`);\n this.command = command;\n }\n}\n\nfunction buildTriggerRegex(trigger: string): RegExp {\n // Escape the trigger char for use in a character class.\n const escaped = trigger.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n // Match: leading boundary (start, whitespace, or open-paren), the trigger,\n // then up to QUERY_LENGTH_LIMIT chars that are not whitespace.\n return new RegExp(\n `(^|\\\\s|\\\\()(${escaped}((?:[^${escaped}\\\\s]){0,${QUERY_LENGTH_LIMIT}}))$`,\n );\n}\n\nexport interface CommandTokenPluginProps {\n /** The data source for the typeahead. When null, the plugin is inert. */\n commandProvider?: CommandProvider | null;\n /**\n * Optional override for the typeahead UI. When supplied, replaces the\n * default Tailwind popover (use this to render an MUI/host-themed menu).\n */\n menuRenderer?: (params: {\n anchorElement: HTMLElement | null;\n options: ChatComposerCommand[];\n selectedIndex: number;\n setSelectedIndex: (index: number) => void;\n selectOption: (option: ChatComposerCommand) => void;\n closeMenu: () => void;\n }) => React.ReactNode;\n /**\n * Notified whenever the typeahead menu opens or closes. Use this to gate\n * keyboard handlers (e.g. don't submit on Enter while the menu is open).\n */\n onMenuStateChange?: (isOpen: boolean) => void;\n}\n\nexport function CommandTokenPlugin({\n commandProvider,\n menuRenderer,\n onMenuStateChange,\n}: CommandTokenPluginProps): React.JSX.Element | null {\n const [editor] = useLexicalComposerContext();\n const [queryString, setQueryString] = useState<string | null>(null);\n const [results, setResults] = useState<ChatComposerCommand[]>([]);\n\n const trigger = commandProvider?.trigger ?? DEFAULT_TRIGGER;\n const triggerRegex = useMemo(() => buildTriggerRegex(trigger), [trigger]);\n\n useEffect(() => {\n onMenuStateChange?.(queryString != null);\n }, [queryString, onMenuStateChange]);\n\n useEffect(() => {\n if (!commandProvider || queryString == null) {\n setResults([]);\n return;\n }\n let cancelled = false;\n Promise.resolve(commandProvider.fetch(queryString)).then((cmds) => {\n if (!cancelled) setResults(cmds);\n });\n return () => {\n cancelled = true;\n };\n }, [commandProvider, queryString]);\n\n const options = useMemo(\n () => results.map((cmd) => new CommandTypeaheadOption(cmd)),\n [results],\n );\n\n const checkForMatch = useCallback(\n (text: string): MenuTextMatch | null => {\n const match = triggerRegex.exec(text);\n if (!match) return null;\n const leading = match[1] ?? \"\";\n const matchingString = match[3] ?? \"\";\n return {\n leadOffset: match.index + leading.length,\n matchingString,\n replaceableString: match[2] ?? \"\",\n };\n },\n [triggerRegex],\n );\n\n const singletonKinds = commandProvider?.singletonKinds;\n\n const onSelectOption = useCallback(\n (\n selectedOption: CommandTypeaheadOption,\n nodeToReplace: TextNode | null,\n closeMenu: () => void,\n ) => {\n editor.update(() => {\n const kind = selectedOption.command.kind;\n const isSingleton = singletonKinds?.includes(kind) ?? false;\n\n if (isSingleton) {\n // Remove any existing chip of the same kind. Walking once with a\n // collected list (rather than mutating during traversal) keeps the\n // tree stable while we iterate.\n const stale: { node: ReturnType<typeof $createCommandTokenNode>; index: number }[] = [];\n const walk = (n: LexicalNode) => {\n if ($isCommandTokenNode(n) && n.getCommandKind() === kind) {\n stale.push({ node: n, index: 0 });\n return;\n }\n if ($isElementNode(n)) {\n for (const c of (n as ElementNode).getChildren()) walk(c);\n }\n };\n walk($getRoot());\n for (const entry of stale) {\n // If the chip is followed by a single space, drop that too so\n // we don't leave an orphan space where the chip used to sit.\n const next = entry.node.getNextSibling();\n entry.node.remove();\n if (next && $isTextNode(next)) {\n const nextText = next.getTextContent();\n if (nextText.startsWith(\" \")) {\n const trimmed = nextText.slice(1);\n if (trimmed.length === 0) next.remove();\n else next.setTextContent(trimmed);\n }\n }\n }\n }\n\n const tokenNode = $createCommandTokenNode(\n selectedOption.command.id,\n kind,\n selectedOption.command.label,\n trigger,\n );\n const trailingSpace = $createTextNode(\" \");\n if (nodeToReplace) {\n nodeToReplace.replace(tokenNode).insertAfter(trailingSpace);\n }\n trailingSpace.select();\n closeMenu();\n });\n },\n [editor, trigger, singletonKinds],\n );\n\n if (!commandProvider) return null;\n\n return (\n <LexicalTypeaheadMenuPlugin<CommandTypeaheadOption>\n onQueryChange={setQueryString}\n onSelectOption={onSelectOption}\n triggerFn={checkForMatch}\n options={options}\n menuRenderFn={(\n anchorElementRef,\n { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex },\n ) => {\n if (menuRenderer) {\n return (\n <>\n {menuRenderer({\n anchorElement: anchorElementRef.current,\n options: results,\n selectedIndex: selectedIndex ?? 0,\n setSelectedIndex: setHighlightedIndex,\n selectOption: (option) => {\n const match = options.find(\n (o) => o.command.id === option.id && o.command.kind === option.kind,\n );\n if (match) selectOptionAndCleanUp(match);\n },\n closeMenu: () => setQueryString(null),\n })}\n </>\n );\n }\n return (\n <DefaultCommandMenu\n anchorElement={anchorElementRef.current}\n options={options}\n selectedIndex={selectedIndex ?? 0}\n setHighlightedIndex={setHighlightedIndex}\n selectOptionAndCleanUp={selectOptionAndCleanUp}\n visible={results.length > 0 || queryString != null}\n queryString={queryString}\n />\n );\n }}\n />\n );\n}\n\nfunction DefaultCommandMenu({\n anchorElement,\n options,\n selectedIndex,\n setHighlightedIndex,\n selectOptionAndCleanUp,\n visible,\n queryString,\n}: {\n anchorElement: HTMLElement | null;\n options: CommandTypeaheadOption[];\n selectedIndex: number;\n setHighlightedIndex: (index: number) => void;\n selectOptionAndCleanUp: (option: CommandTypeaheadOption) => void;\n visible: boolean;\n queryString: string | null;\n}) {\n const menuRef = useRef<HTMLDivElement>(null);\n const [flipped, setFlipped] = useState(false);\n const VERTICAL_GAP = 6;\n\n useEffect(() => {\n const menu = menuRef.current;\n if (!menu || options.length === 0) return;\n const selectedEl = menu.querySelector<HTMLElement>(\n `#command-typeahead-item-${selectedIndex}`,\n );\n if (!selectedEl) return;\n const menuRect = menu.getBoundingClientRect();\n const itemRect = selectedEl.getBoundingClientRect();\n if (itemRect.top < menuRect.top) {\n menu.scrollTop -= menuRect.top - itemRect.top;\n } else if (itemRect.bottom > menuRect.bottom) {\n menu.scrollTop += itemRect.bottom - menuRect.bottom;\n }\n }, [selectedIndex, options.length]);\n\n useLayoutEffect(() => {\n if (!anchorElement || !visible || !menuRef.current) return;\n const reposition = () => {\n const menuEl = menuRef.current;\n if (!menuEl) return;\n const anchorRect = anchorElement.getBoundingClientRect();\n const menuHeight = menuEl.offsetHeight;\n const viewportHeight = window.innerHeight;\n // Lexical positions the anchor right below the cursor; anchorRect.top is\n // where the menu would render by default. anchorRect.height is the\n // cursor-line height (~20px), so the cursor's top is anchorRect.top - height.\n const spaceBelow = viewportHeight - anchorRect.top;\n const cursorTop = anchorRect.top - anchorRect.height;\n const spaceAbove = cursorTop;\n const wouldOverflowBottom = menuHeight + VERTICAL_GAP > spaceBelow;\n const fitsAbove = menuHeight + VERTICAL_GAP <= spaceAbove;\n setFlipped(wouldOverflowBottom && fitsAbove);\n };\n reposition();\n window.addEventListener(\"resize\", reposition);\n window.addEventListener(\"scroll\", reposition, true);\n return () => {\n window.removeEventListener(\"resize\", reposition);\n window.removeEventListener(\"scroll\", reposition, true);\n };\n }, [anchorElement, visible, options.length]);\n\n if (!anchorElement || !visible) return null;\n\n const anchorHeight = anchorElement.offsetHeight || 20;\n const transform = flipped\n ? `translateY(calc(-100% - ${anchorHeight + VERTICAL_GAP}px))`\n : \"none\";\n\n return createPortal(\n <div\n ref={menuRef}\n role=\"listbox\"\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n transform,\n zIndex: 99999,\n minWidth: 320,\n maxWidth: 520,\n maxHeight: 280,\n overflowY: \"auto\",\n borderRadius: 8,\n border: \"1px solid rgba(0,0,0,0.1)\",\n background: \"#ffffff\",\n boxShadow: \"0 8px 24px rgba(0,0,0,0.15)\",\n padding: 4,\n fontSize: 12,\n color: \"#1f2937\",\n }}\n >\n {options.length === 0 ? (\n <div style={{ padding: \"6px 10px\", fontSize: 11, color: \"#6b7280\" }}>\n No matching commands{queryString ? ` for \"${queryString}\"` : \"\"}\n </div>\n ) : (\n options.map((option, i) => {\n const isSelected = i === selectedIndex;\n const cmd = option.command;\n return (\n <CommandTokenView\n key={option.key}\n variant=\"row\"\n id={cmd.id}\n kind={cmd.kind}\n label={cmd.label}\n description={cmd.description}\n icon={cmd.icon}\n selected={isSelected}\n htmlId={`command-typeahead-item-${i}`}\n onMouseEnter={() => setHighlightedIndex(i)}\n onMouseDown={(e) => {\n e.preventDefault();\n e.stopPropagation();\n setHighlightedIndex(i);\n selectOptionAndCleanUp(option);\n }}\n />\n );\n })\n )}\n </div>,\n anchorElement,\n );\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge Tailwind CSS classes with proper precedence.\n * Combines clsx for conditional classes with tailwind-merge for conflict resolution.\n *\n * @example\n * cn(\"px-4 py-2\", \"px-6\") // => \"py-2 px-6\"\n * cn(\"text-red-500\", isActive && \"text-blue-500\") // => \"text-blue-500\" when isActive\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { LexicalComposer } from \"@lexical/react/LexicalComposer\";\nimport { LexicalComposerContext, useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { ContentEditable } from \"@lexical/react/LexicalContentEditable\";\nimport { LexicalErrorBoundary } from \"@lexical/react/LexicalErrorBoundary\";\nimport { HistoryPlugin } from \"@lexical/react/LexicalHistoryPlugin\";\nimport { OnChangePlugin } from \"@lexical/react/LexicalOnChangePlugin\";\nimport { RichTextPlugin } from \"@lexical/react/LexicalRichTextPlugin\";\nimport {\n $getRoot,\n $isElementNode,\n COMMAND_PRIORITY_HIGH,\n KEY_ENTER_COMMAND,\n type EditorState,\n type LexicalEditor,\n type LexicalNode,\n} from \"lexical\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n type ReactNode,\n} from \"react\";\n\nimport { cn } from \"../utils/cn\";\nimport { CommandTokenNode, $isCommandTokenNode } from \"./CommandTokenNode\";\nimport { CommandTokenPlugin } from \"./CommandTokenPlugin\";\nimport type {\n ChatComposerSubmitPayload,\n ChatComposerToken,\n CommandProvider,\n} from \"./types\";\n\nconst EDITOR_THEME = {\n paragraph: \"chat-composer-paragraph\",\n};\n\nexport interface LexicalChatInputHandle {\n /** Clear the editor contents and reset history. */\n clear: () => void;\n /** Programmatically submit the current editor contents. */\n submit: () => void;\n /** Focus the editor. */\n focus: () => void;\n}\n\nexport interface LexicalChatInputProps {\n placeholder?: string;\n disabled?: boolean;\n className?: string;\n /** Tailwind classes applied to the inner ContentEditable element. */\n inputClassName?: string;\n /** Slot for additional Lexical plugins (mounted inside LexicalComposer). */\n children?: ReactNode;\n /**\n * Called whenever the editor's plain-text projection changes. Use this to\n * gate a parent-rendered submit button on `text.trim().length > 0`.\n */\n onChange?: (payload: ChatComposerSubmitPayload) => void;\n /** Called when the user presses Enter (without Shift). */\n onSubmit: (payload: ChatComposerSubmitPayload) => void | Promise<void>;\n /**\n * Optional slash-command typeahead. When omitted, the editor behaves like\n * a plain rich-text input.\n */\n commandProvider?: CommandProvider | null;\n /**\n * Multiple typeahead providers, one per trigger character (e.g. `/` for\n * skills + modes, `@` for ad accounts). When supplied, this takes\n * precedence over the singular `commandProvider`. Each provider mounts its\n * own `CommandTokenPlugin` instance.\n */\n commandProviders?: CommandProvider[];\n}\n\nfunction readPayload(editor: LexicalEditor): ChatComposerSubmitPayload {\n let text = \"\";\n const tokens: ChatComposerToken[] = [];\n editor.getEditorState().read(() => {\n const root = $getRoot();\n text = root.getTextContent();\n const collect = (node: LexicalNode) => {\n if ($isCommandTokenNode(node)) {\n tokens.push({\n id: node.getCommandId(),\n kind: node.getCommandKind(),\n label: node.getCommandLabel(),\n });\n }\n if ($isElementNode(node)) {\n for (const child of node.getChildren()) collect(child);\n }\n };\n collect(root);\n });\n return { text, tokens };\n}\n\nexport const LexicalChatInput = forwardRef<LexicalChatInputHandle, LexicalChatInputProps>(\n function LexicalChatInput(\n {\n placeholder = \"Type a message...\",\n disabled = false,\n className,\n inputClassName,\n children,\n onChange,\n onSubmit,\n commandProvider,\n commandProviders,\n },\n ref,\n ) {\n const initialConfig = useMemo(\n () => ({\n namespace: \"ChatComposer\",\n theme: EDITOR_THEME,\n nodes: [CommandTokenNode],\n editable: !disabled,\n onError: (error: Error) => {\n // eslint-disable-next-line no-console\n console.error(\"[chat-ui composer]\", error);\n },\n }),\n // The initial config can't change after mount; `disabled` is applied via editor.setEditable below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n );\n\n return (\n <LexicalComposer initialConfig={initialConfig}>\n <ChatInputBody\n placeholder={placeholder}\n disabled={disabled}\n className={className}\n inputClassName={inputClassName}\n onChange={onChange}\n onSubmit={onSubmit}\n commandProvider={commandProvider}\n commandProviders={commandProviders}\n imperativeHandle={ref}\n >\n {children}\n </ChatInputBody>\n </LexicalComposer>\n );\n },\n);\n\nfunction ChatInputBody({\n placeholder,\n disabled,\n className,\n inputClassName,\n children,\n onChange,\n onSubmit,\n commandProvider,\n commandProviders,\n imperativeHandle,\n}: LexicalChatInputProps & {\n imperativeHandle: React.ForwardedRef<LexicalChatInputHandle>;\n}) {\n const [editor] = useLexicalComposerContext();\n const onSubmitRef = useRef(onSubmit);\n onSubmitRef.current = onSubmit;\n const commandMenuOpenRef = useRef(false);\n\n useEffect(() => {\n editor.setEditable(!disabled);\n }, [editor, disabled]);\n\n const submit = useCallback(() => {\n const payload = readPayload(editor);\n if (payload.text.trim().length === 0 && payload.tokens.length === 0) return;\n void onSubmitRef.current(payload);\n }, [editor]);\n\n useImperativeHandle(\n imperativeHandle,\n () => ({\n clear: () => {\n editor.update(() => {\n const root = $getRoot();\n root.clear();\n });\n },\n submit,\n focus: () => editor.focus(),\n }),\n [editor, submit],\n );\n\n // Intercept Enter (without Shift) and trigger submit, unless the\n // command-token typeahead menu is open — in which case the typeahead's\n // own LOW-priority handler takes the key to select an option.\n useEffect(() => {\n return editor.registerCommand(\n KEY_ENTER_COMMAND,\n (event) => {\n if (commandMenuOpenRef.current) return false;\n const keyboard = event as KeyboardEvent | null;\n if (keyboard?.shiftKey) return false;\n keyboard?.preventDefault();\n submit();\n return true;\n },\n COMMAND_PRIORITY_HIGH,\n );\n }, [editor, submit]);\n\n const handleCommandMenuStateChange = useCallback((isOpen: boolean) => {\n commandMenuOpenRef.current = isOpen;\n }, []);\n\n const handleChange = useCallback(\n (_state: EditorState) => {\n if (!onChange) return;\n const payload = readPayload(editor);\n onChange(payload);\n },\n [editor, onChange],\n );\n\n return (\n <div className={cn(\"relative w-full\", className)}>\n <RichTextPlugin\n contentEditable={\n <ContentEditable\n className={cn(\n \"outline-none w-full text-sm leading-relaxed\",\n \"min-h-[24px] max-h-[200px] overflow-y-auto\",\n \"text-gray-900 dark:text-zinc-100\",\n disabled && \"opacity-50 cursor-not-allowed\",\n inputClassName,\n )}\n />\n }\n placeholder={\n <div\n className={cn(\n \"pointer-events-none absolute top-0 left-0 text-sm text-gray-400 dark:text-zinc-500\",\n \"select-none\",\n )}\n >\n {placeholder}\n </div>\n }\n ErrorBoundary={LexicalErrorBoundary}\n />\n <OnChangePlugin onChange={handleChange} />\n <HistoryPlugin />\n {(() => {\n // Plural takes precedence; fall back to singular for back-compat.\n // We mount one plugin per provider so different triggers (e.g. `/`\n // and `@`) can each fire their own typeahead. The menu-open ref is\n // shared so submit-on-Enter is gated whichever menu is open.\n const providers =\n commandProviders && commandProviders.length > 0\n ? commandProviders\n : commandProvider\n ? [commandProvider]\n : [];\n return providers.map((p, i) => (\n <CommandTokenPlugin\n key={`${p.trigger ?? \"/\"}-${i}`}\n commandProvider={p}\n onMenuStateChange={handleCommandMenuStateChange}\n />\n ));\n })()}\n {children}\n </div>\n );\n}\n\n// Re-export so consumers that mount their own LexicalComposer can grab the\n// editor instance for advanced wiring.\nexport { useLexicalComposerContext, LexicalComposerContext };\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {prepare,layout,clearCache}from'@chenglou/pretext';var u={spacing:{messageBetween:"1rem",messagePx:"0.875rem",messagePy:"0.625rem",container:"1rem",composerPadding:"1rem"},typography:{message:{fontSize:"1rem",lineHeight:1.5,letterSpacing:"0px"},caption:{fontSize:"0.875rem",lineHeight:1.4},small:{fontSize:"0.8125rem",lineHeight:1.3}},borderRadius:{message:"0.5rem",input:"1rem",panel:"0.5rem",button:"0.5rem",chip:"0.25rem"},colors:{primary:"var(--chat-primary, #106997)",secondary:"var(--chat-secondary, #56C18A)",error:"var(--chat-error, #B1001B)",warning:"var(--chat-warning, #DD9652)",activity:"var(--chat-activity, var(--chat-primary, #106997))",messageBg:"var(--chat-message-bg, rgba(0, 0, 0, 0.03))",userMessageBg:"var(--chat-user-message-bg, var(--chat-primary, #106997))",userMessageText:"var(--chat-user-message-text, #ffffff)",panelBg:"var(--chat-panel-bg, rgba(0, 0, 0, 0.02))",panelBorder:"var(--chat-panel-border, rgba(0, 0, 0, 0.06))",border:"var(--chat-border, rgba(0, 0, 0, 0.06))",borderHover:"var(--chat-border-hover, rgba(0, 0, 0, 0.12))",text:"var(--chat-text, #1D2033)",subtle:"var(--chat-text-subtle, rgba(0, 0, 0, 0.5))",placeholder:"var(--chat-placeholder, rgba(0, 0, 0, 0.4))",status:{pending:{main:"rgba(0, 0, 0, 0.3)",bg:"rgba(0, 0, 0, 0.05)"},running:{main:"rgb(99, 102, 241)",bg:"rgba(99, 102, 241, 0.08)"},completed:{main:"rgb(16, 185, 129)",bg:"rgba(16, 185, 129, 0.08)"},failed:{main:"rgb(239, 68, 68)",bg:"rgba(239, 68, 68, 0.08)"}}},shadows:{subtle:"0 1px 2px rgba(0, 0, 0, 0.04)",input:"0 2px 8px rgba(0, 0, 0, 0.08)",inputFocus:"0 4px 16px rgba(0, 0, 0, 0.12)",button:"0 1px 3px rgba(0, 0, 0, 0.08)",buttonHover:"0 2px 6px rgba(0, 0, 0, 0.12)",primary:"0 2px 6px rgba(16, 105, 151, 0.3)",primaryHover:"0 4px 12px rgba(16, 105, 151, 0.4)"},animations:{messageEntrance:{initial:{opacity:0,y:8},animate:{opacity:1,y:0},transition:{duration:.3,ease:[.4,0,.2,1]}},fadeIn:{initial:{opacity:0},animate:{opacity:1},transition:{duration:.2}}}};var h=16,x=h*u.typography.message.lineHeight,f=`400 ${h}px "Inter", "Assistant", -apple-system, sans-serif`,y=512,t=new Map;function v(e){let r=t.get(e);return r&&(t.delete(e),t.set(e,r)),r}function T(e,r){if(t.set(e,r),t.size>y){let a=t.keys().next().value;a!==void 0&&t.delete(a);}}function P(e,r,a={}){if(!e||r<=0)return {height:0,lineCount:0};if(typeof document>"u")return null;let n=a.font??f,s=a.lineHeightPx??x,i=Math.round(r),o=`${n}|${s}|${i}|${e}`,c=v(o);if(c)return c;try{let m=prepare(e,n),g=layout(m,i,s),p={height:g.height,lineCount:g.lineCount};return T(o,p),p}catch{return null}}function A(){t.clear();try{clearCache();}catch{}}export{u as a,P as b,A as c};//# sourceMappingURL=chunk-NG4HKXYB.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-NG4HKXYB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/styles/tokens.ts","../src/utils/measureMessage.ts"],"names":["chatTokens","DEFAULT_FONT_PX","DEFAULT_LINE_HEIGHT_PX","DEFAULT_FONT","CACHE_MAX","cache","cacheGet","key","hit","cacheSet","value","oldest","measureMessageHeight","text","maxWidth","options","font","lineHeightPx","snappedWidth","cached","prepared","prepare","result","layout","measured","clearMeasurementCache","clearCache"],"mappings":"0DAKO,IAAMA,EAAa,CACxB,OAAA,CAAS,CAEP,cAAA,CAAgB,MAAA,CAEhB,UAAW,UAAA,CAEX,SAAA,CAAW,WAEX,SAAA,CAAW,MAAA,CAEX,gBAAiB,MACnB,CAAA,CAEA,WAAY,CACV,OAAA,CAAS,CACP,QAAA,CAAU,MAAA,CACV,UAAA,CAAY,GAAA,CACZ,cAAe,KACjB,CAAA,CACA,QAAS,CACP,QAAA,CAAU,WACV,UAAA,CAAY,GACd,EACA,KAAA,CAAO,CACL,SAAU,WAAA,CACV,UAAA,CAAY,GACd,CACF,CAAA,CAEA,aAAc,CACZ,OAAA,CAAS,QAAA,CACT,KAAA,CAAO,OACP,KAAA,CAAO,QAAA,CACP,OAAQ,QAAA,CACR,IAAA,CAAM,SACR,CAAA,CAEA,MAAA,CAAQ,CAEN,OAAA,CAAS,8BAAA,CACT,UAAW,gCAAA,CACX,KAAA,CAAO,6BACP,OAAA,CAAS,8BAAA,CAST,SAAU,oDAAA,CAGV,SAAA,CAAW,8CACX,aAAA,CAAe,2DAAA,CACf,gBAAiB,wCAAA,CAGjB,OAAA,CAAS,4CACT,WAAA,CAAa,+CAAA,CAGb,OAAQ,yCAAA,CACR,WAAA,CAAa,gDAGb,IAAA,CAAM,2BAAA,CACN,OAAQ,6CAAA,CACR,WAAA,CAAa,8CAGb,MAAA,CAAQ,CACN,QAAS,CACP,IAAA,CAAM,oBAAA,CACN,EAAA,CAAI,qBACN,CAAA,CACA,OAAA,CAAS,CACP,IAAA,CAAM,mBAAA,CACN,GAAI,0BACN,CAAA,CACA,UAAW,CACT,IAAA,CAAM,oBACN,EAAA,CAAI,0BACN,EACA,MAAA,CAAQ,CACN,KAAM,kBAAA,CACN,EAAA,CAAI,yBACN,CACF,CACF,EAEA,OAAA,CAAS,CACP,OAAQ,+BAAA,CACR,KAAA,CAAO,gCACP,UAAA,CAAY,gCAAA,CACZ,OAAQ,+BAAA,CACR,WAAA,CAAa,gCACb,OAAA,CAAS,mCAAA,CACT,aAAc,oCAChB,CAAA,CAEA,WAAY,CACV,eAAA,CAAiB,CACf,OAAA,CAAS,CAAE,OAAA,CAAS,CAAA,CAAG,EAAG,CAAE,CAAA,CAC5B,QAAS,CAAE,OAAA,CAAS,EAAG,CAAA,CAAG,CAAE,EAC5B,UAAA,CAAY,CAAE,SAAU,EAAA,CAAK,IAAA,CAAM,CAAC,EAAA,CAAK,CAAA,CAAG,GAAK,CAAC,CAAE,CACtD,CAAA,CACA,MAAA,CAAQ,CACN,OAAA,CAAS,CAAE,QAAS,CAAE,CAAA,CACtB,QAAS,CAAE,OAAA,CAAS,CAAE,CAAA,CACtB,UAAA,CAAY,CAAE,QAAA,CAAU,EAAI,CAC9B,CACF,CACF,EC1FA,IAAMC,CAAAA,CAAkB,GAClBC,CAAAA,CACLD,CAAAA,CAAkBD,EAAW,UAAA,CAAW,OAAA,CAAQ,WAE3CG,CAAAA,CAAe,CAAA,IAAA,EAAOF,CAAe,CAAA,kDAAA,CAAA,CAErCG,CAAAA,CAAY,IACZC,CAAAA,CAAQ,IAAI,IAElB,SAASC,CAAAA,CAASC,EAAuC,CACxD,IAAMC,EAAMH,CAAAA,CAAM,GAAA,CAAIE,CAAG,CAAA,CACzB,OAAIC,IAEHH,CAAAA,CAAM,MAAA,CAAOE,CAAG,CAAA,CAChBF,CAAAA,CAAM,IAAIE,CAAAA,CAAKC,CAAG,CAAA,CAAA,CAEZA,CACR,CAEA,SAASC,CAAAA,CAASF,EAAaG,CAAAA,CAA2B,CAEzD,GADAL,CAAAA,CAAM,GAAA,CAAIE,EAAKG,CAAK,CAAA,CAChBL,EAAM,IAAA,CAAOD,CAAAA,CAAW,CAC3B,IAAMO,CAAAA,CAASN,EAAM,IAAA,EAAK,CAAE,MAAK,CAAE,KAAA,CAC/BM,IAAW,MAAA,EAAWN,CAAAA,CAAM,OAAOM,CAAM,EAC9C,CACD,CAOO,SAASC,EACfC,CAAAA,CACAC,CAAAA,CACAC,EAA0B,EAAC,CACL,CACtB,GAAI,CAACF,GAAQC,CAAAA,EAAY,CAAA,CAAG,OAAO,CAAE,OAAQ,CAAA,CAAG,SAAA,CAAW,CAAE,CAAA,CAC7D,GAAI,OAAO,QAAA,CAAa,GAAA,CAAa,OAAO,IAAA,CAE5C,IAAME,EAAOD,CAAAA,CAAQ,IAAA,EAAQZ,EACvBc,CAAAA,CAAeF,CAAAA,CAAQ,cAAgBb,CAAAA,CAGvCgB,CAAAA,CAAe,KAAK,KAAA,CAAMJ,CAAQ,EAClCP,CAAAA,CAAM,CAAA,EAAGS,CAAI,CAAA,CAAA,EAAIC,CAAY,IAAIC,CAAY,CAAA,CAAA,EAAIL,CAAI,CAAA,CAAA,CAErDM,CAAAA,CAASb,EAASC,CAAG,CAAA,CAC3B,GAAIY,CAAAA,CAAQ,OAAOA,EAEnB,GAAI,CACH,IAAMC,CAAAA,CAAWC,QAAQR,CAAAA,CAAMG,CAAI,EAC7BM,CAAAA,CAASC,MAAAA,CAAOH,EAAUF,CAAAA,CAAcD,CAAY,EACpDO,CAAAA,CAAyB,CAC9B,OAAQF,CAAAA,CAAO,MAAA,CACf,UAAWA,CAAAA,CAAO,SACnB,EACA,OAAAb,CAAAA,CAASF,EAAKiB,CAAQ,CAAA,CACfA,CACR,CAAA,KAAQ,CACP,OAAO,IACR,CACD,CAMO,SAASC,CAAAA,EAA8B,CAC7CpB,CAAAA,CAAM,KAAA,GACN,GAAI,CACHqB,aACD,CAAA,KAAQ,CAER,CACD","file":"chunk-NG4HKXYB.js","sourcesContent":["/**\n * Design tokens for the chat UI.\n * These map to CSS custom properties for easy theming.\n */\n\nexport const chatTokens = {\n spacing: {\n /** Vertical spacing between messages (16px) */\n messageBetween: \"1rem\",\n /** Message bubble horizontal padding (14px) */\n messagePx: \"0.875rem\",\n /** Message bubble vertical padding (10px) */\n messagePy: \"0.625rem\",\n /** Container padding (16px) */\n container: \"1rem\",\n /** Composer padding (16px) */\n composerPadding: \"1rem\",\n },\n\n typography: {\n message: {\n fontSize: \"1rem\",\n lineHeight: 1.5,\n letterSpacing: \"0px\",\n },\n caption: {\n fontSize: \"0.875rem\",\n lineHeight: 1.4,\n },\n small: {\n fontSize: \"0.8125rem\",\n lineHeight: 1.3,\n },\n },\n\n borderRadius: {\n message: \"0.5rem\",\n input: \"1rem\",\n panel: \"0.5rem\",\n button: \"0.5rem\",\n chip: \"0.25rem\",\n },\n\n colors: {\n // CSS custom property references\n primary: \"var(--chat-primary, #106997)\",\n secondary: \"var(--chat-secondary, #56C18A)\",\n error: \"var(--chat-error, #B1001B)\",\n warning: \"var(--chat-warning, #DD9652)\",\n\n /**\n * Accent for in-progress / live indicators in the reasoning panel:\n * running halo, animated caret, header wave, rail-flow gradient, and\n * active-row wash. Falls back to --chat-primary so existing usage is\n * unchanged. Override independently when the brand primary is neutral\n * and would otherwise blend into completed-state visuals.\n */\n activity: \"var(--chat-activity, var(--chat-primary, #106997))\",\n\n // Message backgrounds\n messageBg: \"var(--chat-message-bg, rgba(0, 0, 0, 0.03))\",\n userMessageBg: \"var(--chat-user-message-bg, var(--chat-primary, #106997))\",\n userMessageText: \"var(--chat-user-message-text, #ffffff)\",\n\n // Panel backgrounds\n panelBg: \"var(--chat-panel-bg, rgba(0, 0, 0, 0.02))\",\n panelBorder: \"var(--chat-panel-border, rgba(0, 0, 0, 0.06))\",\n\n // Borders\n border: \"var(--chat-border, rgba(0, 0, 0, 0.06))\",\n borderHover: \"var(--chat-border-hover, rgba(0, 0, 0, 0.12))\",\n\n // Text\n text: \"var(--chat-text, #1D2033)\",\n subtle: \"var(--chat-text-subtle, rgba(0, 0, 0, 0.5))\",\n placeholder: \"var(--chat-placeholder, rgba(0, 0, 0, 0.4))\",\n\n // Status colors\n status: {\n pending: {\n main: \"rgba(0, 0, 0, 0.3)\",\n bg: \"rgba(0, 0, 0, 0.05)\",\n },\n running: {\n main: \"rgb(99, 102, 241)\",\n bg: \"rgba(99, 102, 241, 0.08)\",\n },\n completed: {\n main: \"rgb(16, 185, 129)\",\n bg: \"rgba(16, 185, 129, 0.08)\",\n },\n failed: {\n main: \"rgb(239, 68, 68)\",\n bg: \"rgba(239, 68, 68, 0.08)\",\n },\n },\n },\n\n shadows: {\n subtle: \"0 1px 2px rgba(0, 0, 0, 0.04)\",\n input: \"0 2px 8px rgba(0, 0, 0, 0.08)\",\n inputFocus: \"0 4px 16px rgba(0, 0, 0, 0.12)\",\n button: \"0 1px 3px rgba(0, 0, 0, 0.08)\",\n buttonHover: \"0 2px 6px rgba(0, 0, 0, 0.12)\",\n primary: \"0 2px 6px rgba(16, 105, 151, 0.3)\",\n primaryHover: \"0 4px 12px rgba(16, 105, 151, 0.4)\",\n },\n\n animations: {\n messageEntrance: {\n initial: { opacity: 0, y: 8 },\n animate: { opacity: 1, y: 0 },\n transition: { duration: 0.3, ease: [0.4, 0, 0.2, 1] },\n },\n fadeIn: {\n initial: { opacity: 0 },\n animate: { opacity: 1 },\n transition: { duration: 0.2 },\n },\n },\n} as const;\n\nexport type ChatTokens = typeof chatTokens;\n","/**\n * Off-DOM text measurement for chat message bodies.\n *\n * Wraps `@chenglou/pretext` (canvas-based, no layout reflow) so callers can\n * compute a paragraph's pixel height before React renders it. Used to reserve\n * vertical space for streaming assistant messages and prevent scroll jitter.\n *\n * Notes:\n * - We avoid `system-ui` in the font shorthand because pretext is inaccurate\n * with it on macOS. The font family stays in sync with `--chat-font-family`\n * minus the `system-ui` token.\n * - The cache is a small FIFO keyed by `(text, font, maxWidth, lineHeight)`.\n * Chat threads are bounded so this stays cheap.\n */\n\nimport { clearCache, layout, prepare } from \"@chenglou/pretext\";\nimport { chatTokens } from \"../styles/tokens\";\n\nexport interface MeasureOptions {\n\t/** CSS font shorthand. Defaults to the chat message body font. */\n\tfont?: string;\n\t/** Line height in pixels. Defaults to message body fontSize × lineHeight. */\n\tlineHeightPx?: number;\n}\n\nexport interface MeasuredText {\n\theight: number;\n\tlineCount: number;\n}\n\nconst DEFAULT_FONT_PX = 16; // matches chatTokens.typography.message.fontSize \"1rem\"\nconst DEFAULT_LINE_HEIGHT_PX =\n\tDEFAULT_FONT_PX * chatTokens.typography.message.lineHeight;\n\nconst DEFAULT_FONT = `400 ${DEFAULT_FONT_PX}px \"Inter\", \"Assistant\", -apple-system, sans-serif`;\n\nconst CACHE_MAX = 512;\nconst cache = new Map<string, MeasuredText>();\n\nfunction cacheGet(key: string): MeasuredText | undefined {\n\tconst hit = cache.get(key);\n\tif (hit) {\n\t\t// LRU bump\n\t\tcache.delete(key);\n\t\tcache.set(key, hit);\n\t}\n\treturn hit;\n}\n\nfunction cacheSet(key: string, value: MeasuredText): void {\n\tcache.set(key, value);\n\tif (cache.size > CACHE_MAX) {\n\t\tconst oldest = cache.keys().next().value;\n\t\tif (oldest !== undefined) cache.delete(oldest);\n\t}\n}\n\n/**\n * Measure the rendered height of a plain-text message body without touching\n * the DOM. Returns `null` if measurement is not possible in this environment\n * (e.g. SSR, no canvas).\n */\nexport function measureMessageHeight(\n\ttext: string,\n\tmaxWidth: number,\n\toptions: MeasureOptions = {},\n): MeasuredText | null {\n\tif (!text || maxWidth <= 0) return { height: 0, lineCount: 0 };\n\tif (typeof document === \"undefined\") return null;\n\n\tconst font = options.font ?? DEFAULT_FONT;\n\tconst lineHeightPx = options.lineHeightPx ?? DEFAULT_LINE_HEIGHT_PX;\n\n\t// Snap maxWidth so subpixel container resizes share cache entries.\n\tconst snappedWidth = Math.round(maxWidth);\n\tconst key = `${font}|${lineHeightPx}|${snappedWidth}|${text}`;\n\n\tconst cached = cacheGet(key);\n\tif (cached) return cached;\n\n\ttry {\n\t\tconst prepared = prepare(text, font);\n\t\tconst result = layout(prepared, snappedWidth, lineHeightPx);\n\t\tconst measured: MeasuredText = {\n\t\t\theight: result.height,\n\t\t\tlineCount: result.lineCount,\n\t\t};\n\t\tcacheSet(key, measured);\n\t\treturn measured;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Drop pretext's internal caches and our LRU. Call after the active font\n * family changes (e.g. branding swap) so measurements use the new metrics.\n */\nexport function clearMeasurementCache(): void {\n\tcache.clear();\n\ttry {\n\t\tclearCache();\n\t} catch {\n\t\t/* pretext may not be loaded in SSR */\n\t}\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {createContext,useCallback,useMemo,useContext}from'react';import {jsx}from'react/jsx-runtime';var e=createContext(null);function f({children:t,messages:n,isStreaming:o=false,streamingMessageId:a=null,viewerRole:i="user",onSendMessage:r,onStopStreaming:s,onRetryLastMessage:l,customData:d,onVisualizationAction:C,resolveCommandToken:u}){let c=useCallback(async(v,h)=>{await r(v,h);},[r]),g=useMemo(()=>({messages:n,isStreaming:o,streamingMessageId:a,viewerRole:i,sendMessage:c,stopStreaming:s,retryLastMessage:l,customData:d,onVisualizationAction:C,resolveCommandToken:u}),[n,o,a,i,c,s,l,d,C,u]);return jsx(e.Provider,{value:g,children:t})}function R(){let t=useContext(e);if(!t)throw new Error("useChatContext must be used within a ChatProvider");return t}export{e as a,f as b,R as c};//# sourceMappingURL=chunk-NKLA5PPB.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-NKLA5PPB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context/ChatProvider.tsx"],"names":["ChatContext","createContext","ChatProvider","children","messages","isStreaming","streamingMessageId","viewerRole","onSendMessage","onStopStreaming","onRetryLastMessage","customData","onVisualizationAction","resolveCommandToken","sendMessage","useCallback","content","attachments","value","useMemo","jsx","useChatContext","context","useContext"],"mappings":"qGAuCA,IAAMA,CAAAA,CAAcC,aAAAA,CAAuC,IAAI,EAmBxD,SAASC,CAAAA,CAAa,CAC3B,QAAA,CAAAC,CAAAA,CACA,QAAA,CAAAC,CAAAA,CACA,WAAA,CAAAC,CAAAA,CAAc,KAAA,CACd,kBAAA,CAAAC,EAAqB,IAAA,CACrB,UAAA,CAAAC,CAAAA,CAAa,MAAA,CACb,aAAA,CAAAC,CAAAA,CACA,eAAA,CAAAC,CAAAA,CACA,kBAAA,CAAAC,CAAAA,CACA,UAAA,CAAAC,CAAAA,CACA,qBAAA,CAAAC,CAAAA,CACA,mBAAA,CAAAC,CACF,EAAsB,CACpB,IAAMC,CAAAA,CAAcC,WAAAA,CAClB,MAAOC,CAAAA,CAAiBC,CAAAA,GAAyB,CAC/C,MAAMT,CAAAA,CAAcQ,CAAAA,CAASC,CAAW,EAC1C,CAAA,CACA,CAACT,CAAa,CAChB,CAAA,CAEMU,CAAAA,CAAQC,OAAAA,CACZ,KAAO,CACL,QAAA,CAAAf,CAAAA,CACA,WAAA,CAAAC,CAAAA,CACA,kBAAA,CAAAC,CAAAA,CACA,UAAA,CAAAC,CAAAA,CACA,WAAA,CAAAO,CAAAA,CACA,aAAA,CAAeL,CAAAA,CACf,gBAAA,CAAkBC,CAAAA,CAClB,UAAA,CAAAC,CAAAA,CACA,qBAAA,CAAAC,CAAAA,CACA,mBAAA,CAAAC,CACF,CAAA,CAAA,CACA,CACET,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAO,CAAAA,CACAL,EACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CACF,CACF,CAAA,CAEA,OAAOO,GAAAA,CAACpB,CAAAA,CAAY,QAAA,CAAZ,CAAqB,KAAA,CAAOkB,CAAAA,CAAQ,QAAA,CAAAf,CAAAA,CAAS,CACvD,CAEO,SAASkB,CAAAA,EAAiB,CAC/B,IAAMC,CAAAA,CAAUC,UAAAA,CAAWvB,CAAW,CAAA,CACtC,GAAI,CAACsB,CAAAA,CACH,MAAM,IAAI,KAAA,CAAM,mDAAmD,CAAA,CAErE,OAAOA,CACT","file":"chunk-NKLA5PPB.js","sourcesContent":["import {\n createContext,\n useContext,\n useCallback,\n useMemo,\n type ReactNode,\n} from \"react\";\nimport type { ChatMessage, ParticipantRole, VisualizationActionEvent } from \"../types\";\n\nexport interface ChatContextValue {\n /** List of messages in the conversation */\n messages: ChatMessage[];\n /** Whether a message is currently being streamed */\n isStreaming: boolean;\n /** ID of the message currently being streamed */\n streamingMessageId: string | null;\n /** The viewer's role (determines message alignment) */\n viewerRole: ParticipantRole;\n /** Send a new message */\n sendMessage: (content: string, attachments?: File[]) => Promise<void>;\n /** Stop the current streaming response */\n stopStreaming?: () => void;\n /** Retry the last failed message */\n retryLastMessage?: () => Promise<void>;\n /** Custom data passed through context */\n customData?: Record<string, unknown>;\n /** Callback when user interacts with a visualization (form submit, card action, etc.) */\n onVisualizationAction?: (event: VisualizationActionEvent) => void;\n /** Resolve how to render an inline command-token chip (e.g. an\n * `@<id>:ad-account` mention). Wire format only carries id + kind, so the\n * host app supplies the display info. Returning `tag` replaces the default\n * uppercase kind pill (e.g. a platform logo). Returning `label` overrides\n * the id text. */\n resolveCommandToken?: (\n id: string,\n kind: string,\n ) => { label?: string; tag?: ReactNode } | undefined;\n}\n\nconst ChatContext = createContext<ChatContextValue | null>(null);\n\nexport interface ChatProviderProps {\n children: ReactNode;\n messages: ChatMessage[];\n isStreaming?: boolean;\n streamingMessageId?: string | null;\n viewerRole?: ParticipantRole;\n onSendMessage: (content: string, attachments?: File[]) => Promise<void>;\n onStopStreaming?: () => void;\n onRetryLastMessage?: () => Promise<void>;\n customData?: Record<string, unknown>;\n onVisualizationAction?: (event: VisualizationActionEvent) => void;\n resolveCommandToken?: (\n id: string,\n kind: string,\n ) => { label?: string; tag?: ReactNode } | undefined;\n}\n\nexport function ChatProvider({\n children,\n messages,\n isStreaming = false,\n streamingMessageId = null,\n viewerRole = \"user\",\n onSendMessage,\n onStopStreaming,\n onRetryLastMessage,\n customData,\n onVisualizationAction,\n resolveCommandToken,\n}: ChatProviderProps) {\n const sendMessage = useCallback(\n async (content: string, attachments?: File[]) => {\n await onSendMessage(content, attachments);\n },\n [onSendMessage]\n );\n\n const value = useMemo<ChatContextValue>(\n () => ({\n messages,\n isStreaming,\n streamingMessageId,\n viewerRole,\n sendMessage,\n stopStreaming: onStopStreaming,\n retryLastMessage: onRetryLastMessage,\n customData,\n onVisualizationAction,\n resolveCommandToken,\n }),\n [\n messages,\n isStreaming,\n streamingMessageId,\n viewerRole,\n sendMessage,\n onStopStreaming,\n onRetryLastMessage,\n customData,\n onVisualizationAction,\n resolveCommandToken,\n ]\n );\n\n return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;\n}\n\nexport function useChatContext() {\n const context = useContext(ChatContext);\n if (!context) {\n throw new Error(\"useChatContext must be used within a ChatProvider\");\n }\n return context;\n}\n\nexport { ChatContext };\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {useMemo}from'react';function l(t){let r=t.trim();if(r.startsWith("#")){let e=r.slice(1);if(e.length===3)return [parseInt(e[0]+e[0],16),parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16)];if(e.length>=6)return [parseInt(e.slice(0,2),16),parseInt(e.slice(2,4),16),parseInt(e.slice(4,6),16)]}let o=t.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})/);return o?[Number(o[1]),Number(o[2]),Number(o[3])]:null}function i(t,r,o){let[e,s,n]=[t/255,r/255,o/255].map(c=>c<=.03928?c/12.92:Math.pow((c+.055)/1.055,2.4));return .2126*e+.7152*s+.0722*n}function a(t){let r=l(t);return r&&i(...r)>.179?"#1D2033":"#ffffff"}function C(t,r){return useMemo(()=>{if(!t&&!r?.iconColor)return {};let o={},e=t?.backgroundBubbleColor||r?.iconColor;return e&&(o["--chat-primary"]=e,o["--chat-user-message-bg"]=e,o["--chat-user-message-text"]=a(e)),t?.headerBackgroundColor&&(o["--chat-header-bg"]=t.headerBackgroundColor),t?.messageFontSize&&(o["--chat-message-font-size"]=`${t.messageFontSize}px`),t?.fontFamily&&(o["--chat-font-family"]=t.fontFamily),t?.approvalAccentColor&&(o["--chat-approval-accent"]=t.approvalAccentColor,o["--chat-approval-accent-soft"]=t.approvalAccentColor),t?.approveButtonColor&&(o["--chat-approve-bg"]=t.approveButtonColor,o["--chat-approve-bg-hover"]=t.approveButtonHoverColor||t.approveButtonColor),t?.rejectButtonHoverColor&&(o["--chat-reject-bg-hover"]=t.rejectButtonHoverColor),t?.clarificationAccentColor&&(o["--chat-clarification-accent"]=t.clarificationAccentColor,o["--chat-clarification-accent-soft"]=t.clarificationAccentColor),t?.activityAccentColor&&(o["--chat-activity"]=t.activityAccentColor),o},[t,r?.iconColor])}export{a,C as b};//# sourceMappingURL=chunk-QECTWT2H.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-QECTWT2H.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/color-contrast.ts","../src/hooks/use-branding-css-vars.ts"],"names":["parseColor","color","hex","h","rgbMatch","relativeLuminance","r","g","b","rs","gs","bs","getContrastTextColor","bgColor","rgb","useBrandingCSSVars","branding","overrides","useMemo","vars","primaryColor"],"mappings":"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","file":"chunk-QECTWT2H.js","sourcesContent":["/**\n * Parse a CSS color string (hex, rgb, rgba) into [r, g, b] values.\n * Returns null if the color cannot be parsed.\n */\nfunction parseColor(color: string): [number, number, number] | null {\n // Hex: #RGB, #RRGGBB\n const hex = color.trim();\n if (hex.startsWith(\"#\")) {\n const h = hex.slice(1);\n if (h.length === 3) {\n return [\n parseInt(h[0] + h[0], 16),\n parseInt(h[1] + h[1], 16),\n parseInt(h[2] + h[2], 16),\n ];\n }\n if (h.length >= 6) {\n return [\n parseInt(h.slice(0, 2), 16),\n parseInt(h.slice(2, 4), 16),\n parseInt(h.slice(4, 6), 16),\n ];\n }\n }\n\n // rgb(r, g, b) or rgba(r, g, b, a)\n const rgbMatch = color.match(\n /rgba?\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})/\n );\n if (rgbMatch) {\n return [Number(rgbMatch[1]), Number(rgbMatch[2]), Number(rgbMatch[3])];\n }\n\n return null;\n}\n\n/**\n * Compute the relative luminance of an sRGB color per WCAG 2.0.\n * https://www.w3.org/TR/WCAG20/#relativeluminancedef\n */\nfunction relativeLuminance(r: number, g: number, b: number): number {\n const [rs, gs, bs] = [r / 255, g / 255, b / 255].map((c) =>\n c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)\n );\n return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;\n}\n\n/**\n * Given a background color string, return the best contrast text color.\n * Returns \"#ffffff\" for dark backgrounds and \"#1D2033\" for light backgrounds.\n * Falls back to \"#ffffff\" if the color cannot be parsed.\n */\nexport function getContrastTextColor(bgColor: string): string {\n const rgb = parseColor(bgColor);\n if (!rgb) return \"#ffffff\";\n\n const lum = relativeLuminance(...rgb);\n // WCAG threshold: luminance > 0.179 means the background is \"light\"\n return lum > 0.179 ? \"#1D2033\" : \"#ffffff\";\n}\n","import { useMemo } from \"react\";\nimport type { BrandingData } from \"../types/branding\";\nimport { getContrastTextColor } from \"../utils/color-contrast\";\n\n/**\n * Converts BrandingData into CSS custom properties for chat-ui theming.\n * Returns a CSSProperties object that can be spread onto a container element.\n */\nexport function useBrandingCSSVars(\n branding: BrandingData | null | undefined,\n overrides?: { iconColor?: string }\n): React.CSSProperties {\n return useMemo(() => {\n if (!branding && !overrides?.iconColor) return {};\n\n const vars: Record<string, string> = {};\n\n const primaryColor =\n branding?.backgroundBubbleColor || overrides?.iconColor;\n if (primaryColor) {\n vars[\"--chat-primary\"] = primaryColor;\n vars[\"--chat-user-message-bg\"] = primaryColor;\n vars[\"--chat-user-message-text\"] = getContrastTextColor(primaryColor);\n }\n\n if (branding?.headerBackgroundColor) {\n vars[\"--chat-header-bg\"] = branding.headerBackgroundColor;\n }\n\n if (branding?.messageFontSize) {\n vars[\"--chat-message-font-size\"] = `${branding.messageFontSize}px`;\n }\n\n if (branding?.fontFamily) {\n vars[\"--chat-font-family\"] = branding.fontFamily;\n }\n\n if (branding?.approvalAccentColor) {\n vars[\"--chat-approval-accent\"] = branding.approvalAccentColor;\n vars[\"--chat-approval-accent-soft\"] = branding.approvalAccentColor;\n }\n\n if (branding?.approveButtonColor) {\n vars[\"--chat-approve-bg\"] = branding.approveButtonColor;\n vars[\"--chat-approve-bg-hover\"] =\n branding.approveButtonHoverColor || branding.approveButtonColor;\n }\n\n if (branding?.rejectButtonHoverColor) {\n vars[\"--chat-reject-bg-hover\"] = branding.rejectButtonHoverColor;\n }\n\n if (branding?.clarificationAccentColor) {\n vars[\"--chat-clarification-accent\"] = branding.clarificationAccentColor;\n vars[\"--chat-clarification-accent-soft\"] = branding.clarificationAccentColor;\n }\n\n // Activity accent is independent of brand primary so callers whose\n // primary is neutral can still surface a vivid in-progress signal in\n // the reasoning panel. Unset → falls back to --chat-primary at usage.\n if (branding?.activityAccentColor) {\n vars[\"--chat-activity\"] = branding.activityAccentColor;\n }\n\n return vars as React.CSSProperties;\n }, [branding, overrides?.iconColor]);\n}\n"]}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as ChatMessage } from '../message-
|
|
2
|
-
import { B as BrandingData } from '../branding-
|
|
3
|
-
import '../streaming-
|
|
1
|
+
import { C as ChatMessage } from '../message-D2ElX5k7.js';
|
|
2
|
+
import { B as BrandingData } from '../branding-NieTEGQf.js';
|
|
3
|
+
import '../streaming-B_u_jno6.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Types for the Miiflow chat client (transport layer).
|
package/dist/client/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
export{c as clearMeasurementCache,b as measureMessageHeight}from'../chunk-
|
|
1
|
+
export{c as clearMeasurementCache,b as measureMessageHeight}from'../chunk-NG4HKXYB.js';import {b}from'../chunk-QECTWT2H.js';import {useState,useRef,useEffect,useMemo,useCallback}from'react';function $(t){return t.baseUrl?t.baseUrl.replace(/\/api\/?$/,""):t.bundleUrl?.includes("localhost")||t.bundleUrl?.includes("127.0.0.1")||false?"http://localhost:8003":"https://api.miiflow.ai"}function U(){let t="miiflow-user-id",n=null;try{n=localStorage.getItem(t);}catch{}if(!n){n=`muid_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;try{localStorage.setItem(t,n);}catch{}}return n}async function le(t){let n=$(t),s=await fetch(`${n}/api/embed/init`,{method:"POST",headers:{"Content-Type":"application/json","X-Embed-Public-Key":t.publicKey,"x-mii-user-id":U()},body:JSON.stringify({assistant_id:t.assistantId,user_data:{user_id:t.userId,name:t.userName,email:t.userEmail}})});if(!s.ok){let l=await s.text();throw new Error(`Init failed: ${s.status} - ${l}`)}let i=await s.json();if(!i.success)throw new Error(i.error||"Failed to initialize session");return {token:i.token,config:i.config,session_id:i.session_id}}async function ge(t,n){let s=$(t),i=await fetch(`${s}/api/embed/graphql`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`,"x-mii-user-id":U()},body:JSON.stringify({operationName:"CreateThread",variables:{input:{assistantId:n.config.assistant_id,name:"New Thread",isPreview:false}},query:`mutation CreateThread($input: CreateThreadInput!) {
|
|
2
2
|
createThread(input: $input) {
|
|
3
3
|
thread { id status name isPreview }
|
|
4
4
|
}
|
|
5
|
-
}`})});if(!
|
|
6
|
-
`);
|
|
7
|
-
`)?
|
|
8
|
-
`).replace(/\\"/g,'"').replace(/\\t/g," ").replace(/\\\\/g,"\\"));}else if(i==="answer"){let a=e.chunk?.trim()||"",u=p.trim();a&&u&&a===u||(p+=e.chunk||"");}else C+=e.chunk||"";e.previous_message_id&&c.onUserMessageIdUpdate(l,e.previous_message_id),s();}else if(e.type==="claude_text"){p+=e.chunk||"";let i=o.find(a=>a.type==="claude_text");i?i.content+=e.chunk||"":o.push({type:"claude_text",content:e.chunk||"",orchestrator:"claude_agent_sdk"}),s();}else if(e.type==="claude_thinking")o.push({type:"claude_thinking",content:e.content||"",orchestrator:"claude_agent_sdk"}),s();else if(e.type==="claude_file_read"){let i={toolUseId:e.tool_use_id,operation:"read",filePath:e.file_path,status:e.status,content:e.content,totalLines:e.total_lines,language:e.language,error:e.error,durationMs:e.duration_ms},a=o.findIndex(u=>u.type==="file_operation"&&u.toolUseId===e.tool_use_id);a>=0?o[a].fileOperationData=i:o.push({type:"file_operation",content:"",toolUseId:e.tool_use_id,fileOperationData:i,orchestrator:"claude_agent_sdk"}),s();}else if(e.type==="claude_file_edit"){let i={toolUseId:e.tool_use_id,operation:"edit",filePath:e.file_path,status:e.status,oldString:e.old_string,newString:e.new_string,error:e.error,durationMs:e.duration_ms},a=o.findIndex(u=>u.type==="file_operation"&&u.toolUseId===e.tool_use_id);a>=0?o[a].fileOperationData=i:o.push({type:"file_operation",content:"",toolUseId:e.tool_use_id,fileOperationData:i,orchestrator:"claude_agent_sdk"}),s();}else if(e.type==="claude_file_write"){let i={toolUseId:e.tool_use_id,operation:"write",filePath:e.file_path,status:e.status,error:e.error,durationMs:e.duration_ms},a=o.findIndex(u=>u.type==="file_operation"&&u.toolUseId===e.tool_use_id);a>=0?o[a].fileOperationData=i:o.push({type:"file_operation",content:"",toolUseId:e.tool_use_id,fileOperationData:i,orchestrator:"claude_agent_sdk"}),s();}else if(e.type==="claude_bash"){let i={toolUseId:e.tool_use_id,command:e.command,description:e.description,status:e.status,stdout:e.stdout,stderr:e.stderr,exitCode:e.exit_code,durationMs:e.duration_ms},a=o.findIndex(u=>u.type==="terminal"&&u.toolUseId===e.tool_use_id);a>=0?o[a].terminalData=i:o.push({type:"terminal",content:"",toolUseId:e.tool_use_id,terminalData:i,orchestrator:"claude_agent_sdk"}),s();}else if(e.type==="claude_search"){let i={toolUseId:e.tool_use_id,tool:e.tool,pattern:e.pattern,path:e.path,status:e.status,results:(e.results||[]).map(u=>({filePath:u.file_path,lineNumber:u.line_number,snippet:u.snippet})),totalCount:e.total_count||0,error:e.error,durationMs:e.duration_ms},a=o.findIndex(u=>u.type==="search_results"&&u.toolUseId===e.tool_use_id);a>=0?o[a].searchResultsData=i:o.push({type:"search_results",content:"",toolUseId:e.tool_use_id,searchResultsData:i,orchestrator:"claude_agent_sdk"}),s();}else if(e.type==="claude_web_search"||e.type==="claude_web_fetch"){let i={toolUseId:e.tool_use_id,operation:e.type==="claude_web_search"?"search":"fetch",query:e.query,url:e.url,status:e.status,results:e.results,content:e.content,error:e.error,durationMs:e.duration_ms},a=o.findIndex(u=>u.type==="web_operation"&&u.toolUseId===e.tool_use_id);a>=0?o[a].webOperationData=i:o.push({type:"web_operation",content:"",toolUseId:e.tool_use_id,webOperationData:i,orchestrator:"claude_agent_sdk"}),s();}else if(e.type==="claude_subagent_start"){let i={subagentId:e.subagent_id,subagentType:e.subagent_type,description:e.description,prompt:e.prompt,status:"running",nestedChunks:[]};o.push({type:"subagent",content:"",toolUseId:e.parent_tool_use_id,subagentData:i,orchestrator:"claude_agent_sdk"}),s();}else if(e.type==="claude_subagent_chunk"){let i=o.findIndex(a=>a.type==="subagent"&&a.subagentData?.subagentId===e.subagent_id);i>=0&&o[i].subagentData&&o[i].subagentData.nestedChunks.push({type:e.is_tool?"tool":"answer",content:e.chunk||"",toolName:e.tool_name,orchestrator:"claude_agent_sdk"}),s();}else if(e.type==="claude_subagent_complete"){let i=o.findIndex(a=>a.type==="subagent"&&a.subagentData?.subagentId===e.subagent_id);i>=0&&o[i].subagentData&&(o[i].subagentData.status="completed",o[i].subagentData.result=e.result,o[i].subagentData.durationMs=e.duration_ms),s();}else if(e.type==="claude_tool_use"||e.type==="claude_tool_result"){let i={toolUseId:e.tool_use_id,toolName:e.tool_name,toolDescription:e.tool_description,toolInput:e.tool_input,status:e.type==="claude_tool_use"?"pending":e.is_error?"error":"completed",content:e.content,isError:e.is_error,durationMs:e.duration_ms},a=o.findIndex(u=>u.claudeToolData?.toolUseId===e.tool_use_id);a>=0?o[a].claudeToolData=i:o.push({type:"tool",content:e.content||"",toolUseId:e.tool_use_id,toolName:e.tool_name,toolDescription:e.tool_description,status:i.status==="pending"?"planned":"completed",claudeToolData:i,orchestrator:"claude_agent_sdk"}),s();}else if(e.type==="clarification_needed"){N();let i={question:e.question||"",options:e.options||[],context:e.context,allowFreeText:e.allow_free_text!==!1,subtaskId:e.subtask_id,subtaskDescription:e.subtask_description,subagentName:e.subagent_name,subagentRole:e.subagent_role,toolCallId:e.tool_call_id};o.push({type:"clarification_needed",content:i.question,clarificationData:i,subtaskId:e.subtask_id}),$=i,p||(p=i.question),s();}else if(e.type==="tool_approval_needed"){N();let i={toolName:e.tool_name||"",toolDescription:e.tool_description||"",toolInputs:e.tool_inputs||{},toolSchema:e.tool_schema,toolCallId:e.tool_call_id};o.push({type:"tool_approval_needed",content:i.toolDescription,toolApprovalData:i}),te=i,s();}else if(e.type==="media"){let i=e.media_data;i&&(R=[...R,{id:i.id,url:i.url,mediaType:i.media_type||"image",altText:i.alt_text}],s());}else if(e.type==="assistant_complete"){N(),b=!0;let i=e.message?.text_content||p,a=e.message?.id,u=e.message?.metadata?.sources,W=(Date.now()-m)/1e3;c.onComplete(h,i,a,o,T,u,$,W,te),p=i,a&&(h=a);break}else if(e.type==="client_tool_invocation"&&e.invocation&&c.onToolInvocation)c.onToolInvocation(e.invocation);else {if(e.type==="error")throw new Error(e.error||"Stream error");if(e.type==="done"){if(h&&!b){N();let i=(Date.now()-m)/1e3;c.onComplete(h,p||"",e.message_id,o,T,void 0,$,i,te);}else e.message_id&&h&&c.onMessageUpdate({id:h,isStreaming:!1});e.message_id&&(h=e.message_id);break}}}catch(e){if(e instanceof Error&&e.message!=="Stream error"&&(e.message.startsWith("Stream error")||e.message.startsWith("HTTP error")))throw e}}}return {assistantMsgId:h,assistantContent:p}}function Xe(t){let[n,l]=useState([]),[c,d]=useState(false),[m,p]=useState(null),[h,o]=useState(null),[T,$]=useState(true),[te,R]=useState(null),b$1=useRef(t);b$1.current=t;let _=useRef(h);_.current=h;let C=useRef(c);C.current=c;let B=useRef(null),P=useRef(new Map),I=useRef(new Map);useEffect(()=>{let r=false;async function s(){try{let f=await me(b$1.current);r||(o(f),$(!1));}catch(f){r||(R(f instanceof Error?f.message:"Failed to initialize"),$(false));}}return s(),()=>{r=true;}},[t.publicKey,t.assistantId]);let L=useMemo(()=>He(h),[h]),H=b(L),j=useRef(new Map),J=useCallback(async r=>{let s=_.current;if(!s)throw new Error("Not initialized");if(r.type.startsWith("image/"))try{r=await Se(r);}catch(g){console.warn("Image compression failed, using original:",g);}let f=await he(b$1.current,s,r);return j.current.set(f,{id:f,filename:r.name,mimeType:r.type,size:r.size,isImage:r.type.startsWith("image/"),isVideo:r.type.startsWith("video/"),isDocument:!r.type.startsWith("image/")&&!r.type.startsWith("video/")}),f},[]),X=useCallback(r=>{j.current.delete(r);},[]),O=useCallback(async r=>{let s=_.current;if(!s)throw new Error("Not initialized");let{invocation_id:f,tool_name:g,parameters:x}=r;console.log(`[Miiflow] Tool invocation received: "${g}" (id: ${f})`);let k=P.current.get(g);if(!k)return false;try{let w=await Promise.race([k(x),new Promise((z,M)=>setTimeout(()=>M(new Error("Tool execution timeout (30s)")),3e4))]);return console.log(`[Miiflow] Tool "${g}" executed successfully (id: ${f})`),await Q(b$1.current,s,{invocation_id:f,result:w}),console.log(`[Miiflow] Tool result sent for "${g}" (id: ${f})`),!0}catch(w){return console.error(`[Miiflow] Tool '${g}' execution failed:`,w),await Q(b$1.current,s,{invocation_id:f,error:w instanceof Error?w.message:String(w)}),true}},[]);useEffect(()=>{if(!h)return;let r=h,s=null,f=null,g=null,x=0,k=false;function w(){if(k)return;let z=Le(b$1.current,r);s=new WebSocket(z),s.onopen=()=>{console.log("[Miiflow] WebSocket connected"),x=0,f=setInterval(()=>{s?.readyState===WebSocket.OPEN&&s.send(JSON.stringify({type:"heartbeat"}));},ze);},s.onmessage=M=>{try{let e=JSON.parse(M.data);e.type==="client_tool_invocation"&&e.invocation&&O(e.invocation).then(async i=>{if(!i){let a=b$1.current.onToolInvocationFallback;a&&await a(e.invocation)||Q(b$1.current,r,{invocation_id:e.invocation.invocation_id,error:`No handler found for tool '${e.invocation.tool_name}'`}).catch(console.error);}}).catch(console.error);}catch{}},s.onclose=()=>{if(f&&(clearInterval(f),f=null),!k){let M=Math.min(We*Math.pow(2,x),qe);x++,g=setTimeout(w,M);}},s.onerror=()=>{};}return w(),()=>{k=true,f&&clearInterval(f),g&&clearTimeout(g),s&&(s.onclose=null,s.close());}},[h,O]);let G=useCallback(async r=>{let s=_.current;if(!s)throw new Error("Not initialized");await _e(b$1.current,s,r);},[]),Y=useCallback(async r=>{let s=_.current;if(!s)throw new Error("Not initialized");await be(b$1.current,s,r);},[]),V=useCallback(async(r,s,f)=>{let g=_.current,x=!!r.trim(),k=s&&s.length>0;if(!x&&!k||C.current||!g)return;C.current=true;let w=`msg-${Date.now()}`,z=!!f?.is_clarification_response,M=s?.map(a=>j.current.get(a)).filter(Boolean);s?.forEach(a=>j.current.delete(a));let e=`assistant-pending-${Date.now()}`,i={id:e,textContent:"",participant:{id:"assistant",name:g.config.branding?.custom_name||g.config.assistant_name,role:"assistant",avatarUrl:g.config.branding?.assistant_avatar},createdAt:new Date().toISOString(),isStreaming:true};if(z)l(a=>[...a,i]);else {let a={id:w,textContent:r,participant:{id:"user",name:b$1.current.userName||"You",role:"user"},createdAt:new Date().toISOString(),attachments:M?.length?M:void 0};l(u=>[...u,a,i]),b$1.current.onUserMessageCreated?.({id:w,content:r});}d(true),p(e);try{let a=A(b$1.current),u=new AbortController;B.current=u;let W=await fetch(`${a}/assistant/message/stream/`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${g.token}`,"x-mii-user-id":U()},body:JSON.stringify({thread_id:g.config.thread_id,text_content:r,message_id:w,metadata:f||{},attachment_ids:s||[]}),signal:u.signal});if(!W.ok)throw new Error(`HTTP error: ${W.status}`);let re=W.body?.getReader();if(!re)throw new Error("No response body");let ie=await Je(re,g,w,{onMessageCreate:S=>{p(S.id),l(D=>[...D.filter(F=>F.id!==e),S]);},onMessageUpdate:S=>{l(D=>D.map(v=>v.id===S.id?{...v,...S}:v));},onUserMessageIdUpdate:(S,D)=>{l(v=>v.map(F=>F.id===S?{...F,id:D}:F));},onComplete:(S,D,v,F,le,xe,De,ve,Ae)=>{S&&l(Me=>Me.map(ue=>ue.id===S?{...ue,id:v||ue.id,textContent:D,isStreaming:!1,reasoning:F,suggestedActions:le,citations:xe,pendingClarification:De,pendingToolApproval:Ae,executionTime:ve}:ue)),b$1.current.onAssistantMessageComplete?.({id:v||S||"",content:D});},onToolInvocation:async S=>{if(!await O(S)){let v=b$1.current.onToolInvocationFallback;if(!(v?await v(S):!1)){let le=_.current;le&&await Q(b$1.current,le,{invocation_id:S.invocation_id,error:`No handler found for tool '${S.tool_name}'`});}}}});ie.assistantMsgId&&l(S=>S.map(D=>D.id===ie.assistantMsgId?{...D,isStreaming:!1}:D));}catch(a){if(a instanceof DOMException&&a.name==="AbortError")return;console.error("[Miiflow] Send error:",a);let u=_.current,W={id:`error-${Date.now()}`,textContent:"Sorry, I encountered an error. Please try again.",participant:{id:"assistant",name:u?.config.branding?.custom_name||u?.config.assistant_name||"Assistant",role:"assistant",avatarUrl:u?.config.branding?.assistant_avatar},createdAt:new Date().toISOString()};l(re=>[...re.filter(ie=>ie.id!==e),W]),R(a instanceof Error?a.message:"Send failed");}finally{B.current=null,d(false),p(null);}},[O]),K=useCallback(()=>{C.current&&(B.current?.abort(),B.current=null,l(r=>r.map(s=>s.isStreaming?{...s,isStreaming:false}:s)),C.current=false,d(false),p(null));},[]),Z=useCallback(async()=>{let r=_.current;if(!r)throw new Error("Not initialized");let s=await ge(b$1.current,r);s.token||console.warn("[Miiflow] CreateThread did not return new token \u2014 tools may register to wrong thread");let f={...r,config:{...r.config,thread_id:s.threadId},token:s.token||r.token};if(_.current=f,o(f),l([]),I.current.size>0){console.log(`[Miiflow] Re-registering ${I.current.size} tools on new thread`);try{await ae(b$1.current,f,Array.from(I.current.values()));}catch(g){console.warn("[Miiflow] Failed to re-register tools:",g);}}return s.threadId},[]),oe=useCallback(async r=>{let s=_.current;if(!s)throw new Error("Not initialized");de(r),P.current.set(r.name,r.handler);let f=pe(r);I.current.set(r.name,f);try{await ae(b$1.current,s,[f]),console.log(`[Miiflow] Tool registered: "${r.name}"`);}catch(g){throw P.current.delete(r.name),I.current.delete(r.name),g}},[]),ne=useCallback(async r=>{let s=_.current;if(!s)throw new Error("Not initialized");for(let k of r)de(k);let f=new Map(P.current),g=new Map(I.current),x=[];for(let k of r){P.current.set(k.name,k.handler);let w=pe(k);I.current.set(k.name,w),x.push(w);}try{await ae(b$1.current,s,x);let k=r.map(w=>w.name);console.log(`[Miiflow] Tools registered: ${JSON.stringify(k)} (${r.length} tools)`);}catch(k){throw P.current=f,I.current=g,k}},[]),se=useMemo(()=>n.filter(r=>r.participant.role!=="system").map(r=>({id:r.id,textContent:r.textContent?.replace(/\[ref:[^\]]+\]/g,"")||r.textContent,participant:r.participant,createdAt:r.createdAt,isStreaming:r.isStreaming,reasoning:r.reasoning,suggestedActions:r.suggestedActions,citations:r.citations,attachments:r.attachments,pendingClarification:r.pendingClarification,pendingToolApproval:r.pendingToolApproval,executionTime:r.executionTime})),[n]),N=useCallback(r=>{o(r);},[]);return {messages:se,isStreaming:c,streamingMessageId:m,sendMessage:V,uploadFile:J,removeUploadedAttachment:X,session:h,loading:T,error:te,branding:L,brandingCSSVars:H,startNewThread:Z,registerTool:oe,registerTools:ne,sendSystemEvent:G,sendPageContext:Y,handleToolInvocation:O,updateSession:N,stopStreaming:K}}function fe(t){try{let n=t.split(".");if(n.length!==3)return null;let l=n[1].replace(/-/g,"+").replace(/_/g,"/"),c=atob(l),d=JSON.parse(c);return typeof d.exp=="number"?d.exp*1e3:null}catch{return null}}function Ge(t,n){let l=fe(t);return l===null?true:l-Date.now()<=n}function Ye(t){let n=fe(t);return n===null?true:Date.now()>=n}function Ve(t){let n=fe(t);return n===null?0:Math.max(0,n-Date.now())}export{y as ToolValidationError,ge as createThread,A as getBackendBaseUrl,U as getOrCreateUserId,Ve as getTimeUntilExpiry,me as initSession,Ye as isTokenExpired,Ge as isTokenExpiringSoon,fe as parseTokenExpiry,ae as registerToolsOnBackend,be as sendPageContext,_e as sendSystemEvent,Q as sendToolResult,pe as serializeToolDefinition,Oe as updateUser,he as uploadFile,Xe as useMiiflowChat,de as validateToolDefinition};//# sourceMappingURL=index.js.map
|
|
5
|
+
}`})});if(!i.ok)throw new Error(`Failed to create thread: ${i.status}`);let l=await i.json(),p=l.data?.createThread?.thread?.id;if(!p)throw new Error("No thread ID returned");return {threadId:p,token:l.token}}async function Oe(t,n,s){let i=$(t);await fetch(`${i}/api/embed/update`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`},body:JSON.stringify({user_data:s})});}async function me(t,n,s){let l=`${$(t)}/api/embed/upload-attachment`,p=new FormData;p.append("file",s);let c=await fetch(l,{method:"POST",headers:{Authorization:`Bearer ${n.token}`,"x-mii-user-id":U()},body:p});if(!c.ok){let x=await c.text();throw new Error(`Upload failed: ${c.status} - ${x}`)}let f=(await c.json()).attachment?.id;if(!f)throw new Error("No attachment ID returned");return f}async function he(t,n,s){let i=$(t),l=await fetch(`${i}/api/embed/system-event`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`,"x-mii-user-id":U()},body:JSON.stringify({thread_id:n.config.thread_id,system_event:{action:s.action,description:s.description,followUpInstruction:s.followUpInstruction,metadata:s.metadata||{}}})});if(!l.ok){let c=await l.text();throw new Error(`Failed to send system event: ${l.status} - ${c}`)}let p=await l.json();if(!p.success)throw new Error(p.error||"Failed to send system event")}async function we(t,n,s){let i=$(t),l=await fetch(`${i}/api/embed/page-context`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`,"x-mii-user-id":U()},body:JSON.stringify({thread_id:n.config.thread_id,page_context:{action:s.action,content:s.content,metadata:s.metadata||{}}})});if(!l.ok){let c=await l.text();throw new Error(`Failed to send page context: ${l.status} - ${c}`)}let p=await l.json();if(!p.success)throw new Error(p.error||"Failed to send page context")}async function K(t,n,s){let i=$(t),l=await fetch(`${i}/api/embed/tool-result`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`},body:JSON.stringify(s)});if(!l.ok){let c=await l.text();throw new Error(`Failed to send tool result: ${l.status} - ${c}`)}let p=await l.json();if(!p.success)throw new Error(`Failed to send tool result: ${p.error}`)}async function ne(t,n,s){let i=$(t);if(s.length===1){let l=await fetch(`${i}/api/embed/register-tool`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`},body:JSON.stringify(s[0])});if(!l.ok){let c=await l.text();throw new Error(`Failed to register tool: ${l.status} - ${c}`)}let p=await l.json();if(!p.success)throw new Error(`Failed to register tool: ${p.error||"Unknown error"}`)}else if(s.length>1){let l=await fetch(`${i}/api/embed/register-tools`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`},body:JSON.stringify(s)});if(!l.ok){let c=await l.text();throw new Error(`Failed to register tools: ${l.status} - ${c}`)}let p=await l.json();if(!p.success)throw new Error(`Failed to register tools: ${p.error||"Unknown error"}`)}}var ke=new Set(["string","number","integer","boolean","array","object","null"]),Ue=/^[a-zA-Z_][a-zA-Z0-9_]*$/,_=class extends Error{constructor(n){super(n),this.name="ToolValidationError";}};function de(t){if(!t.name)throw new _("Tool name is required");if(!t.description)throw new _("Tool description is required");if(!t.parameters)throw new _("Tool parameters schema is required");if(typeof t.handler!="function")throw new _("Tool handler must be a function");Re(t.name),Be(t.description),je(t.parameters);}function Re(t){if(typeof t!="string")throw new _("Tool name must be a string");if(t.length===0)throw new _("Tool name cannot be empty");if(t.length>64)throw new _("Tool name too long (max 64 characters)");if(!Ue.test(t))throw new _("Tool name must start with letter/underscore and contain only alphanumeric characters and underscores")}function Be(t){if(typeof t!="string")throw new _("Tool description must be a string");if(t.trim().length===0)throw new _("Tool description cannot be empty");if(t.length>500)throw new _("Tool description too long (max 500 characters)")}function je(t){if(typeof t!="object"||t===null||Array.isArray(t))throw new _("Parameters must be a JSON Schema object");if(t.type!=="object")throw new _("Parameters schema must be of type 'object' (function parameters)");if(t.properties&&typeof t.properties=="object")for(let[n,s]of Object.entries(t.properties))ce(n,s);if(t.required!==void 0){if(!Array.isArray(t.required))throw new _("'required' must be an array");for(let n of t.required)if(typeof n!="string")throw new _("'required' array must contain only strings")}if(t.additionalProperties!==void 0&&typeof t.additionalProperties!="boolean"&&(typeof t.additionalProperties!="object"||t.additionalProperties===null))throw new _("'additionalProperties' must be boolean or object")}function ce(t,n){if(typeof n!="object"||n===null||Array.isArray(n))throw new _(`Property '${t}' schema must be an object`);if(n.type!==void 0){let s=Array.isArray(n.type)?n.type:[n.type];for(let i of s)if(!ke.has(i))throw new _(`Invalid type '${i}' for property '${t}'. Valid types: ${Array.from(ke).join(", ")}`)}if(n.enum!==void 0&&!Array.isArray(n.enum))throw new _(`Property '${t}' enum must be an array`);if(n.properties&&typeof n.properties=="object")for(let[s,i]of Object.entries(n.properties))ce(`${t}.${s}`,i);n.items&&(Array.isArray(n.items)?n.items.forEach((s,i)=>{ce(`${t}[${i}]`,s);}):ce(`${t}[]`,n.items));}function ue(t){return {name:t.name,description:t.description,parameters:t.parameters}}var ze=new Set(["image/jpeg","image/jpg","image/png","image/webp","image/bmp"]);function Fe(t){return ze.has(t.type)}function Le(t){return new Promise((n,s)=>{let i=URL.createObjectURL(t),l=new Image;l.onload=()=>{URL.revokeObjectURL(i),n(l);},l.onerror=()=>{URL.revokeObjectURL(i),s(new Error("Failed to load image"));},l.src=i;})}function be(t,n,s,i){return new Promise((l,p)=>{let c=Math.round(t.naturalWidth*n),m=Math.round(t.naturalHeight*n),f=document.createElement("canvas");f.width=c,f.height=m;let x=f.getContext("2d");if(!x){p(new Error("Canvas 2D context unavailable"));return}x.drawImage(t,0,0,c,m),f.toBlob(R=>{R?l(R):p(new Error("Canvas toBlob returned null"));},i,s);})}async function Ce(t){if(!Fe(t)||t.size<=4194304)return t;let n=await Le(t),s=Math.min(1,4096/Math.max(n.naturalWidth,n.naturalHeight)),i="image/jpeg",l=[.85,.7,.5,.3,.15],p=[s,s*.75,s*.5,s*.25];for(let m of l){let f=await be(n,s,m,i);if(f.size<=4194304)return ye(f,t.name,i)}for(let m of p.slice(1))for(let f of l){let x=await be(n,m,f,i);if(x.size<=4194304)return ye(x,t.name,i)}let c=await be(n,.1,.1,i);return ye(c,t.name,i)}function ye(t,n,s){let i=n.replace(/\.[^.]+$/,".jpg");return new File([t],i,{type:s,lastModified:Date.now()})}var We=21e3,Te=1e3,xe=3e4,He=3,qe="miiflow.v1";function Je(t,n){if(t.webSocketUrl){let c=new URL(t.webSocketUrl);return c.pathname=`/ws/assistant/thread/${n.config.thread_id}/`,c.searchParams.set("role","user"),c.searchParams.set("user_id",U()),c.toString()}let l=$(t).replace(/\/api$/,"").replace(/^https:/,"wss:").replace(/^http:/,"ws:"),p=U();return `${l}/ws/assistant/thread/${n.config.thread_id}/?role=user&user_id=${encodeURIComponent(p)}`}function Xe(t){return [qe,`embed-token.${t.token}`]}function Ge(t){let n=t?.config.branding;return n?{customName:n.custom_name,messageFontSize:n.message_font_size,welcomeMessage:n.welcome_message,chatboxPlaceholder:n.chatbox_placeholder,backgroundBubbleColor:n.background_bubble_color,headerBackgroundColor:n.header_background_color,showHeader:n.show_header,rotatingPlaceholders:n.rotating_placeholders,presetQuestions:n.preset_questions,chatbotLogo:n.chatbot_logo,assistantAvatar:n.assistant_avatar}:null}async function Ye(t,n,s,i){let l=new TextDecoder,p=Date.now(),c="",m=null,f=[],x,R,Q,B=[],y=false,T="answer",v="",j,O,A,G,Y,z,V,ee="",L=n.config.branding,W=()=>{(v||T==="tool")&&(f.push({type:T,content:v,toolName:j,toolDescription:O,success:A,status:G,subtaskId:Y,toolArgs:z,progress:V}),v="",j=void 0,O=void 0,A=void 0,G=void 0,Y=void 0,z=void 0,V=void 0);},fe=()=>{let F=f.map(q=>q);return (v||T==="tool")&&F.push({type:T,content:v,toolName:j,toolDescription:O,success:A,status:G,subtaskId:Y,toolArgs:z,progress:V}),F},D=()=>{let F=fe();if(m)i.onMessageUpdate({id:m,textContent:c,reasoning:F,suggestedActions:x,medias:B.length>0?B:void 0});else {m=`assistant-${Date.now()}`;let q={id:m,textContent:c,participant:{id:"assistant",name:L?.custom_name||n.config.assistant_name,role:"assistant",avatarUrl:L?.assistant_avatar},createdAt:new Date().toISOString(),isStreaming:true,reasoning:F,suggestedActions:x,medias:B.length>0?B:void 0};i.onMessageCreate(q);}};for(;;){let{done:F,value:q}=await t.read();if(F)break;let oe=l.decode(q,{stream:true}),re=(ee+oe).split(`
|
|
6
|
+
`);oe.endsWith(`
|
|
7
|
+
`)?ee="":ee=re.pop()||"";for(let se of re){if(!se.startsWith("data: "))continue;let o=se.slice(6);if(o==="[DONE]")break;try{let e=JSON.parse(o);if(e.type==="assistant_chunk"){if(e.is_tool_planned){(v||T!=="answer")&&(W(),T="answer"),c="",f.push({type:"tool",content:"",toolName:e.tool_name,toolDescription:e.tool_description,status:"planned",subtaskId:e.subtask_id}),D();continue}if(e.is_tool_executing){for(let a=f.length-1;a>=0;a--){let g=f[a];if(g.type==="tool"&&g.toolName===e.tool_name&&(e.subtask_id===void 0||g.subtaskId===e.subtask_id)){f[a].status="executing";break}}D();continue}if(e.is_observation){for(let a=f.length-1;a>=0;a--){let g=f[a];if(g.type==="tool"&&g.toolName===e.tool_name&&(e.subtask_id===void 0||g.subtaskId===e.subtask_id)){f[a].status="completed";break}}D();continue}if(e.suggested_actions){x=e.suggested_actions.map(a=>({id:a.action,label:a.label,value:a.action})),D();continue}e.tool_args&&(z=e.tool_args),e.progress&&(V=e.progress);let r="answer";if(e.is_thinking?r="thinking":e.is_planning?r="planning":e.is_progress_update&&(r="progress"),r!==T&&(W(),T=r),e.tool_name&&(j=e.tool_name),e.success!==void 0&&(A=e.success),e.subtask_id!==void 0&&(Y=e.subtask_id),r==="thinking"){v+=e.chunk||"";let a=v.match(/"thought"\s*:\s*"((?:[^"\\]|\\.)*)"/);a&&(v=a[1].replace(/\\n/g,`
|
|
8
|
+
`).replace(/\\"/g,'"').replace(/\\t/g," ").replace(/\\\\/g,"\\"));}else if(r==="answer"){let a=e.chunk?.trim()||"",g=c.trim();a&&g&&a===g||(c+=e.chunk||"");}else v+=e.chunk||"";e.previous_message_id&&i.onUserMessageIdUpdate(s,e.previous_message_id),D(),D();}else if(e.type==="subagent_dispatch"){let r=e.sub_event||"",a=Array.isArray(e.subagent_path)&&e.subagent_path.length>0?e.subagent_path:[e.subagent_id||""],g=f,h,w=!1;for(let b=0;b<a.length;b++){let k=a[b],C=g.findIndex(S=>S.type==="subagent"&&S.subagentData?.subagentId===k);if(b===a.length-1)if(C>=0)h=g[C];else if(r==="start"){let S={type:"subagent",content:"",subagentData:{subagentId:k,subagentType:e.handle||"",status:"running",result:"",nestedChunks:[]}};g.push(S),h=S;}else w=!0;else {if(C<0){w=!0;break}let S=g[C].subagentData;if(!S){w=!0;break}g=S.nestedChunks;}}if(!w&&h?.subagentData){let b=h.subagentData;if(r==="progress"){let k=e.chunk||"";b.result=(b.result||"")+k;}else if(r==="complete")b.status="completed",e.result&&(b.result=e.result),e.duration_ms!=null&&(b.durationMs=e.duration_ms);else if(r==="failed")b.status="failed",e.error?b.result=(b.result||"")+`
|
|
9
|
+
|
|
10
|
+
Error: ${e.error}`:e.result&&(b.result=e.result),e.duration_ms!=null&&(b.durationMs=e.duration_ms);else if(r==="thinking"){let k=e.chunk||"";if(k){let C=b.nestedChunks,S=C[C.length-1];S&&S.type==="thinking"?S.content=(S.content||"")+k:C.push({type:"thinking",content:k});}}else if(r==="tool"){let k=e.tool_name||"",C=e.tool_description,S=e.status||"planned",d=b.nestedChunks,u=-1;for(let M=d.length-1;M>=0;M--)if(d[M].type==="tool"&&d[M].toolName===k){u=M;break}u>=0?d[u]={...d[u],status:S,toolDescription:C||d[u].toolDescription}:d.push({type:"tool",content:"",toolName:k,toolDescription:C,status:S});}else if(r==="observation"){let k=e.tool_name||"",C=e.success!==!1,S=e.chunk||"",d=b.nestedChunks;for(let u=d.length-1;u>=0;u--)if(d[u].type==="tool"&&d[u].toolName===k){d[u]={...d[u],status:"completed",success:C};break}d.push({type:"observation",content:S,toolName:k,success:C});}}D();}else if(e.type==="clarification_needed"){W();let r={question:e.question||"",options:e.options||[],context:e.context,allowFreeText:e.allow_free_text!==!1,subtaskId:e.subtask_id,subtaskDescription:e.subtask_description,subagentName:e.subagent_name,subagentRole:e.subagent_role,toolCallId:e.tool_call_id};f.push({type:"clarification_needed",content:r.question,clarificationData:r,subtaskId:e.subtask_id}),R=r,c||(c=r.question),D();}else if(e.type==="tool_approval_needed"){W();let r={toolName:e.tool_name||"",toolDescription:e.tool_description||"",toolInputs:e.tool_inputs||{},toolSchema:e.tool_schema,toolCallId:e.tool_call_id,toolLabel:e.tool_label};f.push({type:"tool_approval_needed",content:r.toolDescription,toolApprovalData:r}),Q=r,D();}else if(e.type==="media"){let r=e.media_data;r&&(B=[...B,{id:r.id,url:r.url,mediaType:r.media_type||"image",altText:r.alt_text}],D());}else if(e.type==="assistant_complete"){W(),y=!0;let r=e.message?.text_content||c,a=e.message?.id,g=e.message?.metadata?.sources,h=(Date.now()-p)/1e3;i.onComplete(m,r,a,f,x,g,R,h,Q),c=r,a&&(m=a);break}else if(e.type==="client_tool_invocation"&&e.invocation&&i.onToolInvocation)i.onToolInvocation(e.invocation);else {if(e.type==="error")throw new Error(e.error||"Stream error");if(e.type==="done"){if(m&&!y){W();let r=(Date.now()-p)/1e3;i.onComplete(m,c||"",e.message_id,f,x,void 0,R,r,Q);}else e.message_id&&m&&i.onMessageUpdate({id:m,isStreaming:!1});e.message_id&&(m=e.message_id);break}}}catch(e){if(e instanceof Error&&e.message!=="Stream error"&&(e.message.startsWith("Stream error")||e.message.startsWith("HTTP error")))throw e}}}return {assistantMsgId:m,assistantContent:c}}function Ve(t){let[n,s]=useState([]),[i,l]=useState(false),[p,c]=useState(null),[m,f]=useState(null),[x,R]=useState(true),[Q,B]=useState(null),y=useRef(t);y.current=t;let T=useRef(m);T.current=m;let v=useRef(i);v.current=i;let j=useRef(null),O=useRef(new Map),A=useRef(new Map);useEffect(()=>{let o=false;async function e(){try{let r=await le(y.current);o||(f(r),R(!1));}catch(r){o||(B(r instanceof Error?r.message:"Failed to initialize"),R(false));}}return e(),()=>{o=true;}},[t.publicKey,t.assistantId]);let G=useMemo(()=>Ge(m),[m]),Y=b(G),z=useRef(new Map),V=useCallback(async o=>{let e=T.current;if(!e)throw new Error("Not initialized");if(o.type.startsWith("image/"))try{o=await Ce(o);}catch(a){console.warn("Image compression failed, using original:",a);}let r=await me(y.current,e,o);return z.current.set(r,{id:r,filename:o.name,mimeType:o.type,size:o.size,isImage:o.type.startsWith("image/"),isVideo:o.type.startsWith("video/"),isDocument:!o.type.startsWith("image/")&&!o.type.startsWith("video/")}),r},[]),ee=useCallback(o=>{z.current.delete(o);},[]),L=useCallback(async o=>{let e=T.current;if(!e)throw new Error("Not initialized");let{invocation_id:r,tool_name:a,parameters:g}=o;console.log(`[Miiflow] Tool invocation received: "${a}" (id: ${r})`);let h=O.current.get(a);if(!h)return false;try{let w=await Promise.race([h(g),new Promise((b,k)=>setTimeout(()=>k(new Error("Tool execution timeout (30s)")),3e4))]);return console.log(`[Miiflow] Tool "${a}" executed successfully (id: ${r})`),await K(y.current,e,{invocation_id:r,result:w}),console.log(`[Miiflow] Tool result sent for "${a}" (id: ${r})`),!0}catch(w){return console.error(`[Miiflow] Tool '${a}' execution failed:`,w),await K(y.current,e,{invocation_id:r,error:w instanceof Error?w.message:String(w)}),true}},[]);useEffect(()=>{if(!m)return;let o=null,e=null,r=null,a=0,g=0,h=false;function w(){if(h)return;let b=T.current;if(!b)return;let k=Je(y.current,b),C=Xe(b);o=new WebSocket(k,C);let S=false;o.onopen=()=>{console.log("[Miiflow] WebSocket connected"),S=true,a=0,g=0,e=setInterval(()=>{o?.readyState===WebSocket.OPEN&&o.send(JSON.stringify({type:"heartbeat"}));},We);},o.onmessage=d=>{try{let u=JSON.parse(d.data);u.type==="client_tool_invocation"&&u.invocation&&L(u.invocation).then(async M=>{if(!M){let J=y.current.onToolInvocationFallback;if(!(J?await J(u.invocation):!1)){let E=T.current;E&&K(y.current,E,{invocation_id:u.invocation.invocation_id,error:`No handler found for tool '${u.invocation.tool_name}'`}).catch(console.error);}}}).catch(console.error);}catch{}},o.onclose=()=>{if(e&&(clearInterval(e),e=null),h)return;if(!S){if(g>=He){console.error("[Miiflow] WebSocket giving up after repeated handshake failures");return}g++,le(y.current).then(u=>{h||(T.current=u,f(u),r=setTimeout(w,Te));}).catch(u=>{if(h)return;console.error("[Miiflow] Session refresh failed",u);let M=Math.min(Te*Math.pow(2,g),xe);r=setTimeout(w,M);});return}let d=Math.min(Te*Math.pow(2,a),xe);a++,r=setTimeout(w,d);},o.onerror=()=>{};}return w(),()=>{h=true,e&&clearInterval(e),r&&clearTimeout(r),o&&(o.onclose=null,o.close());}},[m,L]);let W=useCallback(async o=>{let e=T.current;if(!e)throw new Error("Not initialized");await he(y.current,e,o);},[]),fe=useCallback(async o=>{let e=T.current;if(!e)throw new Error("Not initialized");await we(y.current,e,o);},[]),D=useCallback(async(o,e,r)=>{let a=T.current,g=!!o.trim(),h=e&&e.length>0;if(!g&&!h||v.current||!a)return;v.current=true;let w=`msg-${Date.now()}`,b=!!r?.is_clarification_response,k=e?.map(d=>z.current.get(d)).filter(Boolean);e?.forEach(d=>z.current.delete(d));let C=`assistant-pending-${Date.now()}`,S={id:C,textContent:"",participant:{id:"assistant",name:a.config.branding?.custom_name||a.config.assistant_name,role:"assistant",avatarUrl:a.config.branding?.assistant_avatar},createdAt:new Date().toISOString(),isStreaming:true};if(b)s(d=>[...d,S]);else {let d={id:w,textContent:o,participant:{id:"user",name:y.current.userName||"You",role:"user"},createdAt:new Date().toISOString(),attachments:k?.length?k:void 0};s(u=>[...u,d,S]),y.current.onUserMessageCreated?.({id:w,content:o});}l(true),c(C);try{let d=$(y.current),u=new AbortController;j.current=u;let M=await fetch(`${d}/assistant/message/stream/`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.token}`,"x-mii-user-id":U()},body:JSON.stringify({thread_id:a.config.thread_id,text_content:o,message_id:w,metadata:r||{},attachment_ids:e||[]}),signal:u.signal});if(!M.ok)throw new Error(`HTTP error: ${M.status}`);let J=M.body?.getReader();if(!J)throw new Error("No response body");let te=await Ye(J,a,w,{onMessageCreate:E=>{c(E.id),s(I=>[...I.filter(H=>H.id!==C),E]);},onMessageUpdate:E=>{s(I=>I.map(P=>P.id===E.id?{...P,...E}:P));},onUserMessageIdUpdate:(E,I)=>{s(P=>P.map(H=>H.id===E?{...H,id:I}:H));},onComplete:(E,I,P,H,ie,Me,Ae,Ie,De)=>{E&&s(Pe=>Pe.map(ae=>ae.id===E?{...ae,id:P||ae.id,textContent:I,isStreaming:!1,reasoning:H,suggestedActions:ie,citations:Me,pendingClarification:Ae,pendingToolApproval:De,executionTime:Ie}:ae)),y.current.onAssistantMessageComplete?.({id:P||E||"",content:I});},onToolInvocation:async E=>{if(!await L(E)){let P=y.current.onToolInvocationFallback;if(!(P?await P(E):!1)){let ie=T.current;ie&&await K(y.current,ie,{invocation_id:E.invocation_id,error:`No handler found for tool '${E.tool_name}'`});}}}});te.assistantMsgId&&s(E=>E.map(I=>I.id===te.assistantMsgId?{...I,isStreaming:!1}:I));}catch(d){if(d instanceof DOMException&&d.name==="AbortError")return;console.error("[Miiflow] Send error:",d);let u=T.current,M={id:`error-${Date.now()}`,textContent:"Sorry, I encountered an error. Please try again.",participant:{id:"assistant",name:u?.config.branding?.custom_name||u?.config.assistant_name||"Assistant",role:"assistant",avatarUrl:u?.config.branding?.assistant_avatar},createdAt:new Date().toISOString()};s(J=>[...J.filter(te=>te.id!==C),M]),B(d instanceof Error?d.message:"Send failed");}finally{j.current=null,l(false),c(null);}},[L]),F=useCallback(()=>{v.current&&(j.current?.abort(),j.current=null,s(o=>o.map(e=>e.isStreaming?{...e,isStreaming:false}:e)),v.current=false,l(false),c(null));},[]),q=useCallback(async()=>{let o=T.current;if(!o)throw new Error("Not initialized");let e=await ge(y.current,o);e.token||console.warn("[Miiflow] CreateThread did not return new token \u2014 tools may register to wrong thread");let r={...o,config:{...o.config,thread_id:e.threadId},token:e.token||o.token};if(T.current=r,f(r),s([]),A.current.size>0){console.log(`[Miiflow] Re-registering ${A.current.size} tools on new thread`);try{await ne(y.current,r,Array.from(A.current.values()));}catch(a){console.warn("[Miiflow] Failed to re-register tools:",a);}}return e.threadId},[]),oe=useCallback(async o=>{let e=T.current;if(!e)throw new Error("Not initialized");de(o),O.current.set(o.name,o.handler);let r=ue(o);A.current.set(o.name,r);try{await ne(y.current,e,[r]),console.log(`[Miiflow] Tool registered: "${o.name}"`);}catch(a){throw O.current.delete(o.name),A.current.delete(o.name),a}},[]),Se=useCallback(async o=>{let e=T.current;if(!e)throw new Error("Not initialized");for(let h of o)de(h);let r=new Map(O.current),a=new Map(A.current),g=[];for(let h of o){O.current.set(h.name,h.handler);let w=ue(h);A.current.set(h.name,w),g.push(w);}try{await ne(y.current,e,g);let h=o.map(w=>w.name);console.log(`[Miiflow] Tools registered: ${JSON.stringify(h)} (${o.length} tools)`);}catch(h){throw O.current=r,A.current=a,h}},[]),re=useMemo(()=>n.filter(o=>o.participant.role!=="system").map(o=>({id:o.id,textContent:o.textContent?.replace(/\[ref:[^\]]+\]/g,"")||o.textContent,participant:o.participant,createdAt:o.createdAt,isStreaming:o.isStreaming,reasoning:o.reasoning,suggestedActions:o.suggestedActions,citations:o.citations,attachments:o.attachments,pendingClarification:o.pendingClarification,pendingToolApproval:o.pendingToolApproval,executionTime:o.executionTime})),[n]),se=useCallback(o=>{f(o);},[]);return {messages:re,isStreaming:i,streamingMessageId:p,sendMessage:D,uploadFile:V,removeUploadedAttachment:ee,session:m,loading:x,error:Q,branding:G,brandingCSSVars:Y,startNewThread:q,registerTool:oe,registerTools:Se,sendSystemEvent:W,sendPageContext:fe,handleToolInvocation:L,updateSession:se,stopStreaming:F}}function pe(t){try{let n=t.split(".");if(n.length!==3)return null;let s=n[1].replace(/-/g,"+").replace(/_/g,"/"),i=atob(s),l=JSON.parse(i);return typeof l.exp=="number"?l.exp*1e3:null}catch{return null}}function Ke(t,n){let s=pe(t);return s===null?true:s-Date.now()<=n}function Ze(t){let n=pe(t);return n===null?true:Date.now()>=n}function Qe(t){let n=pe(t);return n===null?0:Math.max(0,n-Date.now())}export{_ as ToolValidationError,ge as createThread,$ as getBackendBaseUrl,U as getOrCreateUserId,Qe as getTimeUntilExpiry,le as initSession,Ze as isTokenExpired,Ke as isTokenExpiringSoon,pe as parseTokenExpiry,ne as registerToolsOnBackend,we as sendPageContext,he as sendSystemEvent,K as sendToolResult,ue as serializeToolDefinition,Oe as updateUser,me as uploadFile,Ve as useMiiflowChat,de as validateToolDefinition};//# sourceMappingURL=index.js.map
|
|
9
11
|
//# sourceMappingURL=index.js.map
|